@kynetic-ai/spec 0.1.2 → 0.4.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 +250 -17
- package/dist/acp/client.d.ts +18 -4
- package/dist/acp/client.d.ts.map +1 -1
- package/dist/acp/client.js +44 -26
- package/dist/acp/client.js.map +1 -1
- package/dist/acp/framing.d.ts +2 -2
- package/dist/acp/framing.d.ts.map +1 -1
- package/dist/acp/framing.js +37 -29
- package/dist/acp/framing.js.map +1 -1
- package/dist/acp/index.d.ts +6 -7
- package/dist/acp/index.d.ts.map +1 -1
- package/dist/acp/index.js +3 -3
- package/dist/acp/index.js.map +1 -1
- package/dist/acp/types.d.ts +5 -5
- package/dist/acp/types.d.ts.map +1 -1
- package/dist/acp/types.js +18 -18
- package/dist/acp/types.js.map +1 -1
- package/dist/agents/adapters.d.ts.map +1 -1
- package/dist/agents/adapters.js +24 -13
- package/dist/agents/adapters.js.map +1 -1
- package/dist/agents/index.d.ts +2 -2
- package/dist/agents/index.js +2 -2
- package/dist/agents/spawner.d.ts +4 -4
- package/dist/agents/spawner.d.ts.map +1 -1
- package/dist/agents/spawner.js +6 -6
- package/dist/agents/spawner.js.map +1 -1
- package/dist/cli/batch-context.d.ts +43 -0
- package/dist/cli/batch-context.d.ts.map +1 -0
- package/dist/cli/batch-context.js +93 -0
- package/dist/cli/batch-context.js.map +1 -0
- package/dist/cli/batch-exec.d.ts +107 -0
- package/dist/cli/batch-exec.d.ts.map +1 -0
- package/dist/cli/batch-exec.js +706 -0
- package/dist/cli/batch-exec.js.map +1 -0
- package/dist/cli/batch.d.ts +4 -2
- package/dist/cli/batch.d.ts.map +1 -1
- package/dist/cli/batch.js +15 -14
- package/dist/cli/batch.js.map +1 -1
- package/dist/cli/command-annotations.d.ts +23 -0
- package/dist/cli/command-annotations.d.ts.map +1 -0
- package/dist/cli/command-annotations.js +27 -0
- package/dist/cli/command-annotations.js.map +1 -0
- package/dist/cli/commands/agents.d.ts +46 -0
- package/dist/cli/commands/agents.d.ts.map +1 -0
- package/dist/cli/commands/agents.js +377 -0
- package/dist/cli/commands/agents.js.map +1 -0
- package/dist/cli/commands/batch.d.ts +20 -0
- package/dist/cli/commands/batch.d.ts.map +1 -0
- package/dist/cli/commands/batch.js +214 -0
- package/dist/cli/commands/batch.js.map +1 -0
- package/dist/cli/commands/clone-for-testing.d.ts +1 -1
- package/dist/cli/commands/clone-for-testing.d.ts.map +1 -1
- package/dist/cli/commands/clone-for-testing.js +37 -47
- package/dist/cli/commands/clone-for-testing.js.map +1 -1
- package/dist/cli/commands/derive.d.ts +1 -1
- package/dist/cli/commands/derive.d.ts.map +1 -1
- package/dist/cli/commands/derive.js +141 -88
- package/dist/cli/commands/derive.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +11 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +152 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/export.d.ts +12 -0
- package/dist/cli/commands/export.d.ts.map +1 -0
- package/dist/cli/commands/export.js +134 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/help.d.ts +1 -1
- package/dist/cli/commands/help.d.ts.map +1 -1
- package/dist/cli/commands/help.js +163 -37
- package/dist/cli/commands/help.js.map +1 -1
- package/dist/cli/commands/inbox.d.ts +1 -1
- package/dist/cli/commands/inbox.d.ts.map +1 -1
- package/dist/cli/commands/inbox.js +178 -56
- package/dist/cli/commands/inbox.js.map +1 -1
- package/dist/cli/commands/index.d.ts +31 -19
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +31 -19
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/init.d.ts +5 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +108 -57
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/item.d.ts +1 -1
- package/dist/cli/commands/item.d.ts.map +1 -1
- package/dist/cli/commands/item.js +557 -274
- package/dist/cli/commands/item.js.map +1 -1
- package/dist/cli/commands/link.d.ts +1 -1
- package/dist/cli/commands/link.d.ts.map +1 -1
- package/dist/cli/commands/link.js +55 -46
- package/dist/cli/commands/link.js.map +1 -1
- package/dist/cli/commands/log.d.ts +1 -1
- package/dist/cli/commands/log.d.ts.map +1 -1
- package/dist/cli/commands/log.js +58 -51
- package/dist/cli/commands/log.js.map +1 -1
- package/dist/cli/commands/merge-driver.d.ts +19 -0
- package/dist/cli/commands/merge-driver.d.ts.map +1 -0
- package/dist/cli/commands/merge-driver.js +398 -0
- package/dist/cli/commands/merge-driver.js.map +1 -0
- package/dist/cli/commands/meta.d.ts +1 -1
- package/dist/cli/commands/meta.d.ts.map +1 -1
- package/dist/cli/commands/meta.js +534 -399
- package/dist/cli/commands/meta.js.map +1 -1
- package/dist/cli/commands/module.d.ts +1 -1
- package/dist/cli/commands/module.d.ts.map +1 -1
- package/dist/cli/commands/module.js +30 -25
- package/dist/cli/commands/module.js.map +1 -1
- package/dist/cli/commands/plan-import.d.ts +11 -0
- package/dist/cli/commands/plan-import.d.ts.map +1 -0
- package/dist/cli/commands/plan-import.js +547 -0
- package/dist/cli/commands/plan-import.js.map +1 -0
- package/dist/cli/commands/plan.d.ts +10 -0
- package/dist/cli/commands/plan.d.ts.map +1 -0
- package/dist/cli/commands/plan.js +421 -0
- package/dist/cli/commands/plan.js.map +1 -0
- package/dist/cli/commands/ralph.d.ts +1 -1
- package/dist/cli/commands/ralph.d.ts.map +1 -1
- package/dist/cli/commands/ralph.js +1109 -170
- package/dist/cli/commands/ralph.js.map +1 -1
- package/dist/cli/commands/refs.d.ts +13 -0
- package/dist/cli/commands/refs.d.ts.map +1 -0
- package/dist/cli/commands/refs.js +283 -0
- package/dist/cli/commands/refs.js.map +1 -0
- package/dist/cli/commands/search.d.ts +1 -1
- package/dist/cli/commands/search.d.ts.map +1 -1
- package/dist/cli/commands/search.js +199 -37
- package/dist/cli/commands/search.js.map +1 -1
- package/dist/cli/commands/serve.d.ts +10 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/serve.js +491 -0
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/session.d.ts +25 -6
- package/dist/cli/commands/session.d.ts.map +1 -1
- package/dist/cli/commands/session.js +810 -127
- package/dist/cli/commands/session.js.map +1 -1
- package/dist/cli/commands/setup-seeding.d.ts +81 -0
- package/dist/cli/commands/setup-seeding.d.ts.map +1 -0
- package/dist/cli/commands/setup-seeding.js +292 -0
- package/dist/cli/commands/setup-seeding.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +77 -3
- package/dist/cli/commands/setup.d.ts.map +1 -1
- package/dist/cli/commands/setup.js +1267 -274
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/shadow.d.ts +1 -1
- package/dist/cli/commands/shadow.d.ts.map +1 -1
- package/dist/cli/commands/shadow.js +70 -50
- package/dist/cli/commands/shadow.js.map +1 -1
- package/dist/cli/commands/skill-crud.d.ts +58 -0
- package/dist/cli/commands/skill-crud.d.ts.map +1 -0
- package/dist/cli/commands/skill-crud.js +753 -0
- package/dist/cli/commands/skill-crud.js.map +1 -0
- package/dist/cli/commands/skill-diff.d.ts +27 -0
- package/dist/cli/commands/skill-diff.d.ts.map +1 -0
- package/dist/cli/commands/skill-diff.js +840 -0
- package/dist/cli/commands/skill-diff.js.map +1 -0
- package/dist/cli/commands/skill-install.d.ts +56 -0
- package/dist/cli/commands/skill-install.d.ts.map +1 -0
- package/dist/cli/commands/skill-install.js +509 -0
- package/dist/cli/commands/skill-install.js.map +1 -0
- package/dist/cli/commands/skill.d.ts +20 -0
- package/dist/cli/commands/skill.d.ts.map +1 -0
- package/dist/cli/commands/skill.js +36 -0
- package/dist/cli/commands/skill.js.map +1 -0
- package/dist/cli/commands/task.d.ts +1 -1
- package/dist/cli/commands/task.d.ts.map +1 -1
- package/dist/cli/commands/task.js +584 -350
- package/dist/cli/commands/task.js.map +1 -1
- package/dist/cli/commands/tasks.d.ts +26 -1
- package/dist/cli/commands/tasks.d.ts.map +1 -1
- package/dist/cli/commands/tasks.js +225 -122
- package/dist/cli/commands/tasks.js.map +1 -1
- package/dist/cli/commands/trait.d.ts +1 -1
- package/dist/cli/commands/trait.d.ts.map +1 -1
- package/dist/cli/commands/trait.js +166 -101
- package/dist/cli/commands/trait.js.map +1 -1
- package/dist/cli/commands/triage.d.ts +7 -0
- package/dist/cli/commands/triage.d.ts.map +1 -0
- package/dist/cli/commands/triage.js +483 -0
- package/dist/cli/commands/triage.js.map +1 -0
- package/dist/cli/commands/util.d.ts +7 -0
- package/dist/cli/commands/util.d.ts.map +1 -0
- package/dist/cli/commands/util.js +30 -0
- package/dist/cli/commands/util.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +1 -1
- package/dist/cli/commands/validate.d.ts.map +1 -1
- package/dist/cli/commands/validate.js +264 -83
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/cli/commands/workflow.d.ts +16 -0
- package/dist/cli/commands/workflow.d.ts.map +1 -0
- package/dist/cli/commands/workflow.js +851 -0
- package/dist/cli/commands/workflow.js.map +1 -0
- package/dist/cli/exit-codes.d.ts +7 -0
- package/dist/cli/exit-codes.d.ts.map +1 -1
- package/dist/cli/exit-codes.js +26 -18
- package/dist/cli/exit-codes.js.map +1 -1
- package/dist/cli/help/content.d.ts.map +1 -1
- package/dist/cli/help/content.js +86 -71
- package/dist/cli/help/content.js.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +131 -19
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/introspection.d.ts +6 -2
- package/dist/cli/introspection.d.ts.map +1 -1
- package/dist/cli/introspection.js +11 -8
- package/dist/cli/introspection.js.map +1 -1
- package/dist/cli/output.d.ts +64 -4
- package/dist/cli/output.d.ts.map +1 -1
- package/dist/cli/output.js +237 -85
- package/dist/cli/output.js.map +1 -1
- package/dist/cli/parse-utils.d.ts +21 -0
- package/dist/cli/parse-utils.d.ts.map +1 -0
- package/dist/cli/parse-utils.js +32 -0
- package/dist/cli/parse-utils.js.map +1 -0
- package/dist/cli/pid-utils.d.ts +72 -0
- package/dist/cli/pid-utils.d.ts.map +1 -0
- package/dist/cli/pid-utils.js +174 -0
- package/dist/cli/pid-utils.js.map +1 -0
- package/dist/cli/suggest.d.ts.map +1 -1
- package/dist/cli/suggest.js +1 -2
- package/dist/cli/suggest.js.map +1 -1
- package/dist/cli/validators.d.ts +43 -0
- package/dist/cli/validators.d.ts.map +1 -0
- package/dist/cli/validators.js +84 -0
- package/dist/cli/validators.js.map +1 -0
- package/dist/daemon/index.ts +52 -0
- package/dist/daemon/middleware/project-context.ts +126 -0
- package/dist/daemon/pid.ts +179 -0
- package/dist/daemon/project-context.ts +343 -0
- package/dist/daemon/routes/inbox.ts +164 -0
- package/dist/daemon/routes/items.ts +322 -0
- package/dist/daemon/routes/meta.ts +118 -0
- package/dist/daemon/routes/projects.ts +162 -0
- package/dist/daemon/routes/tasks.ts +327 -0
- package/dist/daemon/routes/triage.ts +402 -0
- package/dist/daemon/routes/validation.ts +248 -0
- package/dist/daemon/server.ts +408 -0
- package/dist/daemon/watcher.ts +195 -0
- package/dist/daemon/websocket/handler.ts +138 -0
- package/dist/daemon/websocket/heartbeat.ts +71 -0
- package/dist/daemon/websocket/pubsub.ts +125 -0
- package/dist/daemon/websocket/types.ts +66 -0
- package/dist/export/html.d.ts +19 -0
- package/dist/export/html.d.ts.map +1 -0
- package/dist/export/html.js +239 -0
- package/dist/export/html.js.map +1 -0
- package/dist/export/index.d.ts +10 -0
- package/dist/export/index.d.ts.map +1 -0
- package/dist/export/index.js +10 -0
- package/dist/export/index.js.map +1 -0
- package/dist/export/json.d.ts +24 -0
- package/dist/export/json.d.ts.map +1 -0
- package/dist/export/json.js +198 -0
- package/dist/export/json.js.map +1 -0
- package/dist/export/triage.d.ts +51 -0
- package/dist/export/triage.d.ts.map +1 -0
- package/dist/export/triage.js +83 -0
- package/dist/export/triage.js.map +1 -0
- package/dist/export/types.d.ts +122 -0
- package/dist/export/types.d.ts.map +1 -0
- package/dist/export/types.js +9 -0
- package/dist/export/types.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/lib/claude-plugin-registry.d.ts +66 -0
- package/dist/lib/claude-plugin-registry.d.ts.map +1 -0
- package/dist/lib/claude-plugin-registry.js +318 -0
- package/dist/lib/claude-plugin-registry.js.map +1 -0
- package/dist/merge/arrays.d.ts +87 -0
- package/dist/merge/arrays.d.ts.map +1 -0
- package/dist/merge/arrays.js +164 -0
- package/dist/merge/arrays.js.map +1 -0
- package/dist/merge/file-type.d.ts +32 -0
- package/dist/merge/file-type.d.ts.map +1 -0
- package/dist/merge/file-type.js +70 -0
- package/dist/merge/file-type.js.map +1 -0
- package/dist/merge/index.d.ts +14 -0
- package/dist/merge/index.d.ts.map +1 -0
- package/dist/merge/index.js +11 -0
- package/dist/merge/index.js.map +1 -0
- package/dist/merge/objects.d.ts +46 -0
- package/dist/merge/objects.d.ts.map +1 -0
- package/dist/merge/objects.js +193 -0
- package/dist/merge/objects.js.map +1 -0
- package/dist/merge/parse.d.ts +23 -0
- package/dist/merge/parse.d.ts.map +1 -0
- package/dist/merge/parse.js +78 -0
- package/dist/merge/parse.js.map +1 -0
- package/dist/merge/resolve.d.ts +66 -0
- package/dist/merge/resolve.d.ts.map +1 -0
- package/dist/merge/resolve.js +189 -0
- package/dist/merge/resolve.js.map +1 -0
- package/dist/merge/types.d.ts +82 -0
- package/dist/merge/types.d.ts.map +1 -0
- package/dist/merge/types.js +8 -0
- package/dist/merge/types.js.map +1 -0
- package/dist/parser/agent-data-sections.d.ts +53 -0
- package/dist/parser/agent-data-sections.d.ts.map +1 -0
- package/dist/parser/agent-data-sections.js +118 -0
- package/dist/parser/agent-data-sections.js.map +1 -0
- package/dist/parser/alignment.d.ts +4 -4
- package/dist/parser/alignment.d.ts.map +1 -1
- package/dist/parser/alignment.js +27 -22
- package/dist/parser/alignment.js.map +1 -1
- package/dist/parser/assess.d.ts +5 -5
- package/dist/parser/assess.d.ts.map +1 -1
- package/dist/parser/assess.js +36 -32
- package/dist/parser/assess.js.map +1 -1
- package/dist/parser/config.d.ts +457 -0
- package/dist/parser/config.d.ts.map +1 -0
- package/dist/parser/config.js +373 -0
- package/dist/parser/config.js.map +1 -0
- package/dist/parser/convention-validation.d.ts +1 -1
- package/dist/parser/convention-validation.d.ts.map +1 -1
- package/dist/parser/convention-validation.js +21 -16
- package/dist/parser/convention-validation.js.map +1 -1
- package/dist/parser/coverage-cache.d.ts +49 -0
- package/dist/parser/coverage-cache.d.ts.map +1 -0
- package/dist/parser/coverage-cache.js +123 -0
- package/dist/parser/coverage-cache.js.map +1 -0
- package/dist/parser/daemon-status.d.ts +37 -0
- package/dist/parser/daemon-status.d.ts.map +1 -0
- package/dist/parser/daemon-status.js +67 -0
- package/dist/parser/daemon-status.js.map +1 -0
- package/dist/parser/doctor.d.ts +107 -0
- package/dist/parser/doctor.d.ts.map +1 -0
- package/dist/parser/doctor.js +366 -0
- package/dist/parser/doctor.js.map +1 -0
- package/dist/parser/fix.d.ts +1 -1
- package/dist/parser/fix.d.ts.map +1 -1
- package/dist/parser/fix.js +31 -27
- package/dist/parser/fix.js.map +1 -1
- package/dist/parser/index.d.ts +16 -11
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/index.js +16 -11
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/items.d.ts +8 -2
- package/dist/parser/items.d.ts.map +1 -1
- package/dist/parser/items.js +71 -35
- package/dist/parser/items.js.map +1 -1
- package/dist/parser/meta.d.ts +167 -9
- package/dist/parser/meta.d.ts.map +1 -1
- package/dist/parser/meta.js +379 -46
- package/dist/parser/meta.js.map +1 -1
- package/dist/parser/plan-document.d.ts +197 -0
- package/dist/parser/plan-document.d.ts.map +1 -0
- package/dist/parser/plan-document.js +341 -0
- package/dist/parser/plan-document.js.map +1 -0
- package/dist/parser/plans.d.ts +59 -0
- package/dist/parser/plans.d.ts.map +1 -0
- package/dist/parser/plans.js +239 -0
- package/dist/parser/plans.js.map +1 -0
- package/dist/parser/refs.d.ts +22 -9
- package/dist/parser/refs.d.ts.map +1 -1
- package/dist/parser/refs.js +102 -50
- package/dist/parser/refs.js.map +1 -1
- package/dist/parser/setup-status.d.ts +71 -0
- package/dist/parser/setup-status.d.ts.map +1 -0
- package/dist/parser/setup-status.js +269 -0
- package/dist/parser/setup-status.js.map +1 -0
- package/dist/parser/shadow.d.ts +150 -19
- package/dist/parser/shadow.d.ts.map +1 -1
- package/dist/parser/shadow.js +548 -187
- package/dist/parser/shadow.js.map +1 -1
- package/dist/parser/skill-render.d.ts +317 -0
- package/dist/parser/skill-render.d.ts.map +1 -0
- package/dist/parser/skill-render.js +943 -0
- package/dist/parser/skill-render.js.map +1 -0
- package/dist/parser/traits.d.ts +3 -3
- package/dist/parser/traits.d.ts.map +1 -1
- package/dist/parser/traits.js +2 -2
- package/dist/parser/traits.js.map +1 -1
- package/dist/parser/validate-skills.d.ts +32 -0
- package/dist/parser/validate-skills.d.ts.map +1 -0
- package/dist/parser/validate-skills.js +202 -0
- package/dist/parser/validate-skills.js.map +1 -0
- package/dist/parser/validate.d.ts +45 -3
- package/dist/parser/validate.d.ts.map +1 -1
- package/dist/parser/validate.js +622 -105
- package/dist/parser/validate.js.map +1 -1
- package/dist/parser/yaml.d.ts +83 -19
- package/dist/parser/yaml.d.ts.map +1 -1
- package/dist/parser/yaml.js +478 -173
- package/dist/parser/yaml.js.map +1 -1
- package/dist/ralph/cli-renderer.d.ts +8 -1
- package/dist/ralph/cli-renderer.d.ts.map +1 -1
- package/dist/ralph/cli-renderer.js +105 -34
- package/dist/ralph/cli-renderer.js.map +1 -1
- package/dist/ralph/events.d.ts +10 -10
- package/dist/ralph/events.d.ts.map +1 -1
- package/dist/ralph/events.js +301 -98
- package/dist/ralph/events.js.map +1 -1
- package/dist/ralph/index.d.ts +5 -2
- package/dist/ralph/index.d.ts.map +1 -1
- package/dist/ralph/index.js +9 -3
- package/dist/ralph/index.js.map +1 -1
- package/dist/ralph/loop-errors.d.ts +83 -0
- package/dist/ralph/loop-errors.d.ts.map +1 -0
- package/dist/ralph/loop-errors.js +150 -0
- package/dist/ralph/loop-errors.js.map +1 -0
- package/dist/ralph/subagent.d.ts +94 -0
- package/dist/ralph/subagent.d.ts.map +1 -0
- package/dist/ralph/subagent.js +193 -0
- package/dist/ralph/subagent.js.map +1 -0
- package/dist/ralph/wrap-up.d.ts +125 -0
- package/dist/ralph/wrap-up.d.ts.map +1 -0
- package/dist/ralph/wrap-up.js +270 -0
- package/dist/ralph/wrap-up.js.map +1 -0
- package/dist/schema/batch.d.ts +97 -0
- package/dist/schema/batch.d.ts.map +1 -0
- package/dist/schema/batch.js +24 -0
- package/dist/schema/batch.js.map +1 -0
- package/dist/schema/common.d.ts +8 -2
- package/dist/schema/common.d.ts.map +1 -1
- package/dist/schema/common.js +42 -31
- package/dist/schema/common.js.map +1 -1
- package/dist/schema/inbox.d.ts +12 -12
- package/dist/schema/inbox.js +4 -4
- package/dist/schema/inbox.js.map +1 -1
- package/dist/schema/index.d.ts +8 -5
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +8 -5
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/meta.d.ts +1454 -27
- package/dist/schema/meta.d.ts.map +1 -1
- package/dist/schema/meta.js +198 -21
- package/dist/schema/meta.js.map +1 -1
- package/dist/schema/plan.d.ts +285 -0
- package/dist/schema/plan.d.ts.map +1 -0
- package/dist/schema/plan.js +81 -0
- package/dist/schema/plan.js.map +1 -0
- package/dist/schema/spec.d.ts +72 -33
- package/dist/schema/spec.d.ts.map +1 -1
- package/dist/schema/spec.js +22 -9
- package/dist/schema/spec.js.map +1 -1
- package/dist/schema/task.d.ts +172 -161
- package/dist/schema/task.d.ts.map +1 -1
- package/dist/schema/task.js +21 -12
- package/dist/schema/task.js.map +1 -1
- package/dist/schema/triage.d.ts +266 -0
- package/dist/schema/triage.d.ts.map +1 -0
- package/dist/schema/triage.js +134 -0
- package/dist/schema/triage.js.map +1 -0
- package/dist/sessions/index.d.ts +2 -2
- package/dist/sessions/index.d.ts.map +1 -1
- package/dist/sessions/index.js +3 -3
- package/dist/sessions/index.js.map +1 -1
- package/dist/sessions/store.d.ts +241 -1
- package/dist/sessions/store.d.ts.map +1 -1
- package/dist/sessions/store.js +810 -31
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/types.d.ts +10 -10
- package/dist/sessions/types.d.ts.map +1 -1
- package/dist/sessions/types.js +10 -9
- package/dist/sessions/types.js.map +1 -1
- package/dist/strings/errors.d.ts +55 -0
- package/dist/strings/errors.d.ts.map +1 -1
- package/dist/strings/errors.js +138 -106
- package/dist/strings/errors.js.map +1 -1
- package/dist/strings/guidance.d.ts.map +1 -1
- package/dist/strings/guidance.js +16 -16
- package/dist/strings/guidance.js.map +1 -1
- package/dist/strings/index.d.ts +4 -4
- package/dist/strings/index.d.ts.map +1 -1
- package/dist/strings/index.js +4 -4
- package/dist/strings/index.js.map +1 -1
- package/dist/strings/labels.d.ts +4 -0
- package/dist/strings/labels.d.ts.map +1 -1
- package/dist/strings/labels.js +45 -41
- package/dist/strings/labels.js.map +1 -1
- package/dist/strings/validation.d.ts.map +1 -1
- package/dist/strings/validation.js +71 -71
- package/dist/strings/validation.js.map +1 -1
- package/dist/triage/actions.d.ts +27 -0
- package/dist/triage/actions.d.ts.map +1 -0
- package/dist/triage/actions.js +95 -0
- package/dist/triage/actions.js.map +1 -0
- package/dist/triage/constants.d.ts +6 -0
- package/dist/triage/constants.d.ts.map +1 -0
- package/dist/triage/constants.js +7 -0
- package/dist/triage/constants.js.map +1 -0
- package/dist/triage/index.d.ts +3 -0
- package/dist/triage/index.d.ts.map +1 -0
- package/dist/triage/index.js +3 -0
- package/dist/triage/index.js.map +1 -0
- package/dist/utils/commit.d.ts +1 -1
- package/dist/utils/commit.d.ts.map +1 -1
- package/dist/utils/commit.js +28 -26
- package/dist/utils/commit.js.map +1 -1
- package/dist/utils/git.d.ts +1 -1
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +40 -38
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/grep.js +11 -11
- package/dist/utils/grep.js.map +1 -1
- package/dist/utils/index.d.ts +7 -7
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +4 -4
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/time.d.ts.map +1 -1
- package/dist/utils/time.js +10 -10
- package/dist/utils/time.js.map +1 -1
- package/package.json +28 -5
- package/plugin/.claude-plugin/marketplace.json +17 -0
- package/plugin/.claude-plugin/plugin.json +5 -0
- package/plugin/plugins/kspec/skills/create-workflow/SKILL.md +235 -0
- package/plugin/plugins/kspec/skills/help/SKILL.md +42 -0
- package/plugin/plugins/kspec/skills/observations/SKILL.md +143 -0
- package/plugin/plugins/kspec/skills/plan/SKILL.md +343 -0
- package/plugin/plugins/kspec/skills/reflect/SKILL.md +161 -0
- package/plugin/plugins/kspec/skills/review/SKILL.md +193 -0
- package/plugin/plugins/kspec/skills/task-work/SKILL.md +303 -0
- package/plugin/plugins/kspec/skills/triage/SKILL.md +206 -0
- package/plugin/plugins/kspec/skills/triage/docs/automation.md +120 -0
- package/plugin/plugins/kspec/skills/triage/docs/inbox.md +144 -0
- package/plugin/plugins/kspec/skills/triage/docs/observations.md +85 -0
- package/plugin/plugins/kspec/skills/triage-automation/SKILL.md +140 -0
- package/plugin/plugins/kspec/skills/triage-inbox/SKILL.md +232 -0
- package/plugin/plugins/kspec/skills/writing-specs/SKILL.md +340 -0
- package/templates/agents-sections/01-quick-start.md +22 -0
- package/templates/agents-sections/02-shadow-branch.md +34 -0
- package/templates/agents-sections/03-task-lifecycle.md +48 -0
- package/templates/agents-sections/04-pr-workflow.md +17 -0
- package/templates/agents-sections/05-commit-convention.md +27 -0
- package/templates/agents-sections/06-ralph-loop.md +45 -0
- package/templates/hooks/pre-commit +34 -0
- package/templates/skills/create-workflow/SKILL.md +228 -0
- package/templates/skills/help/SKILL.md +37 -0
- package/templates/skills/manifest.yaml +60 -0
- package/templates/skills/observations/SKILL.md +137 -0
- package/templates/skills/plan/SKILL.md +336 -0
- package/templates/skills/reflect/SKILL.md +155 -0
- package/templates/skills/review/SKILL.md +186 -0
- package/templates/skills/task-work/SKILL.md +296 -0
- package/templates/skills/triage/SKILL.md +199 -0
- package/templates/skills/triage/docs/automation.md +120 -0
- package/templates/skills/triage/docs/inbox.md +144 -0
- package/templates/skills/triage/docs/observations.md +85 -0
- package/templates/skills/triage-automation/SKILL.md +134 -0
- package/templates/skills/triage-inbox/SKILL.md +225 -0
- package/templates/skills/writing-specs/SKILL.md +333 -0
|
@@ -0,0 +1,851 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow run CLI commands
|
|
3
|
+
*
|
|
4
|
+
* Implements workflow run lifecycle management:
|
|
5
|
+
* - kspec workflow start @ref [--task @ref] [--json]
|
|
6
|
+
* - kspec workflow runs [--active] [--completed] [--workflow @ref] [--json]
|
|
7
|
+
* - kspec workflow show @run [--json]
|
|
8
|
+
* - kspec workflow abort @run [--reason text] [--json]
|
|
9
|
+
* - kspec workflow complete @run [--result <result>] [--json]
|
|
10
|
+
*/
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
import Table from "cli-table3";
|
|
13
|
+
import { ulid } from "ulid";
|
|
14
|
+
import { markMutating } from "../command-annotations.js";
|
|
15
|
+
import { deleteWorkflowRuns, findActiveRuns, findWorkflowRunByRef, getAuthor, initContext, loadAllTasks, loadMetaContext, loadWorkflowRuns, ReferenceIndex, saveWorkflowRun, updateWorkflowRun, } from "../../parser/index.js";
|
|
16
|
+
import { commitIfShadow } from "../../parser/shadow.js";
|
|
17
|
+
import { WorkflowRunResultSchema } from "../../schema/index.js";
|
|
18
|
+
import { errors } from "../../strings/errors.js";
|
|
19
|
+
import { EXIT_CODES } from "../exit-codes.js";
|
|
20
|
+
import { error, isJsonMode, output, success } from "../output.js";
|
|
21
|
+
/**
|
|
22
|
+
* Find a workflow by reference
|
|
23
|
+
*/
|
|
24
|
+
function resolveWorkflowRef(ref, workflows) {
|
|
25
|
+
const cleanRef = ref.startsWith("@") ? ref.slice(1) : ref;
|
|
26
|
+
// Try by ID first
|
|
27
|
+
let workflow = workflows.find((w) => w.id === cleanRef);
|
|
28
|
+
if (workflow)
|
|
29
|
+
return workflow;
|
|
30
|
+
// Try by ULID or ULID prefix
|
|
31
|
+
workflow = workflows.find((w) => w._ulid === cleanRef ||
|
|
32
|
+
w._ulid.toLowerCase().startsWith(cleanRef.toLowerCase()));
|
|
33
|
+
return workflow || null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Format a short ULID (first 8 chars)
|
|
37
|
+
*/
|
|
38
|
+
function shortUlid(ulid) {
|
|
39
|
+
return ulid.slice(0, 8).toUpperCase();
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Format run status with color
|
|
43
|
+
*/
|
|
44
|
+
function formatStatus(status) {
|
|
45
|
+
switch (status) {
|
|
46
|
+
case "active":
|
|
47
|
+
return chalk.green(status);
|
|
48
|
+
case "paused":
|
|
49
|
+
return chalk.yellow(status);
|
|
50
|
+
case "completed":
|
|
51
|
+
return chalk.blue(status);
|
|
52
|
+
case "aborted":
|
|
53
|
+
return chalk.red(status);
|
|
54
|
+
default:
|
|
55
|
+
return status;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Command: kspec workflow start @workflow-ref [--task @task-ref] [--json]
|
|
60
|
+
* AC: @workflow-run-foundation ac-1, ac-6
|
|
61
|
+
*/
|
|
62
|
+
async function workflowStart(workflowRef, options) {
|
|
63
|
+
const ctx = await initContext();
|
|
64
|
+
const metaCtx = await loadMetaContext(ctx);
|
|
65
|
+
// DEBUG: Log loaded workflows
|
|
66
|
+
// Resolve workflow reference
|
|
67
|
+
const workflow = resolveWorkflowRef(workflowRef, metaCtx.workflows);
|
|
68
|
+
if (!workflow) {
|
|
69
|
+
error(errors.workflowRun.workflowNotFound(workflowRef));
|
|
70
|
+
process.exit(EXIT_CODES.NOT_FOUND);
|
|
71
|
+
}
|
|
72
|
+
// Validate task reference if provided (AC: @workflow-run-foundation ac-6)
|
|
73
|
+
let taskRef;
|
|
74
|
+
if (options.task) {
|
|
75
|
+
const tasks = await loadAllTasks(ctx);
|
|
76
|
+
const index = new ReferenceIndex(tasks, []);
|
|
77
|
+
const result = index.resolve(options.task);
|
|
78
|
+
if (!result.ok) {
|
|
79
|
+
error(errors.reference.taskNotFound(options.task));
|
|
80
|
+
process.exit(EXIT_CODES.NOT_FOUND);
|
|
81
|
+
}
|
|
82
|
+
taskRef = `@${result.ulid}`;
|
|
83
|
+
}
|
|
84
|
+
// Create new workflow run
|
|
85
|
+
const run = {
|
|
86
|
+
_ulid: ulid(),
|
|
87
|
+
workflow_ref: `@${workflow._ulid}`,
|
|
88
|
+
status: "active",
|
|
89
|
+
current_step: 0,
|
|
90
|
+
total_steps: workflow.steps.length,
|
|
91
|
+
started_at: new Date().toISOString(),
|
|
92
|
+
step_results: [],
|
|
93
|
+
initiated_by: getAuthor(ctx.config?.identity?.author),
|
|
94
|
+
task_ref: taskRef,
|
|
95
|
+
};
|
|
96
|
+
// Save the run
|
|
97
|
+
await saveWorkflowRun(ctx, run);
|
|
98
|
+
// Commit to shadow
|
|
99
|
+
await commitIfShadow(ctx.shadow, "workflow-start");
|
|
100
|
+
// Output result
|
|
101
|
+
if (isJsonMode()) {
|
|
102
|
+
output({
|
|
103
|
+
run_id: run._ulid,
|
|
104
|
+
workflow_ref: run.workflow_ref,
|
|
105
|
+
status: run.status,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
success(`Started workflow run: ${shortUlid(run._ulid)}`);
|
|
110
|
+
console.log(` Workflow: ${workflow.id}`);
|
|
111
|
+
console.log(` Steps: ${run.total_steps}`);
|
|
112
|
+
if (taskRef) {
|
|
113
|
+
console.log(` Linked task: ${taskRef}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Command: kspec workflow runs [--active] [--completed] [--workflow @ref] [--json]
|
|
119
|
+
* AC: @workflow-run-foundation ac-2
|
|
120
|
+
*/
|
|
121
|
+
async function workflowRuns(options) {
|
|
122
|
+
const ctx = await initContext();
|
|
123
|
+
const metaCtx = await loadMetaContext(ctx);
|
|
124
|
+
let runs = await loadWorkflowRuns(ctx);
|
|
125
|
+
// Apply filters
|
|
126
|
+
if (options.active) {
|
|
127
|
+
runs = runs.filter((r) => r.status === "active");
|
|
128
|
+
}
|
|
129
|
+
if (options.completed) {
|
|
130
|
+
runs = runs.filter((r) => r.status === "completed");
|
|
131
|
+
}
|
|
132
|
+
if (options.workflow) {
|
|
133
|
+
const workflow = resolveWorkflowRef(options.workflow, metaCtx.workflows);
|
|
134
|
+
if (!workflow) {
|
|
135
|
+
error(errors.workflowRun.workflowNotFound(options.workflow));
|
|
136
|
+
process.exit(EXIT_CODES.NOT_FOUND);
|
|
137
|
+
}
|
|
138
|
+
runs = runs.filter((r) => r.workflow_ref === `@${workflow._ulid}`);
|
|
139
|
+
}
|
|
140
|
+
if (isJsonMode()) {
|
|
141
|
+
output({ runs });
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
if (runs.length === 0) {
|
|
145
|
+
console.log(chalk.gray("No workflow runs found"));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const table = new Table({
|
|
149
|
+
head: ["ID", "Workflow", "Status", "Step", "Started"],
|
|
150
|
+
colWidths: [12, 25, 12, 10, 20],
|
|
151
|
+
});
|
|
152
|
+
for (const run of runs) {
|
|
153
|
+
const workflow = metaCtx.workflows.find((w) => `@${w._ulid}` === run.workflow_ref);
|
|
154
|
+
const workflowName = workflow?.id || run.workflow_ref;
|
|
155
|
+
const stepProgress = `${run.current_step + 1}/${run.total_steps}`;
|
|
156
|
+
const started = new Date(run.started_at).toLocaleString();
|
|
157
|
+
table.push([
|
|
158
|
+
shortUlid(run._ulid),
|
|
159
|
+
workflowName,
|
|
160
|
+
formatStatus(run.status),
|
|
161
|
+
stepProgress,
|
|
162
|
+
started,
|
|
163
|
+
]);
|
|
164
|
+
}
|
|
165
|
+
console.log(table.toString());
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Command: kspec workflow show @run-id [--json]
|
|
170
|
+
* AC: @workflow-run-foundation ac-4
|
|
171
|
+
*/
|
|
172
|
+
async function workflowShow(runRef, _options) {
|
|
173
|
+
const ctx = await initContext();
|
|
174
|
+
const metaCtx = await loadMetaContext(ctx);
|
|
175
|
+
const run = await findWorkflowRunByRef(ctx, runRef);
|
|
176
|
+
if (!run) {
|
|
177
|
+
error(errors.workflowRun.runNotFound(runRef));
|
|
178
|
+
process.exit(EXIT_CODES.NOT_FOUND);
|
|
179
|
+
}
|
|
180
|
+
if (isJsonMode()) {
|
|
181
|
+
output({ run });
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
const workflow = metaCtx.workflows.find((w) => `@${w._ulid}` === run.workflow_ref);
|
|
185
|
+
const workflowName = workflow?.id || run.workflow_ref;
|
|
186
|
+
console.log(chalk.bold("Workflow Run Details"));
|
|
187
|
+
console.log(chalk.gray("─".repeat(50)));
|
|
188
|
+
console.log(`ID: ${shortUlid(run._ulid)}`);
|
|
189
|
+
console.log(`Workflow: ${workflowName} (${run.workflow_ref})`);
|
|
190
|
+
console.log(`Status: ${formatStatus(run.status)}`);
|
|
191
|
+
console.log(`Progress: ${run.current_step + 1}/${run.total_steps}`);
|
|
192
|
+
console.log(`Started: ${new Date(run.started_at).toLocaleString()}`);
|
|
193
|
+
if (run.initiated_by) {
|
|
194
|
+
console.log(`Initiated by: ${run.initiated_by}`);
|
|
195
|
+
}
|
|
196
|
+
if (run.task_ref) {
|
|
197
|
+
console.log(`Task: ${run.task_ref}`);
|
|
198
|
+
}
|
|
199
|
+
if (run.paused_at) {
|
|
200
|
+
console.log(`Paused: ${new Date(run.paused_at).toLocaleString()}`);
|
|
201
|
+
}
|
|
202
|
+
if (run.completed_at) {
|
|
203
|
+
console.log(`Completed: ${new Date(run.completed_at).toLocaleString()}`);
|
|
204
|
+
}
|
|
205
|
+
if (run.abort_reason) {
|
|
206
|
+
console.log(`Abort reason: ${run.abort_reason}`);
|
|
207
|
+
}
|
|
208
|
+
if (run.step_results.length > 0) {
|
|
209
|
+
console.log(chalk.gray("\nStep Results:"));
|
|
210
|
+
const table = new Table({
|
|
211
|
+
head: ["Step", "Status", "Started", "Completed"],
|
|
212
|
+
colWidths: [8, 12, 20, 20],
|
|
213
|
+
});
|
|
214
|
+
for (const result of run.step_results) {
|
|
215
|
+
table.push([
|
|
216
|
+
(result.step_index + 1).toString(),
|
|
217
|
+
formatStatus(result.status),
|
|
218
|
+
new Date(result.started_at).toLocaleString(),
|
|
219
|
+
new Date(result.completed_at).toLocaleString(),
|
|
220
|
+
]);
|
|
221
|
+
}
|
|
222
|
+
console.log(table.toString());
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Command: kspec workflow abort @run-id [--reason text] [--json]
|
|
228
|
+
* AC: @workflow-run-foundation ac-3, ac-5
|
|
229
|
+
*/
|
|
230
|
+
async function workflowAbort(runRef, options) {
|
|
231
|
+
const ctx = await initContext();
|
|
232
|
+
const run = await findWorkflowRunByRef(ctx, runRef);
|
|
233
|
+
if (!run) {
|
|
234
|
+
error(errors.workflowRun.runNotFound(runRef));
|
|
235
|
+
process.exit(EXIT_CODES.NOT_FOUND);
|
|
236
|
+
}
|
|
237
|
+
// AC: @workflow-run-foundation ac-5 - Cannot abort completed or aborted runs
|
|
238
|
+
if (run.status === "completed") {
|
|
239
|
+
error(errors.workflowRun.cannotAbortCompleted);
|
|
240
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
241
|
+
}
|
|
242
|
+
if (run.status === "aborted") {
|
|
243
|
+
error(errors.workflowRun.cannotAbortAborted);
|
|
244
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
245
|
+
}
|
|
246
|
+
// Update run status
|
|
247
|
+
run.status = "aborted";
|
|
248
|
+
run.abort_reason = options.reason;
|
|
249
|
+
run.completed_at = new Date().toISOString();
|
|
250
|
+
await updateWorkflowRun(ctx, run);
|
|
251
|
+
await commitIfShadow(ctx.shadow, "workflow-abort");
|
|
252
|
+
if (isJsonMode()) {
|
|
253
|
+
output({ run_id: run._ulid, status: run.status });
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
success(`Aborted workflow run: ${shortUlid(run._ulid)}`);
|
|
257
|
+
if (options.reason) {
|
|
258
|
+
console.log(` Reason: ${options.reason}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Command: kspec workflow complete @run-id [--result <result>] [--json]
|
|
264
|
+
* Marks a workflow run as completed with an optional result.
|
|
265
|
+
* Use this for graceful exits (e.g., no_work_available) instead of aborting.
|
|
266
|
+
*/
|
|
267
|
+
async function workflowComplete(runRef, options) {
|
|
268
|
+
const ctx = await initContext();
|
|
269
|
+
const run = await findWorkflowRunByRef(ctx, runRef);
|
|
270
|
+
if (!run) {
|
|
271
|
+
error(errors.workflowRun.runNotFound(runRef));
|
|
272
|
+
process.exit(EXIT_CODES.NOT_FOUND);
|
|
273
|
+
}
|
|
274
|
+
// Cannot complete already finished runs
|
|
275
|
+
if (run.status === "completed") {
|
|
276
|
+
error(`Cannot complete run ${shortUlid(run._ulid)}: already completed`);
|
|
277
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
278
|
+
}
|
|
279
|
+
if (run.status === "aborted") {
|
|
280
|
+
error(`Cannot complete run ${shortUlid(run._ulid)}: already aborted`);
|
|
281
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
282
|
+
}
|
|
283
|
+
// Validate result if provided
|
|
284
|
+
let result;
|
|
285
|
+
if (options.result) {
|
|
286
|
+
const parseResult = WorkflowRunResultSchema.safeParse(options.result);
|
|
287
|
+
if (!parseResult.success) {
|
|
288
|
+
const validResults = WorkflowRunResultSchema.options.join(", ");
|
|
289
|
+
error(`Invalid result: ${options.result}. Must be one of: ${validResults}`);
|
|
290
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
291
|
+
}
|
|
292
|
+
result = parseResult.data;
|
|
293
|
+
}
|
|
294
|
+
// Update run status
|
|
295
|
+
run.status = "completed";
|
|
296
|
+
run.completed_at = new Date().toISOString();
|
|
297
|
+
if (result) {
|
|
298
|
+
run.result = result;
|
|
299
|
+
}
|
|
300
|
+
await updateWorkflowRun(ctx, run);
|
|
301
|
+
await commitIfShadow(ctx.shadow, "workflow-complete");
|
|
302
|
+
if (isJsonMode()) {
|
|
303
|
+
output({
|
|
304
|
+
run_id: run._ulid,
|
|
305
|
+
status: run.status,
|
|
306
|
+
result: run.result,
|
|
307
|
+
completed_at: run.completed_at,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
const resultInfo = run.result ? ` (${run.result})` : "";
|
|
312
|
+
success(`Completed workflow run: ${shortUlid(run._ulid)}${resultInfo}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Command: kspec workflow pause @run-id [--json]
|
|
317
|
+
* AC: @workflow-advanced-features ac-1
|
|
318
|
+
*/
|
|
319
|
+
async function workflowPause(runRef, _options) {
|
|
320
|
+
const ctx = await initContext();
|
|
321
|
+
const run = await findWorkflowRunByRef(ctx, runRef);
|
|
322
|
+
if (!run) {
|
|
323
|
+
error(errors.workflowRun.runNotFound(runRef));
|
|
324
|
+
process.exit(EXIT_CODES.NOT_FOUND);
|
|
325
|
+
}
|
|
326
|
+
// Only active runs can be paused
|
|
327
|
+
if (run.status !== "active") {
|
|
328
|
+
error(`Cannot pause run ${shortUlid(run._ulid)}: current status is ${run.status}`);
|
|
329
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
330
|
+
}
|
|
331
|
+
// Update run status
|
|
332
|
+
run.status = "paused";
|
|
333
|
+
run.paused_at = new Date().toISOString();
|
|
334
|
+
await updateWorkflowRun(ctx, run);
|
|
335
|
+
await commitIfShadow(ctx.shadow, "workflow-pause");
|
|
336
|
+
if (isJsonMode()) {
|
|
337
|
+
output({ run_id: run._ulid, status: run.status, paused_at: run.paused_at });
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
success(`Paused run ${shortUlid(run._ulid)} at step ${run.current_step + 1}/${run.total_steps}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Command: kspec workflow resume @run-id [--json]
|
|
345
|
+
* AC: @workflow-advanced-features ac-2, ac-4
|
|
346
|
+
*/
|
|
347
|
+
async function workflowResume(runRef, _options) {
|
|
348
|
+
const ctx = await initContext();
|
|
349
|
+
const metaCtx = await loadMetaContext(ctx);
|
|
350
|
+
const run = await findWorkflowRunByRef(ctx, runRef);
|
|
351
|
+
if (!run) {
|
|
352
|
+
error(errors.workflowRun.runNotFound(runRef));
|
|
353
|
+
process.exit(EXIT_CODES.NOT_FOUND);
|
|
354
|
+
}
|
|
355
|
+
// AC: @workflow-advanced-features ac-4 - Error if not paused
|
|
356
|
+
if (run.status !== "paused") {
|
|
357
|
+
error(`Cannot resume run ${shortUlid(run._ulid)}: current status is ${run.status} (expected paused)`);
|
|
358
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
359
|
+
}
|
|
360
|
+
// Update run status
|
|
361
|
+
run.status = "active";
|
|
362
|
+
run.paused_at = undefined;
|
|
363
|
+
await updateWorkflowRun(ctx, run);
|
|
364
|
+
await commitIfShadow(ctx.shadow, "workflow-resume");
|
|
365
|
+
// Get workflow definition to show current step
|
|
366
|
+
const workflow = metaCtx.workflows.find((w) => `@${w._ulid}` === run.workflow_ref);
|
|
367
|
+
if (!workflow) {
|
|
368
|
+
error(errors.workflowRun.workflowNotFound(run.workflow_ref));
|
|
369
|
+
process.exit(EXIT_CODES.NOT_FOUND);
|
|
370
|
+
}
|
|
371
|
+
const currentStep = workflow.steps[run.current_step];
|
|
372
|
+
if (isJsonMode()) {
|
|
373
|
+
output({
|
|
374
|
+
run_id: run._ulid,
|
|
375
|
+
status: run.status,
|
|
376
|
+
current_step: run.current_step,
|
|
377
|
+
total_steps: run.total_steps,
|
|
378
|
+
next_step: {
|
|
379
|
+
type: currentStep.type,
|
|
380
|
+
content: currentStep.content,
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
success(`Resumed run ${shortUlid(run._ulid)}`);
|
|
386
|
+
console.log(`\nStep ${run.current_step + 1}/${run.total_steps}: [${currentStep.type}] ${currentStep.content}`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Command: kspec workflow next [run-ref] [--skip] [--notes text] [--confirm] [--force] [--input key=value] [--json]
|
|
391
|
+
* AC: @workflow-step-navigation ac-1 through ac-6
|
|
392
|
+
* AC: @workflow-enforcement-modes ac-1 through ac-4
|
|
393
|
+
* AC: @workflow-advanced-features ac-3
|
|
394
|
+
*/
|
|
395
|
+
async function workflowNext(runRef, options) {
|
|
396
|
+
const ctx = await initContext();
|
|
397
|
+
const metaCtx = await loadMetaContext(ctx);
|
|
398
|
+
let run;
|
|
399
|
+
// AC: @workflow-step-navigation ac-3, ac-4, ac-5
|
|
400
|
+
if (!runRef) {
|
|
401
|
+
const activeRuns = await findActiveRuns(ctx);
|
|
402
|
+
if (activeRuns.length === 0) {
|
|
403
|
+
// AC: @workflow-step-navigation ac-5
|
|
404
|
+
error(errors.workflowRun.noActiveRuns);
|
|
405
|
+
process.exit(EXIT_CODES.NOT_FOUND);
|
|
406
|
+
}
|
|
407
|
+
if (activeRuns.length > 1) {
|
|
408
|
+
// AC: @workflow-step-navigation ac-4
|
|
409
|
+
const runIds = activeRuns.map((r) => shortUlid(r._ulid));
|
|
410
|
+
error(errors.workflowRun.multipleActiveRuns(runIds));
|
|
411
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
412
|
+
}
|
|
413
|
+
// AC: @workflow-step-navigation ac-3 - exactly one active run
|
|
414
|
+
run = activeRuns[0];
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
run = await findWorkflowRunByRef(ctx, runRef);
|
|
418
|
+
if (!run) {
|
|
419
|
+
error(errors.workflowRun.runNotFound(runRef));
|
|
420
|
+
process.exit(EXIT_CODES.NOT_FOUND);
|
|
421
|
+
}
|
|
422
|
+
if (run.status !== "active") {
|
|
423
|
+
error(errors.workflowRun.runNotActive(runRef, run.status));
|
|
424
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
// Get workflow definition to access steps
|
|
428
|
+
const workflow = metaCtx.workflows.find((w) => `@${w._ulid}` === run.workflow_ref);
|
|
429
|
+
if (!workflow) {
|
|
430
|
+
error(errors.workflowRun.workflowNotFound(run.workflow_ref));
|
|
431
|
+
process.exit(EXIT_CODES.NOT_FOUND);
|
|
432
|
+
}
|
|
433
|
+
// Get current step and next step
|
|
434
|
+
const currentStepIndex = run.current_step;
|
|
435
|
+
const currentStep = workflow.steps[currentStepIndex];
|
|
436
|
+
const isStrictMode = workflow.enforcement === "strict";
|
|
437
|
+
const isLastStep = currentStepIndex === run.total_steps - 1;
|
|
438
|
+
// AC: @workflow-advanced-features ac-3 - Handle step inputs
|
|
439
|
+
const stepInputs = {};
|
|
440
|
+
if (currentStep.inputs && currentStep.inputs.length > 0) {
|
|
441
|
+
// Display required inputs if not in JSON mode
|
|
442
|
+
if (!isJsonMode()) {
|
|
443
|
+
console.log(`Step ${currentStepIndex + 1}/${run.total_steps}: [${currentStep.type}] ${currentStep.content}`);
|
|
444
|
+
console.log(chalk.cyan(" Inputs required:"));
|
|
445
|
+
for (const input of currentStep.inputs) {
|
|
446
|
+
const requiredLabel = input.required !== false ? "" : " (optional)";
|
|
447
|
+
const typeLabel = input.type ? ` (${input.type})` : "";
|
|
448
|
+
const desc = input.description ? `: ${input.description}` : "";
|
|
449
|
+
console.log(chalk.cyan(` - ${input.name}${typeLabel}${requiredLabel}${desc}`));
|
|
450
|
+
}
|
|
451
|
+
console.log();
|
|
452
|
+
}
|
|
453
|
+
// Parse --input flags
|
|
454
|
+
if (options.input) {
|
|
455
|
+
for (const inputStr of options.input) {
|
|
456
|
+
const [key, ...valueParts] = inputStr.split("=");
|
|
457
|
+
if (!valueParts.length) {
|
|
458
|
+
error(`Invalid input format: "${inputStr}". Expected format: key=value`);
|
|
459
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
460
|
+
}
|
|
461
|
+
stepInputs[key] = valueParts.join("="); // Rejoin in case value contains '='
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
// Validate required inputs
|
|
465
|
+
for (const input of currentStep.inputs) {
|
|
466
|
+
if (input.required !== false && !stepInputs[input.name]) {
|
|
467
|
+
error(`Missing required input: ${input.name}`);
|
|
468
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
// AC: @workflow-enforcement-modes ac-3 - Strict mode: --skip requires --force
|
|
473
|
+
if (options.skip && isStrictMode && !options.force) {
|
|
474
|
+
error(errors.workflowRun.skipRequiresForce);
|
|
475
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
476
|
+
}
|
|
477
|
+
// AC: @workflow-enforcement-modes ac-2, ac-3 - Check exit criteria for CURRENT step
|
|
478
|
+
if (currentStep.exit_criteria &&
|
|
479
|
+
currentStep.exit_criteria.length > 0 &&
|
|
480
|
+
!options.skip) {
|
|
481
|
+
// AC: @workflow-enforcement-modes ac-3 - Strict mode requires --confirm
|
|
482
|
+
if (isStrictMode && !options.confirm) {
|
|
483
|
+
if (!isJsonMode()) {
|
|
484
|
+
console.log(`Completing step ${currentStepIndex + 1}/${run.total_steps}: [${currentStep.type}] ${currentStep.content}`);
|
|
485
|
+
console.log(chalk.yellow(" Exit criteria:"));
|
|
486
|
+
for (const criterion of currentStep.exit_criteria) {
|
|
487
|
+
console.log(chalk.yellow(` - ${criterion}`));
|
|
488
|
+
}
|
|
489
|
+
console.log();
|
|
490
|
+
}
|
|
491
|
+
error(errors.workflowRun.exitCriteriaNotConfirmed);
|
|
492
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
493
|
+
}
|
|
494
|
+
// AC: @workflow-enforcement-modes ac-4 - Advisory mode shows criteria as guidance
|
|
495
|
+
if (!isStrictMode && !isJsonMode()) {
|
|
496
|
+
console.log(chalk.gray(" Exit criteria:"));
|
|
497
|
+
for (const criterion of currentStep.exit_criteria) {
|
|
498
|
+
console.log(chalk.gray(` - ${criterion}`));
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
// AC: @workflow-enforcement-modes ac-1, ac-3 - Check entry criteria for NEXT step
|
|
503
|
+
// (Only applies when advancing from current step normally, not when skipping or completing the workflow)
|
|
504
|
+
// When skipping, we don't check entry criteria because we're not actually starting the next step yet
|
|
505
|
+
if (!isLastStep && !options.skip) {
|
|
506
|
+
const nextStep = workflow.steps[currentStepIndex + 1];
|
|
507
|
+
if (nextStep.entry_criteria && nextStep.entry_criteria.length > 0) {
|
|
508
|
+
// AC: @workflow-enforcement-modes ac-3 - Strict mode requires --confirm
|
|
509
|
+
if (isStrictMode && !options.confirm) {
|
|
510
|
+
if (!isJsonMode()) {
|
|
511
|
+
console.log(`Step ${currentStepIndex + 2}/${run.total_steps}: [${nextStep.type}] ${nextStep.content}`);
|
|
512
|
+
console.log(chalk.yellow(" Entry criteria:"));
|
|
513
|
+
for (const criterion of nextStep.entry_criteria) {
|
|
514
|
+
console.log(chalk.yellow(` - ${criterion}`));
|
|
515
|
+
}
|
|
516
|
+
console.log();
|
|
517
|
+
}
|
|
518
|
+
error(errors.workflowRun.entryCriteriaNotConfirmed);
|
|
519
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
520
|
+
}
|
|
521
|
+
// AC: @workflow-enforcement-modes ac-4 - Advisory mode shows criteria as guidance
|
|
522
|
+
if (!isStrictMode && !isJsonMode()) {
|
|
523
|
+
console.log(`Step ${currentStepIndex + 2}/${run.total_steps}: [${nextStep.type}] ${nextStep.content}`);
|
|
524
|
+
console.log(chalk.gray(" Entry criteria:"));
|
|
525
|
+
for (const criterion of nextStep.entry_criteria) {
|
|
526
|
+
console.log(chalk.gray(` - ${criterion}`));
|
|
527
|
+
}
|
|
528
|
+
console.log();
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
// AC: @workflow-step-navigation ac-1, ac-6 - Complete current step
|
|
533
|
+
const previousResult = run.step_results[run.step_results.length - 1];
|
|
534
|
+
const startedAt = previousResult
|
|
535
|
+
? previousResult.completed_at
|
|
536
|
+
: run.started_at;
|
|
537
|
+
// AC: @workflow-enforcement-modes ac-3 - Record confirmations in StepResult
|
|
538
|
+
// Check if a stub result already exists for this step (created when we advanced to it)
|
|
539
|
+
const existingResultIndex = run.step_results.findIndex((r) => r.step_index === currentStepIndex);
|
|
540
|
+
if (existingResultIndex >= 0) {
|
|
541
|
+
// Update existing stub result with completion data
|
|
542
|
+
const existingResult = run.step_results[existingResultIndex];
|
|
543
|
+
run.step_results[existingResultIndex] = {
|
|
544
|
+
...existingResult,
|
|
545
|
+
status: options.skip ? "skipped" : "completed",
|
|
546
|
+
completed_at: new Date().toISOString(),
|
|
547
|
+
notes: options.notes,
|
|
548
|
+
exit_confirmed: currentStep.exit_criteria &&
|
|
549
|
+
currentStep.exit_criteria.length > 0 &&
|
|
550
|
+
options.confirm
|
|
551
|
+
? true
|
|
552
|
+
: undefined,
|
|
553
|
+
inputs: Object.keys(stepInputs).length > 0 ? stepInputs : undefined,
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
// No stub exists (first step or old data), create complete result
|
|
558
|
+
const stepResult = {
|
|
559
|
+
step_index: currentStepIndex,
|
|
560
|
+
status: options.skip ? "skipped" : "completed",
|
|
561
|
+
started_at: startedAt,
|
|
562
|
+
completed_at: new Date().toISOString(),
|
|
563
|
+
notes: options.notes,
|
|
564
|
+
exit_confirmed: currentStep.exit_criteria &&
|
|
565
|
+
currentStep.exit_criteria.length > 0 &&
|
|
566
|
+
options.confirm
|
|
567
|
+
? true
|
|
568
|
+
: undefined,
|
|
569
|
+
entry_confirmed: undefined,
|
|
570
|
+
inputs: Object.keys(stepInputs).length > 0 ? stepInputs : undefined,
|
|
571
|
+
};
|
|
572
|
+
run.step_results.push(stepResult);
|
|
573
|
+
}
|
|
574
|
+
// AC: @workflow-step-navigation ac-1 - Advance to next step or complete run
|
|
575
|
+
if (isLastStep) {
|
|
576
|
+
// AC: @workflow-step-navigation ac-2 - Complete the run
|
|
577
|
+
run.status = "completed";
|
|
578
|
+
run.completed_at = new Date().toISOString();
|
|
579
|
+
await updateWorkflowRun(ctx, run);
|
|
580
|
+
await commitIfShadow(ctx.shadow, "workflow-next");
|
|
581
|
+
// Calculate summary stats
|
|
582
|
+
const totalDuration = new Date(run.completed_at).getTime() - new Date(run.started_at).getTime();
|
|
583
|
+
const completedSteps = run.step_results.filter((r) => r.status === "completed").length;
|
|
584
|
+
const skippedSteps = run.step_results.filter((r) => r.status === "skipped").length;
|
|
585
|
+
if (isJsonMode()) {
|
|
586
|
+
output({
|
|
587
|
+
run_id: run._ulid,
|
|
588
|
+
status: run.status,
|
|
589
|
+
completed_at: run.completed_at,
|
|
590
|
+
total_duration_ms: totalDuration,
|
|
591
|
+
steps_completed: completedSteps,
|
|
592
|
+
steps_skipped: skippedSteps,
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
const currentStep = workflow.steps[currentStepIndex];
|
|
597
|
+
success(`Completed step ${currentStepIndex + 1}/${run.total_steps}: [${currentStep.type}] ${currentStep.content}`);
|
|
598
|
+
console.log();
|
|
599
|
+
console.log(chalk.bold("Workflow completed!"));
|
|
600
|
+
console.log(` Duration: ${Math.round(totalDuration / 1000)}s`);
|
|
601
|
+
console.log(` Steps completed: ${completedSteps}`);
|
|
602
|
+
console.log(` Steps skipped: ${skippedSteps}`);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
// Advance to next step
|
|
607
|
+
run.current_step += 1;
|
|
608
|
+
// AC: @workflow-enforcement-modes ac-3 - Record entry confirmation for the next step
|
|
609
|
+
// Create a stub step result for the next step to capture entry_confirmed and started_at
|
|
610
|
+
// This will be updated with completion data when the step is completed
|
|
611
|
+
const nextStep = workflow.steps[run.current_step];
|
|
612
|
+
const nextStepStartedAt = run.step_results[run.step_results.length - 1]?.completed_at ||
|
|
613
|
+
new Date().toISOString();
|
|
614
|
+
const nextStepStub = {
|
|
615
|
+
step_index: run.current_step,
|
|
616
|
+
status: "completed", // Placeholder, will be updated
|
|
617
|
+
started_at: nextStepStartedAt,
|
|
618
|
+
completed_at: nextStepStartedAt, // Placeholder, will be updated
|
|
619
|
+
entry_confirmed: nextStep.entry_criteria &&
|
|
620
|
+
nextStep.entry_criteria.length > 0 &&
|
|
621
|
+
options.confirm
|
|
622
|
+
? true
|
|
623
|
+
: undefined,
|
|
624
|
+
};
|
|
625
|
+
run.step_results.push(nextStepStub);
|
|
626
|
+
await updateWorkflowRun(ctx, run);
|
|
627
|
+
await commitIfShadow(ctx.shadow, "workflow-next");
|
|
628
|
+
if (isJsonMode()) {
|
|
629
|
+
output({
|
|
630
|
+
run_id: run._ulid,
|
|
631
|
+
current_step: run.current_step,
|
|
632
|
+
total_steps: run.total_steps,
|
|
633
|
+
next_step: {
|
|
634
|
+
type: nextStep.type,
|
|
635
|
+
content: nextStep.content,
|
|
636
|
+
},
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
else {
|
|
640
|
+
const previousStep = workflow.steps[currentStepIndex];
|
|
641
|
+
success(`Completed step ${currentStepIndex + 1}/${run.total_steps}: [${previousStep.type}] ${previousStep.content}`);
|
|
642
|
+
console.log();
|
|
643
|
+
console.log(`Step ${run.current_step + 1}/${run.total_steps}: [${nextStep.type}] ${nextStep.content}`);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Command: kspec workflow prune [--older-than <duration>] [--status <status>] [--abandoned] [--dry-run]
|
|
649
|
+
* AC: @workflow-prune ac-1, ac-2, ac-3, ac-4
|
|
650
|
+
*/
|
|
651
|
+
async function workflowPrune(options) {
|
|
652
|
+
const ctx = await initContext();
|
|
653
|
+
let runs = await loadWorkflowRuns(ctx);
|
|
654
|
+
// Track which runs to prune
|
|
655
|
+
let toPrune = [];
|
|
656
|
+
// AC: @workflow-prune ac-1 - Filter by age
|
|
657
|
+
if (options.olderThan) {
|
|
658
|
+
const match = options.olderThan.match(/^(\d+)([dhm])$/);
|
|
659
|
+
if (!match) {
|
|
660
|
+
error('Invalid duration format. Use format like: 30d (days), 12h (hours), 45m (minutes)');
|
|
661
|
+
process.exit(EXIT_CODES.USAGE_ERROR);
|
|
662
|
+
}
|
|
663
|
+
const [, amount, unit] = match;
|
|
664
|
+
const amountNum = parseInt(amount);
|
|
665
|
+
const milliseconds = {
|
|
666
|
+
m: amountNum * 60 * 1000,
|
|
667
|
+
h: amountNum * 60 * 60 * 1000,
|
|
668
|
+
d: amountNum * 24 * 60 * 60 * 1000,
|
|
669
|
+
}[unit];
|
|
670
|
+
if (milliseconds === undefined) {
|
|
671
|
+
error('Invalid duration unit. Use d (days), h (hours), or m (minutes)');
|
|
672
|
+
process.exit(EXIT_CODES.USAGE_ERROR);
|
|
673
|
+
}
|
|
674
|
+
const cutoffTime = Date.now() - milliseconds;
|
|
675
|
+
toPrune = runs.filter((r) => {
|
|
676
|
+
const runTime = new Date(r.started_at).getTime();
|
|
677
|
+
return runTime < cutoffTime;
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
// AC: @workflow-prune ac-2 - Filter by status
|
|
681
|
+
if (options.status) {
|
|
682
|
+
const validStatuses = ['active', 'paused', 'completed', 'aborted'];
|
|
683
|
+
if (!validStatuses.includes(options.status)) {
|
|
684
|
+
error(`Invalid status: ${options.status}. Must be one of: ${validStatuses.join(', ')}`);
|
|
685
|
+
process.exit(EXIT_CODES.USAGE_ERROR);
|
|
686
|
+
}
|
|
687
|
+
if (toPrune.length > 0) {
|
|
688
|
+
// Filter existing toPrune list
|
|
689
|
+
toPrune = toPrune.filter((r) => r.status === options.status);
|
|
690
|
+
}
|
|
691
|
+
else {
|
|
692
|
+
// No age filter, just status
|
|
693
|
+
toPrune = runs.filter((r) => r.status === options.status);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
// AC: @workflow-prune ac-3 - Filter by abandoned (active with no activity for 7+ days)
|
|
697
|
+
if (options.abandoned) {
|
|
698
|
+
const abandonedCutoff = Date.now() - 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
699
|
+
const abandonedRuns = runs.filter((r) => {
|
|
700
|
+
if (r.status !== 'active')
|
|
701
|
+
return false;
|
|
702
|
+
// Find most recent activity time
|
|
703
|
+
let lastActivity = new Date(r.started_at).getTime();
|
|
704
|
+
if (r.step_results.length > 0) {
|
|
705
|
+
const lastStep = r.step_results[r.step_results.length - 1];
|
|
706
|
+
lastActivity = new Date(lastStep.completed_at).getTime();
|
|
707
|
+
}
|
|
708
|
+
return lastActivity < abandonedCutoff;
|
|
709
|
+
});
|
|
710
|
+
if (toPrune.length > 0) {
|
|
711
|
+
// Intersect with existing toPrune list
|
|
712
|
+
const abandonedUlids = new Set(abandonedRuns.map((r) => r._ulid));
|
|
713
|
+
toPrune = toPrune.filter((r) => abandonedUlids.has(r._ulid));
|
|
714
|
+
}
|
|
715
|
+
else {
|
|
716
|
+
toPrune = abandonedRuns;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
// If no filters provided, error
|
|
720
|
+
if (!options.olderThan && !options.status && !options.abandoned) {
|
|
721
|
+
error('Must specify at least one filter: --older-than, --status, or --abandoned');
|
|
722
|
+
process.exit(EXIT_CODES.USAGE_ERROR);
|
|
723
|
+
}
|
|
724
|
+
// AC: @workflow-prune ac-4 - Dry run mode
|
|
725
|
+
if (options.dryRun || isJsonMode()) {
|
|
726
|
+
const output_data = {
|
|
727
|
+
would_delete: toPrune.length,
|
|
728
|
+
runs: toPrune.map((r) => ({
|
|
729
|
+
_ulid: r._ulid,
|
|
730
|
+
workflow_ref: r.workflow_ref,
|
|
731
|
+
status: r.status,
|
|
732
|
+
started_at: r.started_at,
|
|
733
|
+
})),
|
|
734
|
+
};
|
|
735
|
+
if (isJsonMode()) {
|
|
736
|
+
output(output_data);
|
|
737
|
+
}
|
|
738
|
+
else {
|
|
739
|
+
console.log(chalk.yellow(`Would delete ${toPrune.length} workflow run${toPrune.length === 1 ? '' : 's'}:`));
|
|
740
|
+
if (toPrune.length > 0) {
|
|
741
|
+
const table = new Table({
|
|
742
|
+
head: ['ID', 'Workflow', 'Status', 'Started'],
|
|
743
|
+
colWidths: [12, 25, 12, 20],
|
|
744
|
+
});
|
|
745
|
+
const metaCtx = await loadMetaContext(ctx);
|
|
746
|
+
for (const run of toPrune) {
|
|
747
|
+
const workflow = metaCtx.workflows.find((w) => `@${w._ulid}` === run.workflow_ref);
|
|
748
|
+
const workflowName = workflow?.id || run.workflow_ref;
|
|
749
|
+
const started = new Date(run.started_at).toLocaleString();
|
|
750
|
+
table.push([
|
|
751
|
+
shortUlid(run._ulid),
|
|
752
|
+
workflowName,
|
|
753
|
+
formatStatus(run.status),
|
|
754
|
+
started,
|
|
755
|
+
]);
|
|
756
|
+
}
|
|
757
|
+
console.log(table.toString());
|
|
758
|
+
console.log(chalk.gray('\nRun without --dry-run to actually delete these runs'));
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
// Actually delete
|
|
764
|
+
if (toPrune.length === 0) {
|
|
765
|
+
if (!isJsonMode()) {
|
|
766
|
+
console.log(chalk.gray('No runs match the specified criteria'));
|
|
767
|
+
}
|
|
768
|
+
else {
|
|
769
|
+
output({ deleted: 0 });
|
|
770
|
+
}
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
const ulidsToDelete = toPrune.map((r) => r._ulid);
|
|
774
|
+
await deleteWorkflowRuns(ctx, ulidsToDelete);
|
|
775
|
+
await commitIfShadow(ctx.shadow, 'workflow-prune');
|
|
776
|
+
if (isJsonMode()) {
|
|
777
|
+
output({ deleted: toPrune.length });
|
|
778
|
+
}
|
|
779
|
+
else {
|
|
780
|
+
success(`Deleted ${toPrune.length} workflow run${toPrune.length === 1 ? '' : 's'}`);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Register workflow commands
|
|
785
|
+
*/
|
|
786
|
+
export function registerWorkflowCommand(program) {
|
|
787
|
+
const workflow = program
|
|
788
|
+
.command("workflow")
|
|
789
|
+
.description("Manage workflow runs");
|
|
790
|
+
markMutating(workflow.command("start"))
|
|
791
|
+
.description("Start a new workflow run")
|
|
792
|
+
.argument("<workflow-ref>", "Workflow reference (@id or @ulid)")
|
|
793
|
+
.option("--task <task-ref>", "Link run to a task")
|
|
794
|
+
.option("--json", "Output JSON")
|
|
795
|
+
.action(workflowStart);
|
|
796
|
+
workflow
|
|
797
|
+
.command("runs")
|
|
798
|
+
.description("List workflow runs")
|
|
799
|
+
.option("--active", "Show only active runs")
|
|
800
|
+
.option("--completed", "Show only completed runs")
|
|
801
|
+
.option("--workflow <ref>", "Filter by workflow")
|
|
802
|
+
.option("--json", "Output JSON")
|
|
803
|
+
.action(workflowRuns);
|
|
804
|
+
workflow
|
|
805
|
+
.command("show")
|
|
806
|
+
.description("Show workflow run details")
|
|
807
|
+
.argument("<run-ref>", "Run reference (@ulid or ulid prefix)")
|
|
808
|
+
.option("--json", "Output JSON")
|
|
809
|
+
.action(workflowShow);
|
|
810
|
+
markMutating(workflow.command("abort"))
|
|
811
|
+
.description("Abort an active workflow run")
|
|
812
|
+
.argument("<run-ref>", "Run reference (@ulid or ulid prefix)")
|
|
813
|
+
.option("--reason <text>", "Reason for aborting")
|
|
814
|
+
.option("--json", "Output JSON")
|
|
815
|
+
.action(workflowAbort);
|
|
816
|
+
markMutating(workflow.command("complete"))
|
|
817
|
+
.description("Mark workflow run as completed (graceful exit)")
|
|
818
|
+
.argument("<run-ref>", "Run reference (@ulid or ulid prefix)")
|
|
819
|
+
.option("--result <result>", "Completion result (success, no_work_available, early_exit)")
|
|
820
|
+
.option("--json", "Output JSON")
|
|
821
|
+
.action(workflowComplete);
|
|
822
|
+
markMutating(workflow.command("next"))
|
|
823
|
+
.description("Advance workflow run to next step")
|
|
824
|
+
.argument("[run-ref]", "Run reference (optional if only one active run)")
|
|
825
|
+
.option("--skip", "Mark current step as skipped")
|
|
826
|
+
.option("--notes <text>", "Notes for the completed step")
|
|
827
|
+
.option("--confirm", "Acknowledge entry/exit criteria (required in strict mode)")
|
|
828
|
+
.option("--force", "Allow --skip in strict mode")
|
|
829
|
+
.option("--input <key=value>", "Provide step input (repeatable)", (value, previous = []) => [...previous, value])
|
|
830
|
+
.option("--json", "Output JSON")
|
|
831
|
+
.action(workflowNext);
|
|
832
|
+
markMutating(workflow.command("pause"))
|
|
833
|
+
.description("Pause an active workflow run")
|
|
834
|
+
.argument("<run-ref>", "Run reference (@ulid or ulid prefix)")
|
|
835
|
+
.option("--json", "Output JSON")
|
|
836
|
+
.action(workflowPause);
|
|
837
|
+
markMutating(workflow.command("resume"))
|
|
838
|
+
.description("Resume a paused workflow run")
|
|
839
|
+
.argument("<run-ref>", "Run reference (@ulid or ulid prefix)")
|
|
840
|
+
.option("--json", "Output JSON")
|
|
841
|
+
.action(workflowResume);
|
|
842
|
+
markMutating(workflow.command("prune"))
|
|
843
|
+
.description("Remove old or stale workflow runs")
|
|
844
|
+
.option("--older-than <duration>", "Delete runs older than specified duration (e.g., 30d, 12h, 45m)")
|
|
845
|
+
.option("--status <status>", "Delete runs with specific status (active, paused, completed, aborted)")
|
|
846
|
+
.option("--abandoned", "Delete active runs with no activity for 7+ days")
|
|
847
|
+
.option("--dry-run", "Show what would be deleted without deleting")
|
|
848
|
+
.option("--json", "Output JSON")
|
|
849
|
+
.action(workflowPrune);
|
|
850
|
+
}
|
|
851
|
+
//# sourceMappingURL=workflow.js.map
|