@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,546 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command: ap group create|status|add|remove|list
|
|
3
|
+
*
|
|
4
|
+
* Manages TaskGroups for batch work coordination. Groups track collections
|
|
5
|
+
* of issues and auto-close when all member issues are closed.
|
|
6
|
+
*
|
|
7
|
+
* Storage: `.agentplate/groups.json` (array of TaskGroup objects).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { Command } from "commander";
|
|
12
|
+
import { loadConfig } from "../config.ts";
|
|
13
|
+
import { GroupError, ValidationError } from "../errors.ts";
|
|
14
|
+
import { jsonOutput } from "../json.ts";
|
|
15
|
+
import { accent, printHint, printSuccess } from "../logging/color.ts";
|
|
16
|
+
import { createTrackerClient, resolveBackend, type TrackerClient } from "../tracker/factory.ts";
|
|
17
|
+
import type { TaskGroup, TaskGroupProgress } from "../types.ts";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the groups.json path from the project root.
|
|
21
|
+
*/
|
|
22
|
+
function groupsPath(projectRoot: string): string {
|
|
23
|
+
return join(projectRoot, ".agentplate", "groups.json");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Load groups from .agentplate/groups.json.
|
|
28
|
+
* @internal Exported for testing.
|
|
29
|
+
*/
|
|
30
|
+
export async function loadGroups(projectRoot: string): Promise<TaskGroup[]> {
|
|
31
|
+
const path = groupsPath(projectRoot);
|
|
32
|
+
const file = Bun.file(path);
|
|
33
|
+
if (!(await file.exists())) {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const text = await file.text();
|
|
38
|
+
return JSON.parse(text) as TaskGroup[];
|
|
39
|
+
} catch {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Save groups to .agentplate/groups.json.
|
|
46
|
+
*/
|
|
47
|
+
async function saveGroups(projectRoot: string, groups: TaskGroup[]): Promise<void> {
|
|
48
|
+
const path = groupsPath(projectRoot);
|
|
49
|
+
await Bun.write(path, `${JSON.stringify(groups, null, "\t")}\n`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Query a tracker issue status via the tracker client.
|
|
54
|
+
* Returns the status string, or null if the issue cannot be found.
|
|
55
|
+
*/
|
|
56
|
+
async function getIssueStatus(id: string, tracker: TrackerClient): Promise<string | null> {
|
|
57
|
+
try {
|
|
58
|
+
const issue = await tracker.show(id);
|
|
59
|
+
return issue.status ?? null;
|
|
60
|
+
} catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Validate that a tracker issue exists.
|
|
67
|
+
*/
|
|
68
|
+
async function validateIssueExists(id: string, tracker: TrackerClient): Promise<void> {
|
|
69
|
+
const status = await getIssueStatus(id, tracker);
|
|
70
|
+
if (status === null) {
|
|
71
|
+
throw new GroupError(`Issue "${id}" not found in tracker`, { groupId: id });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Generate a group ID.
|
|
77
|
+
*/
|
|
78
|
+
function generateGroupId(): string {
|
|
79
|
+
return `group-${crypto.randomUUID().slice(0, 8)}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Resolve a group by ID or name.
|
|
84
|
+
*
|
|
85
|
+
* Names are not enforced unique by `createGroup`, so live `groups.json` files
|
|
86
|
+
* contain duplicate names — a naive name lookup would silently pick the wrong
|
|
87
|
+
* group. Resolution precedence:
|
|
88
|
+
* 1. Exact ID match wins (UUIDs are unambiguous).
|
|
89
|
+
* 2. Otherwise filter by name. If exactly one match, return it.
|
|
90
|
+
* 3. If multiple name matches, prefer a single `active` one. If still
|
|
91
|
+
* ambiguous, throw with the matching IDs so the caller can disambiguate
|
|
92
|
+
* by passing the UUID.
|
|
93
|
+
*
|
|
94
|
+
* @internal Exported for testing.
|
|
95
|
+
*/
|
|
96
|
+
export function resolveGroup(groups: TaskGroup[], identifier: string): TaskGroup {
|
|
97
|
+
const byId = groups.find((g) => g.id === identifier);
|
|
98
|
+
if (byId) return byId;
|
|
99
|
+
|
|
100
|
+
const byName = groups.filter((g) => g.name === identifier);
|
|
101
|
+
if (byName.length === 1) {
|
|
102
|
+
const only = byName[0];
|
|
103
|
+
if (only) return only;
|
|
104
|
+
}
|
|
105
|
+
if (byName.length > 1) {
|
|
106
|
+
const active = byName.filter((g) => g.status === "active");
|
|
107
|
+
if (active.length === 1) {
|
|
108
|
+
const only = active[0];
|
|
109
|
+
if (only) return only;
|
|
110
|
+
}
|
|
111
|
+
const ids = byName.map((g) => g.id).join(", ");
|
|
112
|
+
throw new GroupError(
|
|
113
|
+
`Group name "${identifier}" is ambiguous (matches: ${ids}). Use the group ID.`,
|
|
114
|
+
{ groupId: identifier },
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
throw new GroupError(`Group "${identifier}" not found`, { groupId: identifier });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Create a new task group.
|
|
122
|
+
* @internal Exported for testing.
|
|
123
|
+
*/
|
|
124
|
+
export async function createGroup(
|
|
125
|
+
projectRoot: string,
|
|
126
|
+
name: string,
|
|
127
|
+
issueIds: string[],
|
|
128
|
+
skipValidation = false,
|
|
129
|
+
tracker?: TrackerClient,
|
|
130
|
+
): Promise<TaskGroup> {
|
|
131
|
+
if (!name || name.trim().length === 0) {
|
|
132
|
+
throw new ValidationError("Group name is required", { field: "name" });
|
|
133
|
+
}
|
|
134
|
+
if (issueIds.length === 0) {
|
|
135
|
+
throw new ValidationError("At least one issue ID is required", { field: "issueIds" });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Validate all issues exist
|
|
139
|
+
if (!skipValidation && tracker) {
|
|
140
|
+
for (const id of issueIds) {
|
|
141
|
+
await validateIssueExists(id, tracker);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Check for duplicate issue IDs in the input
|
|
146
|
+
const unique = new Set(issueIds);
|
|
147
|
+
if (unique.size !== issueIds.length) {
|
|
148
|
+
throw new ValidationError("Duplicate issue IDs provided", { field: "issueIds" });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const groups = await loadGroups(projectRoot);
|
|
152
|
+
const group: TaskGroup = {
|
|
153
|
+
id: generateGroupId(),
|
|
154
|
+
name: name.trim(),
|
|
155
|
+
memberIssueIds: issueIds,
|
|
156
|
+
status: "active",
|
|
157
|
+
createdAt: new Date().toISOString(),
|
|
158
|
+
completedAt: null,
|
|
159
|
+
};
|
|
160
|
+
groups.push(group);
|
|
161
|
+
await saveGroups(projectRoot, groups);
|
|
162
|
+
return group;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Add issues to an existing group.
|
|
167
|
+
* @internal Exported for testing.
|
|
168
|
+
*/
|
|
169
|
+
export async function addToGroup(
|
|
170
|
+
projectRoot: string,
|
|
171
|
+
groupId: string,
|
|
172
|
+
issueIds: string[],
|
|
173
|
+
skipValidation = false,
|
|
174
|
+
tracker?: TrackerClient,
|
|
175
|
+
): Promise<TaskGroup> {
|
|
176
|
+
if (issueIds.length === 0) {
|
|
177
|
+
throw new ValidationError("At least one issue ID is required", { field: "issueIds" });
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const groups = await loadGroups(projectRoot);
|
|
181
|
+
const group = resolveGroup(groups, groupId);
|
|
182
|
+
|
|
183
|
+
// Check for duplicates against existing members
|
|
184
|
+
for (const id of issueIds) {
|
|
185
|
+
if (group.memberIssueIds.includes(id)) {
|
|
186
|
+
throw new GroupError(`Issue "${id}" is already a member of group "${group.id}"`, {
|
|
187
|
+
groupId: group.id,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Validate issues exist
|
|
193
|
+
if (!skipValidation && tracker) {
|
|
194
|
+
for (const id of issueIds) {
|
|
195
|
+
await validateIssueExists(id, tracker);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
group.memberIssueIds.push(...issueIds);
|
|
200
|
+
|
|
201
|
+
// If group was completed, reopen it
|
|
202
|
+
if (group.status === "completed") {
|
|
203
|
+
group.status = "active";
|
|
204
|
+
group.completedAt = null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
await saveGroups(projectRoot, groups);
|
|
208
|
+
return group;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Remove issues from an existing group.
|
|
213
|
+
* @internal Exported for testing.
|
|
214
|
+
*/
|
|
215
|
+
export async function removeFromGroup(
|
|
216
|
+
projectRoot: string,
|
|
217
|
+
groupId: string,
|
|
218
|
+
issueIds: string[],
|
|
219
|
+
): Promise<TaskGroup> {
|
|
220
|
+
if (issueIds.length === 0) {
|
|
221
|
+
throw new ValidationError("At least one issue ID is required", { field: "issueIds" });
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const groups = await loadGroups(projectRoot);
|
|
225
|
+
const group = resolveGroup(groups, groupId);
|
|
226
|
+
|
|
227
|
+
// Validate all issues are members
|
|
228
|
+
for (const id of issueIds) {
|
|
229
|
+
if (!group.memberIssueIds.includes(id)) {
|
|
230
|
+
throw new GroupError(`Issue "${id}" is not a member of group "${group.id}"`, {
|
|
231
|
+
groupId: group.id,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Check that removal won't empty the group
|
|
237
|
+
const remaining = group.memberIssueIds.filter((id) => !issueIds.includes(id));
|
|
238
|
+
if (remaining.length === 0) {
|
|
239
|
+
throw new GroupError("Cannot remove all issues from a group", { groupId: group.id });
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
group.memberIssueIds = remaining;
|
|
243
|
+
await saveGroups(projectRoot, groups);
|
|
244
|
+
return group;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Get progress for a single group. Queries the tracker for member issue statuses.
|
|
249
|
+
* Auto-closes the group if all members are closed.
|
|
250
|
+
* @internal Exported for testing.
|
|
251
|
+
*/
|
|
252
|
+
export async function getGroupProgress(
|
|
253
|
+
projectRoot: string,
|
|
254
|
+
group: TaskGroup,
|
|
255
|
+
groups: TaskGroup[],
|
|
256
|
+
tracker?: TrackerClient,
|
|
257
|
+
): Promise<TaskGroupProgress> {
|
|
258
|
+
let completed = 0;
|
|
259
|
+
let inProgress = 0;
|
|
260
|
+
let blocked = 0;
|
|
261
|
+
let open = 0;
|
|
262
|
+
|
|
263
|
+
for (const id of group.memberIssueIds) {
|
|
264
|
+
const status = tracker ? await getIssueStatus(id, tracker) : null;
|
|
265
|
+
switch (status) {
|
|
266
|
+
case "closed":
|
|
267
|
+
completed++;
|
|
268
|
+
break;
|
|
269
|
+
case "in_progress":
|
|
270
|
+
inProgress++;
|
|
271
|
+
break;
|
|
272
|
+
case "blocked":
|
|
273
|
+
blocked++;
|
|
274
|
+
break;
|
|
275
|
+
default:
|
|
276
|
+
open++;
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const total = group.memberIssueIds.length;
|
|
282
|
+
|
|
283
|
+
// Auto-close: if all members are closed and group is still active
|
|
284
|
+
if (completed === total && total > 0 && group.status === "active") {
|
|
285
|
+
group.status = "completed";
|
|
286
|
+
group.completedAt = new Date().toISOString();
|
|
287
|
+
await saveGroups(projectRoot, groups);
|
|
288
|
+
process.stdout.write(
|
|
289
|
+
`Group "${group.name}" (${accent(group.id)}) auto-closed: all issues done\n`,
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
// Notify coordinator via mail (best-effort)
|
|
293
|
+
try {
|
|
294
|
+
const mailDbPath = join(projectRoot, ".agentplate", "mail.db");
|
|
295
|
+
const mailDbFile = Bun.file(mailDbPath);
|
|
296
|
+
if (await mailDbFile.exists()) {
|
|
297
|
+
const { createMailStore } = await import("../mail/store.ts");
|
|
298
|
+
const mailStore = createMailStore(mailDbPath);
|
|
299
|
+
try {
|
|
300
|
+
mailStore.insert({
|
|
301
|
+
id: "",
|
|
302
|
+
from: "system",
|
|
303
|
+
to: "coordinator",
|
|
304
|
+
subject: `Group auto-closed: ${group.name}`,
|
|
305
|
+
body: `Task group ${group.id} ("${group.name}") completed. All ${total} member issues are closed.`,
|
|
306
|
+
type: "status",
|
|
307
|
+
priority: "normal",
|
|
308
|
+
threadId: null,
|
|
309
|
+
});
|
|
310
|
+
} finally {
|
|
311
|
+
mailStore.close();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
} catch {
|
|
315
|
+
// Non-fatal: mail notification is best-effort
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return { group, total, completed, inProgress, blocked, open };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Print a group's progress in human-readable format.
|
|
324
|
+
* @internal Exported for testing.
|
|
325
|
+
*/
|
|
326
|
+
export function printGroupProgress(progress: TaskGroupProgress): void {
|
|
327
|
+
const w = process.stdout.write.bind(process.stdout);
|
|
328
|
+
const { group, total, completed, inProgress, blocked, open } = progress;
|
|
329
|
+
const status = group.status === "completed" ? "[completed]" : "[active]";
|
|
330
|
+
w(`${group.name} (${accent(group.id)}) ${status}\n`);
|
|
331
|
+
w(` Issues: ${total} total`);
|
|
332
|
+
w(` | ${completed} completed`);
|
|
333
|
+
w(` | ${inProgress} in_progress`);
|
|
334
|
+
w(` | ${blocked} blocked`);
|
|
335
|
+
w(` | ${open} open\n`);
|
|
336
|
+
if (group.status === "completed" && group.completedAt) {
|
|
337
|
+
w(` Completed: ${group.completedAt}\n`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Create the Commander command for `ap group`.
|
|
343
|
+
*/
|
|
344
|
+
export function createGroupCommand(): Command {
|
|
345
|
+
const cmd = new Command("group").description("Manage task groups for batch coordination");
|
|
346
|
+
|
|
347
|
+
cmd
|
|
348
|
+
.command("create")
|
|
349
|
+
.description("Create a new task group")
|
|
350
|
+
.argument("<name>", "Group name")
|
|
351
|
+
.argument("<ids...>", "Issue IDs to include")
|
|
352
|
+
.option("--json", "Output as JSON")
|
|
353
|
+
.option("--skip-validation", "Skip task validation (for offline use)")
|
|
354
|
+
.action(
|
|
355
|
+
async (name: string, ids: string[], opts: { json?: boolean; skipValidation?: boolean }) => {
|
|
356
|
+
const config = await loadConfig(process.cwd());
|
|
357
|
+
const projectRoot = config.project.root;
|
|
358
|
+
const resolvedBackend = await resolveBackend(config.taskTracker.backend, projectRoot);
|
|
359
|
+
const tracker = createTrackerClient(resolvedBackend, projectRoot);
|
|
360
|
+
|
|
361
|
+
const group = await createGroup(
|
|
362
|
+
projectRoot,
|
|
363
|
+
name,
|
|
364
|
+
ids,
|
|
365
|
+
opts.skipValidation ?? false,
|
|
366
|
+
tracker,
|
|
367
|
+
);
|
|
368
|
+
if (opts.json) {
|
|
369
|
+
jsonOutput("group create", { ...group });
|
|
370
|
+
} else {
|
|
371
|
+
printSuccess("Created group", group.name);
|
|
372
|
+
process.stdout.write(
|
|
373
|
+
` Members: ${group.memberIssueIds.map((id) => accent(id)).join(", ")}\n`,
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
cmd
|
|
380
|
+
.command("status")
|
|
381
|
+
.description("Show progress for one or all groups")
|
|
382
|
+
.argument("[group-id-or-name]", "Group ID or name (optional, shows all if omitted)")
|
|
383
|
+
.option("--json", "Output as JSON")
|
|
384
|
+
.option("--skip-validation", "Skip task validation (for offline use)")
|
|
385
|
+
.action(
|
|
386
|
+
async (groupId: string | undefined, opts: { json?: boolean; skipValidation?: boolean }) => {
|
|
387
|
+
const config = await loadConfig(process.cwd());
|
|
388
|
+
const projectRoot = config.project.root;
|
|
389
|
+
const resolvedBackend = await resolveBackend(config.taskTracker.backend, projectRoot);
|
|
390
|
+
const tracker = createTrackerClient(resolvedBackend, projectRoot);
|
|
391
|
+
const json = opts.json ?? false;
|
|
392
|
+
|
|
393
|
+
const groups = await loadGroups(projectRoot);
|
|
394
|
+
|
|
395
|
+
if (groupId) {
|
|
396
|
+
const group = resolveGroup(groups, groupId);
|
|
397
|
+
const progress = await getGroupProgress(projectRoot, group, groups, tracker);
|
|
398
|
+
if (json) {
|
|
399
|
+
jsonOutput("group status", { ...progress });
|
|
400
|
+
} else {
|
|
401
|
+
printGroupProgress(progress);
|
|
402
|
+
}
|
|
403
|
+
} else {
|
|
404
|
+
const activeGroups = groups.filter((g) => g.status === "active");
|
|
405
|
+
if (activeGroups.length === 0) {
|
|
406
|
+
if (json) {
|
|
407
|
+
jsonOutput("group status", { groups: [] });
|
|
408
|
+
} else {
|
|
409
|
+
printHint("No active groups");
|
|
410
|
+
}
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
const progressList: TaskGroupProgress[] = [];
|
|
414
|
+
for (const group of activeGroups) {
|
|
415
|
+
const progress = await getGroupProgress(projectRoot, group, groups, tracker);
|
|
416
|
+
progressList.push(progress);
|
|
417
|
+
}
|
|
418
|
+
if (json) {
|
|
419
|
+
jsonOutput("group status", { groups: progressList });
|
|
420
|
+
} else {
|
|
421
|
+
for (const progress of progressList) {
|
|
422
|
+
printGroupProgress(progress);
|
|
423
|
+
process.stdout.write("\n");
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
cmd
|
|
431
|
+
.command("add")
|
|
432
|
+
.description("Add issues to a group")
|
|
433
|
+
.argument("<group-id-or-name>", "Group ID or name")
|
|
434
|
+
.argument("<ids...>", "Issue IDs to add")
|
|
435
|
+
.option("--json", "Output as JSON")
|
|
436
|
+
.option("--skip-validation", "Skip task validation (for offline use)")
|
|
437
|
+
.action(
|
|
438
|
+
async (
|
|
439
|
+
groupId: string,
|
|
440
|
+
ids: string[],
|
|
441
|
+
opts: { json?: boolean; skipValidation?: boolean },
|
|
442
|
+
) => {
|
|
443
|
+
const config = await loadConfig(process.cwd());
|
|
444
|
+
const projectRoot = config.project.root;
|
|
445
|
+
const resolvedBackend = await resolveBackend(config.taskTracker.backend, projectRoot);
|
|
446
|
+
const tracker = createTrackerClient(resolvedBackend, projectRoot);
|
|
447
|
+
|
|
448
|
+
const group = await addToGroup(
|
|
449
|
+
projectRoot,
|
|
450
|
+
groupId,
|
|
451
|
+
ids,
|
|
452
|
+
opts.skipValidation ?? false,
|
|
453
|
+
tracker,
|
|
454
|
+
);
|
|
455
|
+
if (opts.json) {
|
|
456
|
+
jsonOutput("group add", { ...group });
|
|
457
|
+
} else {
|
|
458
|
+
printSuccess("Added to group", group.name);
|
|
459
|
+
process.stdout.write(
|
|
460
|
+
` Members: ${group.memberIssueIds.map((id) => accent(id)).join(", ")}\n`,
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
cmd
|
|
467
|
+
.command("remove")
|
|
468
|
+
.description("Remove issues from a group")
|
|
469
|
+
.argument("<group-id-or-name>", "Group ID or name")
|
|
470
|
+
.argument("<ids...>", "Issue IDs to remove")
|
|
471
|
+
.option("--json", "Output as JSON")
|
|
472
|
+
.action(async (groupId: string, ids: string[], opts: { json?: boolean }) => {
|
|
473
|
+
const config = await loadConfig(process.cwd());
|
|
474
|
+
const projectRoot = config.project.root;
|
|
475
|
+
|
|
476
|
+
const group = await removeFromGroup(projectRoot, groupId, ids);
|
|
477
|
+
if (opts.json) {
|
|
478
|
+
jsonOutput("group remove", { ...group });
|
|
479
|
+
} else {
|
|
480
|
+
printSuccess("Removed from group", group.name);
|
|
481
|
+
process.stdout.write(
|
|
482
|
+
` Members: ${group.memberIssueIds.map((id) => accent(id)).join(", ")}\n`,
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
cmd
|
|
488
|
+
.command("list")
|
|
489
|
+
.description("List all groups (summary)")
|
|
490
|
+
.option("--json", "Output as JSON")
|
|
491
|
+
.action(async (opts: { json?: boolean }) => {
|
|
492
|
+
const config = await loadConfig(process.cwd());
|
|
493
|
+
const projectRoot = config.project.root;
|
|
494
|
+
|
|
495
|
+
const groups = await loadGroups(projectRoot);
|
|
496
|
+
if (groups.length === 0) {
|
|
497
|
+
if (opts.json) {
|
|
498
|
+
process.stdout.write("[]\n");
|
|
499
|
+
} else {
|
|
500
|
+
printHint("No groups");
|
|
501
|
+
}
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
if (opts.json) {
|
|
505
|
+
jsonOutput("group list", { groups });
|
|
506
|
+
} else {
|
|
507
|
+
for (const group of groups) {
|
|
508
|
+
const status = group.status === "completed" ? "[completed]" : "[active]";
|
|
509
|
+
process.stdout.write(
|
|
510
|
+
`${accent(group.id)} ${status} "${group.name}" (${group.memberIssueIds.length} issues)\n`,
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
return cmd;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Entry point for `ap group <subcommand>`.
|
|
521
|
+
*/
|
|
522
|
+
export async function groupCommand(args: string[]): Promise<void> {
|
|
523
|
+
const cmd = createGroupCommand();
|
|
524
|
+
cmd.exitOverride();
|
|
525
|
+
|
|
526
|
+
if (args.length === 0) {
|
|
527
|
+
process.stdout.write(cmd.helpInformation());
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
try {
|
|
532
|
+
await cmd.parseAsync(args, { from: "user" });
|
|
533
|
+
} catch (err: unknown) {
|
|
534
|
+
if (err && typeof err === "object" && "code" in err) {
|
|
535
|
+
const code = (err as { code: string }).code;
|
|
536
|
+
if (code === "commander.helpDisplayed" || code === "commander.version") {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
if (code === "commander.unknownCommand") {
|
|
540
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
541
|
+
throw new ValidationError(message, { field: "subcommand" });
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
throw err;
|
|
545
|
+
}
|
|
546
|
+
}
|