@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
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File watcher for .kspec directory
|
|
3
|
+
*
|
|
4
|
+
* AC Coverage:
|
|
5
|
+
* - ac-4: Watch .kspec/*.yaml files and broadcast changes
|
|
6
|
+
* - ac-5: Debounce rapid changes (500ms)
|
|
7
|
+
* - ac-6: Handle YAML parse errors gracefully
|
|
8
|
+
* - ac-7: Recovery with exponential backoff for directory access errors
|
|
9
|
+
* - ac-8: Fallback to Chokidar if Bun fs.watch fails
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { watch, type FSWatcher } from 'fs';
|
|
13
|
+
import { readFile } from 'fs/promises';
|
|
14
|
+
import { parse as parseYaml } from 'yaml';
|
|
15
|
+
import chokidar, { type FSWatcher as ChokidarWatcher } from 'chokidar';
|
|
16
|
+
import { join } from 'path';
|
|
17
|
+
|
|
18
|
+
export interface WatcherOptions {
|
|
19
|
+
kspecDir: string;
|
|
20
|
+
onFileChange: (file: string, content: string) => void;
|
|
21
|
+
onError: (error: Error, file?: string) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface WatcherEvent {
|
|
25
|
+
type: 'change' | 'error';
|
|
26
|
+
file: string;
|
|
27
|
+
content?: string;
|
|
28
|
+
error?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* File watcher with debouncing and error handling
|
|
33
|
+
*/
|
|
34
|
+
export class KspecWatcher {
|
|
35
|
+
private watcher: FSWatcher | ChokidarWatcher | null = null;
|
|
36
|
+
private debounceTimers = new Map<string, NodeJS.Timeout>();
|
|
37
|
+
private debounceMs = 500;
|
|
38
|
+
private usingChokidar = false;
|
|
39
|
+
private retryCount = 0;
|
|
40
|
+
private maxRetries = 5;
|
|
41
|
+
private baseBackoffMs = 1000;
|
|
42
|
+
|
|
43
|
+
constructor(private options: WatcherOptions) {}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* AC-4, AC-8: Start watching .kspec directory (with Chokidar fallback)
|
|
47
|
+
*/
|
|
48
|
+
async start(): Promise<void> {
|
|
49
|
+
try {
|
|
50
|
+
// Try Bun's native fs.watch first
|
|
51
|
+
await this.startBunWatcher();
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.warn('[watcher] Bun fs.watch failed, falling back to Chokidar', error);
|
|
54
|
+
// AC-8: Fallback to Chokidar
|
|
55
|
+
this.usingChokidar = true;
|
|
56
|
+
await this.startChokidarWatcher();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Start Bun's native file watcher
|
|
62
|
+
*/
|
|
63
|
+
private async startBunWatcher(): Promise<void> {
|
|
64
|
+
this.watcher = watch(
|
|
65
|
+
this.options.kspecDir,
|
|
66
|
+
{ recursive: true },
|
|
67
|
+
(eventType, filename) => {
|
|
68
|
+
if (!filename || !filename.endsWith('.yaml')) return;
|
|
69
|
+
|
|
70
|
+
const fullPath = join(this.options.kspecDir, filename);
|
|
71
|
+
this.handleFileChange(fullPath);
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
console.log('[watcher] Watching .kspec directory with Bun fs.watch');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* AC-8: Start Chokidar watcher as fallback
|
|
80
|
+
*/
|
|
81
|
+
private async startChokidarWatcher(): Promise<void> {
|
|
82
|
+
this.watcher = chokidar.watch(join(this.options.kspecDir, '**/*.yaml'), {
|
|
83
|
+
ignoreInitial: true,
|
|
84
|
+
awaitWriteFinish: {
|
|
85
|
+
stabilityThreshold: 100,
|
|
86
|
+
pollInterval: 50
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
(this.watcher as ChokidarWatcher).on('change', (path: string) => {
|
|
91
|
+
this.handleFileChange(path);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
(this.watcher as ChokidarWatcher).on('error', (err: unknown) => {
|
|
95
|
+
// AC-7: Recovery with exponential backoff
|
|
96
|
+
this.handleWatcherError(err instanceof Error ? err : new Error(String(err)));
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
console.log('[watcher] Watching .kspec directory with Chokidar');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* AC-5: Debounce file changes (500ms)
|
|
104
|
+
*/
|
|
105
|
+
private handleFileChange(filePath: string): void {
|
|
106
|
+
// Clear existing timer for this file
|
|
107
|
+
const existingTimer = this.debounceTimers.get(filePath);
|
|
108
|
+
if (existingTimer) {
|
|
109
|
+
clearTimeout(existingTimer);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Set new debounced timer
|
|
113
|
+
const timer = setTimeout(async () => {
|
|
114
|
+
this.debounceTimers.delete(filePath);
|
|
115
|
+
await this.processFileChange(filePath);
|
|
116
|
+
}, this.debounceMs);
|
|
117
|
+
|
|
118
|
+
this.debounceTimers.set(filePath, timer);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* AC-4, AC-6: Process file change and broadcast to clients
|
|
123
|
+
*/
|
|
124
|
+
private async processFileChange(filePath: string): Promise<void> {
|
|
125
|
+
try {
|
|
126
|
+
const content = await readFile(filePath, 'utf-8');
|
|
127
|
+
|
|
128
|
+
// AC-6: Validate YAML before broadcasting
|
|
129
|
+
try {
|
|
130
|
+
parseYaml(content);
|
|
131
|
+
this.options.onFileChange(filePath, content);
|
|
132
|
+
this.retryCount = 0; // Reset retry count on success
|
|
133
|
+
} catch (parseError) {
|
|
134
|
+
// AC-6: Log parse error and broadcast error event
|
|
135
|
+
const error = new Error(`YAML parse error in ${filePath}: ${parseError}`);
|
|
136
|
+
console.error('[watcher]', error.message);
|
|
137
|
+
this.options.onError(error, filePath);
|
|
138
|
+
}
|
|
139
|
+
} catch (error) {
|
|
140
|
+
// AC-7: Handle file read errors (directory inaccessible, etc.)
|
|
141
|
+
console.error('[watcher] Error reading file:', error);
|
|
142
|
+
this.handleWatcherError(error as Error);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* AC-7: Handle watcher errors with exponential backoff
|
|
148
|
+
*/
|
|
149
|
+
private async handleWatcherError(error: Error): Promise<void> {
|
|
150
|
+
this.options.onError(error);
|
|
151
|
+
|
|
152
|
+
if (this.retryCount >= this.maxRetries) {
|
|
153
|
+
console.error('[watcher] Max retries reached, giving up');
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.retryCount++;
|
|
158
|
+
const backoffMs = this.baseBackoffMs * Math.pow(2, this.retryCount - 1);
|
|
159
|
+
|
|
160
|
+
console.log(`[watcher] Attempting recovery in ${backoffMs}ms (attempt ${this.retryCount}/${this.maxRetries})`);
|
|
161
|
+
|
|
162
|
+
setTimeout(async () => {
|
|
163
|
+
try {
|
|
164
|
+
await this.stop();
|
|
165
|
+
await this.start();
|
|
166
|
+
console.log('[watcher] Recovery successful');
|
|
167
|
+
} catch (retryError) {
|
|
168
|
+
console.error('[watcher] Recovery failed:', retryError);
|
|
169
|
+
// Will retry again if under max retries
|
|
170
|
+
this.handleWatcherError(retryError as Error);
|
|
171
|
+
}
|
|
172
|
+
}, backoffMs);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Stop watching
|
|
177
|
+
*/
|
|
178
|
+
async stop(): Promise<void> {
|
|
179
|
+
// Clear all debounce timers
|
|
180
|
+
for (const timer of this.debounceTimers.values()) {
|
|
181
|
+
clearTimeout(timer);
|
|
182
|
+
}
|
|
183
|
+
this.debounceTimers.clear();
|
|
184
|
+
|
|
185
|
+
// Close watcher
|
|
186
|
+
if (this.watcher) {
|
|
187
|
+
if (this.usingChokidar) {
|
|
188
|
+
await (this.watcher as ChokidarWatcher).close();
|
|
189
|
+
} else {
|
|
190
|
+
(this.watcher as FSWatcher).close();
|
|
191
|
+
}
|
|
192
|
+
this.watcher = null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket message handler
|
|
3
|
+
*
|
|
4
|
+
* AC coverage:
|
|
5
|
+
* - ac-26 (@api-contract): Command format
|
|
6
|
+
* - ac-27 (@api-contract): Ack response
|
|
7
|
+
* - ac-28 (@api-contract): Subscribe to topics
|
|
8
|
+
* - ac-30 (@api-contract): Malformed command error
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { ServerWebSocket } from 'bun';
|
|
12
|
+
import type { WebSocketCommand, CommandAck, ConnectionData } from './types';
|
|
13
|
+
import type { PubSubManager } from './pubsub';
|
|
14
|
+
|
|
15
|
+
export class WebSocketHandler {
|
|
16
|
+
constructor(private pubsub: PubSubManager) {}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Handle incoming WebSocket command
|
|
20
|
+
* AC: @api-contract ac-26, ac-27, ac-28, ac-30
|
|
21
|
+
*/
|
|
22
|
+
handleMessage(ws: ServerWebSocket<ConnectionData>, rawMessage: string | Buffer) {
|
|
23
|
+
let command: WebSocketCommand;
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
// Parse command
|
|
27
|
+
const messageStr = typeof rawMessage === 'string' ? rawMessage : rawMessage.toString();
|
|
28
|
+
command = JSON.parse(messageStr);
|
|
29
|
+
|
|
30
|
+
// Validate command structure
|
|
31
|
+
if (!command.action) {
|
|
32
|
+
// AC: @api-contract ac-30
|
|
33
|
+
this.sendAck(ws, undefined, false, 'validation_error', 'Missing action field');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
} catch (error) {
|
|
37
|
+
// AC: @api-contract ac-30
|
|
38
|
+
this.sendAck(ws, undefined, false, 'validation_error', 'Invalid JSON');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Process command
|
|
43
|
+
try {
|
|
44
|
+
switch (command.action) {
|
|
45
|
+
case 'subscribe':
|
|
46
|
+
this.handleSubscribe(ws, command);
|
|
47
|
+
break;
|
|
48
|
+
|
|
49
|
+
case 'unsubscribe':
|
|
50
|
+
this.handleUnsubscribe(ws, command);
|
|
51
|
+
break;
|
|
52
|
+
|
|
53
|
+
case 'ping':
|
|
54
|
+
this.handlePing(ws, command);
|
|
55
|
+
break;
|
|
56
|
+
|
|
57
|
+
default:
|
|
58
|
+
// AC: @api-contract ac-30
|
|
59
|
+
this.sendAck(ws, command.request_id, false, 'unknown_action', `Unknown action: ${command.action}`);
|
|
60
|
+
}
|
|
61
|
+
} catch (error) {
|
|
62
|
+
const errorMsg = error instanceof Error ? error.message : 'Internal error';
|
|
63
|
+
this.sendAck(ws, command.request_id, false, 'error', errorMsg);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Handle subscribe command
|
|
69
|
+
* AC: @api-contract ac-28
|
|
70
|
+
*/
|
|
71
|
+
private handleSubscribe(ws: ServerWebSocket<ConnectionData>, command: WebSocketCommand) {
|
|
72
|
+
const topics = command.payload?.topics;
|
|
73
|
+
|
|
74
|
+
if (!topics || !Array.isArray(topics) || topics.length === 0) {
|
|
75
|
+
this.sendAck(ws, command.request_id, false, 'validation_error', 'Missing or invalid topics array');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const success = this.pubsub.subscribe(ws.data.sessionId, topics);
|
|
80
|
+
|
|
81
|
+
if (success) {
|
|
82
|
+
this.sendAck(ws, command.request_id, true);
|
|
83
|
+
console.log(`[ws] ${ws.data.sessionId} subscribed to: ${topics.join(', ')}`);
|
|
84
|
+
} else {
|
|
85
|
+
this.sendAck(ws, command.request_id, false, 'not_found', 'Session not found');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Handle unsubscribe command
|
|
91
|
+
*/
|
|
92
|
+
private handleUnsubscribe(ws: ServerWebSocket<ConnectionData>, command: WebSocketCommand) {
|
|
93
|
+
const topics = command.payload?.topics;
|
|
94
|
+
|
|
95
|
+
if (!topics || !Array.isArray(topics) || topics.length === 0) {
|
|
96
|
+
this.sendAck(ws, command.request_id, false, 'validation_error', 'Missing or invalid topics array');
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const success = this.pubsub.unsubscribe(ws.data.sessionId, topics);
|
|
101
|
+
|
|
102
|
+
if (success) {
|
|
103
|
+
this.sendAck(ws, command.request_id, true);
|
|
104
|
+
console.log(`[ws] ${ws.data.sessionId} unsubscribed from: ${topics.join(', ')}`);
|
|
105
|
+
} else {
|
|
106
|
+
this.sendAck(ws, command.request_id, false, 'not_found', 'Session not found');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Handle ping command (application-level ping, not WebSocket frame)
|
|
112
|
+
*/
|
|
113
|
+
private handlePing(ws: ServerWebSocket<ConnectionData>, command: WebSocketCommand) {
|
|
114
|
+
this.sendAck(ws, command.request_id, true);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Send ack response
|
|
119
|
+
* AC: @api-contract ac-27
|
|
120
|
+
*/
|
|
121
|
+
private sendAck(
|
|
122
|
+
ws: ServerWebSocket<ConnectionData>,
|
|
123
|
+
request_id: string | undefined,
|
|
124
|
+
success: boolean,
|
|
125
|
+
error?: string,
|
|
126
|
+
details?: string
|
|
127
|
+
) {
|
|
128
|
+
const ack: CommandAck = {
|
|
129
|
+
ack: true,
|
|
130
|
+
request_id,
|
|
131
|
+
success,
|
|
132
|
+
error,
|
|
133
|
+
details
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
ws.send(JSON.stringify(ack));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket heartbeat (ping/pong) management
|
|
3
|
+
*
|
|
4
|
+
* AC coverage:
|
|
5
|
+
* - ac-13 (@daemon-server): Heartbeat ping every 30s
|
|
6
|
+
* - ac-14 (@daemon-server): Timeout close after 90s without pong
|
|
7
|
+
* - ac-4 (@trait-websocket-protocol): Send ping after 30s inactivity
|
|
8
|
+
* - ac-5 (@trait-websocket-protocol): Close after 90s without pong
|
|
9
|
+
* - ac-7 (@trait-websocket-protocol): Close code 1001 for timeout
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { ServerWebSocket } from 'bun';
|
|
13
|
+
import type { ConnectionData } from './types';
|
|
14
|
+
|
|
15
|
+
export class HeartbeatManager {
|
|
16
|
+
private pingInterval?: NodeJS.Timeout;
|
|
17
|
+
private readonly PING_INTERVAL = 30_000; // 30 seconds
|
|
18
|
+
private readonly PONG_TIMEOUT = 90_000; // 90 seconds
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Start heartbeat monitoring for all connections
|
|
22
|
+
* AC: @daemon-server ac-13, @trait-websocket-protocol ac-4
|
|
23
|
+
*/
|
|
24
|
+
start(connections: Map<string, ServerWebSocket<ConnectionData>>) {
|
|
25
|
+
this.pingInterval = setInterval(() => {
|
|
26
|
+
const now = Date.now();
|
|
27
|
+
|
|
28
|
+
for (const [sessionId, ws] of connections) {
|
|
29
|
+
// Check if pong timeout exceeded
|
|
30
|
+
if (ws.data.lastPing && !ws.data.lastPong) {
|
|
31
|
+
const timeSincePing = now - ws.data.lastPing;
|
|
32
|
+
|
|
33
|
+
// AC: @daemon-server ac-14, @trait-websocket-protocol ac-5, ac-7
|
|
34
|
+
if (timeSincePing > this.PONG_TIMEOUT) {
|
|
35
|
+
console.warn(`[heartbeat] Closing ${sessionId} - no pong for ${timeSincePing}ms`);
|
|
36
|
+
ws.close(1001, 'Ping timeout'); // AC: @trait-websocket-protocol ac-7
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Send ping if no recent activity
|
|
42
|
+
const lastActivity = ws.data.lastPong ?? ws.data.lastPing ?? 0;
|
|
43
|
+
const timeSinceActivity = now - lastActivity;
|
|
44
|
+
|
|
45
|
+
if (timeSinceActivity >= this.PING_INTERVAL) {
|
|
46
|
+
ws.data.lastPing = now;
|
|
47
|
+
ws.data.lastPong = undefined; // Reset pong until received
|
|
48
|
+
ws.ping();
|
|
49
|
+
console.debug(`[heartbeat] Sent ping to ${sessionId}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}, this.PING_INTERVAL);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Stop heartbeat monitoring
|
|
57
|
+
*/
|
|
58
|
+
stop() {
|
|
59
|
+
if (this.pingInterval) {
|
|
60
|
+
clearInterval(this.pingInterval);
|
|
61
|
+
this.pingInterval = undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Record pong received from connection
|
|
67
|
+
*/
|
|
68
|
+
recordPong(ws: ServerWebSocket<ConnectionData>) {
|
|
69
|
+
ws.data.lastPong = Date.now();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Topic-based pub/sub system for WebSocket connections
|
|
3
|
+
*
|
|
4
|
+
* AC coverage:
|
|
5
|
+
* - ac-28 (@api-contract): Subscribe to topics
|
|
6
|
+
* - ac-29 (@api-contract): Event format with seq
|
|
7
|
+
* - ac-32 (@api-contract): Backpressure handling
|
|
8
|
+
* - ac-2 (@trait-websocket-protocol): Subscribe command tracking
|
|
9
|
+
* - ac-3 (@trait-websocket-protocol): Broadcast event format
|
|
10
|
+
* - ac-6 (@trait-websocket-protocol): Backpressure pause
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ServerWebSocket } from 'bun';
|
|
14
|
+
import { ulid } from 'ulidx';
|
|
15
|
+
import type { BroadcastEvent, ConnectionData } from './types';
|
|
16
|
+
|
|
17
|
+
export class PubSubManager {
|
|
18
|
+
private connections = new Map<string, ServerWebSocket<ConnectionData>>();
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Register a new WebSocket connection
|
|
22
|
+
* AC: @trait-websocket-protocol ac-1
|
|
23
|
+
*/
|
|
24
|
+
addConnection(sessionId: string, ws: ServerWebSocket<ConnectionData>) {
|
|
25
|
+
this.connections.set(sessionId, ws);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Remove a WebSocket connection
|
|
30
|
+
*/
|
|
31
|
+
removeConnection(sessionId: string) {
|
|
32
|
+
this.connections.delete(sessionId);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Subscribe a connection to topics
|
|
37
|
+
* AC: @api-contract ac-28, @trait-websocket-protocol ac-2
|
|
38
|
+
*/
|
|
39
|
+
subscribe(sessionId: string, topics: string[]): boolean {
|
|
40
|
+
const ws = this.connections.get(sessionId);
|
|
41
|
+
if (!ws) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for (const topic of topics) {
|
|
46
|
+
ws.data.topics.add(topic);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Unsubscribe a connection from topics
|
|
54
|
+
*/
|
|
55
|
+
unsubscribe(sessionId: string, topics: string[]): boolean {
|
|
56
|
+
const ws = this.connections.get(sessionId);
|
|
57
|
+
if (!ws) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
for (const topic of topics) {
|
|
62
|
+
ws.data.topics.delete(topic);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Broadcast event to all connections subscribed to a topic
|
|
70
|
+
* AC: @api-contract ac-29, @trait-websocket-protocol ac-3, ac-6
|
|
71
|
+
* AC: @multi-directory-daemon ac-18, ac-21 - Filter by project binding
|
|
72
|
+
*/
|
|
73
|
+
broadcast(topic: string, event: string, data: Record<string, unknown>, projectPath?: string) {
|
|
74
|
+
for (const [sessionId, ws] of this.connections) {
|
|
75
|
+
// AC: @multi-directory-daemon ac-18 - Only send to connections bound to same project
|
|
76
|
+
if (projectPath && ws.data.projectPath !== projectPath) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Only send to connections subscribed to this topic
|
|
81
|
+
if (!ws.data.topics.has(topic)) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// AC: @trait-websocket-protocol ac-6 - Check backpressure
|
|
86
|
+
// Bun's ServerWebSocket doesn't have bufferedAmount, so we use getBufferedAmount()
|
|
87
|
+
const buffered = ws.getBufferedAmount?.() ?? 0;
|
|
88
|
+
const MAX_BUFFER = 1024 * 1024; // 1MB threshold
|
|
89
|
+
|
|
90
|
+
if (buffered > MAX_BUFFER) {
|
|
91
|
+
console.warn(`[pubsub] Skipping broadcast to ${sessionId} - backpressure (${buffered} bytes buffered)`);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Increment sequence number for this connection
|
|
96
|
+
ws.data.seq++;
|
|
97
|
+
|
|
98
|
+
// AC: @api-contract ac-29, @trait-websocket-protocol ac-3
|
|
99
|
+
const message: BroadcastEvent = {
|
|
100
|
+
msg_id: ulid(),
|
|
101
|
+
seq: ws.data.seq,
|
|
102
|
+
timestamp: new Date().toISOString(),
|
|
103
|
+
topic,
|
|
104
|
+
event,
|
|
105
|
+
data
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
ws.send(JSON.stringify(message));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Get all connections (for heartbeat checks)
|
|
114
|
+
*/
|
|
115
|
+
getAllConnections(): Map<string, ServerWebSocket<ConnectionData>> {
|
|
116
|
+
return this.connections;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get connection count
|
|
121
|
+
*/
|
|
122
|
+
getConnectionCount(): number {
|
|
123
|
+
return this.connections.size;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket Protocol Types
|
|
3
|
+
*
|
|
4
|
+
* AC coverage:
|
|
5
|
+
* - ac-25 (@api-contract): Connected event with session_id
|
|
6
|
+
* - ac-26 (@api-contract): Command format
|
|
7
|
+
* - ac-27 (@api-contract): Ack response format
|
|
8
|
+
* - ac-28 (@api-contract): Subscribe to topics
|
|
9
|
+
* - ac-29 (@api-contract): Event format with seq
|
|
10
|
+
* - ac-1 (@trait-websocket-protocol): Connection ID and connected event
|
|
11
|
+
* - ac-2 (@trait-websocket-protocol): Subscribe command tracking
|
|
12
|
+
* - ac-3 (@trait-websocket-protocol): Broadcast event format
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { ServerWebSocket } from 'bun';
|
|
16
|
+
|
|
17
|
+
// AC: @api-contract ac-26
|
|
18
|
+
export interface WebSocketCommand {
|
|
19
|
+
action: 'subscribe' | 'unsubscribe' | 'ping';
|
|
20
|
+
request_id?: string;
|
|
21
|
+
payload?: {
|
|
22
|
+
topics?: string[];
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// AC: @api-contract ac-27
|
|
27
|
+
export interface CommandAck {
|
|
28
|
+
ack: boolean;
|
|
29
|
+
request_id?: string;
|
|
30
|
+
success: boolean;
|
|
31
|
+
error?: string;
|
|
32
|
+
details?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// AC: @api-contract ac-25, @trait-websocket-protocol ac-1
|
|
36
|
+
export interface ConnectedEvent {
|
|
37
|
+
event: 'connected';
|
|
38
|
+
data: {
|
|
39
|
+
session_id: string;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// AC: @api-contract ac-29, @trait-websocket-protocol ac-3
|
|
44
|
+
export interface BroadcastEvent {
|
|
45
|
+
msg_id: string;
|
|
46
|
+
seq: number;
|
|
47
|
+
timestamp: string;
|
|
48
|
+
topic: string;
|
|
49
|
+
event: string;
|
|
50
|
+
data: Record<string, unknown>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Internal connection metadata
|
|
54
|
+
export interface ConnectionData {
|
|
55
|
+
sessionId: string;
|
|
56
|
+
topics: Set<string>;
|
|
57
|
+
lastPing?: number;
|
|
58
|
+
lastPong?: number;
|
|
59
|
+
seq: number; // Per-connection sequence number
|
|
60
|
+
projectPath: string; // AC: @multi-directory-daemon ac-21 - bound project path
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface WebSocketContext {
|
|
64
|
+
ws: ServerWebSocket<ConnectionData>;
|
|
65
|
+
connections: Map<string, ServerWebSocket<ConnectionData>>;
|
|
66
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML Export Module
|
|
3
|
+
*
|
|
4
|
+
* Generates self-contained HTML files with embedded JSON and SPA loader.
|
|
5
|
+
* AC: @gh-pages-export ac-6
|
|
6
|
+
*/
|
|
7
|
+
import type { KspecSnapshot } from "./types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Generate a self-contained HTML file with embedded JSON snapshot.
|
|
10
|
+
*
|
|
11
|
+
* The HTML includes:
|
|
12
|
+
* - Embedded JSON data in a script tag
|
|
13
|
+
* - Redirect to hosted SPA (or minimal inline viewer)
|
|
14
|
+
* - Fallback for viewing raw data
|
|
15
|
+
*
|
|
16
|
+
* AC: @gh-pages-export ac-6
|
|
17
|
+
*/
|
|
18
|
+
export declare function generateHtmlExport(snapshot: KspecSnapshot): string;
|
|
19
|
+
//# sourceMappingURL=html.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/export/html.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAmNlE"}
|