@kynetic-ai/spec 0.1.2 → 0.3.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 +116 -0
- package/dist/cli/batch-exec.d.ts.map +1 -0
- package/dist/cli/batch-exec.js +694 -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 +140 -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 +57 -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 +533 -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 +516 -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 +1097 -169
- 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 +811 -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 +1233 -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 +53 -0
- package/dist/cli/commands/skill-install.d.ts.map +1 -0
- package/dist/cli/commands/skill-install.js +452 -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 +569 -346
- 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 +227 -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 +569 -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 +235 -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 +468 -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 +351 -0
- package/dist/parser/config.d.ts.map +1 -0
- package/dist/parser/config.js +326 -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 +189 -0
- package/dist/parser/plan-document.d.ts.map +1 -0
- package/dist/parser/plan-document.js +340 -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 +277 -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 +83 -0
- package/dist/ralph/subagent.d.ts.map +1 -0
- package/dist/ralph/subagent.js +174 -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 +95 -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 +2 -2
- package/dist/schema/common.d.ts.map +1 -1
- package/dist/schema/common.js +34 -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 +233 -1
- package/dist/sessions/store.d.ts.map +1 -1
- package/dist/sessions/store.js +628 -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 +51 -0
- package/dist/strings/errors.d.ts.map +1 -1
- package/dist/strings/errors.js +136 -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/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/help/SKILL.md +42 -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/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/help/SKILL.md +37 -0
- package/templates/skills/manifest.yaml +15 -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/dist/sessions/store.js
CHANGED
|
@@ -11,15 +11,15 @@
|
|
|
11
11
|
* session.yaml # Metadata
|
|
12
12
|
* events.jsonl # Append-only event log
|
|
13
13
|
*/
|
|
14
|
-
import * as fs from
|
|
15
|
-
import * as fsPromises from
|
|
16
|
-
import * as path from
|
|
17
|
-
import * as YAML from
|
|
18
|
-
import {
|
|
14
|
+
import * as fs from "node:fs";
|
|
15
|
+
import * as fsPromises from "node:fs/promises";
|
|
16
|
+
import * as path from "node:path";
|
|
17
|
+
import * as YAML from "yaml";
|
|
18
|
+
import { SessionEventSchema, SessionMetadataSchema, } from "./types.js";
|
|
19
19
|
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
20
|
-
const SESSIONS_DIR =
|
|
21
|
-
const METADATA_FILE =
|
|
22
|
-
const EVENTS_FILE =
|
|
20
|
+
const SESSIONS_DIR = "sessions";
|
|
21
|
+
const METADATA_FILE = "session.yaml";
|
|
22
|
+
const EVENTS_FILE = "events.jsonl";
|
|
23
23
|
// ─── Path Helpers ────────────────────────────────────────────────────────────
|
|
24
24
|
/**
|
|
25
25
|
* Get the sessions directory path within a spec directory.
|
|
@@ -72,14 +72,18 @@ export async function createSession(specDir, input) {
|
|
|
72
72
|
id: input.id,
|
|
73
73
|
task_id: input.task_id,
|
|
74
74
|
agent_type: input.agent_type,
|
|
75
|
-
status: input.status ??
|
|
75
|
+
status: input.status ?? "active",
|
|
76
76
|
started_at: input.started_at ?? new Date().toISOString(),
|
|
77
77
|
ended_at: undefined,
|
|
78
78
|
};
|
|
79
79
|
// Validate and write metadata
|
|
80
80
|
const validated = SessionMetadataSchema.parse(metadata);
|
|
81
|
-
const content = YAML.stringify(validated, {
|
|
82
|
-
|
|
81
|
+
const content = YAML.stringify(validated, {
|
|
82
|
+
indent: 2,
|
|
83
|
+
lineWidth: 100,
|
|
84
|
+
sortMapEntries: false,
|
|
85
|
+
});
|
|
86
|
+
await fsPromises.writeFile(metadataPath, content, "utf-8");
|
|
83
87
|
return validated;
|
|
84
88
|
}
|
|
85
89
|
/**
|
|
@@ -92,7 +96,7 @@ export async function createSession(specDir, input) {
|
|
|
92
96
|
export async function getSession(specDir, sessionId) {
|
|
93
97
|
const metadataPath = getSessionMetadataPath(specDir, sessionId);
|
|
94
98
|
try {
|
|
95
|
-
const content = await fsPromises.readFile(metadataPath,
|
|
99
|
+
const content = await fsPromises.readFile(metadataPath, "utf-8");
|
|
96
100
|
const raw = YAML.parse(content);
|
|
97
101
|
return SessionMetadataSchema.parse(raw);
|
|
98
102
|
}
|
|
@@ -119,11 +123,15 @@ export async function updateSessionStatus(specDir, sessionId, status) {
|
|
|
119
123
|
const updated = {
|
|
120
124
|
...metadata,
|
|
121
125
|
status,
|
|
122
|
-
ended_at: status !==
|
|
126
|
+
ended_at: status !== "active" ? new Date().toISOString() : metadata.ended_at,
|
|
123
127
|
};
|
|
124
128
|
const metadataPath = getSessionMetadataPath(specDir, sessionId);
|
|
125
|
-
const content = YAML.stringify(updated, {
|
|
126
|
-
|
|
129
|
+
const content = YAML.stringify(updated, {
|
|
130
|
+
indent: 2,
|
|
131
|
+
lineWidth: 100,
|
|
132
|
+
sortMapEntries: false,
|
|
133
|
+
});
|
|
134
|
+
await fsPromises.writeFile(metadataPath, content, "utf-8");
|
|
127
135
|
return updated;
|
|
128
136
|
}
|
|
129
137
|
/**
|
|
@@ -135,10 +143,10 @@ export async function updateSessionStatus(specDir, sessionId, status) {
|
|
|
135
143
|
export async function listSessions(specDir) {
|
|
136
144
|
const sessionsDir = getSessionsDir(specDir);
|
|
137
145
|
try {
|
|
138
|
-
const entries = await fsPromises.readdir(sessionsDir, {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
146
|
+
const entries = await fsPromises.readdir(sessionsDir, {
|
|
147
|
+
withFileTypes: true,
|
|
148
|
+
});
|
|
149
|
+
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
142
150
|
}
|
|
143
151
|
catch {
|
|
144
152
|
return [];
|
|
@@ -168,8 +176,11 @@ export async function sessionExists(specDir, sessionId) {
|
|
|
168
176
|
async function getEventCount(specDir, sessionId) {
|
|
169
177
|
const eventsPath = getSessionEventsPath(specDir, sessionId);
|
|
170
178
|
try {
|
|
171
|
-
const content = await fsPromises.readFile(eventsPath,
|
|
172
|
-
const lines = content
|
|
179
|
+
const content = await fsPromises.readFile(eventsPath, "utf-8");
|
|
180
|
+
const lines = content
|
|
181
|
+
.trim()
|
|
182
|
+
.split("\n")
|
|
183
|
+
.filter((line) => line.length > 0);
|
|
173
184
|
return lines.length;
|
|
174
185
|
}
|
|
175
186
|
catch {
|
|
@@ -200,7 +211,7 @@ export async function appendEvent(specDir, input) {
|
|
|
200
211
|
// Ensure session directory exists (lazy creation)
|
|
201
212
|
await fsPromises.mkdir(sessionDir, { recursive: true });
|
|
202
213
|
// Get current event count for seq assignment
|
|
203
|
-
const seq = input.seq ?? await getEventCount(specDir, input.session_id);
|
|
214
|
+
const seq = input.seq ?? (await getEventCount(specDir, input.session_id));
|
|
204
215
|
// Build full event
|
|
205
216
|
const event = {
|
|
206
217
|
ts: input.ts ?? Date.now(),
|
|
@@ -212,10 +223,10 @@ export async function appendEvent(specDir, input) {
|
|
|
212
223
|
};
|
|
213
224
|
// Validate event
|
|
214
225
|
const validated = SessionEventSchema.parse(event);
|
|
215
|
-
// AC-3
|
|
226
|
+
// AC: @session-events ac-3 - Use synchronous append for crash safety
|
|
216
227
|
// This ensures the line is fully written before returning
|
|
217
|
-
const line = JSON.stringify(validated)
|
|
218
|
-
fs.appendFileSync(eventsPath, line,
|
|
228
|
+
const line = `${JSON.stringify(validated)}\n`;
|
|
229
|
+
fs.appendFileSync(eventsPath, line, "utf-8");
|
|
219
230
|
return validated;
|
|
220
231
|
}
|
|
221
232
|
/**
|
|
@@ -230,8 +241,11 @@ export async function appendEvent(specDir, input) {
|
|
|
230
241
|
export async function readEvents(specDir, sessionId) {
|
|
231
242
|
const eventsPath = getSessionEventsPath(specDir, sessionId);
|
|
232
243
|
try {
|
|
233
|
-
const content = await fsPromises.readFile(eventsPath,
|
|
234
|
-
const lines = content
|
|
244
|
+
const content = await fsPromises.readFile(eventsPath, "utf-8");
|
|
245
|
+
const lines = content
|
|
246
|
+
.trim()
|
|
247
|
+
.split("\n")
|
|
248
|
+
.filter((line) => line.length > 0);
|
|
235
249
|
const events = [];
|
|
236
250
|
for (const line of lines) {
|
|
237
251
|
try {
|
|
@@ -243,7 +257,7 @@ export async function readEvents(specDir, sessionId) {
|
|
|
243
257
|
// Skip invalid lines
|
|
244
258
|
}
|
|
245
259
|
}
|
|
246
|
-
// AC-4
|
|
260
|
+
// AC: @session-events ac-4 - Sort by sequence number
|
|
247
261
|
return events.sort((a, b) => a.seq - b.seq);
|
|
248
262
|
}
|
|
249
263
|
catch {
|
|
@@ -261,7 +275,7 @@ export async function readEvents(specDir, sessionId) {
|
|
|
261
275
|
*/
|
|
262
276
|
export async function readEventsSince(specDir, sessionId, since, until) {
|
|
263
277
|
const events = await readEvents(specDir, sessionId);
|
|
264
|
-
return events.filter(e => {
|
|
278
|
+
return events.filter((e) => {
|
|
265
279
|
if (e.ts < since)
|
|
266
280
|
return false;
|
|
267
281
|
if (until !== undefined && e.ts > until)
|
|
@@ -283,6 +297,121 @@ export async function getLastEvent(specDir, sessionId) {
|
|
|
283
297
|
}
|
|
284
298
|
return events[events.length - 1];
|
|
285
299
|
}
|
|
300
|
+
/**
|
|
301
|
+
* Count lines in events.jsonl without parsing JSON.
|
|
302
|
+
* Much faster than readEvents() for large files.
|
|
303
|
+
*/
|
|
304
|
+
async function countEventLines(specDir, sessionId) {
|
|
305
|
+
const eventsPath = getSessionEventsPath(specDir, sessionId);
|
|
306
|
+
try {
|
|
307
|
+
const content = await fsPromises.readFile(eventsPath, "utf-8");
|
|
308
|
+
if (!content.trim())
|
|
309
|
+
return 0;
|
|
310
|
+
return content.trim().split("\n").length;
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
return 0;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Count context-iter-*.json files for a session (iteration count).
|
|
318
|
+
*/
|
|
319
|
+
async function countIterations(specDir, sessionId) {
|
|
320
|
+
const sessionDir = getSessionDir(specDir, sessionId);
|
|
321
|
+
try {
|
|
322
|
+
const entries = await fsPromises.readdir(sessionDir);
|
|
323
|
+
return entries.filter((e) => e.startsWith("context-iter-") && e.endsWith(".json")).length;
|
|
324
|
+
}
|
|
325
|
+
catch {
|
|
326
|
+
return 0;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Count task completions by scanning events for tool calls that invoke
|
|
331
|
+
* `kspec task complete` or `npm run dev -- task complete`.
|
|
332
|
+
*
|
|
333
|
+
* Real sessions record task completions as session.update events with
|
|
334
|
+
* sessionUpdate: "tool_call" and rawInput.command containing the complete command.
|
|
335
|
+
* We use a fast substring check before JSON parsing for performance.
|
|
336
|
+
*/
|
|
337
|
+
async function countTaskCompletions(specDir, sessionId) {
|
|
338
|
+
const eventsPath = getSessionEventsPath(specDir, sessionId);
|
|
339
|
+
try {
|
|
340
|
+
const content = await fsPromises.readFile(eventsPath, "utf-8");
|
|
341
|
+
if (!content.trim())
|
|
342
|
+
return 0;
|
|
343
|
+
const lines = content.trim().split("\n");
|
|
344
|
+
let count = 0;
|
|
345
|
+
for (const line of lines) {
|
|
346
|
+
// Quick substring pre-filter: only parse lines that might contain task complete commands
|
|
347
|
+
if (!line.includes("task complete"))
|
|
348
|
+
continue;
|
|
349
|
+
// Must be a tool_call event (session.update with sessionUpdate: "tool_call")
|
|
350
|
+
if (!line.includes('"tool_call"'))
|
|
351
|
+
continue;
|
|
352
|
+
try {
|
|
353
|
+
const event = JSON.parse(line);
|
|
354
|
+
const command = event?.data?.update?.rawInput?.command;
|
|
355
|
+
if (typeof command === "string" && /\btask complete\b/.test(command)) {
|
|
356
|
+
count++;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
catch {
|
|
360
|
+
// Skip unparseable lines
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return count;
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
return 0;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Get a summary of a single session for list display.
|
|
371
|
+
*
|
|
372
|
+
* Gathers metadata and computes metrics lazily (only parses what's needed).
|
|
373
|
+
*
|
|
374
|
+
* @param specDir - The .kspec directory path
|
|
375
|
+
* @param sessionId - Session ID
|
|
376
|
+
* @returns Session summary or null if session doesn't exist
|
|
377
|
+
*/
|
|
378
|
+
export async function getSessionLogSummary(specDir, sessionId) {
|
|
379
|
+
const metadata = await getSession(specDir, sessionId);
|
|
380
|
+
if (!metadata)
|
|
381
|
+
return null;
|
|
382
|
+
const [eventCount, iterationCount, tasksCompleted] = await Promise.all([
|
|
383
|
+
countEventLines(specDir, sessionId),
|
|
384
|
+
countIterations(specDir, sessionId),
|
|
385
|
+
countTaskCompletions(specDir, sessionId),
|
|
386
|
+
]);
|
|
387
|
+
const startMs = new Date(metadata.started_at).getTime();
|
|
388
|
+
const endMs = metadata.ended_at
|
|
389
|
+
? new Date(metadata.ended_at).getTime()
|
|
390
|
+
: Date.now();
|
|
391
|
+
const durationMs = endMs - startMs;
|
|
392
|
+
return {
|
|
393
|
+
id: metadata.id,
|
|
394
|
+
status: metadata.status,
|
|
395
|
+
agent_type: metadata.agent_type,
|
|
396
|
+
started_at: metadata.started_at,
|
|
397
|
+
ended_at: metadata.ended_at,
|
|
398
|
+
duration_ms: durationMs,
|
|
399
|
+
event_count: eventCount,
|
|
400
|
+
iteration_count: iterationCount,
|
|
401
|
+
tasks_completed: tasksCompleted,
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Get summaries for all sessions.
|
|
406
|
+
*
|
|
407
|
+
* @param specDir - The .kspec directory path
|
|
408
|
+
* @returns Array of session summaries
|
|
409
|
+
*/
|
|
410
|
+
export async function getAllSessionLogSummaries(specDir) {
|
|
411
|
+
const sessionIds = await listSessions(specDir);
|
|
412
|
+
const summaries = await Promise.all(sessionIds.map((id) => getSessionLogSummary(specDir, id)));
|
|
413
|
+
return summaries.filter((s) => s !== null);
|
|
414
|
+
}
|
|
286
415
|
// ─── Context Snapshots ───────────────────────────────────────────────────────
|
|
287
416
|
/**
|
|
288
417
|
* Save session context snapshot for a given iteration.
|
|
@@ -302,7 +431,7 @@ export async function saveSessionContext(specDir, sessionId, iteration, context)
|
|
|
302
431
|
await fsPromises.mkdir(sessionDir, { recursive: true });
|
|
303
432
|
// Write context snapshot as pretty JSON
|
|
304
433
|
const content = JSON.stringify(context, null, 2);
|
|
305
|
-
await fsPromises.writeFile(contextPath, content,
|
|
434
|
+
await fsPromises.writeFile(contextPath, content, "utf-8");
|
|
306
435
|
}
|
|
307
436
|
/**
|
|
308
437
|
* Read session context snapshot for a given iteration.
|
|
@@ -315,11 +444,479 @@ export async function saveSessionContext(specDir, sessionId, iteration, context)
|
|
|
315
444
|
export async function readSessionContext(specDir, sessionId, iteration) {
|
|
316
445
|
const contextPath = getSessionContextPath(specDir, sessionId, iteration);
|
|
317
446
|
try {
|
|
318
|
-
const content = await fsPromises.readFile(contextPath,
|
|
447
|
+
const content = await fsPromises.readFile(contextPath, "utf-8");
|
|
319
448
|
return JSON.parse(content);
|
|
320
449
|
}
|
|
321
450
|
catch {
|
|
322
451
|
return null;
|
|
323
452
|
}
|
|
324
453
|
}
|
|
454
|
+
/**
|
|
455
|
+
* Resolve a session ID or prefix to a full session ID.
|
|
456
|
+
*
|
|
457
|
+
* AC: @session-log-show ac-7, ac-8, ac-9
|
|
458
|
+
*
|
|
459
|
+
* @param specDir - The .kspec directory path
|
|
460
|
+
* @param idOrPrefix - Full session ID or prefix (e.g., first 8 chars)
|
|
461
|
+
* @returns Resolution result
|
|
462
|
+
*/
|
|
463
|
+
export async function resolveSessionId(specDir, idOrPrefix) {
|
|
464
|
+
const sessionIds = await listSessions(specDir);
|
|
465
|
+
// First, try exact match
|
|
466
|
+
if (sessionIds.includes(idOrPrefix)) {
|
|
467
|
+
return { ok: true, id: idOrPrefix };
|
|
468
|
+
}
|
|
469
|
+
// Try prefix match
|
|
470
|
+
const matches = sessionIds.filter((id) => id.startsWith(idOrPrefix));
|
|
471
|
+
if (matches.length === 0) {
|
|
472
|
+
return { ok: false, error: "not_found" };
|
|
473
|
+
}
|
|
474
|
+
if (matches.length === 1) {
|
|
475
|
+
return { ok: true, id: matches[0] };
|
|
476
|
+
}
|
|
477
|
+
// Ambiguous - multiple matches
|
|
478
|
+
return { ok: false, error: "ambiguous", matches };
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Get iteration number from a context snapshot file.
|
|
482
|
+
*/
|
|
483
|
+
async function getIterationNumbers(specDir, sessionId) {
|
|
484
|
+
const sessionDir = getSessionDir(specDir, sessionId);
|
|
485
|
+
try {
|
|
486
|
+
const entries = await fsPromises.readdir(sessionDir);
|
|
487
|
+
const iterations = [];
|
|
488
|
+
for (const entry of entries) {
|
|
489
|
+
const match = entry.match(/^context-iter-(\d+)\.json$/);
|
|
490
|
+
if (match) {
|
|
491
|
+
iterations.push(parseInt(match[1], 10));
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return iterations.sort((a, b) => a - b);
|
|
495
|
+
}
|
|
496
|
+
catch {
|
|
497
|
+
return [];
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Extract task refs from task commands in event data.
|
|
502
|
+
*/
|
|
503
|
+
function extractTaskRef(command) {
|
|
504
|
+
// Match patterns like: kspec task start @ref, kspec task complete @ref
|
|
505
|
+
const match = command.match(/@[\w-]+/);
|
|
506
|
+
return match ? match[0] : null;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Compute per-iteration summaries from events.
|
|
510
|
+
*
|
|
511
|
+
* Dynamically creates iteration buckets based on both context snapshot files
|
|
512
|
+
* AND event data to handle cases where events are logged before context
|
|
513
|
+
* snapshots exist (e.g., active sessions).
|
|
514
|
+
*
|
|
515
|
+
* AC: @session-log-show ac-2
|
|
516
|
+
*/
|
|
517
|
+
async function computeIterationSummaries(specDir, sessionId) {
|
|
518
|
+
const events = await readEvents(specDir, sessionId);
|
|
519
|
+
const snapshotIterations = await getIterationNumbers(specDir, sessionId);
|
|
520
|
+
// Collect all iteration numbers from both snapshots and events
|
|
521
|
+
const allIterations = new Set(snapshotIterations);
|
|
522
|
+
for (const event of events) {
|
|
523
|
+
const data = event.data;
|
|
524
|
+
if (typeof data?.iteration === "number") {
|
|
525
|
+
allIterations.add(data.iteration);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
// If no iterations found anywhere, create a single iteration-0 summary
|
|
529
|
+
if (allIterations.size === 0) {
|
|
530
|
+
return [
|
|
531
|
+
{
|
|
532
|
+
iteration: 0,
|
|
533
|
+
event_count: events.length,
|
|
534
|
+
tasks_started: [],
|
|
535
|
+
tasks_completed: [],
|
|
536
|
+
},
|
|
537
|
+
];
|
|
538
|
+
}
|
|
539
|
+
// Create buckets for all known iterations
|
|
540
|
+
const iterations = Array.from(allIterations).sort((a, b) => a - b);
|
|
541
|
+
const iterationMap = new Map();
|
|
542
|
+
for (const n of iterations) {
|
|
543
|
+
iterationMap.set(n, []);
|
|
544
|
+
}
|
|
545
|
+
for (const event of events) {
|
|
546
|
+
// Try to get iteration from event data
|
|
547
|
+
const data = event.data;
|
|
548
|
+
const iter = data?.iteration;
|
|
549
|
+
if (typeof iter === "number" && iterationMap.has(iter)) {
|
|
550
|
+
iterationMap.get(iter).push(event);
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
// Events without iteration info (lifecycle events) go to iteration 0
|
|
554
|
+
// or the first known iteration if 0 doesn't exist
|
|
555
|
+
const fallbackIter = iterationMap.has(0) ? 0 : iterations[0];
|
|
556
|
+
iterationMap.get(fallbackIter).push(event);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
const summaries = [];
|
|
560
|
+
for (const [iterNum, iterEvents] of iterationMap) {
|
|
561
|
+
const tasksStarted = [];
|
|
562
|
+
const tasksCompleted = [];
|
|
563
|
+
for (const event of iterEvents) {
|
|
564
|
+
if (event.type === "session.update") {
|
|
565
|
+
const data = event.data;
|
|
566
|
+
const command = data?.update?.rawInput?.command;
|
|
567
|
+
if (typeof command === "string") {
|
|
568
|
+
if (/\btask start\b/.test(command)) {
|
|
569
|
+
const ref = extractTaskRef(command);
|
|
570
|
+
if (ref)
|
|
571
|
+
tasksStarted.push(ref);
|
|
572
|
+
}
|
|
573
|
+
else if (/\btask complete\b/.test(command)) {
|
|
574
|
+
const ref = extractTaskRef(command);
|
|
575
|
+
if (ref)
|
|
576
|
+
tasksCompleted.push(ref);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
summaries.push({
|
|
582
|
+
iteration: iterNum,
|
|
583
|
+
event_count: iterEvents.length,
|
|
584
|
+
tasks_started: tasksStarted,
|
|
585
|
+
tasks_completed: tasksCompleted,
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
return summaries.sort((a, b) => a.iteration - b.iteration);
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Get full session detail for session log show.
|
|
592
|
+
*
|
|
593
|
+
* @param specDir - The .kspec directory path
|
|
594
|
+
* @param sessionId - Session ID (must be resolved first)
|
|
595
|
+
* @returns Session detail or null if not found
|
|
596
|
+
*/
|
|
597
|
+
export async function getSessionLogDetail(specDir, sessionId) {
|
|
598
|
+
const metadata = await getSession(specDir, sessionId);
|
|
599
|
+
if (!metadata)
|
|
600
|
+
return null;
|
|
601
|
+
const [eventCount, iterationCount, iterations] = await Promise.all([
|
|
602
|
+
countEventLines(specDir, sessionId),
|
|
603
|
+
countIterations(specDir, sessionId),
|
|
604
|
+
computeIterationSummaries(specDir, sessionId),
|
|
605
|
+
]);
|
|
606
|
+
const startMs = new Date(metadata.started_at).getTime();
|
|
607
|
+
const endMs = metadata.ended_at
|
|
608
|
+
? new Date(metadata.ended_at).getTime()
|
|
609
|
+
: Date.now();
|
|
610
|
+
const durationMs = endMs - startMs;
|
|
611
|
+
return {
|
|
612
|
+
id: metadata.id,
|
|
613
|
+
status: metadata.status,
|
|
614
|
+
agent_type: metadata.agent_type,
|
|
615
|
+
task_id: metadata.task_id,
|
|
616
|
+
started_at: metadata.started_at,
|
|
617
|
+
ended_at: metadata.ended_at,
|
|
618
|
+
duration_ms: durationMs,
|
|
619
|
+
event_count: eventCount,
|
|
620
|
+
iteration_count: iterationCount,
|
|
621
|
+
iterations,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Compute aggregate statistics from session summaries.
|
|
626
|
+
*
|
|
627
|
+
* @param summaries - Array of session log summaries
|
|
628
|
+
* @returns Aggregate statistics
|
|
629
|
+
*/
|
|
630
|
+
export function computeSessionLogStats(summaries) {
|
|
631
|
+
if (summaries.length === 0) {
|
|
632
|
+
return {
|
|
633
|
+
total_sessions: 0,
|
|
634
|
+
total_events: 0,
|
|
635
|
+
total_iterations: 0,
|
|
636
|
+
total_tasks_completed: 0,
|
|
637
|
+
total_duration_ms: 0,
|
|
638
|
+
avg_duration_ms: 0,
|
|
639
|
+
avg_iterations_per_session: 0,
|
|
640
|
+
avg_tasks_per_session: 0,
|
|
641
|
+
status_breakdown: [],
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
// Compute totals
|
|
645
|
+
let totalEvents = 0;
|
|
646
|
+
let totalIterations = 0;
|
|
647
|
+
let totalTasksCompleted = 0;
|
|
648
|
+
let totalDuration = 0;
|
|
649
|
+
const statusCounts = {
|
|
650
|
+
active: 0,
|
|
651
|
+
completed: 0,
|
|
652
|
+
abandoned: 0,
|
|
653
|
+
};
|
|
654
|
+
for (const s of summaries) {
|
|
655
|
+
totalEvents += s.event_count;
|
|
656
|
+
totalIterations += s.iteration_count;
|
|
657
|
+
totalTasksCompleted += s.tasks_completed;
|
|
658
|
+
totalDuration += s.duration_ms;
|
|
659
|
+
statusCounts[s.status] = (statusCounts[s.status] || 0) + 1;
|
|
660
|
+
}
|
|
661
|
+
const n = summaries.length;
|
|
662
|
+
// Build status breakdown
|
|
663
|
+
const statusBreakdown = [];
|
|
664
|
+
for (const status of ["completed", "active", "abandoned"]) {
|
|
665
|
+
const count = statusCounts[status] || 0;
|
|
666
|
+
if (count > 0) {
|
|
667
|
+
statusBreakdown.push({
|
|
668
|
+
status,
|
|
669
|
+
count,
|
|
670
|
+
percentage: Math.round((count / n) * 100 * 10) / 10, // 1 decimal place
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return {
|
|
675
|
+
total_sessions: n,
|
|
676
|
+
total_events: totalEvents,
|
|
677
|
+
total_iterations: totalIterations,
|
|
678
|
+
total_tasks_completed: totalTasksCompleted,
|
|
679
|
+
total_duration_ms: totalDuration,
|
|
680
|
+
avg_duration_ms: Math.round(totalDuration / n),
|
|
681
|
+
avg_iterations_per_session: Math.round((totalIterations / n) * 10) / 10,
|
|
682
|
+
avg_tasks_per_session: Math.round((totalTasksCompleted / n) * 10) / 10,
|
|
683
|
+
status_breakdown: statusBreakdown,
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Count tool calls by scanning events.jsonl for tool_call events.
|
|
688
|
+
*
|
|
689
|
+
* This is relatively expensive as it parses all events, so only call
|
|
690
|
+
* when --tool-usage is requested.
|
|
691
|
+
*
|
|
692
|
+
* AC: @session-log-stats ac-6
|
|
693
|
+
*/
|
|
694
|
+
export async function computeToolUsageStats(specDir, sessionIds, limit = 10) {
|
|
695
|
+
const toolCounts = {};
|
|
696
|
+
let totalToolCalls = 0;
|
|
697
|
+
for (const sessionId of sessionIds) {
|
|
698
|
+
const eventsPath = getSessionEventsPath(specDir, sessionId);
|
|
699
|
+
try {
|
|
700
|
+
const content = await fsPromises.readFile(eventsPath, "utf-8");
|
|
701
|
+
if (!content.trim())
|
|
702
|
+
continue;
|
|
703
|
+
const lines = content.trim().split("\n");
|
|
704
|
+
for (const line of lines) {
|
|
705
|
+
// Quick pre-filter: only parse lines that might be tool_call events
|
|
706
|
+
if (!line.includes('"tool_call"'))
|
|
707
|
+
continue;
|
|
708
|
+
try {
|
|
709
|
+
const event = JSON.parse(line);
|
|
710
|
+
if (event?.type === "session.update") {
|
|
711
|
+
const update = event?.data?.update;
|
|
712
|
+
if (update?.sessionUpdate === "tool_call") {
|
|
713
|
+
const toolName = update?._meta?.claudeCode?.toolName || "unknown";
|
|
714
|
+
toolCounts[toolName] = (toolCounts[toolName] || 0) + 1;
|
|
715
|
+
totalToolCalls++;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
catch {
|
|
720
|
+
// Skip unparseable lines
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
catch {
|
|
725
|
+
// Skip sessions without events
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
// Sort by count descending, take top N
|
|
729
|
+
const sorted = Object.entries(toolCounts)
|
|
730
|
+
.map(([tool_name, count]) => ({
|
|
731
|
+
tool_name,
|
|
732
|
+
count,
|
|
733
|
+
percentage: totalToolCalls > 0
|
|
734
|
+
? Math.round((count / totalToolCalls) * 100 * 10) / 10
|
|
735
|
+
: 0,
|
|
736
|
+
}))
|
|
737
|
+
.sort((a, b) => b.count - a.count)
|
|
738
|
+
.slice(0, limit);
|
|
739
|
+
return sorted;
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Compute ISO-8601 week number and week-year in UTC.
|
|
743
|
+
*
|
|
744
|
+
* ISO-8601 rules:
|
|
745
|
+
* - Week 1 contains the first Thursday of the year
|
|
746
|
+
* - Week starts on Monday
|
|
747
|
+
* - Week-year may differ from calendar year at year boundaries
|
|
748
|
+
*
|
|
749
|
+
* Returns [weekYear, weekNumber] tuple.
|
|
750
|
+
*/
|
|
751
|
+
function getISOWeekUTC(date) {
|
|
752
|
+
// Clone and normalize to UTC midnight
|
|
753
|
+
const d = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
|
|
754
|
+
// Get day of week (ISO: Monday=1, Sunday=7)
|
|
755
|
+
const dayOfWeek = d.getUTCDay() || 7;
|
|
756
|
+
// Set to nearest Thursday (determines week-year)
|
|
757
|
+
d.setUTCDate(d.getUTCDate() + 4 - dayOfWeek);
|
|
758
|
+
// Week-year is the year of this Thursday
|
|
759
|
+
const weekYear = d.getUTCFullYear();
|
|
760
|
+
// January 4 is always in week 1 (as it's in the first week with a Thursday)
|
|
761
|
+
const jan4 = new Date(Date.UTC(weekYear, 0, 4));
|
|
762
|
+
const jan4DayOfWeek = jan4.getUTCDay() || 7;
|
|
763
|
+
// Start of week 1 (Monday before or on Jan 4)
|
|
764
|
+
const week1Start = new Date(jan4);
|
|
765
|
+
week1Start.setUTCDate(jan4.getUTCDate() - (jan4DayOfWeek - 1));
|
|
766
|
+
// Week number = weeks between week 1 start and the Thursday we found
|
|
767
|
+
const weekNum = Math.floor((d.getTime() - week1Start.getTime()) / (7 * 24 * 60 * 60 * 1000)) + 1;
|
|
768
|
+
return [weekYear, weekNum];
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Group sessions by time period (day or week).
|
|
772
|
+
*
|
|
773
|
+
* AC: @session-log-stats ac-7
|
|
774
|
+
*/
|
|
775
|
+
export function computeTimePeriodStats(summaries, groupBy) {
|
|
776
|
+
const buckets = {};
|
|
777
|
+
for (const s of summaries) {
|
|
778
|
+
const date = new Date(s.started_at);
|
|
779
|
+
let period;
|
|
780
|
+
if (groupBy === "day") {
|
|
781
|
+
period = date.toISOString().split("T")[0]; // YYYY-MM-DD in UTC
|
|
782
|
+
}
|
|
783
|
+
else {
|
|
784
|
+
// Week: use ISO-8601 week format (YYYY-Www) in UTC
|
|
785
|
+
const [weekYear, weekNum] = getISOWeekUTC(date);
|
|
786
|
+
period = `${weekYear}-W${weekNum.toString().padStart(2, "0")}`;
|
|
787
|
+
}
|
|
788
|
+
if (!buckets[period]) {
|
|
789
|
+
buckets[period] = { sessions: 0, tasks: 0, duration: 0 };
|
|
790
|
+
}
|
|
791
|
+
buckets[period].sessions += 1;
|
|
792
|
+
buckets[period].tasks += s.tasks_completed;
|
|
793
|
+
buckets[period].duration += s.duration_ms;
|
|
794
|
+
}
|
|
795
|
+
// Convert to array and sort by period (newest first for display)
|
|
796
|
+
const result = Object.entries(buckets)
|
|
797
|
+
.map(([period, data]) => ({
|
|
798
|
+
period,
|
|
799
|
+
sessions_count: data.sessions,
|
|
800
|
+
tasks_completed: data.tasks,
|
|
801
|
+
total_duration_ms: data.duration,
|
|
802
|
+
}))
|
|
803
|
+
.sort((a, b) => b.period.localeCompare(a.period));
|
|
804
|
+
return result;
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Extract a content excerpt around a match, limited to maxLength chars.
|
|
808
|
+
*
|
|
809
|
+
* AC: @session-log-search ac-4
|
|
810
|
+
*/
|
|
811
|
+
function extractContentExcerpt(data, pattern, maxLength = 200) {
|
|
812
|
+
// Stringify the data for searching
|
|
813
|
+
const str = JSON.stringify(data);
|
|
814
|
+
const lowerStr = str.toLowerCase();
|
|
815
|
+
const lowerPattern = pattern.toLowerCase();
|
|
816
|
+
const matchIndex = lowerStr.indexOf(lowerPattern);
|
|
817
|
+
if (matchIndex === -1) {
|
|
818
|
+
// Shouldn't happen since we pre-filtered, but return start of content
|
|
819
|
+
return str.length > maxLength ? str.slice(0, maxLength - 3) + "..." : str;
|
|
820
|
+
}
|
|
821
|
+
// Calculate excerpt window centered on match
|
|
822
|
+
const matchLen = pattern.length;
|
|
823
|
+
const contextBefore = Math.floor((maxLength - matchLen) / 2);
|
|
824
|
+
const start = Math.max(0, matchIndex - contextBefore);
|
|
825
|
+
const end = Math.min(str.length, start + maxLength);
|
|
826
|
+
let excerpt = str.slice(start, end);
|
|
827
|
+
// Add ellipsis indicators
|
|
828
|
+
if (start > 0) {
|
|
829
|
+
excerpt = "..." + excerpt.slice(3);
|
|
830
|
+
}
|
|
831
|
+
if (end < str.length) {
|
|
832
|
+
excerpt = excerpt.slice(0, -3) + "...";
|
|
833
|
+
}
|
|
834
|
+
return excerpt;
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Search across session events for a pattern.
|
|
838
|
+
*
|
|
839
|
+
* This streams through events.jsonl files, applying filters as early as possible
|
|
840
|
+
* for performance. Sessions are pre-filtered by metadata (--since, --agent) before
|
|
841
|
+
* scanning events to reduce I/O.
|
|
842
|
+
*
|
|
843
|
+
* AC: @session-log-search ac-1, ac-2, ac-3, ac-5, ac-7
|
|
844
|
+
*
|
|
845
|
+
* @param specDir - The .kspec directory path
|
|
846
|
+
* @param pattern - Case-insensitive substring to search for
|
|
847
|
+
* @param options - Search filtering options
|
|
848
|
+
* @returns Array of search results grouped by session
|
|
849
|
+
*/
|
|
850
|
+
export async function searchSessionEvents(specDir, pattern, options = {}) {
|
|
851
|
+
// Defense-in-depth: normalize limit to a valid positive integer
|
|
852
|
+
const rawLimit = options.limit ?? 50;
|
|
853
|
+
const limit = Number.isNaN(rawLimit) || rawLimit <= 0 ? 50 : rawLimit;
|
|
854
|
+
const lowerPattern = pattern.toLowerCase();
|
|
855
|
+
// Get all session summaries for metadata filtering
|
|
856
|
+
const allSummaries = await getAllSessionLogSummaries(specDir);
|
|
857
|
+
// AC: @session-log-search ac-3 - Pre-filter by --since
|
|
858
|
+
let filteredSummaries = allSummaries;
|
|
859
|
+
if (options.sinceDate) {
|
|
860
|
+
filteredSummaries = filteredSummaries.filter((s) => new Date(s.started_at) >= options.sinceDate);
|
|
861
|
+
}
|
|
862
|
+
// AC: @session-log-search ac-7 - Pre-filter by --agent
|
|
863
|
+
if (options.agentType) {
|
|
864
|
+
filteredSummaries = filteredSummaries.filter((s) => s.agent_type === options.agentType);
|
|
865
|
+
}
|
|
866
|
+
const results = [];
|
|
867
|
+
let totalMatches = 0;
|
|
868
|
+
for (const summary of filteredSummaries) {
|
|
869
|
+
if (totalMatches >= limit)
|
|
870
|
+
break;
|
|
871
|
+
const eventsPath = getSessionEventsPath(specDir, summary.id);
|
|
872
|
+
let content;
|
|
873
|
+
try {
|
|
874
|
+
content = await fsPromises.readFile(eventsPath, "utf-8");
|
|
875
|
+
}
|
|
876
|
+
catch {
|
|
877
|
+
continue; // Skip sessions without events
|
|
878
|
+
}
|
|
879
|
+
if (!content.trim())
|
|
880
|
+
continue;
|
|
881
|
+
const matches = [];
|
|
882
|
+
const lines = content.trim().split("\n");
|
|
883
|
+
for (const line of lines) {
|
|
884
|
+
if (totalMatches >= limit)
|
|
885
|
+
break;
|
|
886
|
+
// Quick substring pre-filter before parsing JSON
|
|
887
|
+
if (!line.toLowerCase().includes(lowerPattern))
|
|
888
|
+
continue;
|
|
889
|
+
try {
|
|
890
|
+
const event = JSON.parse(line);
|
|
891
|
+
// AC: @session-log-search ac-2 - Filter by event type
|
|
892
|
+
if (options.eventType && event.type !== options.eventType)
|
|
893
|
+
continue;
|
|
894
|
+
// Verify match in stringified data (not just line, in case pattern appears in metadata)
|
|
895
|
+
const dataStr = JSON.stringify(event.data);
|
|
896
|
+
if (!dataStr.toLowerCase().includes(lowerPattern))
|
|
897
|
+
continue;
|
|
898
|
+
// AC: @session-log-search ac-4 - Create match with excerpt
|
|
899
|
+
matches.push({
|
|
900
|
+
session_id: summary.id,
|
|
901
|
+
timestamp: event.ts,
|
|
902
|
+
event_type: event.type,
|
|
903
|
+
content_excerpt: extractContentExcerpt(event.data, pattern, 200),
|
|
904
|
+
});
|
|
905
|
+
totalMatches++;
|
|
906
|
+
}
|
|
907
|
+
catch {
|
|
908
|
+
// Skip unparseable lines
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
if (matches.length > 0) {
|
|
912
|
+
results.push({
|
|
913
|
+
session_id: summary.id,
|
|
914
|
+
agent_type: summary.agent_type,
|
|
915
|
+
started_at: summary.started_at,
|
|
916
|
+
matches,
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
return results;
|
|
921
|
+
}
|
|
325
922
|
//# sourceMappingURL=store.js.map
|