@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,134 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import { findSproutDir, isInsideWorktree, projectRootFromSproutDir } from "../config.ts";
|
|
3
|
+
import { outputJson, printWarning } from "../output.ts";
|
|
4
|
+
import { SPROUT_DIR_NAME } from "../types.ts";
|
|
5
|
+
|
|
6
|
+
function spawnSync(
|
|
7
|
+
cmd: string[],
|
|
8
|
+
cwd: string,
|
|
9
|
+
): { stdout: string; stderr: string; exitCode: number } {
|
|
10
|
+
const result = Bun.spawnSync(cmd, { cwd, stdout: "pipe", stderr: "pipe" });
|
|
11
|
+
const stdout = new TextDecoder().decode(result.stdout);
|
|
12
|
+
const stderr = new TextDecoder().decode(result.stderr);
|
|
13
|
+
return { stdout, stderr, exitCode: result.exitCode ?? 0 };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function run(args: string[], sproutDir?: string): Promise<void> {
|
|
17
|
+
const jsonMode = args.includes("--json");
|
|
18
|
+
const statusOnly = args.includes("--status");
|
|
19
|
+
const dryRun = args.includes("--dry-run");
|
|
20
|
+
|
|
21
|
+
const dir = sproutDir ?? (await findSproutDir());
|
|
22
|
+
const projectRoot = projectRootFromSproutDir(dir);
|
|
23
|
+
|
|
24
|
+
// Worktree guard: skip commit when running from a worktree
|
|
25
|
+
if (!sproutDir && isInsideWorktree(process.cwd())) {
|
|
26
|
+
const msg = "Inside a git worktree — skipping commit. Issues are stored in the main repo.";
|
|
27
|
+
if (jsonMode) {
|
|
28
|
+
await outputJson({
|
|
29
|
+
success: true,
|
|
30
|
+
command: "sync",
|
|
31
|
+
committed: false,
|
|
32
|
+
worktree: true,
|
|
33
|
+
message: msg,
|
|
34
|
+
});
|
|
35
|
+
} else {
|
|
36
|
+
printWarning(msg);
|
|
37
|
+
}
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const statusResult = spawnSync(
|
|
42
|
+
["git", "-C", projectRoot, "status", "--porcelain", `${SPROUT_DIR_NAME}/`],
|
|
43
|
+
projectRoot,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const changed = statusResult.stdout.trim();
|
|
47
|
+
|
|
48
|
+
if (statusOnly) {
|
|
49
|
+
if (jsonMode) {
|
|
50
|
+
await outputJson({ success: true, command: "sync", hasChanges: !!changed, changes: changed });
|
|
51
|
+
} else {
|
|
52
|
+
if (changed) {
|
|
53
|
+
console.log("Uncommitted .sprout/ changes:");
|
|
54
|
+
console.log(changed);
|
|
55
|
+
} else {
|
|
56
|
+
console.log("No uncommitted .sprout/ changes.");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!changed) {
|
|
63
|
+
if (jsonMode) {
|
|
64
|
+
await outputJson({
|
|
65
|
+
success: true,
|
|
66
|
+
command: "sync",
|
|
67
|
+
committed: false,
|
|
68
|
+
message: "Nothing to commit",
|
|
69
|
+
});
|
|
70
|
+
} else {
|
|
71
|
+
console.log("No changes to commit.");
|
|
72
|
+
}
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (dryRun) {
|
|
77
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
78
|
+
const msg = `sprout: sync ${date}`;
|
|
79
|
+
if (jsonMode) {
|
|
80
|
+
await outputJson({
|
|
81
|
+
success: true,
|
|
82
|
+
command: "sync",
|
|
83
|
+
dryRun: true,
|
|
84
|
+
wouldCommit: true,
|
|
85
|
+
message: msg,
|
|
86
|
+
changes: changed,
|
|
87
|
+
});
|
|
88
|
+
} else {
|
|
89
|
+
console.log("Dry run — would commit:");
|
|
90
|
+
console.log(changed);
|
|
91
|
+
console.log(`Commit message: ${msg}`);
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Stage
|
|
97
|
+
const addResult = spawnSync(
|
|
98
|
+
["git", "-C", projectRoot, "add", `${SPROUT_DIR_NAME}/`],
|
|
99
|
+
projectRoot,
|
|
100
|
+
);
|
|
101
|
+
if (addResult.exitCode !== 0) {
|
|
102
|
+
throw new Error(`git add failed: ${addResult.stderr}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Commit
|
|
106
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
107
|
+
const msg = `sprout: sync ${date}`;
|
|
108
|
+
const commitResult = spawnSync(["git", "-C", projectRoot, "commit", "-m", msg], projectRoot);
|
|
109
|
+
if (commitResult.exitCode !== 0) {
|
|
110
|
+
throw new Error(`git commit failed: ${commitResult.stderr}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (jsonMode) {
|
|
114
|
+
await outputJson({ success: true, command: "sync", committed: true, message: msg });
|
|
115
|
+
} else {
|
|
116
|
+
console.log(`Committed: ${msg}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function register(program: Command): void {
|
|
121
|
+
program
|
|
122
|
+
.command("sync")
|
|
123
|
+
.description("Stage and commit .sprout/ changes")
|
|
124
|
+
.option("--status", "Check status without committing")
|
|
125
|
+
.option("--dry-run", "Show what would be committed without committing")
|
|
126
|
+
.option("--json", "Output as JSON")
|
|
127
|
+
.action(async (opts: { status?: boolean; dryRun?: boolean; json?: boolean }) => {
|
|
128
|
+
const args: string[] = [];
|
|
129
|
+
if (opts.status) args.push("--status");
|
|
130
|
+
if (opts.dryRun) args.push("--dry-run");
|
|
131
|
+
if (opts.json) args.push("--json");
|
|
132
|
+
await run(args);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { findSproutDir, readConfig } from "../config.ts";
|
|
4
|
+
import { generateId } from "../id.ts";
|
|
5
|
+
import { accent, muted, outputJson, printIssueOneLine, printSuccess } from "../output.ts";
|
|
6
|
+
import {
|
|
7
|
+
appendTemplate,
|
|
8
|
+
issuesPath,
|
|
9
|
+
readIssues,
|
|
10
|
+
readTemplates,
|
|
11
|
+
templatesPath,
|
|
12
|
+
withLock,
|
|
13
|
+
writeIssues,
|
|
14
|
+
writeTemplates,
|
|
15
|
+
} from "../store.ts";
|
|
16
|
+
import type { Issue, Template, TemplateStep } from "../types.ts";
|
|
17
|
+
import { VALID_TYPES } from "../types.ts";
|
|
18
|
+
|
|
19
|
+
function parseArgs(args: string[]) {
|
|
20
|
+
const flags: Record<string, string | boolean> = {};
|
|
21
|
+
const positional: string[] = [];
|
|
22
|
+
let i = 0;
|
|
23
|
+
while (i < args.length) {
|
|
24
|
+
const arg = args[i];
|
|
25
|
+
if (!arg) {
|
|
26
|
+
i++;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (arg.startsWith("--")) {
|
|
30
|
+
const key = arg.slice(2);
|
|
31
|
+
const eqIdx = key.indexOf("=");
|
|
32
|
+
if (eqIdx !== -1) {
|
|
33
|
+
flags[key.slice(0, eqIdx)] = key.slice(eqIdx + 1);
|
|
34
|
+
i++;
|
|
35
|
+
} else {
|
|
36
|
+
const next = args[i + 1];
|
|
37
|
+
if (next !== undefined && !next.startsWith("--")) {
|
|
38
|
+
flags[key] = next;
|
|
39
|
+
i += 2;
|
|
40
|
+
} else {
|
|
41
|
+
flags[key] = true;
|
|
42
|
+
i++;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
positional.push(arg);
|
|
47
|
+
i++;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return { flags, positional };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function run(args: string[], sproutDir?: string): Promise<void> {
|
|
54
|
+
const jsonMode = args.includes("--json");
|
|
55
|
+
const { flags, positional } = parseArgs(args);
|
|
56
|
+
const subcmd = positional[0];
|
|
57
|
+
|
|
58
|
+
if (!subcmd) throw new Error("Usage: sr tpl <create|step|list|show|pour|status>");
|
|
59
|
+
|
|
60
|
+
const dir = sproutDir ?? (await findSproutDir());
|
|
61
|
+
|
|
62
|
+
// sr tpl create --name "..."
|
|
63
|
+
if (subcmd === "create") {
|
|
64
|
+
const name = flags.name;
|
|
65
|
+
if (!name || typeof name !== "string") throw new Error("--name is required");
|
|
66
|
+
|
|
67
|
+
let createdId = "";
|
|
68
|
+
await withLock(templatesPath(dir), async () => {
|
|
69
|
+
const existing = await readTemplates(dir);
|
|
70
|
+
const existingIds = new Set(existing.map((t) => t.id));
|
|
71
|
+
const id = generateId("tpl", existingIds);
|
|
72
|
+
const template: Template = { id, name, steps: [] };
|
|
73
|
+
await appendTemplate(dir, template);
|
|
74
|
+
createdId = id;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (jsonMode) {
|
|
78
|
+
await outputJson({ success: true, command: "tpl create", id: createdId });
|
|
79
|
+
} else {
|
|
80
|
+
printSuccess(`Created template ${createdId}: ${name}`);
|
|
81
|
+
}
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// sr tpl step add <template-id> --title "..." [--type task] [--priority 2]
|
|
86
|
+
if (subcmd === "step") {
|
|
87
|
+
const stepSubcmd = positional[1];
|
|
88
|
+
if (stepSubcmd !== "add") throw new Error("Usage: sr tpl step add <id> --title ...");
|
|
89
|
+
const templateId = positional[2];
|
|
90
|
+
if (!templateId) throw new Error("Template ID is required");
|
|
91
|
+
const title = flags.title;
|
|
92
|
+
if (!title || typeof title !== "string") throw new Error("--title is required");
|
|
93
|
+
|
|
94
|
+
const typeVal = typeof flags.type === "string" ? flags.type : "task";
|
|
95
|
+
if (!(VALID_TYPES as readonly string[]).includes(typeVal)) {
|
|
96
|
+
throw new Error(`--type must be one of: ${VALID_TYPES.join(", ")}`);
|
|
97
|
+
}
|
|
98
|
+
const priorityStr = typeof flags.priority === "string" ? flags.priority : "2";
|
|
99
|
+
const priority = Number.parseInt(priorityStr, 10);
|
|
100
|
+
|
|
101
|
+
let stepCount = 0;
|
|
102
|
+
await withLock(templatesPath(dir), async () => {
|
|
103
|
+
const templates = await readTemplates(dir);
|
|
104
|
+
const idx = templates.findIndex((t) => t.id === templateId);
|
|
105
|
+
const tpl = templates[idx];
|
|
106
|
+
if (!tpl) throw new Error(`Template not found: ${templateId}`);
|
|
107
|
+
const step: TemplateStep = { title, type: typeVal, priority };
|
|
108
|
+
const updated: Template = { ...tpl, steps: [...tpl.steps, step] };
|
|
109
|
+
templates[idx] = updated;
|
|
110
|
+
stepCount = updated.steps.length;
|
|
111
|
+
await writeTemplates(dir, templates);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (jsonMode) {
|
|
115
|
+
await outputJson({ success: true, command: "tpl step add", id: templateId, stepCount });
|
|
116
|
+
} else {
|
|
117
|
+
printSuccess(`Added step ${stepCount} to ${templateId}: "${title}"`);
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// sr tpl list
|
|
123
|
+
if (subcmd === "list") {
|
|
124
|
+
const templates = await readTemplates(dir);
|
|
125
|
+
if (jsonMode) {
|
|
126
|
+
await outputJson({ success: true, command: "tpl list", templates, count: templates.length });
|
|
127
|
+
} else {
|
|
128
|
+
if (templates.length === 0) {
|
|
129
|
+
console.log("No templates.");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
for (const tpl of templates) {
|
|
133
|
+
console.log(`${accent.bold(tpl.id)} ${tpl.name} ${muted(`(${tpl.steps.length} steps)`)}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// sr tpl show <id>
|
|
140
|
+
if (subcmd === "show") {
|
|
141
|
+
const templateId = positional[1];
|
|
142
|
+
if (!templateId) throw new Error("Usage: sr tpl show <id>");
|
|
143
|
+
const templates = await readTemplates(dir);
|
|
144
|
+
const tpl = templates.find((t) => t.id === templateId);
|
|
145
|
+
if (!tpl) throw new Error(`Template not found: ${templateId}`);
|
|
146
|
+
|
|
147
|
+
if (jsonMode) {
|
|
148
|
+
await outputJson({ success: true, command: "tpl show", template: tpl });
|
|
149
|
+
} else {
|
|
150
|
+
console.log(`${accent.bold(tpl.id)} ${tpl.name}`);
|
|
151
|
+
console.log(muted(`Steps (${tpl.steps.length}):`));
|
|
152
|
+
tpl.steps.forEach((step, i) => {
|
|
153
|
+
console.log(
|
|
154
|
+
` ${i + 1}. ${step.title} ${muted(`[${step.type ?? "task"} P${step.priority ?? 2}]`)}`,
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// sr tpl pour <id> --prefix "..."
|
|
162
|
+
if (subcmd === "pour") {
|
|
163
|
+
const templateId = positional[1];
|
|
164
|
+
if (!templateId) throw new Error("Usage: sr tpl pour <id> --prefix ...");
|
|
165
|
+
if (typeof flags.prefix !== "string") throw new Error("--prefix is required");
|
|
166
|
+
const prefix = flags.prefix;
|
|
167
|
+
|
|
168
|
+
const templates = await readTemplates(dir);
|
|
169
|
+
const tpl = templates.find((t) => t.id === templateId);
|
|
170
|
+
if (!tpl) throw new Error(`Template not found: ${templateId}`);
|
|
171
|
+
if (tpl.steps.length === 0) throw new Error(`Template ${templateId} has no steps`);
|
|
172
|
+
|
|
173
|
+
const config = await readConfig(dir);
|
|
174
|
+
const createdIds: string[] = [];
|
|
175
|
+
|
|
176
|
+
await withLock(issuesPath(dir), async () => {
|
|
177
|
+
const existing = await readIssues(dir);
|
|
178
|
+
const existingIds = new Set(existing.map((i) => i.id));
|
|
179
|
+
const now = new Date().toISOString();
|
|
180
|
+
|
|
181
|
+
// Create all issues first (collect IDs)
|
|
182
|
+
const newIssues: Issue[] = [];
|
|
183
|
+
for (const step of tpl.steps) {
|
|
184
|
+
const id = generateId(
|
|
185
|
+
config.project,
|
|
186
|
+
new Set([...existingIds, ...newIssues.map((i) => i.id)]),
|
|
187
|
+
);
|
|
188
|
+
const title = step.title.replace(/\{prefix\}/g, prefix);
|
|
189
|
+
const issue: Issue = {
|
|
190
|
+
id,
|
|
191
|
+
title,
|
|
192
|
+
status: "open",
|
|
193
|
+
type: (step.type ?? "task") as Issue["type"],
|
|
194
|
+
priority: step.priority ?? 2,
|
|
195
|
+
createdAt: now,
|
|
196
|
+
updatedAt: now,
|
|
197
|
+
convoy: templateId,
|
|
198
|
+
};
|
|
199
|
+
newIssues.push(issue);
|
|
200
|
+
createdIds.push(id);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Wire dependencies: step[i+1] blocked by step[i]
|
|
204
|
+
for (let i = 1; i < newIssues.length; i++) {
|
|
205
|
+
const prev = newIssues[i - 1];
|
|
206
|
+
const curr = newIssues[i];
|
|
207
|
+
if (!prev || !curr) continue;
|
|
208
|
+
curr.blockedBy = [prev.id];
|
|
209
|
+
prev.blocks = [curr.id];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Append all new issues
|
|
213
|
+
const allIssues = [...existing, ...newIssues];
|
|
214
|
+
await writeIssues(dir, allIssues);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
if (jsonMode) {
|
|
218
|
+
await outputJson({ success: true, command: "tpl pour", ids: createdIds });
|
|
219
|
+
} else {
|
|
220
|
+
printSuccess(`Poured template ${accent(templateId)} — created ${createdIds.length} issues`);
|
|
221
|
+
for (const id of createdIds) console.log(` ${accent(id)}`);
|
|
222
|
+
}
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// sr tpl status <id>
|
|
227
|
+
if (subcmd === "status") {
|
|
228
|
+
const templateId = positional[1];
|
|
229
|
+
if (!templateId) throw new Error("Usage: sr tpl status <id>");
|
|
230
|
+
|
|
231
|
+
const issues = await readIssues(dir);
|
|
232
|
+
const convoyIssues = issues.filter((i: Issue) => i.convoy === templateId);
|
|
233
|
+
|
|
234
|
+
if (convoyIssues.length === 0) {
|
|
235
|
+
if (jsonMode) {
|
|
236
|
+
await outputJson({
|
|
237
|
+
success: true,
|
|
238
|
+
command: "tpl status",
|
|
239
|
+
templateId,
|
|
240
|
+
total: 0,
|
|
241
|
+
issues: [],
|
|
242
|
+
});
|
|
243
|
+
} else {
|
|
244
|
+
console.log(`No issues found for convoy ${templateId}`);
|
|
245
|
+
}
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const closedIds = new Set(issues.filter((i: Issue) => i.status === "closed").map((i) => i.id));
|
|
250
|
+
const completed = convoyIssues.filter((i) => i.status === "closed").length;
|
|
251
|
+
const inProgress = convoyIssues.filter((i) => i.status === "in_progress").length;
|
|
252
|
+
const blocked = convoyIssues.filter((i) => {
|
|
253
|
+
if (i.status === "closed") return false;
|
|
254
|
+
return (i.blockedBy ?? []).some((bid) => !closedIds.has(bid));
|
|
255
|
+
}).length;
|
|
256
|
+
|
|
257
|
+
const status = {
|
|
258
|
+
templateId,
|
|
259
|
+
total: convoyIssues.length,
|
|
260
|
+
completed,
|
|
261
|
+
inProgress,
|
|
262
|
+
blocked,
|
|
263
|
+
issues: convoyIssues.map((i) => i.id),
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
if (jsonMode) {
|
|
267
|
+
await outputJson({ success: true, command: "tpl status", status });
|
|
268
|
+
} else {
|
|
269
|
+
console.log(`${chalk.bold("Convoy:")} ${accent(templateId)}`);
|
|
270
|
+
console.log(` ${muted("Total:")} ${status.total}`);
|
|
271
|
+
console.log(` ${muted("Completed:")} ${completed}`);
|
|
272
|
+
console.log(` ${muted("In progress:")} ${inProgress}`);
|
|
273
|
+
console.log(` ${muted("Blocked:")} ${blocked}`);
|
|
274
|
+
console.log(muted(" Issues:"));
|
|
275
|
+
for (const issue of convoyIssues) {
|
|
276
|
+
process.stdout.write(" ");
|
|
277
|
+
printIssueOneLine(issue, closedIds);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
throw new Error(
|
|
284
|
+
`Unknown tpl subcommand: ${subcmd}. Use create, step, list, show, pour, or status.`,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function register(program: Command): void {
|
|
289
|
+
const tpl = new Command("tpl").description("Manage issue templates (molecules)");
|
|
290
|
+
|
|
291
|
+
tpl
|
|
292
|
+
.command("create")
|
|
293
|
+
.description("Create a new template")
|
|
294
|
+
.requiredOption("--name <text>", "Template name")
|
|
295
|
+
.option("--json", "Output as JSON")
|
|
296
|
+
.action(async (opts: { name: string; json?: boolean }) => {
|
|
297
|
+
const args: string[] = ["create", "--name", opts.name];
|
|
298
|
+
if (opts.json) args.push("--json");
|
|
299
|
+
await run(args);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
const step = new Command("step").description("Manage template steps");
|
|
303
|
+
step
|
|
304
|
+
.command("add <id>")
|
|
305
|
+
.description("Add a step to a template")
|
|
306
|
+
.requiredOption("--title <text>", "Step title")
|
|
307
|
+
.option("--type <type>", "Step type (task|bug|feature|epic)", "task")
|
|
308
|
+
.option("--priority <n>", "Step priority 0-4", "2")
|
|
309
|
+
.option("--json", "Output as JSON")
|
|
310
|
+
.action(
|
|
311
|
+
async (
|
|
312
|
+
id: string,
|
|
313
|
+
opts: { title: string; type?: string; priority?: string; json?: boolean },
|
|
314
|
+
) => {
|
|
315
|
+
const args: string[] = ["step", "add", id, "--title", opts.title];
|
|
316
|
+
if (opts.type) args.push("--type", opts.type);
|
|
317
|
+
if (opts.priority) args.push("--priority", opts.priority);
|
|
318
|
+
if (opts.json) args.push("--json");
|
|
319
|
+
await run(args);
|
|
320
|
+
},
|
|
321
|
+
);
|
|
322
|
+
tpl.addCommand(step);
|
|
323
|
+
|
|
324
|
+
tpl
|
|
325
|
+
.command("list")
|
|
326
|
+
.description("List all templates")
|
|
327
|
+
.option("--json", "Output as JSON")
|
|
328
|
+
.action(async (opts: { json?: boolean }) => {
|
|
329
|
+
await run(opts.json ? ["list", "--json"] : ["list"]);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
tpl
|
|
333
|
+
.command("show <id>")
|
|
334
|
+
.description("Show template with steps")
|
|
335
|
+
.option("--json", "Output as JSON")
|
|
336
|
+
.action(async (id: string, opts: { json?: boolean }) => {
|
|
337
|
+
const args: string[] = ["show", id];
|
|
338
|
+
if (opts.json) args.push("--json");
|
|
339
|
+
await run(args);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
tpl
|
|
343
|
+
.command("pour <id>")
|
|
344
|
+
.description("Instantiate template into issues")
|
|
345
|
+
.requiredOption("--prefix <text>", "Prefix for issue titles")
|
|
346
|
+
.option("--json", "Output as JSON")
|
|
347
|
+
.action(async (id: string, opts: { prefix: string; json?: boolean }) => {
|
|
348
|
+
const args: string[] = ["pour", id, "--prefix", opts.prefix];
|
|
349
|
+
if (opts.json) args.push("--json");
|
|
350
|
+
await run(args);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
tpl
|
|
354
|
+
.command("status <id>")
|
|
355
|
+
.description("Show convoy status for a template")
|
|
356
|
+
.option("--json", "Output as JSON")
|
|
357
|
+
.action(async (id: string, opts: { json?: boolean }) => {
|
|
358
|
+
const args: string[] = ["status", id];
|
|
359
|
+
if (opts.json) args.push("--json");
|
|
360
|
+
await run(args);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
program.addCommand(tpl);
|
|
364
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import { findSproutDir } from "../config.ts";
|
|
3
|
+
import { accent, muted, outputJson } from "../output.ts";
|
|
4
|
+
import { issuesPath, readIssues, withLock, writeIssues } from "../store.ts";
|
|
5
|
+
import type { Issue } from "../types.ts";
|
|
6
|
+
|
|
7
|
+
export async function run(args: string[], sproutDir?: string): Promise<void> {
|
|
8
|
+
const jsonMode = args.includes("--json");
|
|
9
|
+
const allFlag = args.includes("--all");
|
|
10
|
+
|
|
11
|
+
// Parse --from <blocker-id>
|
|
12
|
+
const fromIdx = args.indexOf("--from");
|
|
13
|
+
const blockerId = fromIdx !== -1 ? args[fromIdx + 1] : undefined;
|
|
14
|
+
|
|
15
|
+
// Collect positional args (skip flags and their values)
|
|
16
|
+
const skipNext = new Set<number>();
|
|
17
|
+
if (fromIdx !== -1) skipNext.add(fromIdx + 1);
|
|
18
|
+
const positional = args.filter((a, i) => !a.startsWith("--") && !skipNext.has(i));
|
|
19
|
+
|
|
20
|
+
const issueId = positional[0];
|
|
21
|
+
if (!issueId) throw new Error("Usage: sr unblock <id> [--from <blocker-id> | --all]");
|
|
22
|
+
if (!allFlag && fromIdx === -1) {
|
|
23
|
+
throw new Error("Usage: sr unblock <id> [--from <blocker-id> | --all]");
|
|
24
|
+
}
|
|
25
|
+
if (fromIdx !== -1 && !blockerId) {
|
|
26
|
+
throw new Error("--from requires a blocker ID");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const dir = sproutDir ?? (await findSproutDir());
|
|
30
|
+
let removed: string[] = [];
|
|
31
|
+
|
|
32
|
+
await withLock(issuesPath(dir), async () => {
|
|
33
|
+
const issues = await readIssues(dir);
|
|
34
|
+
const issueIdx = issues.findIndex((i) => i.id === issueId);
|
|
35
|
+
const issue = issues[issueIdx];
|
|
36
|
+
if (!issue) throw new Error(`Issue not found: ${issueId}`);
|
|
37
|
+
|
|
38
|
+
const currentBlockers = issue.blockedBy ?? [];
|
|
39
|
+
|
|
40
|
+
if (allFlag) {
|
|
41
|
+
const closedIds = new Set(issues.filter((i) => i.status === "closed").map((i) => i.id));
|
|
42
|
+
removed = currentBlockers.filter((bid) => closedIds.has(bid));
|
|
43
|
+
const remaining = currentBlockers.filter((bid) => !closedIds.has(bid));
|
|
44
|
+
|
|
45
|
+
const updatedIssue: Issue = { ...issue, updatedAt: new Date().toISOString() };
|
|
46
|
+
if (remaining.length > 0) updatedIssue.blockedBy = remaining;
|
|
47
|
+
else updatedIssue.blockedBy = undefined;
|
|
48
|
+
issues[issueIdx] = updatedIssue;
|
|
49
|
+
|
|
50
|
+
for (const bid of removed) {
|
|
51
|
+
const bidIdx = issues.findIndex((i) => i.id === bid);
|
|
52
|
+
const blocker = issues[bidIdx];
|
|
53
|
+
if (!blocker) continue;
|
|
54
|
+
const newBlocks = (blocker.blocks ?? []).filter((id) => id !== issueId);
|
|
55
|
+
const updatedBlocker: Issue = { ...blocker, updatedAt: new Date().toISOString() };
|
|
56
|
+
if (newBlocks.length > 0) updatedBlocker.blocks = newBlocks;
|
|
57
|
+
else updatedBlocker.blocks = undefined;
|
|
58
|
+
issues[bidIdx] = updatedBlocker;
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
if (!blockerId) throw new Error("--from requires a blocker ID");
|
|
62
|
+
if (!currentBlockers.includes(blockerId)) {
|
|
63
|
+
throw new Error(`${issueId} is not blocked by ${blockerId}`);
|
|
64
|
+
}
|
|
65
|
+
removed = [blockerId];
|
|
66
|
+
const remaining = currentBlockers.filter((bid) => bid !== blockerId);
|
|
67
|
+
|
|
68
|
+
const updatedIssue: Issue = { ...issue, updatedAt: new Date().toISOString() };
|
|
69
|
+
if (remaining.length > 0) updatedIssue.blockedBy = remaining;
|
|
70
|
+
else updatedIssue.blockedBy = undefined;
|
|
71
|
+
issues[issueIdx] = updatedIssue;
|
|
72
|
+
|
|
73
|
+
const bidIdx = issues.findIndex((i) => i.id === blockerId);
|
|
74
|
+
const blocker = issues[bidIdx];
|
|
75
|
+
if (blocker) {
|
|
76
|
+
const newBlocks = (blocker.blocks ?? []).filter((id) => id !== issueId);
|
|
77
|
+
const updatedBlocker: Issue = { ...blocker, updatedAt: new Date().toISOString() };
|
|
78
|
+
if (newBlocks.length > 0) updatedBlocker.blocks = newBlocks;
|
|
79
|
+
else updatedBlocker.blocks = undefined;
|
|
80
|
+
issues[bidIdx] = updatedBlocker;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
await writeIssues(dir, issues);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (jsonMode) {
|
|
88
|
+
await outputJson({ success: true, command: "unblock", issueId, removed });
|
|
89
|
+
} else {
|
|
90
|
+
if (removed.length === 0) {
|
|
91
|
+
console.log(`${muted("No closed blockers to remove from")} ${accent(issueId)}.`);
|
|
92
|
+
} else {
|
|
93
|
+
for (const bid of removed) {
|
|
94
|
+
console.log(`${accent(issueId)} ${muted("unblocked from")} ${accent(bid)}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function register(program: Command): void {
|
|
101
|
+
program
|
|
102
|
+
.command("unblock")
|
|
103
|
+
.description("Remove blockers from an issue")
|
|
104
|
+
.argument("<id>", "Issue ID to unblock")
|
|
105
|
+
.option("--from <blocker-id>", "Remove a specific blocker")
|
|
106
|
+
.option("--all", "Remove all closed blockers")
|
|
107
|
+
.option("--json", "Output as JSON")
|
|
108
|
+
.action(async (id: string, opts: { from?: string; all?: boolean; json?: boolean }) => {
|
|
109
|
+
const args: string[] = [id];
|
|
110
|
+
if (opts.from) args.push("--from", opts.from);
|
|
111
|
+
if (opts.all) args.push("--all");
|
|
112
|
+
if (opts.json) args.push("--json");
|
|
113
|
+
await run(args);
|
|
114
|
+
});
|
|
115
|
+
}
|