@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../../src/parser/yaml.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../../src/parser/yaml.ts"],"names":[],"mappings":"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"}
|
package/dist/parser/yaml.js
CHANGED
|
@@ -4,6 +4,7 @@ import * as path from "node:path";
|
|
|
4
4
|
import { ulid } from "ulid";
|
|
5
5
|
import * as YAML from "yaml";
|
|
6
6
|
import { withFileLock } from "./file-lock.js";
|
|
7
|
+
import { accessBufferAware, getActiveBatchBuffer, readdirBufferAware, } from "../cli/batch-write-buffer.js";
|
|
7
8
|
import { InboxFileSchema, InboxItemSchema, ManifestSchema, SpecItemSchema, TaskSchema, TasksFileSchema, TriageFileSchema, TriageRecordSchema, } from "../schema/index.js";
|
|
8
9
|
import { errors } from "../strings/index.js";
|
|
9
10
|
import { ItemIndex } from "./items.js";
|
|
@@ -68,10 +69,32 @@ export function toYaml(obj) {
|
|
|
68
69
|
return yamlString;
|
|
69
70
|
}
|
|
70
71
|
/**
|
|
71
|
-
* Read
|
|
72
|
+
* Read a text file with batch buffer overlay semantics.
|
|
73
|
+
*/
|
|
74
|
+
export async function readFileBufferAware(filePath) {
|
|
75
|
+
// AC: @batch-write-buffer ac-2 — check buffer first for read-after-write consistency
|
|
76
|
+
const buffer = getActiveBatchBuffer();
|
|
77
|
+
if (buffer?.isInScope(filePath)) {
|
|
78
|
+
const buffered = buffer.read(filePath);
|
|
79
|
+
if (buffered !== undefined) {
|
|
80
|
+
if (buffered === null) {
|
|
81
|
+
// File was deleted in this batch
|
|
82
|
+
throw Object.assign(new Error(`ENOENT: no such file or directory, open '${filePath}'`), {
|
|
83
|
+
code: "ENOENT",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return typeof buffered === "string"
|
|
87
|
+
? buffered
|
|
88
|
+
: Buffer.from(buffered).toString("utf-8");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return fs.readFile(filePath, "utf-8");
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Read and parse a YAML file.
|
|
72
95
|
*/
|
|
73
96
|
export async function readYamlFile(filePath) {
|
|
74
|
-
const content = await
|
|
97
|
+
const content = await readFileBufferAware(filePath);
|
|
75
98
|
return parseYaml(content);
|
|
76
99
|
}
|
|
77
100
|
/**
|
|
@@ -79,6 +102,12 @@ export async function readYamlFile(filePath) {
|
|
|
79
102
|
*/
|
|
80
103
|
export async function writeYamlFile(filePath, data) {
|
|
81
104
|
const content = toYaml(data);
|
|
105
|
+
// AC: @batch-write-buffer ac-1 — buffer write if in batch mode
|
|
106
|
+
const buffer = getActiveBatchBuffer();
|
|
107
|
+
if (buffer?.isInScope(filePath)) {
|
|
108
|
+
buffer.write(filePath, content);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
82
111
|
await fs.writeFile(filePath, content, "utf-8");
|
|
83
112
|
}
|
|
84
113
|
/**
|
|
@@ -90,6 +119,12 @@ export async function writeYamlFile(filePath, data) {
|
|
|
90
119
|
*/
|
|
91
120
|
export async function writeYamlFilePreserveFormat(filePath, data) {
|
|
92
121
|
const content = toYaml(data);
|
|
122
|
+
// AC: @batch-write-buffer ac-1 — buffer write if in batch mode
|
|
123
|
+
const buffer = getActiveBatchBuffer();
|
|
124
|
+
if (buffer?.isInScope(filePath)) {
|
|
125
|
+
buffer.write(filePath, content);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
93
128
|
await fs.writeFile(filePath, content, "utf-8");
|
|
94
129
|
}
|
|
95
130
|
/**
|
|
@@ -98,7 +133,7 @@ export async function writeYamlFilePreserveFormat(filePath, data) {
|
|
|
98
133
|
export async function findTaskFiles(dir) {
|
|
99
134
|
const files = [];
|
|
100
135
|
try {
|
|
101
|
-
const entries = await
|
|
136
|
+
const entries = await readdirBufferAware(dir, { withFileTypes: true });
|
|
102
137
|
for (const entry of entries) {
|
|
103
138
|
const fullPath = path.join(dir, entry.name);
|
|
104
139
|
if (entry.isDirectory()) {
|
|
@@ -292,7 +327,7 @@ async function findManifestInDir(dir) {
|
|
|
292
327
|
for (const candidate of priorityCandidates) {
|
|
293
328
|
const filePath = path.join(dir, candidate);
|
|
294
329
|
try {
|
|
295
|
-
await
|
|
330
|
+
await accessBufferAware(filePath);
|
|
296
331
|
return filePath;
|
|
297
332
|
}
|
|
298
333
|
catch {
|
|
@@ -301,7 +336,7 @@ async function findManifestInDir(dir) {
|
|
|
301
336
|
}
|
|
302
337
|
// AC: @manifest-discovery ac-3, ac-4, ac-5 - glob fallback with validation
|
|
303
338
|
try {
|
|
304
|
-
const entries = await
|
|
339
|
+
const entries = await readdirBufferAware(dir);
|
|
305
340
|
// AC: @manifest-discovery ac-4 - alphabetical order
|
|
306
341
|
const candidates = entries.filter(isManifestCandidate).sort();
|
|
307
342
|
for (const candidate of candidates) {
|
|
@@ -388,7 +423,7 @@ export async function loadAllTasks(ctx) {
|
|
|
388
423
|
];
|
|
389
424
|
for (const loc of standaloneLocations) {
|
|
390
425
|
try {
|
|
391
|
-
await
|
|
426
|
+
await accessBufferAware(loc);
|
|
392
427
|
if (!taskFiles.includes(loc)) {
|
|
393
428
|
taskFiles.push(loc);
|
|
394
429
|
}
|
|
@@ -426,7 +461,7 @@ export async function loadAllTasks(ctx) {
|
|
|
426
461
|
];
|
|
427
462
|
for (const loc of standaloneLocations) {
|
|
428
463
|
try {
|
|
429
|
-
await
|
|
464
|
+
await accessBufferAware(loc);
|
|
430
465
|
if (!taskFiles.includes(loc)) {
|
|
431
466
|
taskFiles.push(loc);
|
|
432
467
|
}
|
|
@@ -556,6 +591,58 @@ export async function saveTask(ctx, task) {
|
|
|
556
591
|
}
|
|
557
592
|
});
|
|
558
593
|
}
|
|
594
|
+
/**
|
|
595
|
+
* Atomically mutate a task using the latest on-disk state.
|
|
596
|
+
*
|
|
597
|
+
* The callback receives the current task value while holding the task file lock,
|
|
598
|
+
* so concurrent writers cannot clobber unrelated fields (for example status vs notes).
|
|
599
|
+
*/
|
|
600
|
+
export async function mutateTaskAtomically(ctx, task, mutate) {
|
|
601
|
+
const taskFilePath = task._sourceFile || getDefaultTaskFilePath(ctx);
|
|
602
|
+
let updatedTask;
|
|
603
|
+
await withFileLock(taskFilePath, async () => {
|
|
604
|
+
// Ensure directory exists (important for default path in new repos)
|
|
605
|
+
const dir = path.dirname(taskFilePath);
|
|
606
|
+
await fs.mkdir(dir, { recursive: true });
|
|
607
|
+
// Preserve existing file format (tasks wrapper vs plain array)
|
|
608
|
+
let existingRaw = null;
|
|
609
|
+
let useTasksWrapper = false;
|
|
610
|
+
try {
|
|
611
|
+
existingRaw = await readYamlFile(taskFilePath);
|
|
612
|
+
if (existingRaw &&
|
|
613
|
+
typeof existingRaw === "object" &&
|
|
614
|
+
"tasks" in existingRaw) {
|
|
615
|
+
useTasksWrapper = true;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
catch {
|
|
619
|
+
throw new Error(`Task file not found: ${taskFilePath}`);
|
|
620
|
+
}
|
|
621
|
+
const fileTasks = await loadTasksFromFile(taskFilePath);
|
|
622
|
+
const taskIndex = fileTasks.findIndex((t) => t._ulid === task._ulid);
|
|
623
|
+
if (taskIndex === -1) {
|
|
624
|
+
throw new Error(`Task not found in file: ${task._ulid}`);
|
|
625
|
+
}
|
|
626
|
+
const latestTask = fileTasks[taskIndex];
|
|
627
|
+
const mutatedTask = await mutate(latestTask);
|
|
628
|
+
const cleanMutatedTask = stripRuntimeMetadata(mutatedTask);
|
|
629
|
+
const serializedTasks = fileTasks.map((fileTask, index) => index === taskIndex ? cleanMutatedTask : stripRuntimeMetadata(fileTask));
|
|
630
|
+
if (useTasksWrapper) {
|
|
631
|
+
await writeYamlFilePreserveFormat(taskFilePath, { tasks: serializedTasks });
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
await writeYamlFilePreserveFormat(taskFilePath, serializedTasks);
|
|
635
|
+
}
|
|
636
|
+
updatedTask = {
|
|
637
|
+
...cleanMutatedTask,
|
|
638
|
+
_sourceFile: taskFilePath,
|
|
639
|
+
};
|
|
640
|
+
});
|
|
641
|
+
if (!updatedTask) {
|
|
642
|
+
throw new Error(`Failed to mutate task atomically: ${task._ulid}`);
|
|
643
|
+
}
|
|
644
|
+
return updatedTask;
|
|
645
|
+
}
|
|
559
646
|
/**
|
|
560
647
|
* Delete a task from its source file.
|
|
561
648
|
* Requires _sourceFile to know which file to modify.
|
|
@@ -738,28 +825,34 @@ export function areDependenciesMet(task, allTasks) {
|
|
|
738
825
|
return true;
|
|
739
826
|
}
|
|
740
827
|
/**
|
|
741
|
-
* Check if task is ready (pending + deps met + not blocked)
|
|
828
|
+
* Check if task is ready (pending/needs_work + deps met + not blocked)
|
|
742
829
|
*/
|
|
743
830
|
export function isTaskReady(task, allTasks) {
|
|
744
|
-
if (task.status !== "pending")
|
|
831
|
+
if (task.status !== "pending" && task.status !== "needs_work")
|
|
745
832
|
return false;
|
|
746
833
|
if (task.blocked_by.length > 0)
|
|
747
834
|
return false;
|
|
748
835
|
return areDependenciesMet(task, allTasks);
|
|
749
836
|
}
|
|
750
837
|
/**
|
|
751
|
-
* Get ready tasks (pending + deps met + not blocked), sorted by
|
|
752
|
-
*
|
|
838
|
+
* Get ready tasks (pending/needs_work + deps met + not blocked), sorted by
|
|
839
|
+
* status (needs_work first), then priority, then creation time.
|
|
840
|
+
* Within the same tier, older tasks come first (FIFO).
|
|
753
841
|
*/
|
|
754
842
|
export function getReadyTasks(tasks) {
|
|
755
843
|
return tasks
|
|
756
844
|
.filter((task) => isTaskReady(task, tasks))
|
|
757
845
|
.sort((a, b) => {
|
|
758
|
-
// Primary:
|
|
846
|
+
// Primary: needs_work before pending (fix cycles take priority)
|
|
847
|
+
const statusOrder = (s) => (s === "needs_work" ? 0 : 1);
|
|
848
|
+
const statusDiff = statusOrder(a.status) - statusOrder(b.status);
|
|
849
|
+
if (statusDiff !== 0)
|
|
850
|
+
return statusDiff;
|
|
851
|
+
// Secondary: priority (lower number = higher priority)
|
|
759
852
|
if (a.priority !== b.priority) {
|
|
760
853
|
return a.priority - b.priority;
|
|
761
854
|
}
|
|
762
|
-
//
|
|
855
|
+
// Tertiary: creation time (older first - FIFO within priority)
|
|
763
856
|
return (new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
|
|
764
857
|
});
|
|
765
858
|
}
|
|
@@ -777,7 +870,7 @@ export async function expandIncludePattern(pattern, baseDir) {
|
|
|
777
870
|
// If no glob characters, just return the path if it exists
|
|
778
871
|
if (!pattern.includes("*")) {
|
|
779
872
|
try {
|
|
780
|
-
await
|
|
873
|
+
await accessBufferAware(fullPattern);
|
|
781
874
|
return [fullPattern];
|
|
782
875
|
}
|
|
783
876
|
catch {
|
|
@@ -807,7 +900,7 @@ async function expandGlobRecursive(dir, pattern, result) {
|
|
|
807
900
|
const currentPattern = parts[0];
|
|
808
901
|
const remainingPattern = parts.slice(1).join("/");
|
|
809
902
|
try {
|
|
810
|
-
const entries = await
|
|
903
|
+
const entries = await readdirBufferAware(dir, { withFileTypes: true });
|
|
811
904
|
for (const entry of entries) {
|
|
812
905
|
const matches = matchGlobPart(entry.name, currentPattern);
|
|
813
906
|
if (matches) {
|
|
@@ -950,7 +1043,7 @@ export function extractItemsFromRaw(raw, sourceFile, items = [], currentPath = "
|
|
|
950
1043
|
*/
|
|
951
1044
|
export async function loadSpecFile(filePath) {
|
|
952
1045
|
try {
|
|
953
|
-
const content = await
|
|
1046
|
+
const content = await readFileBufferAware(filePath);
|
|
954
1047
|
const items = [];
|
|
955
1048
|
// Parse all YAML documents in the file (handles files with ---)
|
|
956
1049
|
const documents = YAML.parseAllDocuments(content);
|
|
@@ -1060,6 +1153,12 @@ function stripSpecItemMetadata(item) {
|
|
|
1060
1153
|
const { _sourceFile, _path, ...cleanItem } = item;
|
|
1061
1154
|
return cleanItem;
|
|
1062
1155
|
}
|
|
1156
|
+
function assertSpecItemPatch(updates, operation) {
|
|
1157
|
+
const patch = updates;
|
|
1158
|
+
if ("_sourceFile" in patch || "_path" in patch) {
|
|
1159
|
+
throw new Error(`${operation} expects a patch object, not a full LoadedSpecItem. Pass only intended fields to update.`);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1063
1162
|
/**
|
|
1064
1163
|
* Parse a path string into segments.
|
|
1065
1164
|
* e.g., "features[0].requirements[2]" -> [["features", 0], ["requirements", 2]]
|
|
@@ -1227,6 +1326,7 @@ export async function updateSpecItem(_ctx, item, updates) {
|
|
|
1227
1326
|
if (!item._sourceFile) {
|
|
1228
1327
|
throw new Error("Item has no source file");
|
|
1229
1328
|
}
|
|
1329
|
+
assertSpecItemPatch(updates, "updateSpecItem");
|
|
1230
1330
|
// Lock the file to prevent concurrent read-modify-write races
|
|
1231
1331
|
return withFileLock(item._sourceFile, async () => {
|
|
1232
1332
|
// Load the raw YAML
|
|
@@ -1235,10 +1335,19 @@ export async function updateSpecItem(_ctx, item, updates) {
|
|
|
1235
1335
|
let targetObj;
|
|
1236
1336
|
if (item._path) {
|
|
1237
1337
|
const nav = navigateToPath(raw, item._path);
|
|
1238
|
-
|
|
1239
|
-
|
|
1338
|
+
const candidate = nav?.array[nav.index];
|
|
1339
|
+
if (candidate &&
|
|
1340
|
+
typeof candidate === "object" &&
|
|
1341
|
+
candidate._ulid === item._ulid) {
|
|
1342
|
+
targetObj = candidate;
|
|
1343
|
+
}
|
|
1344
|
+
else {
|
|
1345
|
+
const found = findItemInStructure(raw, item._ulid);
|
|
1346
|
+
if (!found) {
|
|
1347
|
+
throw new Error(`Could not find item ${item._ulid} in structure (path: ${item._path})`);
|
|
1348
|
+
}
|
|
1349
|
+
targetObj = found.item;
|
|
1240
1350
|
}
|
|
1241
|
-
targetObj = nav.array[nav.index];
|
|
1242
1351
|
}
|
|
1243
1352
|
else {
|
|
1244
1353
|
// Item might be the root, or we need to find it
|
|
@@ -1336,10 +1445,14 @@ export async function deleteSpecItem(_ctx, item) {
|
|
|
1336
1445
|
* Save a spec item - either updates existing or adds to parent.
|
|
1337
1446
|
* For new items, use addChildItem instead.
|
|
1338
1447
|
*/
|
|
1339
|
-
export async function saveSpecItem(ctx, item) {
|
|
1448
|
+
export async function saveSpecItem(ctx, item, updates) {
|
|
1449
|
+
assertSpecItemPatch(updates, "saveSpecItem");
|
|
1450
|
+
if (Object.keys(updates).length === 0) {
|
|
1451
|
+
throw new Error("Cannot save spec item without updates. Pass a patch.");
|
|
1452
|
+
}
|
|
1340
1453
|
// If item has a source file and path, it's an update
|
|
1341
1454
|
if (item._sourceFile && item._path) {
|
|
1342
|
-
await updateSpecItem(ctx, item,
|
|
1455
|
+
await updateSpecItem(ctx, item, updates);
|
|
1343
1456
|
return;
|
|
1344
1457
|
}
|
|
1345
1458
|
// Otherwise, this is more complex - would need a parent
|
|
@@ -1355,34 +1468,60 @@ export function getInboxFilePath(ctx) {
|
|
|
1355
1468
|
return path.join(ctx.specDir, "project.inbox.yaml");
|
|
1356
1469
|
}
|
|
1357
1470
|
/**
|
|
1358
|
-
*
|
|
1471
|
+
* Parse inbox items from raw YAML payload.
|
|
1472
|
+
*
|
|
1473
|
+
* Supports canonical { inbox: [...] } shape and legacy plain-array shape.
|
|
1359
1474
|
*/
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
const
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
...item,
|
|
1370
|
-
_sourceFile: inboxPath,
|
|
1371
|
-
}));
|
|
1372
|
-
}
|
|
1373
|
-
}
|
|
1374
|
-
// Handle plain array format
|
|
1375
|
-
if (Array.isArray(raw)) {
|
|
1475
|
+
function parseInboxItemsFromRaw(raw) {
|
|
1476
|
+
// Handle { inbox: [...] } format
|
|
1477
|
+
if (raw && typeof raw === "object" && "inbox" in raw) {
|
|
1478
|
+
const parsed = InboxFileSchema.safeParse(raw);
|
|
1479
|
+
if (parsed.success) {
|
|
1480
|
+
return parsed.data.inbox;
|
|
1481
|
+
}
|
|
1482
|
+
const fallbackItems = raw.inbox;
|
|
1483
|
+
if (Array.isArray(fallbackItems)) {
|
|
1376
1484
|
const items = [];
|
|
1377
|
-
for (const item of
|
|
1485
|
+
for (const item of fallbackItems) {
|
|
1378
1486
|
const result = InboxItemSchema.safeParse(item);
|
|
1379
1487
|
if (result.success) {
|
|
1380
|
-
items.push(
|
|
1488
|
+
items.push(result.data);
|
|
1381
1489
|
}
|
|
1382
1490
|
}
|
|
1383
1491
|
return items;
|
|
1384
1492
|
}
|
|
1385
|
-
|
|
1493
|
+
}
|
|
1494
|
+
// Handle plain array format
|
|
1495
|
+
if (Array.isArray(raw)) {
|
|
1496
|
+
const items = [];
|
|
1497
|
+
for (const item of raw) {
|
|
1498
|
+
const result = InboxItemSchema.safeParse(item);
|
|
1499
|
+
if (result.success) {
|
|
1500
|
+
items.push(result.data);
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
return items;
|
|
1504
|
+
}
|
|
1505
|
+
return [];
|
|
1506
|
+
}
|
|
1507
|
+
/**
|
|
1508
|
+
* Load inbox items from an explicit file path.
|
|
1509
|
+
*/
|
|
1510
|
+
async function loadInboxItemsFromFile(inboxPath) {
|
|
1511
|
+
const raw = await readYamlFile(inboxPath);
|
|
1512
|
+
return parseInboxItemsFromRaw(raw);
|
|
1513
|
+
}
|
|
1514
|
+
/**
|
|
1515
|
+
* Load all inbox items from the project.
|
|
1516
|
+
*/
|
|
1517
|
+
export async function loadInboxItems(ctx) {
|
|
1518
|
+
const inboxPath = getInboxFilePath(ctx);
|
|
1519
|
+
try {
|
|
1520
|
+
const items = await loadInboxItemsFromFile(inboxPath);
|
|
1521
|
+
return items.map((item) => ({
|
|
1522
|
+
...item,
|
|
1523
|
+
_sourceFile: inboxPath,
|
|
1524
|
+
}));
|
|
1386
1525
|
}
|
|
1387
1526
|
catch {
|
|
1388
1527
|
// File doesn't exist or parse error
|
|
@@ -1426,21 +1565,7 @@ export async function saveInboxItem(ctx, item) {
|
|
|
1426
1565
|
// Load existing items
|
|
1427
1566
|
let existingItems = [];
|
|
1428
1567
|
try {
|
|
1429
|
-
|
|
1430
|
-
if (raw && typeof raw === "object" && "inbox" in raw) {
|
|
1431
|
-
const parsed = InboxFileSchema.safeParse(raw);
|
|
1432
|
-
if (parsed.success) {
|
|
1433
|
-
existingItems = parsed.data.inbox;
|
|
1434
|
-
}
|
|
1435
|
-
}
|
|
1436
|
-
else if (Array.isArray(raw)) {
|
|
1437
|
-
for (const i of raw) {
|
|
1438
|
-
const result = InboxItemSchema.safeParse(i);
|
|
1439
|
-
if (result.success) {
|
|
1440
|
-
existingItems.push(result.data);
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1568
|
+
existingItems = await loadInboxItemsFromFile(inboxPath);
|
|
1444
1569
|
}
|
|
1445
1570
|
catch {
|
|
1446
1571
|
// File doesn't exist, start fresh
|
|
@@ -1458,6 +1583,48 @@ export async function saveInboxItem(ctx, item) {
|
|
|
1458
1583
|
await writeYamlFilePreserveFormat(inboxPath, { inbox: existingItems });
|
|
1459
1584
|
});
|
|
1460
1585
|
}
|
|
1586
|
+
/**
|
|
1587
|
+
* Atomically mutate an inbox item using the latest on-disk state.
|
|
1588
|
+
*
|
|
1589
|
+
* The callback receives the current item value while holding the inbox file lock,
|
|
1590
|
+
* so concurrent writers do not clobber unrelated fields (for example text vs tags).
|
|
1591
|
+
*/
|
|
1592
|
+
export async function mutateInboxItemAtomically(ctx, item, mutate) {
|
|
1593
|
+
const inboxPath = item._sourceFile || getInboxFilePath(ctx);
|
|
1594
|
+
let updatedItem;
|
|
1595
|
+
await withFileLock(inboxPath, async () => {
|
|
1596
|
+
// Ensure directory exists (important for default path in new repos)
|
|
1597
|
+
const dir = path.dirname(inboxPath);
|
|
1598
|
+
await fs.mkdir(dir, { recursive: true });
|
|
1599
|
+
let existingItems = [];
|
|
1600
|
+
try {
|
|
1601
|
+
existingItems = await loadInboxItemsFromFile(inboxPath);
|
|
1602
|
+
}
|
|
1603
|
+
catch {
|
|
1604
|
+
throw new Error(`Inbox file not found: ${inboxPath}`);
|
|
1605
|
+
}
|
|
1606
|
+
const itemIndex = existingItems.findIndex((existingItem) => existingItem._ulid === item._ulid);
|
|
1607
|
+
if (itemIndex === -1) {
|
|
1608
|
+
throw new Error(`Inbox item not found in file: ${item._ulid}`);
|
|
1609
|
+
}
|
|
1610
|
+
const latestItem = {
|
|
1611
|
+
...existingItems[itemIndex],
|
|
1612
|
+
_sourceFile: inboxPath,
|
|
1613
|
+
};
|
|
1614
|
+
const mutatedItem = await mutate(latestItem);
|
|
1615
|
+
const cleanMutatedItem = stripInboxMetadata(mutatedItem);
|
|
1616
|
+
existingItems[itemIndex] = cleanMutatedItem;
|
|
1617
|
+
await writeYamlFilePreserveFormat(inboxPath, { inbox: existingItems });
|
|
1618
|
+
updatedItem = {
|
|
1619
|
+
...cleanMutatedItem,
|
|
1620
|
+
_sourceFile: inboxPath,
|
|
1621
|
+
};
|
|
1622
|
+
});
|
|
1623
|
+
if (!updatedItem) {
|
|
1624
|
+
throw new Error(`Failed to mutate inbox item atomically: ${item._ulid}`);
|
|
1625
|
+
}
|
|
1626
|
+
return updatedItem;
|
|
1627
|
+
}
|
|
1461
1628
|
/**
|
|
1462
1629
|
* Delete an inbox item by ULID.
|
|
1463
1630
|
*/
|
|
@@ -1466,14 +1633,7 @@ export async function deleteInboxItem(ctx, ulid) {
|
|
|
1466
1633
|
// Lock the file to prevent concurrent read-modify-write races
|
|
1467
1634
|
return withFileLock(inboxPath, async () => {
|
|
1468
1635
|
try {
|
|
1469
|
-
const
|
|
1470
|
-
let existingItems = [];
|
|
1471
|
-
if (raw && typeof raw === "object" && "inbox" in raw) {
|
|
1472
|
-
const parsed = InboxFileSchema.safeParse(raw);
|
|
1473
|
-
if (parsed.success) {
|
|
1474
|
-
existingItems = parsed.data.inbox;
|
|
1475
|
-
}
|
|
1476
|
-
}
|
|
1636
|
+
const existingItems = await loadInboxItemsFromFile(inboxPath);
|
|
1477
1637
|
const index = existingItems.findIndex((i) => i._ulid === ulid);
|
|
1478
1638
|
if (index < 0) {
|
|
1479
1639
|
return false;
|