@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,355 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import type { Config, PlanTemplate, SectionSpec } from "./types.ts";
|
|
4
|
+
import { CONFIG_FILE, DEFAULT_MAX_PLAN_DEPTH, SECTION_KINDS, SPROUT_DIR_NAME } from "./types.ts";
|
|
5
|
+
import { parseYaml, stringifyYaml, type YamlValue } from "./yaml.ts";
|
|
6
|
+
|
|
7
|
+
export async function readConfig(sproutDir: string): Promise<Config> {
|
|
8
|
+
const file = Bun.file(join(sproutDir, CONFIG_FILE));
|
|
9
|
+
const content = await file.text();
|
|
10
|
+
const data = parseYaml(content);
|
|
11
|
+
const config: Config = {
|
|
12
|
+
project: typeof data.project === "string" ? data.project : "sprout",
|
|
13
|
+
version: typeof data.version === "string" ? data.version : "1",
|
|
14
|
+
};
|
|
15
|
+
if (typeof data.max_plan_depth === "number" && Number.isInteger(data.max_plan_depth)) {
|
|
16
|
+
config.max_plan_depth = data.max_plan_depth;
|
|
17
|
+
}
|
|
18
|
+
return config;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function writeConfig(sproutDir: string, config: Config): Promise<void> {
|
|
22
|
+
const out: Record<string, YamlValue> = { project: config.project, version: config.version };
|
|
23
|
+
if (config.max_plan_depth !== undefined) out.max_plan_depth = config.max_plan_depth;
|
|
24
|
+
await Bun.write(join(sproutDir, CONFIG_FILE), stringifyYaml(out));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function maxPlanDepth(config: Config): number {
|
|
28
|
+
return config.max_plan_depth ?? DEFAULT_MAX_PLAN_DEPTH;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function gitCommonDir(cwd: string): string | null {
|
|
32
|
+
try {
|
|
33
|
+
const result = Bun.spawnSync(["git", "rev-parse", "--git-common-dir"], {
|
|
34
|
+
cwd,
|
|
35
|
+
stdout: "pipe",
|
|
36
|
+
stderr: "pipe",
|
|
37
|
+
});
|
|
38
|
+
if ((result.exitCode ?? 0) !== 0) return null;
|
|
39
|
+
const raw = new TextDecoder().decode(result.stdout).trim();
|
|
40
|
+
if (!raw) return null;
|
|
41
|
+
return resolve(cwd, raw);
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function gitDir(cwd: string): string | null {
|
|
48
|
+
try {
|
|
49
|
+
const result = Bun.spawnSync(["git", "rev-parse", "--git-dir"], {
|
|
50
|
+
cwd,
|
|
51
|
+
stdout: "pipe",
|
|
52
|
+
stderr: "pipe",
|
|
53
|
+
});
|
|
54
|
+
if ((result.exitCode ?? 0) !== 0) return null;
|
|
55
|
+
const raw = new TextDecoder().decode(result.stdout).trim();
|
|
56
|
+
if (!raw) return null;
|
|
57
|
+
return resolve(cwd, raw);
|
|
58
|
+
} catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function resolveWorktreeRoot(candidateSproutDir: string): string {
|
|
64
|
+
const candidateRoot = dirname(candidateSproutDir);
|
|
65
|
+
const common = gitCommonDir(candidateRoot);
|
|
66
|
+
if (!common) return candidateSproutDir;
|
|
67
|
+
|
|
68
|
+
// .git/worktrees/<name> → strip to repo root; .git → already main
|
|
69
|
+
const mainRoot = common.endsWith(".git") ? dirname(common) : dirname(dirname(common));
|
|
70
|
+
|
|
71
|
+
const mainResolved = resolve(mainRoot);
|
|
72
|
+
if (mainResolved === resolve(candidateRoot)) return candidateSproutDir;
|
|
73
|
+
|
|
74
|
+
const mainSproutDir = join(mainResolved, SPROUT_DIR_NAME);
|
|
75
|
+
if (existsSync(join(mainSproutDir, CONFIG_FILE))) {
|
|
76
|
+
return mainSproutDir;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return candidateSproutDir;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function isInsideWorktree(dir?: string): boolean {
|
|
83
|
+
const cwd = dir ?? process.cwd();
|
|
84
|
+
// In a linked worktree, --git-dir points to .git/worktrees/<name> while
|
|
85
|
+
// --git-common-dir points to the main .git — they differ.
|
|
86
|
+
// In the main repo and in submodules, both return the same path.
|
|
87
|
+
const gd = gitDir(cwd);
|
|
88
|
+
const common = gitCommonDir(cwd);
|
|
89
|
+
if (!gd || !common) return false;
|
|
90
|
+
return gd !== common;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export async function findSproutDir(startDir?: string): Promise<string> {
|
|
94
|
+
let dir = startDir ?? process.cwd();
|
|
95
|
+
while (true) {
|
|
96
|
+
const configPath = join(dir, SPROUT_DIR_NAME, CONFIG_FILE);
|
|
97
|
+
const file = Bun.file(configPath);
|
|
98
|
+
if (await file.exists()) {
|
|
99
|
+
return resolveWorktreeRoot(join(dir, SPROUT_DIR_NAME));
|
|
100
|
+
}
|
|
101
|
+
const parent = dirname(dir);
|
|
102
|
+
if (parent === dir) {
|
|
103
|
+
throw new Error("Not in a sprout project. Run `sr init` first.");
|
|
104
|
+
}
|
|
105
|
+
dir = parent;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function projectRootFromSproutDir(sproutDir: string): string {
|
|
110
|
+
return dirname(sproutDir);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Built-in `feature` template (PLAN_SPEC.md:36-78, 313-325). Loaded when
|
|
114
|
+
// config.yaml has no `plan_templates:` block, or as a fallback when the user
|
|
115
|
+
// declares other templates without redefining `feature`.
|
|
116
|
+
export const BUILTIN_FEATURE_TEMPLATE: PlanTemplate = {
|
|
117
|
+
name: "feature",
|
|
118
|
+
description: "New capability or significant change. Default for type: feature.",
|
|
119
|
+
sections: {
|
|
120
|
+
context: {
|
|
121
|
+
required: true,
|
|
122
|
+
kind: "text",
|
|
123
|
+
min_length: 50,
|
|
124
|
+
prompt: "Why does this work need to happen? What problem or opportunity drives it?",
|
|
125
|
+
},
|
|
126
|
+
approach: {
|
|
127
|
+
required: true,
|
|
128
|
+
kind: "text",
|
|
129
|
+
prompt: "What's the chosen approach, and why this over alternatives?",
|
|
130
|
+
},
|
|
131
|
+
alternatives: {
|
|
132
|
+
required: false,
|
|
133
|
+
kind: "list",
|
|
134
|
+
item: {
|
|
135
|
+
name: { required: true, kind: "text", prompt: "" },
|
|
136
|
+
rejected_because: { required: true, kind: "text", prompt: "" },
|
|
137
|
+
},
|
|
138
|
+
prompt: "What other approaches were considered and rejected?",
|
|
139
|
+
},
|
|
140
|
+
steps: {
|
|
141
|
+
required: true,
|
|
142
|
+
kind: "steps",
|
|
143
|
+
min: 2,
|
|
144
|
+
prompt:
|
|
145
|
+
"Decompose into ordered, independent implementation steps. Each becomes a child seed.",
|
|
146
|
+
},
|
|
147
|
+
risks: {
|
|
148
|
+
required: false,
|
|
149
|
+
kind: "list",
|
|
150
|
+
item: "text",
|
|
151
|
+
loam_source: "failure",
|
|
152
|
+
prompt:
|
|
153
|
+
"What could go wrong? Known failure modes from prior work are pre-filled when loam is available.",
|
|
154
|
+
},
|
|
155
|
+
acceptance: {
|
|
156
|
+
required: true,
|
|
157
|
+
kind: "list",
|
|
158
|
+
item: "text",
|
|
159
|
+
min: 1,
|
|
160
|
+
prompt: "Concrete, verifiable conditions for plan completion.",
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// Built-in `bug` template (PLAN_SPEC.md:268). Defect-fix framing: requires
|
|
166
|
+
// reproduction + root_cause separately from the fix approach. Wired into the
|
|
167
|
+
// type → template mapping so seed.type=bug picks this template by default.
|
|
168
|
+
export const BUILTIN_BUG_TEMPLATE: PlanTemplate = {
|
|
169
|
+
name: "bug",
|
|
170
|
+
description: "Defect fix. Adds reproduction and root_cause sections. Default for type: bug.",
|
|
171
|
+
sections: {
|
|
172
|
+
context: {
|
|
173
|
+
required: true,
|
|
174
|
+
kind: "text",
|
|
175
|
+
prompt: "Why does fixing this matter? Who is affected and how?",
|
|
176
|
+
},
|
|
177
|
+
reproduction: {
|
|
178
|
+
required: true,
|
|
179
|
+
kind: "text",
|
|
180
|
+
min_length: 50,
|
|
181
|
+
prompt: "Concrete steps to reproduce. Inputs, environment, observed vs. expected.",
|
|
182
|
+
},
|
|
183
|
+
root_cause: {
|
|
184
|
+
required: true,
|
|
185
|
+
kind: "text",
|
|
186
|
+
min_length: 50,
|
|
187
|
+
prompt: "What's actually broken? Trace the defect to its source, not just the symptom.",
|
|
188
|
+
},
|
|
189
|
+
approach: {
|
|
190
|
+
required: true,
|
|
191
|
+
kind: "text",
|
|
192
|
+
prompt: "Chosen fix and the rationale for it over alternatives.",
|
|
193
|
+
},
|
|
194
|
+
steps: {
|
|
195
|
+
required: true,
|
|
196
|
+
kind: "steps",
|
|
197
|
+
min: 1,
|
|
198
|
+
prompt: "Ordered fix steps. Each becomes a child seed.",
|
|
199
|
+
},
|
|
200
|
+
acceptance: {
|
|
201
|
+
required: true,
|
|
202
|
+
kind: "list",
|
|
203
|
+
item: "text",
|
|
204
|
+
min: 1,
|
|
205
|
+
prompt: "Verifiable conditions: regression test, behavior, etc.",
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// Built-in `refactor` template (PLAN_SPEC.md:269). Internal restructuring with
|
|
211
|
+
// no observable behavior change; the invariant is the load-bearing field.
|
|
212
|
+
//
|
|
213
|
+
// Inference choice (PLAN_SPEC.md open question 4 / sprout-6730 decision):
|
|
214
|
+
// `refactor` is opt-in via `--template refactor` only. There is no `refactor`
|
|
215
|
+
// seed type in the wider sprout taxonomy, so seed.type → template mapping does
|
|
216
|
+
// not auto-route to this template. `bug` chose the opposite direction because
|
|
217
|
+
// `bug` is already a seed type.
|
|
218
|
+
export const BUILTIN_REFACTOR_TEMPLATE: PlanTemplate = {
|
|
219
|
+
name: "refactor",
|
|
220
|
+
description:
|
|
221
|
+
"Internal restructuring. Adds behavior_invariant (must stay equal). Opt-in via --template refactor.",
|
|
222
|
+
sections: {
|
|
223
|
+
context: {
|
|
224
|
+
required: true,
|
|
225
|
+
kind: "text",
|
|
226
|
+
prompt: "Why this refactor? What pain does it relieve?",
|
|
227
|
+
},
|
|
228
|
+
behavior_invariant: {
|
|
229
|
+
required: true,
|
|
230
|
+
kind: "text",
|
|
231
|
+
min_length: 50,
|
|
232
|
+
prompt:
|
|
233
|
+
"The contract that MUST remain equal across the refactor. Be specific — this is what acceptance tests verify.",
|
|
234
|
+
},
|
|
235
|
+
approach: {
|
|
236
|
+
required: true,
|
|
237
|
+
kind: "text",
|
|
238
|
+
prompt: "Chosen restructuring strategy.",
|
|
239
|
+
},
|
|
240
|
+
steps: {
|
|
241
|
+
required: true,
|
|
242
|
+
kind: "steps",
|
|
243
|
+
min: 1,
|
|
244
|
+
prompt: "Ordered restructuring steps. Each becomes a child seed.",
|
|
245
|
+
},
|
|
246
|
+
acceptance: {
|
|
247
|
+
required: true,
|
|
248
|
+
kind: "list",
|
|
249
|
+
item: "text",
|
|
250
|
+
min: 1,
|
|
251
|
+
prompt: "How we'll verify the invariant is preserved.",
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const BUILTIN_PLAN_TEMPLATES: Record<string, PlanTemplate> = {
|
|
257
|
+
feature: BUILTIN_FEATURE_TEMPLATE,
|
|
258
|
+
bug: BUILTIN_BUG_TEMPLATE,
|
|
259
|
+
refactor: BUILTIN_REFACTOR_TEMPLATE,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
export async function loadPlanTemplates(sproutDir: string): Promise<Record<string, PlanTemplate>> {
|
|
263
|
+
const file = Bun.file(join(sproutDir, CONFIG_FILE));
|
|
264
|
+
if (!(await file.exists())) {
|
|
265
|
+
return { ...BUILTIN_PLAN_TEMPLATES };
|
|
266
|
+
}
|
|
267
|
+
const content = await file.text();
|
|
268
|
+
const data = parseYaml(content);
|
|
269
|
+
const userBlock = data.plan_templates;
|
|
270
|
+
const builtins: Record<string, PlanTemplate> = { ...BUILTIN_PLAN_TEMPLATES };
|
|
271
|
+
if (!isPlainObject(userBlock)) return builtins;
|
|
272
|
+
|
|
273
|
+
const result: Record<string, PlanTemplate> = { ...builtins };
|
|
274
|
+
for (const [name, raw] of Object.entries(userBlock)) {
|
|
275
|
+
if (!isPlainObject(raw)) {
|
|
276
|
+
throw new Error(`plan_templates.${name} must be a mapping`);
|
|
277
|
+
}
|
|
278
|
+
const sectionsRaw = raw.sections;
|
|
279
|
+
if (!isPlainObject(sectionsRaw)) {
|
|
280
|
+
throw new Error(`plan_templates.${name}.sections must be a mapping`);
|
|
281
|
+
}
|
|
282
|
+
const sections: Record<string, SectionSpec> = {};
|
|
283
|
+
for (const [secName, secRaw] of Object.entries(sectionsRaw)) {
|
|
284
|
+
sections[secName] = parseSectionSpec(secRaw, `plan_templates.${name}.sections.${secName}`);
|
|
285
|
+
}
|
|
286
|
+
const tpl: PlanTemplate = { name, sections };
|
|
287
|
+
if (typeof raw.description === "string") tpl.description = raw.description;
|
|
288
|
+
result[name] = tpl;
|
|
289
|
+
}
|
|
290
|
+
return result;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function isPlainObject(v: unknown): v is Record<string, YamlValue> {
|
|
294
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function parseSectionSpec(raw: YamlValue, path: string): SectionSpec {
|
|
298
|
+
if (!isPlainObject(raw)) {
|
|
299
|
+
throw new Error(`${path}: must be a mapping`);
|
|
300
|
+
}
|
|
301
|
+
if (typeof raw.required !== "boolean") {
|
|
302
|
+
throw new Error(`${path}.required: must be a boolean (got: ${describe(raw.required)})`);
|
|
303
|
+
}
|
|
304
|
+
if (typeof raw.prompt !== "string") {
|
|
305
|
+
throw new Error(`${path}.prompt: must be a string (got: ${describe(raw.prompt)})`);
|
|
306
|
+
}
|
|
307
|
+
const kind = parseKind(raw.kind, `${path}.kind`);
|
|
308
|
+
const spec: SectionSpec = {
|
|
309
|
+
required: raw.required,
|
|
310
|
+
kind,
|
|
311
|
+
prompt: raw.prompt,
|
|
312
|
+
};
|
|
313
|
+
if (typeof raw.min_length === "number") spec.min_length = raw.min_length;
|
|
314
|
+
if (typeof raw.min === "number") spec.min = raw.min;
|
|
315
|
+
if (raw.item !== undefined) spec.item = parseItem(raw.item, `${path}.item`);
|
|
316
|
+
if (typeof raw.loam_source === "string") spec.loam_source = raw.loam_source;
|
|
317
|
+
return spec;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function parseKind(raw: YamlValue | undefined, path: string): SectionSpec["kind"] {
|
|
321
|
+
if (typeof raw === "string") {
|
|
322
|
+
if ((SECTION_KINDS as readonly string[]).includes(raw)) {
|
|
323
|
+
return raw as SectionSpec["kind"];
|
|
324
|
+
}
|
|
325
|
+
throw new Error(`${path}: unknown kind '${raw}' (expected ${SECTION_KINDS.join("|")}|object)`);
|
|
326
|
+
}
|
|
327
|
+
if (isPlainObject(raw)) {
|
|
328
|
+
const fields: Record<string, SectionSpec> = {};
|
|
329
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
330
|
+
fields[k] = parseSectionSpec(v, `${path}.${k}`);
|
|
331
|
+
}
|
|
332
|
+
return fields;
|
|
333
|
+
}
|
|
334
|
+
throw new Error(
|
|
335
|
+
`${path}: unknown kind '${describe(raw)}' (expected ${SECTION_KINDS.join("|")}|object)`,
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function parseItem(raw: YamlValue, path: string): "text" | Record<string, SectionSpec> {
|
|
340
|
+
if (raw === "text") return "text";
|
|
341
|
+
if (isPlainObject(raw)) {
|
|
342
|
+
const fields: Record<string, SectionSpec> = {};
|
|
343
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
344
|
+
fields[k] = parseSectionSpec(v, `${path}.${k}`);
|
|
345
|
+
}
|
|
346
|
+
return fields;
|
|
347
|
+
}
|
|
348
|
+
throw new Error(`${path}: must be 'text' or an object spec (got: ${describe(raw)})`);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function describe(v: unknown): string {
|
|
352
|
+
if (v === null || v === undefined) return "null";
|
|
353
|
+
if (Array.isArray(v)) return "array";
|
|
354
|
+
return typeof v;
|
|
355
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { Issue } from "./types.ts";
|
|
2
|
+
|
|
3
|
+
export interface IssueFilterOptions {
|
|
4
|
+
type?: string;
|
|
5
|
+
assignee?: string;
|
|
6
|
+
label?: string;
|
|
7
|
+
labelAny?: string;
|
|
8
|
+
unlabeled?: boolean;
|
|
9
|
+
priority?: Set<number>;
|
|
10
|
+
priorityMax?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Parse a --limit flag value.
|
|
15
|
+
*
|
|
16
|
+
* Returns the default when the flag is absent. Throws on negative, fractional,
|
|
17
|
+
* or non-numeric input. Accepts 0 (callers should treat 0 as "return no rows",
|
|
18
|
+
* not "use the default").
|
|
19
|
+
*/
|
|
20
|
+
export function parseLimitFlag(raw: unknown, defaultValue = 50): number {
|
|
21
|
+
if (raw === undefined || raw === null || raw === "") return defaultValue;
|
|
22
|
+
if (typeof raw !== "string") {
|
|
23
|
+
throw new Error(`Invalid --limit value: must be a non-negative integer`);
|
|
24
|
+
}
|
|
25
|
+
const trimmed = raw.trim();
|
|
26
|
+
if (!/^\d+$/.test(trimmed)) {
|
|
27
|
+
throw new Error(`Invalid --limit "${raw}": must be a non-negative integer`);
|
|
28
|
+
}
|
|
29
|
+
return Number.parseInt(trimmed, 10);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function splitLabels(value: string): string[] {
|
|
33
|
+
return value
|
|
34
|
+
.split(",")
|
|
35
|
+
.map((l) => l.trim().toLowerCase())
|
|
36
|
+
.filter(Boolean);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function parsePriorityToken(raw: string): number {
|
|
40
|
+
const trimmed = raw.trim();
|
|
41
|
+
const stripped = trimmed.toUpperCase().startsWith("P") ? trimmed.slice(1) : trimmed;
|
|
42
|
+
const n = Number.parseInt(stripped, 10);
|
|
43
|
+
if (Number.isNaN(n) || n < 0 || n > 4 || String(n) !== stripped) {
|
|
44
|
+
throw new Error(`Invalid priority "${trimmed}": must be 0-4 or P0-P4`);
|
|
45
|
+
}
|
|
46
|
+
return n;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function applyIssueFilters(issues: Issue[], opts: IssueFilterOptions): Issue[] {
|
|
50
|
+
let result = issues;
|
|
51
|
+
if (opts.type) result = result.filter((i) => i.type === opts.type);
|
|
52
|
+
if (opts.assignee) result = result.filter((i) => i.assignee === opts.assignee);
|
|
53
|
+
if (opts.label) {
|
|
54
|
+
const required = splitLabels(opts.label);
|
|
55
|
+
result = result.filter((i) => {
|
|
56
|
+
const labels = i.labels ?? [];
|
|
57
|
+
return required.every((r) => labels.includes(r));
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (opts.labelAny) {
|
|
61
|
+
const any = new Set(splitLabels(opts.labelAny));
|
|
62
|
+
result = result.filter((i) => {
|
|
63
|
+
const labels = i.labels ?? [];
|
|
64
|
+
return labels.some((l) => any.has(l));
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
if (opts.unlabeled) {
|
|
68
|
+
result = result.filter((i) => !i.labels || i.labels.length === 0);
|
|
69
|
+
}
|
|
70
|
+
if (opts.priority && opts.priority.size > 0) {
|
|
71
|
+
const set = opts.priority;
|
|
72
|
+
result = result.filter((i) => set.has(i.priority));
|
|
73
|
+
}
|
|
74
|
+
if (opts.priorityMax !== undefined) {
|
|
75
|
+
const max = opts.priorityMax;
|
|
76
|
+
result = result.filter((i) => i.priority <= max);
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function filterOptionsFromFlags(
|
|
82
|
+
flags: Record<string, string | boolean>,
|
|
83
|
+
): IssueFilterOptions {
|
|
84
|
+
let priority: Set<number> | undefined;
|
|
85
|
+
if (typeof flags.priority === "string") {
|
|
86
|
+
const tokens = flags.priority
|
|
87
|
+
.split(",")
|
|
88
|
+
.map((t) => t.trim())
|
|
89
|
+
.filter(Boolean);
|
|
90
|
+
if (tokens.length > 0) {
|
|
91
|
+
priority = new Set(tokens.map(parsePriorityToken));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const priorityMax =
|
|
95
|
+
typeof flags["priority-max"] === "string"
|
|
96
|
+
? parsePriorityToken(flags["priority-max"])
|
|
97
|
+
: undefined;
|
|
98
|
+
return {
|
|
99
|
+
type: typeof flags.type === "string" ? flags.type : undefined,
|
|
100
|
+
assignee: typeof flags.assignee === "string" ? flags.assignee : undefined,
|
|
101
|
+
label: typeof flags.label === "string" ? flags.label : undefined,
|
|
102
|
+
labelAny: typeof flags["label-any"] === "string" ? flags["label-any"] : undefined,
|
|
103
|
+
unlabeled: flags.unlabeled === true,
|
|
104
|
+
priority,
|
|
105
|
+
priorityMax,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type FormatMode = "markdown" | "compact" | "plain" | "ids" | "json";
|
|
2
|
+
|
|
3
|
+
export const VALID_FORMATS: readonly FormatMode[] = ["markdown", "compact", "plain", "ids", "json"];
|
|
4
|
+
|
|
5
|
+
export function isFormatMode(value: string): value is FormatMode {
|
|
6
|
+
return (VALID_FORMATS as readonly string[]).includes(value);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ResolvedFormat {
|
|
10
|
+
mode: FormatMode;
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function resolveFormat(args: string[]): ResolvedFormat {
|
|
15
|
+
let formatVal: string | undefined;
|
|
16
|
+
for (let i = 0; i < args.length; i++) {
|
|
17
|
+
const arg = args[i];
|
|
18
|
+
if (arg === "--format") {
|
|
19
|
+
const next = args[i + 1];
|
|
20
|
+
if (next !== undefined && !next.startsWith("--")) formatVal = next;
|
|
21
|
+
} else if (arg?.startsWith("--format=")) {
|
|
22
|
+
formatVal = arg.slice("--format=".length);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (formatVal !== undefined) {
|
|
26
|
+
if (!isFormatMode(formatVal)) {
|
|
27
|
+
return {
|
|
28
|
+
mode: "markdown",
|
|
29
|
+
error: `Invalid --format value: ${formatVal}. Valid: ${VALID_FORMATS.join("|")}`,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return { mode: formatVal };
|
|
33
|
+
}
|
|
34
|
+
if (args.includes("--json")) return { mode: "json" };
|
|
35
|
+
return { mode: "markdown" };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: \x1b is the ESC byte required to match real ANSI escape sequences
|
|
39
|
+
const ANSI_REGEX = /\x1b\[[0-9;]*m/g;
|
|
40
|
+
|
|
41
|
+
export function stripAnsi(s: string): string {
|
|
42
|
+
return s.replace(ANSI_REGEX, "");
|
|
43
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
export function generateId(prefix: string, existingIds: Set<string> | string[]): string {
|
|
4
|
+
const idSet = existingIds instanceof Set ? existingIds : new Set(existingIds);
|
|
5
|
+
const makeId = (hexLen: number) =>
|
|
6
|
+
`${prefix}-${randomBytes(Math.ceil(hexLen / 2))
|
|
7
|
+
.toString("hex")
|
|
8
|
+
.slice(0, hexLen)}`;
|
|
9
|
+
|
|
10
|
+
let attempts = 0;
|
|
11
|
+
while (attempts < 100) {
|
|
12
|
+
const id = makeId(4);
|
|
13
|
+
if (!idSet.has(id)) return id;
|
|
14
|
+
attempts++;
|
|
15
|
+
}
|
|
16
|
+
// Fallback to 8 hex chars after 100 collisions
|
|
17
|
+
let id = makeId(8);
|
|
18
|
+
while (idSet.has(id)) {
|
|
19
|
+
id = makeId(8);
|
|
20
|
+
}
|
|
21
|
+
return id;
|
|
22
|
+
}
|