@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,24 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
export function generateId(prefix: string, existingIds: string[]): string {
|
|
4
|
+
const existingSet = new Set(existingIds);
|
|
5
|
+
|
|
6
|
+
for (let attempt = 0; attempt < 100; attempt++) {
|
|
7
|
+
const hex = randomBytes(2).toString("hex");
|
|
8
|
+
const id = `${prefix}-${hex}`;
|
|
9
|
+
if (!existingSet.has(id)) {
|
|
10
|
+
return id;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Fallback to 8 hex chars after 100 collisions
|
|
15
|
+
for (let attempt = 0; attempt < 1000; attempt++) {
|
|
16
|
+
const hex = randomBytes(4).toString("hex");
|
|
17
|
+
const id = `${prefix}-${hex}`;
|
|
18
|
+
if (!existingSet.has(id)) {
|
|
19
|
+
return id;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
throw new Error(`Failed to generate unique ID with prefix "${prefix}"`);
|
|
24
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { Command, Help } from "commander";
|
|
4
|
+
import { VERSION } from "../../version.ts";
|
|
5
|
+
import { errorOut, isJsonMode, jsonOut, palette, setQuiet } from "./output.ts";
|
|
6
|
+
import { ExitError } from "./types.ts";
|
|
7
|
+
|
|
8
|
+
export { VERSION };
|
|
9
|
+
|
|
10
|
+
const t0 = performance.now();
|
|
11
|
+
|
|
12
|
+
const rawArgs = process.argv.slice(2);
|
|
13
|
+
|
|
14
|
+
// Apply quiet mode early (before any output)
|
|
15
|
+
if (rawArgs.includes("--quiet") || rawArgs.includes("-q")) {
|
|
16
|
+
setQuiet(true);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// --version --json: rich metadata output (before Commander processes version flag)
|
|
20
|
+
if ((rawArgs.includes("-v") || rawArgs.includes("--version")) && rawArgs.includes("--json")) {
|
|
21
|
+
const platform = `${process.platform}-${process.arch}`;
|
|
22
|
+
jsonOut({ name: "@ag-eco/trellis-cli", version: VERSION, runtime: "bun", platform });
|
|
23
|
+
if (rawArgs.includes("--timing")) {
|
|
24
|
+
const elapsed = Math.round(performance.now() - t0);
|
|
25
|
+
process.stderr.write(`[timing] ${elapsed}ms\n`);
|
|
26
|
+
}
|
|
27
|
+
process.exit();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const program = new Command();
|
|
31
|
+
program
|
|
32
|
+
.name("tl")
|
|
33
|
+
.description("Prompt management & composition")
|
|
34
|
+
.version(VERSION, "-v, --version", "Show version")
|
|
35
|
+
.option("-q, --quiet", "Suppress non-error output")
|
|
36
|
+
.option("--verbose", "Extra diagnostic output")
|
|
37
|
+
.option("--timing", "Show command execution time")
|
|
38
|
+
.option("--json", "Output as JSON")
|
|
39
|
+
.addHelpCommand(false)
|
|
40
|
+
.configureHelp({
|
|
41
|
+
formatHelp(cmd: Command, helper: Help): string {
|
|
42
|
+
if (cmd.parent) {
|
|
43
|
+
return Help.prototype.formatHelp.call(helper, cmd, helper);
|
|
44
|
+
}
|
|
45
|
+
const header = `${palette.brand(chalk.bold("trellis"))} ${palette.muted(`v${VERSION}`)} — Prompt management & composition\n\nUsage: tl <command> [options]`;
|
|
46
|
+
|
|
47
|
+
const cmdLines: string[] = ["\nCommands:"];
|
|
48
|
+
for (const sub of cmd.commands) {
|
|
49
|
+
const name = sub.name();
|
|
50
|
+
const argStr = sub.registeredArguments
|
|
51
|
+
.map((a) => (a.required ? `<${a.name()}>` : `[${a.name()}]`))
|
|
52
|
+
.join(" ");
|
|
53
|
+
const rawEntry = argStr ? `${name} ${argStr}` : name;
|
|
54
|
+
const colored = argStr ? `${chalk.green(name)} ${chalk.dim(argStr)}` : chalk.green(name);
|
|
55
|
+
const pad = " ".repeat(Math.max(18 - rawEntry.length, 2));
|
|
56
|
+
cmdLines.push(` ${colored}${pad}${sub.description()}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const opts: [string, string][] = [
|
|
60
|
+
["-h, --help", "Show help"],
|
|
61
|
+
["-v, --version", "Show version"],
|
|
62
|
+
["--json", "Output as JSON"],
|
|
63
|
+
["-q, --quiet", "Suppress non-error output"],
|
|
64
|
+
["--verbose", "Extra diagnostic output"],
|
|
65
|
+
["--timing", "Show command execution time"],
|
|
66
|
+
];
|
|
67
|
+
const optLines: string[] = ["\nOptions:"];
|
|
68
|
+
for (const [flag, desc] of opts) {
|
|
69
|
+
const pad = " ".repeat(Math.max(18 - flag.length, 2));
|
|
70
|
+
optLines.push(` ${chalk.dim(flag)}${pad}${desc}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const footer = `\nRun '${chalk.dim("tl")} <command> --help' for command-specific help.`;
|
|
74
|
+
|
|
75
|
+
return `${[header, ...cmdLines, ...optLines, footer].join("\n")}\n`;
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const { registerInitCommand } = await import("./commands/init.ts");
|
|
80
|
+
const { registerShowCommand } = await import("./commands/show.ts");
|
|
81
|
+
const { registerListCommand } = await import("./commands/list.ts");
|
|
82
|
+
const { registerArchiveCommand } = await import("./commands/archive.ts");
|
|
83
|
+
const { registerHistoryCommand } = await import("./commands/history.ts");
|
|
84
|
+
const { registerTreeCommand } = await import("./commands/tree.ts");
|
|
85
|
+
const { registerStatsCommand } = await import("./commands/stats.ts");
|
|
86
|
+
const { registerSyncCommand } = await import("./commands/sync.ts");
|
|
87
|
+
const { registerDiffCommand } = await import("./commands/diff.ts");
|
|
88
|
+
const { registerRenderCommand } = await import("./commands/render.ts");
|
|
89
|
+
const { registerCreateCommand } = await import("./commands/create.ts");
|
|
90
|
+
const { registerUpdateCommand } = await import("./commands/update.ts");
|
|
91
|
+
const { registerEmitCommand } = await import("./commands/emit.ts");
|
|
92
|
+
const { registerSchemaCommand } = await import("./commands/schema.ts");
|
|
93
|
+
const { registerValidateCommand } = await import("./commands/validate.ts");
|
|
94
|
+
const { registerConfigCommand } = await import("./commands/config.ts");
|
|
95
|
+
const { registerImportCommand } = await import("./commands/import.ts");
|
|
96
|
+
const { registerPrimeCommand } = await import("./commands/prime.ts");
|
|
97
|
+
const { registerOnboardCommand } = await import("./commands/onboard.ts");
|
|
98
|
+
const { registerPinCommand } = await import("./commands/pin.ts");
|
|
99
|
+
const { registerDoctorCommand } = await import("./commands/doctor.ts");
|
|
100
|
+
const { registerUpgradeCommand } = await import("./commands/upgrade.ts");
|
|
101
|
+
const { registerCompletionsCommand } = await import("./commands/completions.ts");
|
|
102
|
+
|
|
103
|
+
registerInitCommand(program);
|
|
104
|
+
registerShowCommand(program);
|
|
105
|
+
registerListCommand(program);
|
|
106
|
+
registerArchiveCommand(program);
|
|
107
|
+
registerHistoryCommand(program);
|
|
108
|
+
registerTreeCommand(program);
|
|
109
|
+
registerStatsCommand(program);
|
|
110
|
+
registerSyncCommand(program);
|
|
111
|
+
registerDiffCommand(program);
|
|
112
|
+
registerRenderCommand(program);
|
|
113
|
+
registerCreateCommand(program);
|
|
114
|
+
registerUpdateCommand(program);
|
|
115
|
+
registerEmitCommand(program);
|
|
116
|
+
registerSchemaCommand(program);
|
|
117
|
+
registerValidateCommand(program);
|
|
118
|
+
registerConfigCommand(program);
|
|
119
|
+
registerImportCommand(program);
|
|
120
|
+
registerPrimeCommand(program);
|
|
121
|
+
registerOnboardCommand(program);
|
|
122
|
+
registerPinCommand(program); // registers both pin and unpin
|
|
123
|
+
registerDoctorCommand(program);
|
|
124
|
+
registerUpgradeCommand(program);
|
|
125
|
+
registerCompletionsCommand(program);
|
|
126
|
+
|
|
127
|
+
// --- Typo suggestions via Levenshtein distance ---
|
|
128
|
+
|
|
129
|
+
function editDistance(a: string, b: string): number {
|
|
130
|
+
const m = a.length;
|
|
131
|
+
const n = b.length;
|
|
132
|
+
const dp = new Array<number>((m + 1) * (n + 1)).fill(0);
|
|
133
|
+
const idx = (i: number, j: number) => i * (n + 1) + j;
|
|
134
|
+
for (let i = 0; i <= m; i++) dp[idx(i, 0)] = i;
|
|
135
|
+
for (let j = 0; j <= n; j++) dp[idx(0, j)] = j;
|
|
136
|
+
for (let i = 1; i <= m; i++) {
|
|
137
|
+
for (let j = 1; j <= n; j++) {
|
|
138
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
139
|
+
const del = (dp[idx(i - 1, j)] ?? 0) + 1;
|
|
140
|
+
const ins = (dp[idx(i, j - 1)] ?? 0) + 1;
|
|
141
|
+
const sub = (dp[idx(i - 1, j - 1)] ?? 0) + cost;
|
|
142
|
+
dp[idx(i, j)] = Math.min(del, ins, sub);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return dp[idx(m, n)] ?? 0;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function suggestCommand(input: string): string | undefined {
|
|
149
|
+
const commands = program.commands.map((c) => c.name());
|
|
150
|
+
let bestMatch: string | undefined;
|
|
151
|
+
let bestDist = 3; // Only suggest if distance <= 2
|
|
152
|
+
for (const cmd of commands) {
|
|
153
|
+
const dist = editDistance(input, cmd);
|
|
154
|
+
if (dist < bestDist) {
|
|
155
|
+
bestDist = dist;
|
|
156
|
+
bestMatch = cmd;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return bestMatch;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
program.on("command:*", (operands) => {
|
|
163
|
+
const unknown = operands[0] ?? "";
|
|
164
|
+
const json = isJsonMode(rawArgs);
|
|
165
|
+
const suggestion = suggestCommand(unknown);
|
|
166
|
+
if (json) {
|
|
167
|
+
jsonOut({
|
|
168
|
+
success: false,
|
|
169
|
+
command: unknown,
|
|
170
|
+
error: `Unknown command: ${unknown}`,
|
|
171
|
+
suggestion: suggestion ?? undefined,
|
|
172
|
+
});
|
|
173
|
+
} else {
|
|
174
|
+
process.stderr.write(`Unknown command: ${unknown}\n`);
|
|
175
|
+
if (suggestion) {
|
|
176
|
+
process.stderr.write(`Did you mean '${suggestion}'?\n`);
|
|
177
|
+
}
|
|
178
|
+
process.stderr.write("Run 'tl --help' for usage.\n");
|
|
179
|
+
}
|
|
180
|
+
process.exitCode = 1;
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
program
|
|
184
|
+
.parseAsync(process.argv)
|
|
185
|
+
.then(() => {
|
|
186
|
+
if (program.opts().timing) {
|
|
187
|
+
const elapsed = Math.round(performance.now() - t0);
|
|
188
|
+
process.stderr.write(`[timing] ${elapsed}ms\n`);
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
.catch((err: unknown) => {
|
|
192
|
+
if (program.opts().timing) {
|
|
193
|
+
const elapsed = Math.round(performance.now() - t0);
|
|
194
|
+
process.stderr.write(`[timing] ${elapsed}ms\n`);
|
|
195
|
+
}
|
|
196
|
+
if (err instanceof ExitError) {
|
|
197
|
+
process.exitCode = err.exitCode;
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
201
|
+
const command = process.argv[2] ?? "";
|
|
202
|
+
const json = isJsonMode(process.argv.slice(2));
|
|
203
|
+
if (json) {
|
|
204
|
+
jsonOut({ success: false, command, error: msg });
|
|
205
|
+
} else {
|
|
206
|
+
errorOut(`Error: ${msg}`);
|
|
207
|
+
}
|
|
208
|
+
process.exitCode = 1;
|
|
209
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const START_MARKER = "<!-- trellis:start -->";
|
|
2
|
+
export const END_MARKER = "<!-- trellis:end -->";
|
|
3
|
+
|
|
4
|
+
export const ONBOARD_VERSION = 2;
|
|
5
|
+
export const VERSION_MARKER = `<!-- trellis-onboard-v:${String(ONBOARD_VERSION)} -->`;
|
|
6
|
+
|
|
7
|
+
export function hasMarkerSection(content: string): boolean {
|
|
8
|
+
return content.includes(START_MARKER) && content.includes(END_MARKER);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function detectStatus(content: string): "missing" | "current" | "outdated" {
|
|
12
|
+
if (!hasMarkerSection(content)) return "missing";
|
|
13
|
+
if (content.includes(VERSION_MARKER)) return "current";
|
|
14
|
+
return "outdated";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function replaceMarkerSection(content: string, newSection: string): string | null {
|
|
18
|
+
const startIdx = content.indexOf(START_MARKER);
|
|
19
|
+
const endIdx = content.indexOf(END_MARKER);
|
|
20
|
+
if (startIdx === -1 || endIdx === -1) return null;
|
|
21
|
+
const before = content.slice(0, startIdx);
|
|
22
|
+
const after = content.slice(endIdx + END_MARKER.length);
|
|
23
|
+
return before + wrapInMarkers(newSection) + after;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function wrapInMarkers(section: string): string {
|
|
27
|
+
return `${START_MARKER}\n${section}\n${END_MARKER}`;
|
|
28
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
|
|
3
|
+
let _quiet = false;
|
|
4
|
+
|
|
5
|
+
export function setQuiet(v: boolean): void {
|
|
6
|
+
_quiet = v;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function isQuiet(): boolean {
|
|
10
|
+
return _quiet;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function jsonOut(data: unknown): void {
|
|
14
|
+
if (_quiet) {
|
|
15
|
+
const isError =
|
|
16
|
+
data != null &&
|
|
17
|
+
typeof data === "object" &&
|
|
18
|
+
"success" in data &&
|
|
19
|
+
(data as Record<string, unknown>).success === false;
|
|
20
|
+
if (!isError) return;
|
|
21
|
+
}
|
|
22
|
+
console.log(JSON.stringify(data, null, 2));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function humanOut(text: string): void {
|
|
26
|
+
if (_quiet) return;
|
|
27
|
+
console.log(text);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function errorOut(msg: string): void {
|
|
31
|
+
console.error(msg);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function isJsonMode(args: string[]): boolean {
|
|
35
|
+
return args.includes("--json");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Brand palette — chalk instances (supports chaining e.g. palette.brand.bold(...))
|
|
39
|
+
// chalk handles NO_COLOR and TTY detection automatically
|
|
40
|
+
export const palette = {
|
|
41
|
+
brand: chalk.rgb(56, 142, 60), // Trellis deep green
|
|
42
|
+
accent: chalk.rgb(255, 183, 77), // amber — IDs and accents
|
|
43
|
+
muted: chalk.rgb(120, 120, 110), // stone gray — metadata
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Color helpers
|
|
47
|
+
export const c = {
|
|
48
|
+
bold: (s: string) => chalk.bold(s),
|
|
49
|
+
dim: (s: string) => chalk.dim(s),
|
|
50
|
+
green: (s: string) => palette.brand(s),
|
|
51
|
+
red: (s: string) => chalk.red(s),
|
|
52
|
+
yellow: (s: string) => palette.accent(s),
|
|
53
|
+
cyan: (s: string) => chalk.cyan(s),
|
|
54
|
+
blue: (s: string) => chalk.blue(s),
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Status icons: Set D (minimal, maximum terminal compatibility)
|
|
58
|
+
// Use these for list status indicators, not message prefixes
|
|
59
|
+
export const icons = {
|
|
60
|
+
pending: chalk.green("-"), // open / pending
|
|
61
|
+
active: chalk.cyan(">"), // in_progress / active
|
|
62
|
+
done: chalk.dim("x"), // closed / done
|
|
63
|
+
blocked: chalk.yellow("!"), // blocked / warning
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Message format helpers per visual-spec.md
|
|
67
|
+
export const fmt = {
|
|
68
|
+
// brand bold ✓ + brand message text
|
|
69
|
+
success: (msg: string) => `${palette.brand.bold("✓")} ${palette.brand(msg)}`,
|
|
70
|
+
// highlight an ID or reference in accent (amber)
|
|
71
|
+
id: (id: string) => palette.accent(id),
|
|
72
|
+
// yellow bold ! + yellow message + optional dim hint
|
|
73
|
+
warning: (msg: string, hint?: string) =>
|
|
74
|
+
hint
|
|
75
|
+
? `${chalk.yellow.bold("!")} ${chalk.yellow(msg)} ${chalk.dim(hint)}`
|
|
76
|
+
: `${chalk.yellow.bold("!")} ${chalk.yellow(msg)}`,
|
|
77
|
+
// red bold ✗ + red message + optional dim hint
|
|
78
|
+
error: (msg: string, hint?: string) =>
|
|
79
|
+
hint
|
|
80
|
+
? `${chalk.red.bold("✗")} ${chalk.red(msg)} ${chalk.dim(hint)}`
|
|
81
|
+
: `${chalk.red.bold("✗")} ${chalk.red(msg)}`,
|
|
82
|
+
// dim indented info/hint text
|
|
83
|
+
info: (msg: string) => chalk.dim(` ${msg}`),
|
|
84
|
+
};
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import type { LoamBlock, Prompt, Section } from "./types.ts";
|
|
2
|
+
import { MAX_INHERIT_DEPTH } from "./types.ts";
|
|
3
|
+
|
|
4
|
+
export interface RenderResult {
|
|
5
|
+
sections: Section[];
|
|
6
|
+
frontmatter: Record<string, unknown>;
|
|
7
|
+
resolvedFrom: string[];
|
|
8
|
+
version: number;
|
|
9
|
+
loam?: LoamBlock;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Resolve a prompt's full section list by walking the inheritance chain
|
|
14
|
+
* and applying mixins. Resolution order:
|
|
15
|
+
* 1. Resolve extends chain (parent first)
|
|
16
|
+
* 2. For each mixin (left-to-right), resolve it fully
|
|
17
|
+
* 3. Merge: base → mixin₁ → mixin₂ → … → focal prompt
|
|
18
|
+
* Later entries override earlier on section name conflicts.
|
|
19
|
+
*/
|
|
20
|
+
export function resolvePrompt(name: string, prompts: Prompt[], version?: number): RenderResult {
|
|
21
|
+
const visited: string[] = [];
|
|
22
|
+
return resolveInner(name, prompts, version, visited);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function resolveInner(
|
|
26
|
+
name: string,
|
|
27
|
+
prompts: Prompt[],
|
|
28
|
+
version: number | undefined,
|
|
29
|
+
visited: string[],
|
|
30
|
+
): RenderResult {
|
|
31
|
+
if (visited.includes(name)) {
|
|
32
|
+
throw new Error(`Circular inheritance: ${[...visited, name].join(" → ")}`);
|
|
33
|
+
}
|
|
34
|
+
if (visited.length >= MAX_INHERIT_DEPTH) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`Inheritance depth limit (${MAX_INHERIT_DEPTH}) exceeded at "${name}". Chain: ${visited.join(" → ")}`,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Find the prompt
|
|
41
|
+
const prompt = findPrompt(prompts, name, version);
|
|
42
|
+
if (!prompt) {
|
|
43
|
+
const versionStr = version !== undefined ? `@${version}` : "";
|
|
44
|
+
throw new Error(`Prompt "${name}${versionStr}" not found`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
visited.push(name);
|
|
48
|
+
|
|
49
|
+
// No parent and no mixins — return own sections (excluding empty-body removals)
|
|
50
|
+
if (!prompt.extends && (!prompt.mixins || prompt.mixins.length === 0)) {
|
|
51
|
+
const sections = prompt.sections.filter((s) => s.body !== "");
|
|
52
|
+
return withLoam(
|
|
53
|
+
{
|
|
54
|
+
sections,
|
|
55
|
+
frontmatter: prompt.frontmatter ?? {},
|
|
56
|
+
resolvedFrom: [name],
|
|
57
|
+
version: prompt.version,
|
|
58
|
+
},
|
|
59
|
+
prompt.loam,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Start with parent chain if exists
|
|
64
|
+
let baseSections: Section[] = [];
|
|
65
|
+
let baseFrontmatter: Record<string, unknown> = {};
|
|
66
|
+
let baseResolvedFrom: string[] = [];
|
|
67
|
+
let baseLoam: LoamBlock | undefined;
|
|
68
|
+
const mixinLoames: (LoamBlock | undefined)[] = [];
|
|
69
|
+
|
|
70
|
+
if (prompt.extends) {
|
|
71
|
+
const parentResult = resolveInner(prompt.extends, prompts, undefined, visited);
|
|
72
|
+
baseSections = parentResult.sections;
|
|
73
|
+
baseFrontmatter = parentResult.frontmatter;
|
|
74
|
+
baseResolvedFrom = parentResult.resolvedFrom;
|
|
75
|
+
baseLoam = parentResult.loam;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Apply each mixin left-to-right on top of the base
|
|
79
|
+
if (prompt.mixins && prompt.mixins.length > 0) {
|
|
80
|
+
for (const mixinName of prompt.mixins) {
|
|
81
|
+
// Each mixin resolves with its own visited-branch to allow
|
|
82
|
+
// the same ancestor to appear via extends AND a mixin (diamond).
|
|
83
|
+
// But we must still detect cycles involving the focal prompt.
|
|
84
|
+
const mixinVisited = [...visited];
|
|
85
|
+
const mixinResult = resolveInner(mixinName, prompts, undefined, mixinVisited);
|
|
86
|
+
baseSections = mergeSections(baseSections, mixinResult.sections);
|
|
87
|
+
baseFrontmatter = { ...baseFrontmatter, ...mixinResult.frontmatter };
|
|
88
|
+
baseResolvedFrom = [...baseResolvedFrom, ...mixinResult.resolvedFrom];
|
|
89
|
+
mixinLoames.push(mixinResult.loam);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Finally apply the focal prompt's own sections on top
|
|
94
|
+
const merged = mergeSections(baseSections, prompt.sections);
|
|
95
|
+
|
|
96
|
+
// Resolve loam with override-vs-merge semantics gated by extends_loam.
|
|
97
|
+
// extends_loam=true: union parent + each mixin's resolved loam + focal.loam
|
|
98
|
+
// (domains/files unioned, budget/on_empty last-wins).
|
|
99
|
+
// extends_loam=false (default): focal.loam wholesale overrides — no inheritance,
|
|
100
|
+
// not even from mixins. If focal has no loam block, result is undefined.
|
|
101
|
+
let resolvedLoam: LoamBlock | undefined;
|
|
102
|
+
if (prompt.extends_loam) {
|
|
103
|
+
resolvedLoam = baseLoam;
|
|
104
|
+
for (const m of mixinLoames) {
|
|
105
|
+
resolvedLoam = mergeLoam(resolvedLoam, m);
|
|
106
|
+
}
|
|
107
|
+
resolvedLoam = mergeLoam(resolvedLoam, prompt.loam);
|
|
108
|
+
} else {
|
|
109
|
+
resolvedLoam = prompt.loam;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return withLoam(
|
|
113
|
+
{
|
|
114
|
+
sections: merged,
|
|
115
|
+
frontmatter: { ...baseFrontmatter, ...(prompt.frontmatter ?? {}) },
|
|
116
|
+
resolvedFrom: [...baseResolvedFrom, name],
|
|
117
|
+
version: prompt.version,
|
|
118
|
+
},
|
|
119
|
+
resolvedLoam,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function withLoam(result: RenderResult, loam: LoamBlock | undefined): RenderResult {
|
|
124
|
+
if (loam === undefined) return result;
|
|
125
|
+
return { ...result, loam };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function mergeLoam(
|
|
129
|
+
base: LoamBlock | undefined,
|
|
130
|
+
overlay: LoamBlock | undefined,
|
|
131
|
+
): LoamBlock | undefined {
|
|
132
|
+
if (base === undefined) return overlay;
|
|
133
|
+
if (overlay === undefined) return base;
|
|
134
|
+
|
|
135
|
+
const merged: LoamBlock = {};
|
|
136
|
+
|
|
137
|
+
const baseDomains = base.prime?.domains;
|
|
138
|
+
const overlayDomains = overlay.prime?.domains;
|
|
139
|
+
const baseFiles = base.prime?.files;
|
|
140
|
+
const overlayFiles = overlay.prime?.files;
|
|
141
|
+
const domains =
|
|
142
|
+
baseDomains || overlayDomains ? unionPreserveOrder(baseDomains, overlayDomains) : undefined;
|
|
143
|
+
const files = baseFiles || overlayFiles ? unionPreserveOrder(baseFiles, overlayFiles) : undefined;
|
|
144
|
+
if (domains !== undefined || files !== undefined) {
|
|
145
|
+
merged.prime = {};
|
|
146
|
+
if (domains !== undefined) merged.prime.domains = domains;
|
|
147
|
+
if (files !== undefined) merged.prime.files = files;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const budget = overlay.budget !== undefined ? overlay.budget : base.budget;
|
|
151
|
+
if (budget !== undefined) merged.budget = budget;
|
|
152
|
+
|
|
153
|
+
const on_empty = overlay.on_empty !== undefined ? overlay.on_empty : base.on_empty;
|
|
154
|
+
if (on_empty !== undefined) merged.on_empty = on_empty;
|
|
155
|
+
|
|
156
|
+
return merged;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function unionPreserveOrder(a: string[] | undefined, b: string[] | undefined): string[] {
|
|
160
|
+
const seen = new Set<string>();
|
|
161
|
+
const out: string[] = [];
|
|
162
|
+
for (const item of a ?? []) {
|
|
163
|
+
if (!seen.has(item)) {
|
|
164
|
+
seen.add(item);
|
|
165
|
+
out.push(item);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
for (const item of b ?? []) {
|
|
169
|
+
if (!seen.has(item)) {
|
|
170
|
+
seen.add(item);
|
|
171
|
+
out.push(item);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return out;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function findPrompt(prompts: Prompt[], name: string, version?: number): Prompt | undefined {
|
|
178
|
+
if (version !== undefined) {
|
|
179
|
+
return prompts.find((p) => p.name === name && p.version === version);
|
|
180
|
+
}
|
|
181
|
+
// Get latest version for this name
|
|
182
|
+
const candidates = prompts.filter((p) => p.name === name);
|
|
183
|
+
if (candidates.length === 0) return undefined;
|
|
184
|
+
return candidates.reduce((best, p) => (p.version > best.version ? p : best));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function mergeSections(parentSections: Section[], childSections: Section[]): Section[] {
|
|
188
|
+
// Start with parent sections
|
|
189
|
+
const result: Section[] = [...parentSections];
|
|
190
|
+
|
|
191
|
+
for (const childSection of childSections) {
|
|
192
|
+
const parentIdx = result.findIndex((s) => s.name === childSection.name);
|
|
193
|
+
|
|
194
|
+
if (childSection.body === "") {
|
|
195
|
+
// Empty body = remove the section
|
|
196
|
+
if (parentIdx !== -1) {
|
|
197
|
+
result.splice(parentIdx, 1);
|
|
198
|
+
}
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (parentIdx !== -1) {
|
|
203
|
+
// Override parent section
|
|
204
|
+
result[parentIdx] = childSection;
|
|
205
|
+
} else {
|
|
206
|
+
// Append new section
|
|
207
|
+
result.push(childSection);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return result;
|
|
212
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { closeSync, constants, openSync, renameSync, statSync, unlinkSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { LOCK_RETRY_MS, LOCK_STALE_MS, LOCK_TIMEOUT_MS } from "./types.ts";
|
|
5
|
+
|
|
6
|
+
function lockPath(filePath: string): string {
|
|
7
|
+
return `${filePath}.lock`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function acquireLock(filePath: string): Promise<void> {
|
|
11
|
+
const lock = lockPath(filePath);
|
|
12
|
+
const deadline = Date.now() + LOCK_TIMEOUT_MS;
|
|
13
|
+
|
|
14
|
+
while (Date.now() < deadline) {
|
|
15
|
+
try {
|
|
16
|
+
// O_CREAT | O_EXCL — atomic, fails if exists
|
|
17
|
+
const fd = openSync(lock, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY);
|
|
18
|
+
closeSync(fd);
|
|
19
|
+
return;
|
|
20
|
+
} catch (err: unknown) {
|
|
21
|
+
if ((err as NodeJS.ErrnoException).code !== "EEXIST") throw err;
|
|
22
|
+
|
|
23
|
+
// Check if stale
|
|
24
|
+
try {
|
|
25
|
+
const stat = statSync(lock);
|
|
26
|
+
const age = Date.now() - stat.mtimeMs;
|
|
27
|
+
if (age > LOCK_STALE_MS) {
|
|
28
|
+
unlinkSync(lock);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
} catch {
|
|
32
|
+
// Lock was removed between our check and stat — retry
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
await Bun.sleep(LOCK_RETRY_MS);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
throw new Error(`Timeout acquiring lock on ${filePath} after ${LOCK_TIMEOUT_MS}ms`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function releaseLock(filePath: string): void {
|
|
44
|
+
try {
|
|
45
|
+
unlinkSync(lockPath(filePath));
|
|
46
|
+
} catch {
|
|
47
|
+
// Best-effort release
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function readJsonl<T>(filePath: string): Promise<T[]> {
|
|
52
|
+
try {
|
|
53
|
+
const text = await Bun.file(filePath).text();
|
|
54
|
+
const records: T[] = [];
|
|
55
|
+
|
|
56
|
+
for (const line of text.split("\n")) {
|
|
57
|
+
const trimmed = line.trim();
|
|
58
|
+
if (!trimmed) continue;
|
|
59
|
+
try {
|
|
60
|
+
records.push(JSON.parse(trimmed) as T);
|
|
61
|
+
} catch {
|
|
62
|
+
// Skip malformed lines
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return records;
|
|
67
|
+
} catch {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function writeJsonl<T>(filePath: string, records: T[]): Promise<void> {
|
|
73
|
+
const dir = filePath.substring(0, filePath.lastIndexOf("/"));
|
|
74
|
+
const tmp = join(dir, `.jsonl.tmp.${randomBytes(4).toString("hex")}`);
|
|
75
|
+
|
|
76
|
+
const lines = records.map((r) => JSON.stringify(r)).join("\n");
|
|
77
|
+
const content = lines ? `${lines}\n` : "";
|
|
78
|
+
|
|
79
|
+
await Bun.write(tmp, content);
|
|
80
|
+
renameSync(tmp, filePath);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function appendJsonl<T>(filePath: string, record: T): Promise<void> {
|
|
84
|
+
const line = `${JSON.stringify(record)}\n`;
|
|
85
|
+
|
|
86
|
+
// Check if file exists and has content
|
|
87
|
+
let existing = "";
|
|
88
|
+
try {
|
|
89
|
+
existing = await Bun.file(filePath).text();
|
|
90
|
+
} catch {
|
|
91
|
+
existing = "";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const content = existing ? existing + line : line;
|
|
95
|
+
|
|
96
|
+
const dir = filePath.substring(0, filePath.lastIndexOf("/"));
|
|
97
|
+
const tmp = join(dir, `.jsonl.tmp.${randomBytes(4).toString("hex")}`);
|
|
98
|
+
|
|
99
|
+
await Bun.write(tmp, content);
|
|
100
|
+
renameSync(tmp, filePath);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Dedup records by ID + version — last occurrence wins.
|
|
105
|
+
* For getting current state, return only highest version per ID.
|
|
106
|
+
*/
|
|
107
|
+
export function dedupById<T extends { id: string; version: number }>(records: T[]): T[] {
|
|
108
|
+
const map = new Map<string, T>();
|
|
109
|
+
for (const record of records) {
|
|
110
|
+
const existing = map.get(record.id);
|
|
111
|
+
if (!existing || record.version >= existing.version) {
|
|
112
|
+
map.set(record.id, record);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return Array.from(map.values());
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Dedup records by ID — last occurrence wins (no version required).
|
|
120
|
+
* Use this for record types without version fields (e.g., schemas).
|
|
121
|
+
*/
|
|
122
|
+
export function dedupByIdLast<T extends { id: string }>(records: T[]): T[] {
|
|
123
|
+
const map = new Map<string, T>();
|
|
124
|
+
for (const record of records) {
|
|
125
|
+
map.set(record.id, record);
|
|
126
|
+
}
|
|
127
|
+
return Array.from(map.values());
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get all versions for a specific ID.
|
|
132
|
+
*/
|
|
133
|
+
export function getVersions<T extends { id: string; version: number }>(
|
|
134
|
+
records: T[],
|
|
135
|
+
id: string,
|
|
136
|
+
): T[] {
|
|
137
|
+
const seen = new Map<number, T>();
|
|
138
|
+
for (const record of records) {
|
|
139
|
+
if (record.id === id) {
|
|
140
|
+
seen.set(record.version, record);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return Array.from(seen.values()).sort((a, b) => a.version - b.version);
|
|
144
|
+
}
|