@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,753 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill CRUD commands: list, add, get, set, delete, import
|
|
3
|
+
*
|
|
4
|
+
* AC: @skill-cli ac-1 - kspec skill list outputs table with ID, Name, Origin, Version, Platforms
|
|
5
|
+
* AC: @skill-cli ac-2 - kspec skill list --json outputs JSON array with full skill metadata
|
|
6
|
+
* AC: @skill-cli ac-3 - kspec skill add creates meta entry with origin custom
|
|
7
|
+
* AC: @skill-cli ac-4 - kspec skill add creates .kspec/skills/<id>/SKILL.md
|
|
8
|
+
* AC: @skill-cli ac-5 - kspec skill get outputs metadata including id, name, origin, platforms
|
|
9
|
+
* AC: @skill-cli ac-6 - kspec skill get outputs SKILL.md content
|
|
10
|
+
* AC: @skill-cli ac-7 - kspec skill delete --confirm removes meta entry
|
|
11
|
+
* AC: @skill-cli ac-8 - kspec skill delete removes .kspec/skills/<id>/ directory
|
|
12
|
+
*/
|
|
13
|
+
import * as fs from "node:fs/promises";
|
|
14
|
+
import * as path from "node:path";
|
|
15
|
+
import chalk from "chalk";
|
|
16
|
+
import Table from "cli-table3";
|
|
17
|
+
import { ulid } from "ulid";
|
|
18
|
+
import yaml from "yaml";
|
|
19
|
+
import { markMutating } from "../command-annotations.js";
|
|
20
|
+
import { deleteMetaItem, findMetaItemByRef, getSkillContentPath, initContext, isSkill, loadMetaContext, loadSkillContent, loadSkillDocs, saveMetaItem, } from "../../parser/index.js";
|
|
21
|
+
import { commitIfShadow } from "../../parser/shadow.js";
|
|
22
|
+
import { copyDirectory } from "../../parser/skill-render.js";
|
|
23
|
+
import { SkillSchema, ClaudeCodeConfigSchema, CodexConfigSchema, } from "../../schema/index.js";
|
|
24
|
+
import { errors } from "../../strings/errors.js";
|
|
25
|
+
import { EXIT_CODES } from "../exit-codes.js";
|
|
26
|
+
import { error, output, success } from "../output.js";
|
|
27
|
+
import { parseTagsArray } from "../parse-utils.js";
|
|
28
|
+
/**
|
|
29
|
+
* Parse YAML frontmatter from markdown content.
|
|
30
|
+
* Returns null if no valid frontmatter found.
|
|
31
|
+
*
|
|
32
|
+
* AC: @import-frontmatter-strip ac-1 - Parse all Agent Skills frontmatter fields
|
|
33
|
+
* AC: @import-frontmatter-strip ac-3 - Parse Claude Code platform frontmatter
|
|
34
|
+
*/
|
|
35
|
+
export function parseFrontmatter(content) {
|
|
36
|
+
const frontmatterRegex = /^---\r?\n([\s\S]*?)\r?\n---/;
|
|
37
|
+
const match = content.match(frontmatterRegex);
|
|
38
|
+
if (!match) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const parsed = yaml.parse(match[1]);
|
|
43
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
44
|
+
const result = {};
|
|
45
|
+
// Core metadata
|
|
46
|
+
if (typeof parsed.name === "string")
|
|
47
|
+
result.name = parsed.name;
|
|
48
|
+
if (typeof parsed.description === "string")
|
|
49
|
+
result.description = parsed.description;
|
|
50
|
+
// Portable Agent Skills fields (AC: ac-1)
|
|
51
|
+
if (typeof parsed.license === "string")
|
|
52
|
+
result.license = parsed.license;
|
|
53
|
+
if (typeof parsed.compatibility === "string")
|
|
54
|
+
result.compatibility = parsed.compatibility;
|
|
55
|
+
if (Array.isArray(parsed.allowed_tools)) {
|
|
56
|
+
result.allowed_tools = parsed.allowed_tools.filter((t) => typeof t === "string");
|
|
57
|
+
}
|
|
58
|
+
// Claude Code platform fields (AC: ac-3)
|
|
59
|
+
// Support both underscore and hyphen naming for user-invocable
|
|
60
|
+
if (typeof parsed.user_invocable === "boolean")
|
|
61
|
+
result.user_invocable = parsed.user_invocable;
|
|
62
|
+
if (typeof parsed["user-invocable"] === "boolean")
|
|
63
|
+
result.user_invocable = parsed["user-invocable"];
|
|
64
|
+
if (typeof parsed.disable_model_invocation === "boolean")
|
|
65
|
+
result.disable_model_invocation = parsed.disable_model_invocation;
|
|
66
|
+
if (typeof parsed["disable-model-invocation"] === "boolean")
|
|
67
|
+
result.disable_model_invocation = parsed["disable-model-invocation"];
|
|
68
|
+
if (typeof parsed.context === "string")
|
|
69
|
+
result.context = parsed.context;
|
|
70
|
+
if (typeof parsed.agent === "string")
|
|
71
|
+
result.agent = parsed.agent;
|
|
72
|
+
if (typeof parsed.model === "string")
|
|
73
|
+
result.model = parsed.model;
|
|
74
|
+
if (typeof parsed.argument_hint === "string")
|
|
75
|
+
result.argument_hint = parsed.argument_hint;
|
|
76
|
+
if (typeof parsed["argument-hint"] === "string")
|
|
77
|
+
result.argument_hint = parsed["argument-hint"];
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Invalid YAML in frontmatter
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Strip YAML frontmatter from markdown content.
|
|
88
|
+
* AC: @import-frontmatter-strip ac-2 - Remove frontmatter for body-only storage
|
|
89
|
+
*/
|
|
90
|
+
export function stripFrontmatter(content) {
|
|
91
|
+
return content.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "");
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Normalize base-directory paths in skill content.
|
|
95
|
+
* AC: @skill-import ac-7 - Strip or convert absolute paths to relative.
|
|
96
|
+
*
|
|
97
|
+
* Matches patterns like:
|
|
98
|
+
* - "Base directory for this skill: /absolute/path/to/skill"
|
|
99
|
+
* - Lines starting with hardcoded paths
|
|
100
|
+
*/
|
|
101
|
+
export function normalizeBaseDirectory(content) {
|
|
102
|
+
// Remove or normalize "Base directory for/of [this] skill:" lines with absolute paths
|
|
103
|
+
// Common pattern in Claude-generated skill files
|
|
104
|
+
// AC: @cross-platform-and-version-robustness ac-5 - case-insensitive with wording variations
|
|
105
|
+
const baseDirLineRegex = /^base\s+directory\s+(?:for|of)\s+(?:this\s+)?skill:.*$/gim;
|
|
106
|
+
return content.replace(baseDirLineRegex, "");
|
|
107
|
+
}
|
|
108
|
+
// ============================================================================
|
|
109
|
+
// Formatting Helpers
|
|
110
|
+
// ============================================================================
|
|
111
|
+
/**
|
|
112
|
+
* Format skills as a table
|
|
113
|
+
* AC: @skill-cli ac-1 - table displays ID, Name, Origin, Version, Platforms
|
|
114
|
+
*/
|
|
115
|
+
function formatSkillsTable(skills) {
|
|
116
|
+
if (skills.length === 0) {
|
|
117
|
+
console.log(chalk.yellow("No skills defined"));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const table = new Table({
|
|
121
|
+
head: [
|
|
122
|
+
chalk.bold("ID"),
|
|
123
|
+
chalk.bold("Name"),
|
|
124
|
+
chalk.bold("Origin"),
|
|
125
|
+
chalk.bold("Version"),
|
|
126
|
+
chalk.bold("Platforms"),
|
|
127
|
+
],
|
|
128
|
+
style: {
|
|
129
|
+
head: [],
|
|
130
|
+
border: [],
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
for (const skill of skills) {
|
|
134
|
+
table.push([
|
|
135
|
+
skill.id,
|
|
136
|
+
skill.name,
|
|
137
|
+
skill.origin,
|
|
138
|
+
skill.version || "-",
|
|
139
|
+
skill.platforms.join(", "),
|
|
140
|
+
]);
|
|
141
|
+
}
|
|
142
|
+
console.log(table.toString());
|
|
143
|
+
console.log(chalk.gray(`\n${skills.length} skill(s)`));
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Format skill details
|
|
147
|
+
* AC: @skill-cli ac-5 - outputs metadata including id, name, origin, platforms
|
|
148
|
+
*/
|
|
149
|
+
function formatSkillDetails(skill, content) {
|
|
150
|
+
console.log(chalk.bold(skill.name));
|
|
151
|
+
console.log(chalk.gray("─".repeat(40)));
|
|
152
|
+
console.log(`ID: ${skill.id}`);
|
|
153
|
+
console.log(`ULID: ${skill._ulid}`);
|
|
154
|
+
console.log(`Origin: ${skill.origin}`);
|
|
155
|
+
console.log(`Platforms: ${skill.platforms.join(", ")}`);
|
|
156
|
+
if (skill.version) {
|
|
157
|
+
console.log(`Version: ${skill.version}`);
|
|
158
|
+
}
|
|
159
|
+
if (skill.description) {
|
|
160
|
+
console.log(`\n─── Description ───`);
|
|
161
|
+
console.log(skill.description);
|
|
162
|
+
}
|
|
163
|
+
if (skill.depends_on && skill.depends_on.length > 0) {
|
|
164
|
+
console.log(`\n─── Dependencies ───`);
|
|
165
|
+
for (const dep of skill.depends_on) {
|
|
166
|
+
console.log(` • ${dep}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (skill.tags && skill.tags.length > 0) {
|
|
170
|
+
console.log(`\nTags: ${skill.tags.join(", ")}`);
|
|
171
|
+
}
|
|
172
|
+
// AC: @skill-cli ac-6 - display SKILL.md content
|
|
173
|
+
if (content) {
|
|
174
|
+
console.log(`\n─── SKILL.md Content ───`);
|
|
175
|
+
console.log(content);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
console.log(chalk.gray("\n(No SKILL.md content found)"));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// ============================================================================
|
|
182
|
+
// Command Registration
|
|
183
|
+
// ============================================================================
|
|
184
|
+
/**
|
|
185
|
+
* Register CRUD skill commands (list, add, get, set, delete, import)
|
|
186
|
+
*/
|
|
187
|
+
export function registerSkillCrudCommands(skill) {
|
|
188
|
+
// AC: @skill-cli ac-1, ac-2 - kspec skill list
|
|
189
|
+
skill
|
|
190
|
+
.command("list")
|
|
191
|
+
.description("List all skills")
|
|
192
|
+
.option("--origin <origin>", "Filter by origin (core, project, local)")
|
|
193
|
+
.option("--tag <tag>", "Filter by tag")
|
|
194
|
+
.action(async (options) => {
|
|
195
|
+
try {
|
|
196
|
+
const ctx = await initContext();
|
|
197
|
+
if (!ctx.manifestPath) {
|
|
198
|
+
error(errors.project.noKspecProject);
|
|
199
|
+
process.exit(EXIT_CODES.ERROR);
|
|
200
|
+
}
|
|
201
|
+
const metaCtx = await loadMetaContext(ctx);
|
|
202
|
+
let skills = metaCtx.skills || [];
|
|
203
|
+
// Apply filters
|
|
204
|
+
if (options.origin) {
|
|
205
|
+
skills = skills.filter((s) => s.origin === options.origin);
|
|
206
|
+
}
|
|
207
|
+
if (options.tag) {
|
|
208
|
+
skills = skills.filter((s) => s.tags?.includes(options.tag));
|
|
209
|
+
}
|
|
210
|
+
// AC: @skill-cli ac-2 - JSON output includes full skill metadata
|
|
211
|
+
output(skills.map((s) => ({
|
|
212
|
+
_ulid: s._ulid,
|
|
213
|
+
id: s.id,
|
|
214
|
+
name: s.name,
|
|
215
|
+
description: s.description,
|
|
216
|
+
origin: s.origin,
|
|
217
|
+
version: s.version,
|
|
218
|
+
platforms: s.platforms,
|
|
219
|
+
depends_on: s.depends_on,
|
|
220
|
+
tags: s.tags,
|
|
221
|
+
})),
|
|
222
|
+
// AC: @skill-cli ac-1 - Table output
|
|
223
|
+
() => formatSkillsTable(skills));
|
|
224
|
+
}
|
|
225
|
+
catch (err) {
|
|
226
|
+
error("Failed to list skills", err);
|
|
227
|
+
process.exit(EXIT_CODES.ERROR);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
// AC: @skill-cli ac-3, ac-4 - kspec skill add
|
|
231
|
+
// AC: @skill-add ac-3 - --content-file copies existing file to SKILL.md
|
|
232
|
+
markMutating(skill.command("add"))
|
|
233
|
+
.description("Create a new skill")
|
|
234
|
+
.requiredOption("--id <id>", "Skill ID (kebab-case)")
|
|
235
|
+
.requiredOption("--name <name>", "Skill name")
|
|
236
|
+
.option("--description <desc>", "Skill description")
|
|
237
|
+
.option("--origin <origin>", "Skill origin (core, project, local)", "project")
|
|
238
|
+
.option("--skill-version <version>", "Skill version")
|
|
239
|
+
.option("--platform <platform...>", "Target platforms")
|
|
240
|
+
.option("--tag <tag...>", "Tags for the skill")
|
|
241
|
+
.option("--depends-on <ref...>", "Skill dependencies")
|
|
242
|
+
.option("--content-file <path>", "Path to existing file to use as SKILL.md content")
|
|
243
|
+
.action(async (options) => {
|
|
244
|
+
try {
|
|
245
|
+
const ctx = await initContext();
|
|
246
|
+
if (!ctx.manifestPath) {
|
|
247
|
+
error(errors.project.noKspecProject);
|
|
248
|
+
process.exit(EXIT_CODES.ERROR);
|
|
249
|
+
}
|
|
250
|
+
// Validate origin
|
|
251
|
+
const validOrigins = ["core", "project", "local"];
|
|
252
|
+
if (!validOrigins.includes(options.origin)) {
|
|
253
|
+
error(`Invalid origin: ${options.origin}. Valid origins: ${validOrigins.join(", ")}`);
|
|
254
|
+
process.exit(EXIT_CODES.ERROR);
|
|
255
|
+
}
|
|
256
|
+
// Check if skill with this ID already exists
|
|
257
|
+
const metaCtx = await loadMetaContext(ctx);
|
|
258
|
+
const existingSkill = metaCtx.skills.find((s) => s.id === options.id);
|
|
259
|
+
if (existingSkill) {
|
|
260
|
+
error(`Skill with ID '${options.id}' already exists`);
|
|
261
|
+
process.exit(EXIT_CODES.CONFLICT);
|
|
262
|
+
}
|
|
263
|
+
// Build skill object (schema provides defaults for platforms, depends_on, tags)
|
|
264
|
+
const skillData = {
|
|
265
|
+
_ulid: ulid(),
|
|
266
|
+
id: options.id,
|
|
267
|
+
name: options.name,
|
|
268
|
+
description: options.description,
|
|
269
|
+
origin: options.origin,
|
|
270
|
+
version: options.skillVersion,
|
|
271
|
+
...(options.platform && options.platform.length > 0 && { platforms: options.platform }),
|
|
272
|
+
...(options.dependsOn && options.dependsOn.length > 0 && { depends_on: options.dependsOn }),
|
|
273
|
+
allowed_tools: [],
|
|
274
|
+
...(options.tag && options.tag.length > 0 && { tags: parseTagsArray(options.tag) }),
|
|
275
|
+
};
|
|
276
|
+
// Validate with schema
|
|
277
|
+
const parsed = SkillSchema.safeParse(skillData);
|
|
278
|
+
if (!parsed.success) {
|
|
279
|
+
const issues = parsed.error.issues
|
|
280
|
+
.map((i) => `${i.path.join(".")}: ${i.message}`)
|
|
281
|
+
.join("; ");
|
|
282
|
+
error(`Invalid skill data: ${issues}`);
|
|
283
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
284
|
+
}
|
|
285
|
+
const skill = { ...parsed.data };
|
|
286
|
+
// AC: @skill-cli ac-3 - save meta entry (also creates directory per ac-4)
|
|
287
|
+
await saveMetaItem(ctx, skill, "skill");
|
|
288
|
+
// AC: @skill-cli ac-4 - create SKILL.md with placeholder content
|
|
289
|
+
// AC: @skill-add ac-3 - if --content-file provided, copy its contents
|
|
290
|
+
const skillMdPath = getSkillContentPath(ctx, skill.id);
|
|
291
|
+
let initialContent;
|
|
292
|
+
if (options.contentFile) {
|
|
293
|
+
// Read content from the specified file
|
|
294
|
+
const contentFilePath = path.isAbsolute(options.contentFile)
|
|
295
|
+
? options.contentFile
|
|
296
|
+
: path.resolve(process.cwd(), options.contentFile);
|
|
297
|
+
try {
|
|
298
|
+
initialContent = await fs.readFile(contentFilePath, "utf-8");
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
// Clean up: remove the skill we just created
|
|
302
|
+
await deleteMetaItem(ctx, skill._ulid, "skill");
|
|
303
|
+
error(`Failed to read content file: ${contentFilePath}`);
|
|
304
|
+
process.exit(EXIT_CODES.ERROR);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
initialContent = `# ${skill.name}\n\n${skill.description || "Add skill content here."}\n`;
|
|
309
|
+
}
|
|
310
|
+
await fs.writeFile(skillMdPath, initialContent, "utf-8");
|
|
311
|
+
// Commit changes
|
|
312
|
+
await commitIfShadow(ctx.shadow, "skill-add", skill.id, skill.name);
|
|
313
|
+
output(skill, () => success(`Created skill: ${skill.id}`, { skill }));
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
error("Failed to create skill", err);
|
|
317
|
+
process.exit(EXIT_CODES.ERROR);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
// AC: @skill-cli ac-5, ac-6 - kspec skill get
|
|
321
|
+
skill
|
|
322
|
+
.command("get <ref>")
|
|
323
|
+
.description("Show skill details")
|
|
324
|
+
.action(async (ref) => {
|
|
325
|
+
try {
|
|
326
|
+
const ctx = await initContext();
|
|
327
|
+
if (!ctx.manifestPath) {
|
|
328
|
+
error(errors.project.noKspecProject);
|
|
329
|
+
process.exit(EXIT_CODES.ERROR);
|
|
330
|
+
}
|
|
331
|
+
const metaCtx = await loadMetaContext(ctx);
|
|
332
|
+
const item = findMetaItemByRef(metaCtx, ref);
|
|
333
|
+
if (!item) {
|
|
334
|
+
error(`Skill not found: ${ref}`);
|
|
335
|
+
console.log(chalk.gray("Try: kspec skill list"));
|
|
336
|
+
process.exit(EXIT_CODES.NOT_FOUND);
|
|
337
|
+
}
|
|
338
|
+
// Check it's a skill (uses _type discriminant)
|
|
339
|
+
if (!isSkill(item)) {
|
|
340
|
+
error(`Item ${ref} is not a skill`);
|
|
341
|
+
process.exit(EXIT_CODES.ERROR);
|
|
342
|
+
}
|
|
343
|
+
const skill = item;
|
|
344
|
+
// AC: @skill-cli ac-6 - load SKILL.md content
|
|
345
|
+
const content = await loadSkillContent(ctx, skill);
|
|
346
|
+
const docs = await loadSkillDocs(ctx, skill);
|
|
347
|
+
// AC: @skill-cli ac-5, ac-6 - output metadata and content
|
|
348
|
+
// Include extended skill schema fields (license, compatibility, allowed_tools, metadata, platform_config)
|
|
349
|
+
output({
|
|
350
|
+
_ulid: skill._ulid,
|
|
351
|
+
id: skill.id,
|
|
352
|
+
name: skill.name,
|
|
353
|
+
description: skill.description,
|
|
354
|
+
origin: skill.origin,
|
|
355
|
+
version: skill.version,
|
|
356
|
+
platforms: skill.platforms,
|
|
357
|
+
depends_on: skill.depends_on,
|
|
358
|
+
tags: skill.tags,
|
|
359
|
+
// Extended fields from @extended-skill-schema
|
|
360
|
+
license: skill.license,
|
|
361
|
+
compatibility: skill.compatibility,
|
|
362
|
+
allowed_tools: skill.allowed_tools,
|
|
363
|
+
metadata: skill.metadata,
|
|
364
|
+
platform_config: skill.platform_config,
|
|
365
|
+
content,
|
|
366
|
+
docs: docs.map((d) => ({ name: d.name, path: d.path })),
|
|
367
|
+
}, () => formatSkillDetails(skill, content));
|
|
368
|
+
}
|
|
369
|
+
catch (err) {
|
|
370
|
+
error("Failed to get skill", err);
|
|
371
|
+
process.exit(EXIT_CODES.ERROR);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
// AC: @skill-set ac-1, ac-2, ac-3 - kspec skill set
|
|
375
|
+
markMutating(skill.command("set <ref>"))
|
|
376
|
+
.description("Update skill metadata fields")
|
|
377
|
+
.option("--name <name>", "Update skill name")
|
|
378
|
+
.option("--description <desc>", "Update skill description")
|
|
379
|
+
.option("--origin <origin>", "Update skill origin (core, project, local)")
|
|
380
|
+
.option("--skill-version <version>", "Update skill version")
|
|
381
|
+
.option("--add-platform <platform>", "Add a platform to the platforms array")
|
|
382
|
+
.option("--remove-platform <platform>", "Remove a platform from the platforms array")
|
|
383
|
+
.option("--add-tag <tag>", "Add a tag to the tags array")
|
|
384
|
+
.option("--remove-tag <tag>", "Remove a tag from the tags array")
|
|
385
|
+
.option("--add-depends-on <ref>", "Add a dependency reference")
|
|
386
|
+
.option("--remove-depends-on <ref>", "Remove a dependency reference")
|
|
387
|
+
.option("--platform-config <config>", "Set platform config (format: platform.key=value, e.g., claude_code.user_invocable=false)", (value, previous) => {
|
|
388
|
+
// Collect multiple --platform-config options
|
|
389
|
+
return previous.concat([value]);
|
|
390
|
+
}, [])
|
|
391
|
+
.action(async (ref, options) => {
|
|
392
|
+
try {
|
|
393
|
+
const ctx = await initContext();
|
|
394
|
+
if (!ctx.manifestPath) {
|
|
395
|
+
error(errors.project.noKspecProject);
|
|
396
|
+
process.exit(EXIT_CODES.ERROR);
|
|
397
|
+
}
|
|
398
|
+
const metaCtx = await loadMetaContext(ctx);
|
|
399
|
+
const item = findMetaItemByRef(metaCtx, ref);
|
|
400
|
+
if (!item) {
|
|
401
|
+
error(`Skill not found: ${ref}`);
|
|
402
|
+
console.log(chalk.gray("Try: kspec skill list"));
|
|
403
|
+
process.exit(EXIT_CODES.NOT_FOUND);
|
|
404
|
+
}
|
|
405
|
+
// Check it's a skill (uses _type discriminant)
|
|
406
|
+
if (!isSkill(item)) {
|
|
407
|
+
error(`Item ${ref} is not a skill`);
|
|
408
|
+
process.exit(EXIT_CODES.ERROR);
|
|
409
|
+
}
|
|
410
|
+
// Clone before mutating to protect against partial save failure
|
|
411
|
+
const skill = structuredClone(item);
|
|
412
|
+
// AC: @skill-set ac-1 - update description field
|
|
413
|
+
if (options.name !== undefined) {
|
|
414
|
+
skill.name = options.name;
|
|
415
|
+
}
|
|
416
|
+
if (options.description !== undefined) {
|
|
417
|
+
skill.description = options.description;
|
|
418
|
+
}
|
|
419
|
+
// Update origin
|
|
420
|
+
if (options.origin !== undefined) {
|
|
421
|
+
const validOrigins = ["core", "project", "local"];
|
|
422
|
+
if (!validOrigins.includes(options.origin)) {
|
|
423
|
+
error(`Invalid origin: ${options.origin}. Valid origins: ${validOrigins.join(", ")}`);
|
|
424
|
+
process.exit(EXIT_CODES.ERROR);
|
|
425
|
+
}
|
|
426
|
+
skill.origin = options.origin;
|
|
427
|
+
}
|
|
428
|
+
// Update version
|
|
429
|
+
if (options.skillVersion !== undefined) {
|
|
430
|
+
skill.version = options.skillVersion;
|
|
431
|
+
}
|
|
432
|
+
// AC: @skill-set ac-2 - add platform to array
|
|
433
|
+
if (options.addPlatform) {
|
|
434
|
+
if (!skill.platforms.includes(options.addPlatform)) {
|
|
435
|
+
skill.platforms.push(options.addPlatform);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
// Remove platform
|
|
439
|
+
if (options.removePlatform) {
|
|
440
|
+
const idx = skill.platforms.indexOf(options.removePlatform);
|
|
441
|
+
if (idx >= 0) {
|
|
442
|
+
skill.platforms.splice(idx, 1);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
// AC: @skill-set ac-3 - add tag to array
|
|
446
|
+
if (options.addTag) {
|
|
447
|
+
if (!skill.tags) {
|
|
448
|
+
skill.tags = [];
|
|
449
|
+
}
|
|
450
|
+
if (!skill.tags.includes(options.addTag)) {
|
|
451
|
+
skill.tags.push(options.addTag);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
// Remove tag
|
|
455
|
+
if (options.removeTag && skill.tags) {
|
|
456
|
+
const idx = skill.tags.indexOf(options.removeTag);
|
|
457
|
+
if (idx >= 0) {
|
|
458
|
+
skill.tags.splice(idx, 1);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
// Add dependency
|
|
462
|
+
if (options.addDependsOn) {
|
|
463
|
+
if (!skill.depends_on) {
|
|
464
|
+
skill.depends_on = [];
|
|
465
|
+
}
|
|
466
|
+
if (!skill.depends_on.includes(options.addDependsOn)) {
|
|
467
|
+
skill.depends_on.push(options.addDependsOn);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
// Remove dependency
|
|
471
|
+
if (options.removeDependsOn && skill.depends_on) {
|
|
472
|
+
const idx = skill.depends_on.indexOf(options.removeDependsOn);
|
|
473
|
+
if (idx >= 0) {
|
|
474
|
+
skill.depends_on.splice(idx, 1);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
// AC: @skill-platform-config-cli ac-1, ac-2 - Handle platform config updates
|
|
478
|
+
if (options.platformConfig && options.platformConfig.length > 0) {
|
|
479
|
+
// Initialize platform_config if not present
|
|
480
|
+
if (!skill.platform_config) {
|
|
481
|
+
skill.platform_config = {};
|
|
482
|
+
}
|
|
483
|
+
for (const configStr of options.platformConfig) {
|
|
484
|
+
// Parse "platform.key=value" format
|
|
485
|
+
const match = configStr.match(/^([^.]+)\.([^=]+)=(.*)$/);
|
|
486
|
+
if (!match) {
|
|
487
|
+
error(`Invalid platform config format: ${configStr}\n` +
|
|
488
|
+
`Expected format: platform.key=value (e.g., claude_code.user_invocable=false)`);
|
|
489
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
490
|
+
}
|
|
491
|
+
const [, platform, key, rawValue] = match;
|
|
492
|
+
// Parse value type: "true"/"false" → boolean, otherwise string
|
|
493
|
+
let value;
|
|
494
|
+
if (rawValue === "true") {
|
|
495
|
+
value = true;
|
|
496
|
+
}
|
|
497
|
+
else if (rawValue === "false") {
|
|
498
|
+
value = false;
|
|
499
|
+
}
|
|
500
|
+
else {
|
|
501
|
+
// Remove surrounding quotes if present
|
|
502
|
+
value = rawValue.replace(/^["']|["']$/g, "");
|
|
503
|
+
}
|
|
504
|
+
// Deep merge: initialize platform object if needed
|
|
505
|
+
if (!(platform in skill.platform_config)) {
|
|
506
|
+
skill.platform_config[platform] = {};
|
|
507
|
+
}
|
|
508
|
+
// Set the value
|
|
509
|
+
skill.platform_config[platform][key] = value;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
// Validate updated skill
|
|
513
|
+
// AC: @skill-platform-config-cli ac-4 - validation error with guidance on valid keys
|
|
514
|
+
const parsed = SkillSchema.safeParse(skill);
|
|
515
|
+
if (!parsed.success) {
|
|
516
|
+
const issues = parsed.error.issues
|
|
517
|
+
.map((i) => `${i.path.join(".")}: ${i.message}`)
|
|
518
|
+
.join("; ");
|
|
519
|
+
// Check if any errors are related to platform_config and provide guidance
|
|
520
|
+
const hasPlatformConfigError = parsed.error.issues.some((i) => i.path[0] === "platform_config");
|
|
521
|
+
let errorMsg = `Invalid skill data: ${issues}`;
|
|
522
|
+
if (hasPlatformConfigError) {
|
|
523
|
+
// Get valid keys from schemas
|
|
524
|
+
const claudeCodeKeys = Object.keys(ClaudeCodeConfigSchema.shape).join(", ");
|
|
525
|
+
const codexKeys = Object.keys(CodexConfigSchema.shape).join(", ");
|
|
526
|
+
errorMsg +=
|
|
527
|
+
`\n\nValid platform config keys:` +
|
|
528
|
+
`\n claude_code: ${claudeCodeKeys}` +
|
|
529
|
+
`\n codex: ${codexKeys}`;
|
|
530
|
+
}
|
|
531
|
+
error(errorMsg);
|
|
532
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
533
|
+
}
|
|
534
|
+
// Save the updated skill
|
|
535
|
+
await saveMetaItem(ctx, skill, "skill");
|
|
536
|
+
// Commit changes
|
|
537
|
+
await commitIfShadow(ctx.shadow, "skill-set", skill.id, skill.name);
|
|
538
|
+
output(skill, () => success(`Updated skill: ${skill.id}`));
|
|
539
|
+
}
|
|
540
|
+
catch (err) {
|
|
541
|
+
error("Failed to update skill", err);
|
|
542
|
+
process.exit(EXIT_CODES.ERROR);
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
// AC: @skill-cli ac-7, ac-8 - kspec skill delete
|
|
546
|
+
markMutating(skill.command("delete <ref>"))
|
|
547
|
+
.description("Delete a skill")
|
|
548
|
+
.option("--confirm", "Skip confirmation prompt")
|
|
549
|
+
.action(async (ref, options) => {
|
|
550
|
+
try {
|
|
551
|
+
const ctx = await initContext();
|
|
552
|
+
if (!ctx.manifestPath) {
|
|
553
|
+
error(errors.project.noKspecProject);
|
|
554
|
+
process.exit(EXIT_CODES.ERROR);
|
|
555
|
+
}
|
|
556
|
+
const metaCtx = await loadMetaContext(ctx);
|
|
557
|
+
const item = findMetaItemByRef(metaCtx, ref);
|
|
558
|
+
if (!item) {
|
|
559
|
+
error(`Skill not found: ${ref}`);
|
|
560
|
+
console.log(chalk.gray("Try: kspec skill list"));
|
|
561
|
+
process.exit(EXIT_CODES.NOT_FOUND);
|
|
562
|
+
}
|
|
563
|
+
// Check it's a skill (uses _type discriminant)
|
|
564
|
+
if (!isSkill(item)) {
|
|
565
|
+
error(`Item ${ref} is not a skill`);
|
|
566
|
+
process.exit(EXIT_CODES.ERROR);
|
|
567
|
+
}
|
|
568
|
+
const skill = item;
|
|
569
|
+
// Check for confirmation
|
|
570
|
+
if (!options.confirm) {
|
|
571
|
+
error(`Confirm deletion of skill '${skill.id}' with --confirm flag`);
|
|
572
|
+
process.exit(EXIT_CODES.ERROR);
|
|
573
|
+
}
|
|
574
|
+
// Check for dependencies from other skills
|
|
575
|
+
const dependentSkills = metaCtx.skills.filter((s) => s._ulid !== skill._ulid &&
|
|
576
|
+
s.depends_on?.some((dep) => dep === `@${skill.id}` ||
|
|
577
|
+
dep === skill.id ||
|
|
578
|
+
dep.startsWith(`@${skill._ulid.substring(0, 8)}`)));
|
|
579
|
+
if (dependentSkills.length > 0) {
|
|
580
|
+
const depRefs = dependentSkills.map((s) => `@${s.id}`).join(", ");
|
|
581
|
+
error(`Cannot delete skill '${skill.id}': referenced by ${dependentSkills.length} skill(s): ${depRefs}`);
|
|
582
|
+
process.exit(EXIT_CODES.ERROR);
|
|
583
|
+
}
|
|
584
|
+
// AC: @skill-cli ac-7, ac-8 - delete meta entry and directory
|
|
585
|
+
const deleted = await deleteMetaItem(ctx, skill._ulid, "skill");
|
|
586
|
+
if (!deleted) {
|
|
587
|
+
error(`Failed to delete skill: ${skill.id}`);
|
|
588
|
+
process.exit(EXIT_CODES.ERROR);
|
|
589
|
+
}
|
|
590
|
+
// Commit changes
|
|
591
|
+
await commitIfShadow(ctx.shadow, "skill-delete", skill.id);
|
|
592
|
+
success(`Deleted skill: ${skill.id}`);
|
|
593
|
+
}
|
|
594
|
+
catch (err) {
|
|
595
|
+
error("Failed to delete skill", err);
|
|
596
|
+
process.exit(EXIT_CODES.ERROR);
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
// AC: @skill-import ac-1 through ac-7 - kspec skill import
|
|
600
|
+
// AC: @import-frontmatter-strip ac-1 through ac-6 - Extended frontmatter parsing
|
|
601
|
+
markMutating(skill.command("import <file>"))
|
|
602
|
+
.description("Import an existing SKILL.md file into kspec")
|
|
603
|
+
.option("--id <id>", "Custom skill ID (defaults to directory name)")
|
|
604
|
+
.option("--name <name>", "Skill name (required if no frontmatter)")
|
|
605
|
+
.option("--description <desc>", "Skill description (required if no frontmatter)")
|
|
606
|
+
.option("--origin <origin>", "Skill origin (core, project, local)", "project")
|
|
607
|
+
.option("--skill-version <version>", "Skill version")
|
|
608
|
+
.action(async (file, options) => {
|
|
609
|
+
try {
|
|
610
|
+
const ctx = await initContext();
|
|
611
|
+
if (!ctx.manifestPath) {
|
|
612
|
+
error(errors.project.noKspecProject);
|
|
613
|
+
process.exit(EXIT_CODES.ERROR);
|
|
614
|
+
}
|
|
615
|
+
// Resolve file path
|
|
616
|
+
const filePath = path.isAbsolute(file)
|
|
617
|
+
? file
|
|
618
|
+
: path.resolve(process.cwd(), file);
|
|
619
|
+
// Check file exists
|
|
620
|
+
try {
|
|
621
|
+
await fs.access(filePath);
|
|
622
|
+
}
|
|
623
|
+
catch {
|
|
624
|
+
error(`File not found: ${filePath}`);
|
|
625
|
+
process.exit(EXIT_CODES.NOT_FOUND);
|
|
626
|
+
}
|
|
627
|
+
// Read file content
|
|
628
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
629
|
+
// AC: @skill-import ac-1, ac-6 - Parse YAML frontmatter
|
|
630
|
+
// AC: @import-frontmatter-strip ac-1, ac-3 - Parse all Agent Skills fields
|
|
631
|
+
const frontmatter = parseFrontmatter(content);
|
|
632
|
+
// Determine name and description from frontmatter or options
|
|
633
|
+
// AC: @import-frontmatter-strip ac-6 - CLI flags work when no frontmatter
|
|
634
|
+
const skillName = options.name || frontmatter?.name;
|
|
635
|
+
const skillDescription = options.description || frontmatter?.description;
|
|
636
|
+
// AC: @skill-import ac-6, @import-frontmatter-strip ac-6 - Error if no name/description and no frontmatter
|
|
637
|
+
if (!skillName) {
|
|
638
|
+
error("Name is required. Either add YAML frontmatter with 'name' field or use --name option.");
|
|
639
|
+
console.log(chalk.gray("Example frontmatter:\n---\nname: my-skill\ndescription: My skill description\n---"));
|
|
640
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
641
|
+
}
|
|
642
|
+
if (!skillDescription) {
|
|
643
|
+
error("Description is required. Either add YAML frontmatter with 'description' field or use --description option.");
|
|
644
|
+
console.log(chalk.gray("Example frontmatter:\n---\nname: my-skill\ndescription: My skill description\n---"));
|
|
645
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
646
|
+
}
|
|
647
|
+
// AC: @skill-import ac-5 - Derive ID from directory name or use custom
|
|
648
|
+
const sourceDir = path.dirname(filePath);
|
|
649
|
+
const derivedId = path.basename(sourceDir);
|
|
650
|
+
const skillId = options.id || derivedId;
|
|
651
|
+
// Validate origin
|
|
652
|
+
const validOrigins = ["core", "project", "local"];
|
|
653
|
+
if (!validOrigins.includes(options.origin)) {
|
|
654
|
+
error(`Invalid origin: ${options.origin}. Valid origins: ${validOrigins.join(", ")}`);
|
|
655
|
+
process.exit(EXIT_CODES.ERROR);
|
|
656
|
+
}
|
|
657
|
+
// Check if skill with this ID already exists
|
|
658
|
+
const metaCtx = await loadMetaContext(ctx);
|
|
659
|
+
const existingSkill = metaCtx.skills.find((s) => s.id === skillId);
|
|
660
|
+
if (existingSkill) {
|
|
661
|
+
error(`Skill with ID '${skillId}' already exists. Use --id to specify a different ID.`);
|
|
662
|
+
process.exit(EXIT_CODES.CONFLICT);
|
|
663
|
+
}
|
|
664
|
+
// AC: @import-frontmatter-strip ac-3 - Build platform_config.claude_code from frontmatter
|
|
665
|
+
let platformConfig;
|
|
666
|
+
if (frontmatter) {
|
|
667
|
+
const claudeCodeConfig = {};
|
|
668
|
+
if (frontmatter.user_invocable !== undefined) {
|
|
669
|
+
claudeCodeConfig.user_invocable = frontmatter.user_invocable;
|
|
670
|
+
}
|
|
671
|
+
if (frontmatter.disable_model_invocation !== undefined) {
|
|
672
|
+
claudeCodeConfig.disable_model_invocation = frontmatter.disable_model_invocation;
|
|
673
|
+
}
|
|
674
|
+
if (frontmatter.context !== undefined) {
|
|
675
|
+
claudeCodeConfig.context = frontmatter.context;
|
|
676
|
+
}
|
|
677
|
+
if (frontmatter.agent !== undefined) {
|
|
678
|
+
claudeCodeConfig.agent = frontmatter.agent;
|
|
679
|
+
}
|
|
680
|
+
if (frontmatter.model !== undefined) {
|
|
681
|
+
claudeCodeConfig.model = frontmatter.model;
|
|
682
|
+
}
|
|
683
|
+
if (frontmatter.argument_hint !== undefined) {
|
|
684
|
+
claudeCodeConfig.argument_hint = frontmatter.argument_hint;
|
|
685
|
+
}
|
|
686
|
+
// Only set platform_config if we have Claude Code config fields
|
|
687
|
+
if (Object.keys(claudeCodeConfig).length > 0) {
|
|
688
|
+
platformConfig = { claude_code: claudeCodeConfig };
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
// Build skill object
|
|
692
|
+
// AC: @import-frontmatter-strip ac-1 - All recognized fields populate meta.yaml
|
|
693
|
+
// Schema provides defaults for platforms, depends_on, tags
|
|
694
|
+
const skillData = {
|
|
695
|
+
_ulid: ulid(),
|
|
696
|
+
id: skillId,
|
|
697
|
+
name: skillName,
|
|
698
|
+
description: skillDescription,
|
|
699
|
+
origin: options.origin,
|
|
700
|
+
version: options.skillVersion,
|
|
701
|
+
// AC: @import-frontmatter-strip ac-1 - license, compatibility, allowed_tools from frontmatter
|
|
702
|
+
license: frontmatter?.license,
|
|
703
|
+
compatibility: frontmatter?.compatibility,
|
|
704
|
+
allowed_tools: frontmatter?.allowed_tools || [],
|
|
705
|
+
// AC: @import-frontmatter-strip ac-3 - platform_config.claude_code from frontmatter
|
|
706
|
+
platform_config: platformConfig,
|
|
707
|
+
};
|
|
708
|
+
// Validate with schema
|
|
709
|
+
const parsed = SkillSchema.safeParse(skillData);
|
|
710
|
+
if (!parsed.success) {
|
|
711
|
+
const issues = parsed.error.issues
|
|
712
|
+
.map((i) => `${i.path.join(".")}: ${i.message}`)
|
|
713
|
+
.join("; ");
|
|
714
|
+
error(`Invalid skill data: ${issues}`);
|
|
715
|
+
process.exit(EXIT_CODES.VALIDATION_FAILED);
|
|
716
|
+
}
|
|
717
|
+
const skill = { ...parsed.data };
|
|
718
|
+
// Save meta entry (also creates directory)
|
|
719
|
+
await saveMetaItem(ctx, skill, "skill");
|
|
720
|
+
// AC: @skill-import ac-7 - Strip/normalize base-directory paths
|
|
721
|
+
// AC: @import-frontmatter-strip ac-2 - Store body-only content (strip frontmatter)
|
|
722
|
+
const normalizedContent = normalizeBaseDirectory(content);
|
|
723
|
+
const bodyOnlyContent = stripFrontmatter(normalizedContent);
|
|
724
|
+
// AC: @skill-import ac-2 - Copy content to .kspec/skills/<id>/SKILL.md
|
|
725
|
+
const skillMdPath = getSkillContentPath(ctx, skill.id);
|
|
726
|
+
await fs.writeFile(skillMdPath, bodyOnlyContent, "utf-8");
|
|
727
|
+
// AC: @import-frontmatter-strip ac-4, ac-5 - Copy all supporting directories
|
|
728
|
+
const supportingDirs = ["references", "scripts", "assets", "docs"];
|
|
729
|
+
for (const dirName of supportingDirs) {
|
|
730
|
+
const sourceSubDir = path.join(sourceDir, dirName);
|
|
731
|
+
try {
|
|
732
|
+
const stats = await fs.stat(sourceSubDir);
|
|
733
|
+
if (stats.isDirectory()) {
|
|
734
|
+
const targetSubDir = path.join(ctx.specDir, "skills", skill.id, dirName);
|
|
735
|
+
await fs.mkdir(targetSubDir, { recursive: true });
|
|
736
|
+
await copyDirectory(sourceSubDir, targetSubDir);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
catch {
|
|
740
|
+
// Directory doesn't exist, that's fine
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
// Commit changes
|
|
744
|
+
await commitIfShadow(ctx.shadow, "skill-import", skill.id, skill.name);
|
|
745
|
+
output(skill, () => success(`Imported skill: ${skill.id}`, { skill }));
|
|
746
|
+
}
|
|
747
|
+
catch (err) {
|
|
748
|
+
error("Failed to import skill", err);
|
|
749
|
+
process.exit(EXIT_CODES.ERROR);
|
|
750
|
+
}
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
//# sourceMappingURL=skill-crud.js.map
|