openvibers/openvibe.codes Commits

Improve OpenVibe as a guided path: four steps, one search box, every repository as a row (#148)

OP OpenVibers committed 15 days ago · 14b82b2a63 · 6 files, +66 −39

/improve was 5,504 px on a phone: a paragraph, a numbered list, then 48 repository cards. It is now step 1 of 4
(kit.steps: pick a repository, describe the change, review the result, a maintainer reviews), one search box that
narrows the list by name or what it does (a plain ?q= form; a search result is noindex; no match says so and offers
every repository), and the repositories as rows in two groups, counted. A repository's page is step 2 with step 1
linking back: the form (or, signed out, what happens and one 'Sign in to describe a change' button) comes first; how
the run works and the clone command fold. Shared pinned to v4.36.2, whose kit-web re-enhances after boost swaps.

Changes

ddiff --git a/README.md b/README.md
iindex 07503fd..a1ad351 100644
---- a/README.md
++++ b/README.md
@@@ -160,5 +160,5 @@ Part of the [OpenVibe network](https://openvibe.network). Built in the open by [
<!-- versions:start -->
- openvibe-contracts: v0.232.0
- openvibe-sdk: v0.67.0
--- openvibe-shared: v4.33.0
++- openvibe-shared: v4.36.2
<!-- versions:end -->
ddiff --git a/STATUS.json b/STATUS.json
iindex f4885f8..78a8ba4 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -9,11 +9,11 @@
"domain": "openvibe.codes",
"contracts": "openvibe-contracts v0.232.0",
"sdk": "openvibe-sdk v0.67.0",
-- "shared": "openvibe-shared v4.33.0",
++ "shared": "openvibe-shared v4.36.2",
"packages": [
"openvibe-contracts v0.232.0",
"openvibe-sdk v0.67.0",
-- "openvibe-shared v4.33.0"
++ "openvibe-shared v4.36.2"
],
"summary": "Public at https://openvibe.codes (sudo ovhost deploy codes; unit openvibe-codes on 127.0.0.1:4900). Since 2026-10-08 Codes is the open coding-agent harness: openvibe-codes (a command-line tool and Node.js module, v0.2.0) runs a coding task on Claude Code, Codex, OpenCode, Command Code, Aider, the DeepSeek API or a person's own OpenAI-compatible model on their machine, hands it to the next agent when one fails or goes quiet, and keeps every run; the site carries the agent catalog with a public routing API, getting started, Improve OpenVibe and the community documents. The developer console that used to live here moved to OpenVibe.Services (https://openvibe.services) and every one of its addresses answers a permanent redirect there.",
"works": [
@@@ -52,5 +52,5 @@
"run a coding task on your own machine from any browser (/run; openvibe-codes job through OpenVibe Run and the person's own OpenVibe Node)",
"user JSON API for signed-in people to list, start, read and cancel their own tasks"
],
-- "updated": "2026-10-28"
++ "updated": "2026-10-29"
}
ddiff --git a/package-lock.json b/package-lock.json
iindex ae5c166..8dd0e8f 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -17,7 +17,7 @@
"iovalkey": "^0.4.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.232.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.67.0",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.34.0",
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.36.2",
"pg": "^8.23.0"
},
"bin": {
@@@ -1193,9 +1193,9 @@
}
},
"node_modules/openvibe-shared": {
-- "version": "4.34.0",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.34.0",
-- "integrity": "sha512-Rj5uRO3MbWkyIYAm+vK4Y94ugDNYEjEgGkYnPjSej4yMqhalpaPCkU96dzphMGiOUGP1PNWsiKhwaBj8tewgBg==",
++ "version": "4.36.2",
++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.36.2",
++ "integrity": "sha512-2BGqPZhNr3BhQL/C2pBCBo0zQ4RSu8EX0S2PnZL83Rb5IOZO2BOT/Avdz3AloqORt7X+91kIHLu6T4HPBubmKA==",
"license": "MIT",
"dependencies": {
"jsonwebtoken": "^9.0.0",
ddiff --git a/package.json b/package.json
iindex 3cd144f..a06e017 100644
---- a/package.json
++++ b/package.json
@@@ -32,7 +32,7 @@
"iovalkey": "^0.4.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.232.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.67.0",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.34.0",
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.36.2",
"pg": "^8.23.0"
},
"engines": {
ddiff --git a/server/http/pages.js b/server/http/pages.js
iindex 7e9a825..a4be90b 100644
---- a/server/http/pages.js
++++ b/server/http/pages.js
@@@ -22,6 +22,7 @@ const cache = require('openvibe-shared/cache-policy');
const { asyncRouter } = require('./router');
const { createDiscoveryRoutes, homeJsonLd, SERVICES_ORIGIN } = require('./discovery');
const { html, raw, table, notice } = require('../render/html');
++const kit = require('openvibe-shared/kit');
const { send } = require('../render/layout');
const { markdown } = require('../render/markdown');
const placement = require('../domain/harness-placement');
@@@ -315,30 +316,41 @@ ${offers.map((o) => html`<h3>${o.name} <code>${o.id}</code></h3>${o.agents.lengt
const repoForgejoUrl = (owner, name) => `${config.git.publicUrl}/${owner}/${name}`;
const repoCloneUrl = (owner, name) => `${config.git.publicUrl}/${owner}/${name}.git`;
-- function improvePage() {
++ // The four steps of every change; the index is step 1, a repository's page is step 2.
++ const IMPROVE_STEPS = [
++ { label: 'Pick a repository', href: '/improve' },
++ { label: 'Describe the change', note: 'the harness runs it on your machine' },
++ { label: 'Review the result', note: 'then open the pull request' },
++ { label: 'A maintainer reviews', note: 'the owner approves what ships' },
++ ];
++ const improveSteps = (at) => kit.steps({ label: 'From a change to a pull request', current: at, items: IMPROVE_STEPS });
++ const shortName = (m) => m.repository.split('/')[1].replace(/^OpenVibe\./, '');
++ const tagline = (m) => (m.site && (m.site.tagline || m.site.what)) || m.name;
++
++ /** Step 1: pick the repository. One search box (it works without JavaScript: ?q=), then every repository as a row. */
++ function improvePage(q = '') {
const { running, libraries } = repoGroups();
-- const repoCards = (list) => html`<ul class="cards">${list.map((m) => html`<li><a href="/improve/${m.repository}"><strong>${m.repository.split('/')[1]}</strong></a><span>${(m.site && (m.site.tagline || m.site.what)) || m.name}</span></li>`)}</ul>`;
++ const needle = String(q || '').trim().toLowerCase().slice(0, 60);
++ const hit = (m) => !needle || [m.repository, m.name, tagline(m)].some((t) => String(t || '').toLowerCase().includes(needle));
++ const rows = (list) => list.filter(hit).map((m) => kit.row({ href: `/improve/${m.repository}`, title: shortName(m), sub: tagline(m), trail: kit.icon('chevron') }));
++ const services = rows(running), libs = rows(libraries);
++ const found = services.length + libs.length;
++ const lists = !found
++ ? kit.empty({ icon: 'search', title: `No repository matches “${needle}”`, text: 'Try a site\'s name (chat, video, games) or what it does.', action: { href: '/improve', label: 'Every repository' } })
++ : (services.length ? kit.list(services, { title: `Services · ${services.length}` }) : '') + (libs.length ? kit.list(libs, { title: `Shared libraries · ${libs.length}` }) : '');
return {
-- agent: true, index: true, cache: PUBLIC_CACHE,
++ agent: true, index: !needle, cache: PUBLIC_CACHE,
++ styles: ['kit.css'], sharedScripts: ['kit-web.js'],
title: 'Improve OpenVibe',
description: 'OpenVibe is open source: every service and library is on GitHub. Pick a repository, describe the change, and send a pull request; the harness makes the change on your machine.',
-- crumbs: [{ label: 'Improve OpenVibe' }],
-- body: html`<h1>Improve OpenVibe</h1>
--<p>Every OpenVibe service and library is open source under <a href="${GITHUB}">OpenVibers</a>, and changes arrive as pull requests that the owner approves. Every change takes the same path: repository → change → tests → independent review → pull request, never straight to <code>main</code>. Pick a repository, describe the change, and start a run on your own machine.</p>
--<ol class="steps">
--<li><strong>Pick a repository</strong> from the lists below.</li>
--<li><strong>Describe the change</strong> in plain language; the harness runs the tests your way.</li>
--<li><strong>Review the result</strong> on the task page, then open a pull request from the branch it pushed.</li>
--<li><strong>A maintainer reviews</strong>; the owner approves what ships. See the <a href="/policy/contributor-ladder">contributor ladder</a>.</li>
--</ol>
--<p>Before you start: the <a href="/policy/contributing">contributing guide</a> and the <a href="/policy/code-of-conduct">code of conduct</a>. Building your own app? That is <a href="${SERVICES_ORIGIN}">OpenVibe.Services</a>.</p>
--<h2>Services that run</h2>
--${repoCards(running)}
--<h2>Shared libraries</h2>
--${repoCards(libraries)}`,
++ body: html`${raw(kit.head({ title: 'Improve OpenVibe', sub: 'All open source. Pick one, describe a change.', sticky: false }))}
++${raw(improveSteps(0))}
++${raw(kit.find({ action: '/improve', name: 'q', value: needle, placeholder: 'Find: chat, video, games…', label: 'Find a repository' }))}
++${raw(lists)}
++<p class="ovk-muted">Changes arrive as pull requests the owner approves — never straight to <code>main</code>. Before you start: the <a href="/policy/contributing">contributing guide</a>, the <a href="/policy/code-of-conduct">code of conduct</a> and the <a href="/policy/contributor-ladder">contributor ladder</a>. Building your own app instead? That is <a href="${SERVICES_ORIGIN}">OpenVibe.Services</a>.</p>`,
};
}
-- r.get('/improve', (req, res) => page(req, res, improvePage()));
++ r.get('/improve', (req, res) => page(req, res, improvePage(req.query && req.query.q)));
async function improveRepoPage(owner, name, { signed = false } = {}) {
const meta = repoMeta().find((m) => m.repository === `${owner}/${name}`);
@@@ -363,23 +375,18 @@ ${forgejoError ? html`<p class="run-notice run-notice-error" role="alert">OpenVi
<label class="run-check"><input type="checkbox" name="push" value="1" checked> Push the change to a new branch on OpenVibe Git <small>(needs a token on your machine; never the branch it started from)</small></label>
<label class="run-check"><input type="checkbox" name="pull_request" value="1" checked> and open a pull request</label>
<button class="run-btn run-btn-primary" type="submit">Start on my machine</button>
--</form>` : html`<p><a class="run-btn run-btn-primary" href="/auth/login?next=${encodeURIComponent(`/improve/${owner}/${name}`)}">Sign in to start a run</a></p>`;
++</form>` : html`<p class="ovk-muted">Say what should change, in plain words. The harness makes the change on your machine, runs the tests, and can open the pull request.</p><p>${raw(kit.button({ href: `/auth/login?next=${encodeURIComponent(`/improve/${owner}/${name}`)}`, label: 'Sign in to describe a change', icon: 'user' }))}</p>`;
return {
agent: true, index: true, cache: PUBLIC_CACHE,
title: `Improve ${name}`,
description: `Contribute to ${meta.name}: describe a change and run it on your machine through OpenVibe.Codes.`,
crumbs: [{ label: 'Improve OpenVibe', href: '/improve' }, { label: meta.name }],
-- body: html`<h1>${meta.name}</h1>
--<p class="lead">${(meta.site && (meta.site.tagline || meta.site.what)) || meta.name}. The source lives at <a href="${repoForgejoUrl(owner, name)}">${repoForgejoUrl(owner, name)}</a> and is mirrored from <a href="${GITHUB}/${name}">GitHub</a>.</p>
--<pre tabindex="0" class="codes-clone"><code>git clone ${repoCloneUrl(owner, name)}</code></pre>
--<h2>The path from a change to a pull request</h2>
--<ol class="steps">
--<li>The harness clones the repository onto <strong>your machine</strong> and checks out the default branch.</li>
--<li>The agent makes the change you described and runs the tests.</li>
--<li>If you asked it to push, it commits to a new branch and opens a pull request on OpenVibe Git.</li>
--<li>You review the result, then the maintainers review the pull request.</li>
--</ol>
--${form}`,
++ styles: ['kit.css'], sharedScripts: ['kit-web.js'],
++ body: html`${raw(kit.head({ title: meta.name, sub: tagline(meta), sticky: false }))}
++${raw(improveSteps(1))}
++${form}
++${raw(kit.fold({ title: 'How the run works', body: '<ol class="steps"><li>The harness clones the repository onto <strong>your machine</strong> and checks out the default branch.</li><li>The agent makes the change you described and runs the tests.</li><li>If you asked it to push, it commits to a new branch and opens a pull request on OpenVibe Git.</li><li>You review the result, then the maintainers review the pull request.</li></ol>' }))}
++${raw(kit.fold({ title: 'Clone it yourself', meta: 'git clone', body: `${kit.snippet({ code: `git clone ${repoCloneUrl(owner, name)}`, id: 'clone-cmd' })}<p class="ovk-muted">Browse it at <a href="${kit.esc(repoForgejoUrl(owner, name))}">${kit.esc(repoForgejoUrl(owner, name))}</a> or on <a href="${kit.esc(`${GITHUB}/${name}`)}">GitHub</a>.</p>` }))}`,
};
}
r.get('/improve/:owner/:name', async (req, res) => {
ddiff --git a/test/improve.test.js b/test/improve.test.js
iindex bda23a5..21de3ec 100644
---- a/test/improve.test.js
++++ b/test/improve.test.js
@@@ -29,6 +29,26 @@ const { boot, check, done } = require('./helpers/boot');
assert.ok(r.text.includes('OpenVibe.Codes'));
});
++ await check('the landing is step 1 of 4, and one search box narrows the repositories (no JavaScript needed)', async () => {
++ const all = await t.get('/improve');
++ assert.ok(all.text.includes('<nav class="ovk-steps"') && all.text.includes('Step 1 of 4') && all.text.includes('aria-current="step"'), 'the steps');
++ assert.ok(all.text.includes('<form class="ovk-find"') && all.text.includes('name="q"'), 'the search box');
++ assert.ok(all.text.includes('Services · ') && all.text.includes('Shared libraries · '), 'both groups, counted');
++ const some = await t.get('/improve?q=codes');
++ assert.ok(some.text.includes('href="/improve/OpenVibers/OpenVibe.Codes"'), 'a match stays');
++ assert.ok(!some.text.includes('href="/improve/OpenVibers/OpenVibe.Shared"'), 'a non-match goes');
++ assert.ok(/<meta name="robots" content="noindex/.test(some.text), 'a search result is not indexed');
++ const none = await t.get('/improve?q=zzzz-nothing');
++ assert.ok(none.text.includes('No repository matches') && none.text.includes('href="/improve"'), 'an empty search says so and offers every repository');
++ });
++
++ await check('a repository page is step 2: describe the change; the clone command and the run explanation fold', async () => {
++ const r = await t.get('/improve/OpenVibers/OpenVibe.Codes', { as: ada });
++ assert.ok(r.text.includes('Step 2 of 4') && r.text.includes('href="/improve"'), 'step 2, with step 1 linking back');
++ assert.ok(r.text.includes('Clone it yourself') && r.text.includes('git clone '), 'the clone command, folded');
++ assert.ok(r.text.indexOf('name="prompt"') < r.text.indexOf('How the run works'), 'the form comes before the explanation');
++ });
++
await check('a repository page shows the guided path and asks guests to sign in', async () => {
const r = await t.get('/improve/OpenVibers/OpenVibe.Codes');
assert.strictEqual(r.status, 200);