@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,382 @@
|
|
|
1
|
+
// `tl config` — schema-driven read/write surface for `.trellis/config.yaml`.
|
|
2
|
+
//
|
|
3
|
+
// Designed as warren V2's wire contract (warren ROADMAP R-10):
|
|
4
|
+
// - `tl config schema --json` emits the JSON Schema (warren auto-renders a form)
|
|
5
|
+
// - `tl config show [--path <p>]` reads the current value
|
|
6
|
+
// - `tl config set <path> <value>` validates + writes atomically (YAML-parsed)
|
|
7
|
+
// - `tl 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. Errors are thrown
|
|
11
|
+
// (never process.exit) so the lock's finally block runs and no stale .lock
|
|
12
|
+
// file is left behind (failure mx-4a480a, convention mx-301cd1).
|
|
13
|
+
|
|
14
|
+
import { randomBytes } from "node:crypto";
|
|
15
|
+
import { renameSync, unlinkSync } from "node:fs";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import Ajv, { type ErrorObject } from "ajv";
|
|
18
|
+
import type { Command } from "commander";
|
|
19
|
+
import { configSchema } from "../config-schema.ts";
|
|
20
|
+
import { errorOut, fmt, humanOut, jsonOut, palette } from "../output.ts";
|
|
21
|
+
import { acquireLock, releaseLock } from "../store.ts";
|
|
22
|
+
import { ExitError } from "../types.ts";
|
|
23
|
+
import {
|
|
24
|
+
parseScalarOrFlow,
|
|
25
|
+
parseYaml,
|
|
26
|
+
serializeYaml,
|
|
27
|
+
type YamlMap,
|
|
28
|
+
type YamlScalar,
|
|
29
|
+
} from "../yaml.ts";
|
|
30
|
+
|
|
31
|
+
function configPath(cwd: string): string {
|
|
32
|
+
return join(cwd, ".trellis", "config.yaml");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function pathParts(path: string): string[] {
|
|
36
|
+
const parts = path.split(".").filter((p) => p.length > 0);
|
|
37
|
+
if (parts.length === 0) throw new Error("Config path must be non-empty");
|
|
38
|
+
return parts;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function describeType(v: unknown): string {
|
|
42
|
+
if (v === null) return "null";
|
|
43
|
+
if (Array.isArray(v)) return "array";
|
|
44
|
+
return typeof v;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isPlainObject(v: unknown): v is Record<string, unknown> {
|
|
48
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Coerce the trellis string-only YamlMap into a typed view for schema validation.
|
|
52
|
+
// trellis's parseYaml returns strings for all scalars; the schema expects a
|
|
53
|
+
// boolean for `targets.<name>.default`, so map "true"/"false" → boolean here.
|
|
54
|
+
// Other fields stay as strings (project, version, dir) which matches the schema.
|
|
55
|
+
function toTypedView(raw: YamlMap): Record<string, YamlScalar> {
|
|
56
|
+
const out: Record<string, YamlScalar> = {};
|
|
57
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
58
|
+
if (k === "targets" && isPlainObject(v)) {
|
|
59
|
+
out.targets = coerceTargetsBlock(v as YamlMap);
|
|
60
|
+
} else {
|
|
61
|
+
out[k] = v as YamlScalar;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function coerceTargetsBlock(targets: YamlMap): Record<string, YamlScalar> {
|
|
68
|
+
const out: Record<string, YamlScalar> = {};
|
|
69
|
+
for (const [name, target] of Object.entries(targets)) {
|
|
70
|
+
if (!isPlainObject(target)) {
|
|
71
|
+
out[name] = target as YamlScalar;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const t: Record<string, YamlScalar> = {};
|
|
75
|
+
for (const [tk, tv] of Object.entries(target as YamlMap)) {
|
|
76
|
+
if (tk === "default" && (tv === "true" || tv === "false")) {
|
|
77
|
+
t[tk] = tv === "true";
|
|
78
|
+
} else {
|
|
79
|
+
t[tk] = tv as YamlScalar;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
out[name] = t;
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Convert the typed view back to trellis's string-only YamlMap for serialization.
|
|
88
|
+
// Booleans/numbers stringify (matches existing trellis convention: `default: "true"`
|
|
89
|
+
// stored in saveConfig). Arrays of non-strings get String()-coerced; the schema
|
|
90
|
+
// only allows string arrays (targets.<name>.tags), so this is lossless in practice.
|
|
91
|
+
function toYamlMap(data: Record<string, YamlScalar>): YamlMap {
|
|
92
|
+
const out: YamlMap = {};
|
|
93
|
+
for (const [k, v] of Object.entries(data)) {
|
|
94
|
+
out[k] = scalarToYamlValue(v);
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function scalarToYamlValue(v: YamlScalar): YamlMap[string] {
|
|
100
|
+
if (v === null) return "";
|
|
101
|
+
if (typeof v === "boolean") return v ? "true" : "false";
|
|
102
|
+
if (typeof v === "number") return String(v);
|
|
103
|
+
if (typeof v === "string") return v;
|
|
104
|
+
if (Array.isArray(v)) return v.map((item) => String(item));
|
|
105
|
+
return toYamlMap(v as Record<string, YamlScalar>);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function getAtPath(data: YamlScalar, parts: string[]): YamlScalar | undefined {
|
|
109
|
+
let cur: YamlScalar = data;
|
|
110
|
+
for (const p of parts) {
|
|
111
|
+
if (cur === null || typeof cur !== "object" || Array.isArray(cur)) return undefined;
|
|
112
|
+
cur = (cur as Record<string, YamlScalar>)[p] as YamlScalar;
|
|
113
|
+
if (cur === undefined) return undefined;
|
|
114
|
+
}
|
|
115
|
+
return cur;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function setAtPath(data: Record<string, YamlScalar>, parts: string[], value: YamlScalar): void {
|
|
119
|
+
let cur: Record<string, YamlScalar> = data;
|
|
120
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
121
|
+
const p = parts[i];
|
|
122
|
+
if (p === undefined) continue;
|
|
123
|
+
const nxt = cur[p];
|
|
124
|
+
if (nxt === undefined || nxt === null) {
|
|
125
|
+
const empty: Record<string, YamlScalar> = {};
|
|
126
|
+
cur[p] = empty;
|
|
127
|
+
cur = empty;
|
|
128
|
+
} else if (typeof nxt === "object" && !Array.isArray(nxt)) {
|
|
129
|
+
cur = nxt as Record<string, YamlScalar>;
|
|
130
|
+
} else {
|
|
131
|
+
const soFar = parts.slice(0, i + 1).join(".");
|
|
132
|
+
throw new Error(`Cannot set: '${soFar}' is not an object (got: ${describeType(nxt)})`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const last = parts[parts.length - 1];
|
|
136
|
+
if (last !== undefined) cur[last] = value;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function unsetAtPath(data: Record<string, YamlScalar>, parts: string[]): boolean {
|
|
140
|
+
let cur: Record<string, YamlScalar> = data;
|
|
141
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
142
|
+
const p = parts[i];
|
|
143
|
+
if (p === undefined) continue;
|
|
144
|
+
const nxt = cur[p];
|
|
145
|
+
if (nxt === null || typeof nxt !== "object" || Array.isArray(nxt)) return false;
|
|
146
|
+
cur = nxt as Record<string, YamlScalar>;
|
|
147
|
+
}
|
|
148
|
+
const last = parts[parts.length - 1];
|
|
149
|
+
if (last === undefined) return false;
|
|
150
|
+
if (!(last in cur)) return false;
|
|
151
|
+
delete cur[last];
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function validateConfig(data: unknown): void {
|
|
156
|
+
// Strip the meta-schema URI: configSchema() advertises draft 2020-12 for
|
|
157
|
+
// downstream consumers (warren's UI), but AJV runs default draft-07 mode and
|
|
158
|
+
// rejects unknown $schema URIs under strict mode.
|
|
159
|
+
const { $schema: _meta, ...schema } = configSchema();
|
|
160
|
+
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
161
|
+
const validate = ajv.compile(schema);
|
|
162
|
+
if (!validate(data)) {
|
|
163
|
+
const lines = (validate.errors ?? []).map(formatAjvError);
|
|
164
|
+
throw new Error(`Config validation failed:\n${lines.join("\n")}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function formatAjvError(e: ErrorObject): string {
|
|
169
|
+
const base = e.instancePath.replace(/^\//, "").replace(/\//g, ".");
|
|
170
|
+
let path = base;
|
|
171
|
+
if (e.keyword === "required") {
|
|
172
|
+
const prop = (e.params as { missingProperty?: string }).missingProperty;
|
|
173
|
+
if (prop) path = base ? `${base}.${prop}` : prop;
|
|
174
|
+
}
|
|
175
|
+
return ` ${path || "(root)"}: ${e.message ?? e.keyword}`;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function readRawConfig(cwd: string): Promise<Record<string, YamlScalar>> {
|
|
179
|
+
const path = configPath(cwd);
|
|
180
|
+
const file = Bun.file(path);
|
|
181
|
+
if (!(await file.exists())) {
|
|
182
|
+
throw new Error(`Config not found at ${path}`);
|
|
183
|
+
}
|
|
184
|
+
const content = await file.text();
|
|
185
|
+
return toTypedView(parseYaml(content));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function writeRawConfig(cwd: string, data: Record<string, YamlScalar>): Promise<void> {
|
|
189
|
+
const path = configPath(cwd);
|
|
190
|
+
const tmpPath = `${path}.tmp.${randomBytes(4).toString("hex")}`;
|
|
191
|
+
try {
|
|
192
|
+
await Bun.write(tmpPath, serializeYaml(toYamlMap(data)));
|
|
193
|
+
renameSync(tmpPath, path);
|
|
194
|
+
} catch (err) {
|
|
195
|
+
try {
|
|
196
|
+
unlinkSync(tmpPath);
|
|
197
|
+
} catch {
|
|
198
|
+
// best-effort tmp cleanup
|
|
199
|
+
}
|
|
200
|
+
throw err;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function displayValue(v: YamlScalar): string {
|
|
205
|
+
if (typeof v === "string") return v;
|
|
206
|
+
return JSON.stringify(v);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function runSchema(jsonMode: boolean): Promise<void> {
|
|
210
|
+
const schema = configSchema();
|
|
211
|
+
if (jsonMode) {
|
|
212
|
+
console.log(JSON.stringify(schema));
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
console.log(JSON.stringify(schema, null, 2));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function runShow(pathArg: string | undefined, jsonMode: boolean): Promise<void> {
|
|
219
|
+
const cwd = process.cwd();
|
|
220
|
+
let raw: Record<string, YamlScalar>;
|
|
221
|
+
try {
|
|
222
|
+
raw = await readRawConfig(cwd);
|
|
223
|
+
} catch (err) {
|
|
224
|
+
emitError("config show", err, jsonMode);
|
|
225
|
+
throw new ExitError(1);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (pathArg) {
|
|
229
|
+
const parts = pathParts(pathArg);
|
|
230
|
+
const value = getAtPath(raw as YamlScalar, parts);
|
|
231
|
+
if (value === undefined) {
|
|
232
|
+
emitError("config show", new Error(`Path not found: ${pathArg}`), jsonMode);
|
|
233
|
+
throw new ExitError(1);
|
|
234
|
+
}
|
|
235
|
+
if (jsonMode) {
|
|
236
|
+
jsonOut({ success: true, command: "config show", path: pathArg, value });
|
|
237
|
+
} else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
238
|
+
process.stdout.write(serializeYaml(toYamlMap(value as Record<string, YamlScalar>)));
|
|
239
|
+
} else if (typeof value === "string") {
|
|
240
|
+
console.log(value);
|
|
241
|
+
} else {
|
|
242
|
+
console.log(JSON.stringify(value));
|
|
243
|
+
}
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (jsonMode) {
|
|
248
|
+
jsonOut({ success: true, command: "config show", config: raw });
|
|
249
|
+
} else {
|
|
250
|
+
process.stdout.write(serializeYaml(toYamlMap(raw)));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async function runSet(pathArg: string, valueArg: string, jsonMode: boolean): Promise<void> {
|
|
255
|
+
const cwd = process.cwd();
|
|
256
|
+
const path = configPath(cwd);
|
|
257
|
+
|
|
258
|
+
let parts: string[];
|
|
259
|
+
let value: YamlScalar;
|
|
260
|
+
try {
|
|
261
|
+
parts = pathParts(pathArg);
|
|
262
|
+
value = parseScalarOrFlow(valueArg);
|
|
263
|
+
} catch (err) {
|
|
264
|
+
emitError("config set", err, jsonMode);
|
|
265
|
+
throw new ExitError(1);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
await acquireLock(path);
|
|
269
|
+
try {
|
|
270
|
+
let raw: Record<string, YamlScalar>;
|
|
271
|
+
try {
|
|
272
|
+
raw = await readRawConfig(cwd);
|
|
273
|
+
setAtPath(raw, parts, value);
|
|
274
|
+
validateConfig(raw);
|
|
275
|
+
await writeRawConfig(cwd, raw);
|
|
276
|
+
} catch (err) {
|
|
277
|
+
emitError("config set", err, jsonMode);
|
|
278
|
+
throw new ExitError(1);
|
|
279
|
+
}
|
|
280
|
+
} finally {
|
|
281
|
+
releaseLock(path);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (jsonMode) {
|
|
285
|
+
jsonOut({ success: true, command: "config set", path: pathArg, value });
|
|
286
|
+
} else {
|
|
287
|
+
humanOut(
|
|
288
|
+
`${fmt.success("Set")} ${palette.accent(pathArg)} ${palette.muted("=")} ${displayValue(value)}`,
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async function runUnset(pathArg: string, jsonMode: boolean): Promise<void> {
|
|
294
|
+
const cwd = process.cwd();
|
|
295
|
+
const path = configPath(cwd);
|
|
296
|
+
|
|
297
|
+
let parts: string[];
|
|
298
|
+
try {
|
|
299
|
+
parts = pathParts(pathArg);
|
|
300
|
+
} catch (err) {
|
|
301
|
+
emitError("config unset", err, jsonMode);
|
|
302
|
+
throw new ExitError(1);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
let removed = false;
|
|
306
|
+
await acquireLock(path);
|
|
307
|
+
try {
|
|
308
|
+
try {
|
|
309
|
+
const raw = await readRawConfig(cwd);
|
|
310
|
+
removed = unsetAtPath(raw, parts);
|
|
311
|
+
if (removed) {
|
|
312
|
+
validateConfig(raw);
|
|
313
|
+
await writeRawConfig(cwd, raw);
|
|
314
|
+
}
|
|
315
|
+
} catch (err) {
|
|
316
|
+
emitError("config unset", err, jsonMode);
|
|
317
|
+
throw new ExitError(1);
|
|
318
|
+
}
|
|
319
|
+
} finally {
|
|
320
|
+
releaseLock(path);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (jsonMode) {
|
|
324
|
+
jsonOut({ success: true, command: "config unset", path: pathArg, removed });
|
|
325
|
+
} else if (removed) {
|
|
326
|
+
humanOut(`${fmt.success("Unset")} ${palette.accent(pathArg)}`);
|
|
327
|
+
} else {
|
|
328
|
+
humanOut(palette.muted(`No such path: ${pathArg}`));
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function emitError(command: string, err: unknown, jsonMode: boolean): void {
|
|
333
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
334
|
+
if (jsonMode) {
|
|
335
|
+
jsonOut({ success: false, command, error: msg });
|
|
336
|
+
} else {
|
|
337
|
+
errorOut(`Error: ${msg}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export function registerConfigCommand(program: Command): void {
|
|
342
|
+
const config = program
|
|
343
|
+
.command("config")
|
|
344
|
+
.description("Read, write, and inspect .trellis/config.yaml");
|
|
345
|
+
|
|
346
|
+
config
|
|
347
|
+
.command("schema")
|
|
348
|
+
.description("Emit the JSON Schema for .trellis/config.yaml")
|
|
349
|
+
.option("--json", "Compact single-line JSON (default is pretty-printed)")
|
|
350
|
+
.action(async (opts: Record<string, unknown>) => {
|
|
351
|
+
const jsonMode = opts.json === true || program.opts().json === true;
|
|
352
|
+
await runSchema(jsonMode);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
config
|
|
356
|
+
.command("show")
|
|
357
|
+
.description("Print the current config (or a value at --path)")
|
|
358
|
+
.option("--path <path>", "Dot-path to read (e.g. targets.default.dir)")
|
|
359
|
+
.option("--json", "Output as JSON")
|
|
360
|
+
.action(async (opts: Record<string, unknown>) => {
|
|
361
|
+
const jsonMode = opts.json === true || program.opts().json === true;
|
|
362
|
+
await runShow(opts.path as string | undefined, jsonMode);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
config
|
|
366
|
+
.command("set <path> <value>")
|
|
367
|
+
.description("Set a config value at <path>; <value> is YAML-parsed")
|
|
368
|
+
.option("--json", "Output as JSON")
|
|
369
|
+
.action(async (path: string, value: string, opts: Record<string, unknown>) => {
|
|
370
|
+
const jsonMode = opts.json === true || program.opts().json === true;
|
|
371
|
+
await runSet(path, value, jsonMode);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
config
|
|
375
|
+
.command("unset <path>")
|
|
376
|
+
.description("Remove the config value at <path>")
|
|
377
|
+
.option("--json", "Output as JSON")
|
|
378
|
+
.action(async (path: string, opts: Record<string, unknown>) => {
|
|
379
|
+
const jsonMode = opts.json === true || program.opts().json === true;
|
|
380
|
+
await runUnset(path, jsonMode);
|
|
381
|
+
});
|
|
382
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import type { Command } from "commander";
|
|
3
|
+
import { loadConfig } from "../config.ts";
|
|
4
|
+
import { generateId } from "../id.ts";
|
|
5
|
+
import { c, errorOut, fmt, humanOut, jsonOut } from "../output.ts";
|
|
6
|
+
import { acquireLock, appendJsonl, dedupById, readJsonl, releaseLock } from "../store.ts";
|
|
7
|
+
import type { Prompt, Section } from "../types.ts";
|
|
8
|
+
import { ExitError } from "../types.ts";
|
|
9
|
+
|
|
10
|
+
export default async function create(args: string[], json: boolean): Promise<void> {
|
|
11
|
+
const cwd = process.cwd();
|
|
12
|
+
const promptsPath = join(cwd, ".trellis", "prompts.jsonl");
|
|
13
|
+
|
|
14
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
15
|
+
humanOut(`Usage: tl create --name <name> [options]
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
--name <name> Prompt name (required)
|
|
19
|
+
--description <text> Short description
|
|
20
|
+
--extends <name> Inherit from parent prompt
|
|
21
|
+
--mixin <name> Add mixin prompt (repeatable)
|
|
22
|
+
--tag <tag> Add tag (repeatable)
|
|
23
|
+
--schema <name> Assign validation schema
|
|
24
|
+
--emit-as <filename> Custom emit filename
|
|
25
|
+
--emit-dir <path> Custom emit directory
|
|
26
|
+
--status draft|active Initial status (default: active)
|
|
27
|
+
--section <name> --body <text> Add section
|
|
28
|
+
--section <name>=<text> Add section (shorthand)
|
|
29
|
+
--fm <key=value> Set frontmatter field (repeatable)
|
|
30
|
+
--json Output as JSON`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Parse flags
|
|
35
|
+
let name = "";
|
|
36
|
+
let description: string | undefined;
|
|
37
|
+
let extendsName: string | undefined;
|
|
38
|
+
const mixins: string[] = [];
|
|
39
|
+
const tags: string[] = [];
|
|
40
|
+
let schema: string | undefined;
|
|
41
|
+
let emitAs: string | undefined;
|
|
42
|
+
let emitDir: string | undefined;
|
|
43
|
+
let status: "draft" | "active" = "active";
|
|
44
|
+
const sections: Section[] = [];
|
|
45
|
+
const frontmatterEntries: Record<string, string> = {};
|
|
46
|
+
|
|
47
|
+
for (let i = 0; i < args.length; i++) {
|
|
48
|
+
const arg = args[i];
|
|
49
|
+
if (arg === "--name" && args[i + 1]) {
|
|
50
|
+
name = args[++i] ?? "";
|
|
51
|
+
} else if (arg === "--description" && args[i + 1]) {
|
|
52
|
+
description = args[++i];
|
|
53
|
+
} else if (arg === "--extends" && args[i + 1]) {
|
|
54
|
+
extendsName = args[++i];
|
|
55
|
+
} else if (arg === "--mixin" && args[i + 1]) {
|
|
56
|
+
mixins.push(args[++i] ?? "");
|
|
57
|
+
} else if (arg === "--tag" && args[i + 1]) {
|
|
58
|
+
tags.push(args[++i] ?? "");
|
|
59
|
+
} else if (arg === "--schema" && args[i + 1]) {
|
|
60
|
+
schema = args[++i];
|
|
61
|
+
} else if (arg === "--emit-as" && args[i + 1]) {
|
|
62
|
+
emitAs = args[++i];
|
|
63
|
+
} else if (arg === "--emit-dir" && args[i + 1]) {
|
|
64
|
+
emitDir = args[++i];
|
|
65
|
+
} else if (arg === "--status" && args[i + 1]) {
|
|
66
|
+
const s = args[++i];
|
|
67
|
+
if (s === "draft" || s === "active") {
|
|
68
|
+
status = s;
|
|
69
|
+
}
|
|
70
|
+
} else if (arg === "--section" && args[i + 1]) {
|
|
71
|
+
const next = args[++i] ?? "";
|
|
72
|
+
const eqIdx = next.indexOf("=");
|
|
73
|
+
if (eqIdx !== -1) {
|
|
74
|
+
// --section name=body
|
|
75
|
+
const sName = next.slice(0, eqIdx);
|
|
76
|
+
const sBody = next.slice(eqIdx + 1);
|
|
77
|
+
if (sName) sections.push({ name: sName, body: sBody });
|
|
78
|
+
} else {
|
|
79
|
+
// --section name --body value
|
|
80
|
+
const sName = next;
|
|
81
|
+
if (args[i + 1] === "--body" && args[i + 2] !== undefined) {
|
|
82
|
+
i++; // skip --body
|
|
83
|
+
const sBody = args[++i] ?? "";
|
|
84
|
+
sections.push({ name: sName, body: sBody });
|
|
85
|
+
} else {
|
|
86
|
+
sections.push({ name: sName, body: "" });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} else if (arg === "--fm" && args[i + 1]) {
|
|
90
|
+
const fmArg = args[++i] ?? "";
|
|
91
|
+
const eqIdx = fmArg.indexOf("=");
|
|
92
|
+
if (eqIdx !== -1) {
|
|
93
|
+
const fmKey = fmArg.slice(0, eqIdx);
|
|
94
|
+
const fmValue = fmArg.slice(eqIdx + 1);
|
|
95
|
+
if (fmKey) frontmatterEntries[fmKey] = fmValue;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!name) {
|
|
101
|
+
if (json) {
|
|
102
|
+
jsonOut({ success: false, command: "create", error: "--name is required" });
|
|
103
|
+
} else {
|
|
104
|
+
errorOut("--name is required");
|
|
105
|
+
}
|
|
106
|
+
throw new ExitError(1);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const config = await loadConfig(cwd);
|
|
110
|
+
|
|
111
|
+
await acquireLock(promptsPath);
|
|
112
|
+
try {
|
|
113
|
+
const allRecords = await readJsonl<Prompt>(promptsPath);
|
|
114
|
+
const current = dedupById(allRecords);
|
|
115
|
+
|
|
116
|
+
// Check for name collision
|
|
117
|
+
const exists = current.find((p) => p.name === name && p.status !== "archived");
|
|
118
|
+
if (exists) {
|
|
119
|
+
if (json) {
|
|
120
|
+
jsonOut({
|
|
121
|
+
success: false,
|
|
122
|
+
command: "create",
|
|
123
|
+
error: `Prompt name '${name}' already exists`,
|
|
124
|
+
});
|
|
125
|
+
} else {
|
|
126
|
+
errorOut(`Prompt name '${name}' already exists`);
|
|
127
|
+
}
|
|
128
|
+
throw new ExitError(1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Validate parent if specified
|
|
132
|
+
if (extendsName) {
|
|
133
|
+
const parent = current.find((p) => p.name === extendsName);
|
|
134
|
+
if (!parent) {
|
|
135
|
+
if (json) {
|
|
136
|
+
jsonOut({
|
|
137
|
+
success: false,
|
|
138
|
+
command: "create",
|
|
139
|
+
error: `Parent prompt '${extendsName}' not found`,
|
|
140
|
+
});
|
|
141
|
+
} else {
|
|
142
|
+
errorOut(`Parent prompt '${extendsName}' not found`);
|
|
143
|
+
}
|
|
144
|
+
throw new ExitError(1);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Validate mixins if specified
|
|
149
|
+
for (const mixinName of mixins) {
|
|
150
|
+
const mixin = current.find((p) => p.name === mixinName);
|
|
151
|
+
if (!mixin) {
|
|
152
|
+
if (json) {
|
|
153
|
+
jsonOut({
|
|
154
|
+
success: false,
|
|
155
|
+
command: "create",
|
|
156
|
+
error: `Mixin prompt '${mixinName}' not found`,
|
|
157
|
+
});
|
|
158
|
+
} else {
|
|
159
|
+
errorOut(`Mixin prompt '${mixinName}' not found`);
|
|
160
|
+
}
|
|
161
|
+
throw new ExitError(1);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const id = generateId(
|
|
166
|
+
config.project,
|
|
167
|
+
current.map((p) => p.id),
|
|
168
|
+
);
|
|
169
|
+
const now = new Date().toISOString();
|
|
170
|
+
|
|
171
|
+
const prompt: Prompt = {
|
|
172
|
+
id,
|
|
173
|
+
name,
|
|
174
|
+
version: 1,
|
|
175
|
+
sections,
|
|
176
|
+
status,
|
|
177
|
+
createdAt: now,
|
|
178
|
+
updatedAt: now,
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
if (description) prompt.description = description;
|
|
182
|
+
if (extendsName) prompt.extends = extendsName;
|
|
183
|
+
if (mixins.length > 0) prompt.mixins = mixins;
|
|
184
|
+
if (tags.length > 0) prompt.tags = tags;
|
|
185
|
+
if (schema) prompt.schema = schema;
|
|
186
|
+
if (emitAs) prompt.emitAs = emitAs;
|
|
187
|
+
if (emitDir) prompt.emitDir = emitDir;
|
|
188
|
+
if (Object.keys(frontmatterEntries).length > 0) prompt.frontmatter = frontmatterEntries;
|
|
189
|
+
|
|
190
|
+
await appendJsonl(promptsPath, prompt);
|
|
191
|
+
|
|
192
|
+
if (json) {
|
|
193
|
+
jsonOut({ success: true, command: "create", id, name });
|
|
194
|
+
} else {
|
|
195
|
+
humanOut(`${fmt.success("Created prompt")} ${c.bold(name)} ${fmt.id(id)}`);
|
|
196
|
+
}
|
|
197
|
+
} finally {
|
|
198
|
+
releaseLock(promptsPath);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function registerCreateCommand(program: Command): void {
|
|
203
|
+
program
|
|
204
|
+
.command("create")
|
|
205
|
+
.description("Create a new prompt")
|
|
206
|
+
.requiredOption("--name <name>", "Prompt name")
|
|
207
|
+
.option("--description <text>", "Short description")
|
|
208
|
+
.option("--extends <name>", "Inherit from parent prompt")
|
|
209
|
+
.option(
|
|
210
|
+
"--mixin <name>",
|
|
211
|
+
"Add mixin prompt (repeatable)",
|
|
212
|
+
(v: string, a: string[]) => a.concat([v]),
|
|
213
|
+
[] as string[],
|
|
214
|
+
)
|
|
215
|
+
.option(
|
|
216
|
+
"--tag <tag>",
|
|
217
|
+
"Add tag (repeatable)",
|
|
218
|
+
(v: string, a: string[]) => a.concat([v]),
|
|
219
|
+
[] as string[],
|
|
220
|
+
)
|
|
221
|
+
.option("--schema <name>", "Assign validation schema")
|
|
222
|
+
.option("--emit-as <filename>", "Custom emit filename")
|
|
223
|
+
.option("--emit-dir <path>", "Custom emit directory")
|
|
224
|
+
.option("--status <status>", "Initial status (draft|active)", "active")
|
|
225
|
+
.option(
|
|
226
|
+
"--section <name=body>",
|
|
227
|
+
"Add section (name=body shorthand, repeatable)",
|
|
228
|
+
(v: string, a: string[]) => a.concat([v]),
|
|
229
|
+
[] as string[],
|
|
230
|
+
)
|
|
231
|
+
.option(
|
|
232
|
+
"--fm <key=value>",
|
|
233
|
+
"Set frontmatter field (repeatable)",
|
|
234
|
+
(v: string, a: string[]) => a.concat([v]),
|
|
235
|
+
[] as string[],
|
|
236
|
+
)
|
|
237
|
+
.action(async (opts: Record<string, unknown>) => {
|
|
238
|
+
const json: boolean = program.opts().json ?? false;
|
|
239
|
+
const args: string[] = ["--name", opts.name as string];
|
|
240
|
+
if (opts.description) args.push("--description", opts.description as string);
|
|
241
|
+
if (opts.extends) args.push("--extends", opts.extends as string);
|
|
242
|
+
for (const mixin of opts.mixin as string[]) args.push("--mixin", mixin);
|
|
243
|
+
for (const tag of opts.tag as string[]) args.push("--tag", tag);
|
|
244
|
+
if (opts.schema) args.push("--schema", opts.schema as string);
|
|
245
|
+
if (opts.emitAs) args.push("--emit-as", opts.emitAs as string);
|
|
246
|
+
if (opts.emitDir) args.push("--emit-dir", opts.emitDir as string);
|
|
247
|
+
if (opts.status) args.push("--status", opts.status as string);
|
|
248
|
+
for (const section of opts.section as string[]) args.push("--section", section);
|
|
249
|
+
for (const fm of opts.fm as string[]) args.push("--fm", fm);
|
|
250
|
+
await create(args, json);
|
|
251
|
+
});
|
|
252
|
+
}
|