@kvman/kvbuilder 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +9 -0
- package/dist/api.d.ts +271 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +2 -0
- package/dist/api.js.map +1 -0
- package/dist/app/install-source.d.ts +3 -0
- package/dist/app/install-source.d.ts.map +1 -0
- package/dist/app/install-source.js +11 -0
- package/dist/app/install-source.js.map +1 -0
- package/dist/app/register-app-reads.d.ts +3 -0
- package/dist/app/register-app-reads.d.ts.map +1 -0
- package/dist/app/register-app-reads.js +36 -0
- package/dist/app/register-app-reads.js.map +1 -0
- package/dist/app/register-app.d.ts +3 -0
- package/dist/app/register-app.d.ts.map +1 -0
- package/dist/app/register-app.js +124 -0
- package/dist/app/register-app.js.map +1 -0
- package/dist/build/register-build.d.ts +3 -0
- package/dist/build/register-build.d.ts.map +1 -0
- package/dist/build/register-build.js +34 -0
- package/dist/build/register-build.js.map +1 -0
- package/dist/connectors/docs.d.ts +30 -0
- package/dist/connectors/docs.d.ts.map +1 -0
- package/dist/connectors/docs.js +18 -0
- package/dist/connectors/docs.js.map +1 -0
- package/dist/connectors/ext.d.ts +40 -0
- package/dist/connectors/ext.d.ts.map +1 -0
- package/dist/connectors/ext.js +13 -0
- package/dist/connectors/ext.js.map +1 -0
- package/dist/connectors/kvman.d.ts +113 -0
- package/dist/connectors/kvman.d.ts.map +1 -0
- package/dist/connectors/kvman.js +41 -0
- package/dist/connectors/kvman.js.map +1 -0
- package/dist/connectors/preset.d.ts +26 -0
- package/dist/connectors/preset.d.ts.map +1 -0
- package/dist/connectors/preset.js +11 -0
- package/dist/connectors/preset.js.map +1 -0
- package/dist/connectors/preview.d.ts +41 -0
- package/dist/connectors/preview.d.ts.map +1 -0
- package/dist/connectors/preview.js +14 -0
- package/dist/connectors/preview.js.map +1 -0
- package/dist/docs/guides.d.ts +54 -0
- package/dist/docs/guides.d.ts.map +1 -0
- package/dist/docs/guides.js +68 -0
- package/dist/docs/guides.js.map +1 -0
- package/dist/docs/own-docs.d.ts +5 -0
- package/dist/docs/own-docs.d.ts.map +1 -0
- package/dist/docs/own-docs.js +36 -0
- package/dist/docs/own-docs.js.map +1 -0
- package/dist/docs/pages.d.ts +18 -0
- package/dist/docs/pages.d.ts.map +1 -0
- package/dist/docs/pages.js +44 -0
- package/dist/docs/pages.js.map +1 -0
- package/dist/docs/register-docs.d.ts +3 -0
- package/dist/docs/register-docs.d.ts.map +1 -0
- package/dist/docs/register-docs.js +25 -0
- package/dist/docs/register-docs.js.map +1 -0
- package/dist/ext/ext-check.d.ts +8 -0
- package/dist/ext/ext-check.d.ts.map +1 -0
- package/dist/ext/ext-check.js +26 -0
- package/dist/ext/ext-check.js.map +1 -0
- package/dist/ext/ext-list.d.ts +8 -0
- package/dist/ext/ext-list.d.ts.map +1 -0
- package/dist/ext/ext-list.js +20 -0
- package/dist/ext/ext-list.js.map +1 -0
- package/dist/ext/ext-new.d.ts +15 -0
- package/dist/ext/ext-new.d.ts.map +1 -0
- package/dist/ext/ext-new.js +11 -0
- package/dist/ext/ext-new.js.map +1 -0
- package/dist/ext/findings.d.ts +12 -0
- package/dist/ext/findings.d.ts.map +1 -0
- package/dist/ext/findings.js +44 -0
- package/dist/ext/findings.js.map +1 -0
- package/dist/ext/register-ext.d.ts +3 -0
- package/dist/ext/register-ext.d.ts.map +1 -0
- package/dist/ext/register-ext.js +49 -0
- package/dist/ext/register-ext.js.map +1 -0
- package/dist/folders.d.ts +18 -0
- package/dist/folders.d.ts.map +1 -0
- package/dist/folders.js +53 -0
- package/dist/folders.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/job-options.d.ts +33 -0
- package/dist/job-options.d.ts.map +1 -0
- package/dist/job-options.js +13 -0
- package/dist/job-options.js.map +1 -0
- package/dist/preset/register-preset.d.ts +3 -0
- package/dist/preset/register-preset.d.ts.map +1 -0
- package/dist/preset/register-preset.js +55 -0
- package/dist/preset/register-preset.js.map +1 -0
- package/dist/preview/preview-call.d.ts +20 -0
- package/dist/preview/preview-call.d.ts.map +1 -0
- package/dist/preview/preview-call.js +39 -0
- package/dist/preview/preview-call.js.map +1 -0
- package/dist/preview/preview-start.d.ts +8 -0
- package/dist/preview/preview-start.d.ts.map +1 -0
- package/dist/preview/preview-start.js +103 -0
- package/dist/preview/preview-start.js.map +1 -0
- package/dist/preview/preview-state.d.ts +11 -0
- package/dist/preview/preview-state.d.ts.map +1 -0
- package/dist/preview/preview-state.js +18 -0
- package/dist/preview/preview-state.js.map +1 -0
- package/dist/preview/register-preview.d.ts +3 -0
- package/dist/preview/register-preview.d.ts.map +1 -0
- package/dist/preview/register-preview.js +75 -0
- package/dist/preview/register-preview.js.map +1 -0
- package/dist/problems.d.ts +8 -0
- package/dist/problems.d.ts.map +1 -0
- package/dist/problems.js +11 -0
- package/dist/problems.js.map +1 -0
- package/dist/program-command.d.ts +17 -0
- package/dist/program-command.d.ts.map +1 -0
- package/dist/program-command.js +12 -0
- package/dist/program-command.js.map +1 -0
- package/dist/query/register-app-query.d.ts +3 -0
- package/dist/query/register-app-query.d.ts.map +1 -0
- package/dist/query/register-app-query.js +39 -0
- package/dist/query/register-app-query.js.map +1 -0
- package/dist/register-with-kvcoder.d.ts +3 -0
- package/dist/register-with-kvcoder.d.ts.map +1 -0
- package/dist/register-with-kvcoder.js +20 -0
- package/dist/register-with-kvcoder.js.map +1 -0
- package/dist/run-bin.d.ts +31 -0
- package/dist/run-bin.d.ts.map +1 -0
- package/dist/run-bin.js +137 -0
- package/dist/run-bin.js.map +1 -0
- package/dist/run-program.d.ts +12 -0
- package/dist/run-program.d.ts.map +1 -0
- package/dist/run-program.js +49 -0
- package/dist/run-program.js.map +1 -0
- package/docs/building.md +154 -0
- package/docs/guide.md +34 -0
- package/locales/ar.json +12 -0
- package/locales/en.json +12 -0
- package/package.json +55 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Program } from './program-command.ts';
|
|
2
|
+
export type ProgramRun = {
|
|
3
|
+
exitCode: number;
|
|
4
|
+
stdout: string;
|
|
5
|
+
output: string;
|
|
6
|
+
};
|
|
7
|
+
export { treeKill } from './program-command.ts';
|
|
8
|
+
export declare function killTree(pid: number | undefined): void;
|
|
9
|
+
export declare function runProgram(program: Program, args: readonly string[], cwd: string, signal: AbortSignal): Promise<ProgramRun>;
|
|
10
|
+
/** The last lines of a run's output, for a Problem's message. */
|
|
11
|
+
export declare function lastLines(output: string, count?: number): string;
|
|
12
|
+
//# sourceMappingURL=run-program.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-program.d.ts","sourceRoot":"","sources":["../src/run-program.ts"],"names":[],"mappings":"AAEA,OAAO,EAA4B,KAAK,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAK9E,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9E,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAYtD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAuB3H;AAED,iEAAiE;AACjE,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,MAAM,CAE5D"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { kvbuilderProblem } from "./problems.js";
|
|
3
|
+
import { programCommand, treeKill } from "./program-command.js";
|
|
4
|
+
export { treeKill } from "./program-command.js";
|
|
5
|
+
export function killTree(pid) {
|
|
6
|
+
if (pid === undefined)
|
|
7
|
+
return;
|
|
8
|
+
const kill = treeKill(process.platform, pid);
|
|
9
|
+
if (kill.kind === 'taskkill') {
|
|
10
|
+
spawn(kill.command, kill.args, { stdio: 'ignore', windowsHide: true });
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
process.kill(-kill.pid, 'SIGKILL');
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
if (!(error instanceof Error && 'code' in error && error.code === 'ESRCH'))
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function runProgram(program, args, cwd, signal) {
|
|
22
|
+
const start = programCommand(process.platform, program, args);
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
const child = spawn(start.command, start.args, { cwd, stdio: ['ignore', 'pipe', 'pipe'], detached: start.detached, windowsHide: true });
|
|
25
|
+
let stdout = '';
|
|
26
|
+
let output = '';
|
|
27
|
+
child.stdout.setEncoding('utf8').on('data', (text) => {
|
|
28
|
+
stdout += text;
|
|
29
|
+
output += text;
|
|
30
|
+
});
|
|
31
|
+
child.stderr.setEncoding('utf8').on('data', (text) => (output += text));
|
|
32
|
+
const abort = () => killTree(child.pid);
|
|
33
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
34
|
+
child.once('error', (error) => {
|
|
35
|
+
signal.removeEventListener('abort', abort);
|
|
36
|
+
const missing = 'code' in error && error.code === 'ENOENT';
|
|
37
|
+
reject(missing ? kvbuilderProblem('NPM_FAILED', `${program} isn't on the PATH; install Node.js with npm.`, { program }) : error);
|
|
38
|
+
});
|
|
39
|
+
child.once('close', (code) => {
|
|
40
|
+
signal.removeEventListener('abort', abort);
|
|
41
|
+
resolve({ exitCode: code ?? 1, stdout, output });
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/** The last lines of a run's output, for a Problem's message. */
|
|
46
|
+
export function lastLines(output, count = 20) {
|
|
47
|
+
return output.trimEnd().split('\n').slice(-count).join('\n');
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=run-program.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-program.js","sourceRoot":"","sources":["../src/run-program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAgB,MAAM,sBAAsB,CAAC;AAO9E,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,MAAM,UAAU,QAAQ,CAAC,GAAuB;IAC9C,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO;IAC9B,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC7C,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QACvE,OAAO;IACT,CAAC;IACD,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC;YAAE,MAAM,KAAK,CAAC;IAC1F,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAgB,EAAE,IAAuB,EAAE,GAAW,EAAE,MAAmB;IACpG,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAC9D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QACxI,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;YAC3D,MAAM,IAAI,IAAI,CAAC;YACf,MAAM,IAAI,IAAI,CAAC;QACjB,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC;QAChF,MAAM,KAAK,GAAG,GAAS,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC9C,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAC5B,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YAC3C,MAAM,OAAO,GAAG,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;YAC3D,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,YAAY,EAAE,GAAG,OAAO,+CAA+C,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACnI,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YAC3B,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YAC3C,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,SAAS,CAAC,MAAc,EAAE,KAAK,GAAG,EAAE;IAClD,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/D,CAAC"}
|
package/docs/building.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Building kvman
|
|
2
|
+
|
|
3
|
+
kvbuilder ("kvman builder") is the extension that lets kvman's agent build and manage kvman itself. It adds nothing to a chat until the person types `/build-kvman` in it: then that chat gets kvbuilder's connectors and its guide. It has no agent of its own, and everything it does for extensions is also available to any other harness, or to a person, through the testkit's command-line tools (below).
|
|
4
|
+
|
|
5
|
+
## The connectors
|
|
6
|
+
|
|
7
|
+
The agent calls each connector with kvcoder's `run` tool: `run { description, connector, command, payload }`. Every connector also has `help`, which describes its commands, and one command's payload with `{ "command": "<name>" }`.
|
|
8
|
+
|
|
9
|
+
| Connector | Commands | Use it to |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| `kvman` | `model-list`, `model-set`, `settings-list`, `settings-set`, `settings-reset`, `extensions-list`, `extensions-install`, `extensions-uninstall`, `preset-get`, `workspaces-list`, `jobs-list`, `jobs-get`, `processes-list`, `health-get`, `query-get` | see and change the app you are running in: its default model, settings, extensions, and preset; its workspaces, jobs, processes, and health; and any public query of an installed extension |
|
|
12
|
+
| `ext` | `new`, `list`, `check`, `test` | scaffold an extension project in the workspace, list the projects, type-check one and see what kvman would refuse, run its tests |
|
|
13
|
+
| `preset` | `new`, `check` | write a preset file and check it before running it |
|
|
14
|
+
| `preview` | `start`, `stop`, `status`, `query-get`, `command-run` | run projects in a separate kvman, get its URL, and call their commands and queries there |
|
|
15
|
+
| `docs` | `list`, `get` | read the guides of kvman and the pages of every installed extension |
|
|
16
|
+
|
|
17
|
+
Folders and files are always relative to the workspace folder, and must stay inside it.
|
|
18
|
+
|
|
19
|
+
The payload is the command's input as JSON:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{ "description": "Scaffolding the notes extension", "connector": "ext", "command": "new", "payload": { "name": "notes", "namespace": "notes", "folder": "notes" } }
|
|
23
|
+
{ "description": "Checking the notes project", "connector": "ext", "command": "check", "payload": { "folder": "notes" } }
|
|
24
|
+
{ "description": "Switching the default model", "connector": "kvman", "command": "model-set", "payload": { "model": "anthropic/claude-sonnet-5-5" } }
|
|
25
|
+
{ "description": "Adding the notes extension to the app", "connector": "kvman", "command": "extensions-install", "payload": { "source": "npm:@acme/notes@1.2.3" } }
|
|
26
|
+
{ "description": "Adding the notes project of this workspace to the app", "connector": "kvman", "command": "extensions-install", "payload": { "source": "path:notes" } }
|
|
27
|
+
{ "description": "Finding the jobs that failed", "connector": "kvman", "command": "jobs-list", "payload": { "status": "failed", "limit": 20 } }
|
|
28
|
+
{ "description": "Listing the chats", "connector": "kvman", "command": "query-get", "payload": { "name": "kvcoder.session.list", "input": { "limit": 10 } } }
|
|
29
|
+
{ "description": "Previewing the notes project", "connector": "preview", "command": "start", "payload": { "extensions": ["notes"] } }
|
|
30
|
+
{ "description": "Reading the greeting in the preview", "connector": "preview", "command": "query-get", "payload": { "name": "notes.greeting.get" } }
|
|
31
|
+
{ "description": "Adding a note in the preview", "connector": "preview", "command": "command-run", "payload": { "name": "notes.item.add", "input": { "text": "Milk" } } }
|
|
32
|
+
{ "description": "Reading the views guide", "connector": "docs", "command": "get", "payload": { "extension": "@kvman/kvwebui", "topic": "views" } }
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
A command that takes nothing, such as `ext list`, `kvman preset-get`, `kvman health-get`, or `docs list`, needs no payload.
|
|
36
|
+
|
|
37
|
+
## `/build-kvman`: starting to build kvman in a chat
|
|
38
|
+
|
|
39
|
+
Building kvman starts with the person, never with the agent. In a chat's send box they type `/build-kvman`, or `/build-kvman` followed by what they want ("/build-kvman add a notes page"). kvcoder then runs `kvbuilder.build.start { sessionId, argument }`, which:
|
|
40
|
+
|
|
41
|
+
- enables the five connectors for that chat only. They are registered with `optIn: true`, so no other chat has them in its prompt or can call them;
|
|
42
|
+
- sets the chat's prompt section `guide` from `docs/guide.md`: what to ask the person, the rules below, and the method of the next parts. A section is in every prompt of the chat, so a summary of the chat never drops it;
|
|
43
|
+
- adds the note "Building kvman is on for this chat", the first time.
|
|
44
|
+
|
|
45
|
+
The send box then sends the text after the command as the person's message, or "I want to change this app." when there was none, and the agent's turn starts. Running `/build-kvman` again in the same chat renews the guide and adds no second note. Nothing switches building off: a new chat starts without it. `guide` is not a page of `docs get`.
|
|
46
|
+
|
|
47
|
+
## Talking with a person who isn't a developer
|
|
48
|
+
|
|
49
|
+
The guide tells the agent to choose the mechanism and leave the person to choose the result:
|
|
50
|
+
|
|
51
|
+
- **Which app?** "The app" can mean kvman or the project in the workspace folder; the agent asks once, with a choice, unless the request is clear.
|
|
52
|
+
- **Ask about the result, and only what is missing.** One question per `ask` call, the options written as outcomes, the recommended one first. It never asks what `extensions-list`, `settings-list`, or `preset-get` would answer.
|
|
53
|
+
- **No kvman words.** Extensions, namespaces, presets, setting keys, and commands stay out of what the agent asks and says, and out of a call's `description`, which is the text of the approval card.
|
|
54
|
+
- **Look, then use the smallest thing.** A setting, then an extension that is installed, then one the agent builds, and a new preset only for a different app. If kvman can't do it, the agent says so.
|
|
55
|
+
- **Show before changing.** For an extension it built, the agent starts the preview, gives the address, and asks the person to confirm before it adds the extension to the app.
|
|
56
|
+
- **Undo and restart.** The agent says how to undo each change (`settings-reset`, `extensions-uninstall`), what is still unchecked, and, after a change to the extensions, restarts kvman (below).
|
|
57
|
+
|
|
58
|
+
## Building an extension, step by step
|
|
59
|
+
|
|
60
|
+
1. **Read.** `docs list`, then `docs get` for `sdk`, and for `i18n` or `presets` when the work touches them; before building on an installed extension, its own pages.
|
|
61
|
+
2. **Scaffold.** `ext new { name, namespace, folder, web? }` writes the project and runs `npm install`.
|
|
62
|
+
3. **Write.** Edit `src/`, `locales/`, and `test/` with `fs`. Don't edit `dist/`.
|
|
63
|
+
4. **Check.** After every change, `ext check { folder }`, then `ext test { folder }`. `ext check` answers `[{ file?, message, hint }]`: TypeScript's errors first, then what kvman would refuse or show untranslated.
|
|
64
|
+
5. **Run.** `preview start { extensions: [folder] }` answers `{ url }`.
|
|
65
|
+
6. **Check that it works.** Call the project in the preview (below), and open its page at the URL.
|
|
66
|
+
7. **Install**, when the person asks: `kvman extensions-install { source: "path:<folder>" }`, then `kvman restart`.
|
|
67
|
+
|
|
68
|
+
To improve an extension that exists, read its code and its docs page first, find what is wrong with `ext check`, `ext test`, and the preview, change the smallest thing, and run the checks again.
|
|
69
|
+
|
|
70
|
+
## Seeing the app you run in
|
|
71
|
+
|
|
72
|
+
These `kvman` commands only read, and never ask the person:
|
|
73
|
+
|
|
74
|
+
| Command | Payload | Answers |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `model-list` | none | the models that can be called now: `[{ id, name, provider, isDefault }]` |
|
|
77
|
+
| `settings-list` | none | every setting: `[{ key, description, schema, scopes, value, source }]`, where `source` is `workspace`, `global`, `preset`, or `default` |
|
|
78
|
+
| `extensions-list` | none | the extensions that run: `[{ name, version, source, namespace, commands, queries, settings }]`, the last three as names |
|
|
79
|
+
| `preset-get` | none | the preset as stored now: `{ name, origin, file?, extensions, settings }` |
|
|
80
|
+
| `workspaces-list` | none | the open workspaces, Home first |
|
|
81
|
+
| `jobs-list` | `{ status?, limit }` | the async and scheduled jobs of the chat's workspace, newest first, each `{ id, name, input, status, attempts, retries, output?, problem?, createdAt, startedAt?, endedAt? }`; `status` is `queued`, `running`, `succeeded`, `failed`, or `cancelled`, and a failed job holds its `problem` |
|
|
82
|
+
| `jobs-get` | `{ id }` | one job of any workspace |
|
|
83
|
+
| `processes-list` | none | the long-lived processes: `[{ extension, workspaceId, name, pid, startedAt }]` |
|
|
84
|
+
| `health-get` | none | `{ version, preset, mode, workers, uptimeMs, languages }` |
|
|
85
|
+
| `query-get` | `{ name, input? }` | the output of the public query `name`, run with `input` (`{}` when left out) in the chat's workspace |
|
|
86
|
+
|
|
87
|
+
`query-get` runs a query of the kernel or of any installed extension, so the agent can use what is installed. It runs queries only:
|
|
88
|
+
|
|
89
|
+
- a command's name fails `VALIDATION_FAILED`;
|
|
90
|
+
- a name that no public query has (unknown, or private) fails `NOT_FOUND`;
|
|
91
|
+
- a name under `kernel.secrets.` fails `VALIDATION_FAILED`;
|
|
92
|
+
- the query's own failure comes back as it is.
|
|
93
|
+
|
|
94
|
+
There is no `kvman` command that runs an arbitrary command of the app: a command is reached only through a connector that names it. There is none that opens or closes a workspace.
|
|
95
|
+
|
|
96
|
+
## Changing the app you run in
|
|
97
|
+
|
|
98
|
+
`kvman` is for the running app; `ext` is for a project in the workspace. Six commands change the app: `model-set`, `settings-set`, `settings-reset`, `extensions-install`, `extensions-uninstall`, and `restart`.
|
|
99
|
+
|
|
100
|
+
- **The person is asked before each one runs.** The call becomes an approval card showing its description, the command, and its payload, whatever `kvcoder.shell.approval` says. Allowed, it runs; denied, the call answers `denied by the user` and nothing changed. Make one call for one change, and say in its `description` what changes.
|
|
101
|
+
- **Model and settings change at once.** `model-set` takes the full model id of a model `model-list` shows (a model of a connected provider or of a custom provider); `settings-set` and `settings-reset` take a setting key and a scope, `global` or `workspace`. `settings-set` replaces the whole value, so for a list read it with `settings-list` first.
|
|
102
|
+
- **Extensions and the preset change the preset file, and apply at the next start.** `extensions-install` takes a source, which carries the package name, and `extensions-uninstall` takes a name. Both answer `{ file, restartRequired: true }`. Nothing is installed, loaded, or trusted by the call: `restart` applies it, and the terminal asks the person to trust a new extension then. The first change to the bundled preset saves a copy of it as `<home>/presets/<name>.json`.
|
|
103
|
+
|
|
104
|
+
`restart` takes nothing and answers `{ restarting: true }`. kvman stops and starts again in the same process, with the same arguments, the same terminal, and no new browser tab, and the person is asked first. It stops running work: a chat's step that is running ends `interrupted` (the person sends a message to go on), long-lived processes such as a preview or a server the agent started stop, and an async job that didn't finish runs again if it has retries left. Open workspaces stay open. If kvman can't start with the change, because an extension is invalid or a setting fails, it puts the preset back as it was, starts again, and `health-get` has `rolledBack` with the failure's Problem; that is only for the start that follows. A new extension that isn't bundled still asks for trust in the terminal, so the agent says so.
|
|
105
|
+
|
|
106
|
+
The three sources of `extensions-install`:
|
|
107
|
+
|
|
108
|
+
| Source | Means | Checked by the call |
|
|
109
|
+
|---|---|---|
|
|
110
|
+
| `npm:<package name>@<exact version>` | a published package | its format only |
|
|
111
|
+
| `path:<folder>` | a project in the workspace | the folder resolves against the workspace folder and must stay inside it (`VALIDATION_FAILED`); it must hold a package.json with a `kvman` field (`kvbuilder/NOT_A_PROJECT`); kvman takes the extension's name from that package.json |
|
|
112
|
+
| `bundled:<package name>` | an extension that ships with kvman | the name must be a bundled extension's (`VALIDATION_FAILED`) |
|
|
113
|
+
|
|
114
|
+
A `path:` source is stored with the absolute folder, because kvman resolves a relative one against the preset file, not the workspace. A `path:` extension reloads when its files change.
|
|
115
|
+
|
|
116
|
+
kvcoder's own configuration is settings too: `kvcoder.delegate.workers` (the workers of `delegate`), `kvcoder.mcp.servers` (the MCP servers), `kvcoder.connectors` (programs as connectors), and `kvcoder.connectors.disabled` (the connectors that are off).
|
|
117
|
+
|
|
118
|
+
None of these calls reads, lists, or changes a secret.
|
|
119
|
+
|
|
120
|
+
## The tools underneath
|
|
121
|
+
|
|
122
|
+
The connectors run the tools of `@kvman/testkit`, which you can run by hand in any terminal:
|
|
123
|
+
|
|
124
|
+
| Tool | Does |
|
|
125
|
+
|---|---|
|
|
126
|
+
| `kvman-new <folder> --name <name> --namespace <namespace> [--web]` | writes a project with its guides, `AGENTS.md`, and a docs page, then runs `npm install` |
|
|
127
|
+
| `kvman-check` | loads the project in a test kernel and reports what kvman would refuse or show untranslated |
|
|
128
|
+
| `kvman-preset new <file> --name <name>` and `kvman-preset check <file>` | writes and checks a preset |
|
|
129
|
+
| `kvman-preview <folder>…` | runs a preview kvman on a temporary home until you stop it |
|
|
130
|
+
| `kvman-docs list` and `kvman-docs get <extension> <topic>` | reads the same guides and pages as the `docs` connector from a running kvman |
|
|
131
|
+
|
|
132
|
+
## Docs: what `docs list` shows
|
|
133
|
+
|
|
134
|
+
`docs list` answers every page, grouped by extension. The built-in guides belong to `kvman`: `sdk`, `i18n`, and `presets`. Every installed extension that documents itself adds its own pages. An extension documents itself with two public queries:
|
|
135
|
+
|
|
136
|
+
- `<namespace>.docs.list` answers `[{ topic, title }]`;
|
|
137
|
+
- `<namespace>.docs.get` takes `{ topic }` and answers `{ topic, title, markdown }`.
|
|
138
|
+
|
|
139
|
+
A topic is a lowercase kebab-case word. kvbuilder asks each extension itself, so nothing registers with kvbuilder and no extension depends on it. An extension whose answer fails is listed with its error and hides no other. A project made with `kvman-new` already has the pair and a sample page in `extension-docs/usage.md`; `kvman-check` warns when the pair is half done.
|
|
140
|
+
|
|
141
|
+
## Previews
|
|
142
|
+
|
|
143
|
+
`preview start` runs a second kvman on its own temporary home, with the projects you name as `path:` extensions and kvwebui's Extensions page as its home page. Edits to a project's `src/` reload live, with no build. The preview takes the first free port from 3738 to 3837, and `preview stop` ends it and removes its home. A preview also stops when the kvman that started it stops.
|
|
144
|
+
|
|
145
|
+
### Calling a previewed project
|
|
146
|
+
|
|
147
|
+
`preview query-get { name, input? }` runs a public query of the preview, and `preview command-run { name, input? }` a public command. They call the preview's own HTTP API, in the preview's Home workspace, and never ask the person, because the preview's home is temporary. Both answer what the preview answered:
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
{ "ok": true, "output": { "text": "Hello from notes!" } }
|
|
151
|
+
{ "ok": false, "problem": { "code": "VALIDATION_FAILED", "message": "…", "params": { } } }
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
A failure of the project's own command is therefore an answer to read, not a failed call. The call itself fails only when there is nothing to call: `NOT_FOUND` when no preview is running, and `kvbuilder/PREVIEW_FAILED` when the preview doesn't answer or answers something that isn't kvman's. A query's name on `command-run`, or a command's on `query-get`, comes back as `{ ok: false }` with `NOT_FOUND`. Each call may take up to 2 minutes.
|
package/docs/guide.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
The person wants something about the app itself. kvman is an app built from extensions: a kernel runs them, and a preset chooses which run and how they are set up.
|
|
2
|
+
|
|
3
|
+
Which app? "The app" can mean kvman, the app you run in, which you change with the `kvman` connector, or the project in this workspace folder, which you edit with `fs`. If the request doesn't make clear which, ask once with `ask choice`.
|
|
4
|
+
|
|
5
|
+
What the person wants. Find out what they want to see or do, in their own words. If you don't know yet, make one `ask text` call: what should the app do, or look like, that it doesn't now? If they already said, ask only about what is missing: one question per `ask` call, with all the calls in one reply, the options written as outcomes ("a page of its own, or on the home page?"), and your recommended one first. Don't ask what reading `extensions-list`, `settings-list`, or `preset-get` would answer.
|
|
6
|
+
|
|
7
|
+
A person who isn't a developer. Many people who ask for this don't know kvman's internals, so you choose the mechanism, and they only choose the result.
|
|
8
|
+
- Never ask them about, or say to them, extensions, namespaces, presets, setting keys, or commands. Leave those words out of a call's `description` too, since it is the text of the card that asks them: write "Add a Notes page to your app", not "Install the notes extension".
|
|
9
|
+
- Look before you build. Read `extensions-list`, `settings-list`, and `preset-get`, then use the smallest thing that gives them what they asked for: a setting, an extension that is already installed, an extension you build, and a new preset only when they want a different app. If kvman can't do it, say so plainly.
|
|
10
|
+
- Show before you change. For an extension you built, start the preview, give them its address, and call `ask confirm` ("Does this look right?") before you add it to the app.
|
|
11
|
+
- Say how to undo each change in your final answer: `settings-reset` for a setting, `extensions-uninstall` for an extension.
|
|
12
|
+
- After a change to the extensions, finish it with one `restart` call (below), and say what is still unchecked.
|
|
13
|
+
|
|
14
|
+
An extension is a package with a `kvman` field; `src/index.ts` registers commands, queries, settings, and handlers with zod schemas. Every name starts with its namespace. Use the connectors `kvman`, `ext`, `preset`, `preview`, and `docs` for everything they cover, and `shell` only for the rest. Read and edit a project's files with `fs`. `ext` builds a project in the workspace; it is not for the app you run in.
|
|
15
|
+
|
|
16
|
+
Build an extension, in this order:
|
|
17
|
+
1. Read first. `docs list` shows every page: the built-in guides (`sdk`, `i18n`, `presets`) and the pages that installed extensions serve about themselves, such as the views and components of kvwebui. `docs get` with `{"topic":"sdk"}` reads a built-in guide, and with `{"extension":"@kvman/kvwebui","topic":"views"}` an extension's page. Read the guides before writing an extension, and an extension's pages before building on it.
|
|
18
|
+
2. Scaffold. `ext list` lists the projects here. `ext new` with `{"name":"notes","namespace":"notes","folder":"notes"}` scaffolds one (`"web": true` adds a Vue component).
|
|
19
|
+
3. Write it with `fs`, in `src/`, `locales/`, and `test/`. Never edit `dist/`.
|
|
20
|
+
4. Check after every change: `ext check` with `{"folder":"notes"}` type-checks it and reports what kvman would refuse, then `ext test` with the same payload runs its tests. Fix every finding before you go on.
|
|
21
|
+
5. Run it. `preview start` with `{"extensions":["notes"]}` runs a separate kvman with those projects and returns its URL; edits to `src/` reload live, and `preview stop` ends it.
|
|
22
|
+
6. Check that it works, in the preview: `preview query-get` with `{"name":"notes.greeting.get"}` runs one of its public queries (this one is the scaffold's own), and `preview command-run` with `{"name":"notes.item.add","input":{"text":"Milk"}}` a public command you wrote. Each answers `{ ok: true, output }`, or `{ ok: false, problem }` when the call failed. The preview's data is temporary, so call freely.
|
|
23
|
+
7. Add it to this app only when the person asks: `kvman extensions-install` with `{"source":"path:notes"}`, where the folder is relative to the workspace folder; kvman reads the project's name from its package.json. Then `restart` applies it.
|
|
24
|
+
|
|
25
|
+
Improve an extension that exists: read its code and its docs page first, and find what is wrong with `ext check`, `ext test`, and the preview before you change anything. Make the smallest change that fixes or improves it, keep its names and shapes unless the person asked to change them, then run `ext check`, `ext test`, and the preview calls again. `kvman extensions-list` names the commands, queries, and settings of every extension that runs.
|
|
26
|
+
|
|
27
|
+
Manage the app: `kvman` changes the app you are running in.
|
|
28
|
+
- See its state with `model-list`, `settings-list`, `extensions-list`, `preset-get`, `workspaces-list`, `jobs-list` (with `{"status":"failed","limit":20}` to find what failed), `jobs-get`, `processes-list`, and `health-get`. `query-get` with `{"name":"<a public query>","input":{}}` runs a query of any installed extension.
|
|
29
|
+
- Change it with `model-set`, `settings-set`, `settings-reset`, `extensions-install`, `extensions-uninstall`, and `restart`. The person is asked before each of these runs, so make one call for one change and say in its description what changes.
|
|
30
|
+
- A model or a setting changes at once. A change to the extensions or the preset is saved to the preset file and applies at the next start of kvman, which `restart` makes. Make one `restart` call, and say in its description what stops: the chats' running work, a preview, and any server you started. Your own turn ends when kvman stops, so say what you are doing in the same reply as the call. Tell the person that the terminal where kvman runs may ask them to trust a new extension, and that the page may need a reload. When they write again, read `health-get`: if it has `rolledBack`, kvman couldn't start with the change and put the app back as it was, so say so, say why from its message, and fix the cause.
|
|
31
|
+
- Your own workers, MCP servers, and connectors are settings: `kvcoder.delegate.workers`, `kvcoder.mcp.servers`, `kvcoder.connectors`, and `kvcoder.connectors.disabled`. Read one with `settings-list` before you change it, and send the whole new value.
|
|
32
|
+
- You never read, list, or change a secret, and there is no command that runs an arbitrary command of the app.
|
|
33
|
+
|
|
34
|
+
`preset new` and `preset check` write and check presets.
|
package/locales/ar.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kvbuilder.title": "kvman builder",
|
|
3
|
+
"kvbuilder.slash.build-kvman": "ابدأ بناء kvman في هذه المحادثة",
|
|
4
|
+
"kvbuilder.slash.build-kvman.message": "أريد تغيير هذا التطبيق.",
|
|
5
|
+
"kvbuilder.build.started": "بناء kvman مفعّل في هذه المحادثة",
|
|
6
|
+
"kvbuilder.errors.FOLDER_NOT_EMPTY": "هذا المجلد غير فارغ. اختر مجلدًا جديدًا أو فارغًا.",
|
|
7
|
+
"kvbuilder.errors.FILE_EXISTS": "هذا الملف موجود بالفعل.",
|
|
8
|
+
"kvbuilder.errors.NOT_A_PROJECT": "هذا المجلد ليس مشروع إضافة (لا يحتوي على package.json فيه حقل kvman).",
|
|
9
|
+
"kvbuilder.errors.NPM_FAILED": "فشل تنفيذ npm.",
|
|
10
|
+
"kvbuilder.errors.NO_FREE_PORT": "لا يوجد منفذ متاح للمعاينة بين 3738 و3837.",
|
|
11
|
+
"kvbuilder.errors.PREVIEW_FAILED": "لم يبدأ تشغيل kvman الخاص بالمعاينة."
|
|
12
|
+
}
|
package/locales/en.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kvbuilder.title": "kvman builder",
|
|
3
|
+
"kvbuilder.slash.build-kvman": "Start building kvman in this chat",
|
|
4
|
+
"kvbuilder.slash.build-kvman.message": "I want to change this app.",
|
|
5
|
+
"kvbuilder.build.started": "Building kvman is on for this chat",
|
|
6
|
+
"kvbuilder.errors.FOLDER_NOT_EMPTY": "That folder isn't empty; choose a new or empty folder.",
|
|
7
|
+
"kvbuilder.errors.FILE_EXISTS": "That file already exists.",
|
|
8
|
+
"kvbuilder.errors.NOT_A_PROJECT": "That folder isn't an extension project (no package.json with a kvman field).",
|
|
9
|
+
"kvbuilder.errors.NPM_FAILED": "npm failed.",
|
|
10
|
+
"kvbuilder.errors.NO_FREE_PORT": "No port from 3738 to 3837 is free for the preview.",
|
|
11
|
+
"kvbuilder.errors.PREVIEW_FAILED": "The preview kvman didn't start."
|
|
12
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kvman/kvbuilder",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "kvbuilder: the kvcoder extension for building kvman, developing extensions and presets and managing the running app.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/ibndeif/kvman.git",
|
|
9
|
+
"directory": "extensions/kvbuilder"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/ibndeif/kvman#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/ibndeif/kvman/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "dist/index.js",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"@kvman/source": "./src/index.ts",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"docs",
|
|
28
|
+
"locales"
|
|
29
|
+
],
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"@kvman/sdk": "^0.1.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@kvman/kvai": "0.1.1",
|
|
35
|
+
"@kvman/kvcoder": "0.1.2",
|
|
36
|
+
"@kvman/kvwebui": "0.1.1",
|
|
37
|
+
"@kvman/sdk": "0.1.0",
|
|
38
|
+
"playwright": "1.63.0"
|
|
39
|
+
},
|
|
40
|
+
"kvman": {
|
|
41
|
+
"namespace": "kvbuilder",
|
|
42
|
+
"source": "src/index.ts",
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@kvman/kvai": "^0.1.0",
|
|
45
|
+
"@kvman/kvcoder": "^0.1.2"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@kvman/testkit": "0.1.2"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsc -p tsconfig.build.json",
|
|
53
|
+
"typecheck": "tsc -p tsconfig.json"
|
|
54
|
+
}
|
|
55
|
+
}
|