@kynetic-ai/spec 0.9.1 → 0.11.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/README.md +2 -1
- package/dist/acp/client.d.ts +6 -1
- package/dist/acp/client.d.ts.map +1 -1
- package/dist/acp/client.js +7 -2
- package/dist/acp/client.js.map +1 -1
- package/dist/acp/framing.d.ts +12 -1
- package/dist/acp/framing.d.ts.map +1 -1
- package/dist/acp/framing.js +27 -4
- package/dist/acp/framing.js.map +1 -1
- package/dist/agent-runtime/dispatch.d.ts +292 -0
- package/dist/agent-runtime/dispatch.d.ts.map +1 -0
- package/dist/agent-runtime/dispatch.js +860 -0
- package/dist/agent-runtime/dispatch.js.map +1 -0
- package/dist/agent-runtime/index.d.ts +11 -0
- package/dist/agent-runtime/index.d.ts.map +1 -0
- package/dist/agent-runtime/index.js +11 -0
- package/dist/agent-runtime/index.js.map +1 -0
- package/dist/agent-runtime/invocation.d.ts +86 -0
- package/dist/agent-runtime/invocation.d.ts.map +1 -0
- package/dist/agent-runtime/invocation.js +442 -0
- package/dist/agent-runtime/invocation.js.map +1 -0
- package/dist/agent-runtime/prompts.d.ts +50 -0
- package/dist/agent-runtime/prompts.d.ts.map +1 -0
- package/dist/agent-runtime/prompts.js +108 -0
- package/dist/agent-runtime/prompts.js.map +1 -0
- package/dist/agents/spawner.d.ts.map +1 -1
- package/dist/agents/spawner.js +60 -4
- package/dist/agents/spawner.js.map +1 -1
- package/dist/cli/batch-exec.d.ts.map +1 -1
- package/dist/cli/batch-exec.js +140 -62
- package/dist/cli/batch-exec.js.map +1 -1
- package/dist/cli/batch-write-buffer.d.ts +141 -0
- package/dist/cli/batch-write-buffer.d.ts.map +1 -0
- package/dist/cli/batch-write-buffer.js +400 -0
- package/dist/cli/batch-write-buffer.js.map +1 -0
- package/dist/cli/commands/agent.d.ts +20 -0
- package/dist/cli/commands/agent.d.ts.map +1 -0
- package/dist/cli/commands/agent.js +831 -0
- package/dist/cli/commands/agent.js.map +1 -0
- package/dist/cli/commands/inbox.d.ts.map +1 -1
- package/dist/cli/commands/inbox.js +46 -22
- package/dist/cli/commands/inbox.js.map +1 -1
- package/dist/cli/commands/index.d.ts +1 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +1 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/item.d.ts.map +1 -1
- package/dist/cli/commands/item.js +22 -16
- package/dist/cli/commands/item.js.map +1 -1
- package/dist/cli/commands/log.js +1 -1
- package/dist/cli/commands/log.js.map +1 -1
- package/dist/cli/commands/meta.d.ts.map +1 -1
- package/dist/cli/commands/meta.js +168 -6
- package/dist/cli/commands/meta.js.map +1 -1
- package/dist/cli/commands/module.d.ts.map +1 -1
- package/dist/cli/commands/module.js +2 -1
- package/dist/cli/commands/module.js.map +1 -1
- package/dist/cli/commands/plan-import.js +19 -3
- package/dist/cli/commands/plan-import.js.map +1 -1
- package/dist/cli/commands/plan.d.ts.map +1 -1
- package/dist/cli/commands/plan.js +87 -43
- package/dist/cli/commands/plan.js.map +1 -1
- package/dist/cli/commands/ralph.d.ts +5 -56
- package/dist/cli/commands/ralph.d.ts.map +1 -1
- package/dist/cli/commands/ralph.js +52 -1502
- package/dist/cli/commands/ralph.js.map +1 -1
- package/dist/cli/commands/search.d.ts.map +1 -1
- package/dist/cli/commands/search.js +22 -13
- package/dist/cli/commands/search.js.map +1 -1
- package/dist/cli/commands/serve.d.ts.map +1 -1
- package/dist/cli/commands/serve.js +70 -11
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/session/checkpoint.d.ts.map +1 -1
- package/dist/cli/commands/session/checkpoint.js +7 -2
- package/dist/cli/commands/session/checkpoint.js.map +1 -1
- package/dist/cli/commands/session/commands.d.ts.map +1 -1
- package/dist/cli/commands/session/commands.js +15 -0
- package/dist/cli/commands/session/commands.js.map +1 -1
- package/dist/cli/commands/session/context.d.ts.map +1 -1
- package/dist/cli/commands/session/context.js +10 -5
- package/dist/cli/commands/session/context.js.map +1 -1
- package/dist/cli/commands/session/log.d.ts +1 -0
- package/dist/cli/commands/session/log.d.ts.map +1 -1
- package/dist/cli/commands/session/log.js +124 -8
- package/dist/cli/commands/session/log.js.map +1 -1
- package/dist/cli/commands/session/stale-close.d.ts +17 -0
- package/dist/cli/commands/session/stale-close.d.ts.map +1 -0
- package/dist/cli/commands/session/stale-close.js +378 -0
- package/dist/cli/commands/session/stale-close.js.map +1 -0
- package/dist/cli/commands/setup.d.ts.map +1 -1
- package/dist/cli/commands/setup.js +95 -0
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/skill-crud.d.ts.map +1 -1
- package/dist/cli/commands/skill-crud.js +4 -3
- package/dist/cli/commands/skill-crud.js.map +1 -1
- package/dist/cli/commands/skill-diff.d.ts.map +1 -1
- package/dist/cli/commands/skill-diff.js +15 -0
- package/dist/cli/commands/skill-diff.js.map +1 -1
- package/dist/cli/commands/skill-install.d.ts.map +1 -1
- package/dist/cli/commands/skill-install.js +50 -18
- package/dist/cli/commands/skill-install.js.map +1 -1
- package/dist/cli/commands/task.d.ts.map +1 -1
- package/dist/cli/commands/task.js +536 -310
- package/dist/cli/commands/task.js.map +1 -1
- package/dist/cli/commands/tasks.js +1 -1
- package/dist/cli/commands/tasks.js.map +1 -1
- package/dist/cli/commands/triage.d.ts.map +1 -1
- package/dist/cli/commands/triage.js +37 -13
- package/dist/cli/commands/triage.js.map +1 -1
- package/dist/cli/commands/validate.d.ts.map +1 -1
- package/dist/cli/commands/validate.js +65 -25
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/cli/help/content.d.ts.map +1 -1
- package/dist/cli/help/content.js +5 -0
- package/dist/cli/help/content.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/output.d.ts.map +1 -1
- package/dist/cli/output.js +5 -1
- package/dist/cli/output.js.map +1 -1
- package/dist/daemon/project-context.ts +22 -0
- package/dist/daemon/routes/agent-dispatch.ts +279 -0
- package/dist/daemon/routes/items.ts +22 -0
- package/dist/daemon/routes/meta.ts +141 -1
- package/dist/daemon/routes/plans.ts +147 -0
- package/dist/daemon/routes/sessions.ts +180 -0
- package/dist/daemon/routes/tasks.ts +198 -0
- package/dist/daemon/routes/validation.ts +1 -1
- package/dist/daemon/server.ts +77 -21
- package/dist/daemon/websocket/handler.ts +67 -6
- package/dist/daemon/websocket/lifecycle.ts +19 -0
- package/dist/daemon/websocket/pubsub.ts +74 -3
- package/dist/export/html.d.ts.map +1 -1
- package/dist/export/html.js +5 -2
- package/dist/export/html.js.map +1 -1
- package/dist/export/triage.d.ts +1 -1
- package/dist/export/triage.d.ts.map +1 -1
- package/dist/export/triage.js +5 -3
- package/dist/export/triage.js.map +1 -1
- package/dist/parser/alignment.d.ts.map +1 -1
- package/dist/parser/alignment.js +10 -5
- package/dist/parser/alignment.js.map +1 -1
- package/dist/parser/assess.js +1 -1
- package/dist/parser/assess.js.map +1 -1
- package/dist/parser/config.d.ts +6 -6
- package/dist/parser/meta.d.ts.map +1 -1
- package/dist/parser/meta.js +9 -8
- package/dist/parser/meta.js.map +1 -1
- package/dist/parser/plan-document.d.ts +12 -12
- package/dist/parser/plans.d.ts +7 -0
- package/dist/parser/plans.d.ts.map +1 -1
- package/dist/parser/plans.js +100 -15
- package/dist/parser/plans.js.map +1 -1
- package/dist/parser/refs.d.ts +5 -0
- package/dist/parser/refs.d.ts.map +1 -1
- package/dist/parser/refs.js +17 -12
- package/dist/parser/refs.js.map +1 -1
- package/dist/parser/shadow.d.ts +1 -1
- package/dist/parser/shadow.d.ts.map +1 -1
- package/dist/parser/shadow.js +71 -4
- package/dist/parser/shadow.js.map +1 -1
- package/dist/parser/skill-render.d.ts.map +1 -1
- package/dist/parser/skill-render.js +6 -3
- package/dist/parser/skill-render.js.map +1 -1
- package/dist/parser/validate.d.ts.map +1 -1
- package/dist/parser/validate.js +35 -76
- package/dist/parser/validate.js.map +1 -1
- package/dist/parser/yaml.d.ts +24 -5
- package/dist/parser/yaml.d.ts.map +1 -1
- package/dist/parser/yaml.js +224 -64
- package/dist/parser/yaml.js.map +1 -1
- package/dist/schema/meta.d.ts +457 -119
- package/dist/schema/meta.d.ts.map +1 -1
- package/dist/schema/meta.js +56 -0
- package/dist/schema/meta.js.map +1 -1
- package/dist/schema/plan.d.ts +22 -22
- package/dist/schema/spec.d.ts +39 -39
- package/dist/schema/task.d.ts +43 -32
- package/dist/schema/task.d.ts.map +1 -1
- package/dist/schema/task.js +5 -0
- package/dist/schema/task.js.map +1 -1
- package/dist/sessions/store.d.ts +126 -0
- package/dist/sessions/store.d.ts.map +1 -1
- package/dist/sessions/store.js +440 -22
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/types.d.ts +75 -17
- package/dist/sessions/types.d.ts.map +1 -1
- package/dist/sessions/types.js +51 -1
- package/dist/sessions/types.js.map +1 -1
- package/dist/triage/actions.d.ts +1 -0
- package/dist/triage/actions.d.ts.map +1 -1
- package/dist/triage/actions.js +34 -7
- package/dist/triage/actions.js.map +1 -1
- package/dist/utils/commit.js +1 -1
- package/dist/utils/commit.js.map +1 -1
- package/dist/web-ui/_app/env.js +1 -0
- package/dist/web-ui/_app/immutable/assets/0.BJaYkGW2.css +1 -0
- package/dist/web-ui/_app/immutable/assets/9.SzGLxi4x.css +1 -0
- package/dist/web-ui/_app/immutable/assets/select-trigger.CV-KWLNP.css +1 -0
- package/dist/web-ui/_app/immutable/chunks/-lc0BifF.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/62JVKtnb.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/8RBjHMN1.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/B5LJFxqa.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/B5wTVqxm.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/B6VSmczZ.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/B8a0xDxR.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/BEOQc37C.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/BHtYorjv.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/BJ0JX3ea.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/BMuCqDX8.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/BP352uRn.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/BUZujXJ2.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/BVA9Exy-.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/BWET-efb.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/BXkNecpt.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/BYzrIfX8.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/BkOJ8DkV.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/BpuwufMc.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/BwMO4RrG.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/BysXJlZb.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/C076q4JN.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/C33JaVbg.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/CGtqifKp.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/CHDZZ7OG.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/CPPfDSei.js +5 -0
- package/dist/web-ui/_app/immutable/chunks/CUir3f4J.js +60 -0
- package/dist/web-ui/_app/immutable/chunks/Cncwi6fQ.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/CrCIbn0C.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/CwELQvbx.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/D3vxvonu.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/D6TVmR9T.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/D7LTux4W.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/D82RulSH.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/D9QNBZM2.js +2 -0
- package/dist/web-ui/_app/immutable/chunks/DAMmvwn4.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/DAh4Wfku.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/DAx07bEQ.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/DBYE9jOd.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/DOno4cA2.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/DQA8NZIH.js +2 -0
- package/dist/web-ui/_app/immutable/chunks/DRfPm2bo.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/DhQhksaB.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/DjG7s6hm.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/DjcCz-PU.js +2 -0
- package/dist/web-ui/_app/immutable/chunks/DkltRNvh.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/DlaTnPKL.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/DvA-KON-.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/DxCk-KHc.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/DzO4hlg9.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/Eo4gF7ih.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/ExCq5swK.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/T3zZGv51.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/XZumBYeP.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/_ySfNjkF.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/iEtR5cV6.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/k_Qegko0.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/pE6cYWlS.js +1 -0
- package/dist/web-ui/_app/immutable/entry/app.Cgu6uKeS.js +2 -0
- package/dist/web-ui/_app/immutable/entry/start.9XifnLoB.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/0.DISwcKSK.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/1.Cx2Ufqp1.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/10.C3z8ijXL.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/11.DZdIjZmM.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/12.FsIGfAOa.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/13.DZoFwagf.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/14.DaIzDKbQ.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/15.BYyt4XWF.js +2 -0
- package/dist/web-ui/_app/immutable/nodes/16.CQkSqpOe.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/2.Bkf_j2UJ.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/3.kaMCurJG.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/4.BSsFPTHG.js +2 -0
- package/dist/web-ui/_app/immutable/nodes/5.CpPlcCEZ.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/6.BN4FqQmY.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/7.9kBYIZik.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/8.BuijtZ6B.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/9.C-Weba8R.js +1 -0
- package/dist/web-ui/_app/version.json +1 -0
- package/dist/web-ui/index.html +39 -0
- package/dist/web-ui/robots.txt +3 -0
- package/package.json +4 -2
- package/plugin/.claude-plugin/marketplace.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/plugins/kspec/skills/task-work/SKILL.md +25 -2
- package/templates/agents-sections/06-ralph-loop.md +64 -11
- package/templates/skills/task-work/SKILL.md +25 -2
- package/dist/ralph/cli-renderer.d.ts +0 -27
- package/dist/ralph/cli-renderer.d.ts.map +0 -1
- package/dist/ralph/cli-renderer.js +0 -250
- package/dist/ralph/cli-renderer.js.map +0 -1
- package/dist/ralph/events.d.ts +0 -65
- package/dist/ralph/events.d.ts.map +0 -1
- package/dist/ralph/events.js +0 -600
- package/dist/ralph/events.js.map +0 -1
- package/dist/ralph/index.d.ts +0 -11
- package/dist/ralph/index.d.ts.map +0 -1
- package/dist/ralph/index.js +0 -16
- package/dist/ralph/index.js.map +0 -1
- package/dist/ralph/loop-errors.d.ts +0 -83
- package/dist/ralph/loop-errors.d.ts.map +0 -1
- package/dist/ralph/loop-errors.js +0 -150
- package/dist/ralph/loop-errors.js.map +0 -1
- package/dist/ralph/subagent.d.ts +0 -127
- package/dist/ralph/subagent.d.ts.map +0 -1
- package/dist/ralph/subagent.js +0 -268
- package/dist/ralph/subagent.js.map +0 -1
- package/dist/ralph/wrap-up.d.ts +0 -127
- package/dist/ralph/wrap-up.d.ts.map +0 -1
- package/dist/ralph/wrap-up.js +0 -271
- package/dist/ralph/wrap-up.js.map +0 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Prompt Building
|
|
3
|
+
*
|
|
4
|
+
* Resolves skills from the skill registry and builds structured prompts
|
|
5
|
+
* for agent invocations. Extracted from ralph.ts to be reusable by both
|
|
6
|
+
* the dispatch engine and CLI one-shot mode.
|
|
7
|
+
*
|
|
8
|
+
* AC: @agent-invocation-lifecycle ac-7
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* A resolved skill with its content loaded.
|
|
12
|
+
*/
|
|
13
|
+
export interface ResolvedSkill {
|
|
14
|
+
/** Skill ID */
|
|
15
|
+
id: string;
|
|
16
|
+
/** Full markdown content of the skill */
|
|
17
|
+
content: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Options for building an agent prompt.
|
|
21
|
+
*/
|
|
22
|
+
export interface BuildPromptOptions {
|
|
23
|
+
/** Base prompt text */
|
|
24
|
+
basePrompt: string;
|
|
25
|
+
/** Skill IDs to resolve and include */
|
|
26
|
+
skillIds: string[];
|
|
27
|
+
/** The .kspec directory for resolving skill content */
|
|
28
|
+
specDir: string;
|
|
29
|
+
/** Adapter identifier used to format cross-skill references */
|
|
30
|
+
adapterId?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Resolve skill content from the skill registry.
|
|
34
|
+
*
|
|
35
|
+
* Skills are stored in .kspec/skills/<id>/SKILL.md.
|
|
36
|
+
* Missing skills are silently skipped.
|
|
37
|
+
*
|
|
38
|
+
* AC: @agent-invocation-lifecycle ac-7
|
|
39
|
+
*/
|
|
40
|
+
export declare function resolveSkills(skillIds: string[], specDir: string): Promise<ResolvedSkill[]>;
|
|
41
|
+
/**
|
|
42
|
+
* Build a prompt with resolved skill content appended.
|
|
43
|
+
*
|
|
44
|
+
* When the agent definition specifies skills, their content is resolved
|
|
45
|
+
* from the skill registry and included in the prompt.
|
|
46
|
+
*
|
|
47
|
+
* AC: @agent-invocation-lifecycle ac-7
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildPromptWithSkills(options: BuildPromptOptions): Promise<string>;
|
|
50
|
+
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/prompts.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,eAAe;IACf,EAAE,EAAE,MAAM,CAAC;IACX,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,uBAAuB;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA6DD;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,aAAa,EAAE,CAAC,CAc1B;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CA2BxF"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Prompt Building
|
|
3
|
+
*
|
|
4
|
+
* Resolves skills from the skill registry and builds structured prompts
|
|
5
|
+
* for agent invocations. Extracted from ralph.ts to be reusable by both
|
|
6
|
+
* the dispatch engine and CLI one-shot mode.
|
|
7
|
+
*
|
|
8
|
+
* AC: @agent-invocation-lifecycle ac-7
|
|
9
|
+
*/
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
import * as fs from "node:fs/promises";
|
|
12
|
+
import { resolveSkillReferenceTokensForPlatform } from "../parser/skill-render.js";
|
|
13
|
+
function getSkillReferencePlatform(adapterId) {
|
|
14
|
+
switch (adapterId) {
|
|
15
|
+
case "claude-agent-acp":
|
|
16
|
+
case "claude-code-acp":
|
|
17
|
+
return "claude-code";
|
|
18
|
+
case "codex-acp":
|
|
19
|
+
return "codex";
|
|
20
|
+
default:
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function getProjectRootFromSpecDir(specDir) {
|
|
25
|
+
return path.basename(specDir) === ".kspec" ? path.dirname(specDir) : specDir;
|
|
26
|
+
}
|
|
27
|
+
async function loadKspecSkillIds(specDir) {
|
|
28
|
+
const projectRoot = getProjectRootFromSpecDir(specDir);
|
|
29
|
+
const skillsDir = path.join(projectRoot, ".agents", "skills");
|
|
30
|
+
try {
|
|
31
|
+
const entries = await fs.readdir(skillsDir, { withFileTypes: true });
|
|
32
|
+
const ids = new Set();
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
if (!entry.isDirectory()) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (!entry.name.startsWith("kspec-")) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
ids.add(entry.name.slice("kspec-".length));
|
|
41
|
+
}
|
|
42
|
+
return ids;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return new Set();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function rewriteSkillReferences(text, specDir, adapterId) {
|
|
49
|
+
const platform = getSkillReferencePlatform(adapterId);
|
|
50
|
+
if (!platform) {
|
|
51
|
+
return text;
|
|
52
|
+
}
|
|
53
|
+
const kspecSkillIds = await loadKspecSkillIds(specDir);
|
|
54
|
+
const skillOrigins = new Map();
|
|
55
|
+
for (const skillId of kspecSkillIds) {
|
|
56
|
+
skillOrigins.set(skillId, "core");
|
|
57
|
+
}
|
|
58
|
+
return resolveSkillReferenceTokensForPlatform(text, platform, skillOrigins);
|
|
59
|
+
}
|
|
60
|
+
// ─── Skill Resolution ─────────────────────────────────────────────────────────
|
|
61
|
+
/**
|
|
62
|
+
* Resolve skill content from the skill registry.
|
|
63
|
+
*
|
|
64
|
+
* Skills are stored in .kspec/skills/<id>/SKILL.md.
|
|
65
|
+
* Missing skills are silently skipped.
|
|
66
|
+
*
|
|
67
|
+
* AC: @agent-invocation-lifecycle ac-7
|
|
68
|
+
*/
|
|
69
|
+
export async function resolveSkills(skillIds, specDir) {
|
|
70
|
+
const resolved = [];
|
|
71
|
+
for (const skillId of skillIds) {
|
|
72
|
+
const contentPath = path.join(specDir, "skills", skillId, "SKILL.md");
|
|
73
|
+
try {
|
|
74
|
+
const content = await fs.readFile(contentPath, "utf-8");
|
|
75
|
+
resolved.push({ id: skillId, content });
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Skill not found — skip silently
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return resolved;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Build a prompt with resolved skill content appended.
|
|
85
|
+
*
|
|
86
|
+
* When the agent definition specifies skills, their content is resolved
|
|
87
|
+
* from the skill registry and included in the prompt.
|
|
88
|
+
*
|
|
89
|
+
* AC: @agent-invocation-lifecycle ac-7
|
|
90
|
+
*/
|
|
91
|
+
export async function buildPromptWithSkills(options) {
|
|
92
|
+
const { basePrompt, skillIds, specDir, adapterId, } = options;
|
|
93
|
+
if (skillIds.length === 0) {
|
|
94
|
+
return basePrompt;
|
|
95
|
+
}
|
|
96
|
+
const resolvedSkills = await resolveSkills(skillIds, specDir);
|
|
97
|
+
if (resolvedSkills.length === 0) {
|
|
98
|
+
return basePrompt;
|
|
99
|
+
}
|
|
100
|
+
const skillSections = resolvedSkills
|
|
101
|
+
.map((skill) => `<!-- Skill: ${skill.id} -->\n${skill.content}`)
|
|
102
|
+
.join("\n\n");
|
|
103
|
+
const promptWithSkills = `${basePrompt}\n\n## Skills\n\n${skillSections}`;
|
|
104
|
+
// AC: @agent-invocation-lifecycle ac-10
|
|
105
|
+
// Rewrite portable {skill:<id>} references to adapter-specific invocation syntax.
|
|
106
|
+
return rewriteSkillReferences(promptWithSkills, specDir, adapterId);
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/agent-runtime/prompts.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spawner.d.ts","sourceRoot":"","sources":["../../src/agents/spawner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,sCAAsC;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,uCAAuC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,yBAAyB;IACzB,aAAa,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,OAAO,GAAG,QAAQ,CAAC,CAAC;CAC5D;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,MAAM,EAAE,SAAS,CAAC;IAClB,+BAA+B;IAC/B,OAAO,EAAE,YAAY,CAAC;IACtB,6BAA6B;IAC7B,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC;CACzC;
|
|
1
|
+
{"version":3,"file":"spawner.d.ts","sourceRoot":"","sources":["../../src/agents/spawner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,sCAAsC;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,uCAAuC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,yBAAyB;IACzB,aAAa,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,OAAO,GAAG,QAAQ,CAAC,CAAC;CAC5D;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,MAAM,EAAE,SAAS,CAAC;IAClB,+BAA+B;IAC/B,OAAO,EAAE,YAAY,CAAC;IACtB,6BAA6B;IAC7B,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC;CACzC;AAmDD;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,iBAAiB,GACzB,YAAY,CA+Dd;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,YAAY,CAAC,CAWvB"}
|
package/dist/agents/spawner.js
CHANGED
|
@@ -6,6 +6,50 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { spawn } from "node:child_process";
|
|
8
8
|
import { ACPClient } from "../acp/index.js";
|
|
9
|
+
const UNEXPECTED_CASE_PREFIX = "Unexpected case:";
|
|
10
|
+
const RATE_LIMIT_EVENT_TYPE = "rate_limit_event";
|
|
11
|
+
function isNonActionableAdapterStderrLine(line) {
|
|
12
|
+
const trimmed = line.trim();
|
|
13
|
+
if (!trimmed.startsWith(UNEXPECTED_CASE_PREFIX))
|
|
14
|
+
return false;
|
|
15
|
+
const payload = trimmed.slice(UNEXPECTED_CASE_PREFIX.length).trim();
|
|
16
|
+
if (!payload)
|
|
17
|
+
return false;
|
|
18
|
+
try {
|
|
19
|
+
const parsed = JSON.parse(payload);
|
|
20
|
+
return parsed.type === RATE_LIMIT_EVENT_TYPE;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// Keep a narrow fallback pattern in case adapter logs malformed JSON.
|
|
24
|
+
return /"type"\s*:\s*"rate_limit_event"/.test(payload);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function forwardFilteredAdapterStderr(child) {
|
|
28
|
+
if (!child.stderr)
|
|
29
|
+
return;
|
|
30
|
+
child.stderr.setEncoding("utf-8");
|
|
31
|
+
let pending = "";
|
|
32
|
+
const forward = (line, withNewline) => {
|
|
33
|
+
if (isNonActionableAdapterStderrLine(line))
|
|
34
|
+
return;
|
|
35
|
+
process.stderr.write(withNewline ? `${line}\n` : line);
|
|
36
|
+
};
|
|
37
|
+
child.stderr.on("data", (chunk) => {
|
|
38
|
+
pending += chunk.toString();
|
|
39
|
+
let newlineIndex = pending.indexOf("\n");
|
|
40
|
+
while (newlineIndex !== -1) {
|
|
41
|
+
const line = pending.slice(0, newlineIndex);
|
|
42
|
+
pending = pending.slice(newlineIndex + 1);
|
|
43
|
+
forward(line, true);
|
|
44
|
+
newlineIndex = pending.indexOf("\n");
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
child.stderr.on("end", () => {
|
|
48
|
+
if (pending.length > 0) {
|
|
49
|
+
forward(pending, false);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
9
53
|
/**
|
|
10
54
|
* Spawn an ACP agent using the specified adapter.
|
|
11
55
|
*
|
|
@@ -31,8 +75,10 @@ export function spawnAgent(adapter, options) {
|
|
|
31
75
|
cwd,
|
|
32
76
|
env: processEnv,
|
|
33
77
|
shell: adapter.shell,
|
|
34
|
-
stdio: ["pipe", "pipe", "
|
|
78
|
+
stdio: ["pipe", "pipe", "pipe"], // pipe all stdio so stderr can be filtered
|
|
35
79
|
});
|
|
80
|
+
// Keep actionable adapter stderr visible while dropping known non-actionable noise.
|
|
81
|
+
forwardFilteredAdapterStderr(child);
|
|
36
82
|
// Ensure stdin/stdout are available
|
|
37
83
|
if (!child.stdin || !child.stdout) {
|
|
38
84
|
child.kill();
|
|
@@ -47,10 +93,20 @@ export function spawnAgent(adapter, options) {
|
|
|
47
93
|
stdin: child.stdout, // We read from child's stdout
|
|
48
94
|
stdout: child.stdin, // We write to child's stdin
|
|
49
95
|
});
|
|
50
|
-
// Forward process exit to client close
|
|
51
|
-
child.on("exit", () => {
|
|
96
|
+
// Forward process exit to client close, surfacing exit code/signal
|
|
97
|
+
child.on("exit", (code, signal) => {
|
|
52
98
|
if (!client.isClosed()) {
|
|
53
|
-
|
|
99
|
+
let reason;
|
|
100
|
+
if (signal !== null) {
|
|
101
|
+
reason = `Subagent process exited with signal ${signal}`;
|
|
102
|
+
}
|
|
103
|
+
else if (code !== null) {
|
|
104
|
+
reason = `Subagent process exited with code ${code}`;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
reason = "Subagent process exited unexpectedly";
|
|
108
|
+
}
|
|
109
|
+
client.close(reason);
|
|
54
110
|
}
|
|
55
111
|
});
|
|
56
112
|
// Kill function with graceful shutdown
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spawner.js","sourceRoot":"","sources":["../../src/agents/spawner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAqB,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAyB,MAAM,iBAAiB,CAAC;AA6BnE;;;;;;;;;GASG;AACH,MAAM,UAAU,UAAU,CACxB,OAAqB,EACrB,OAA0B;IAE1B,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,SAAS,EAAE,aAAa,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC;IAEjE,8BAA8B;IAC9B,MAAM,UAAU,GAAG;QACjB,GAAG,OAAO,CAAC,GAAG;QACd,GAAG,OAAO,CAAC,GAAG;QACd,GAAG,GAAG;KACP,CAAC;IAEF,2DAA2D;IAC3D,MAAM,IAAI,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC;IAErD,0BAA0B;IAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE;QACzC,GAAG;QACH,GAAG,EAAE,UAAU;QACf,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"spawner.js","sourceRoot":"","sources":["../../src/agents/spawner.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batch-exec.d.ts","sourceRoot":"","sources":["../../src/cli/batch-exec.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"batch-exec.d.ts","sourceRoot":"","sources":["../../src/cli/batch-exec.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAkB,MAAM,WAAW,CAAC;AAEzD,OAAO,EAEL,KAAK,YAAY,EAGjB,KAAK,eAAe,EACpB,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAoBtD,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAMrC,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,MAAM,wBAAwB,GAChC,iBAAiB,GACjB,kBAAkB,GAClB,aAAa,GACb,kBAAkB,CAAC;AAEvB,MAAM,WAAW,oBAAoB;IACnC,+BAA+B;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,IAAI,EAAE,wBAAwB,CAAC;IAC/B,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,6CAA6C;IAC7C,KAAK,EAAE,OAAO,CAAC;IACf,mDAAmD;IACnD,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,wBAAwB;IACxB,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC;CAC/C;AA+FD;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,UAAU,CAAC,CA4BrB;AAuGD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,YAAY,EAAE,EACxB,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,qBAAqB,CAkHvB;AAID;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,qBAAqB,EAC7B,IAAI,GAAE,OAAe,GACpB,MAAM,CA6BR;AAID,MAAM,WAAW,mBAAmB;IAClC,qCAAqC;IACrC,MAAM,EAAE,OAAO,CAAC;IAChB,iDAAiD;IACjD,eAAe,EAAE,OAAO,CAAC;IACzB,yBAAyB;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,kBAAkB;IAClB,IAAI,EAAE,OAAO,CAAC;CACf;AAmDD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,GAAG,MAAM,EAAE,CAoFlF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAOnD;AAYD;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,YAAY,EAAE,EACxB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,eAAe,CAAC,CAqD1B"}
|
package/dist/cli/batch-exec.js
CHANGED
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
* mode, and produces helpful error messages.
|
|
7
7
|
*/
|
|
8
8
|
import * as fs from "node:fs/promises";
|
|
9
|
-
import * as path from "node:path";
|
|
10
|
-
import * as os from "node:os";
|
|
11
9
|
import chalk from "chalk";
|
|
12
10
|
import { ZodError } from "zod";
|
|
13
11
|
import { BatchInputSchema, } from "../schema/batch.js";
|
|
@@ -18,6 +16,8 @@ import { findClosestCommand } from "./suggest.js";
|
|
|
18
16
|
import { createBatchCommandFilter } from "./command-annotations.js";
|
|
19
17
|
import { setJsonMode, setVerboseMode } from "./output.js";
|
|
20
18
|
import { BatchExitError, OutputCapture, installExitInterceptor, uninstallExitInterceptor, setBatchMode, } from "./batch-context.js";
|
|
19
|
+
import { activateBatchBuffer, deactivateBatchBuffer, } from "./batch-write-buffer.js";
|
|
20
|
+
const BATCH_STDIN_IDLE_TIMEOUT_MS = 1000;
|
|
21
21
|
// ── Error Types ─────────────────────────────────────────────────────
|
|
22
22
|
export class BatchParseError extends Error {
|
|
23
23
|
constructor(message) {
|
|
@@ -26,6 +26,52 @@ export class BatchParseError extends Error {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
// ── Parsing ─────────────────────────────────────────────────────────
|
|
29
|
+
/**
|
|
30
|
+
* Read stdin with an idle timeout so non-interactive callers that keep
|
|
31
|
+
* stdin open without writing do not block forever.
|
|
32
|
+
*/
|
|
33
|
+
async function readStdinWithIdleTimeout(timeoutMs) {
|
|
34
|
+
return new Promise((resolve) => {
|
|
35
|
+
let data = "";
|
|
36
|
+
let timeout;
|
|
37
|
+
const cleanup = () => {
|
|
38
|
+
if (timeout) {
|
|
39
|
+
clearTimeout(timeout);
|
|
40
|
+
}
|
|
41
|
+
process.stdin.removeListener("data", onData);
|
|
42
|
+
process.stdin.removeListener("end", onEnd);
|
|
43
|
+
process.stdin.removeListener("error", onError);
|
|
44
|
+
};
|
|
45
|
+
const armTimeout = () => {
|
|
46
|
+
if (timeout) {
|
|
47
|
+
clearTimeout(timeout);
|
|
48
|
+
}
|
|
49
|
+
timeout = setTimeout(() => {
|
|
50
|
+
cleanup();
|
|
51
|
+
resolve(data);
|
|
52
|
+
}, timeoutMs);
|
|
53
|
+
timeout.unref?.();
|
|
54
|
+
};
|
|
55
|
+
const onData = (chunk) => {
|
|
56
|
+
data += chunk;
|
|
57
|
+
armTimeout();
|
|
58
|
+
};
|
|
59
|
+
const onEnd = () => {
|
|
60
|
+
cleanup();
|
|
61
|
+
resolve(data);
|
|
62
|
+
};
|
|
63
|
+
const onError = () => {
|
|
64
|
+
cleanup();
|
|
65
|
+
resolve(data);
|
|
66
|
+
};
|
|
67
|
+
process.stdin.setEncoding("utf8");
|
|
68
|
+
process.stdin.on("data", onData);
|
|
69
|
+
process.stdin.on("end", onEnd);
|
|
70
|
+
process.stdin.on("error", onError);
|
|
71
|
+
process.stdin.resume();
|
|
72
|
+
armTimeout();
|
|
73
|
+
});
|
|
74
|
+
}
|
|
29
75
|
/**
|
|
30
76
|
* Read raw text from a batch input source.
|
|
31
77
|
*/
|
|
@@ -41,13 +87,17 @@ async function readSource(source) {
|
|
|
41
87
|
throw new BatchParseError(`Failed to read batch file: ${err.message}`);
|
|
42
88
|
}
|
|
43
89
|
case "stdin": {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
90
|
+
const text = process.stdin.isTTY
|
|
91
|
+
? await (async () => {
|
|
92
|
+
const chunks = [];
|
|
93
|
+
for await (const chunk of process.stdin) {
|
|
94
|
+
chunks.push(chunk);
|
|
95
|
+
}
|
|
96
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
97
|
+
})()
|
|
98
|
+
: await readStdinWithIdleTimeout(BATCH_STDIN_IDLE_TIMEOUT_MS);
|
|
49
99
|
if (!text.trim()) {
|
|
50
|
-
throw new BatchParseError(
|
|
100
|
+
throw new BatchParseError(`No input received on stdin. Provide --commands <json>, --file <path>, or pipe JSON via stdin.`);
|
|
51
101
|
}
|
|
52
102
|
return text;
|
|
53
103
|
}
|
|
@@ -116,6 +166,17 @@ function normalizeArgKey(key) {
|
|
|
116
166
|
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
|
|
117
167
|
.toLowerCase();
|
|
118
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Batch-only argument aliases for common plural forms.
|
|
171
|
+
* Keys and values are canonical kebab-case.
|
|
172
|
+
*/
|
|
173
|
+
const BATCH_ARG_ALIASES = {
|
|
174
|
+
tags: "tag",
|
|
175
|
+
};
|
|
176
|
+
function resolveBatchArgAlias(key) {
|
|
177
|
+
const canonical = normalizeArgKey(key);
|
|
178
|
+
return BATCH_ARG_ALIASES[canonical] ?? canonical;
|
|
179
|
+
}
|
|
119
180
|
/**
|
|
120
181
|
* Get all known argument and option names for a command, returning both
|
|
121
182
|
* kebab-case, camelCase, and underscore variants.
|
|
@@ -231,7 +292,7 @@ export function validateBatchCommands(commands, program, options) {
|
|
|
231
292
|
// Collect all known names as flat array for suggestion matching
|
|
232
293
|
const knownNamesArray = Array.from(knownNames);
|
|
233
294
|
for (const argKey of Object.keys(cmd.args)) {
|
|
234
|
-
if (!knownCanonicalNames.has(
|
|
295
|
+
if (!knownCanonicalNames.has(resolveBatchArgAlias(argKey))) {
|
|
235
296
|
const suggestion = findClosestCommand(argKey, knownNamesArray);
|
|
236
297
|
errors.push({
|
|
237
298
|
index: i,
|
|
@@ -246,7 +307,7 @@ export function validateBatchCommands(commands, program, options) {
|
|
|
246
307
|
// 5. Check for missing required args
|
|
247
308
|
for (const reqName of requiredNames) {
|
|
248
309
|
const normalizedReqName = normalizeArgKey(reqName);
|
|
249
|
-
if (!Object.keys(cmd.args).some((k) =>
|
|
310
|
+
if (!Object.keys(cmd.args).some((k) => resolveBatchArgAlias(k) === normalizedReqName)) {
|
|
250
311
|
errors.push({
|
|
251
312
|
index: i,
|
|
252
313
|
id: cmd.id,
|
|
@@ -314,6 +375,30 @@ function toArgString(value) {
|
|
|
314
375
|
}
|
|
315
376
|
return String(value);
|
|
316
377
|
}
|
|
378
|
+
function normalizePriorityAlias(value) {
|
|
379
|
+
if (Array.isArray(value)) {
|
|
380
|
+
return value.map((v) => normalizePriorityAlias(v));
|
|
381
|
+
}
|
|
382
|
+
if (typeof value !== "string") {
|
|
383
|
+
return value;
|
|
384
|
+
}
|
|
385
|
+
const match = value.match(/^[Pp]([1-5])$/);
|
|
386
|
+
return match ? match[1] : value;
|
|
387
|
+
}
|
|
388
|
+
function isNumericPriorityOption(option) {
|
|
389
|
+
if (!option || normalizeArgKey(option.name) !== "priority") {
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
const hasNumericPlaceholder = /<n(?:umber)?>/.test(option.flags);
|
|
393
|
+
const hasNumericRangeHint = /\b1-5\b/.test(option.description ?? "");
|
|
394
|
+
return hasNumericPlaceholder || hasNumericRangeHint;
|
|
395
|
+
}
|
|
396
|
+
function isNumericPriorityArgument(arg) {
|
|
397
|
+
if (normalizeArgKey(arg.name) !== "priority") {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
return /\b1-5\b/.test(arg.description ?? "");
|
|
401
|
+
}
|
|
317
402
|
export function buildCommandArgv(cmd, cmdMeta) {
|
|
318
403
|
const argv = [...cmd.command.trim().split(/\s+/)];
|
|
319
404
|
const consumedKeys = new Set();
|
|
@@ -331,23 +416,27 @@ export function buildCommandArgv(cmd, cmdMeta) {
|
|
|
331
416
|
flags: opt.flags,
|
|
332
417
|
variadic: opt.variadic,
|
|
333
418
|
name,
|
|
419
|
+
description: opt.description,
|
|
334
420
|
});
|
|
335
421
|
}
|
|
336
422
|
}
|
|
337
423
|
// Phase 1: Emit positional args in Commander definition order
|
|
338
424
|
for (const argDef of positionalDefs) {
|
|
339
425
|
const canonicalName = normalizeArgKey(argDef.name);
|
|
340
|
-
const matchedKey = Object.keys(cmd.args).find((k) =>
|
|
426
|
+
const matchedKey = Object.keys(cmd.args).find((k) => resolveBatchArgAlias(k) === canonicalName);
|
|
341
427
|
const value = matchedKey ? cmd.args[matchedKey] : undefined;
|
|
342
428
|
if (value === undefined)
|
|
343
429
|
continue;
|
|
344
430
|
consumedKeys.add(matchedKey);
|
|
345
|
-
|
|
346
|
-
|
|
431
|
+
const normalizedValue = isNumericPriorityArgument(argDef)
|
|
432
|
+
? normalizePriorityAlias(value)
|
|
433
|
+
: value;
|
|
434
|
+
if (Array.isArray(normalizedValue)) {
|
|
435
|
+
for (const v of normalizedValue)
|
|
347
436
|
argv.push(toArgString(v));
|
|
348
437
|
}
|
|
349
438
|
else {
|
|
350
|
-
argv.push(toArgString(
|
|
439
|
+
argv.push(toArgString(normalizedValue));
|
|
351
440
|
}
|
|
352
441
|
}
|
|
353
442
|
// Phase 2: Emit options from remaining keys
|
|
@@ -355,27 +444,30 @@ export function buildCommandArgv(cmd, cmdMeta) {
|
|
|
355
444
|
if (consumedKeys.has(key)) {
|
|
356
445
|
continue;
|
|
357
446
|
}
|
|
358
|
-
const canonicalKey =
|
|
447
|
+
const canonicalKey = resolveBatchArgAlias(key);
|
|
359
448
|
// Skip positional args (already emitted)
|
|
360
449
|
if (positionalCanonicalNameSet.has(canonicalKey)) {
|
|
361
450
|
continue;
|
|
362
451
|
}
|
|
363
452
|
const knownOption = optionMap.get(canonicalKey);
|
|
364
453
|
const flagName = `--${knownOption?.name ?? canonicalKey}`;
|
|
365
|
-
|
|
366
|
-
|
|
454
|
+
const normalizedValue = isNumericPriorityOption(knownOption)
|
|
455
|
+
? normalizePriorityAlias(value)
|
|
456
|
+
: value;
|
|
457
|
+
if (typeof normalizedValue === "boolean") {
|
|
458
|
+
if (normalizedValue) {
|
|
367
459
|
argv.push(flagName);
|
|
368
460
|
}
|
|
369
461
|
// false booleans: omit (Commander treats absence as false)
|
|
370
462
|
}
|
|
371
|
-
else if (Array.isArray(
|
|
463
|
+
else if (Array.isArray(normalizedValue)) {
|
|
372
464
|
// Variadic or repeated options
|
|
373
|
-
for (const v of
|
|
465
|
+
for (const v of normalizedValue) {
|
|
374
466
|
argv.push(flagName, toArgString(v));
|
|
375
467
|
}
|
|
376
468
|
}
|
|
377
|
-
else if (
|
|
378
|
-
argv.push(flagName, toArgString(
|
|
469
|
+
else if (normalizedValue !== null && normalizedValue !== undefined) {
|
|
470
|
+
argv.push(flagName, toArgString(normalizedValue));
|
|
379
471
|
}
|
|
380
472
|
}
|
|
381
473
|
return argv;
|
|
@@ -464,34 +556,37 @@ export async function executeBatch(commands, program, options) {
|
|
|
464
556
|
}
|
|
465
557
|
}
|
|
466
558
|
/**
|
|
467
|
-
* Atomic execution:
|
|
559
|
+
* Atomic execution: buffer all writes in memory, flush to disk on success.
|
|
560
|
+
*
|
|
561
|
+
* Replaces the old fs.cp(realSpecDir, tempDir) approach with an in-memory
|
|
562
|
+
* write buffer. Writes during batch execution are intercepted by yaml.ts and
|
|
563
|
+
* stored in the buffer. On success, the buffer is flushed to the real specDir.
|
|
564
|
+
* On failure, the buffer is discarded — the real .kspec/ is never touched.
|
|
468
565
|
*
|
|
469
566
|
* AC: @batch-exec ac-default-atomic
|
|
470
567
|
* AC: @batch-exec ac-atomic-rollback
|
|
471
568
|
* AC: @batch-exec ac-atomic-isolation
|
|
472
569
|
* AC: @batch-exec ac-single-commit
|
|
570
|
+
* AC: @batch-write-buffer ac-1 — writes buffered in memory, not on disk
|
|
571
|
+
* AC: @batch-write-buffer ac-4 — rollback discards buffer
|
|
572
|
+
* AC: @batch-write-buffer ac-5 — sessions/ never copied (buffer is per-file)
|
|
573
|
+
* AC: @batch-write-buffer ac-6 — real .kspec/ unchanged until flush
|
|
473
574
|
*/
|
|
474
575
|
async function executeAtomic(commands, program, tree, options) {
|
|
475
|
-
// Get the real context for copy-back
|
|
476
576
|
const ctx = await initContext();
|
|
477
577
|
const realSpecDir = ctx.specDir;
|
|
478
|
-
//
|
|
479
|
-
const
|
|
480
|
-
await fs.cp(realSpecDir, tempDir, { recursive: true });
|
|
481
|
-
// Remove .git from temp copy to prevent worktree pointer leaks
|
|
482
|
-
await fs.rm(path.join(tempDir, ".git"), { force: true, recursive: true });
|
|
578
|
+
// Activate in-memory write buffer — writes to specDir go to buffer
|
|
579
|
+
const buffer = activateBatchBuffer(realSpecDir);
|
|
483
580
|
// Set up atomic context
|
|
484
581
|
const savedChalkLevel = chalk.level;
|
|
485
|
-
const savedSpecDir = process.env.KSPEC_SPEC_DIR;
|
|
486
582
|
const savedBatchProjectRoot = process.env.KSPEC_BATCH_PROJECT_ROOT;
|
|
487
|
-
// AC: @project-config ac-7 — set real project root
|
|
583
|
+
// AC: @project-config ac-7 — set real project root for config resolution
|
|
488
584
|
process.env.KSPEC_BATCH_PROJECT_ROOT = ctx.rootDir;
|
|
489
|
-
process.env.KSPEC_SPEC_DIR = tempDir;
|
|
490
585
|
setBatchMode(true);
|
|
491
586
|
chalk.level = 0;
|
|
492
587
|
const results = [];
|
|
493
588
|
let allSucceeded = true;
|
|
494
|
-
let
|
|
589
|
+
let flushFailed = false;
|
|
495
590
|
try {
|
|
496
591
|
for (let i = 0; i < commands.length; i++) {
|
|
497
592
|
const cmd = commands[i];
|
|
@@ -499,7 +594,7 @@ async function executeAtomic(commands, program, tree, options) {
|
|
|
499
594
|
results.push(result);
|
|
500
595
|
if (!result.success) {
|
|
501
596
|
allSucceeded = false;
|
|
502
|
-
// Atomic mode: stop on first failure, discard
|
|
597
|
+
// Atomic mode: stop on first failure, discard buffer
|
|
503
598
|
// Fill remaining as not-executed
|
|
504
599
|
for (let j = i + 1; j < commands.length; j++) {
|
|
505
600
|
results.push({
|
|
@@ -513,21 +608,12 @@ async function executeAtomic(commands, program, tree, options) {
|
|
|
513
608
|
break;
|
|
514
609
|
}
|
|
515
610
|
}
|
|
516
|
-
//
|
|
611
|
+
// Flush buffer to disk on success
|
|
612
|
+
// AC: @batch-write-buffer ac-3 — only written files flushed
|
|
613
|
+
// AC: @batch-write-buffer ac-7 — flush failure reported, pre-batch state preserved
|
|
517
614
|
if (allSucceeded) {
|
|
518
615
|
try {
|
|
519
|
-
|
|
520
|
-
const entries = await fs.readdir(realSpecDir);
|
|
521
|
-
for (const entry of entries) {
|
|
522
|
-
if (entry === ".git" || entry === ".gitattributes")
|
|
523
|
-
continue;
|
|
524
|
-
await fs.rm(path.join(realSpecDir, entry), { recursive: true, force: true });
|
|
525
|
-
}
|
|
526
|
-
// Copy temp contents back
|
|
527
|
-
const tempEntries = await fs.readdir(tempDir);
|
|
528
|
-
for (const entry of tempEntries) {
|
|
529
|
-
await fs.cp(path.join(tempDir, entry), path.join(realSpecDir, entry), { recursive: true });
|
|
530
|
-
}
|
|
616
|
+
await buffer.flush();
|
|
531
617
|
// Single shadow commit for all changes
|
|
532
618
|
if (ctx.shadow?.enabled) {
|
|
533
619
|
const successCount = results.filter((r) => r.success).length;
|
|
@@ -535,25 +621,21 @@ async function executeAtomic(commands, program, tree, options) {
|
|
|
535
621
|
shadowPushAsync(ctx.shadow.worktreeDir);
|
|
536
622
|
}
|
|
537
623
|
}
|
|
538
|
-
catch (
|
|
539
|
-
|
|
540
|
-
copyBackFailed = true;
|
|
624
|
+
catch (flushErr) {
|
|
625
|
+
flushFailed = true;
|
|
541
626
|
allSucceeded = false;
|
|
542
|
-
console.error(`Batch
|
|
543
|
-
console.error(`Temp dir preserved for recovery: ${tempDir}`);
|
|
627
|
+
console.error(`Batch flush failed: ${flushErr instanceof Error ? flushErr.message : flushErr}`);
|
|
544
628
|
}
|
|
545
629
|
}
|
|
546
630
|
}
|
|
547
631
|
finally {
|
|
548
|
-
//
|
|
632
|
+
// Always deactivate buffer (discard if not flushed)
|
|
633
|
+
if (!allSucceeded || flushFailed) {
|
|
634
|
+
buffer.discard();
|
|
635
|
+
}
|
|
636
|
+
deactivateBatchBuffer();
|
|
549
637
|
setBatchMode(false);
|
|
550
638
|
chalk.level = savedChalkLevel;
|
|
551
|
-
if (savedSpecDir !== undefined) {
|
|
552
|
-
process.env.KSPEC_SPEC_DIR = savedSpecDir;
|
|
553
|
-
}
|
|
554
|
-
else {
|
|
555
|
-
delete process.env.KSPEC_SPEC_DIR;
|
|
556
|
-
}
|
|
557
639
|
// AC: @project-config ac-7 — restore batch project root env var
|
|
558
640
|
if (savedBatchProjectRoot !== undefined) {
|
|
559
641
|
process.env.KSPEC_BATCH_PROJECT_ROOT = savedBatchProjectRoot;
|
|
@@ -561,10 +643,6 @@ async function executeAtomic(commands, program, tree, options) {
|
|
|
561
643
|
else {
|
|
562
644
|
delete process.env.KSPEC_BATCH_PROJECT_ROOT;
|
|
563
645
|
}
|
|
564
|
-
// Only remove temp dir if copy-back succeeded (or commands failed)
|
|
565
|
-
if (!copyBackFailed) {
|
|
566
|
-
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
567
|
-
}
|
|
568
646
|
}
|
|
569
647
|
const succeeded = results.filter((r) => r.success).length;
|
|
570
648
|
const failed = results.filter((r) => !r.success).length;
|