@zenera/cli 1.1.25 → 1.1.26
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/README.md +13 -1
- package/dist/catalog.d.ts +20 -0
- package/dist/catalog.js +60 -0
- package/dist/commands/index.js +2 -0
- package/dist/commands/inspect.d.ts +5 -1
- package/dist/commands/inspect.js +54 -14
- package/dist/commands/memory.js +28 -3
- package/dist/commands/meta.js +24 -5
- package/dist/commands/models.js +2 -1
- package/dist/commands/sandbox.js +73 -3
- package/dist/commands/view.d.ts +8 -0
- package/dist/commands/view.js +114 -0
- package/dist/engine.d.ts +3 -1
- package/dist/engine.js +6 -0
- package/dist/eventlog.d.ts +5 -0
- package/dist/eventlog.js +25 -1
- package/dist/liveness.js +20 -2
- package/dist/main.js +0 -0
- package/dist/meta/editor.d.ts +1 -1
- package/dist/meta/editor.js +4 -21
- package/dist/meta/finetune/analyze-brief.d.ts +26 -0
- package/dist/meta/finetune/analyze-brief.js +131 -0
- package/dist/meta/finetune/apply-brief.d.ts +23 -0
- package/dist/meta/finetune/apply-brief.js +247 -0
- package/dist/meta/finetune/brief.d.ts +45 -0
- package/dist/meta/finetune/brief.js +158 -0
- package/dist/meta/finetune/command.d.ts +1 -1
- package/dist/meta/finetune/command.js +224 -46
- package/dist/meta/finetune/cost.d.ts +46 -0
- package/dist/meta/finetune/cost.js +105 -0
- package/dist/meta/finetune/dataset/types.d.ts +3 -1
- package/dist/meta/finetune/feedback.d.ts +14 -2
- package/dist/meta/finetune/feedback.js +22 -3
- package/dist/meta/finetune/guard.d.ts +29 -0
- package/dist/meta/finetune/guard.js +104 -0
- package/dist/meta/finetune/improvements.d.ts +50 -3
- package/dist/meta/finetune/improvements.js +290 -33
- package/dist/meta/finetune/live.d.ts +17 -7
- package/dist/meta/finetune/live.js +56 -10
- package/dist/meta/finetune/loop.js +59 -10
- package/dist/meta/finetune/recheck.d.ts +58 -0
- package/dist/meta/finetune/recheck.js +311 -0
- package/dist/meta/finetune/report.d.ts +9 -2
- package/dist/meta/finetune/report.js +551 -438
- package/dist/meta/finetune/sampler.js +8 -2
- package/dist/meta/finetune/train.d.ts +6 -2
- package/dist/meta/finetune/train.js +55 -21
- package/dist/meta/finetune/tui.d.ts +3 -3
- package/dist/meta/finetune/tui.js +37 -12
- package/dist/meta/finetune/tuning.d.ts +27 -1
- package/dist/meta/finetune/tuning.js +21 -2
- package/dist/meta/finetune/usage.d.ts +4 -0
- package/dist/meta/finetune/usage.js +35 -0
- package/dist/meta/finetune/view.d.ts +9 -0
- package/dist/meta/finetune/view.js +27 -0
- package/dist/meta/host.d.ts +2 -1
- package/dist/meta/host.js +2 -1
- package/dist/meta.d.ts +291 -0
- package/dist/meta.js +981 -0
- package/dist/resources.d.ts +215 -0
- package/dist/resources.js +856 -0
- package/dist/scaffold.d.ts +3 -4
- package/dist/scaffold.js +21 -9
- package/dist/trace.d.ts +5 -0
- package/dist/trace.js +54 -5
- package/dist/tui/app.js +19 -5
- package/dist/tui/links.d.ts +27 -0
- package/dist/tui/links.js +142 -0
- package/dist/tui/models.d.ts +3 -1
- package/dist/tui/models.js +65 -7
- package/dist/tui/sandbox.d.ts +10 -0
- package/dist/tui/sandbox.js +131 -0
- package/package.json +3 -2
- package/templates/editor/.github/copilot-instructions.md +29 -43
- package/templates/editor/.github/prompts/analyze.prompt.md +3 -1
- package/templates/editor/.github/prompts/finetune-apply.prompt.md +221 -42
- package/templates/editor/.github/prompts/project-review.prompt.md +1 -1
- package/templates/editor/.github/skills/zen-analyze-run/SKILL.md +162 -57
- package/templates/editor/.github/skills/zen-cli/references/finetune.md +74 -14
- package/templates/editor/.github/skills/zen-cli/references/inspect.md +16 -2
- package/templates/editor/.github/skills/zen-cli/references/projects.md +4 -6
- package/templates/editor/.github/skills/zen-cli/references/sandbox.md +25 -13
- package/templates/editor/.github/skills/zen-finetune/SKILL.md +74 -33
- package/templates/editor/.github/skills/zen-inspect/SKILL.md +14 -1
- package/templates/editor/.github/skills/zen-inspect-ask/SKILL.md +4 -3
- package/templates/editor/.github/skills/zen-instructions/SKILL.md +4 -12
- package/templates/editor/.github/skills/zen-memory/SKILL.md +64 -39
- package/templates/editor/.github/skills/zen-review/SKILL.md +6 -31
- package/templates/editor/.github/skills/zen-review/scripts/check-paths.sh +1 -2
- package/templates/editor/.github/skills/zen-review/scripts/review.sh +3 -12
- package/templates/editor/.github/skills/zen-sandbox/SKILL.md +2 -1
- package/templates/editor/.github/skills/zen-spec-sync/SKILL.md +2 -4
- package/templates/project/agents/fork-instructions.md +78 -28
- package/templates/project/agents/memory-instructions.md +222 -493
- package/templates/project/agents/tools-instructions.md +7 -0
- package/templates/editor/.github/skills/zen-memory/scripts/check-instructions.sh +0 -116
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { existsSync, statSync } from 'node:fs';
|
|
2
|
+
import { basename, dirname, relative, resolve, sep } from 'node:path';
|
|
3
|
+
import { parse } from "../args.js";
|
|
4
|
+
import { dim, json, note, usageError, write } from "../term.js";
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// zen view - a folder in the browser, redrawn as its files change
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
const USAGE = 'zen view [path] [--port <n>] [--throttle <ms>] [--open] [--hidden]';
|
|
9
|
+
const OPTIONS = {
|
|
10
|
+
port: { type: 'string' },
|
|
11
|
+
throttle: { type: 'string' },
|
|
12
|
+
open: { type: 'boolean' },
|
|
13
|
+
hidden: { type: 'boolean' },
|
|
14
|
+
};
|
|
15
|
+
/** Resolves when one of the signals arrives, and stops listening for them. */
|
|
16
|
+
function until(signals) {
|
|
17
|
+
return new Promise((settle) => {
|
|
18
|
+
const done = () => {
|
|
19
|
+
for (const s of signals) {
|
|
20
|
+
process.off(s, done);
|
|
21
|
+
}
|
|
22
|
+
settle();
|
|
23
|
+
};
|
|
24
|
+
for (const s of signals) {
|
|
25
|
+
process.once(s, done);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/** A file inside the cwd is served from the cwd, so its links up the tree still resolve. */
|
|
30
|
+
export function placeOf(cwd, target) {
|
|
31
|
+
if (statSync(target).isDirectory()) {
|
|
32
|
+
return { root: target, start: '/' };
|
|
33
|
+
}
|
|
34
|
+
const rel = relative(cwd, target);
|
|
35
|
+
const parts = rel.split(sep);
|
|
36
|
+
// A dot-folder on the way would be refused as hidden, so serve from the file's own folder.
|
|
37
|
+
const within = rel !== '' &&
|
|
38
|
+
rel !== '..' &&
|
|
39
|
+
!rel.startsWith(`..${sep}`) &&
|
|
40
|
+
!parts.slice(0, -1).some((p) => p.startsWith('.'));
|
|
41
|
+
const root = within ? cwd : dirname(target);
|
|
42
|
+
const path = within ? parts : [basename(target)];
|
|
43
|
+
return { root, start: `/${path.join('/')}` };
|
|
44
|
+
}
|
|
45
|
+
export const view = {
|
|
46
|
+
summary: 'Show a folder in the browser: markdown, diagrams and code, redrawn as files change.',
|
|
47
|
+
usage: USAGE,
|
|
48
|
+
details: [
|
|
49
|
+
'Serves [path] (default: here) on 127.0.0.1 until Ctrl-C and prints its address.',
|
|
50
|
+
'Markdown is drawn with its tables and mermaid diagrams; json, jsonl, python and',
|
|
51
|
+
'other code is highlighted; a directory is a listing. An open page follows its',
|
|
52
|
+
'file: when the file changes, only what changed is redrawn. Images, html and pdf',
|
|
53
|
+
'are handed to the browser as they are. Dotfiles are not served without --hidden,',
|
|
54
|
+
'and .env and .git never are.',
|
|
55
|
+
'',
|
|
56
|
+
'Options:',
|
|
57
|
+
' --port <n> This port. Default: one derived from the folder, so a page left',
|
|
58
|
+
' open reconnects after a restart; any free one if it is taken.',
|
|
59
|
+
' --throttle <ms> Least time between two redraws of one page. Default 1000.',
|
|
60
|
+
' --open Open the page in the browser.',
|
|
61
|
+
' --hidden Serve dotfiles too.',
|
|
62
|
+
'',
|
|
63
|
+
'Examples:',
|
|
64
|
+
' zen view',
|
|
65
|
+
' zen view finetune/STATUS.md --open',
|
|
66
|
+
],
|
|
67
|
+
banner: { head: 'Zenera', accent: 'View', subtitle: 'Live File Viewer', hue: 'pink' },
|
|
68
|
+
run: async (ctx) => {
|
|
69
|
+
const { values, positionals } = parse(ctx.args, OPTIONS, USAGE);
|
|
70
|
+
if (positionals.length > 1) {
|
|
71
|
+
throw usageError('zen view takes one path', USAGE);
|
|
72
|
+
}
|
|
73
|
+
const target = resolve(ctx.cwd, positionals[0] ?? '.');
|
|
74
|
+
if (!existsSync(target)) {
|
|
75
|
+
throw usageError(`no such file or directory: ${target}`);
|
|
76
|
+
}
|
|
77
|
+
let port;
|
|
78
|
+
if (values.port !== undefined) {
|
|
79
|
+
port = Number(values.port);
|
|
80
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
81
|
+
throw usageError('--port takes a port number', `got "${values.port}"`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
let throttleMs;
|
|
85
|
+
if (values.throttle !== undefined) {
|
|
86
|
+
throttleMs = Number(values.throttle);
|
|
87
|
+
if (!Number.isInteger(throttleMs) || throttleMs < 0) {
|
|
88
|
+
throw usageError('--throttle takes milliseconds', `got "${values.throttle}"`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const { root, start } = placeOf(ctx.cwd, target);
|
|
92
|
+
const { openBrowser, serveView } = await import('@zenera/view');
|
|
93
|
+
const server = await serveView({
|
|
94
|
+
root,
|
|
95
|
+
start,
|
|
96
|
+
hidden: values.hidden ?? false,
|
|
97
|
+
...(port !== undefined ? { port } : {}),
|
|
98
|
+
...(throttleMs !== undefined ? { throttleMs } : {}),
|
|
99
|
+
});
|
|
100
|
+
if (ctx.json) {
|
|
101
|
+
json({ url: server.url, root: server.root, port: server.port });
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
write(server.url);
|
|
105
|
+
note(dim(`serving ${server.root} · Ctrl-C stops it`));
|
|
106
|
+
}
|
|
107
|
+
if (values.open) {
|
|
108
|
+
openBrowser(server.url);
|
|
109
|
+
}
|
|
110
|
+
await until(['SIGINT', 'SIGTERM']);
|
|
111
|
+
await server.close();
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
//# sourceMappingURL=view.js.map
|
package/dist/engine.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AgentRunner, type AgentEvent, type AgentProject, type AgentState, type Input, type RunResult } from '@zenera/neo';
|
|
1
|
+
import { AgentRunner, Workspace, type AgentEvent, type AgentProject, type AgentState, type Input, type RunResult } from '@zenera/neo';
|
|
2
2
|
import { type Project } from './projects.ts';
|
|
3
3
|
import { type SandboxSetup } from './sandbox.ts';
|
|
4
4
|
import { type Held, type RunPaths, type SessionPaths } from './session.ts';
|
|
@@ -37,6 +37,8 @@ export interface Engine {
|
|
|
37
37
|
workspace: string;
|
|
38
38
|
/** where the workspace appears inside the container */
|
|
39
39
|
workspaceMount: string;
|
|
40
|
+
/** every tree the file tools reach, and the names they reach them by */
|
|
41
|
+
files: Workspace;
|
|
40
42
|
/** whether the agent was denied every writing tool */
|
|
41
43
|
readOnly: boolean;
|
|
42
44
|
session: SessionPaths;
|
package/dist/engine.js
CHANGED
|
@@ -2,6 +2,7 @@ import { AgentRunner, FilePayloadStore, SANDBOX_MOUNT, SKILLS_MOUNT, Workspace,
|
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import { writeFile } from 'node:fs/promises';
|
|
4
4
|
import { resolve } from 'node:path';
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
5
6
|
import { auditModels, describeIssue } from "./audit.js";
|
|
6
7
|
import { loadProjectEnv } from "./env.js";
|
|
7
8
|
import { readJson, writeJson } from "./home.js";
|
|
@@ -123,6 +124,7 @@ export async function open(opts) {
|
|
|
123
124
|
name: opts.project.name,
|
|
124
125
|
workspace,
|
|
125
126
|
workspaceMount: sandbox.spec.workdir ?? SANDBOX_MOUNT,
|
|
127
|
+
files,
|
|
126
128
|
readOnly: Boolean(opts.readOnly),
|
|
127
129
|
session: opts.session,
|
|
128
130
|
memory,
|
|
@@ -233,6 +235,10 @@ async function record(engine, input, result, startedAt, durationMs) {
|
|
|
233
235
|
title: `${engine.name} · ${run.id}`,
|
|
234
236
|
architecture: await engine.runner.describe(),
|
|
235
237
|
memory: engine.runner.services.memory?.store,
|
|
238
|
+
mounts: [
|
|
239
|
+
{ at: engine.workspaceMount, href: pathToFileURL(engine.files.root).href },
|
|
240
|
+
...engine.files.mounts.map((m) => ({ at: m.at, href: pathToFileURL(m.host).href })),
|
|
241
|
+
],
|
|
236
242
|
});
|
|
237
243
|
await writeFile(run.report, renderReportHtml(built), 'utf8');
|
|
238
244
|
report = run.report;
|
package/dist/eventlog.d.ts
CHANGED
|
@@ -23,10 +23,15 @@ export type EventBody = {
|
|
|
23
23
|
} | {
|
|
24
24
|
type: 'say';
|
|
25
25
|
text: string;
|
|
26
|
+
} | {
|
|
27
|
+
type: 'think';
|
|
28
|
+
text: string;
|
|
26
29
|
};
|
|
27
30
|
export type EventLine = EventBody & {
|
|
28
31
|
t: string;
|
|
29
32
|
};
|
|
33
|
+
/** A thought summary's last `**heading**`, else its first sentence. */
|
|
34
|
+
export declare function heading(thinking: string): string;
|
|
30
35
|
/** A line that cannot be written is dropped: watching a run must not break it. */
|
|
31
36
|
export declare function eventLog(path: string): (body: EventBody) => void;
|
|
32
37
|
/** For `zen run`: the runtime's own events, folded to the shared lines. */
|
package/dist/eventlog.js
CHANGED
|
@@ -6,6 +6,15 @@ const clip = (text) => {
|
|
|
6
6
|
const one = text.replace(/\s+/g, ' ').trim();
|
|
7
7
|
return one.length > MAX_TEXT ? `${one.slice(0, MAX_TEXT - 1)}…` : one;
|
|
8
8
|
};
|
|
9
|
+
/** A thought summary's last `**heading**`, else its first sentence. */
|
|
10
|
+
export function heading(thinking) {
|
|
11
|
+
const bold = [...thinking.matchAll(/\*\*([^*\n]+)\*\*/g)].at(-1)?.[1];
|
|
12
|
+
if (bold) {
|
|
13
|
+
return clip(bold);
|
|
14
|
+
}
|
|
15
|
+
const one = clip(thinking);
|
|
16
|
+
return one.match(/^.+?[.!?](?=\s|$)/)?.[0] ?? one;
|
|
17
|
+
}
|
|
9
18
|
/** A line that cannot be written is dropped: watching a run must not break it. */
|
|
10
19
|
export function eventLog(path) {
|
|
11
20
|
mkdirSync(dirname(path), { recursive: true });
|
|
@@ -24,18 +33,23 @@ export function agentEventLog(path) {
|
|
|
24
33
|
// Keyed by branch, since a fork has several model calls open at once.
|
|
25
34
|
const began = new Map();
|
|
26
35
|
const said = new Map();
|
|
36
|
+
const thought = new Map();
|
|
27
37
|
return (event) => {
|
|
28
38
|
const key = event.branch?.name ?? '';
|
|
29
39
|
if (!isCheckpoint(event)) {
|
|
30
40
|
if (event.type === 'text_delta') {
|
|
31
41
|
said.set(key, (said.get(key) ?? '') + event.delta);
|
|
32
42
|
}
|
|
43
|
+
else if (event.type === 'thinking_delta') {
|
|
44
|
+
thought.set(key, (thought.get(key) ?? '') + event.delta);
|
|
45
|
+
}
|
|
33
46
|
return;
|
|
34
47
|
}
|
|
35
48
|
switch (event.type) {
|
|
36
49
|
case 'before_llm_call':
|
|
37
50
|
began.set(key, Date.now());
|
|
38
51
|
said.delete(key);
|
|
52
|
+
thought.delete(key);
|
|
39
53
|
break;
|
|
40
54
|
case 'after_llm_call': {
|
|
41
55
|
const u = event.node.usage;
|
|
@@ -49,6 +63,11 @@ export function agentEventLog(path) {
|
|
|
49
63
|
reasoning: u.reasoningTokens ?? 0,
|
|
50
64
|
...(at !== undefined ? { ms: Date.now() - at } : {}),
|
|
51
65
|
});
|
|
66
|
+
const thinking = thought.get(key)?.trim();
|
|
67
|
+
thought.delete(key);
|
|
68
|
+
if (thinking) {
|
|
69
|
+
write({ type: 'think', text: heading(thinking) });
|
|
70
|
+
}
|
|
52
71
|
const text = said.get(key)?.trim();
|
|
53
72
|
said.delete(key);
|
|
54
73
|
if (text) {
|
|
@@ -103,7 +122,12 @@ export function copilotEventLog(path) {
|
|
|
103
122
|
}
|
|
104
123
|
case 'tool.execution_start': {
|
|
105
124
|
const args = (data.arguments ?? {});
|
|
106
|
-
const subject = args.command ??
|
|
125
|
+
const subject = args.command ??
|
|
126
|
+
args.description ??
|
|
127
|
+
args.path ??
|
|
128
|
+
args.skill ??
|
|
129
|
+
args.pattern ??
|
|
130
|
+
'';
|
|
107
131
|
const name = String(data.toolName ?? 'tool');
|
|
108
132
|
began.set(id, { at: Date.now(), name });
|
|
109
133
|
write({
|
package/dist/liveness.js
CHANGED
|
@@ -407,7 +407,7 @@ export function probeChat(provider, id) {
|
|
|
407
407
|
* accepted and the id was the thing refused, so the fix is another model rather
|
|
408
408
|
* than another key. `dead` is left to mean the credential itself was rejected.
|
|
409
409
|
*/
|
|
410
|
-
function classifyModel(err, target) {
|
|
410
|
+
function classifyModel(err, target, ms) {
|
|
411
411
|
const at = new Date().toISOString();
|
|
412
412
|
const message = err instanceof Error ? err.message : String(err);
|
|
413
413
|
const e = err;
|
|
@@ -419,8 +419,25 @@ function classifyModel(err, target) {
|
|
|
419
419
|
return { state: 'blocked', at, detail: firstLine(message), fix: instead(target) };
|
|
420
420
|
}
|
|
421
421
|
const check = classify(err);
|
|
422
|
+
// classify() calls a 429 a live key; for a model it means nothing answered.
|
|
423
|
+
if (check.state === 'live') {
|
|
424
|
+
const said = err.detail;
|
|
425
|
+
return {
|
|
426
|
+
state: 'unknown',
|
|
427
|
+
at,
|
|
428
|
+
detail: `rate limited after ${(ms / 1000).toFixed(1)}s of retries: ${typeof said === 'string' ? said : firstLine(message)}`,
|
|
429
|
+
fix: limited(target),
|
|
430
|
+
};
|
|
431
|
+
}
|
|
422
432
|
return check.state === 'blocked' && !check.fix ? { ...check, fix: instead(target) } : check;
|
|
423
433
|
}
|
|
434
|
+
function limited(target) {
|
|
435
|
+
const blame = "the key and the model id are fine, the provider's rate limit is not";
|
|
436
|
+
if (target.ref.endsWith(':free')) {
|
|
437
|
+
return `${blame} — free models share a small upstream quota; retry later, or test the paid id: zen models test ${target.ref.slice(0, -':free'.length)}`;
|
|
438
|
+
}
|
|
439
|
+
return `${blame} — retry later`;
|
|
440
|
+
}
|
|
424
441
|
/** The command that finds something this account can actually use. */
|
|
425
442
|
function instead(target) {
|
|
426
443
|
const provider = target.ref.includes(':') ? target.ref.split(':')[0] : undefined;
|
|
@@ -469,7 +486,8 @@ export async function probeModel(target) {
|
|
|
469
486
|
};
|
|
470
487
|
}
|
|
471
488
|
catch (err) {
|
|
472
|
-
|
|
489
|
+
const ms = Date.now() - started;
|
|
490
|
+
return { ...common, check: classifyModel(err, target, ms), ms };
|
|
473
491
|
}
|
|
474
492
|
}
|
|
475
493
|
/**
|
package/dist/main.js
CHANGED
|
File without changes
|
package/dist/meta/editor.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* ours: they say how the editor is to treat a directory the agents write into,
|
|
4
4
|
* and they describe the file formats of the version of `zen` in hand, so the
|
|
5
5
|
* current answer is the only one worth having and a stale one is worse than
|
|
6
|
-
* none. Returns the relative paths
|
|
6
|
+
* none. Returns the relative paths that actually changed.
|
|
7
7
|
*/
|
|
8
8
|
export declare function editorFiles(dir: string): string[];
|
|
9
9
|
//# sourceMappingURL=editor.d.ts.map
|
package/dist/meta/editor.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { copyTree, MEMORY_RULES, TEMPLATES } from "./host.js";
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { copyTree, TEMPLATES } from "./host.js";
|
|
4
3
|
// ---------------------------------------------------------------------------
|
|
5
4
|
// Telling the editor which instructions are not for it
|
|
6
5
|
//
|
|
@@ -31,30 +30,14 @@ import { copyTree, MEMORY_RULES, TEMPLATES } from "./host.js";
|
|
|
31
30
|
// `.github/` tree is: the standing brief, plus the prompt files and skills the
|
|
32
31
|
// editor picks up from the same place.
|
|
33
32
|
// ---------------------------------------------------------------------------
|
|
34
|
-
/** The same bytes, kept in the `zen-memory` skill to restore or diff against. */
|
|
35
|
-
const MEMORY_REFERENCE = join('.github', 'skills', 'zen-memory', 'references', 'memory-instructions.md');
|
|
36
33
|
/**
|
|
37
34
|
* Writes the editor's files under `dir`, replacing what is there. They are
|
|
38
35
|
* ours: they say how the editor is to treat a directory the agents write into,
|
|
39
36
|
* and they describe the file formats of the version of `zen` in hand, so the
|
|
40
37
|
* current answer is the only one worth having and a stale one is worse than
|
|
41
|
-
* none. Returns the relative paths
|
|
38
|
+
* none. Returns the relative paths that actually changed.
|
|
42
39
|
*/
|
|
43
40
|
export function editorFiles(dir) {
|
|
44
|
-
|
|
45
|
-
// The one file that belongs to both trees. It is a project file first —
|
|
46
|
-
// house rules reaching every agent that can see the graph — and it is
|
|
47
|
-
// written once and edited from then on, like every other project file. But
|
|
48
|
-
// a project whose copy drifted, or that deleted it and turned memory back
|
|
49
|
-
// on later, needs somewhere to get the current text from, and the editor
|
|
50
|
-
// tree is replaced on every `init` and `open`. Copying the same bytes into
|
|
51
|
-
// the skill's references is what makes `diff` between the two mean
|
|
52
|
-
// something, and what stops a second copy of these rules existing in the
|
|
53
|
-
// repository to fall out of step with the first.
|
|
54
|
-
const reference = join(dir, MEMORY_REFERENCE);
|
|
55
|
-
mkdirSync(dirname(reference), { recursive: true });
|
|
56
|
-
copyFileSync(join(TEMPLATES, 'project', MEMORY_RULES), reference);
|
|
57
|
-
written.push(MEMORY_REFERENCE);
|
|
58
|
-
return written;
|
|
41
|
+
return copyTree(join(TEMPLATES, 'editor'), dir, '', {});
|
|
59
42
|
}
|
|
60
43
|
//# sourceMappingURL=editor.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Brief, type Step } from './brief.ts';
|
|
2
|
+
import type { Case } from './dataset/types.ts';
|
|
3
|
+
import { type Phase } from './feedback.ts';
|
|
4
|
+
import { type Attempt } from './report.ts';
|
|
5
|
+
import type { Run } from './train.ts';
|
|
6
|
+
import type { Tuning } from './tuning.ts';
|
|
7
|
+
export interface AnalyzeBrief extends Brief {
|
|
8
|
+
run: Run;
|
|
9
|
+
case: Case;
|
|
10
|
+
phase: Phase;
|
|
11
|
+
attempt: number;
|
|
12
|
+
/** earlier tries of this case revision, oldest first */
|
|
13
|
+
earlier: Attempt[];
|
|
14
|
+
/** the patch a recheck checks */
|
|
15
|
+
recheck?: string;
|
|
16
|
+
/** skills an apply made for this case that the run never loaded */
|
|
17
|
+
missed?: string[];
|
|
18
|
+
/** the try's folder: brief.md, feedback.json and ask/ live here */
|
|
19
|
+
dir: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const ANALYZE_BRIEF: Step<AnalyzeBrief>[];
|
|
22
|
+
/** The analysis brief as markdown. */
|
|
23
|
+
export declare function renderAnalyzeBrief(b: AnalyzeBrief, log: string, steps?: Step<AnalyzeBrief>[]): Promise<string>;
|
|
24
|
+
/** Writes `brief.md` into the try's folder; the path is what `--prompt-file` takes. */
|
|
25
|
+
export declare function writeAnalyzeBrief(t: Tuning, dir: string, at: Omit<AnalyzeBrief, 't' | 'systemDir' | 'project' | 'dir'>): Promise<string>;
|
|
26
|
+
//# sourceMappingURL=analyze-brief.d.ts.map
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { SHARED_RULES, validateProject } from "../host.js";
|
|
4
|
+
import { cut, files, oneLine, PHASE_WORD, renderBrief, skillIndex, STEP_MAX, word, } from "./brief.js";
|
|
5
|
+
import { feedbackId } from "./feedback.js";
|
|
6
|
+
import { fence, rubricScore, since } from "./report.js";
|
|
7
|
+
export const ANALYZE_BRIEF = [
|
|
8
|
+
{ zen: (b) => ['meta', 'dataset', 'show', `${b.case.id}@${b.case.rev}`, '--json'] },
|
|
9
|
+
{ sh: () => "grep -nE 'memory:|handoffs:|fork:' agents.yaml" },
|
|
10
|
+
{ zen: (b) => ['inspect', 'graph', '--dir', b.run.dir, '--no-style'] },
|
|
11
|
+
{ cat: (b) => [join(b.run.dir, 'meta.json'), join(b.run.dir, 'input.md')] },
|
|
12
|
+
{ cat: (b) => [join(b.run.dir, 'output.md')], max: 8000 },
|
|
13
|
+
{ title: 'Earlier tries of this case', fn: earlierTries },
|
|
14
|
+
// It cuts the diff itself, inside its fence.
|
|
15
|
+
{
|
|
16
|
+
title: 'What changed in agents.yaml and agents/ since the last try',
|
|
17
|
+
fn: systemDiff,
|
|
18
|
+
max: Infinity,
|
|
19
|
+
},
|
|
20
|
+
{ cat: (b) => (b.recheck ? [b.recheck] : undefined) },
|
|
21
|
+
{ title: 'Skills an apply made for this case, never loaded by this run', fn: missedSkills },
|
|
22
|
+
{ cat: () => ['agents.yaml'] },
|
|
23
|
+
{
|
|
24
|
+
cat: (b) => files(b, 'agents/*instructions.md').filter((f) => !SHARED_RULES.includes(f)),
|
|
25
|
+
},
|
|
26
|
+
{ title: 'Skills: which agent can load which, and the index', fn: skillIndex },
|
|
27
|
+
// Last: the largest, and the agent can read a left-out prompt itself.
|
|
28
|
+
{ cat: (b) => files(b, 'agents/prompts/*.md'), max: 40_000 },
|
|
29
|
+
];
|
|
30
|
+
/** The analysis brief as markdown. */
|
|
31
|
+
export async function renderAnalyzeBrief(b, log, steps = ANALYZE_BRIEF) {
|
|
32
|
+
return renderBrief(b, log, steps, [
|
|
33
|
+
`The run is \`${b.run.dir}\`, on system v${b.run.system}: the project files below are as that version had them.\n`,
|
|
34
|
+
[
|
|
35
|
+
'Where to write - both folders exist, so no `mkdir` and no `test -f`:',
|
|
36
|
+
'',
|
|
37
|
+
`- each question for \`zen inspect ask\`: \`${join(b.dir, 'ask')}/<node>-<topic>.md\``,
|
|
38
|
+
`- the feedback file: \`${join(b.dir, 'feedback.json')}\`, not written yet - write it in one call`,
|
|
39
|
+
'',
|
|
40
|
+
].join('\n'),
|
|
41
|
+
]);
|
|
42
|
+
}
|
|
43
|
+
/** Writes `brief.md` into the try's folder; the path is what `--prompt-file` takes. */
|
|
44
|
+
export async function writeAnalyzeBrief(t, dir, at) {
|
|
45
|
+
mkdirSync(join(dir, 'ask'), { recursive: true });
|
|
46
|
+
const snapshot = t.system.snapshotDir(at.run.system);
|
|
47
|
+
const systemDir = existsSync(snapshot) ? snapshot : t.root;
|
|
48
|
+
let report;
|
|
49
|
+
const b = {
|
|
50
|
+
...at,
|
|
51
|
+
t,
|
|
52
|
+
dir,
|
|
53
|
+
systemDir,
|
|
54
|
+
project: () => (report ??= validateProject({ dir: systemDir })
|
|
55
|
+
.catch(() => validateProject({ dir: t.root }))
|
|
56
|
+
.catch(() => undefined)),
|
|
57
|
+
};
|
|
58
|
+
const file = join(dir, 'brief.md');
|
|
59
|
+
writeFileSync(file, await renderAnalyzeBrief(b, join(dir, 'brief.log')));
|
|
60
|
+
return file;
|
|
61
|
+
}
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// The computed sections
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
function earlierTries(b) {
|
|
66
|
+
const decided = b.t.improvements.decisions();
|
|
67
|
+
const round = b.t.round(b.case);
|
|
68
|
+
return b.earlier
|
|
69
|
+
.filter((a) => a.feedback)
|
|
70
|
+
.map((a) => {
|
|
71
|
+
const f = a.feedback;
|
|
72
|
+
const id = feedbackId(b.case.id, a.phase, a.attempt, round);
|
|
73
|
+
const cost = a.run
|
|
74
|
+
? `, v${a.run.system}, ${a.run.turns ?? '?'} LLM calls, ${since(a.run.durationMs ?? 0)}`
|
|
75
|
+
: '';
|
|
76
|
+
const rows = f.improvements.map((i) => {
|
|
77
|
+
const d = decided[id]?.[i.id];
|
|
78
|
+
const decision = d?.decision
|
|
79
|
+
? `${d.decision}${d.note ? `: ${d.note}` : ''}`
|
|
80
|
+
: 'not applied yet';
|
|
81
|
+
return `| ${i.id} | ${oneLine(i.change)} | ${oneLine(i.file ?? '-')} | ${oneLine(decision)} |`;
|
|
82
|
+
});
|
|
83
|
+
return [
|
|
84
|
+
`#### ${PHASE_WORD[a.phase]}, try ${a.attempt} - ${f.verdict}, rubric ${rubricScore(f)}${f.done ? ', done' : ''}${cost}`,
|
|
85
|
+
'',
|
|
86
|
+
...f.summary
|
|
87
|
+
.trim()
|
|
88
|
+
.split('\n')
|
|
89
|
+
.map((l) => (l.trim() ? `> ${l}` : '>')),
|
|
90
|
+
'',
|
|
91
|
+
...(rows.length > 0
|
|
92
|
+
? ['| # | Change | File | Decision |', '| --- | --- | --- | --- |', ...rows, '']
|
|
93
|
+
: []),
|
|
94
|
+
].join('\n');
|
|
95
|
+
})
|
|
96
|
+
.join('\n');
|
|
97
|
+
}
|
|
98
|
+
function systemDiff(b) {
|
|
99
|
+
const last = [...b.earlier].reverse().find((a) => a.run);
|
|
100
|
+
if (!last?.run || last.run.system === b.run.system) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
const diff = b.t.system.diff(last.run.system, b.run.system).trim();
|
|
104
|
+
if (!diff) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
const pad = (v) => `v${String(v).padStart(2, '0')}`;
|
|
108
|
+
const { body, note } = cut(diff, STEP_MAX, `the whole diff: git -C ${word(b.t.system.dir)} diff --no-index ${pad(last.run.system)} ${pad(b.run.system)}`);
|
|
109
|
+
const tick = fence(body);
|
|
110
|
+
return [
|
|
111
|
+
`From v${last.run.system} (${PHASE_WORD[last.phase]}, try ${last.attempt}) to v${b.run.system} (this run):`,
|
|
112
|
+
'',
|
|
113
|
+
`${tick}diff`,
|
|
114
|
+
body,
|
|
115
|
+
tick,
|
|
116
|
+
...(note ? [note] : []),
|
|
117
|
+
].join('\n');
|
|
118
|
+
}
|
|
119
|
+
async function missedSkills(b) {
|
|
120
|
+
if (!b.missed?.length) {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
const entries = (await b.project())?.skills.entries ?? [];
|
|
124
|
+
return b.missed
|
|
125
|
+
.map((name) => {
|
|
126
|
+
const s = entries.find((e) => e.name === name);
|
|
127
|
+
return `- \`${name}\`: ${s ? s.description : '(no such skill in this version)'}`;
|
|
128
|
+
})
|
|
129
|
+
.join('\n');
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=analyze-brief.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Brief, type Step } from './brief.ts';
|
|
2
|
+
import { type Feedback } from './feedback.ts';
|
|
3
|
+
import type { Deferred } from './improvements.ts';
|
|
4
|
+
import { type Tuning } from './tuning.ts';
|
|
5
|
+
/** One entry of inputs.json. */
|
|
6
|
+
export type Request = Feedback & {
|
|
7
|
+
file: string;
|
|
8
|
+
missedSkills?: string[];
|
|
9
|
+
};
|
|
10
|
+
export interface ApplyBrief extends Brief {
|
|
11
|
+
dir: string;
|
|
12
|
+
attempt: number;
|
|
13
|
+
inputs: Request[];
|
|
14
|
+
deferred: Deferred[];
|
|
15
|
+
}
|
|
16
|
+
/** The editor skills this project's capabilities call for, each with why. */
|
|
17
|
+
export declare function editorSkills(root: string, inputs: {
|
|
18
|
+
phase: string;
|
|
19
|
+
}[]): [string, string][];
|
|
20
|
+
export declare const APPLY_BRIEF: Step<ApplyBrief>[];
|
|
21
|
+
/** Writes `brief.md` into the apply's folder, for this attempt; returns its path. */
|
|
22
|
+
export declare function writeApplyBrief(t: Tuning, dir: string, attempt: number): Promise<string>;
|
|
23
|
+
//# sourceMappingURL=apply-brief.d.ts.map
|