@ag-eco/agentplate-cli 0.13.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/LICENSE +21 -0
- package/README.md +462 -0
- package/agents/ap-co-creation.md +90 -0
- package/agents/builder.md +144 -0
- package/agents/coordinator.md +377 -0
- package/agents/lead.md +435 -0
- package/agents/merger.md +164 -0
- package/agents/monitor.md +214 -0
- package/agents/orchestrator.md +239 -0
- package/agents/reviewer.md +140 -0
- package/agents/scout.md +125 -0
- package/agents/supervisor.md +427 -0
- package/package.json +66 -0
- package/src/agents/capabilities.test.ts +85 -0
- package/src/agents/capabilities.ts +125 -0
- package/src/agents/checkpoint.test.ts +88 -0
- package/src/agents/checkpoint.ts +101 -0
- package/src/agents/copilot-hooks-deployer.test.ts +162 -0
- package/src/agents/copilot-hooks-deployer.ts +93 -0
- package/src/agents/guard-rules.test.ts +372 -0
- package/src/agents/guard-rules.ts +97 -0
- package/src/agents/headless-mail-injector.test.ts +709 -0
- package/src/agents/headless-mail-injector.ts +377 -0
- package/src/agents/headless-prompt.test.ts +102 -0
- package/src/agents/headless-prompt.ts +68 -0
- package/src/agents/hooks-deployer.test.ts +3119 -0
- package/src/agents/hooks-deployer.ts +804 -0
- package/src/agents/identity.test.ts +604 -0
- package/src/agents/identity.ts +384 -0
- package/src/agents/lifecycle.test.ts +196 -0
- package/src/agents/lifecycle.ts +183 -0
- package/src/agents/mail-poll-detect.test.ts +153 -0
- package/src/agents/mail-poll-detect.ts +73 -0
- package/src/agents/manifest.test.ts +1026 -0
- package/src/agents/manifest.ts +376 -0
- package/src/agents/overlay.test.ts +1058 -0
- package/src/agents/overlay.ts +490 -0
- package/src/agents/scope-detect.test.ts +190 -0
- package/src/agents/scope-detect.ts +146 -0
- package/src/agents/turn-lock.test.ts +181 -0
- package/src/agents/turn-lock.ts +235 -0
- package/src/agents/turn-runner-dispatch.test.ts +182 -0
- package/src/agents/turn-runner-dispatch.ts +105 -0
- package/src/agents/turn-runner.test.ts +2312 -0
- package/src/agents/turn-runner.ts +1383 -0
- package/src/beads/client.test.ts +217 -0
- package/src/beads/client.ts +230 -0
- package/src/beads/molecules.test.ts +338 -0
- package/src/beads/molecules.ts +198 -0
- package/src/commands/agents.test.ts +328 -0
- package/src/commands/agents.ts +299 -0
- package/src/commands/clean.test.ts +797 -0
- package/src/commands/clean.ts +791 -0
- package/src/commands/completions.test.ts +348 -0
- package/src/commands/completions.ts +981 -0
- package/src/commands/coordinator.test.ts +2975 -0
- package/src/commands/coordinator.ts +1841 -0
- package/src/commands/costs.test.ts +1183 -0
- package/src/commands/costs.ts +599 -0
- package/src/commands/dashboard.test.ts +954 -0
- package/src/commands/dashboard.ts +1212 -0
- package/src/commands/discover.test.ts +288 -0
- package/src/commands/discover.ts +202 -0
- package/src/commands/doctor.test.ts +303 -0
- package/src/commands/doctor.ts +311 -0
- package/src/commands/ecosystem.test.ts +226 -0
- package/src/commands/ecosystem.ts +248 -0
- package/src/commands/errors.test.ts +654 -0
- package/src/commands/errors.ts +197 -0
- package/src/commands/feed.test.ts +709 -0
- package/src/commands/feed.ts +260 -0
- package/src/commands/group.test.ts +475 -0
- package/src/commands/group.ts +546 -0
- package/src/commands/hooks.test.ts +458 -0
- package/src/commands/hooks.ts +263 -0
- package/src/commands/init.test.ts +1011 -0
- package/src/commands/init.ts +967 -0
- package/src/commands/inspect.test.ts +1239 -0
- package/src/commands/inspect.ts +648 -0
- package/src/commands/log.test.ts +1913 -0
- package/src/commands/log.ts +958 -0
- package/src/commands/logs.test.ts +801 -0
- package/src/commands/logs.ts +483 -0
- package/src/commands/mail.test.ts +1501 -0
- package/src/commands/mail.ts +848 -0
- package/src/commands/merge.test.ts +864 -0
- package/src/commands/merge.ts +381 -0
- package/src/commands/metrics.test.ts +458 -0
- package/src/commands/metrics.ts +129 -0
- package/src/commands/monitor.test.ts +191 -0
- package/src/commands/monitor.ts +409 -0
- package/src/commands/nudge.test.ts +579 -0
- package/src/commands/nudge.ts +646 -0
- package/src/commands/orchestrator.ts +42 -0
- package/src/commands/prime.test.ts +612 -0
- package/src/commands/prime.ts +359 -0
- package/src/commands/replay.test.ts +757 -0
- package/src/commands/replay.ts +231 -0
- package/src/commands/run.test.ts +469 -0
- package/src/commands/run.ts +353 -0
- package/src/commands/serve/agent-actions.test.ts +210 -0
- package/src/commands/serve/agent-actions.ts +192 -0
- package/src/commands/serve/build.test.ts +202 -0
- package/src/commands/serve/build.ts +206 -0
- package/src/commands/serve/coordinator-actions.test.ts +339 -0
- package/src/commands/serve/coordinator-actions.ts +410 -0
- package/src/commands/serve/dev.test.ts +168 -0
- package/src/commands/serve/dev.ts +117 -0
- package/src/commands/serve/mail-actions.test.ts +312 -0
- package/src/commands/serve/mail-actions.ts +167 -0
- package/src/commands/serve/rest.test.ts +1680 -0
- package/src/commands/serve/rest.ts +1130 -0
- package/src/commands/serve/static.ts +51 -0
- package/src/commands/serve/ws.test.ts +361 -0
- package/src/commands/serve/ws.ts +332 -0
- package/src/commands/serve.test.ts +459 -0
- package/src/commands/serve.ts +654 -0
- package/src/commands/sling.test.ts +1583 -0
- package/src/commands/sling.ts +1351 -0
- package/src/commands/spec.test.ts +179 -0
- package/src/commands/spec.ts +105 -0
- package/src/commands/status.test.ts +614 -0
- package/src/commands/status.ts +403 -0
- package/src/commands/stop.test.ts +964 -0
- package/src/commands/stop.ts +319 -0
- package/src/commands/supervisor.test.ts +185 -0
- package/src/commands/supervisor.ts +537 -0
- package/src/commands/trace.test.ts +762 -0
- package/src/commands/trace.ts +205 -0
- package/src/commands/update.test.ts +466 -0
- package/src/commands/update.ts +263 -0
- package/src/commands/upgrade.test.ts +48 -0
- package/src/commands/upgrade.ts +240 -0
- package/src/commands/watch.test.ts +257 -0
- package/src/commands/watch.ts +308 -0
- package/src/commands/worktree.test.ts +1297 -0
- package/src/commands/worktree.ts +451 -0
- package/src/config.test.ts +1535 -0
- package/src/config.ts +1064 -0
- package/src/doctor/agents.test.ts +523 -0
- package/src/doctor/agents.ts +399 -0
- package/src/doctor/config-check.test.ts +191 -0
- package/src/doctor/config-check.ts +183 -0
- package/src/doctor/consistency.test.ts +807 -0
- package/src/doctor/consistency.ts +347 -0
- package/src/doctor/databases.test.ts +350 -0
- package/src/doctor/databases.ts +243 -0
- package/src/doctor/dependencies.test.ts +296 -0
- package/src/doctor/dependencies.ts +272 -0
- package/src/doctor/ecosystem.test.ts +308 -0
- package/src/doctor/ecosystem.ts +156 -0
- package/src/doctor/logs.test.ts +253 -0
- package/src/doctor/logs.ts +295 -0
- package/src/doctor/merge-queue.test.ts +315 -0
- package/src/doctor/merge-queue.ts +167 -0
- package/src/doctor/providers.test.ts +409 -0
- package/src/doctor/providers.ts +250 -0
- package/src/doctor/serve.test.ts +95 -0
- package/src/doctor/serve.ts +86 -0
- package/src/doctor/structure.test.ts +423 -0
- package/src/doctor/structure.ts +285 -0
- package/src/doctor/types.ts +43 -0
- package/src/doctor/version.test.ts +241 -0
- package/src/doctor/version.ts +132 -0
- package/src/doctor/watchdog.test.ts +167 -0
- package/src/doctor/watchdog.ts +214 -0
- package/src/e2e/init-sling-lifecycle.test.ts +283 -0
- package/src/errors.test.ts +350 -0
- package/src/errors.ts +217 -0
- package/src/events/store.test.ts +660 -0
- package/src/events/store.ts +369 -0
- package/src/events/tailer.test.ts +719 -0
- package/src/events/tailer.ts +332 -0
- package/src/events/tool-filter.test.ts +330 -0
- package/src/events/tool-filter.ts +126 -0
- package/src/index.ts +533 -0
- package/src/insights/analyzer.test.ts +466 -0
- package/src/insights/analyzer.ts +203 -0
- package/src/insights/quality-gates.test.ts +141 -0
- package/src/insights/quality-gates.ts +156 -0
- package/src/json.test.ts +72 -0
- package/src/json.ts +53 -0
- package/src/loam/client.test.ts +752 -0
- package/src/loam/client.ts +664 -0
- package/src/logging/color.test.ts +252 -0
- package/src/logging/color.ts +105 -0
- package/src/logging/format.test.ts +110 -0
- package/src/logging/format.ts +255 -0
- package/src/logging/logger.test.ts +814 -0
- package/src/logging/logger.ts +266 -0
- package/src/logging/reporter.test.ts +259 -0
- package/src/logging/reporter.ts +110 -0
- package/src/logging/sanitizer.test.ts +190 -0
- package/src/logging/sanitizer.ts +57 -0
- package/src/logging/theme.ts +140 -0
- package/src/mail/broadcast.test.ts +204 -0
- package/src/mail/broadcast.ts +92 -0
- package/src/mail/client.test.ts +774 -0
- package/src/mail/client.ts +236 -0
- package/src/mail/store.test.ts +898 -0
- package/src/mail/store.ts +425 -0
- package/src/merge/lock.test.ts +149 -0
- package/src/merge/lock.ts +140 -0
- package/src/merge/predict.test.ts +387 -0
- package/src/merge/predict.ts +249 -0
- package/src/merge/queue.test.ts +426 -0
- package/src/merge/queue.ts +246 -0
- package/src/merge/resolver.test.ts +1993 -0
- package/src/merge/resolver.ts +926 -0
- package/src/metrics/pricing.test.ts +258 -0
- package/src/metrics/pricing.ts +135 -0
- package/src/metrics/store.test.ts +978 -0
- package/src/metrics/store.ts +501 -0
- package/src/metrics/summary.test.ts +398 -0
- package/src/metrics/summary.ts +178 -0
- package/src/metrics/transcript.test.ts +483 -0
- package/src/metrics/transcript.ts +114 -0
- package/src/runtimes/__fixtures__/claude-stream-fixture.ts +22 -0
- package/src/runtimes/aider.test.ts +124 -0
- package/src/runtimes/aider.ts +147 -0
- package/src/runtimes/amp.test.ts +164 -0
- package/src/runtimes/amp.ts +154 -0
- package/src/runtimes/claude.test.ts +1474 -0
- package/src/runtimes/claude.ts +579 -0
- package/src/runtimes/codex.test.ts +805 -0
- package/src/runtimes/codex.ts +273 -0
- package/src/runtimes/connections.test.ts +214 -0
- package/src/runtimes/connections.ts +103 -0
- package/src/runtimes/copilot.test.ts +707 -0
- package/src/runtimes/copilot.ts +316 -0
- package/src/runtimes/cursor.test.ts +497 -0
- package/src/runtimes/cursor.ts +205 -0
- package/src/runtimes/gemini.test.ts +537 -0
- package/src/runtimes/gemini.ts +243 -0
- package/src/runtimes/goose.test.ts +133 -0
- package/src/runtimes/goose.ts +157 -0
- package/src/runtimes/headless-connection.test.ts +264 -0
- package/src/runtimes/headless-connection.ts +158 -0
- package/src/runtimes/opencode.test.ts +325 -0
- package/src/runtimes/opencode.ts +188 -0
- package/src/runtimes/pi-guards.test.ts +486 -0
- package/src/runtimes/pi-guards.ts +367 -0
- package/src/runtimes/pi.test.ts +789 -0
- package/src/runtimes/pi.ts +305 -0
- package/src/runtimes/registry.test.ts +196 -0
- package/src/runtimes/registry.ts +99 -0
- package/src/runtimes/sapling.test.ts +1267 -0
- package/src/runtimes/sapling.ts +710 -0
- package/src/runtimes/types.ts +266 -0
- package/src/schema-consistency.test.ts +246 -0
- package/src/sessions/compat.test.ts +281 -0
- package/src/sessions/compat.ts +105 -0
- package/src/sessions/store.test.ts +1748 -0
- package/src/sessions/store.ts +858 -0
- package/src/test-helpers.test.ts +124 -0
- package/src/test-helpers.ts +145 -0
- package/src/test-setup.test.ts +31 -0
- package/src/test-setup.ts +28 -0
- package/src/tools/loam/api.ts +368 -0
- package/src/tools/loam/cli.ts +278 -0
- package/src/tools/loam/commands/add.ts +52 -0
- package/src/tools/loam/commands/archive.ts +214 -0
- package/src/tools/loam/commands/audit.ts +276 -0
- package/src/tools/loam/commands/compact.ts +1062 -0
- package/src/tools/loam/commands/completions.ts +79 -0
- package/src/tools/loam/commands/config.ts +381 -0
- package/src/tools/loam/commands/delete-domain.ts +121 -0
- package/src/tools/loam/commands/delete.ts +316 -0
- package/src/tools/loam/commands/diff.ts +200 -0
- package/src/tools/loam/commands/doctor.ts +1113 -0
- package/src/tools/loam/commands/edit.ts +226 -0
- package/src/tools/loam/commands/init.ts +31 -0
- package/src/tools/loam/commands/learn.ts +179 -0
- package/src/tools/loam/commands/move.ts +323 -0
- package/src/tools/loam/commands/onboard.ts +374 -0
- package/src/tools/loam/commands/outcome.ts +185 -0
- package/src/tools/loam/commands/prime.ts +688 -0
- package/src/tools/loam/commands/prune.ts +614 -0
- package/src/tools/loam/commands/query.ts +218 -0
- package/src/tools/loam/commands/rank.ts +180 -0
- package/src/tools/loam/commands/ready.ts +189 -0
- package/src/tools/loam/commands/record.ts +1210 -0
- package/src/tools/loam/commands/restore.ts +166 -0
- package/src/tools/loam/commands/search.ts +327 -0
- package/src/tools/loam/commands/setup.ts +887 -0
- package/src/tools/loam/commands/status.ts +103 -0
- package/src/tools/loam/commands/sync.ts +298 -0
- package/src/tools/loam/commands/update.ts +19 -0
- package/src/tools/loam/commands/upgrade.ts +93 -0
- package/src/tools/loam/commands/validate.ts +190 -0
- package/src/tools/loam/index.ts +62 -0
- package/src/tools/loam/log.ts +127 -0
- package/src/tools/loam/registry/builtins.ts +409 -0
- package/src/tools/loam/registry/custom.ts +431 -0
- package/src/tools/loam/registry/init.ts +55 -0
- package/src/tools/loam/registry/template.ts +40 -0
- package/src/tools/loam/registry/type-registry.ts +113 -0
- package/src/tools/loam/schemas/config-schema.ts +489 -0
- package/src/tools/loam/schemas/config.ts +245 -0
- package/src/tools/loam/schemas/index.ts +18 -0
- package/src/tools/loam/schemas/record-schema.ts +191 -0
- package/src/tools/loam/schemas/record.ts +115 -0
- package/src/tools/loam/utils/active-work.ts +205 -0
- package/src/tools/loam/utils/anchor-validity.ts +80 -0
- package/src/tools/loam/utils/archive.ts +146 -0
- package/src/tools/loam/utils/audit.ts +667 -0
- package/src/tools/loam/utils/bm25.ts +238 -0
- package/src/tools/loam/utils/budget.ts +142 -0
- package/src/tools/loam/utils/config.ts +344 -0
- package/src/tools/loam/utils/dir-anchors.ts +62 -0
- package/src/tools/loam/utils/domain-rules.ts +114 -0
- package/src/tools/loam/utils/expertise.ts +393 -0
- package/src/tools/loam/utils/format-helpers.ts +96 -0
- package/src/tools/loam/utils/format.ts +1234 -0
- package/src/tools/loam/utils/git-context.ts +50 -0
- package/src/tools/loam/utils/git.ts +183 -0
- package/src/tools/loam/utils/hooks.ts +299 -0
- package/src/tools/loam/utils/index.ts +52 -0
- package/src/tools/loam/utils/json-output.ts +13 -0
- package/src/tools/loam/utils/lock.ts +76 -0
- package/src/tools/loam/utils/markers.ts +48 -0
- package/src/tools/loam/utils/numeric-flags.ts +20 -0
- package/src/tools/loam/utils/palette.ts +44 -0
- package/src/tools/loam/utils/prime-ranking.ts +135 -0
- package/src/tools/loam/utils/recipe-discovery.ts +195 -0
- package/src/tools/loam/utils/runtime-flags.ts +28 -0
- package/src/tools/loam/utils/scoring.ts +94 -0
- package/src/tools/loam/utils/version.ts +116 -0
- package/src/tools/sprout/commands/block.ts +64 -0
- package/src/tools/sprout/commands/blocked.ts +86 -0
- package/src/tools/sprout/commands/close.ts +129 -0
- package/src/tools/sprout/commands/completions.ts +198 -0
- package/src/tools/sprout/commands/config.ts +238 -0
- package/src/tools/sprout/commands/create.ts +164 -0
- package/src/tools/sprout/commands/dep.ts +148 -0
- package/src/tools/sprout/commands/doctor.ts +979 -0
- package/src/tools/sprout/commands/init.ts +83 -0
- package/src/tools/sprout/commands/label.ts +178 -0
- package/src/tools/sprout/commands/list.ts +210 -0
- package/src/tools/sprout/commands/migrate.ts +133 -0
- package/src/tools/sprout/commands/onboard.ts +207 -0
- package/src/tools/sprout/commands/plan-show.ts +278 -0
- package/src/tools/sprout/commands/plan.ts +2526 -0
- package/src/tools/sprout/commands/prime.ts +399 -0
- package/src/tools/sprout/commands/ready.ts +245 -0
- package/src/tools/sprout/commands/search.ts +221 -0
- package/src/tools/sprout/commands/show.ts +277 -0
- package/src/tools/sprout/commands/stats.ts +146 -0
- package/src/tools/sprout/commands/sync.ts +134 -0
- package/src/tools/sprout/commands/tpl.ts +364 -0
- package/src/tools/sprout/commands/unblock.ts +115 -0
- package/src/tools/sprout/commands/update.ts +257 -0
- package/src/tools/sprout/commands/upgrade.ts +91 -0
- package/src/tools/sprout/config-schema.ts +152 -0
- package/src/tools/sprout/config.ts +355 -0
- package/src/tools/sprout/filter.ts +107 -0
- package/src/tools/sprout/format.ts +43 -0
- package/src/tools/sprout/id.ts +22 -0
- package/src/tools/sprout/index.ts +204 -0
- package/src/tools/sprout/log.ts +76 -0
- package/src/tools/sprout/markers.ts +22 -0
- package/src/tools/sprout/output.ts +121 -0
- package/src/tools/sprout/plan-backref.ts +93 -0
- package/src/tools/sprout/plan-context.ts +81 -0
- package/src/tools/sprout/plan-domain.ts +139 -0
- package/src/tools/sprout/plan-lifecycle.ts +65 -0
- package/src/tools/sprout/plan-loam.ts +207 -0
- package/src/tools/sprout/plan-schema.ts +209 -0
- package/src/tools/sprout/sort.ts +31 -0
- package/src/tools/sprout/store.ts +172 -0
- package/src/tools/sprout/types.ts +118 -0
- package/src/tools/sprout/validation.ts +119 -0
- package/src/tools/sprout/version.ts +1 -0
- package/src/tools/sprout/yaml.ts +387 -0
- package/src/tools/trellis/commands/archive.ts +87 -0
- package/src/tools/trellis/commands/completions.ts +610 -0
- package/src/tools/trellis/commands/config.ts +382 -0
- package/src/tools/trellis/commands/create.ts +252 -0
- package/src/tools/trellis/commands/diff.ts +150 -0
- package/src/tools/trellis/commands/doctor.ts +771 -0
- package/src/tools/trellis/commands/emit.ts +365 -0
- package/src/tools/trellis/commands/history.ts +83 -0
- package/src/tools/trellis/commands/import.ts +198 -0
- package/src/tools/trellis/commands/init.ts +81 -0
- package/src/tools/trellis/commands/list.ts +103 -0
- package/src/tools/trellis/commands/onboard.ts +156 -0
- package/src/tools/trellis/commands/pin.ts +172 -0
- package/src/tools/trellis/commands/prime.ts +193 -0
- package/src/tools/trellis/commands/render.ts +122 -0
- package/src/tools/trellis/commands/schema.ts +353 -0
- package/src/tools/trellis/commands/show.ts +115 -0
- package/src/tools/trellis/commands/stats.ts +65 -0
- package/src/tools/trellis/commands/sync.ts +112 -0
- package/src/tools/trellis/commands/tree.ts +123 -0
- package/src/tools/trellis/commands/update.ts +330 -0
- package/src/tools/trellis/commands/upgrade.ts +95 -0
- package/src/tools/trellis/commands/validate.ts +166 -0
- package/src/tools/trellis/config-schema.ts +81 -0
- package/src/tools/trellis/config.ts +108 -0
- package/src/tools/trellis/frontmatter.ts +348 -0
- package/src/tools/trellis/id.ts +24 -0
- package/src/tools/trellis/index.ts +209 -0
- package/src/tools/trellis/markers.ts +28 -0
- package/src/tools/trellis/output.ts +84 -0
- package/src/tools/trellis/render.ts +212 -0
- package/src/tools/trellis/store.ts +144 -0
- package/src/tools/trellis/types.ts +82 -0
- package/src/tools/trellis/validate.ts +199 -0
- package/src/tools/trellis/yaml.ts +309 -0
- package/src/tracker/beads.test.ts +454 -0
- package/src/tracker/beads.ts +56 -0
- package/src/tracker/factory.test.ts +90 -0
- package/src/tracker/factory.ts +65 -0
- package/src/tracker/sprout.test.ts +461 -0
- package/src/tracker/sprout.ts +182 -0
- package/src/tracker/types.ts +52 -0
- package/src/trellis/client.test.ts +107 -0
- package/src/trellis/client.ts +179 -0
- package/src/types.ts +970 -0
- package/src/utils/bin.test.ts +10 -0
- package/src/utils/bin.ts +37 -0
- package/src/utils/browser.test.ts +49 -0
- package/src/utils/browser.ts +48 -0
- package/src/utils/fs.test.ts +119 -0
- package/src/utils/fs.ts +62 -0
- package/src/utils/pid.test.ts +152 -0
- package/src/utils/pid.ts +130 -0
- package/src/utils/process-scan.test.ts +53 -0
- package/src/utils/process-scan.ts +76 -0
- package/src/utils/time.test.ts +43 -0
- package/src/utils/time.ts +37 -0
- package/src/utils/version.test.ts +33 -0
- package/src/utils/version.ts +70 -0
- package/src/version.ts +5 -0
- package/src/watchdog/daemon.test.ts +3721 -0
- package/src/watchdog/daemon.ts +1257 -0
- package/src/watchdog/health.test.ts +830 -0
- package/src/watchdog/health.ts +434 -0
- package/src/watchdog/triage.test.ts +205 -0
- package/src/watchdog/triage.ts +205 -0
- package/src/worktree/manager.test.ts +720 -0
- package/src/worktree/manager.ts +405 -0
- package/src/worktree/process.test.ts +172 -0
- package/src/worktree/process.ts +131 -0
- package/src/worktree/tmux.test.ts +1616 -0
- package/src/worktree/tmux.ts +721 -0
- package/templates/CLAUDE.md.tmpl +100 -0
- package/templates/copilot-hooks.json.tmpl +13 -0
- package/templates/hooks.json.tmpl +109 -0
- package/templates/overlay.md.tmpl +88 -0
- package/ui/dist/apple-touch-icon-bdy6teep.png +0 -0
- package/ui/dist/chunk-8s31f05k.css +1 -0
- package/ui/dist/chunk-vm5rz679.js +300 -0
- package/ui/dist/favicon-nzb39vza.svg +4 -0
- package/ui/dist/index.html +17 -0
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sprout tracker adapter tests.
|
|
3
|
+
*
|
|
4
|
+
* Uses Bun.spawn mocks — legitimate exception to "never mock what you can use for real".
|
|
5
|
+
* The `sr` CLI may not be installed in all environments and would modify real tracker
|
|
6
|
+
* state (creating/closing actual issues) if invoked directly in tests.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
|
|
10
|
+
import { AgentError } from "../errors.ts";
|
|
11
|
+
import { createSproutTracker } from "./sprout.ts";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Helper to create a mock Bun.spawn return value.
|
|
15
|
+
*
|
|
16
|
+
* The actual code reads stdout/stderr via `new Response(proc.stdout).text()`
|
|
17
|
+
* and `new Response(proc.stderr).text()`, so we need ReadableStreams.
|
|
18
|
+
*/
|
|
19
|
+
function mockSpawnResult(
|
|
20
|
+
stdout: string,
|
|
21
|
+
stderr: string,
|
|
22
|
+
exitCode: number,
|
|
23
|
+
): {
|
|
24
|
+
stdout: ReadableStream<Uint8Array>;
|
|
25
|
+
stderr: ReadableStream<Uint8Array>;
|
|
26
|
+
exited: Promise<number>;
|
|
27
|
+
pid: number;
|
|
28
|
+
} {
|
|
29
|
+
return {
|
|
30
|
+
stdout: new Response(stdout).body as ReadableStream<Uint8Array>,
|
|
31
|
+
stderr: new Response(stderr).body as ReadableStream<Uint8Array>,
|
|
32
|
+
exited: Promise.resolve(exitCode),
|
|
33
|
+
pid: 12345,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const TEST_CWD = "/test/repo";
|
|
38
|
+
|
|
39
|
+
describe("createSproutTracker — ready()", () => {
|
|
40
|
+
let spawnSpy: ReturnType<typeof spyOn>;
|
|
41
|
+
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
spawnSpy = spyOn(Bun, "spawn");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
spawnSpy.mockRestore();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("parses envelope and returns normalized TrackerIssue[]", async () => {
|
|
51
|
+
const envelope = {
|
|
52
|
+
success: true,
|
|
53
|
+
command: "ready",
|
|
54
|
+
issues: [
|
|
55
|
+
{ id: "sr-1", title: "Fix bug", status: "open", priority: 1, type: "task" },
|
|
56
|
+
{
|
|
57
|
+
id: "sr-2",
|
|
58
|
+
title: "Add feature",
|
|
59
|
+
status: "open",
|
|
60
|
+
priority: 2,
|
|
61
|
+
type: "feature",
|
|
62
|
+
assignee: "alice",
|
|
63
|
+
blocks: ["sr-5"],
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
};
|
|
67
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(envelope), "", 0));
|
|
68
|
+
|
|
69
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
70
|
+
const issues = await tracker.ready();
|
|
71
|
+
|
|
72
|
+
expect(issues).toHaveLength(2);
|
|
73
|
+
expect(issues[0]).toMatchObject({ id: "sr-1", title: "Fix bug", type: "task" });
|
|
74
|
+
expect(issues[1]).toMatchObject({ id: "sr-2", assignee: "alice", blocks: ["sr-5"] });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("verifies CLI args: [sr, ready, --json]", async () => {
|
|
78
|
+
spawnSpy.mockImplementation(() =>
|
|
79
|
+
mockSpawnResult(JSON.stringify({ success: true, command: "ready", issues: [] }), "", 0),
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
83
|
+
await tracker.ready();
|
|
84
|
+
|
|
85
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
86
|
+
const cmd = callArgs[0] as string[];
|
|
87
|
+
expect(cmd).toEqual(["sr", "ready", "--json"]);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("throws AgentError on non-zero exit code", async () => {
|
|
91
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("", "sr: command failed", 1));
|
|
92
|
+
|
|
93
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
94
|
+
await expect(tracker.ready()).rejects.toThrow(AgentError);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("throws AgentError on envelope failure", async () => {
|
|
98
|
+
const envelope = { success: false, command: "ready", error: "no issues available" };
|
|
99
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(envelope), "", 0));
|
|
100
|
+
|
|
101
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
102
|
+
await expect(tracker.ready()).rejects.toThrow(AgentError);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("throws AgentError on empty output", async () => {
|
|
106
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("", "", 0));
|
|
107
|
+
|
|
108
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
109
|
+
await expect(tracker.ready()).rejects.toThrow(AgentError);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe("createSproutTracker — show()", () => {
|
|
114
|
+
let spawnSpy: ReturnType<typeof spyOn>;
|
|
115
|
+
|
|
116
|
+
beforeEach(() => {
|
|
117
|
+
spawnSpy = spyOn(Bun, "spawn");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
afterEach(() => {
|
|
121
|
+
spawnSpy.mockRestore();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("returns normalized TrackerIssue from envelope", async () => {
|
|
125
|
+
const envelope = {
|
|
126
|
+
success: true,
|
|
127
|
+
command: "show",
|
|
128
|
+
issue: {
|
|
129
|
+
id: "sr-42",
|
|
130
|
+
title: "My issue",
|
|
131
|
+
status: "open",
|
|
132
|
+
priority: 3,
|
|
133
|
+
type: "bug",
|
|
134
|
+
description: "A detailed bug report",
|
|
135
|
+
blockedBy: ["sr-10"],
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(envelope), "", 0));
|
|
139
|
+
|
|
140
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
141
|
+
const issue = await tracker.show("sr-42");
|
|
142
|
+
|
|
143
|
+
expect(issue).toMatchObject({
|
|
144
|
+
id: "sr-42",
|
|
145
|
+
title: "My issue",
|
|
146
|
+
type: "bug",
|
|
147
|
+
description: "A detailed bug report",
|
|
148
|
+
blockedBy: ["sr-10"],
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("verifies CLI args: [sr, show, <id>, --json]", async () => {
|
|
153
|
+
const envelope = {
|
|
154
|
+
success: true,
|
|
155
|
+
command: "show",
|
|
156
|
+
issue: { id: "sr-1", title: "t", status: "open", priority: 1, type: "task" },
|
|
157
|
+
};
|
|
158
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(envelope), "", 0));
|
|
159
|
+
|
|
160
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
161
|
+
await tracker.show("sr-1");
|
|
162
|
+
|
|
163
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
164
|
+
const cmd = callArgs[0] as string[];
|
|
165
|
+
expect(cmd).toEqual(["sr", "show", "sr-1", "--json"]);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("throws AgentError on non-zero exit code", async () => {
|
|
169
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("", "issue not found", 1));
|
|
170
|
+
|
|
171
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
172
|
+
await expect(tracker.show("sr-999")).rejects.toThrow(AgentError);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe("createSproutTracker — create()", () => {
|
|
177
|
+
let spawnSpy: ReturnType<typeof spyOn>;
|
|
178
|
+
|
|
179
|
+
beforeEach(() => {
|
|
180
|
+
spawnSpy = spyOn(Bun, "spawn");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
afterEach(() => {
|
|
184
|
+
spawnSpy.mockRestore();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("returns issue ID from envelope.id", async () => {
|
|
188
|
+
const envelope = { success: true, command: "create", id: "sr-100" };
|
|
189
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(envelope), "", 0));
|
|
190
|
+
|
|
191
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
192
|
+
const id = await tracker.create("New issue");
|
|
193
|
+
|
|
194
|
+
expect(id).toBe("sr-100");
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("returns issue ID from envelope.issue.id (alternate format)", async () => {
|
|
198
|
+
const envelope = { success: true, command: "create", issue: { id: "sr-200" } };
|
|
199
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(envelope), "", 0));
|
|
200
|
+
|
|
201
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
202
|
+
const id = await tracker.create("Another issue");
|
|
203
|
+
|
|
204
|
+
expect(id).toBe("sr-200");
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("passes optional --type, --priority, --description args", async () => {
|
|
208
|
+
const envelope = { success: true, command: "create", id: "sr-300" };
|
|
209
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(envelope), "", 0));
|
|
210
|
+
|
|
211
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
212
|
+
await tracker.create("My task", {
|
|
213
|
+
type: "feature",
|
|
214
|
+
priority: 2,
|
|
215
|
+
description: "A detailed description",
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
219
|
+
const cmd = callArgs[0] as string[];
|
|
220
|
+
expect(cmd).toContain("--type");
|
|
221
|
+
expect(cmd).toContain("feature");
|
|
222
|
+
expect(cmd).toContain("--priority");
|
|
223
|
+
expect(cmd).toContain("2");
|
|
224
|
+
expect(cmd).toContain("--description");
|
|
225
|
+
expect(cmd).toContain("A detailed description");
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("throws AgentError when no ID returned in envelope", async () => {
|
|
229
|
+
const envelope = { success: true, command: "create" };
|
|
230
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(envelope), "", 0));
|
|
231
|
+
|
|
232
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
233
|
+
await expect(tracker.create("No ID")).rejects.toThrow(AgentError);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("throws AgentError on envelope failure", async () => {
|
|
237
|
+
const envelope = { success: false, command: "create", error: "validation failed" };
|
|
238
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(envelope), "", 0));
|
|
239
|
+
|
|
240
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
241
|
+
await expect(tracker.create("Bad issue")).rejects.toThrow(AgentError);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
describe("createSproutTracker — claim()", () => {
|
|
246
|
+
let spawnSpy: ReturnType<typeof spyOn>;
|
|
247
|
+
|
|
248
|
+
beforeEach(() => {
|
|
249
|
+
spawnSpy = spyOn(Bun, "spawn");
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
afterEach(() => {
|
|
253
|
+
spawnSpy.mockRestore();
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("calls [sr, update, <id>, --status, in_progress]", async () => {
|
|
257
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("", "", 0));
|
|
258
|
+
|
|
259
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
260
|
+
await tracker.claim("sr-5");
|
|
261
|
+
|
|
262
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
263
|
+
const cmd = callArgs[0] as string[];
|
|
264
|
+
expect(cmd).toEqual(["sr", "update", "sr-5", "--status", "in_progress"]);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test("throws AgentError on failure", async () => {
|
|
268
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("", "issue already claimed", 1));
|
|
269
|
+
|
|
270
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
271
|
+
await expect(tracker.claim("sr-5")).rejects.toThrow(AgentError);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
describe("createSproutTracker — close()", () => {
|
|
276
|
+
let spawnSpy: ReturnType<typeof spyOn>;
|
|
277
|
+
|
|
278
|
+
beforeEach(() => {
|
|
279
|
+
spawnSpy = spyOn(Bun, "spawn");
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
afterEach(() => {
|
|
283
|
+
spawnSpy.mockRestore();
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
test("calls [sr, close, <id>] without reason", async () => {
|
|
287
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("", "", 0));
|
|
288
|
+
|
|
289
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
290
|
+
await tracker.close("sr-10");
|
|
291
|
+
|
|
292
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
293
|
+
const cmd = callArgs[0] as string[];
|
|
294
|
+
expect(cmd).toEqual(["sr", "close", "sr-10"]);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
test("calls [sr, close, <id>, --reason, ...] with reason", async () => {
|
|
298
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("", "", 0));
|
|
299
|
+
|
|
300
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
301
|
+
await tracker.close("sr-10", "Done implementing");
|
|
302
|
+
|
|
303
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
304
|
+
const cmd = callArgs[0] as string[];
|
|
305
|
+
expect(cmd).toEqual(["sr", "close", "sr-10", "--reason", "Done implementing"]);
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
describe("createSproutTracker — list()", () => {
|
|
310
|
+
let spawnSpy: ReturnType<typeof spyOn>;
|
|
311
|
+
|
|
312
|
+
beforeEach(() => {
|
|
313
|
+
spawnSpy = spyOn(Bun, "spawn");
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
afterEach(() => {
|
|
317
|
+
spawnSpy.mockRestore();
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test("returns normalized issues from envelope", async () => {
|
|
321
|
+
const envelope = {
|
|
322
|
+
success: true,
|
|
323
|
+
command: "list",
|
|
324
|
+
issues: [
|
|
325
|
+
{ id: "sr-1", title: "Issue A", status: "open", priority: 1, type: "task" },
|
|
326
|
+
{ id: "sr-2", title: "Issue B", status: "in_progress", priority: 2, type: "bug" },
|
|
327
|
+
],
|
|
328
|
+
};
|
|
329
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(envelope), "", 0));
|
|
330
|
+
|
|
331
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
332
|
+
const issues = await tracker.list();
|
|
333
|
+
|
|
334
|
+
expect(issues).toHaveLength(2);
|
|
335
|
+
expect(issues[0]).toMatchObject({ id: "sr-1", status: "open" });
|
|
336
|
+
expect(issues[1]).toMatchObject({ id: "sr-2", status: "in_progress" });
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test("verifies CLI args: [sr, list, --json]", async () => {
|
|
340
|
+
spawnSpy.mockImplementation(() =>
|
|
341
|
+
mockSpawnResult(JSON.stringify({ success: true, command: "list", issues: [] }), "", 0),
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
345
|
+
await tracker.list();
|
|
346
|
+
|
|
347
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
348
|
+
const cmd = callArgs[0] as string[];
|
|
349
|
+
expect(cmd[0]).toBe("sr");
|
|
350
|
+
expect(cmd[1]).toBe("list");
|
|
351
|
+
expect(cmd).toContain("--json");
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
test("passes --status and --limit options", async () => {
|
|
355
|
+
const envelope = { success: true, command: "list", issues: [] };
|
|
356
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(envelope), "", 0));
|
|
357
|
+
|
|
358
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
359
|
+
await tracker.list({ status: "open", limit: 10 });
|
|
360
|
+
|
|
361
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
362
|
+
const cmd = callArgs[0] as string[];
|
|
363
|
+
expect(cmd).toContain("--status");
|
|
364
|
+
expect(cmd).toContain("open");
|
|
365
|
+
expect(cmd).toContain("--limit");
|
|
366
|
+
expect(cmd).toContain("10");
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
describe("createSproutTracker — sync()", () => {
|
|
371
|
+
let spawnSpy: ReturnType<typeof spyOn>;
|
|
372
|
+
|
|
373
|
+
beforeEach(() => {
|
|
374
|
+
spawnSpy = spyOn(Bun, "spawn");
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
afterEach(() => {
|
|
378
|
+
spawnSpy.mockRestore();
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
test("calls [sr, sync]", async () => {
|
|
382
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("", "", 0));
|
|
383
|
+
|
|
384
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
385
|
+
await tracker.sync();
|
|
386
|
+
|
|
387
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
388
|
+
const cmd = callArgs[0] as string[];
|
|
389
|
+
expect(cmd).toEqual(["sr", "sync"]);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
test("throws AgentError on failure", async () => {
|
|
393
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("", "sync failed: dirty working tree", 1));
|
|
394
|
+
|
|
395
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
396
|
+
await expect(tracker.sync()).rejects.toThrow(AgentError);
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
describe("createSproutTracker — edge cases", () => {
|
|
401
|
+
let spawnSpy: ReturnType<typeof spyOn>;
|
|
402
|
+
|
|
403
|
+
beforeEach(() => {
|
|
404
|
+
spawnSpy = spyOn(Bun, "spawn");
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
afterEach(() => {
|
|
408
|
+
spawnSpy.mockRestore();
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
test("strips non-JSON prefix lines before parsing", async () => {
|
|
412
|
+
const envelope = {
|
|
413
|
+
success: true,
|
|
414
|
+
command: "ready",
|
|
415
|
+
issues: [{ id: "sr-1", title: "Test", status: "open", priority: 1, type: "task" }],
|
|
416
|
+
};
|
|
417
|
+
const output = `Syncing with remote...\nDone.\n${JSON.stringify(envelope)}`;
|
|
418
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(output, "", 0));
|
|
419
|
+
|
|
420
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
421
|
+
const issues = await tracker.ready();
|
|
422
|
+
|
|
423
|
+
expect(issues).toHaveLength(1);
|
|
424
|
+
expect(issues[0]).toMatchObject({ id: "sr-1" });
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
test("throws AgentError on invalid JSON", async () => {
|
|
428
|
+
spawnSpy.mockImplementation(() => mockSpawnResult("{not: valid json}", "", 0));
|
|
429
|
+
|
|
430
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
431
|
+
await expect(tracker.ready()).rejects.toThrow(AgentError);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
test("handles envelope with missing error field (defaults to 'unknown error')", async () => {
|
|
435
|
+
const envelope = { success: false, command: "ready" }; // No error field
|
|
436
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(envelope), "", 0));
|
|
437
|
+
|
|
438
|
+
const tracker = createSproutTracker(TEST_CWD);
|
|
439
|
+
try {
|
|
440
|
+
await tracker.ready();
|
|
441
|
+
expect(true).toBe(false); // Should have thrown
|
|
442
|
+
} catch (err: unknown) {
|
|
443
|
+
expect(err).toBeInstanceOf(AgentError);
|
|
444
|
+
const agentErr = err as AgentError;
|
|
445
|
+
expect(agentErr.message).toContain("unknown error");
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
test("propagates cwd to Bun.spawn", async () => {
|
|
450
|
+
const envelope = { success: true, command: "ready", issues: [] };
|
|
451
|
+
spawnSpy.mockImplementation(() => mockSpawnResult(JSON.stringify(envelope), "", 0));
|
|
452
|
+
|
|
453
|
+
const customCwd = "/my/custom/project";
|
|
454
|
+
const tracker = createSproutTracker(customCwd);
|
|
455
|
+
await tracker.ready();
|
|
456
|
+
|
|
457
|
+
const callArgs = spawnSpy.mock.calls[0] as unknown[];
|
|
458
|
+
const opts = callArgs[1] as { cwd: string };
|
|
459
|
+
expect(opts.cwd).toBe(customCwd);
|
|
460
|
+
});
|
|
461
|
+
});
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sprout tracker adapter.
|
|
3
|
+
*
|
|
4
|
+
* Implements the unified TrackerClient interface by calling the `sr` CLI directly
|
|
5
|
+
* via Bun.spawn. Sprout uses a { success, command, ...data } JSON envelope.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { AgentError } from "../errors.ts";
|
|
9
|
+
import type { TrackerClient, TrackerIssue } from "./types.ts";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Run an sr command and return its output.
|
|
13
|
+
*/
|
|
14
|
+
async function runSd(
|
|
15
|
+
args: string[],
|
|
16
|
+
cwd: string,
|
|
17
|
+
context: string,
|
|
18
|
+
): Promise<{ stdout: string; stderr: string }> {
|
|
19
|
+
const proc = Bun.spawn(["sr", ...args], { cwd, stdout: "pipe", stderr: "pipe" });
|
|
20
|
+
const stdout = await new Response(proc.stdout).text();
|
|
21
|
+
const stderr = await new Response(proc.stderr).text();
|
|
22
|
+
const exitCode = await proc.exited;
|
|
23
|
+
if (exitCode !== 0) {
|
|
24
|
+
throw new AgentError(`sr ${context} failed (exit ${exitCode}): ${stderr.trim()}`);
|
|
25
|
+
}
|
|
26
|
+
return { stdout, stderr };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Parse JSON from sr output, stripping any non-JSON prefix lines.
|
|
31
|
+
*/
|
|
32
|
+
function parseSdJson<T>(stdout: string, context: string): T {
|
|
33
|
+
const trimmed = stdout.trim();
|
|
34
|
+
if (trimmed === "") {
|
|
35
|
+
throw new AgentError(`Empty output from sr ${context}`);
|
|
36
|
+
}
|
|
37
|
+
// Sprout may emit non-JSON lines before the JSON object; find the first '{' or '['
|
|
38
|
+
const jsonStart = trimmed.search(/[{[]/);
|
|
39
|
+
const jsonStr = jsonStart >= 0 ? trimmed.slice(jsonStart) : trimmed;
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(jsonStr) as T;
|
|
42
|
+
} catch {
|
|
43
|
+
throw new AgentError(
|
|
44
|
+
`Failed to parse JSON output from sr ${context}: ${trimmed.slice(0, 200)}`,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Base envelope shape shared by all sr JSON responses. */
|
|
50
|
+
interface SdEnvelopeBase {
|
|
51
|
+
success: boolean;
|
|
52
|
+
command: string;
|
|
53
|
+
error?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Sprout JSON envelope for list-style responses. */
|
|
57
|
+
interface SdListEnvelope extends SdEnvelopeBase {
|
|
58
|
+
issues: SdRawIssue[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Sprout JSON envelope for single-issue responses. */
|
|
62
|
+
interface SdShowEnvelope extends SdEnvelopeBase {
|
|
63
|
+
issue: SdRawIssue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Sprout JSON envelope for create responses. */
|
|
67
|
+
interface SdCreateEnvelope extends SdEnvelopeBase {
|
|
68
|
+
id?: string;
|
|
69
|
+
issue?: { id: string };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Validate that an sr envelope indicates success.
|
|
74
|
+
* Throws AgentError if the envelope reports failure.
|
|
75
|
+
*/
|
|
76
|
+
function assertEnvelopeSuccess(envelope: SdEnvelopeBase, context: string): void {
|
|
77
|
+
if (envelope.success === false) {
|
|
78
|
+
const detail = envelope.error ?? "unknown error";
|
|
79
|
+
throw new AgentError(`sr ${context} returned failure: ${detail}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Raw issue shape from the sr CLI. Sprout uses `type` directly (no issue_type mapping). */
|
|
84
|
+
interface SdRawIssue {
|
|
85
|
+
id: string;
|
|
86
|
+
title: string;
|
|
87
|
+
status: string;
|
|
88
|
+
priority: number;
|
|
89
|
+
type: string;
|
|
90
|
+
assignee?: string;
|
|
91
|
+
description?: string;
|
|
92
|
+
blocks?: string[];
|
|
93
|
+
blockedBy?: string[];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function normalizeIssue(raw: SdRawIssue): TrackerIssue {
|
|
97
|
+
return {
|
|
98
|
+
id: raw.id,
|
|
99
|
+
title: raw.title,
|
|
100
|
+
status: raw.status,
|
|
101
|
+
priority: raw.priority,
|
|
102
|
+
type: raw.type ?? "unknown",
|
|
103
|
+
assignee: raw.assignee,
|
|
104
|
+
description: raw.description,
|
|
105
|
+
blocks: raw.blocks,
|
|
106
|
+
blockedBy: raw.blockedBy,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Create a TrackerClient backed by the sprout (sr) CLI.
|
|
112
|
+
*
|
|
113
|
+
* @param cwd - Working directory for sr commands
|
|
114
|
+
*/
|
|
115
|
+
export function createSproutTracker(cwd: string): TrackerClient {
|
|
116
|
+
return {
|
|
117
|
+
async ready() {
|
|
118
|
+
const { stdout } = await runSd(["ready", "--json"], cwd, "ready");
|
|
119
|
+
const envelope = parseSdJson<SdListEnvelope>(stdout, "ready");
|
|
120
|
+
assertEnvelopeSuccess(envelope, "ready");
|
|
121
|
+
return envelope.issues.map(normalizeIssue);
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
async show(id) {
|
|
125
|
+
const { stdout } = await runSd(["show", id, "--json"], cwd, `show ${id}`);
|
|
126
|
+
const envelope = parseSdJson<SdShowEnvelope>(stdout, `show ${id}`);
|
|
127
|
+
assertEnvelopeSuccess(envelope, `show ${id}`);
|
|
128
|
+
return normalizeIssue(envelope.issue);
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
async create(title, options) {
|
|
132
|
+
const args = ["create", "--title", title, "--json"];
|
|
133
|
+
if (options?.type) {
|
|
134
|
+
args.push("--type", options.type);
|
|
135
|
+
}
|
|
136
|
+
if (options?.priority !== undefined) {
|
|
137
|
+
args.push("--priority", String(options.priority));
|
|
138
|
+
}
|
|
139
|
+
if (options?.description) {
|
|
140
|
+
args.push("--description", options.description);
|
|
141
|
+
}
|
|
142
|
+
const { stdout } = await runSd(args, cwd, "create");
|
|
143
|
+
const envelope = parseSdJson<SdCreateEnvelope>(stdout, "create");
|
|
144
|
+
assertEnvelopeSuccess(envelope, "create");
|
|
145
|
+
const id = envelope.id ?? envelope.issue?.id;
|
|
146
|
+
if (!id) {
|
|
147
|
+
throw new AgentError("sr create did not return an issue ID");
|
|
148
|
+
}
|
|
149
|
+
return id;
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
async claim(id) {
|
|
153
|
+
await runSd(["update", id, "--status", "in_progress"], cwd, `claim ${id}`);
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
async close(id, reason) {
|
|
157
|
+
const args = ["close", id];
|
|
158
|
+
if (reason) {
|
|
159
|
+
args.push("--reason", reason);
|
|
160
|
+
}
|
|
161
|
+
await runSd(args, cwd, `close ${id}`);
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
async list(options) {
|
|
165
|
+
const args = ["list", "--json"];
|
|
166
|
+
if (options?.status) {
|
|
167
|
+
args.push("--status", options.status);
|
|
168
|
+
}
|
|
169
|
+
if (options?.limit !== undefined) {
|
|
170
|
+
args.push("--limit", String(options.limit));
|
|
171
|
+
}
|
|
172
|
+
const { stdout } = await runSd(args, cwd, "list");
|
|
173
|
+
const envelope = parseSdJson<SdListEnvelope>(stdout, "list");
|
|
174
|
+
assertEnvelopeSuccess(envelope, "list");
|
|
175
|
+
return envelope.issues.map(normalizeIssue);
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
async sync() {
|
|
179
|
+
await runSd(["sync"], cwd, "sync");
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified tracker types — shared across beads and sprout backends.
|
|
3
|
+
* This module is self-contained and does NOT import from src/types.ts.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A tracker issue — unified across beads and sprout backends.
|
|
8
|
+
*/
|
|
9
|
+
export interface TrackerIssue {
|
|
10
|
+
id: string;
|
|
11
|
+
title: string;
|
|
12
|
+
status: string;
|
|
13
|
+
priority: number;
|
|
14
|
+
type: string;
|
|
15
|
+
assignee?: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
blocks?: string[];
|
|
18
|
+
blockedBy?: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Unified tracker client interface.
|
|
23
|
+
* Both beads and sprout backends implement this.
|
|
24
|
+
*/
|
|
25
|
+
export interface TrackerClient {
|
|
26
|
+
/** List issues that are ready for work (open, unblocked). */
|
|
27
|
+
ready(): Promise<TrackerIssue[]>;
|
|
28
|
+
|
|
29
|
+
/** Show details for a specific issue. */
|
|
30
|
+
show(id: string): Promise<TrackerIssue>;
|
|
31
|
+
|
|
32
|
+
/** Create a new issue. Returns the new issue ID. */
|
|
33
|
+
create(
|
|
34
|
+
title: string,
|
|
35
|
+
options?: { type?: string; priority?: number; description?: string },
|
|
36
|
+
): Promise<string>;
|
|
37
|
+
|
|
38
|
+
/** Claim an issue (mark as in_progress). */
|
|
39
|
+
claim(id: string): Promise<void>;
|
|
40
|
+
|
|
41
|
+
/** Close an issue with an optional reason. */
|
|
42
|
+
close(id: string, reason?: string): Promise<void>;
|
|
43
|
+
|
|
44
|
+
/** List issues with optional filters. */
|
|
45
|
+
list(options?: { status?: string; limit?: number }): Promise<TrackerIssue[]>;
|
|
46
|
+
|
|
47
|
+
/** Sync tracker state with git (if supported). */
|
|
48
|
+
sync(): Promise<void>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Which tracker backend to use. */
|
|
52
|
+
export type TrackerBackend = "beads" | "sprout";
|