@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,238 @@
|
|
|
1
|
+
// `sr config` — schema-driven read/write surface for `.sprout/config.yaml`.
|
|
2
|
+
//
|
|
3
|
+
// Designed as warren V2's wire contract (warren ROADMAP R-10):
|
|
4
|
+
// - `sr config schema --json` emits the JSON Schema (warren auto-renders a form)
|
|
5
|
+
// - `sr config show [--path <p>]` reads the current value
|
|
6
|
+
// - `sr config set <path> <value>` validates + writes atomically (YAML-parsed)
|
|
7
|
+
// - `sr config unset <path>` removes a value
|
|
8
|
+
//
|
|
9
|
+
// Writes hold the config.yaml advisory lock; mutations validate the entire
|
|
10
|
+
// post-write file against configSchema() before persisting, so a partial set
|
|
11
|
+
// that would leave the file inconsistent is rejected.
|
|
12
|
+
|
|
13
|
+
import { randomBytes } from "node:crypto";
|
|
14
|
+
import { renameSync } from "node:fs";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { Command } from "commander";
|
|
17
|
+
import { findSproutDir } from "../config.ts";
|
|
18
|
+
import { configSchema } from "../config-schema.ts";
|
|
19
|
+
import { accent, muted, outputJson, printSuccess } from "../output.ts";
|
|
20
|
+
import { withLock } from "../store.ts";
|
|
21
|
+
import { CONFIG_FILE } from "../types.ts";
|
|
22
|
+
import { compileSchema } from "../validation.ts";
|
|
23
|
+
import { parseScalarOrFlow, parseYaml, stringifyYaml, type YamlValue } from "../yaml.ts";
|
|
24
|
+
|
|
25
|
+
function configPath(sproutDir: string): string {
|
|
26
|
+
return join(sproutDir, CONFIG_FILE);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function pathParts(path: string): string[] {
|
|
30
|
+
const parts = path.split(".").filter((p) => p.length > 0);
|
|
31
|
+
if (parts.length === 0) throw new Error("Config path must be non-empty");
|
|
32
|
+
return parts;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function describeType(v: unknown): string {
|
|
36
|
+
if (v === null) return "null";
|
|
37
|
+
if (Array.isArray(v)) return "array";
|
|
38
|
+
return typeof v;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getAtPath(data: YamlValue, parts: string[]): YamlValue | undefined {
|
|
42
|
+
let cur: YamlValue = data;
|
|
43
|
+
for (const p of parts) {
|
|
44
|
+
if (cur === null || typeof cur !== "object" || Array.isArray(cur)) return undefined;
|
|
45
|
+
cur = (cur as Record<string, YamlValue>)[p] as YamlValue;
|
|
46
|
+
if (cur === undefined) return undefined;
|
|
47
|
+
}
|
|
48
|
+
return cur;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function setAtPath(data: Record<string, YamlValue>, parts: string[], value: YamlValue): void {
|
|
52
|
+
let cur: Record<string, YamlValue> = data;
|
|
53
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
54
|
+
const p = parts[i];
|
|
55
|
+
if (p === undefined) continue;
|
|
56
|
+
const nxt = cur[p];
|
|
57
|
+
if (nxt === undefined || nxt === null) {
|
|
58
|
+
const empty: Record<string, YamlValue> = {};
|
|
59
|
+
cur[p] = empty;
|
|
60
|
+
cur = empty;
|
|
61
|
+
} else if (typeof nxt === "object" && !Array.isArray(nxt)) {
|
|
62
|
+
cur = nxt as Record<string, YamlValue>;
|
|
63
|
+
} else {
|
|
64
|
+
const soFar = parts.slice(0, i + 1).join(".");
|
|
65
|
+
throw new Error(`Cannot set: '${soFar}' is not an object (got: ${describeType(nxt)})`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const last = parts[parts.length - 1];
|
|
69
|
+
if (last !== undefined) cur[last] = value;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function unsetAtPath(data: Record<string, YamlValue>, parts: string[]): boolean {
|
|
73
|
+
let cur: Record<string, YamlValue> = data;
|
|
74
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
75
|
+
const p = parts[i];
|
|
76
|
+
if (p === undefined) continue;
|
|
77
|
+
const nxt = cur[p];
|
|
78
|
+
if (nxt === null || typeof nxt !== "object" || Array.isArray(nxt)) return false;
|
|
79
|
+
cur = nxt as Record<string, YamlValue>;
|
|
80
|
+
}
|
|
81
|
+
const last = parts[parts.length - 1];
|
|
82
|
+
if (last === undefined) return false;
|
|
83
|
+
if (!(last in cur)) return false;
|
|
84
|
+
delete cur[last];
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function validateConfig(data: unknown): void {
|
|
89
|
+
// Strip the meta-schema URI: configSchema() advertises draft 2020-12 for
|
|
90
|
+
// downstream consumers (warren's UI), but our shared compileSchema runs
|
|
91
|
+
// AJV in default (draft-07) mode and rejects unknown $schema URIs.
|
|
92
|
+
const { $schema: _meta, ...schema } = configSchema();
|
|
93
|
+
const validator = compileSchema(schema);
|
|
94
|
+
const result = validator(data);
|
|
95
|
+
if (!result.valid) {
|
|
96
|
+
const lines = result.diff.errors.map((e) => ` ${e.path || "(root)"}: ${e.fix}`);
|
|
97
|
+
throw new Error(`Config validation failed:\n${lines.join("\n")}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function readRawConfig(sproutDir: string): Promise<Record<string, YamlValue>> {
|
|
102
|
+
const file = Bun.file(configPath(sproutDir));
|
|
103
|
+
if (!(await file.exists())) {
|
|
104
|
+
throw new Error(`Config not found at ${configPath(sproutDir)}`);
|
|
105
|
+
}
|
|
106
|
+
const content = await file.text();
|
|
107
|
+
return parseYaml(content);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function writeRawConfig(sproutDir: string, data: Record<string, YamlValue>): Promise<void> {
|
|
111
|
+
const path = configPath(sproutDir);
|
|
112
|
+
const tmpPath = `${path}.tmp.${randomBytes(4).toString("hex")}`;
|
|
113
|
+
await Bun.write(tmpPath, stringifyYaml(data));
|
|
114
|
+
renameSync(tmpPath, path);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function displayValue(v: YamlValue): string {
|
|
118
|
+
if (typeof v === "string") return v;
|
|
119
|
+
return JSON.stringify(v);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function runSchema(jsonMode: boolean): Promise<void> {
|
|
123
|
+
const schema = configSchema();
|
|
124
|
+
if (jsonMode) {
|
|
125
|
+
await Bun.write(Bun.stdout, `${JSON.stringify(schema)}\n`);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
await Bun.write(Bun.stdout, `${JSON.stringify(schema, null, 2)}\n`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function runShow(pathArg: string | undefined, jsonMode: boolean): Promise<void> {
|
|
132
|
+
const dir = await findSproutDir();
|
|
133
|
+
const raw = await readRawConfig(dir);
|
|
134
|
+
|
|
135
|
+
if (pathArg) {
|
|
136
|
+
const parts = pathParts(pathArg);
|
|
137
|
+
const value = getAtPath(raw, parts);
|
|
138
|
+
if (value === undefined) {
|
|
139
|
+
throw new Error(`Path not found: ${pathArg}`);
|
|
140
|
+
}
|
|
141
|
+
if (jsonMode) {
|
|
142
|
+
await outputJson({ success: true, command: "config show", path: pathArg, value });
|
|
143
|
+
} else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
144
|
+
process.stdout.write(stringifyYaml(value as Record<string, YamlValue>));
|
|
145
|
+
} else if (typeof value === "string") {
|
|
146
|
+
console.log(value);
|
|
147
|
+
} else {
|
|
148
|
+
await Bun.write(Bun.stdout, `${JSON.stringify(value)}\n`);
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (jsonMode) {
|
|
154
|
+
await outputJson({ success: true, command: "config show", config: raw });
|
|
155
|
+
} else {
|
|
156
|
+
process.stdout.write(stringifyYaml(raw));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function runSet(pathArg: string, valueArg: string, jsonMode: boolean): Promise<void> {
|
|
161
|
+
const dir = await findSproutDir();
|
|
162
|
+
const parts = pathParts(pathArg);
|
|
163
|
+
const value = parseScalarOrFlow(valueArg);
|
|
164
|
+
|
|
165
|
+
await withLock(configPath(dir), async () => {
|
|
166
|
+
const raw = await readRawConfig(dir);
|
|
167
|
+
setAtPath(raw, parts, value);
|
|
168
|
+
validateConfig(raw);
|
|
169
|
+
await writeRawConfig(dir, raw);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
if (jsonMode) {
|
|
173
|
+
await outputJson({ success: true, command: "config set", path: pathArg, value });
|
|
174
|
+
} else {
|
|
175
|
+
printSuccess(`Set ${accent(pathArg)} ${muted("=")} ${displayValue(value)}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function runUnset(pathArg: string, jsonMode: boolean): Promise<void> {
|
|
180
|
+
const dir = await findSproutDir();
|
|
181
|
+
const parts = pathParts(pathArg);
|
|
182
|
+
|
|
183
|
+
let removed = false;
|
|
184
|
+
await withLock(configPath(dir), async () => {
|
|
185
|
+
const raw = await readRawConfig(dir);
|
|
186
|
+
removed = unsetAtPath(raw, parts);
|
|
187
|
+
if (!removed) return;
|
|
188
|
+
validateConfig(raw);
|
|
189
|
+
await writeRawConfig(dir, raw);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
if (jsonMode) {
|
|
193
|
+
await outputJson({ success: true, command: "config unset", path: pathArg, removed });
|
|
194
|
+
} else if (removed) {
|
|
195
|
+
printSuccess(`Unset ${accent(pathArg)}`);
|
|
196
|
+
} else {
|
|
197
|
+
console.log(muted(`No such path: ${pathArg}`));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function register(program: Command): void {
|
|
202
|
+
const config = new Command("config").description("Read, write, and inspect .sprout/config.yaml");
|
|
203
|
+
|
|
204
|
+
config
|
|
205
|
+
.command("schema")
|
|
206
|
+
.description("Emit the JSON Schema for .sprout/config.yaml")
|
|
207
|
+
.option("--json", "Compact single-line JSON (default is pretty-printed)")
|
|
208
|
+
.action(async (opts: { json?: boolean }) => {
|
|
209
|
+
await runSchema(opts.json === true);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
config
|
|
213
|
+
.command("show")
|
|
214
|
+
.description("Print the current config (or a value at --path)")
|
|
215
|
+
.option("--path <path>", "Dot-path to read (e.g. plan_templates.feature.sections.context)")
|
|
216
|
+
.option("--json", "Output as JSON")
|
|
217
|
+
.action(async (opts: { path?: string; json?: boolean }) => {
|
|
218
|
+
await runShow(opts.path, opts.json === true);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
config
|
|
222
|
+
.command("set <path> <value>")
|
|
223
|
+
.description("Set a config value at <path>; <value> is YAML-parsed")
|
|
224
|
+
.option("--json", "Output as JSON")
|
|
225
|
+
.action(async (path: string, value: string, opts: { json?: boolean }) => {
|
|
226
|
+
await runSet(path, value, opts.json === true);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
config
|
|
230
|
+
.command("unset <path>")
|
|
231
|
+
.description("Remove the config value at <path>")
|
|
232
|
+
.option("--json", "Output as JSON")
|
|
233
|
+
.action(async (path: string, opts: { json?: boolean }) => {
|
|
234
|
+
await runUnset(path, opts.json === true);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
program.addCommand(config);
|
|
238
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { type Command, Option } from "commander";
|
|
2
|
+
import { findSproutDir, readConfig } from "../config.ts";
|
|
3
|
+
import { generateId } from "../id.ts";
|
|
4
|
+
import { outputJson, printSuccess } from "../output.ts";
|
|
5
|
+
import { appendIssue, issuesPath, readIssues, withLock } from "../store.ts";
|
|
6
|
+
import type { Issue } from "../types.ts";
|
|
7
|
+
import { VALID_TYPES } from "../types.ts";
|
|
8
|
+
|
|
9
|
+
function parseArgs(args: string[]) {
|
|
10
|
+
const flags: Record<string, string | boolean> = {};
|
|
11
|
+
let i = 0;
|
|
12
|
+
while (i < args.length) {
|
|
13
|
+
const arg = args[i];
|
|
14
|
+
if (!arg) {
|
|
15
|
+
i++;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (arg.startsWith("--")) {
|
|
19
|
+
const key = arg.slice(2);
|
|
20
|
+
const eqIdx = key.indexOf("=");
|
|
21
|
+
if (eqIdx !== -1) {
|
|
22
|
+
flags[key.slice(0, eqIdx)] = key.slice(eqIdx + 1);
|
|
23
|
+
i++;
|
|
24
|
+
} else {
|
|
25
|
+
const next = args[i + 1];
|
|
26
|
+
if (next !== undefined && !next.startsWith("--")) {
|
|
27
|
+
flags[key] = next;
|
|
28
|
+
i += 2;
|
|
29
|
+
} else {
|
|
30
|
+
flags[key] = true;
|
|
31
|
+
i++;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
i++;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return flags;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function parsePriority(val: string | boolean | undefined, defaultVal = 2): number {
|
|
42
|
+
if (val === undefined || val === true) return defaultVal;
|
|
43
|
+
const s = String(val);
|
|
44
|
+
if (s.toUpperCase().startsWith("P")) return Number.parseInt(s.slice(1), 10);
|
|
45
|
+
return Number.parseInt(s, 10);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function run(args: string[], sproutDir?: string): Promise<void> {
|
|
49
|
+
const jsonMode = args.includes("--json");
|
|
50
|
+
const flags = parseArgs(args);
|
|
51
|
+
const title = flags.title;
|
|
52
|
+
if (!title || typeof title !== "string" || !title.trim()) {
|
|
53
|
+
throw new Error("--title is required");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const typeVal = flags.type ?? "task";
|
|
57
|
+
if (typeof typeVal !== "string" || !(VALID_TYPES as readonly string[]).includes(typeVal)) {
|
|
58
|
+
throw new Error(`--type must be one of: ${VALID_TYPES.join(", ")}`);
|
|
59
|
+
}
|
|
60
|
+
const issueType = typeVal as Issue["type"];
|
|
61
|
+
|
|
62
|
+
const priority = parsePriority(flags.priority);
|
|
63
|
+
if (Number.isNaN(priority) || priority < 0 || priority > 4) {
|
|
64
|
+
throw new Error("--priority must be 0-4 or P0-P4");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// --label is a hidden alias for --labels; --labels wins when both are supplied.
|
|
68
|
+
const labelsRaw =
|
|
69
|
+
typeof flags.labels === "string"
|
|
70
|
+
? flags.labels
|
|
71
|
+
: typeof flags.label === "string"
|
|
72
|
+
? flags.label
|
|
73
|
+
: undefined;
|
|
74
|
+
const labels = labelsRaw
|
|
75
|
+
? labelsRaw
|
|
76
|
+
.split(",")
|
|
77
|
+
.map((l) => l.trim().toLowerCase())
|
|
78
|
+
.filter(Boolean)
|
|
79
|
+
: undefined;
|
|
80
|
+
|
|
81
|
+
const assignee = typeof flags.assignee === "string" ? flags.assignee : undefined;
|
|
82
|
+
const description =
|
|
83
|
+
typeof flags.description === "string"
|
|
84
|
+
? flags.description
|
|
85
|
+
: typeof flags.desc === "string"
|
|
86
|
+
? flags.desc
|
|
87
|
+
: typeof flags.body === "string"
|
|
88
|
+
? flags.body
|
|
89
|
+
: undefined;
|
|
90
|
+
|
|
91
|
+
const dir = sproutDir ?? (await findSproutDir());
|
|
92
|
+
const config = await readConfig(dir);
|
|
93
|
+
|
|
94
|
+
let createdId = "";
|
|
95
|
+
await withLock(issuesPath(dir), async () => {
|
|
96
|
+
const existing = await readIssues(dir);
|
|
97
|
+
const existingIds = new Set(existing.map((i) => i.id));
|
|
98
|
+
const id = generateId(config.project, existingIds);
|
|
99
|
+
const now = new Date().toISOString();
|
|
100
|
+
const issue: Issue = {
|
|
101
|
+
id,
|
|
102
|
+
title: title.trim(),
|
|
103
|
+
status: "open",
|
|
104
|
+
type: issueType,
|
|
105
|
+
priority,
|
|
106
|
+
createdAt: now,
|
|
107
|
+
updatedAt: now,
|
|
108
|
+
...(assignee ? { assignee } : {}),
|
|
109
|
+
...(description ? { description } : {}),
|
|
110
|
+
...(labels && labels.length > 0 ? { labels } : {}),
|
|
111
|
+
};
|
|
112
|
+
await appendIssue(dir, issue);
|
|
113
|
+
createdId = id;
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if (jsonMode) {
|
|
117
|
+
await outputJson({ success: true, command: "create", id: createdId });
|
|
118
|
+
} else {
|
|
119
|
+
printSuccess(`Created ${createdId}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function register(program: Command): void {
|
|
124
|
+
program
|
|
125
|
+
.command("create")
|
|
126
|
+
.description("Create a new issue")
|
|
127
|
+
.requiredOption("--title <text>", "Issue title")
|
|
128
|
+
.option("--type <type>", "Issue type (task|bug|feature|epic)", "task")
|
|
129
|
+
.option("--priority <n>", "Priority 0-4 or P0-P4", "2")
|
|
130
|
+
.option("--assignee <name>", "Assignee name")
|
|
131
|
+
.option("--description <text>", "Issue description")
|
|
132
|
+
.option("--desc <text>", "Issue description (alias for --description)")
|
|
133
|
+
.option("--body <text>", "Issue description (alias for --description)")
|
|
134
|
+
.option("--labels <labels>", "Comma-separated labels")
|
|
135
|
+
.addOption(new Option("--label <labels>", "Comma-separated labels (alias)").hideHelp())
|
|
136
|
+
.option("--json", "Output as JSON")
|
|
137
|
+
.action(
|
|
138
|
+
async (opts: {
|
|
139
|
+
title: string;
|
|
140
|
+
type?: string;
|
|
141
|
+
priority?: string;
|
|
142
|
+
assignee?: string;
|
|
143
|
+
description?: string;
|
|
144
|
+
desc?: string;
|
|
145
|
+
body?: string;
|
|
146
|
+
labels?: string;
|
|
147
|
+
label?: string;
|
|
148
|
+
json?: boolean;
|
|
149
|
+
}) => {
|
|
150
|
+
const args: string[] = ["--title", opts.title];
|
|
151
|
+
if (opts.type) args.push("--type", opts.type);
|
|
152
|
+
if (opts.priority) args.push("--priority", opts.priority);
|
|
153
|
+
if (opts.assignee) args.push("--assignee", opts.assignee);
|
|
154
|
+
if (opts.description) args.push("--description", opts.description);
|
|
155
|
+
if (opts.desc) args.push("--desc", opts.desc);
|
|
156
|
+
if (opts.body) args.push("--body", opts.body);
|
|
157
|
+
// --labels wins over --label alias when both are supplied.
|
|
158
|
+
if (opts.labels) args.push("--labels", opts.labels);
|
|
159
|
+
else if (opts.label) args.push("--labels", opts.label);
|
|
160
|
+
if (opts.json) args.push("--json");
|
|
161
|
+
await run(args);
|
|
162
|
+
},
|
|
163
|
+
);
|
|
164
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { findSproutDir } from "../config.ts";
|
|
3
|
+
import { accent, muted, outputJson, printIssueOneLine } 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 positional = args.filter((a) => !a.startsWith("--"));
|
|
10
|
+
|
|
11
|
+
const subcmd = positional[0];
|
|
12
|
+
if (!subcmd) throw new Error("Usage: sr dep <add|remove|list> <issue> [depends-on]");
|
|
13
|
+
|
|
14
|
+
const dir = sproutDir ?? (await findSproutDir());
|
|
15
|
+
|
|
16
|
+
if (subcmd === "list") {
|
|
17
|
+
const issueId = positional[1];
|
|
18
|
+
if (!issueId) throw new Error("Usage: sr dep list <issue>");
|
|
19
|
+
const issues = await readIssues(dir);
|
|
20
|
+
const issue = issues.find((i) => i.id === issueId);
|
|
21
|
+
if (!issue) throw new Error(`Issue not found: ${issueId}`);
|
|
22
|
+
|
|
23
|
+
const blockedBy = issue.blockedBy ?? [];
|
|
24
|
+
const blocks = issue.blocks ?? [];
|
|
25
|
+
const closedBlockerIds = new Set(issues.filter((i) => i.status === "closed").map((i) => i.id));
|
|
26
|
+
|
|
27
|
+
if (jsonMode) {
|
|
28
|
+
await outputJson({ success: true, command: "dep list", issueId, blockedBy, blocks });
|
|
29
|
+
} else {
|
|
30
|
+
console.log(`${accent.bold(issueId)} ${muted("dependencies:")}`);
|
|
31
|
+
if (blockedBy.length > 0) {
|
|
32
|
+
console.log(muted(" Blocked by:"));
|
|
33
|
+
for (const bid of blockedBy) {
|
|
34
|
+
const b = issues.find((i) => i.id === bid);
|
|
35
|
+
if (b) {
|
|
36
|
+
process.stdout.write(" ");
|
|
37
|
+
printIssueOneLine(b, closedBlockerIds);
|
|
38
|
+
} else {
|
|
39
|
+
console.log(` ${accent(bid)} ${muted("(not found)")}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (blocks.length > 0) {
|
|
44
|
+
console.log(muted(" Blocks:"));
|
|
45
|
+
for (const bid of blocks) {
|
|
46
|
+
const b = issues.find((i) => i.id === bid);
|
|
47
|
+
if (b) {
|
|
48
|
+
process.stdout.write(" ");
|
|
49
|
+
printIssueOneLine(b, closedBlockerIds);
|
|
50
|
+
} else {
|
|
51
|
+
console.log(` ${accent(bid)} ${muted("(not found)")}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (blockedBy.length === 0 && blocks.length === 0) {
|
|
56
|
+
console.log(muted(" No dependencies."));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (subcmd === "add" || subcmd === "remove") {
|
|
63
|
+
const issueId = positional[1];
|
|
64
|
+
const dependsOnId = positional[2];
|
|
65
|
+
if (!issueId || !dependsOnId) {
|
|
66
|
+
throw new Error(`Usage: sr dep ${subcmd} <issue> <depends-on>`);
|
|
67
|
+
}
|
|
68
|
+
if (subcmd === "add" && issueId === dependsOnId) {
|
|
69
|
+
throw new Error(`Cannot add a self-dependency: ${issueId}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
await withLock(issuesPath(dir), async () => {
|
|
73
|
+
const issues = await readIssues(dir);
|
|
74
|
+
const issueIdx = issues.findIndex((i) => i.id === issueId);
|
|
75
|
+
const issue = issues[issueIdx];
|
|
76
|
+
if (!issue) throw new Error(`Issue not found: ${issueId}`);
|
|
77
|
+
|
|
78
|
+
const depIdx = issues.findIndex((i) => i.id === dependsOnId);
|
|
79
|
+
const dep = issues[depIdx];
|
|
80
|
+
if (!dep) throw new Error(`Issue not found: ${dependsOnId}`);
|
|
81
|
+
|
|
82
|
+
if (subcmd === "add") {
|
|
83
|
+
const blockedBy = Array.from(new Set([...(issue.blockedBy ?? []), dependsOnId]));
|
|
84
|
+
const depBlocks = Array.from(new Set([...(dep.blocks ?? []), issueId]));
|
|
85
|
+
issues[issueIdx] = { ...issue, blockedBy, updatedAt: new Date().toISOString() };
|
|
86
|
+
issues[depIdx] = { ...dep, blocks: depBlocks, updatedAt: new Date().toISOString() };
|
|
87
|
+
} else {
|
|
88
|
+
const blockedBy = (issue.blockedBy ?? []).filter((id: string) => id !== dependsOnId);
|
|
89
|
+
const depBlocks = (dep.blocks ?? []).filter((id: string) => id !== issueId);
|
|
90
|
+
const updatedIssue: Issue = { ...issue, updatedAt: new Date().toISOString() };
|
|
91
|
+
if (blockedBy.length > 0) updatedIssue.blockedBy = blockedBy;
|
|
92
|
+
else updatedIssue.blockedBy = undefined;
|
|
93
|
+
const updatedDep: Issue = { ...dep, updatedAt: new Date().toISOString() };
|
|
94
|
+
if (depBlocks.length > 0) updatedDep.blocks = depBlocks;
|
|
95
|
+
else updatedDep.blocks = undefined;
|
|
96
|
+
issues[issueIdx] = updatedIssue;
|
|
97
|
+
issues[depIdx] = updatedDep;
|
|
98
|
+
}
|
|
99
|
+
await writeIssues(dir, issues);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (jsonMode) {
|
|
103
|
+
await outputJson({ success: true, command: `dep ${subcmd}`, issueId, dependsOnId });
|
|
104
|
+
} else {
|
|
105
|
+
const verb = subcmd === "add" ? "Added" : "Removed";
|
|
106
|
+
console.log(`${verb} dependency: ${accent(issueId)} ${muted("→")} ${accent(dependsOnId)}`);
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
throw new Error(`Unknown dep subcommand: ${subcmd}. Use add, remove, or list.`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function register(program: Command): void {
|
|
115
|
+
const dep = new Command("dep").description("Manage issue dependencies");
|
|
116
|
+
|
|
117
|
+
dep
|
|
118
|
+
.command("add <issue> <depends-on>")
|
|
119
|
+
.description("Add a dependency (issue depends on depends-on)")
|
|
120
|
+
.option("--json", "Output as JSON")
|
|
121
|
+
.action(async (issue: string, dependsOn: string, opts: { json?: boolean }) => {
|
|
122
|
+
const args: string[] = ["add", issue, dependsOn];
|
|
123
|
+
if (opts.json) args.push("--json");
|
|
124
|
+
await run(args);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
dep
|
|
128
|
+
.command("remove <issue> <depends-on>")
|
|
129
|
+
.description("Remove a dependency")
|
|
130
|
+
.option("--json", "Output as JSON")
|
|
131
|
+
.action(async (issue: string, dependsOn: string, opts: { json?: boolean }) => {
|
|
132
|
+
const args: string[] = ["remove", issue, dependsOn];
|
|
133
|
+
if (opts.json) args.push("--json");
|
|
134
|
+
await run(args);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
dep
|
|
138
|
+
.command("list <issue>")
|
|
139
|
+
.description("Show dependencies for an issue")
|
|
140
|
+
.option("--json", "Output as JSON")
|
|
141
|
+
.action(async (issue: string, opts: { json?: boolean }) => {
|
|
142
|
+
const args: string[] = ["list", issue];
|
|
143
|
+
if (opts.json) args.push("--json");
|
|
144
|
+
await run(args);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
program.addCommand(dep);
|
|
148
|
+
}
|