@kynetic-ai/spec 0.11.0 → 0.12.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 +55 -455
- package/dist/agent-runtime/bootstrap.d.ts +31 -0
- package/dist/agent-runtime/bootstrap.d.ts.map +1 -0
- package/dist/agent-runtime/bootstrap.js +302 -0
- package/dist/agent-runtime/bootstrap.js.map +1 -0
- package/dist/agent-runtime/dispatch.d.ts +119 -10
- package/dist/agent-runtime/dispatch.d.ts.map +1 -1
- package/dist/agent-runtime/dispatch.js +1154 -219
- package/dist/agent-runtime/dispatch.js.map +1 -1
- package/dist/agent-runtime/invocation.d.ts +28 -1
- package/dist/agent-runtime/invocation.d.ts.map +1 -1
- package/dist/agent-runtime/invocation.js +171 -59
- package/dist/agent-runtime/invocation.js.map +1 -1
- package/dist/agent-runtime/prompts.d.ts +9 -0
- package/dist/agent-runtime/prompts.d.ts.map +1 -1
- package/dist/agent-runtime/prompts.js +42 -7
- package/dist/agent-runtime/prompts.js.map +1 -1
- package/dist/agent-runtime/session-event-accumulator.d.ts +83 -0
- package/dist/agent-runtime/session-event-accumulator.d.ts.map +1 -0
- package/dist/agent-runtime/session-event-accumulator.js +203 -0
- package/dist/agent-runtime/session-event-accumulator.js.map +1 -0
- package/dist/agent-runtime/session-event-types.d.ts +67 -0
- package/dist/agent-runtime/session-event-types.d.ts.map +1 -0
- package/dist/agent-runtime/session-event-types.js +13 -0
- package/dist/agent-runtime/session-event-types.js.map +1 -0
- package/dist/agent-runtime/workspace.d.ts +244 -0
- package/dist/agent-runtime/workspace.d.ts.map +1 -0
- package/dist/agent-runtime/workspace.js +2025 -0
- package/dist/agent-runtime/workspace.js.map +1 -0
- package/dist/agents/adapters.d.ts.map +1 -1
- package/dist/agents/adapters.js +58 -13
- package/dist/agents/adapters.js.map +1 -1
- package/dist/agents/spawner.d.ts +8 -0
- package/dist/agents/spawner.d.ts.map +1 -1
- package/dist/agents/spawner.js +25 -3
- package/dist/agents/spawner.js.map +1 -1
- package/dist/cli/batch-exec.js +1 -1
- package/dist/cli/batch-exec.js.map +1 -1
- package/dist/cli/command-annotations.d.ts +15 -3
- package/dist/cli/command-annotations.d.ts.map +1 -1
- package/dist/cli/command-annotations.js +23 -3
- package/dist/cli/command-annotations.js.map +1 -1
- package/dist/cli/commands/agent.d.ts +2 -0
- package/dist/cli/commands/agent.d.ts.map +1 -1
- package/dist/cli/commands/agent.js +144 -27
- package/dist/cli/commands/agent.js.map +1 -1
- package/dist/cli/commands/agents.d.ts.map +1 -1
- package/dist/cli/commands/agents.js +5 -5
- package/dist/cli/commands/agents.js.map +1 -1
- package/dist/cli/commands/derive.d.ts.map +1 -1
- package/dist/cli/commands/derive.js +118 -3
- package/dist/cli/commands/derive.js.map +1 -1
- package/dist/cli/commands/guard.d.ts.map +1 -1
- package/dist/cli/commands/guard.js +8 -6
- package/dist/cli/commands/guard.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/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +20 -0
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/item.d.ts.map +1 -1
- package/dist/cli/commands/item.js +205 -47
- package/dist/cli/commands/item.js.map +1 -1
- package/dist/cli/commands/log.d.ts.map +1 -1
- package/dist/cli/commands/log.js +24 -10
- package/dist/cli/commands/log.js.map +1 -1
- package/dist/cli/commands/plan-import.d.ts +3 -3
- package/dist/cli/commands/plan-import.d.ts.map +1 -1
- package/dist/cli/commands/plan-import.js +213 -528
- 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 +533 -83
- package/dist/cli/commands/plan.js.map +1 -1
- package/dist/cli/commands/review.d.ts +14 -0
- package/dist/cli/commands/review.d.ts.map +1 -0
- package/dist/cli/commands/review.js +1142 -0
- package/dist/cli/commands/review.js.map +1 -0
- package/dist/cli/commands/serve.d.ts +1 -0
- package/dist/cli/commands/serve.d.ts.map +1 -1
- package/dist/cli/commands/serve.js +33 -10
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/session/checkpoint.d.ts +2 -4
- package/dist/cli/commands/session/checkpoint.d.ts.map +1 -1
- package/dist/cli/commands/session/checkpoint.js +6 -107
- 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 +33 -23
- package/dist/cli/commands/session/commands.js.map +1 -1
- package/dist/cli/commands/session/compact.js +4 -4
- package/dist/cli/commands/session/compact.js.map +1 -1
- package/dist/cli/commands/session/create.js +2 -2
- package/dist/cli/commands/session/create.js.map +1 -1
- package/dist/cli/commands/session/format.d.ts.map +1 -1
- package/dist/cli/commands/session/format.js +1 -6
- package/dist/cli/commands/session/format.js.map +1 -1
- package/dist/cli/commands/session/log.d.ts +32 -7
- package/dist/cli/commands/session/log.d.ts.map +1 -1
- package/dist/cli/commands/session/log.js +166 -60
- package/dist/cli/commands/session/log.js.map +1 -1
- package/dist/cli/commands/session/migrate.d.ts +9 -0
- package/dist/cli/commands/session/migrate.d.ts.map +1 -0
- package/dist/cli/commands/session/migrate.js +46 -0
- package/dist/cli/commands/session/migrate.js.map +1 -0
- package/dist/cli/commands/session/stale-close.d.ts.map +1 -1
- package/dist/cli/commands/session/stale-close.js +5 -8
- package/dist/cli/commands/session/stale-close.js.map +1 -1
- package/dist/cli/commands/session/types.d.ts +1 -1
- package/dist/cli/commands/session/types.d.ts.map +1 -1
- package/dist/cli/commands/setup.d.ts +2 -2
- package/dist/cli/commands/setup.d.ts.map +1 -1
- package/dist/cli/commands/setup.js +287 -257
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/shadow.d.ts.map +1 -1
- package/dist/cli/commands/shadow.js +147 -31
- package/dist/cli/commands/shadow.js.map +1 -1
- package/dist/cli/commands/skill-crud.d.ts +7 -0
- package/dist/cli/commands/skill-crud.d.ts.map +1 -1
- package/dist/cli/commands/skill-crud.js +41 -18
- 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 +29 -3
- 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 +5 -4
- 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 +359 -49
- package/dist/cli/commands/task.js.map +1 -1
- package/dist/cli/commands/trait.d.ts.map +1 -1
- package/dist/cli/commands/trait.js +5 -27
- package/dist/cli/commands/trait.js.map +1 -1
- package/dist/cli/commands/validate.d.ts.map +1 -1
- package/dist/cli/commands/validate.js +113 -52
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +69 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/output.d.ts +26 -0
- package/dist/cli/output.d.ts.map +1 -1
- package/dist/cli/output.js +108 -1
- package/dist/cli/output.js.map +1 -1
- package/dist/cli/sync-mode.d.ts +44 -0
- package/dist/cli/sync-mode.d.ts.map +1 -0
- package/dist/cli/sync-mode.js +64 -0
- package/dist/cli/sync-mode.js.map +1 -0
- package/dist/daemon/middleware/project-context.ts +25 -7
- package/dist/daemon/project-context.ts +18 -0
- package/dist/daemon/routes/agent-dispatch.ts +99 -22
- package/dist/daemon/routes/aggregation.ts +184 -0
- package/dist/daemon/routes/inbox.ts +5 -0
- package/dist/daemon/routes/items.ts +145 -0
- package/dist/daemon/routes/meta.ts +1 -1
- package/dist/daemon/routes/projects.ts +28 -6
- package/dist/daemon/routes/ref-resolution.ts +119 -0
- package/dist/daemon/routes/refs.ts +42 -0
- package/dist/daemon/routes/session-related.ts +140 -0
- package/dist/daemon/routes/sessions.ts +420 -19
- package/dist/daemon/routes/tasks.ts +62 -5
- package/dist/daemon/routes/triage.ts +40 -1
- package/dist/daemon/server.ts +143 -49
- package/dist/daemon/session-sync.ts +11 -0
- package/dist/daemon/shadow-sync.ts +11 -0
- package/dist/daemon/watcher.ts +56 -5
- package/dist/daemon/websocket/project-resolution.ts +77 -0
- package/dist/export/json.d.ts.map +1 -1
- package/dist/export/json.js +104 -1
- package/dist/export/json.js.map +1 -1
- package/dist/export/types.d.ts +52 -1
- package/dist/export/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/parser/agent-detection.d.ts +1 -1
- package/dist/parser/agent-detection.d.ts.map +1 -1
- package/dist/parser/agent-detection.js +10 -0
- package/dist/parser/agent-detection.js.map +1 -1
- package/dist/parser/config.d.ts +397 -2
- package/dist/parser/config.d.ts.map +1 -1
- package/dist/parser/config.js +125 -3
- package/dist/parser/config.js.map +1 -1
- package/dist/parser/dispatch-workspaces.d.ts +18 -0
- package/dist/parser/dispatch-workspaces.d.ts.map +1 -0
- package/dist/parser/dispatch-workspaces.js +209 -0
- package/dist/parser/dispatch-workspaces.js.map +1 -0
- package/dist/parser/doctor.d.ts.map +1 -1
- package/dist/parser/doctor.js +27 -8
- package/dist/parser/doctor.js.map +1 -1
- package/dist/parser/file-lock.d.ts.map +1 -1
- package/dist/parser/file-lock.js +9 -2
- package/dist/parser/file-lock.js.map +1 -1
- package/dist/parser/index.d.ts +6 -0
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/index.js +6 -0
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/plans.d.ts.map +1 -1
- package/dist/parser/plans.js +1 -0
- package/dist/parser/plans.js.map +1 -1
- package/dist/parser/refs.d.ts +8 -1
- package/dist/parser/refs.d.ts.map +1 -1
- package/dist/parser/refs.js +27 -1
- package/dist/parser/refs.js.map +1 -1
- package/dist/parser/review-operations.d.ts +72 -0
- package/dist/parser/review-operations.d.ts.map +1 -0
- package/dist/parser/review-operations.js +185 -0
- package/dist/parser/review-operations.js.map +1 -0
- package/dist/parser/review-task-integration.d.ts +78 -0
- package/dist/parser/review-task-integration.d.ts.map +1 -0
- package/dist/parser/review-task-integration.js +173 -0
- package/dist/parser/review-task-integration.js.map +1 -0
- package/dist/parser/review-threads.d.ts +101 -0
- package/dist/parser/review-threads.d.ts.map +1 -0
- package/dist/parser/review-threads.js +222 -0
- package/dist/parser/review-threads.js.map +1 -0
- package/dist/parser/review-validation.d.ts +69 -0
- package/dist/parser/review-validation.d.ts.map +1 -0
- package/dist/parser/review-validation.js +207 -0
- package/dist/parser/review-validation.js.map +1 -0
- package/dist/parser/reviews.d.ts +58 -0
- package/dist/parser/reviews.d.ts.map +1 -0
- package/dist/parser/reviews.js +230 -0
- package/dist/parser/reviews.js.map +1 -0
- package/dist/parser/session-branch.d.ts +91 -0
- package/dist/parser/session-branch.d.ts.map +1 -0
- package/dist/parser/session-branch.js +565 -0
- package/dist/parser/session-branch.js.map +1 -0
- package/dist/parser/session-sync-scheduler.d.ts +53 -0
- package/dist/parser/session-sync-scheduler.d.ts.map +1 -0
- package/dist/parser/session-sync-scheduler.js +100 -0
- package/dist/parser/session-sync-scheduler.js.map +1 -0
- package/dist/parser/setup-status.d.ts +7 -1
- package/dist/parser/setup-status.d.ts.map +1 -1
- package/dist/parser/setup-status.js +104 -39
- package/dist/parser/setup-status.js.map +1 -1
- package/dist/parser/shadow-sync-scheduler.d.ts +71 -0
- package/dist/parser/shadow-sync-scheduler.d.ts.map +1 -0
- package/dist/parser/shadow-sync-scheduler.js +139 -0
- package/dist/parser/shadow-sync-scheduler.js.map +1 -0
- package/dist/parser/shadow.d.ts +121 -14
- package/dist/parser/shadow.d.ts.map +1 -1
- package/dist/parser/shadow.js +752 -27
- package/dist/parser/shadow.js.map +1 -1
- package/dist/parser/skill-render.d.ts +24 -0
- package/dist/parser/skill-render.d.ts.map +1 -1
- package/dist/parser/skill-render.js +98 -26
- package/dist/parser/skill-render.js.map +1 -1
- package/dist/parser/validate.d.ts +43 -3
- package/dist/parser/validate.d.ts.map +1 -1
- package/dist/parser/validate.js +204 -30
- package/dist/parser/validate.js.map +1 -1
- package/dist/parser/yaml.d.ts +47 -11
- package/dist/parser/yaml.d.ts.map +1 -1
- package/dist/parser/yaml.js +329 -149
- package/dist/parser/yaml.js.map +1 -1
- package/dist/review/checks.d.ts +97 -0
- package/dist/review/checks.d.ts.map +1 -0
- package/dist/review/checks.js +175 -0
- package/dist/review/checks.js.map +1 -0
- package/dist/review/index.d.ts +3 -0
- package/dist/review/index.d.ts.map +1 -0
- package/dist/review/index.js +3 -0
- package/dist/review/index.js.map +1 -0
- package/dist/review/subject-bindings.d.ts +83 -0
- package/dist/review/subject-bindings.d.ts.map +1 -0
- package/dist/review/subject-bindings.js +175 -0
- package/dist/review/subject-bindings.js.map +1 -0
- package/dist/schema/common.d.ts +26 -0
- package/dist/schema/common.d.ts.map +1 -1
- package/dist/schema/common.js +13 -0
- package/dist/schema/common.js.map +1 -1
- package/dist/schema/dispatch-workspace.d.ts +2643 -0
- package/dist/schema/dispatch-workspace.d.ts.map +1 -0
- package/dist/schema/dispatch-workspace.js +187 -0
- package/dist/schema/dispatch-workspace.js.map +1 -0
- package/dist/schema/inbox.d.ts +8 -8
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +2 -0
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/meta.d.ts +648 -116
- package/dist/schema/meta.d.ts.map +1 -1
- package/dist/schema/meta.js +27 -0
- package/dist/schema/meta.js.map +1 -1
- package/dist/schema/plan.d.ts +30 -19
- package/dist/schema/plan.d.ts.map +1 -1
- package/dist/schema/plan.js +3 -1
- package/dist/schema/plan.js.map +1 -1
- package/dist/schema/review-records.d.ts +2676 -0
- package/dist/schema/review-records.d.ts.map +1 -0
- package/dist/schema/review-records.js +232 -0
- package/dist/schema/review-records.js.map +1 -0
- package/dist/schema/spec.d.ts +32 -14
- package/dist/schema/spec.d.ts.map +1 -1
- package/dist/schema/spec.js +5 -0
- package/dist/schema/spec.js.map +1 -1
- package/dist/schema/task.d.ts +187 -29
- package/dist/schema/task.d.ts.map +1 -1
- package/dist/schema/task.js +12 -2
- package/dist/schema/task.js.map +1 -1
- package/dist/schema/triage.d.ts +22 -22
- package/dist/sessions/cache.d.ts +119 -0
- package/dist/sessions/cache.d.ts.map +1 -0
- package/dist/sessions/cache.js +284 -0
- package/dist/sessions/cache.js.map +1 -0
- package/dist/sessions/index.d.ts +1 -0
- package/dist/sessions/index.d.ts.map +1 -1
- package/dist/sessions/index.js +2 -0
- package/dist/sessions/index.js.map +1 -1
- package/dist/sessions/legacy.d.ts +77 -0
- package/dist/sessions/legacy.d.ts.map +1 -0
- package/dist/sessions/legacy.js +146 -0
- package/dist/sessions/legacy.js.map +1 -0
- package/dist/sessions/store.d.ts +103 -73
- package/dist/sessions/store.d.ts.map +1 -1
- package/dist/sessions/store.js +335 -186
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/types.d.ts +44 -16
- package/dist/sessions/types.d.ts.map +1 -1
- package/dist/sessions/types.js +11 -2
- package/dist/sessions/types.js.map +1 -1
- package/dist/strings/errors.d.ts +32 -0
- package/dist/strings/errors.d.ts.map +1 -1
- package/dist/strings/errors.js +17 -0
- package/dist/strings/errors.js.map +1 -1
- package/dist/strings/labels.d.ts +1 -0
- package/dist/strings/labels.d.ts.map +1 -1
- package/dist/strings/labels.js +1 -0
- package/dist/strings/labels.js.map +1 -1
- package/dist/utils/activity.d.ts +101 -0
- package/dist/utils/activity.d.ts.map +1 -0
- package/dist/utils/activity.js +408 -0
- package/dist/utils/activity.js.map +1 -0
- package/dist/utils/git.d.ts +31 -0
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +87 -0
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/web-ui/_app/immutable/assets/0.tmlwn-Ih.css +1 -0
- package/dist/web-ui/_app/immutable/assets/9.BwwJybWx.css +1 -0
- package/dist/web-ui/_app/immutable/chunks/2KqE8gtn.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/70-t_QvE.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/AiWQj974.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/{CPPfDSei.js → B25nWFyA.js} +4 -4
- package/dist/web-ui/_app/immutable/chunks/{DBYE9jOd.js → B2bcA_Q_.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/B5e5HYyB.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/B7-5z6eA.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/B7bGmhK0.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/{DzO4hlg9.js → B8tYZKAE.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/{B5LJFxqa.js → BFGAyJjD.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/BG0850zf.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/{DAMmvwn4.js → BG8eSzAd.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/BIMxXS8I.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/BSzL1fpU.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/BYtjHfeq.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/{DxCk-KHc.js → Bp5pFYXL.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/{B8a0xDxR.js → BsJFsuAT.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/BvpNHcD6.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/BypqA25-.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/{BVA9Exy-.js → C0w6WDm5.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/C5_PAZ0y.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/CDRO15Iv.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/CF1CoqD5.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/CS2sa4_m.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/{BJ0JX3ea.js → CWUQwB9H.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/CY5FDdSU.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/C_7MTDoj.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/{D3vxvonu.js → CaAJD3dl.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/{BP352uRn.js → ChB5iyEL.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/{pE6cYWlS.js → ChQD-6N8.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/{Eo4gF7ih.js → CqbsoCwA.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/DCeJW50p.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/{Cncwi6fQ.js → DJtZNgcs.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/DKIeaprD.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/DLd2uVIA.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/{DjcCz-PU.js → DW_subyT.js} +2 -2
- package/dist/web-ui/_app/immutable/chunks/DbU6lVn0.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/Dc7ZCC5m.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/Dd5umPsk.js +2 -0
- package/dist/web-ui/_app/immutable/chunks/{BysXJlZb.js → Dg_zDpDS.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/Dgqu8Yuc.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/DmxsPZTB.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/DphTaFUB.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/DqK4iHp0.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/{D9QNBZM2.js → DqT6OH_u.js} +2 -2
- package/dist/web-ui/_app/immutable/chunks/Ds9I9wQb.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/Du5ng3u4.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/DxJw79Wi.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/GFTX8GgV.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/{C076q4JN.js → HNjs76Zz.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/HVMjDi4_.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/{BkOJ8DkV.js → P0A_fJvS.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/T3vGWjIL.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/VTmrX9Qu.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/{k_Qegko0.js → Xvwhx_F1.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/Yyz1XMQA.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/{62JVKtnb.js → dh5HeqUr.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/fZMteyca.js +62 -0
- package/dist/web-ui/_app/immutable/chunks/{D82RulSH.js → gPrj-hqC.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/htcWMiYN.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/{CwELQvbx.js → oTsvd9y4.js} +1 -1
- package/dist/web-ui/_app/immutable/chunks/qJfLUwU4.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/xCtiO_JE.js +1 -0
- package/dist/web-ui/_app/immutable/chunks/{DvA-KON-.js → y4GeEH6k.js} +1 -1
- package/dist/web-ui/_app/immutable/entry/app.C4h_eOn6.js +2 -0
- package/dist/web-ui/_app/immutable/entry/start.CQFTf9ep.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/0.Dh1xO970.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/1.l75D3Opx.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/10.DBidBPc-.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/11.Ab0gUKWe.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/12.CMsnoxfs.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/13.D8YKuknB.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/14.DZ0aan7y.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/15.CUIKreDL.js +2 -0
- package/dist/web-ui/_app/immutable/nodes/16.BWc8--BO.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/2.CDUonbuh.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/3.Ctg3M00i.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/4.Ci-JDwbA.js +2 -0
- package/dist/web-ui/_app/immutable/nodes/5.CTyEDAq0.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/6.BTZZqsAb.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/7.BI52g_Jo.js +137 -0
- package/dist/web-ui/_app/immutable/nodes/8.3hZPaB9x.js +1 -0
- package/dist/web-ui/_app/immutable/nodes/9.DS49kvwl.js +29 -0
- package/dist/web-ui/_app/version.json +1 -1
- package/dist/web-ui/favicon-192.png +0 -0
- package/dist/web-ui/favicon-32.png +0 -0
- package/dist/web-ui/favicon.ico +0 -0
- package/dist/web-ui/index.html +14 -14
- package/package.json +12 -6
- package/plugin/.claude-plugin/marketplace.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/plugins/kspec/skills/merge/SKILL.md +127 -0
- package/plugin/plugins/kspec/skills/plan/SKILL.md +55 -26
- package/plugin/plugins/kspec/skills/review/SKILL.md +350 -133
- package/plugin/plugins/kspec/skills/task-work/SKILL.md +96 -106
- package/templates/agents-sections/04-pr-workflow.md +15 -12
- package/templates/agents-sections/06-ralph-loop.md +15 -10
- package/templates/skills/manifest.yaml +25 -7
- package/templates/skills/merge/SKILL.md +120 -0
- package/templates/skills/plan/SKILL.md +55 -26
- package/templates/skills/review/SKILL.md +346 -130
- package/templates/skills/task-work/SKILL.md +93 -103
- package/dist/web-ui/_app/immutable/assets/0.BJaYkGW2.css +0 -1
- package/dist/web-ui/_app/immutable/assets/9.SzGLxi4x.css +0 -1
- package/dist/web-ui/_app/immutable/chunks/-lc0BifF.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/8RBjHMN1.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/B5wTVqxm.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/B6VSmczZ.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/BEOQc37C.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/BHtYorjv.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/BMuCqDX8.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/BUZujXJ2.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/BWET-efb.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/BXkNecpt.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/BYzrIfX8.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/BpuwufMc.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/BwMO4RrG.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/C33JaVbg.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/CGtqifKp.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/CHDZZ7OG.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/CUir3f4J.js +0 -60
- package/dist/web-ui/_app/immutable/chunks/CrCIbn0C.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/D6TVmR9T.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/D7LTux4W.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/DAh4Wfku.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/DAx07bEQ.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/DOno4cA2.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/DQA8NZIH.js +0 -2
- package/dist/web-ui/_app/immutable/chunks/DRfPm2bo.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/DhQhksaB.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/DjG7s6hm.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/DkltRNvh.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/DlaTnPKL.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/ExCq5swK.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/T3zZGv51.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/XZumBYeP.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/_ySfNjkF.js +0 -1
- package/dist/web-ui/_app/immutable/chunks/iEtR5cV6.js +0 -1
- package/dist/web-ui/_app/immutable/entry/app.Cgu6uKeS.js +0 -2
- package/dist/web-ui/_app/immutable/entry/start.9XifnLoB.js +0 -1
- package/dist/web-ui/_app/immutable/nodes/0.DISwcKSK.js +0 -1
- package/dist/web-ui/_app/immutable/nodes/1.Cx2Ufqp1.js +0 -1
- package/dist/web-ui/_app/immutable/nodes/10.C3z8ijXL.js +0 -1
- package/dist/web-ui/_app/immutable/nodes/11.DZdIjZmM.js +0 -1
- package/dist/web-ui/_app/immutable/nodes/12.FsIGfAOa.js +0 -1
- package/dist/web-ui/_app/immutable/nodes/13.DZoFwagf.js +0 -1
- package/dist/web-ui/_app/immutable/nodes/14.DaIzDKbQ.js +0 -1
- package/dist/web-ui/_app/immutable/nodes/15.BYyt4XWF.js +0 -2
- package/dist/web-ui/_app/immutable/nodes/16.CQkSqpOe.js +0 -1
- package/dist/web-ui/_app/immutable/nodes/2.Bkf_j2UJ.js +0 -1
- package/dist/web-ui/_app/immutable/nodes/3.kaMCurJG.js +0 -1
- package/dist/web-ui/_app/immutable/nodes/4.BSsFPTHG.js +0 -2
- package/dist/web-ui/_app/immutable/nodes/5.CpPlcCEZ.js +0 -1
- package/dist/web-ui/_app/immutable/nodes/6.BN4FqQmY.js +0 -1
- package/dist/web-ui/_app/immutable/nodes/7.9kBYIZik.js +0 -1
- package/dist/web-ui/_app/immutable/nodes/8.BuijtZ6B.js +0 -1
- package/dist/web-ui/_app/immutable/nodes/9.C-Weba8R.js +0 -1
package/dist/parser/yaml.js
CHANGED
|
@@ -9,8 +9,10 @@ import { InboxFileSchema, InboxItemSchema, ManifestSchema, SpecItemSchema, TaskS
|
|
|
9
9
|
import { errors } from "../strings/index.js";
|
|
10
10
|
import { ItemIndex } from "./items.js";
|
|
11
11
|
import { ReferenceIndex } from "./refs.js";
|
|
12
|
-
import {
|
|
12
|
+
import { loadReviewRecords } from "./reviews.js";
|
|
13
|
+
import { createShadowError, detectRunningFromShadowWorktree, detectShadow, getShadowStatus, hasRemoteTracking, resolveProjectRoots, shadowNeedsSync, shadowPull, ShadowError, } from "./shadow.js";
|
|
13
14
|
import { loadProjectConfig, } from "./config.js";
|
|
15
|
+
import { consumeSyncMode } from "../cli/sync-mode.js";
|
|
14
16
|
import { TraitIndex } from "./traits.js";
|
|
15
17
|
/**
|
|
16
18
|
* Log a debug message (only when KSPEC_DEBUG=1)
|
|
@@ -54,11 +56,98 @@ export function parseYaml(content) {
|
|
|
54
56
|
* causes lines containing only spaces to grow. We post-process the output to filter
|
|
55
57
|
* these whitespace-only lines. See: https://github.com/eemeli/yaml - stringifyString.ts
|
|
56
58
|
*/
|
|
59
|
+
/**
|
|
60
|
+
* Canonical key priority tiers for YAML serialization.
|
|
61
|
+
* Keys are sorted by tier first, then alphabetically within each tier.
|
|
62
|
+
* _ulid is always first (tier 0). Keys not listed default to tier 50.
|
|
63
|
+
*/
|
|
64
|
+
const KEY_PRIORITY = {
|
|
65
|
+
// Tier 0: ULID — always first for record boundary detection
|
|
66
|
+
_ulid: 0,
|
|
67
|
+
// Tier 1: Identity fields
|
|
68
|
+
slugs: 1,
|
|
69
|
+
title: 2,
|
|
70
|
+
type: 3,
|
|
71
|
+
// Tier 2: Content / description
|
|
72
|
+
description: 10,
|
|
73
|
+
text: 10,
|
|
74
|
+
content: 10,
|
|
75
|
+
// Tier 3: Spec relationships
|
|
76
|
+
spec_ref: 15,
|
|
77
|
+
derivation: 16,
|
|
78
|
+
meta_ref: 17,
|
|
79
|
+
plan_ref: 18,
|
|
80
|
+
origin: 19,
|
|
81
|
+
// Tier 4: Status / state
|
|
82
|
+
status: 20,
|
|
83
|
+
maturity: 20,
|
|
84
|
+
blocked_by: 21,
|
|
85
|
+
closed_reason: 22,
|
|
86
|
+
disposition: 23,
|
|
87
|
+
// Tier 5: Relationships
|
|
88
|
+
depends_on: 25,
|
|
89
|
+
context: 26,
|
|
90
|
+
implements: 27,
|
|
91
|
+
relates_to: 28,
|
|
92
|
+
tests: 29,
|
|
93
|
+
traits: 30,
|
|
94
|
+
supersedes: 31,
|
|
95
|
+
acceptance_criteria: 32,
|
|
96
|
+
// Tier 6: Work metadata
|
|
97
|
+
priority: 35,
|
|
98
|
+
complexity: 36,
|
|
99
|
+
tags: 37,
|
|
100
|
+
assignee: 38,
|
|
101
|
+
// Tier 7: VCS / review
|
|
102
|
+
vcs_refs: 40,
|
|
103
|
+
review_url: 41,
|
|
104
|
+
review_ref: 42,
|
|
105
|
+
submission_linkage: 43,
|
|
106
|
+
session_id: 44,
|
|
107
|
+
// Tier 8: Timestamps
|
|
108
|
+
created: 60,
|
|
109
|
+
created_at: 60,
|
|
110
|
+
created_by: 61,
|
|
111
|
+
started_at: 62,
|
|
112
|
+
submitted_at: 63,
|
|
113
|
+
completed_at: 64,
|
|
114
|
+
updated_at: 65,
|
|
115
|
+
acted_at: 66,
|
|
116
|
+
deprecated_in: 67,
|
|
117
|
+
superseded_by: 68,
|
|
118
|
+
verified_at: 69,
|
|
119
|
+
verified_by: 70,
|
|
120
|
+
// Tier 9: Audit / append-only
|
|
121
|
+
notes: 80,
|
|
122
|
+
todos: 81,
|
|
123
|
+
// Tier 10: Automation / config
|
|
124
|
+
automation: 90,
|
|
125
|
+
traceability: 91,
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Compare two YAML map entries for canonical field ordering.
|
|
129
|
+
* _ulid is always first. Known keys are ordered by priority tier,
|
|
130
|
+
* with alphabetical tiebreaking within the same tier.
|
|
131
|
+
* Unknown keys sort after tier 50 (alphabetically among themselves).
|
|
132
|
+
*/
|
|
133
|
+
export function canonicalKeyComparator(a, b) {
|
|
134
|
+
const aKey = String(a.key);
|
|
135
|
+
const bKey = String(b.key);
|
|
136
|
+
const aPriority = KEY_PRIORITY[aKey] ?? 50;
|
|
137
|
+
const bPriority = KEY_PRIORITY[bKey] ?? 50;
|
|
138
|
+
if (aPriority !== bPriority)
|
|
139
|
+
return aPriority - bPriority;
|
|
140
|
+
return aKey.localeCompare(bKey);
|
|
141
|
+
}
|
|
57
142
|
export function toYaml(obj) {
|
|
58
|
-
|
|
143
|
+
// JSON round-trip breaks shared object references so the yaml library
|
|
144
|
+
// won't generate anchors/aliases that crash when sortMapEntries reorders keys.
|
|
145
|
+
// structuredClone preserves shared refs, so JSON.parse(JSON.stringify()) is needed.
|
|
146
|
+
const cloned = JSON.parse(JSON.stringify(obj));
|
|
147
|
+
let yamlString = YAML.stringify(cloned, {
|
|
59
148
|
indent: 2,
|
|
60
149
|
lineWidth: 100,
|
|
61
|
-
sortMapEntries:
|
|
150
|
+
sortMapEntries: canonicalKeyComparator,
|
|
62
151
|
});
|
|
63
152
|
// Post-process to fix yaml library blank line accumulation bug.
|
|
64
153
|
// Filter out lines that contain only spaces/tabs (not truly empty lines).
|
|
@@ -108,7 +197,7 @@ export async function writeYamlFile(filePath, data) {
|
|
|
108
197
|
buffer.write(filePath, content);
|
|
109
198
|
return;
|
|
110
199
|
}
|
|
111
|
-
await
|
|
200
|
+
await writeFileAtomic(filePath, content);
|
|
112
201
|
}
|
|
113
202
|
/**
|
|
114
203
|
* Write object to YAML file while preserving formatting and comments.
|
|
@@ -125,7 +214,14 @@ export async function writeYamlFilePreserveFormat(filePath, data) {
|
|
|
125
214
|
buffer.write(filePath, content);
|
|
126
215
|
return;
|
|
127
216
|
}
|
|
128
|
-
await
|
|
217
|
+
await writeFileAtomic(filePath, content);
|
|
218
|
+
}
|
|
219
|
+
async function writeFileAtomic(filePath, content) {
|
|
220
|
+
const dir = path.dirname(filePath);
|
|
221
|
+
await fs.mkdir(dir, { recursive: true });
|
|
222
|
+
const tmpPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
223
|
+
await fs.writeFile(tmpPath, content, "utf-8");
|
|
224
|
+
await fs.rename(tmpPath, filePath);
|
|
129
225
|
}
|
|
130
226
|
/**
|
|
131
227
|
* Find task files in a directory
|
|
@@ -204,9 +300,10 @@ export async function findManifest(startDir) {
|
|
|
204
300
|
*/
|
|
205
301
|
export async function initContext(startDir) {
|
|
206
302
|
const cwd = startDir || process.cwd();
|
|
303
|
+
const projectRoots = resolveProjectRoots(cwd);
|
|
207
304
|
// AC: @project-config ac-2, ac-6, ac-7 — load config before shadow detection
|
|
208
305
|
// Config is loaded from git root, not cwd or KSPEC_SPEC_DIR temp dir
|
|
209
|
-
const configResult = await loadProjectConfig(cwd);
|
|
306
|
+
const configResult = await loadProjectConfig(cwd, projectRoots?.mainRoot);
|
|
210
307
|
// AC: @project-config ac-3 — emit warning to stderr if config had issues
|
|
211
308
|
if (configResult.warning) {
|
|
212
309
|
console.error(`Warning: ${configResult.warning}`);
|
|
@@ -227,9 +324,12 @@ export async function initContext(startDir) {
|
|
|
227
324
|
// Manifest exists but may be invalid
|
|
228
325
|
}
|
|
229
326
|
}
|
|
327
|
+
const rootDir = path.dirname(specDir);
|
|
230
328
|
return {
|
|
231
|
-
rootDir
|
|
329
|
+
rootDir,
|
|
330
|
+
projectRoot: rootDir,
|
|
232
331
|
specDir,
|
|
332
|
+
sessionsDir: path.join(rootDir, ".kspec-sessions"),
|
|
233
333
|
manifestPath,
|
|
234
334
|
manifest,
|
|
235
335
|
shadow: null, // No shadow in overridden context
|
|
@@ -247,10 +347,54 @@ export async function initContext(startDir) {
|
|
|
247
347
|
const shadow = await detectShadow(cwd, {
|
|
248
348
|
branchName: config.shadow.branch,
|
|
249
349
|
directory: config.shadow.directory,
|
|
250
|
-
});
|
|
350
|
+
}, projectRoots?.mainRoot);
|
|
251
351
|
if (shadow?.enabled) {
|
|
252
352
|
// Shadow mode: use .kspec/ for everything
|
|
253
353
|
const specDir = shadow.worktreeDir;
|
|
354
|
+
// AC: @shadow-lazy-read-sync ac-no-sync-env — KSPEC_NO_SYNC disables all sync
|
|
355
|
+
// AC: @shadow-lazy-read-sync ac-syncmode-consume-once — consume-once prevents double-pull
|
|
356
|
+
// AC: @shadow-lazy-read-sync ac-drift-check — drift check replaces unconditional pull
|
|
357
|
+
if (!process.env.KSPEC_NO_SYNC) {
|
|
358
|
+
const syncMode = consumeSyncMode();
|
|
359
|
+
// AC: @config-shadow ac-3 ac-7 — pass configured shadow options so sync
|
|
360
|
+
// uses the right branch name and remote instead of hardcoded defaults
|
|
361
|
+
const shadowOpts = {
|
|
362
|
+
branchName: config.shadow.branch,
|
|
363
|
+
directory: config.shadow.directory,
|
|
364
|
+
remote: config.shadow.remote?.value,
|
|
365
|
+
remoteType: config.shadow.remote?.type,
|
|
366
|
+
};
|
|
367
|
+
// AC: @shadow-write-sync ac-write-skips-read-check — skip pre-read sync for mutating commands
|
|
368
|
+
if (syncMode !== "skip") {
|
|
369
|
+
try {
|
|
370
|
+
const tracked = await hasRemoteTracking(specDir, shadowOpts);
|
|
371
|
+
if (tracked) {
|
|
372
|
+
let shouldPull = false;
|
|
373
|
+
if (syncMode === "always") {
|
|
374
|
+
// AC: @shadow-lazy-read-sync ac-session-start-always-pulls
|
|
375
|
+
shouldPull = true;
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
// AC: @shadow-lazy-read-sync ac-drift-check — lightweight drift check
|
|
379
|
+
// AC: @shadow-lazy-read-sync ac-threshold-from-config
|
|
380
|
+
const remoteName = config.shadow.remote?.value ?? "origin";
|
|
381
|
+
const thresholdMs = config.shadow.sync_interval * 1000;
|
|
382
|
+
shouldPull = await shadowNeedsSync(specDir, remoteName, thresholdMs);
|
|
383
|
+
}
|
|
384
|
+
if (shouldPull) {
|
|
385
|
+
const syncResult = await shadowPull(specDir, shadowOpts);
|
|
386
|
+
if (syncResult.hadConflict) {
|
|
387
|
+
console.error("Warning: Shadow sync conflict detected. Run `kspec shadow resolve` to fix.");
|
|
388
|
+
console.error("Continuing with local state...");
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
catch {
|
|
394
|
+
// Pre-read sync is best-effort — don't fail the command
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
254
398
|
const manifestPath = await findManifestInDir(specDir);
|
|
255
399
|
let manifest = null;
|
|
256
400
|
if (manifestPath) {
|
|
@@ -263,19 +407,33 @@ export async function initContext(startDir) {
|
|
|
263
407
|
}
|
|
264
408
|
}
|
|
265
409
|
return {
|
|
266
|
-
rootDir: shadow.projectRoot,
|
|
410
|
+
rootDir: projectRoots?.worktreeRoot ?? shadow.projectRoot,
|
|
411
|
+
projectRoot: shadow.projectRoot,
|
|
267
412
|
specDir,
|
|
413
|
+
sessionsDir: path.join(shadow.projectRoot, ".kspec-sessions"),
|
|
268
414
|
manifestPath,
|
|
269
415
|
manifest,
|
|
270
416
|
shadow,
|
|
271
417
|
config,
|
|
272
418
|
};
|
|
273
419
|
}
|
|
420
|
+
// Fail closed when a repo already has shadow state but the configured worktree
|
|
421
|
+
// is missing or disconnected, rather than silently degrading into repo-root mode.
|
|
422
|
+
if (projectRoots?.mainRoot) {
|
|
423
|
+
const shadowStatus = await getShadowStatus(projectRoots.mainRoot, {
|
|
424
|
+
branchName: config.shadow.branch,
|
|
425
|
+
directory: config.shadow.directory,
|
|
426
|
+
});
|
|
427
|
+
if (shadowStatus.branchExists && !shadowStatus.healthy) {
|
|
428
|
+
throw createShadowError(shadowStatus);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
274
431
|
// Traditional mode: find manifest in spec/ or current directory
|
|
275
432
|
const manifestPath = await findManifest(cwd);
|
|
276
433
|
let manifest = null;
|
|
277
|
-
let rootDir = cwd;
|
|
278
|
-
|
|
434
|
+
let rootDir = projectRoots?.worktreeRoot ?? cwd;
|
|
435
|
+
const projectRoot = projectRoots?.mainRoot ?? rootDir;
|
|
436
|
+
let specDir = rootDir;
|
|
279
437
|
if (manifestPath) {
|
|
280
438
|
const manifestDir = path.dirname(manifestPath);
|
|
281
439
|
// Handle spec/ subdirectory
|
|
@@ -295,7 +453,16 @@ export async function initContext(startDir) {
|
|
|
295
453
|
// Manifest exists but may be invalid
|
|
296
454
|
}
|
|
297
455
|
}
|
|
298
|
-
return {
|
|
456
|
+
return {
|
|
457
|
+
rootDir,
|
|
458
|
+
projectRoot,
|
|
459
|
+
specDir,
|
|
460
|
+
sessionsDir: path.join(projectRoot, ".kspec-sessions"),
|
|
461
|
+
manifestPath,
|
|
462
|
+
manifest,
|
|
463
|
+
shadow: null,
|
|
464
|
+
config,
|
|
465
|
+
};
|
|
299
466
|
}
|
|
300
467
|
/**
|
|
301
468
|
* Check if a filename is a potential manifest file.
|
|
@@ -321,7 +488,7 @@ function isManifestCandidate(filename) {
|
|
|
321
488
|
*
|
|
322
489
|
* AC: @manifest-discovery ac-1, ac-2, ac-3, ac-4, ac-5
|
|
323
490
|
*/
|
|
324
|
-
async function findManifestInDir(dir) {
|
|
491
|
+
export async function findManifestInDir(dir) {
|
|
325
492
|
// AC: @manifest-discovery ac-1, ac-2 - explicit names have priority
|
|
326
493
|
const priorityCandidates = ["kynetic.yaml", "kynetic.spec.yaml"];
|
|
327
494
|
for (const candidate of priorityCandidates) {
|
|
@@ -513,9 +680,96 @@ function stripRuntimeMetadata(task) {
|
|
|
513
680
|
const { _sourceFile, ...cleanTask } = task;
|
|
514
681
|
return cleanTask;
|
|
515
682
|
}
|
|
683
|
+
/**
|
|
684
|
+
* Extract the raw task array and format info from a YAML file.
|
|
685
|
+
* Does NOT run schema validation — preserves original data for round-trip stability.
|
|
686
|
+
*/
|
|
687
|
+
async function extractRawTaskArray(filePath) {
|
|
688
|
+
let existingRaw = null;
|
|
689
|
+
let useTasksWrapper = false;
|
|
690
|
+
try {
|
|
691
|
+
existingRaw = await readYamlFile(filePath);
|
|
692
|
+
if (existingRaw &&
|
|
693
|
+
typeof existingRaw === "object" &&
|
|
694
|
+
"tasks" in existingRaw) {
|
|
695
|
+
useTasksWrapper = true;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
catch {
|
|
699
|
+
// File doesn't exist
|
|
700
|
+
return { rawTasks: [], useTasksWrapper: false };
|
|
701
|
+
}
|
|
702
|
+
if (!existingRaw) {
|
|
703
|
+
return { rawTasks: [], useTasksWrapper: false };
|
|
704
|
+
}
|
|
705
|
+
if (Array.isArray(existingRaw)) {
|
|
706
|
+
return { rawTasks: existingRaw, useTasksWrapper: false };
|
|
707
|
+
}
|
|
708
|
+
if (useTasksWrapper) {
|
|
709
|
+
const wrapper = existingRaw;
|
|
710
|
+
const tasks = wrapper.tasks;
|
|
711
|
+
return {
|
|
712
|
+
rawTasks: Array.isArray(tasks) ? tasks : [],
|
|
713
|
+
useTasksWrapper: true,
|
|
714
|
+
wrapperObj: wrapper,
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
return { rawTasks: [], useTasksWrapper: false };
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* Write raw task array back to file, preserving the wrapper format.
|
|
721
|
+
*/
|
|
722
|
+
async function writeRawTaskArray(filePath, rawTasks, useTasksWrapper, wrapperObj) {
|
|
723
|
+
if (useTasksWrapper) {
|
|
724
|
+
// Preserve any extra top-level fields (e.g. kynetic_tasks version)
|
|
725
|
+
const output = wrapperObj ? { ...wrapperObj, tasks: rawTasks } : { tasks: rawTasks };
|
|
726
|
+
await writeYamlFilePreserveFormat(filePath, output);
|
|
727
|
+
}
|
|
728
|
+
else {
|
|
729
|
+
await writeYamlFilePreserveFormat(filePath, rawTasks);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Find task index in a raw array by ULID match.
|
|
734
|
+
*/
|
|
735
|
+
function findRawTaskIndex(rawTasks, ulid) {
|
|
736
|
+
return rawTasks.findIndex((t) => t && typeof t === "object" && t._ulid === ulid);
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Merge a schema-normalized task onto the original raw task data.
|
|
740
|
+
* Only adds fields that were in the original raw data or that contain
|
|
741
|
+
* non-default values. This prevents Zod defaults from polluting YAML
|
|
742
|
+
* output with fields that weren't originally present.
|
|
743
|
+
*
|
|
744
|
+
* Fields present in rawTask are always updated with the new value.
|
|
745
|
+
* Fields NOT in rawTask are only added if they carry meaningful data
|
|
746
|
+
* (i.e. non-empty arrays, non-null values, etc.).
|
|
747
|
+
*/
|
|
748
|
+
function mergeTaskPreservingRawShape(rawTask, normalizedTask) {
|
|
749
|
+
const result = {};
|
|
750
|
+
for (const [key, value] of Object.entries(normalizedTask)) {
|
|
751
|
+
if (key in rawTask) {
|
|
752
|
+
// Field existed in raw — always include (even if value changed)
|
|
753
|
+
result[key] = value;
|
|
754
|
+
}
|
|
755
|
+
else {
|
|
756
|
+
// Field was added by schema normalization — only include if non-trivial
|
|
757
|
+
const isEmptyArray = Array.isArray(value) && value.length === 0;
|
|
758
|
+
const isNull = value === null || value === undefined;
|
|
759
|
+
if (!isEmptyArray && !isNull) {
|
|
760
|
+
result[key] = value;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
return result;
|
|
765
|
+
}
|
|
516
766
|
/**
|
|
517
767
|
* Save a task to its source file (or default location for new tasks).
|
|
518
768
|
* Preserves file format (tasks: [...] wrapper vs plain array).
|
|
769
|
+
*
|
|
770
|
+
* Non-target tasks are preserved as raw data (no schema parsing) to ensure
|
|
771
|
+
* round-trip stability — fields not present in the original YAML won't be
|
|
772
|
+
* added by Zod defaults.
|
|
519
773
|
*/
|
|
520
774
|
export async function saveTask(ctx, task) {
|
|
521
775
|
// Determine target file: use _sourceFile if present, otherwise default
|
|
@@ -525,70 +779,21 @@ export async function saveTask(ctx, task) {
|
|
|
525
779
|
// Ensure directory exists
|
|
526
780
|
const dir = path.dirname(taskFilePath);
|
|
527
781
|
await fs.mkdir(dir, { recursive: true });
|
|
528
|
-
// Load
|
|
529
|
-
|
|
530
|
-
let useTasksWrapper = false;
|
|
531
|
-
try {
|
|
532
|
-
existingRaw = await readYamlFile(taskFilePath);
|
|
533
|
-
// Detect if file uses { tasks: [...] } format
|
|
534
|
-
if (existingRaw &&
|
|
535
|
-
typeof existingRaw === "object" &&
|
|
536
|
-
"tasks" in existingRaw) {
|
|
537
|
-
useTasksWrapper = true;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
catch {
|
|
541
|
-
// File doesn't exist, start fresh
|
|
542
|
-
}
|
|
543
|
-
// Parse existing tasks from file
|
|
544
|
-
let fileTasks = [];
|
|
545
|
-
if (existingRaw) {
|
|
546
|
-
if (Array.isArray(existingRaw)) {
|
|
547
|
-
for (const t of existingRaw) {
|
|
548
|
-
const result = TaskSchema.safeParse(t);
|
|
549
|
-
if (result.success) {
|
|
550
|
-
fileTasks.push(result.data);
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
else if (useTasksWrapper) {
|
|
555
|
-
// Try TasksFileSchema first (has kynetic_tasks version)
|
|
556
|
-
const parsed = TasksFileSchema.safeParse(existingRaw);
|
|
557
|
-
if (parsed.success) {
|
|
558
|
-
fileTasks = parsed.data.tasks;
|
|
559
|
-
}
|
|
560
|
-
else {
|
|
561
|
-
// Fall back to raw tasks array (common format without version field)
|
|
562
|
-
const rawTasks = existingRaw.tasks;
|
|
563
|
-
if (Array.isArray(rawTasks)) {
|
|
564
|
-
for (const t of rawTasks) {
|
|
565
|
-
const result = TaskSchema.safeParse(t);
|
|
566
|
-
if (result.success) {
|
|
567
|
-
fileTasks.push(result.data);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
}
|
|
782
|
+
// Load raw task data without schema normalization
|
|
783
|
+
const { rawTasks, useTasksWrapper, wrapperObj } = await extractRawTaskArray(taskFilePath);
|
|
574
784
|
// Strip runtime metadata before saving
|
|
575
785
|
const cleanTask = stripRuntimeMetadata(task);
|
|
576
|
-
// Update existing or add new
|
|
577
|
-
const existingIndex =
|
|
786
|
+
// Update existing or add new — replace only the target task
|
|
787
|
+
const existingIndex = findRawTaskIndex(rawTasks, task._ulid);
|
|
578
788
|
if (existingIndex >= 0) {
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
fileTasks.push(cleanTask);
|
|
583
|
-
}
|
|
584
|
-
// Save in the same format as original (or tasks: wrapper for new files)
|
|
585
|
-
// Use format-preserving write to maintain formatting and comments
|
|
586
|
-
if (useTasksWrapper) {
|
|
587
|
-
await writeYamlFilePreserveFormat(taskFilePath, { tasks: fileTasks });
|
|
789
|
+
// Merge onto raw data to avoid adding Zod defaults for absent fields
|
|
790
|
+
const rawTarget = rawTasks[existingIndex];
|
|
791
|
+
rawTasks[existingIndex] = mergeTaskPreservingRawShape(rawTarget, cleanTask);
|
|
588
792
|
}
|
|
589
793
|
else {
|
|
590
|
-
|
|
794
|
+
rawTasks.push(cleanTask);
|
|
591
795
|
}
|
|
796
|
+
await writeRawTaskArray(taskFilePath, rawTasks, useTasksWrapper, wrapperObj);
|
|
592
797
|
});
|
|
593
798
|
}
|
|
594
799
|
/**
|
|
@@ -596,6 +801,9 @@ export async function saveTask(ctx, task) {
|
|
|
596
801
|
*
|
|
597
802
|
* The callback receives the current task value while holding the task file lock,
|
|
598
803
|
* so concurrent writers cannot clobber unrelated fields (for example status vs notes).
|
|
804
|
+
*
|
|
805
|
+
* Non-target tasks are preserved as raw data (no schema parsing) to ensure
|
|
806
|
+
* round-trip stability.
|
|
599
807
|
*/
|
|
600
808
|
export async function mutateTaskAtomically(ctx, task, mutate) {
|
|
601
809
|
const taskFilePath = task._sourceFile || getDefaultTaskFilePath(ctx);
|
|
@@ -604,35 +812,24 @@ export async function mutateTaskAtomically(ctx, task, mutate) {
|
|
|
604
812
|
// Ensure directory exists (important for default path in new repos)
|
|
605
813
|
const dir = path.dirname(taskFilePath);
|
|
606
814
|
await fs.mkdir(dir, { recursive: true });
|
|
607
|
-
//
|
|
608
|
-
|
|
609
|
-
|
|
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);
|
|
815
|
+
// Load raw task data without schema normalization for non-target tasks
|
|
816
|
+
const { rawTasks, useTasksWrapper, wrapperObj } = await extractRawTaskArray(taskFilePath);
|
|
817
|
+
const taskIndex = findRawTaskIndex(rawTasks, task._ulid);
|
|
623
818
|
if (taskIndex === -1) {
|
|
624
819
|
throw new Error(`Task not found in file: ${task._ulid}`);
|
|
625
820
|
}
|
|
626
|
-
|
|
821
|
+
// Schema-parse only the target task for the mutation callback
|
|
822
|
+
const rawTarget = rawTasks[taskIndex];
|
|
823
|
+
const parsed = TaskSchema.safeParse(rawTarget);
|
|
824
|
+
if (!parsed.success) {
|
|
825
|
+
throw new Error(`Invalid task data for ${task._ulid}: ${parsed.error.message}`);
|
|
826
|
+
}
|
|
827
|
+
const latestTask = { ...parsed.data, _sourceFile: taskFilePath };
|
|
627
828
|
const mutatedTask = await mutate(latestTask);
|
|
628
829
|
const cleanMutatedTask = stripRuntimeMetadata(mutatedTask);
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
}
|
|
633
|
-
else {
|
|
634
|
-
await writeYamlFilePreserveFormat(taskFilePath, serializedTasks);
|
|
635
|
-
}
|
|
830
|
+
// Merge onto raw data to avoid adding Zod defaults for absent fields
|
|
831
|
+
rawTasks[taskIndex] = mergeTaskPreservingRawShape(rawTarget, cleanMutatedTask);
|
|
832
|
+
await writeRawTaskArray(taskFilePath, rawTasks, useTasksWrapper, wrapperObj);
|
|
636
833
|
updatedTask = {
|
|
637
834
|
...cleanMutatedTask,
|
|
638
835
|
_sourceFile: taskFilePath,
|
|
@@ -654,62 +851,18 @@ export async function deleteTask(_ctx, task) {
|
|
|
654
851
|
const taskFilePath = task._sourceFile;
|
|
655
852
|
// Lock the file to prevent concurrent read-modify-write races
|
|
656
853
|
await withFileLock(taskFilePath, async () => {
|
|
657
|
-
// Load
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
try {
|
|
661
|
-
existingRaw = await readYamlFile(taskFilePath);
|
|
662
|
-
if (existingRaw &&
|
|
663
|
-
typeof existingRaw === "object" &&
|
|
664
|
-
"tasks" in existingRaw) {
|
|
665
|
-
useTasksWrapper = true;
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
catch {
|
|
854
|
+
// Load raw task data without schema normalization for round-trip stability
|
|
855
|
+
const { rawTasks, useTasksWrapper, wrapperObj } = await extractRawTaskArray(taskFilePath);
|
|
856
|
+
if (rawTasks.length === 0) {
|
|
669
857
|
throw new Error(`Task file not found: ${taskFilePath}`);
|
|
670
858
|
}
|
|
671
|
-
//
|
|
672
|
-
|
|
673
|
-
if (
|
|
674
|
-
if (Array.isArray(existingRaw)) {
|
|
675
|
-
for (const t of existingRaw) {
|
|
676
|
-
const result = TaskSchema.safeParse(t);
|
|
677
|
-
if (result.success) {
|
|
678
|
-
fileTasks.push(result.data);
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
else if (useTasksWrapper) {
|
|
683
|
-
const parsed = TasksFileSchema.safeParse(existingRaw);
|
|
684
|
-
if (parsed.success) {
|
|
685
|
-
fileTasks = parsed.data.tasks;
|
|
686
|
-
}
|
|
687
|
-
else {
|
|
688
|
-
const rawTasks = existingRaw.tasks;
|
|
689
|
-
if (Array.isArray(rawTasks)) {
|
|
690
|
-
for (const t of rawTasks) {
|
|
691
|
-
const result = TaskSchema.safeParse(t);
|
|
692
|
-
if (result.success) {
|
|
693
|
-
fileTasks.push(result.data);
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
// Remove the task
|
|
701
|
-
const originalCount = fileTasks.length;
|
|
702
|
-
fileTasks = fileTasks.filter((t) => t._ulid !== task._ulid);
|
|
703
|
-
if (fileTasks.length === originalCount) {
|
|
859
|
+
// Remove the task by ULID match on raw data
|
|
860
|
+
const taskIndex = findRawTaskIndex(rawTasks, task._ulid);
|
|
861
|
+
if (taskIndex === -1) {
|
|
704
862
|
throw new Error(`Task not found in file: ${task._ulid}`);
|
|
705
863
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
await writeYamlFilePreserveFormat(taskFilePath, { tasks: fileTasks });
|
|
709
|
-
}
|
|
710
|
-
else {
|
|
711
|
-
await writeYamlFilePreserveFormat(taskFilePath, fileTasks);
|
|
712
|
-
}
|
|
864
|
+
rawTasks.splice(taskIndex, 1);
|
|
865
|
+
await writeRawTaskArray(taskFilePath, rawTasks, useTasksWrapper, wrapperObj);
|
|
713
866
|
});
|
|
714
867
|
}
|
|
715
868
|
/**
|
|
@@ -1126,7 +1279,8 @@ export function findAnyItemByRef(tasks, items, ref) {
|
|
|
1126
1279
|
export async function buildReferenceIndex(ctx) {
|
|
1127
1280
|
const tasks = await loadAllTasks(ctx);
|
|
1128
1281
|
const items = await loadAllItems(ctx);
|
|
1129
|
-
const
|
|
1282
|
+
const reviews = await loadReviewRecords(ctx);
|
|
1283
|
+
const index = new ReferenceIndex(tasks, items, [], [], reviews);
|
|
1130
1284
|
return { index, tasks, items };
|
|
1131
1285
|
}
|
|
1132
1286
|
/**
|
|
@@ -1138,7 +1292,8 @@ export async function buildReferenceIndex(ctx) {
|
|
|
1138
1292
|
export async function buildIndexes(ctx, plans = []) {
|
|
1139
1293
|
const tasks = await loadAllTasks(ctx);
|
|
1140
1294
|
const items = await loadAllItems(ctx);
|
|
1141
|
-
const
|
|
1295
|
+
const reviews = await loadReviewRecords(ctx);
|
|
1296
|
+
const refIndex = new ReferenceIndex(tasks, items, [], plans, reviews);
|
|
1142
1297
|
const itemIndex = new ItemIndex(tasks, items);
|
|
1143
1298
|
const traitIndex = new TraitIndex(items, refIndex);
|
|
1144
1299
|
return { refIndex, itemIndex, traitIndex, tasks, items };
|
|
@@ -1318,6 +1473,31 @@ export async function addChildItem(_ctx, parent, child, childField) {
|
|
|
1318
1473
|
return { item: cleanChild, path: childPath };
|
|
1319
1474
|
});
|
|
1320
1475
|
}
|
|
1476
|
+
/**
|
|
1477
|
+
* Add a trait item to the project-level traits array in kynetic.yaml.
|
|
1478
|
+
*/
|
|
1479
|
+
export async function addProjectLevelTraitItem(ctx, item) {
|
|
1480
|
+
if (!ctx.manifestPath) {
|
|
1481
|
+
throw new Error("Could not find kynetic.yaml");
|
|
1482
|
+
}
|
|
1483
|
+
return withFileLock(ctx.manifestPath, async () => {
|
|
1484
|
+
const manifest = await readYamlFile(ctx.manifestPath);
|
|
1485
|
+
if (!manifest) {
|
|
1486
|
+
throw new Error("Could not load kynetic.yaml");
|
|
1487
|
+
}
|
|
1488
|
+
if (!Array.isArray(manifest.traits)) {
|
|
1489
|
+
manifest.traits = [];
|
|
1490
|
+
}
|
|
1491
|
+
const cleanItem = stripSpecItemMetadata(item);
|
|
1492
|
+
manifest.traits.push(cleanItem);
|
|
1493
|
+
await writeYamlFilePreserveFormat(ctx.manifestPath, manifest);
|
|
1494
|
+
const traitIndex = manifest.traits.length - 1;
|
|
1495
|
+
return {
|
|
1496
|
+
item: cleanItem,
|
|
1497
|
+
path: `traits[${traitIndex}]`,
|
|
1498
|
+
};
|
|
1499
|
+
});
|
|
1500
|
+
}
|
|
1321
1501
|
/**
|
|
1322
1502
|
* Update a spec item in place within its source file.
|
|
1323
1503
|
* Works with nested structures using the _path field.
|