zelari-code 1.29.0 → 1.30.1

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.
Files changed (54) hide show
  1. package/dist/cli/headless.js +14 -0
  2. package/dist/cli/headless.js.map +1 -1
  3. package/dist/cli/hooks/useSlashDispatch.js +11 -0
  4. package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
  5. package/dist/cli/kraken/executor.js +63 -1
  6. package/dist/cli/kraken/executor.js.map +1 -1
  7. package/dist/cli/kraken/planner.js +87 -2
  8. package/dist/cli/kraken/planner.js.map +1 -1
  9. package/dist/cli/kraken/planner.test.js +43 -0
  10. package/dist/cli/kraken/planner.test.js.map +1 -0
  11. package/dist/cli/kraken/runtime/compile.js +73 -0
  12. package/dist/cli/kraken/runtime/compile.js.map +1 -0
  13. package/dist/cli/kraken/runtime/runScriptPlan.js +195 -0
  14. package/dist/cli/kraken/runtime/runScriptPlan.js.map +1 -0
  15. package/dist/cli/kraken/scriptPlanner.js +286 -0
  16. package/dist/cli/kraken/scriptPlanner.js.map +1 -0
  17. package/dist/cli/kraken/scriptPlanner.test.js +152 -0
  18. package/dist/cli/kraken/scriptPlanner.test.js.map +1 -0
  19. package/dist/cli/kraken/skillSuggest.js +97 -0
  20. package/dist/cli/kraken/skillSuggest.js.map +1 -0
  21. package/dist/cli/kraken/skillSuggest.test.js +157 -0
  22. package/dist/cli/kraken/skillSuggest.test.js.map +1 -0
  23. package/dist/cli/kraken/weaknessMeter.js +183 -0
  24. package/dist/cli/kraken/weaknessMeter.js.map +1 -0
  25. package/dist/cli/kraken/weaknessMeter.test.js +212 -0
  26. package/dist/cli/kraken/weaknessMeter.test.js.map +1 -0
  27. package/dist/cli/kraken/workbench.js +296 -0
  28. package/dist/cli/kraken/workbench.js.map +1 -0
  29. package/dist/cli/kraken/workbench.test.js +253 -0
  30. package/dist/cli/kraken/workbench.test.js.map +1 -0
  31. package/dist/cli/kraken/workbenchView.js +155 -0
  32. package/dist/cli/kraken/workbenchView.js.map +1 -0
  33. package/dist/cli/kraken/workbenchView.test.js +130 -0
  34. package/dist/cli/kraken/workbenchView.test.js.map +1 -0
  35. package/dist/cli/main.bundled.js +2214 -299
  36. package/dist/cli/main.bundled.js.map +4 -4
  37. package/dist/cli/runHeadless.js +58 -7
  38. package/dist/cli/runHeadless.js.map +1 -1
  39. package/dist/cli/slashCommands.js +16 -0
  40. package/dist/cli/slashCommands.js.map +1 -1
  41. package/dist/cli/slashHandlers/krakenFanout.js +200 -0
  42. package/dist/cli/slashHandlers/krakenFanout.js.map +1 -0
  43. package/dist/cli/slashHandlers/krakenWorkbench.js +49 -0
  44. package/dist/cli/slashHandlers/krakenWorkbench.js.map +1 -0
  45. package/dist/cli/tools/krakenCsvFanout.js +260 -0
  46. package/dist/cli/tools/krakenCsvFanout.js.map +1 -0
  47. package/dist/cli/tools/krakenCsvFanout.test.js +200 -0
  48. package/dist/cli/tools/krakenCsvFanout.test.js.map +1 -0
  49. package/dist/cli/tools/krakenModel.js +32 -0
  50. package/dist/cli/tools/krakenModel.js.map +1 -1
  51. package/dist/cli/tools/krakenRadio.js.map +1 -1
  52. package/dist/cli/tools/taskTool.js +1 -1
  53. package/dist/cli/tools/taskTool.js.map +1 -1
  54. package/package.json +2 -2
@@ -0,0 +1,286 @@
1
+ /**
2
+ * Kraken script planner — emits `.ts` plans instead of JSON DAGs.
3
+ *
4
+ * Companion to `planner.ts` (the JSON-DAG path). Same LLM plumbing, same
5
+ * model selection, same workspace summary — different system prompt and
6
+ * different output (TypeScript source, not a parsed JSON object).
7
+ *
8
+ * The script planner is opt-in: the JSON path stays the default for small
9
+ * goals. Select via `ZELARI_KRAKEN_PLAN_FORMAT=script`. The `auto` mode
10
+ * (future) picks the script path when the goal suggests > 4 nodes.
11
+ *
12
+ * On parse / compile failure, retry once with corrective feedback; on
13
+ * second failure, surface the error so the caller can fall back to the
14
+ * JSON-DAG path.
15
+ *
16
+ * @since Kraken v1.30.x — workflow script runtime (F1.2)
17
+ */
18
+ import { promises as fs } from 'node:fs';
19
+ import { build } from 'esbuild';
20
+ import path from 'node:path';
21
+ import { z } from 'zod';
22
+ import { buildWorkspaceSummary } from '../workspace/workspaceSummary.js';
23
+ import { resolveApiKeyWithMeta } from '../keyStore.js';
24
+ import { resolveBaseUrl } from '../provider/openai-compatible.js';
25
+ import { getModelForProvider, getProviderConfig } from '../providerConfig.js';
26
+ import { PlannerTransportError } from './planner.js';
27
+ const MAX_PLAN_ATTEMPTS = 2;
28
+ const ScriptPlannerOptionsSchema = z.object({
29
+ prompt: z.string().min(1),
30
+ graphId: z.string().optional(),
31
+ provider: z.string().optional(),
32
+ model: z.string().optional(),
33
+ previousAttempt: z.string().optional(),
34
+ cwd: z.string().optional(),
35
+ workspace: z.string().optional(),
36
+ llmClient: z.custom().optional(),
37
+ });
38
+ /** SDK surface the script can import. Mirrored in the system prompt. */
39
+ const SDK_SURFACE_DOC = `\
40
+ - \`tentacle({ kind, label, prompt, scope?, acceptance?, deps?, maxRetries?, maxRuntimeMs? })\`
41
+ Returns a \`TentacleRef { id, kind, label, status, findings, verdict?, scope? }\`.
42
+ - \`kind\`: "explore" | "general" | "verify" | "fix" | "merge".
43
+ - \`prompt\`: self-contained instruction handed to the sub-agent.
44
+ - \`scope\`: path/glob allowlist (required for parallel writers).
45
+ - \`acceptance\`: checkable checklist (enforced by verify tentacles).
46
+ - \`merge([refs], { strategy?, message?, cleanup? })\` — ONE-SHOT per plan.
47
+ - \`barrier([t1, t2, t3])\` — typed wait over N parallel tentacles.
48
+ - \`race([t1, t2])\` — first-completed wins; losers are skipped.
49
+ - \`while_(cond, body, maxIter)\` / \`until(cond, body, maxIter)\` — bounded loops.
50
+ - \`checkpoint(label?)\` — persist the current plan state to disk.
51
+ - \`log(msg, data?)\` / \`emit({ kind, detail? })\` — radio + workbench events.
52
+ - \`getContext()\` — read-only { graphId, goal, parentCwd, maxTentacles, planTimeoutMs }.`;
53
+ export const KRAKEN_SCRIPT_PLANNER_SYSTEM_PROMPT = [
54
+ 'You are the SCRIPT PLANNER for Kraken, a multi-agent graph executor.',
55
+ '',
56
+ 'You write a TypeScript module that imports capabilities from',
57
+ "'@zelari/kraken-runtime' and calls them to drive a multi-agent run.",
58
+ '',
59
+ 'Return ONLY the TypeScript source — no markdown fence, no prose, no',
60
+ "explanation. The whole response is parsed as code, so even one extra",
61
+ "character outside the source breaks the run.",
62
+ '',
63
+ '# SDK',
64
+ '',
65
+ '```ts',
66
+ "import { tentacle, merge, barrier, race, while_, until, checkpoint, log, emit, getContext } from '@zelari/kraken-runtime';",
67
+ '```',
68
+ '',
69
+ SDK_SURFACE_DOC,
70
+ '',
71
+ '# Constraints',
72
+ '',
73
+ '- The plan is a script, NOT a graph: call `tentacle()` in the order you',
74
+ ' want, with whatever control flow makes sense. Sequential calls are',
75
+ ' sequential; `Promise.all([...])` is parallel.',
76
+ '- You do NOT need to declare deps explicitly. The script awaits each',
77
+ " `tentacle()` in turn; whatever has already run is in scope by id.",
78
+ '- You can only `merge()` ONCE. If you need to merge at two stages, split',
79
+ ' into two plans (run them sequentially) or use checkpoint + follow-up.',
80
+ '- Use `while_` / `until` for the Gauntlet Loop pattern: a writer runs,',
81
+ ' a verify judges it, and on FAIL the body retries with the verify',
82
+ " findings as context. Cap `maxIter` — the wall-clock budget cuts past it.",
83
+ '- Reach for `Promise.all` to fan out independent writers in parallel.',
84
+ ' When you do, give each a `scope` of paths it may touch; the executor',
85
+ ' refuses to run two parallel writers with overlapping scopes.',
86
+ '- End with either `merge(...)` or no merge and a final `log(...)`. Both',
87
+ ' are valid convergence shapes.',
88
+ '',
89
+ '# Style',
90
+ '',
91
+ '- Top-level `await` is fine. Write the plan as a sequence of',
92
+ ' `const x = await ...`.',
93
+ '- No `import` other than the SDK. The plan is single-file.',
94
+ '- No `process`, `require`, `Buffer`, `eval`, or `new Function` — the',
95
+ ' sandbox will reject the bundle.',
96
+ '- Keep it under ~200 lines. The plan is a sketch of intent, not full code.',
97
+ '',
98
+ '# Default shape (most goals fit this)',
99
+ '',
100
+ '1. `const ctx = await tentacle({ kind: "explore", label: "map", prompt: "..." })`',
101
+ '2. `const [a, b, c] = await Promise.all([tentacle({ kind: "general", scope: [...] }), ...])`',
102
+ '3. `const verify = await tentacle({ kind: "verify", label: "judge", deps: [a, b, c] })`',
103
+ '4. `if (verify.verdict === "fail") { ... rework via while_ ... }`',
104
+ '5. `await merge([a, b, c])`',
105
+ '',
106
+ '# Reviewer personas (Pillar 2)',
107
+ '',
108
+ 'Three reviewer kinds exist, all using the same trailer format:',
109
+ '',
110
+ '- `kind: "verify"` — checks `acceptance[]` on disk. Default after every writer.',
111
+ '- `kind: "spec"` — compares the writer\'s output against a written spec,',
112
+ ' per requirement. System prompt is the spec-reviewer persona (conservative).',
113
+ ' Use when the task has a written spec / plan you can paste into the prompt.',
114
+ '- `kind: "conformance"` — compares the writer\'s output against the user\'s',
115
+ ' ORIGINAL VERBATIM PROMPT. System prompt is the conformance-reviewer persona',
116
+ ' (literal). Use as the LAST reviewer before `merge()` on goal-aligned tasks.',
117
+ '',
118
+ 'All three return `{ kind, status, findings, verdict }`. The trailer is',
119
+ '`VERDICT: PASS|FAIL`; the per-requirement table (when present) is a JSON',
120
+ 'code block before the trailer. Failure rewrites the work via the existing',
121
+ '`while_` pattern.',
122
+ '',
123
+ 'Example with all three personas:',
124
+ '',
125
+ '```ts',
126
+ 'const writer = await tentacle({ kind: "general", label: "do it", prompt: "...", scope: ["src/x"] });',
127
+ 'const verify = await tentacle({ kind: "verify", label: "acceptance", deps: [writer] });',
128
+ 'const spec = await tentacle({ kind: "spec", label: "spec review", deps: [writer], prompt: "<paste spec here>" });',
129
+ 'const conf = await tentacle({ kind: "conformance", label: "conformance", deps: [writer], prompt: "<paste user prompt here>" });',
130
+ 'if (verify.verdict === "fail" || spec.verdict === "fail" || conf.verdict === "fail") {',
131
+ ' // rework via while_',
132
+ '}',
133
+ 'await merge([writer]);',
134
+ '```',
135
+ ].join('\n');
136
+ /**
137
+ * Plan a script: ask the LLM for a `.ts` source, strip any markdown fence,
138
+ * compile it (esbuild) to validate syntax, write to
139
+ * `.zelari/kraken/runs/<graphId>/plan.ts`, return the path. On any failure,
140
+ * retry once with corrective feedback; on second failure, throw.
141
+ *
142
+ * NOTE: the resolver / `createDefaultLlmClient` is duplicated from
143
+ * `planner.ts` to keep this slice's diff small. A future slice will
144
+ * extract both into `plannerHelpers.ts`.
145
+ */
146
+ export async function planScript(opts) {
147
+ // Empty-prompt check before Zod so the error message is friendlier.
148
+ if (!opts.prompt || !opts.prompt.trim()) {
149
+ throw new Error('planScript: prompt is required');
150
+ }
151
+ const parsed = ScriptPlannerOptionsSchema.parse(opts);
152
+ const client = parsed.llmClient ?? (await createScriptPlannerLlmClient({ provider: parsed.provider, model: parsed.model }));
153
+ const graphId = parsed.graphId ?? `kraken-script-${Date.now().toString(36)}`;
154
+ const workspace = await resolveWorkspaceListing(parsed);
155
+ const baseUser = buildUserPrompt(parsed.prompt, workspace, parsed.previousAttempt);
156
+ const runDir = parsed.cwd
157
+ ? path.join(parsed.cwd, '.zelari', 'kraken', 'runs', graphId)
158
+ : path.join('.zelari', 'kraken', 'runs', graphId);
159
+ const planPath = path.join(runDir, 'plan.ts');
160
+ let lastError;
161
+ let userMessage = baseUser;
162
+ let source = '';
163
+ let bytes = 0;
164
+ let attempts = 0;
165
+ for (let attempt = 1; attempt <= MAX_PLAN_ATTEMPTS; attempt++) {
166
+ attempts = attempt;
167
+ let text;
168
+ try {
169
+ text = await client.complete({ system: KRAKEN_SCRIPT_PLANNER_SYSTEM_PROMPT, user: userMessage });
170
+ }
171
+ catch (err) {
172
+ throw new PlannerTransportError(err instanceof Error ? err.message : String(err));
173
+ }
174
+ bytes = text.length;
175
+ source = extractCodeBlock(text, 'ts');
176
+ if (!source.trim()) {
177
+ lastError = 'response was empty or not a code block';
178
+ userMessage = `${baseUser}\n\n---\n\nYour last reply was empty or not a TypeScript code block. Return ONLY TypeScript source — no markdown fence.`;
179
+ continue;
180
+ }
181
+ try {
182
+ await build({
183
+ stdin: { contents: source, resolveDir: parsed.cwd ?? process.cwd(), loader: 'ts' },
184
+ bundle: false,
185
+ write: false,
186
+ logLevel: 'silent',
187
+ });
188
+ await fs.mkdir(runDir, { recursive: true });
189
+ await fs.writeFile(planPath, source, 'utf8');
190
+ return { planPath, source, bytes, attempts, compiled: true };
191
+ }
192
+ catch (err) {
193
+ lastError = err instanceof Error ? err.message : String(err);
194
+ userMessage = `${baseUser}\n\n---\n\nYour last script failed to compile with esbuild. The error was:\n\n${lastError}\n\nFix the error and return ONLY the corrected TypeScript source.`;
195
+ }
196
+ }
197
+ throw new Error(`kraken script planner: failed to produce a compilable plan after ${MAX_PLAN_ATTEMPTS} attempts — ${lastError ?? 'unknown'}`);
198
+ }
199
+ /** Build the workspace listing handed to the planner. Reuses the JSON
200
+ * planner's summary so the two paths see the same project context. */
201
+ async function resolveWorkspaceListing(opts) {
202
+ if (opts.workspace)
203
+ return opts.workspace;
204
+ if (!opts.cwd)
205
+ return '(no project listing — no cwd provided)';
206
+ const budget = Number.parseInt(process.env.ZELARI_KRAKEN_PLANNER_WORKSPACE_CHARS ?? '3000', 10);
207
+ const maxChars = Number.isFinite(budget) && budget > 0 ? budget : 3000;
208
+ return buildWorkspaceSummary(opts.cwd, { maxChars });
209
+ }
210
+ function buildUserPrompt(prompt, workspace, previousAttempt) {
211
+ return [
212
+ '## Goal',
213
+ prompt.trim(),
214
+ '',
215
+ '## Project listing (real paths on disk; build `scope` from these)',
216
+ workspace,
217
+ '',
218
+ previousAttempt ? `## Previous unfinished graph in this project\n${previousAttempt}\n` : '',
219
+ 'Write the plan now. Return ONLY TypeScript source — no markdown fence.',
220
+ ].join('\n');
221
+ }
222
+ /**
223
+ * Strip a markdown ```ts / ```typescript / ``` fence from a model reply, if
224
+ * present. Returns the inner text or the original if no fence is found.
225
+ *
226
+ * Mirrors the JSON planner's `bodyOf` for the `.ts` language.
227
+ */
228
+ export function extractCodeBlock(text, language) {
229
+ const trimmed = text.trim();
230
+ const re = new RegExp('^```(?:' + language + '|typescript|javascript)?\\s*([\\s\\S]*?)```\\s*$', 'i');
231
+ const m = re.exec(trimmed);
232
+ if (m)
233
+ return m[1].trim();
234
+ // Unfenced: assume the whole reply is code (most common shape for
235
+ // models that follow the "no fence" instruction).
236
+ return trimmed;
237
+ }
238
+ /** Local LLM client builder, duplicated from `planner.ts` to keep the diff
239
+ * small. Will be unified in a follow-up slice. */
240
+ async function createScriptPlannerLlmClient(opts) {
241
+ const active = (opts.provider?.trim() || getProviderConfig().activeProviderId);
242
+ const meta = await resolveApiKeyWithMeta(active);
243
+ if (!meta?.apiKey) {
244
+ throw new Error(`No API key for provider '${active}'. Save a key in Settings → Provider.`);
245
+ }
246
+ const baseUrl = resolveBaseUrl(active);
247
+ if (!baseUrl) {
248
+ throw new Error(`No base URL for provider '${active}'. Set a custom endpoint in Settings.`);
249
+ }
250
+ const model = opts.model?.trim() ||
251
+ process.env.ZELARI_KRAKEN_PLANNER_MODEL?.trim() ||
252
+ getModelForProvider(active) ||
253
+ 'grok-4.5';
254
+ // We just need the `complete` function. The metadata is unused past this
255
+ // point; the planner only needs to call the LLM.
256
+ return {
257
+ async complete({ system, user }) {
258
+ const res = await fetch(`${baseUrl}/chat/completions`, {
259
+ method: 'POST',
260
+ headers: {
261
+ 'content-type': 'application/json',
262
+ ...(meta.apiKey ? { authorization: `Bearer ${meta.apiKey}` } : {}),
263
+ },
264
+ body: JSON.stringify({
265
+ model,
266
+ messages: [
267
+ { role: 'system', content: system },
268
+ { role: 'user', content: user },
269
+ ],
270
+ stream: false,
271
+ temperature: 0.2,
272
+ }),
273
+ });
274
+ if (!res.ok) {
275
+ const body = await res.text().catch(() => '');
276
+ throw new Error(`LLM HTTP ${res.status}: ${body.slice(0, 200)}`);
277
+ }
278
+ const json = (await res.json());
279
+ const content = json.choices?.[0]?.message?.content;
280
+ if (typeof content !== 'string')
281
+ throw new Error('LLM returned no content');
282
+ return content;
283
+ },
284
+ };
285
+ }
286
+ //# sourceMappingURL=scriptPlanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scriptPlanner.js","sourceRoot":"","sources":["../../../src/cli/kraken/scriptPlanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Kraken script planner — tests.
3
+ *
4
+ * Covers:
5
+ * - `extractCodeBlock` — markdown fence stripping
6
+ * - `planScript` — happy path, retry on compile failure, empty response
7
+ *
8
+ * Tests pass a `llmClient` override so the planner never hits the network.
9
+ */
10
+ import { describe, it, expect } from 'vitest';
11
+ import { promises as fs } from 'node:fs';
12
+ import os from 'node:os';
13
+ import path from 'node:path';
14
+ import { extractCodeBlock, planScript, KRAKEN_SCRIPT_PLANNER_SYSTEM_PROMPT, } from './scriptPlanner.js';
15
+ function fakeClient(responses) {
16
+ const calls = [];
17
+ let i = 0;
18
+ const client = {
19
+ async complete({ system, user }) {
20
+ calls.push(user);
21
+ const out = responses[i] ?? responses[responses.length - 1] ?? '';
22
+ i += 1;
23
+ // Sanity: the system prompt should mention the SDK.
24
+ if (!system.includes('tentacle')) {
25
+ throw new Error('system prompt missing tentacle mention');
26
+ }
27
+ return out;
28
+ },
29
+ };
30
+ return { client, calls };
31
+ }
32
+ describe('extractCodeBlock', () => {
33
+ it('strips a ```ts fence', () => {
34
+ expect(extractCodeBlock('```ts\nconst x = 1;\n```', 'ts')).toBe('const x = 1;');
35
+ });
36
+ it('strips a ```typescript fence', () => {
37
+ expect(extractCodeBlock('```typescript\nconst x = 1;\n```', 'ts')).toBe('const x = 1;');
38
+ });
39
+ it('strips a ``` fence with no language', () => {
40
+ expect(extractCodeBlock('```\nconst x = 1;\n```', 'ts')).toBe('const x = 1;');
41
+ });
42
+ it('returns the original text if no fence is present', () => {
43
+ const src = 'const x = 1;';
44
+ expect(extractCodeBlock(src, 'ts')).toBe(src);
45
+ });
46
+ it('trims surrounding whitespace', () => {
47
+ expect(extractCodeBlock(' \n const x = 1;\n ', 'ts')).toBe('const x = 1;');
48
+ });
49
+ });
50
+ describe('KRAKEN_SCRIPT_PLANNER_SYSTEM_PROMPT', () => {
51
+ it('mentions the SDK capabilities', () => {
52
+ expect(KRAKEN_SCRIPT_PLANNER_SYSTEM_PROMPT).toContain('tentacle');
53
+ expect(KRAKEN_SCRIPT_PLANNER_SYSTEM_PROMPT).toContain('merge');
54
+ expect(KRAKEN_SCRIPT_PLANNER_SYSTEM_PROMPT).toContain('while_');
55
+ expect(KRAKEN_SCRIPT_PLANNER_SYSTEM_PROMPT).toContain('until');
56
+ expect(KRAKEN_SCRIPT_PLANNER_SYSTEM_PROMPT).toContain('checkpoint');
57
+ });
58
+ it('warns against process / require / Buffer', () => {
59
+ expect(KRAKEN_SCRIPT_PLANNER_SYSTEM_PROMPT).toContain('process');
60
+ expect(KRAKEN_SCRIPT_PLANNER_SYSTEM_PROMPT).toContain('require');
61
+ expect(KRAKEN_SCRIPT_PLANNER_SYSTEM_PROMPT).toContain('Buffer');
62
+ });
63
+ it('says return ONLY code (no fence)', () => {
64
+ expect(KRAKEN_SCRIPT_PLANNER_SYSTEM_PROMPT).toMatch(/no markdown fence/i);
65
+ });
66
+ });
67
+ describe('planScript', () => {
68
+ it('writes a compilable plan to .zelari/kraken/runs/<graphId>/plan.ts', async () => {
69
+ const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'kraken-scriptplan-'));
70
+ const goodSource = `
71
+ import { tentacle, merge } from '@zelari/kraken-runtime';
72
+ const a = await tentacle({ kind: 'explore', label: 'map', prompt: 'x' });
73
+ await merge([a]);
74
+ `;
75
+ const { client, calls } = fakeClient([goodSource]);
76
+ const result = await planScript({
77
+ prompt: 'map the auth system',
78
+ graphId: 'g-happy',
79
+ cwd: tmp,
80
+ llmClient: client,
81
+ });
82
+ expect(result.compiled).toBe(true);
83
+ expect(result.attempts).toBe(1);
84
+ expect(result.source).toContain("import { tentacle");
85
+ expect(calls).toHaveLength(1);
86
+ // The file is on disk.
87
+ const written = await fs.readFile(result.planPath, 'utf8');
88
+ expect(written).toBe(goodSource.trim());
89
+ // Path is the expected one.
90
+ expect(result.planPath).toBe(path.join(tmp, '.zelari', 'kraken', 'runs', 'g-happy', 'plan.ts'));
91
+ });
92
+ it('retries on compile failure and succeeds on the second attempt', async () => {
93
+ const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'kraken-scriptplan-'));
94
+ // A real SYNTAX error: unbalanced brace. esbuild fails on parse.
95
+ const broken = `function oops( { return 1;`;
96
+ const good = `import { log } from '@zelari/kraken-runtime';\nlog('hello');`;
97
+ const { client, calls } = fakeClient([broken, good]);
98
+ const result = await planScript({
99
+ prompt: 'do a thing',
100
+ graphId: 'g-retry',
101
+ cwd: tmp,
102
+ llmClient: client,
103
+ });
104
+ expect(result.attempts).toBe(2);
105
+ expect(result.compiled).toBe(true);
106
+ expect(calls).toHaveLength(2);
107
+ // The corrective user message should mention the previous error.
108
+ expect(calls[1]).toMatch(/failed to compile/i);
109
+ });
110
+ it('throws after MAX_PLAN_ATTEMPTS on persistent compile failure', async () => {
111
+ const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'kraken-scriptplan-'));
112
+ const broken = `function oops( { return 1;`;
113
+ const { client } = fakeClient([broken, broken]);
114
+ await expect(planScript({
115
+ prompt: 'still broken',
116
+ graphId: 'g-fail',
117
+ cwd: tmp,
118
+ llmClient: client,
119
+ })).rejects.toThrowError(/failed to produce a compilable plan/);
120
+ });
121
+ it('retries on empty response and succeeds on the second attempt', async () => {
122
+ const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'kraken-scriptplan-'));
123
+ const good = `import { log } from '@zelari/kraken-runtime';\nlog('ok');`;
124
+ const { client } = fakeClient(['', good]);
125
+ const result = await planScript({
126
+ prompt: 'empty first',
127
+ graphId: 'g-empty',
128
+ cwd: tmp,
129
+ llmClient: client,
130
+ });
131
+ expect(result.attempts).toBe(2);
132
+ expect(result.compiled).toBe(true);
133
+ });
134
+ it('strips a ```ts fence from the LLM reply', async () => {
135
+ const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'kraken-scriptplan-'));
136
+ const fenced = '```ts\nimport { log } from \'@zelari/kraken-runtime\';\nlog(\'ok\');\n```';
137
+ const { client } = fakeClient([fenced]);
138
+ const result = await planScript({
139
+ prompt: 'fenced',
140
+ graphId: 'g-fence',
141
+ cwd: tmp,
142
+ llmClient: client,
143
+ });
144
+ expect(result.compiled).toBe(true);
145
+ expect(result.source.startsWith('import')).toBe(true);
146
+ expect(result.source).not.toContain('```');
147
+ });
148
+ it('rejects an empty prompt (Zod schema)', async () => {
149
+ await expect(planScript({ prompt: '', llmClient: fakeClient(['']).client })).rejects.toThrowError(/prompt|Too small/i);
150
+ });
151
+ });
152
+ //# sourceMappingURL=scriptPlanner.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scriptPlanner.test.js","sourceRoot":"","sources":["../../../src/cli/kraken/scriptPlanner.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Kraken — skill auto-suggest (Gauntlet Loop step 8, Cross-cutting C1).
3
+ *
4
+ * After a converged graph, scan the run for patterns worth promoting to
5
+ * a reusable skill. Today the patterns are simple:
6
+ *
7
+ * - A `verify`/`spec`/`conformance` reviewer returned FAIL with a
8
+ * concrete, actionable gap, the next attempt fixed it. The (gap,
9
+ * fix) pair is a candidate.
10
+ * - A writer hit the same scope twice (rework round) and the second
11
+ * attempt succeeded where the first failed.
12
+ *
13
+ * The suggestion is *offered, not applied*. The user accepts via the
14
+ * existing `/promote-skill <id>` command. We deliberately do not
15
+ * auto-promote: a converged run is not always a good run, and a
16
+ * suggestion the user can audit is much safer than a silent
17
+ * write-to-SKILL.md.
18
+ *
19
+ * The threshold is "did the same kind of review reject a writer and a
20
+ * later iteration accept the work?". The function below is pure: it
21
+ * takes a structured run summary and returns zero or more suggestions.
22
+ *
23
+ * @since Kraken v1.30.x — workflow script runtime (Cross-cutting C1)
24
+ */
25
+ const MAX_FINDINGS_CHARS = 600;
26
+ /** Produce a short, kebab-case id from a free-text label. */
27
+ export function slugifyLabel(label) {
28
+ return label
29
+ .toLowerCase()
30
+ .replace(/[^a-z0-9]+/g, '-')
31
+ .replace(/^-+|-+$/g, '')
32
+ .slice(0, 40) || 'kraken-skill';
33
+ }
34
+ /** Compute skill suggestions from a finished run. Pure function. */
35
+ export function suggestSkillsFromRun(result, opts = { graphId: 'g', goal: '' }) {
36
+ if (!result.converged)
37
+ return [];
38
+ const refs = [...result.tentacles.values()];
39
+ // 1. Pattern: a reviewer FAILed with concrete findings, and a later
40
+ // pass produced a `done` with a similar scope/label. Heuristic: a
41
+ // reviewer FAIL followed by a `fix` node that the executor resolved
42
+ // (a rework round).
43
+ const reviewerFails = refs.filter((r) => (r.kind === 'verify' || r.kind === 'spec' || r.kind === 'conformance') && r.verdict === 'fail');
44
+ const fixNodes = refs.filter((r) => r.kind === 'fix' && r.status === 'done');
45
+ if (reviewerFails.length === 0 || fixNodes.length === 0)
46
+ return [];
47
+ // Pair the first reviewer FAIL with the first fix-node. One suggestion
48
+ // per run is the right cadence for v1; multi-suggestion can come later.
49
+ const fail = reviewerFails[0];
50
+ const fix = fixNodes[0];
51
+ const failureFindings = (fail.findings || '').slice(0, MAX_FINDINGS_CHARS);
52
+ const fixFindings = (fix.findings || '').slice(0, MAX_FINDINGS_CHARS);
53
+ const id = `kraken-skill-${slugifyLabel(fail.label)}-${opts.graphId}`;
54
+ // Confidence: stronger when the fix succeeded on a fresh attempt (no
55
+ // cascading errors), when the failure findings are concrete, and when
56
+ // the goal is non-empty.
57
+ let confidence = 0.4;
58
+ if (failureFindings.length > 60)
59
+ confidence += 0.2;
60
+ if (fixFindings.length > 60)
61
+ confidence += 0.2;
62
+ if (opts.goal.trim().length > 0)
63
+ confidence += 0.1;
64
+ if (result.tentacles.size > 1)
65
+ confidence += 0.1;
66
+ confidence = Math.min(1, confidence);
67
+ return [
68
+ {
69
+ id,
70
+ title: `Skill from "${fail.label}" (fixed on rework)`,
71
+ body: [
72
+ `The reviewer \`${fail.label}\` (${fail.kind}) FAILed with:`,
73
+ '',
74
+ '```',
75
+ failureFindings,
76
+ '```',
77
+ '',
78
+ `The follow-up \`${fix.label}\` fixed the issue.`,
79
+ '',
80
+ `Promote this to a skill with:`,
81
+ '```',
82
+ `/promote-skill ${id}`,
83
+ '```',
84
+ '',
85
+ `Confidence: ${(confidence * 100).toFixed(0)}%`,
86
+ ].join('\n'),
87
+ sourceKind: fail.kind,
88
+ failureFindings,
89
+ confidence,
90
+ },
91
+ ];
92
+ }
93
+ /** Render a single suggestion as a Markdown card for the transcript. */
94
+ export function renderSuggestionCard(s) {
95
+ return s.body;
96
+ }
97
+ //# sourceMappingURL=skillSuggest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skillSuggest.js","sourceRoot":"","sources":["../../../src/cli/kraken/skillSuggest.ts"],"names":[],"mappings":"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"}