@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
|
@@ -1,46 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Plan import CLI command
|
|
3
|
-
* AC: @plan-import ac-
|
|
3
|
+
* AC: @plan-import-content-only ac-draft-default through ac-module-stored
|
|
4
4
|
*/
|
|
5
5
|
import * as fs from "node:fs/promises";
|
|
6
6
|
import * as path from "node:path";
|
|
7
7
|
import { markMutating } from "../command-annotations.js";
|
|
8
|
-
import {
|
|
9
|
-
import { parsePlanDocument, topologicalSort, validateParentRefs, } from "../../parser/plan-document.js";
|
|
8
|
+
import { buildIndexes, createPlan, findPlanByRef, getAuthor, initContext, mutatePlanAtomically, loadPlans, savePlan, } from "../../parser/index.js";
|
|
10
9
|
import { commitIfShadow } from "../../parser/shadow.js";
|
|
11
|
-
import {
|
|
10
|
+
import { PlanStatusSchema, } from "../../schema/index.js";
|
|
12
11
|
import { errors } from "../../strings/index.js";
|
|
13
12
|
import { EXIT_CODES } from "../exit-codes.js";
|
|
14
|
-
import { error,
|
|
13
|
+
import { error, isJsonMode, output, success, warn } from "../output.js";
|
|
15
14
|
import { ulid } from "ulid";
|
|
16
15
|
/**
|
|
17
|
-
* Register plan import command
|
|
18
|
-
* AC: @plan-import ac-
|
|
16
|
+
* Register plan import command.
|
|
17
|
+
* AC: @plan-import-content-only ac-module-optional, ac-status-override, ac-update-ignored
|
|
19
18
|
*/
|
|
20
19
|
export function registerPlanImportCommand(planCommand) {
|
|
21
20
|
markMutating(planCommand.command("import <path>"))
|
|
22
|
-
.description("Import plan document
|
|
23
|
-
.
|
|
24
|
-
.option("--
|
|
25
|
-
.option("--
|
|
21
|
+
.description("Import a plan document as stored content without deriving work")
|
|
22
|
+
.option("--into <ref>", "Update an existing draft or approved plan with file content")
|
|
23
|
+
.option("--module <ref>", "Optional module to store on the plan for later derive")
|
|
24
|
+
.option("--status <status>", "Initial plan status (default: draft)")
|
|
25
|
+
.option("--dry-run", "Show the plan record that would be created without saving")
|
|
26
|
+
.option("--update", "Ignored for content-only import; derivation happens separately")
|
|
27
|
+
.option("--reason <text>", "Reason note when updating an existing plan via --into")
|
|
26
28
|
.option("--json", "Output as JSON")
|
|
27
29
|
.addHelpText("after", `
|
|
28
30
|
Format:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
## Specs
|
|
33
|
-
\`\`\`yaml
|
|
34
|
-
- title: My Feature
|
|
35
|
-
type: feature
|
|
36
|
-
\`\`\`
|
|
37
|
-
|
|
38
|
-
Without the fenced code block, specs will not be detected.
|
|
31
|
+
Import stores the markdown document as plan content. Specs and tasks are not
|
|
32
|
+
created during import; use "kspec plan derive" after approval to materialize
|
|
33
|
+
the stored document.
|
|
39
34
|
|
|
40
35
|
Examples:
|
|
41
|
-
$ kspec plan import ./plan.md
|
|
42
|
-
$ kspec plan import ./plan.md --
|
|
43
|
-
$ kspec plan import ./plan.md --module @
|
|
36
|
+
$ kspec plan import ./plan.md
|
|
37
|
+
$ kspec plan import ./plan.md --status approved --json
|
|
38
|
+
$ kspec plan import ./plan.md --module @core --dry-run
|
|
39
|
+
$ kspec plan import ./edited.md --into @plan-ref --reason "Addressed review feedback"`)
|
|
44
40
|
.action(async (planPath, options) => {
|
|
45
41
|
try {
|
|
46
42
|
await importPlan(planPath, options);
|
|
@@ -51,30 +47,12 @@ Examples:
|
|
|
51
47
|
}
|
|
52
48
|
});
|
|
53
49
|
}
|
|
54
|
-
function mergeAcceptanceCriteriaById(existingCriteria, incomingCriteria) {
|
|
55
|
-
if (!existingCriteria || existingCriteria.length === 0) {
|
|
56
|
-
return incomingCriteria;
|
|
57
|
-
}
|
|
58
|
-
const incomingById = new Map(incomingCriteria.map(ac => [ac.id, ac]));
|
|
59
|
-
const existingIds = new Set(existingCriteria.map(ac => ac.id));
|
|
60
|
-
const merged = existingCriteria.map(ac => incomingById.get(ac.id) ?? ac);
|
|
61
|
-
for (const ac of incomingCriteria) {
|
|
62
|
-
if (!existingIds.has(ac.id)) {
|
|
63
|
-
merged.push(ac);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return merged;
|
|
67
|
-
}
|
|
68
50
|
/**
|
|
69
|
-
* Import plan document
|
|
70
|
-
* AC: @plan-import ac-
|
|
51
|
+
* Import plan document as content-only storage.
|
|
52
|
+
* AC: @plan-import-content-only ac-draft-default through ac-module-stored
|
|
71
53
|
*/
|
|
72
54
|
async function importPlan(planPath, options) {
|
|
73
|
-
// JSON mode is set by global preAction hook
|
|
74
55
|
const ctx = await initContext();
|
|
75
|
-
const author = getAuthor(ctx.config?.identity?.author);
|
|
76
|
-
// Read plan file
|
|
77
|
-
// AC: @plan-import ac-21 - Handle file read errors
|
|
78
56
|
const fullPath = path.resolve(process.cwd(), planPath);
|
|
79
57
|
let content;
|
|
80
58
|
try {
|
|
@@ -84,513 +62,220 @@ async function importPlan(planPath, options) {
|
|
|
84
62
|
error(`Failed to read plan file: ${planPath}`, err);
|
|
85
63
|
process.exit(EXIT_CODES.USAGE_ERROR);
|
|
86
64
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
// Report parsing errors
|
|
91
|
-
if (parsed.errors.length > 0) {
|
|
92
|
-
for (const err of parsed.errors) {
|
|
93
|
-
if (err.type === "yaml") {
|
|
94
|
-
// AC: @plan-import ac-21 - YAML parse errors (fatal)
|
|
95
|
-
error(err.message);
|
|
96
|
-
process.exit(EXIT_CODES.USAGE_ERROR);
|
|
97
|
-
}
|
|
98
|
-
else if (err.type === "validation") {
|
|
99
|
-
// AC: @plan-import ac-22 - Validation errors (non-fatal, add to warnings)
|
|
100
|
-
warn(err.message);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
65
|
+
if (options.into) {
|
|
66
|
+
await importIntoExistingPlan(ctx, fullPath, content, options);
|
|
67
|
+
return;
|
|
103
68
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
// Build indexes to check existing specs (include plans for cross-namespace slug safety)
|
|
107
|
-
const { refIndex, items } = await buildIndexes(ctx, plans);
|
|
108
|
-
const existingSpecRefs = new Set();
|
|
109
|
-
for (const item of items) {
|
|
110
|
-
for (const slug of item.slugs || []) {
|
|
111
|
-
existingSpecRefs.add(slug);
|
|
112
|
-
}
|
|
113
|
-
existingSpecRefs.add(refIndex.shortUlid(item._ulid));
|
|
114
|
-
}
|
|
115
|
-
// Resolve module reference
|
|
116
|
-
const moduleResult = refIndex.resolve(options.module);
|
|
117
|
-
if (!moduleResult.ok) {
|
|
118
|
-
error(errors.reference.itemNotFound(options.module));
|
|
119
|
-
process.exit(EXIT_CODES.NOT_FOUND);
|
|
69
|
+
if (options.update) {
|
|
70
|
+
warn("--update is ignored for content-only import. Use `kspec plan derive` to materialize specs and tasks.");
|
|
120
71
|
}
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
error(`${options.module} is not a module (type: ${moduleItem.type})`);
|
|
72
|
+
const statusResult = PlanStatusSchema.safeParse(options.status || "draft");
|
|
73
|
+
if (!statusResult.success) {
|
|
74
|
+
error(`Invalid status: '${options.status}'. Valid values: ${PlanStatusSchema.options.join(", ")}`);
|
|
125
75
|
process.exit(EXIT_CODES.USAGE_ERROR);
|
|
126
76
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
77
|
+
const plans = await loadPlans(ctx);
|
|
78
|
+
const { refIndex } = await buildIndexes(ctx, plans);
|
|
79
|
+
let storedModuleRef = null;
|
|
80
|
+
if (options.module) {
|
|
81
|
+
const moduleResult = refIndex.resolve(options.module);
|
|
82
|
+
if (!moduleResult.ok) {
|
|
83
|
+
error(errors.reference.itemNotFound(options.module));
|
|
84
|
+
process.exit(EXIT_CODES.NOT_FOUND);
|
|
85
|
+
}
|
|
86
|
+
const moduleItem = moduleResult.item;
|
|
87
|
+
if (moduleItem.type !== "module") {
|
|
88
|
+
error(`${options.module} is not a module (type: ${moduleItem.type})`);
|
|
89
|
+
process.exit(EXIT_CODES.USAGE_ERROR);
|
|
90
|
+
}
|
|
91
|
+
storedModuleRef = options.module.startsWith("@")
|
|
92
|
+
? options.module
|
|
93
|
+
: `@${options.module}`;
|
|
138
94
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
95
|
+
const title = extractOptionalPlanTitle(content) ?? "Untitled Plan";
|
|
96
|
+
const planSlug = nextAvailablePlanSlug(title, refIndex);
|
|
97
|
+
const preview = {
|
|
98
|
+
ref: `@${planSlug}`,
|
|
99
|
+
title,
|
|
100
|
+
status: statusResult.data,
|
|
101
|
+
content,
|
|
102
|
+
module_ref: storedModuleRef,
|
|
103
|
+
source_path: fullPath,
|
|
104
|
+
derived_specs: [],
|
|
105
|
+
derived_tasks: [],
|
|
106
|
+
changes: [],
|
|
107
|
+
note_message: null,
|
|
108
|
+
};
|
|
109
|
+
if (options.dryRun) {
|
|
110
|
+
emitImportResult(preview, { dryRun: true });
|
|
111
|
+
return;
|
|
149
112
|
}
|
|
150
|
-
// Track all slugs created during this import (refIndex is stale after mutations)
|
|
151
|
-
const importReservedSlugs = new Set([planSlug]);
|
|
152
113
|
const planInput = {
|
|
153
|
-
title
|
|
154
|
-
content
|
|
155
|
-
status:
|
|
114
|
+
title,
|
|
115
|
+
content,
|
|
116
|
+
status: statusResult.data,
|
|
156
117
|
slugs: [planSlug],
|
|
157
118
|
source_path: fullPath,
|
|
119
|
+
module_ref: storedModuleRef,
|
|
158
120
|
};
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
121
|
+
const plan = createPlan(planInput);
|
|
122
|
+
await saveImportedPlan(ctx, plan, preview);
|
|
123
|
+
}
|
|
124
|
+
async function saveImportedPlan(ctx, plan, preview) {
|
|
125
|
+
await savePlan(ctx, plan);
|
|
126
|
+
await commitIfShadow(ctx.shadow, "plan-import", plan.slugs[0] || plan._ulid.slice(0, 8), plan.title);
|
|
127
|
+
emitImportResult({
|
|
128
|
+
...preview,
|
|
129
|
+
ref: plan.slugs[0] ? `@${plan.slugs[0]}` : `@${plan._ulid}`,
|
|
130
|
+
}, { dryRun: false, createdAt: plan.created_at });
|
|
131
|
+
}
|
|
132
|
+
async function importIntoExistingPlan(ctx, fullPath, content, options) {
|
|
133
|
+
const foundPlan = await findPlanByRef(ctx, options.into);
|
|
134
|
+
if (!foundPlan) {
|
|
135
|
+
exitImportWithGuidance(errors.reference.planNotFound(options.into), EXIT_CODES.NOT_FOUND, "Check available plans with: kspec plan list", { ref: options.into, entity: "plan" });
|
|
169
136
|
}
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
137
|
+
const titleFromFile = extractOptionalPlanTitle(content);
|
|
138
|
+
const nextTitle = titleFromFile ?? foundPlan.title;
|
|
139
|
+
const noteMessage = options.reason || "Content updated from file";
|
|
140
|
+
const preview = {
|
|
141
|
+
ref: foundPlan.slugs[0] ? `@${foundPlan.slugs[0]}` : `@${foundPlan._ulid}`,
|
|
142
|
+
title: nextTitle,
|
|
143
|
+
status: foundPlan.status,
|
|
144
|
+
content,
|
|
145
|
+
module_ref: foundPlan.module_ref ?? null,
|
|
146
|
+
source_path: fullPath,
|
|
147
|
+
derived_specs: [...foundPlan.derived_specs],
|
|
148
|
+
derived_tasks: [...foundPlan.derived_tasks],
|
|
149
|
+
changes: titleFromFile ? ["title", "content"] : ["content"],
|
|
150
|
+
note_message: noteMessage,
|
|
178
151
|
};
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
for (const err of parsed.errors) {
|
|
182
|
-
if (err.type === "validation") {
|
|
183
|
-
result.errors.push({ message: err.message });
|
|
184
|
-
}
|
|
152
|
+
if (options.module) {
|
|
153
|
+
warn("--module is ignored with --into. Existing plan module assignment is unchanged.");
|
|
185
154
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const createdSpecsMap = new Map();
|
|
189
|
-
// Process specs
|
|
190
|
-
// AC: @plan-import ac-14, ac-16, ac-17, ac-22, ac-23, ac-25, ac-26, ac-29
|
|
191
|
-
for (let i = 0; i < sorted.length; i++) {
|
|
192
|
-
const spec = sorted[i];
|
|
193
|
-
try {
|
|
194
|
-
// Validate required fields first
|
|
195
|
-
// AC: @plan-import ac-22
|
|
196
|
-
if (!spec.title) {
|
|
197
|
-
const errMsg = `Spec at index ${i} missing required field: title`;
|
|
198
|
-
warn(errMsg);
|
|
199
|
-
result.errors.push({ message: errMsg, spec });
|
|
200
|
-
continue;
|
|
201
|
-
}
|
|
202
|
-
const specSlug = spec.slug || generateSlug(spec.title);
|
|
203
|
-
const specRef = `@${specSlug}`;
|
|
204
|
-
// Check against slugs reserved during this import (plan slug, earlier specs)
|
|
205
|
-
if (importReservedSlugs.has(specSlug)) {
|
|
206
|
-
warn(`Spec slug "${specSlug}" collides with another item in this import, skipping`);
|
|
207
|
-
result.skipped.push({ slug: specSlug, reason: "Slug collision within import" });
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
// Check if spec already exists in pre-import state
|
|
211
|
-
const exists = refIndex.resolve(specRef);
|
|
212
|
-
if (exists.ok && !options.update) {
|
|
213
|
-
// AC: @plan-import ac-14, ac-25 - Skip existing specs
|
|
214
|
-
warn(`Skipping existing spec: ${specRef}`);
|
|
215
|
-
result.skipped.push({ slug: specSlug, reason: "Already exists" });
|
|
216
|
-
continue;
|
|
217
|
-
}
|
|
218
|
-
if (exists.ok && options.update) {
|
|
219
|
-
// AC: @plan-import ac-26 - Update existing spec
|
|
220
|
-
// Updates: description, type, tags, traits, and acceptance_criteria
|
|
221
|
-
const existingSpec = exists.item;
|
|
222
|
-
if (options.dryRun) {
|
|
223
|
-
info(`Would update spec: ${specRef}`);
|
|
224
|
-
result.updatedSpecs.push(specRef);
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
// Build updates from plan spec
|
|
228
|
-
const updates = {};
|
|
229
|
-
if (spec.description !== undefined) {
|
|
230
|
-
updates.description = spec.description;
|
|
231
|
-
}
|
|
232
|
-
if (spec.type !== undefined) {
|
|
233
|
-
updates.type = spec.type;
|
|
234
|
-
}
|
|
235
|
-
if (spec.traits !== undefined) {
|
|
236
|
-
updates.traits = spec.traits.map(normalizeRefInput);
|
|
237
|
-
}
|
|
238
|
-
if (spec.depends_on !== undefined) {
|
|
239
|
-
updates.depends_on = spec.depends_on.map(normalizeRefInput);
|
|
240
|
-
}
|
|
241
|
-
if (spec.acceptance_criteria !== undefined) {
|
|
242
|
-
updates.acceptance_criteria = mergeAcceptanceCriteriaById(existingSpec.acceptance_criteria, spec.acceptance_criteria);
|
|
243
|
-
}
|
|
244
|
-
await updateSpecItem(ctx, existingSpec, updates);
|
|
245
|
-
info(`Updated spec: ${specRef}`);
|
|
246
|
-
result.updatedSpecs.push(specRef);
|
|
247
|
-
}
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
// Resolve parent reference
|
|
251
|
-
// AC: @plan-import ac-16, ac-17
|
|
252
|
-
let parent = null;
|
|
253
|
-
if (spec.parent) {
|
|
254
|
-
const parentRef = normalizeRefInput(spec.parent);
|
|
255
|
-
// Check if parent was just created in this import
|
|
256
|
-
const parentSlug = parentRef.slice(1);
|
|
257
|
-
if (createdSpecsMap.has(parentSlug)) {
|
|
258
|
-
parent = createdSpecsMap.get(parentSlug);
|
|
259
|
-
}
|
|
260
|
-
else {
|
|
261
|
-
// Check existing specs
|
|
262
|
-
const parentResult = refIndex.resolve(parentRef);
|
|
263
|
-
if (!parentResult.ok) {
|
|
264
|
-
// AC: @plan-import ac-17, ac-33 - Missing parent with hint
|
|
265
|
-
const errMsg = `Parent ${parentRef} not found. Check parent exists or define it earlier in plan`;
|
|
266
|
-
warn(errMsg);
|
|
267
|
-
result.errors.push({ message: errMsg, spec });
|
|
268
|
-
result.skipped.push({ slug: specSlug, reason: "Missing parent" });
|
|
269
|
-
continue;
|
|
270
|
-
}
|
|
271
|
-
parent = parentResult.item;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
// Determine if this is a root-level trait (type: trait with no parent)
|
|
275
|
-
const isRootTrait = (spec.type === "trait") && !parent;
|
|
276
|
-
if (options.dryRun) {
|
|
277
|
-
// AC: @plan-import ac-15 - Dry run mode
|
|
278
|
-
info(`Would create spec: ${specRef} ${spec.parent ? `under ${spec.parent}` : isRootTrait ? "(project-level trait)" : "(root)"}`);
|
|
279
|
-
result.createdSpecs.push(specRef);
|
|
280
|
-
importReservedSlugs.add(specSlug);
|
|
281
|
-
// Track would-be created spec for parent resolution in dry-run
|
|
282
|
-
// AC: @plan-import ac-35 - Map depends_on in dry-run too
|
|
283
|
-
const dryRunSpec = createSpecItem({
|
|
284
|
-
title: spec.title,
|
|
285
|
-
type: spec.type || "feature",
|
|
286
|
-
slugs: [specSlug],
|
|
287
|
-
description: spec.description,
|
|
288
|
-
...(spec.acceptance_criteria ? { acceptance_criteria: spec.acceptance_criteria } : {}),
|
|
289
|
-
priority: undefined,
|
|
290
|
-
tags: [],
|
|
291
|
-
depends_on: (spec.depends_on || []).map(normalizeRefInput),
|
|
292
|
-
implements: [],
|
|
293
|
-
relates_to: [],
|
|
294
|
-
tests: [],
|
|
295
|
-
traits: (spec.traits || []).map(normalizeRefInput),
|
|
296
|
-
notes: [],
|
|
297
|
-
});
|
|
298
|
-
createdSpecsMap.set(specSlug, dryRunSpec);
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
// Create spec item
|
|
302
|
-
// AC: @plan-import ac-35 - Map depends_on to spec depends_on
|
|
303
|
-
const specInput = {
|
|
304
|
-
title: spec.title,
|
|
305
|
-
type: spec.type || "feature",
|
|
306
|
-
slugs: [specSlug],
|
|
307
|
-
description: spec.description,
|
|
308
|
-
priority: undefined,
|
|
309
|
-
tags: [],
|
|
310
|
-
depends_on: (spec.depends_on || []).map(normalizeRefInput),
|
|
311
|
-
implements: [],
|
|
312
|
-
relates_to: [],
|
|
313
|
-
tests: [],
|
|
314
|
-
traits: (spec.traits || []).map(normalizeRefInput),
|
|
315
|
-
notes: [],
|
|
316
|
-
};
|
|
317
|
-
// Add acceptance criteria if present
|
|
318
|
-
if (spec.acceptance_criteria) {
|
|
319
|
-
specInput.acceptance_criteria = spec.acceptance_criteria;
|
|
320
|
-
}
|
|
321
|
-
const newSpec = createSpecItem(specInput);
|
|
322
|
-
if (isRootTrait) {
|
|
323
|
-
// Traits without a parent go to kynetic.yaml project-level traits array,
|
|
324
|
-
// not the module-level traits array (same logic as `kspec trait add`)
|
|
325
|
-
if (!ctx.manifestPath) {
|
|
326
|
-
throw new Error("Could not find kynetic.yaml");
|
|
327
|
-
}
|
|
328
|
-
const { readYamlFile, writeYamlFilePreserveFormat } = await import("../../parser/yaml.js");
|
|
329
|
-
const manifest = await readYamlFile(ctx.manifestPath);
|
|
330
|
-
if (!manifest) {
|
|
331
|
-
throw new Error("Could not load kynetic.yaml");
|
|
332
|
-
}
|
|
333
|
-
// Ensure traits array exists at root
|
|
334
|
-
if (!Array.isArray(manifest.traits)) {
|
|
335
|
-
manifest.traits = [];
|
|
336
|
-
}
|
|
337
|
-
// Strip metadata from newSpec (_sourceFile, _path)
|
|
338
|
-
const { _sourceFile, _path, ...cleanItem } = newSpec;
|
|
339
|
-
manifest.traits.push(cleanItem);
|
|
340
|
-
await writeYamlFilePreserveFormat(ctx.manifestPath, manifest);
|
|
341
|
-
const traitIndex = manifest.traits.length - 1;
|
|
342
|
-
const createdSpec = {
|
|
343
|
-
...newSpec,
|
|
344
|
-
_sourceFile: ctx.manifestPath,
|
|
345
|
-
_path: `traits[${traitIndex}]`,
|
|
346
|
-
};
|
|
347
|
-
createdSpecsMap.set(specSlug, createdSpec);
|
|
348
|
-
}
|
|
349
|
-
else {
|
|
350
|
-
// Add spec to parent (or module if no parent)
|
|
351
|
-
const actualParent = parent || moduleItem;
|
|
352
|
-
const addResult = await addChildItem(ctx, actualParent, newSpec);
|
|
353
|
-
// Track the created spec for parent resolution
|
|
354
|
-
// Need to construct LoadedSpecItem with _sourceFile and _path for nested specs
|
|
355
|
-
const createdSpec = {
|
|
356
|
-
...addResult.item,
|
|
357
|
-
_sourceFile: actualParent._sourceFile,
|
|
358
|
-
_path: addResult.path,
|
|
359
|
-
};
|
|
360
|
-
createdSpecsMap.set(specSlug, createdSpec);
|
|
361
|
-
}
|
|
362
|
-
result.createdSpecs.push(specRef);
|
|
363
|
-
importReservedSlugs.add(specSlug);
|
|
364
|
-
newPlan.derived_specs.push(specRef);
|
|
365
|
-
info(`Created spec: ${specRef}`);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
catch (err) {
|
|
369
|
-
const errMsg = `Failed to create spec "${spec.title}": ${err instanceof Error ? err.message : String(err)}`;
|
|
370
|
-
warn(errMsg);
|
|
371
|
-
result.errors.push({ message: errMsg, spec });
|
|
372
|
-
}
|
|
155
|
+
if (options.update) {
|
|
156
|
+
warn("--update is ignored with --into. Existing plan specs are not modified during re-import.");
|
|
373
157
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if (parsed.tasks.derive_from_specs && result.createdSpecs.length > 0) {
|
|
377
|
-
const tasks = await loadAllTasks(ctx);
|
|
378
|
-
// Combine existing task slugs + all slugs reserved during this import (plan + specs)
|
|
379
|
-
const importSlugs = new Set([
|
|
380
|
-
...tasks.flatMap((t) => t.slugs),
|
|
381
|
-
...importReservedSlugs,
|
|
382
|
-
]);
|
|
383
|
-
// AC: @plan-import ac-35 - Build spec→task slug mapping for depends_on resolution
|
|
384
|
-
// First pass: generate all task slugs so we can resolve spec depends_on → task depends_on
|
|
385
|
-
const specToTaskSlug = new Map();
|
|
386
|
-
for (const specRef of result.createdSpecs) {
|
|
387
|
-
const specSlug = specRef.slice(1);
|
|
388
|
-
const spec = createdSpecsMap.get(specSlug);
|
|
389
|
-
if (!spec)
|
|
390
|
-
continue;
|
|
391
|
-
const taskTitle = `Implement ${spec.title}`;
|
|
392
|
-
const taskSlug = generateSlug(taskTitle);
|
|
393
|
-
let uniqueSlug = taskSlug;
|
|
394
|
-
let counter = 1;
|
|
395
|
-
while (importSlugs.has(uniqueSlug) || !refIndex.isSlugAvailable(uniqueSlug)) {
|
|
396
|
-
uniqueSlug = `${taskSlug}-${counter}`;
|
|
397
|
-
counter++;
|
|
398
|
-
}
|
|
399
|
-
importSlugs.add(uniqueSlug);
|
|
400
|
-
importReservedSlugs.add(uniqueSlug);
|
|
401
|
-
specToTaskSlug.set(specSlug, uniqueSlug);
|
|
402
|
-
}
|
|
403
|
-
for (const specRef of result.createdSpecs) {
|
|
404
|
-
// Get spec from createdSpecsMap (works in both dry-run and real mode)
|
|
405
|
-
const specSlug = specRef.slice(1);
|
|
406
|
-
const spec = createdSpecsMap.get(specSlug);
|
|
407
|
-
if (!spec)
|
|
408
|
-
continue;
|
|
409
|
-
const uniqueSlug = specToTaskSlug.get(specSlug);
|
|
410
|
-
if (options.dryRun) {
|
|
411
|
-
info(`Would derive task: @${uniqueSlug} from ${specRef}`);
|
|
412
|
-
result.createdTasks.push(`@${uniqueSlug}`);
|
|
413
|
-
}
|
|
414
|
-
else {
|
|
415
|
-
// AC: @plan-import ac-35 - Resolve spec depends_on to task depends_on
|
|
416
|
-
const planSpec = parsed.specs.find(s => (s.slug || generateSlug(s.title)) === specSlug);
|
|
417
|
-
const taskDependsOn = [];
|
|
418
|
-
if (planSpec?.depends_on) {
|
|
419
|
-
for (const depRef of planSpec.depends_on) {
|
|
420
|
-
const depSlug = depRef.startsWith("@") ? depRef.slice(1) : depRef;
|
|
421
|
-
const depTaskSlug = specToTaskSlug.get(depSlug);
|
|
422
|
-
if (depTaskSlug) {
|
|
423
|
-
// Dependency spec was in this import — link to its derived task
|
|
424
|
-
taskDependsOn.push(`@${depTaskSlug}`);
|
|
425
|
-
}
|
|
426
|
-
else {
|
|
427
|
-
// Dependency spec already existed — find an existing task with that spec_ref
|
|
428
|
-
const normalizedDepRef = normalizeRefInput(depRef);
|
|
429
|
-
const existingTask = tasks.find(t => t.spec_ref === normalizedDepRef);
|
|
430
|
-
if (existingTask && existingTask.slugs.length > 0) {
|
|
431
|
-
taskDependsOn.push(`@${existingTask.slugs[0]}`);
|
|
432
|
-
}
|
|
433
|
-
else {
|
|
434
|
-
// No task found for this spec — pass the spec ref as-is
|
|
435
|
-
taskDependsOn.push(normalizedDepRef);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
// AC: @plan-import ac-19 - Task has both spec_ref and plan_ref
|
|
441
|
-
// AC: @plan-import ac-36 - Derived task inherits priority from plan spec
|
|
442
|
-
const taskInput = {
|
|
443
|
-
title: `Implement ${spec.title}`,
|
|
444
|
-
type: "task",
|
|
445
|
-
spec_ref: specRef,
|
|
446
|
-
plan_ref: planRef,
|
|
447
|
-
priority: planSpec?.priority ?? 3,
|
|
448
|
-
slugs: [uniqueSlug],
|
|
449
|
-
tags: [],
|
|
450
|
-
depends_on: taskDependsOn,
|
|
451
|
-
notes: [],
|
|
452
|
-
};
|
|
453
|
-
const newTask = createTask(taskInput);
|
|
454
|
-
// AC: @plan-import ac-13 - Scoped implementation notes
|
|
455
|
-
if (planSpec?.implementation_notes) {
|
|
456
|
-
// Per-spec notes go directly to this task
|
|
457
|
-
newTask.notes.push({
|
|
458
|
-
_ulid: ulid(),
|
|
459
|
-
created_at: new Date().toISOString(),
|
|
460
|
-
author,
|
|
461
|
-
content: `Implementation notes:\n\n${planSpec.implementation_notes}`,
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
|
-
else if (parsed.implementationNotes) {
|
|
465
|
-
// No per-spec notes — reference the plan
|
|
466
|
-
newTask.notes.push({
|
|
467
|
-
_ulid: ulid(),
|
|
468
|
-
created_at: new Date().toISOString(),
|
|
469
|
-
author,
|
|
470
|
-
content: `See plan ${planRef} for implementation notes`,
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
await saveTask(ctx, newTask);
|
|
474
|
-
result.createdTasks.push(`@${uniqueSlug}`);
|
|
475
|
-
newPlan.derived_tasks.push(`@${uniqueSlug}`);
|
|
476
|
-
info(`Derived task: @${uniqueSlug}`);
|
|
477
|
-
}
|
|
478
|
-
}
|
|
158
|
+
if (options.status) {
|
|
159
|
+
warn("--status is ignored with --into. Existing plan status is unchanged.");
|
|
479
160
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
// Combine with all slugs reserved during this import (plan + specs)
|
|
485
|
-
const manualTasks = await loadAllTasks(ctx);
|
|
486
|
-
const manualImportSlugs = new Set([
|
|
487
|
-
...manualTasks.flatMap((t) => t.slugs),
|
|
488
|
-
...importReservedSlugs,
|
|
489
|
-
]);
|
|
490
|
-
for (const taskDef of parsed.tasks.additional_tasks) {
|
|
491
|
-
const taskSlug = taskDef.slug || generateSlug(taskDef.title);
|
|
492
|
-
// Ensure slug uniqueness across all namespaces + this import's slugs
|
|
493
|
-
let uniqueSlug = taskSlug;
|
|
494
|
-
let counter = 1;
|
|
495
|
-
while (manualImportSlugs.has(uniqueSlug) || !refIndex.isSlugAvailable(uniqueSlug)) {
|
|
496
|
-
uniqueSlug = `${taskSlug}-${counter}`;
|
|
497
|
-
counter++;
|
|
498
|
-
}
|
|
499
|
-
manualImportSlugs.add(uniqueSlug);
|
|
500
|
-
if (options.dryRun) {
|
|
501
|
-
info(`Would create manual task: @${uniqueSlug}`);
|
|
502
|
-
result.createdTasks.push(`@${uniqueSlug}`);
|
|
503
|
-
}
|
|
504
|
-
else {
|
|
505
|
-
// AC: @plan-import ac-27, ac-35 - Manual tasks have plan_ref; spec_ref and depends_on honored
|
|
506
|
-
const taskInput = {
|
|
507
|
-
title: taskDef.title,
|
|
508
|
-
type: "task",
|
|
509
|
-
plan_ref: planRef,
|
|
510
|
-
priority: taskDef.priority || 3,
|
|
511
|
-
slugs: [uniqueSlug],
|
|
512
|
-
tags: taskDef.tags || [],
|
|
513
|
-
depends_on: (taskDef.depends_on || []).map(normalizeRefInput),
|
|
514
|
-
notes: [],
|
|
515
|
-
...(taskDef.spec_ref ? { spec_ref: normalizeRefInput(taskDef.spec_ref) } : {}),
|
|
516
|
-
};
|
|
517
|
-
if (taskDef.description) {
|
|
518
|
-
taskInput.notes = [
|
|
519
|
-
{
|
|
520
|
-
_ulid: ulid(),
|
|
521
|
-
created_at: new Date().toISOString(),
|
|
522
|
-
author,
|
|
523
|
-
content: taskDef.description,
|
|
524
|
-
},
|
|
525
|
-
];
|
|
526
|
-
}
|
|
527
|
-
const newTask = createTask(taskInput);
|
|
528
|
-
await saveTask(ctx, newTask);
|
|
529
|
-
result.createdTasks.push(`@${uniqueSlug}`);
|
|
530
|
-
newPlan.derived_tasks.push(`@${uniqueSlug}`);
|
|
531
|
-
info(`Created manual task: @${uniqueSlug}`);
|
|
532
|
-
}
|
|
533
|
-
}
|
|
161
|
+
assertImportIntoAllowed(foundPlan);
|
|
162
|
+
if (options.dryRun) {
|
|
163
|
+
emitImportResult(preview, { dryRun: true });
|
|
164
|
+
return;
|
|
534
165
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
166
|
+
const author = getAuthor(ctx.config?.identity?.author);
|
|
167
|
+
const note = createPlanNote(noteMessage, author);
|
|
168
|
+
const updatedPlan = await mutatePlanAtomically(ctx, foundPlan, (latestPlan) => {
|
|
169
|
+
assertImportIntoAllowed(latestPlan);
|
|
170
|
+
return {
|
|
171
|
+
...latestPlan,
|
|
172
|
+
title: nextTitle,
|
|
173
|
+
content,
|
|
174
|
+
notes: [...latestPlan.notes, note],
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
await commitIfShadow(ctx.shadow, "plan-import", updatedPlan.slugs[0] || updatedPlan._ulid.slice(0, 8), updatedPlan.title);
|
|
178
|
+
emitImportResult(preview, { dryRun: false, createdAt: updatedPlan.created_at });
|
|
179
|
+
}
|
|
180
|
+
function assertImportIntoAllowed(plan) {
|
|
181
|
+
if (plan.status === "active") {
|
|
182
|
+
exitImportWithGuidance("Cannot update active plan. Derive is a one-shot operation.", EXIT_CODES.CONFLICT, "Create a new plan iteration instead of re-importing into an active plan.", {
|
|
183
|
+
current_status: plan.status,
|
|
184
|
+
valid_statuses: ["draft", "approved"],
|
|
185
|
+
});
|
|
540
186
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
const errorCount = result.errors.length + result.skipped.length;
|
|
546
|
-
// AC: @plan-import ac-32 - JSON output
|
|
547
|
-
if (isJsonMode()) {
|
|
548
|
-
output({
|
|
549
|
-
plan: planRef,
|
|
550
|
-
created_specs: result.createdSpecs,
|
|
551
|
-
updated_specs: result.updatedSpecs,
|
|
552
|
-
created_tasks: result.createdTasks,
|
|
553
|
-
errors: result.errors,
|
|
554
|
-
skipped: result.skipped,
|
|
187
|
+
if (plan.status === "completed" || plan.status === "rejected") {
|
|
188
|
+
exitImportWithGuidance("Cannot update plan in terminal status", EXIT_CODES.CONFLICT, "Reopen or replace the plan with a new draft/approved plan.", {
|
|
189
|
+
current_status: plan.status,
|
|
190
|
+
valid_statuses: ["draft", "approved"],
|
|
555
191
|
});
|
|
556
192
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
if (
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
if (result.skipped.length > 0) {
|
|
575
|
-
console.log(`\nSkipped (${result.skipped.length}):`);
|
|
576
|
-
for (const skip of result.skipped) {
|
|
577
|
-
console.log(` - @${skip.slug}: ${skip.reason}`);
|
|
578
|
-
}
|
|
193
|
+
}
|
|
194
|
+
function createPlanNote(content, author) {
|
|
195
|
+
return {
|
|
196
|
+
_ulid: ulid(),
|
|
197
|
+
created_at: new Date().toISOString(),
|
|
198
|
+
author,
|
|
199
|
+
content,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function exitImportWithGuidance(message, exitCode, suggestion, details) {
|
|
203
|
+
if (suggestion) {
|
|
204
|
+
if (isJsonMode()) {
|
|
205
|
+
error(message, {
|
|
206
|
+
...details,
|
|
207
|
+
suggestion,
|
|
208
|
+
guidance: suggestion,
|
|
209
|
+
});
|
|
579
210
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
211
|
+
else {
|
|
212
|
+
error(message);
|
|
213
|
+
console.error(`Suggestion: ${suggestion}`);
|
|
583
214
|
}
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
error(message, isJsonMode() ? details : undefined);
|
|
218
|
+
}
|
|
219
|
+
process.exit(exitCode);
|
|
220
|
+
}
|
|
221
|
+
function emitImportResult(preview, options) {
|
|
222
|
+
const payload = {
|
|
223
|
+
dry_run: options.dryRun,
|
|
224
|
+
plan_ref: preview.ref,
|
|
225
|
+
title: preview.title,
|
|
226
|
+
status: preview.status,
|
|
227
|
+
module_ref: preview.module_ref,
|
|
228
|
+
source_path: preview.source_path,
|
|
229
|
+
created_at: options.createdAt ?? null,
|
|
230
|
+
derived_specs: preview.derived_specs,
|
|
231
|
+
derived_tasks: preview.derived_tasks,
|
|
232
|
+
content: preview.content,
|
|
233
|
+
changes: preview.changes,
|
|
234
|
+
note_message: preview.note_message,
|
|
235
|
+
};
|
|
236
|
+
if (isJsonMode()) {
|
|
237
|
+
output(payload);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (options.dryRun) {
|
|
241
|
+
console.log("Dry run - no changes made\n");
|
|
242
|
+
}
|
|
243
|
+
console.log(`Plan: ${preview.ref}`);
|
|
244
|
+
console.log(`Title: ${preview.title}`);
|
|
245
|
+
console.log(`Status: ${preview.status}`);
|
|
246
|
+
if (preview.module_ref) {
|
|
247
|
+
console.log(`Stored module: ${preview.module_ref}`);
|
|
248
|
+
}
|
|
249
|
+
console.log(`Source: ${preview.source_path}`);
|
|
250
|
+
console.log("Content stored: full document");
|
|
251
|
+
console.log("Derived specs: 0");
|
|
252
|
+
console.log("Derived tasks: 0");
|
|
253
|
+
if (preview.changes.length > 0) {
|
|
254
|
+
console.log(`Changes: ${preview.changes.join(", ")}`);
|
|
255
|
+
}
|
|
256
|
+
if (preview.note_message) {
|
|
257
|
+
console.log(`Note: ${preview.note_message}`);
|
|
258
|
+
}
|
|
259
|
+
if (!options.dryRun) {
|
|
260
|
+
success("Imported plan content");
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function extractOptionalPlanTitle(content) {
|
|
264
|
+
const titleMatch = content.match(/^#\s+(.+)$/m);
|
|
265
|
+
return titleMatch ? titleMatch[1].trim() : null;
|
|
266
|
+
}
|
|
267
|
+
function nextAvailablePlanSlug(title, refIndex) {
|
|
268
|
+
let planSlug = `plan-${generateSlug(title)}`;
|
|
269
|
+
let counter = 1;
|
|
270
|
+
const baseSlug = planSlug;
|
|
271
|
+
while (!refIndex.isSlugAvailable(planSlug)) {
|
|
272
|
+
planSlug = `${baseSlug}-${counter}`;
|
|
273
|
+
counter++;
|
|
274
|
+
}
|
|
275
|
+
return planSlug;
|
|
591
276
|
}
|
|
592
277
|
/**
|
|
593
|
-
* Generate URL-safe slug from title
|
|
278
|
+
* Generate URL-safe slug from title.
|
|
594
279
|
*/
|
|
595
280
|
function generateSlug(title) {
|
|
596
281
|
return title
|