@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,305 @@
|
|
|
1
|
+
// Pi runtime adapter for agentplate's AgentRuntime interface.
|
|
2
|
+
// Implements the AgentRuntime contract for the `pi` CLI (Mario Zechner's Pi coding agent).
|
|
3
|
+
|
|
4
|
+
import { mkdir } from "node:fs/promises";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import type { PiRuntimeConfig, ResolvedModel } from "../types.ts";
|
|
7
|
+
import { generatePiGuardExtension } from "./pi-guards.ts";
|
|
8
|
+
import type {
|
|
9
|
+
AgentRuntime,
|
|
10
|
+
HooksDef,
|
|
11
|
+
OverlayContent,
|
|
12
|
+
ReadyState,
|
|
13
|
+
SpawnOpts,
|
|
14
|
+
TranscriptSummary,
|
|
15
|
+
} from "./types.ts";
|
|
16
|
+
|
|
17
|
+
/** Default Pi runtime config used when no config is provided. */
|
|
18
|
+
const DEFAULT_PI_CONFIG: PiRuntimeConfig = {
|
|
19
|
+
provider: "anthropic",
|
|
20
|
+
modelMap: {
|
|
21
|
+
opus: "anthropic/claude-opus-4-6",
|
|
22
|
+
sonnet: "anthropic/claude-sonnet-4-6",
|
|
23
|
+
haiku: "anthropic/claude-haiku-4-5",
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Pi runtime adapter.
|
|
29
|
+
*
|
|
30
|
+
* Implements AgentRuntime for the `pi` CLI (Mario Zechner's Pi coding agent).
|
|
31
|
+
* Security is enforced via Pi guard extensions rather than permission-mode flags —
|
|
32
|
+
* Pi has no --permission-mode equivalent.
|
|
33
|
+
*/
|
|
34
|
+
export class PiRuntime implements AgentRuntime {
|
|
35
|
+
/** Unique identifier for this runtime. */
|
|
36
|
+
readonly id = "pi";
|
|
37
|
+
|
|
38
|
+
/** Stability level. Pi adapter is experimental — not fully validated. */
|
|
39
|
+
readonly stability = "experimental" as const;
|
|
40
|
+
|
|
41
|
+
/** Relative path to the instruction file within a worktree. Pi reads AGENTS.md at startup. */
|
|
42
|
+
readonly instructionPath = "AGENTS.md";
|
|
43
|
+
|
|
44
|
+
private readonly config: PiRuntimeConfig;
|
|
45
|
+
|
|
46
|
+
constructor(config?: PiRuntimeConfig) {
|
|
47
|
+
this.config = config ?? DEFAULT_PI_CONFIG;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Expand a model alias to a provider-qualified model ID.
|
|
52
|
+
*
|
|
53
|
+
* 1. If model contains "/" → already qualified, pass through
|
|
54
|
+
* 2. If model is in modelMap → return the mapped value
|
|
55
|
+
* 3. Otherwise → return `${provider}/${model}`
|
|
56
|
+
*/
|
|
57
|
+
expandModel(model: string): string {
|
|
58
|
+
if (model.includes("/")) return model;
|
|
59
|
+
const mapped = this.config.modelMap[model];
|
|
60
|
+
if (mapped) return mapped;
|
|
61
|
+
return `${this.config.provider}/${model}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Build the shell command string to spawn an interactive Pi agent.
|
|
66
|
+
*
|
|
67
|
+
* Maps SpawnOpts to the `pi` CLI flags:
|
|
68
|
+
* - `model` → `--model <model>`
|
|
69
|
+
* - `permissionMode` is accepted but NOT mapped — Pi has no permission-mode flag.
|
|
70
|
+
* Security is enforced via guard extensions deployed by deployConfig().
|
|
71
|
+
* - `appendSystemPrompt` → `--append-system-prompt '<escaped>'` (POSIX single-quote escaping)
|
|
72
|
+
*
|
|
73
|
+
* The `cwd` and `env` fields are handled by the tmux session creator, not embedded here.
|
|
74
|
+
*
|
|
75
|
+
* @param opts - Spawn options (model, appendSystemPrompt; permissionMode is ignored)
|
|
76
|
+
* @returns Shell command string suitable for tmux new-session -c
|
|
77
|
+
*/
|
|
78
|
+
buildSpawnCommand(opts: SpawnOpts): string {
|
|
79
|
+
let cmd = `pi --model ${this.expandModel(opts.model)}`;
|
|
80
|
+
|
|
81
|
+
if (opts.appendSystemPromptFile) {
|
|
82
|
+
// Read from file at shell expansion time — avoids tmux command length limits.
|
|
83
|
+
const escaped = opts.appendSystemPromptFile.replace(/'/g, "'\\''");
|
|
84
|
+
cmd += ` --append-system-prompt "$(cat '${escaped}')"`;
|
|
85
|
+
} else if (opts.appendSystemPrompt) {
|
|
86
|
+
// POSIX single-quote escape: end quote, backslash-quote, start quote.
|
|
87
|
+
const escaped = opts.appendSystemPrompt.replace(/'/g, "'\\''");
|
|
88
|
+
cmd += ` --append-system-prompt '${escaped}'`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return cmd;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Build the argv array for a headless one-shot Pi invocation.
|
|
96
|
+
*
|
|
97
|
+
* Returns an argv array suitable for `Bun.spawn()`. The `--print` flag causes Pi
|
|
98
|
+
* to run the prompt and exit. Unlike Claude Code, the prompt is a positional argument
|
|
99
|
+
* (last), not passed via `-p`.
|
|
100
|
+
*
|
|
101
|
+
* @param prompt - The prompt to pass as a positional argument
|
|
102
|
+
* @param model - Optional model override
|
|
103
|
+
* @returns Argv array for Bun.spawn
|
|
104
|
+
*/
|
|
105
|
+
buildPrintCommand(prompt: string, model?: string): string[] {
|
|
106
|
+
const cmd = ["pi", "--print"];
|
|
107
|
+
if (model !== undefined) {
|
|
108
|
+
cmd.push("--model", this.expandModel(model));
|
|
109
|
+
}
|
|
110
|
+
cmd.push(prompt);
|
|
111
|
+
return cmd;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Deploy per-agent instructions and guards to a worktree.
|
|
116
|
+
*
|
|
117
|
+
* Writes up to three files:
|
|
118
|
+
* 1. `AGENTS.md` — agent's task-specific overlay. Skipped when overlay is undefined.
|
|
119
|
+
* 2. `.pi/extensions/agentplate-guard.ts` — Pi guard extension (always deployed).
|
|
120
|
+
* 3. `.pi/settings.json` — Pi settings enabling the extensions directory (always deployed).
|
|
121
|
+
*
|
|
122
|
+
* @param worktreePath - Absolute path to the agent's git worktree
|
|
123
|
+
* @param overlay - Overlay content to write as AGENTS.md, or undefined for guard-only deployment
|
|
124
|
+
* @param hooks - Agent identity, capability, worktree path, and optional quality gates
|
|
125
|
+
*/
|
|
126
|
+
async deployConfig(
|
|
127
|
+
worktreePath: string,
|
|
128
|
+
overlay: OverlayContent | undefined,
|
|
129
|
+
hooks: HooksDef,
|
|
130
|
+
): Promise<void> {
|
|
131
|
+
if (overlay) {
|
|
132
|
+
await mkdir(worktreePath, { recursive: true });
|
|
133
|
+
await Bun.write(join(worktreePath, this.instructionPath), overlay.content);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Always deploy Pi guard extension.
|
|
137
|
+
const piExtDir = join(worktreePath, ".pi", "extensions");
|
|
138
|
+
await mkdir(piExtDir, { recursive: true });
|
|
139
|
+
await Bun.write(join(piExtDir, "agentplate-guard.ts"), generatePiGuardExtension(hooks));
|
|
140
|
+
|
|
141
|
+
// Always deploy Pi settings pointing at the extensions directory.
|
|
142
|
+
const piDir = join(worktreePath, ".pi");
|
|
143
|
+
const settings = { extensions: ["./extensions"] };
|
|
144
|
+
await Bun.write(join(piDir, "settings.json"), `${JSON.stringify(settings, null, "\t")}\n`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Pi does not require beacon verification/resend.
|
|
149
|
+
*
|
|
150
|
+
* Claude Code's TUI sometimes swallows Enter during late initialization, so the
|
|
151
|
+
* orchestrator resends the beacon until the pane leaves the "idle" state. Pi's TUI
|
|
152
|
+
* does not have this issue AND its idle vs. processing states are indistinguishable
|
|
153
|
+
* via detectReady (the header "pi v..." and status bar token counter are visible in
|
|
154
|
+
* both states). Enabling the resend loop would spam Pi with duplicate beacon messages.
|
|
155
|
+
*/
|
|
156
|
+
requiresBeaconVerification(): boolean {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Detect Pi TUI readiness from a tmux pane content snapshot.
|
|
162
|
+
*
|
|
163
|
+
* Pi shows a header containing "pi" and "model:" when the TUI has fully rendered.
|
|
164
|
+
* Pi has no trust dialog phase.
|
|
165
|
+
*
|
|
166
|
+
* @param paneContent - Captured tmux pane content to analyze
|
|
167
|
+
* @returns Current readiness phase
|
|
168
|
+
*/
|
|
169
|
+
detectReady(paneContent: string): ReadyState {
|
|
170
|
+
// Pi's TUI shows "pi v<version>" in the header and a status bar with
|
|
171
|
+
// a token usage indicator like "0.0%/200k" or "0.0%/1.0M" when fully rendered.
|
|
172
|
+
// The context window size uses k-scale (e.g. 200k) for smaller models and
|
|
173
|
+
// M-scale (e.g. 1.0M) for Opus/Sonnet with 1M+ context windows.
|
|
174
|
+
const hasHeader = paneContent.includes("pi v");
|
|
175
|
+
const hasStatusBar = /\d+\.\d+%\/[\d.]+[kKmM]/.test(paneContent);
|
|
176
|
+
if (hasHeader && hasStatusBar) {
|
|
177
|
+
return { phase: "ready" };
|
|
178
|
+
}
|
|
179
|
+
return { phase: "loading" };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Parse a Pi transcript JSONL file into normalized token usage.
|
|
184
|
+
*
|
|
185
|
+
* Pi JSONL format (version 3):
|
|
186
|
+
* - Session metadata: `{ type: "session", version: 3, id, cwd }`
|
|
187
|
+
* - Model identity: `{ type: "model_change", provider, modelId }`
|
|
188
|
+
* - Token usage: on `{ type: "message" }` events where `message.role === "assistant"`,
|
|
189
|
+
* nested under `message.usage`: `{ input, output, cacheRead, cacheWrite, totalTokens, cost }`
|
|
190
|
+
* - Cost data: `message.usage.cost.total` (USD)
|
|
191
|
+
*
|
|
192
|
+
* Returns null if the file does not exist or cannot be parsed.
|
|
193
|
+
*
|
|
194
|
+
* @param path - Absolute path to the Pi transcript JSONL file
|
|
195
|
+
* @returns Aggregated token usage, or null if unavailable
|
|
196
|
+
*/
|
|
197
|
+
async parseTranscript(path: string): Promise<TranscriptSummary | null> {
|
|
198
|
+
const file = Bun.file(path);
|
|
199
|
+
if (!(await file.exists())) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
const text = await file.text();
|
|
205
|
+
const lines = text.split("\n").filter((l) => l.trim().length > 0);
|
|
206
|
+
|
|
207
|
+
let inputTokens = 0;
|
|
208
|
+
let outputTokens = 0;
|
|
209
|
+
let model = "";
|
|
210
|
+
|
|
211
|
+
for (const line of lines) {
|
|
212
|
+
let entry: Record<string, unknown>;
|
|
213
|
+
try {
|
|
214
|
+
entry = JSON.parse(line) as Record<string, unknown>;
|
|
215
|
+
} catch {
|
|
216
|
+
// Skip malformed lines — Pi transcripts may have partial writes.
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Model identity from model_change events.
|
|
221
|
+
if (entry.type === "model_change") {
|
|
222
|
+
if (typeof entry.modelId === "string") {
|
|
223
|
+
model = entry.modelId;
|
|
224
|
+
} else if (typeof entry.model === "string") {
|
|
225
|
+
model = entry.model;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Token usage from assistant message events.
|
|
230
|
+
// Pi v3 format: message.usage.input / message.usage.output
|
|
231
|
+
if (entry.type === "message") {
|
|
232
|
+
const msg = entry.message as Record<string, unknown> | undefined;
|
|
233
|
+
if (msg?.role === "assistant") {
|
|
234
|
+
const usage = msg.usage as Record<string, unknown> | undefined;
|
|
235
|
+
if (usage) {
|
|
236
|
+
if (typeof usage.input === "number") {
|
|
237
|
+
inputTokens += usage.input;
|
|
238
|
+
}
|
|
239
|
+
if (typeof usage.output === "number") {
|
|
240
|
+
outputTokens += usage.output;
|
|
241
|
+
}
|
|
242
|
+
// Also count cache tokens toward input for compatibility.
|
|
243
|
+
if (typeof usage.cacheRead === "number") {
|
|
244
|
+
inputTokens += usage.cacheRead;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Capture model from message if model_change was missed.
|
|
249
|
+
if (typeof msg.model === "string" && model === "") {
|
|
250
|
+
model = msg.model;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Fallback: message_end events (older Pi versions).
|
|
256
|
+
if (entry.type === "message_end") {
|
|
257
|
+
if (typeof entry.inputTokens === "number") {
|
|
258
|
+
inputTokens += entry.inputTokens;
|
|
259
|
+
}
|
|
260
|
+
if (typeof entry.outputTokens === "number") {
|
|
261
|
+
outputTokens += entry.outputTokens;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return { inputTokens, outputTokens, model };
|
|
267
|
+
} catch {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Build runtime-specific environment variables for model/provider routing.
|
|
274
|
+
*
|
|
275
|
+
* Returns the provider environment variables from the resolved model, or an empty
|
|
276
|
+
* object if none are set.
|
|
277
|
+
*
|
|
278
|
+
* @param model - Resolved model with optional provider env vars
|
|
279
|
+
* @returns Environment variable map (may be empty)
|
|
280
|
+
*/
|
|
281
|
+
buildEnv(model: ResolvedModel): Record<string, string> {
|
|
282
|
+
return model.env ?? {};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Return the directory containing Pi session transcript files.
|
|
287
|
+
*
|
|
288
|
+
* Pi stores JSONL transcripts in `~/.pi/agent/sessions/{encoded-project-path}/`.
|
|
289
|
+
* The project path is encoded by replacing path separators with `--` and
|
|
290
|
+
* prefixing/suffixing with `--`.
|
|
291
|
+
*
|
|
292
|
+
* Example: `/home/user/project` → `~/.pi/agent/sessions/--home-user-project--/`
|
|
293
|
+
*
|
|
294
|
+
* @param projectRoot - Absolute path to the project root
|
|
295
|
+
* @returns Absolute path to the transcript directory
|
|
296
|
+
*/
|
|
297
|
+
getTranscriptDir(projectRoot: string): string | null {
|
|
298
|
+
const home = process.env.HOME ?? process.env.USERPROFILE;
|
|
299
|
+
if (!home) return null;
|
|
300
|
+
|
|
301
|
+
// Pi encodes the project path: replace separators with dashes, wrap with --
|
|
302
|
+
const encoded = `--${projectRoot.replace(/[\\/]/g, "-").replace(/:/g, "")}--`;
|
|
303
|
+
return join(home, ".pi", "agent", "sessions", encoded);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import type { AgentplateConfig } from "../types.ts";
|
|
3
|
+
import { ClaudeRuntime } from "./claude.ts";
|
|
4
|
+
import { CodexRuntime } from "./codex.ts";
|
|
5
|
+
import { CopilotRuntime } from "./copilot.ts";
|
|
6
|
+
import { CursorRuntime } from "./cursor.ts";
|
|
7
|
+
import { GeminiRuntime } from "./gemini.ts";
|
|
8
|
+
import { OpenCodeRuntime } from "./opencode.ts";
|
|
9
|
+
import { PiRuntime } from "./pi.ts";
|
|
10
|
+
import { getRuntime } from "./registry.ts";
|
|
11
|
+
|
|
12
|
+
describe("getRuntime", () => {
|
|
13
|
+
it("returns a ClaudeRuntime by default (no args)", () => {
|
|
14
|
+
const runtime = getRuntime();
|
|
15
|
+
expect(runtime).toBeInstanceOf(ClaudeRuntime);
|
|
16
|
+
expect(runtime.id).toBe("claude");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('returns a ClaudeRuntime when name is "claude"', () => {
|
|
20
|
+
const runtime = getRuntime("claude");
|
|
21
|
+
expect(runtime).toBeInstanceOf(ClaudeRuntime);
|
|
22
|
+
expect(runtime.id).toBe("claude");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("throws with a helpful message for an unknown runtime", () => {
|
|
26
|
+
expect(() => getRuntime("unknown-runtime")).toThrow(
|
|
27
|
+
'Unknown runtime: "unknown-runtime". Available: aider, amp, claude, codex, copilot, cursor, gemini, goose, opencode, pi, sapling',
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("uses config.runtime.default when name is omitted", () => {
|
|
32
|
+
const config = { runtime: { default: "claude" } } as AgentplateConfig;
|
|
33
|
+
const runtime = getRuntime(undefined, config);
|
|
34
|
+
expect(runtime).toBeInstanceOf(ClaudeRuntime);
|
|
35
|
+
expect(runtime.id).toBe("claude");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("explicit name overrides config.runtime.default", () => {
|
|
39
|
+
const config = { runtime: { default: "claude" } } as AgentplateConfig;
|
|
40
|
+
// Both are "claude" here since that's the only registered runtime,
|
|
41
|
+
// but the name arg takes precedence over config.
|
|
42
|
+
const runtime = getRuntime("claude", config);
|
|
43
|
+
expect(runtime).toBeInstanceOf(ClaudeRuntime);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("resolves codex runtime from config default", () => {
|
|
47
|
+
const config = { runtime: { default: "codex" } } as AgentplateConfig;
|
|
48
|
+
const runtime = getRuntime(undefined, config);
|
|
49
|
+
expect(runtime).toBeInstanceOf(CodexRuntime);
|
|
50
|
+
expect(runtime.id).toBe("codex");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("returns a new instance on each call (factory pattern)", () => {
|
|
54
|
+
const a = getRuntime();
|
|
55
|
+
const b = getRuntime();
|
|
56
|
+
expect(a).not.toBe(b);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("returns PiRuntime when name is 'pi'", () => {
|
|
60
|
+
const runtime = getRuntime("pi");
|
|
61
|
+
expect(runtime).toBeInstanceOf(PiRuntime);
|
|
62
|
+
expect(runtime.id).toBe("pi");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("passes Pi config from AgentplateConfig to PiRuntime", () => {
|
|
66
|
+
const config = {
|
|
67
|
+
runtime: {
|
|
68
|
+
default: "pi",
|
|
69
|
+
pi: {
|
|
70
|
+
provider: "amazon-bedrock",
|
|
71
|
+
modelMap: {
|
|
72
|
+
opus: "amazon-bedrock/us.anthropic.claude-opus-4-6-v1",
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
} as unknown as AgentplateConfig;
|
|
77
|
+
const runtime = getRuntime(undefined, config) as PiRuntime;
|
|
78
|
+
expect(runtime).toBeInstanceOf(PiRuntime);
|
|
79
|
+
// Verify the config was applied by testing model expansion
|
|
80
|
+
expect(runtime.expandModel("opus")).toBe("amazon-bedrock/us.anthropic.claude-opus-4-6-v1");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("Pi runtime uses defaults when no Pi config in AgentplateConfig", () => {
|
|
84
|
+
const config = { runtime: { default: "pi" } } as AgentplateConfig;
|
|
85
|
+
const runtime = getRuntime(undefined, config) as PiRuntime;
|
|
86
|
+
expect(runtime).toBeInstanceOf(PiRuntime);
|
|
87
|
+
// Should use default anthropic mappings
|
|
88
|
+
expect(runtime.expandModel("sonnet")).toBe("anthropic/claude-sonnet-4-6");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("returns CopilotRuntime when name is 'copilot'", () => {
|
|
92
|
+
const runtime = getRuntime("copilot");
|
|
93
|
+
expect(runtime).toBeInstanceOf(CopilotRuntime);
|
|
94
|
+
expect(runtime.id).toBe("copilot");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("uses config.runtime.default 'copilot' when name is omitted", () => {
|
|
98
|
+
const config = { runtime: { default: "copilot" } } as AgentplateConfig;
|
|
99
|
+
const runtime = getRuntime(undefined, config);
|
|
100
|
+
expect(runtime).toBeInstanceOf(CopilotRuntime);
|
|
101
|
+
expect(runtime.id).toBe("copilot");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("copilot runtime returns a new instance on each call", () => {
|
|
105
|
+
const a = getRuntime("copilot");
|
|
106
|
+
const b = getRuntime("copilot");
|
|
107
|
+
expect(a).not.toBe(b);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("returns CursorRuntime when name is 'cursor'", () => {
|
|
111
|
+
const runtime = getRuntime("cursor");
|
|
112
|
+
expect(runtime).toBeInstanceOf(CursorRuntime);
|
|
113
|
+
expect(runtime.id).toBe("cursor");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("uses config.runtime.default 'cursor' when name is omitted", () => {
|
|
117
|
+
const config = { runtime: { default: "cursor" } } as AgentplateConfig;
|
|
118
|
+
const runtime = getRuntime(undefined, config);
|
|
119
|
+
expect(runtime).toBeInstanceOf(CursorRuntime);
|
|
120
|
+
expect(runtime.id).toBe("cursor");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("cursor runtime returns a new instance on each call", () => {
|
|
124
|
+
const a = getRuntime("cursor");
|
|
125
|
+
const b = getRuntime("cursor");
|
|
126
|
+
expect(a).not.toBe(b);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("returns GeminiRuntime when name is 'gemini'", () => {
|
|
130
|
+
const runtime = getRuntime("gemini");
|
|
131
|
+
expect(runtime).toBeInstanceOf(GeminiRuntime);
|
|
132
|
+
expect(runtime.id).toBe("gemini");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("uses config.runtime.default 'gemini' when name is omitted", () => {
|
|
136
|
+
const config = { runtime: { default: "gemini" } } as AgentplateConfig;
|
|
137
|
+
const runtime = getRuntime(undefined, config);
|
|
138
|
+
expect(runtime).toBeInstanceOf(GeminiRuntime);
|
|
139
|
+
expect(runtime.id).toBe("gemini");
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("returns OpenCodeRuntime when name is 'opencode'", () => {
|
|
143
|
+
const runtime = getRuntime("opencode");
|
|
144
|
+
expect(runtime).toBeInstanceOf(OpenCodeRuntime);
|
|
145
|
+
expect(runtime.id).toBe("opencode");
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("uses config.runtime.default 'opencode' when name is omitted", () => {
|
|
149
|
+
const config = { runtime: { default: "opencode" } } as AgentplateConfig;
|
|
150
|
+
const runtime = getRuntime(undefined, config);
|
|
151
|
+
expect(runtime).toBeInstanceOf(OpenCodeRuntime);
|
|
152
|
+
expect(runtime.id).toBe("opencode");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("opencode runtime returns a new instance on each call", () => {
|
|
156
|
+
const a = getRuntime("opencode");
|
|
157
|
+
const b = getRuntime("opencode");
|
|
158
|
+
expect(a).not.toBe(b);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe("capability routing", () => {
|
|
162
|
+
it("resolves capability-specific runtime from config", () => {
|
|
163
|
+
const config = {
|
|
164
|
+
runtime: { default: "claude", capabilities: { builder: "gemini" } },
|
|
165
|
+
} as unknown as AgentplateConfig;
|
|
166
|
+
const runtime = getRuntime(undefined, config, "builder");
|
|
167
|
+
expect(runtime).toBeInstanceOf(GeminiRuntime);
|
|
168
|
+
expect(runtime.id).toBe("gemini");
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("falls back to default when capability has no override", () => {
|
|
172
|
+
const config = {
|
|
173
|
+
runtime: { default: "codex", capabilities: { builder: "gemini" } },
|
|
174
|
+
} as unknown as AgentplateConfig;
|
|
175
|
+
const runtime = getRuntime(undefined, config, "scout");
|
|
176
|
+
expect(runtime).toBeInstanceOf(CodexRuntime);
|
|
177
|
+
expect(runtime.id).toBe("codex");
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("explicit name overrides capability routing", () => {
|
|
181
|
+
const config = {
|
|
182
|
+
runtime: { default: "claude", capabilities: { builder: "gemini" } },
|
|
183
|
+
} as unknown as AgentplateConfig;
|
|
184
|
+
const runtime = getRuntime("copilot", config, "builder");
|
|
185
|
+
expect(runtime).toBeInstanceOf(CopilotRuntime);
|
|
186
|
+
expect(runtime.id).toBe("copilot");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("works when capabilities is undefined", () => {
|
|
190
|
+
const config = { runtime: { default: "claude" } } as AgentplateConfig;
|
|
191
|
+
const runtime = getRuntime(undefined, config, "coordinator");
|
|
192
|
+
expect(runtime).toBeInstanceOf(ClaudeRuntime);
|
|
193
|
+
expect(runtime.id).toBe("claude");
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Runtime registry — maps runtime names to adapter factory functions.
|
|
2
|
+
// This is the ONLY module that imports concrete adapter classes.
|
|
3
|
+
|
|
4
|
+
import type { AgentplateConfig } from "../types.ts";
|
|
5
|
+
import { AiderRuntime } from "./aider.ts";
|
|
6
|
+
import { AmpRuntime } from "./amp.ts";
|
|
7
|
+
import { ClaudeRuntime } from "./claude.ts";
|
|
8
|
+
import { CodexRuntime } from "./codex.ts";
|
|
9
|
+
import { CopilotRuntime } from "./copilot.ts";
|
|
10
|
+
import { CursorRuntime } from "./cursor.ts";
|
|
11
|
+
import { GeminiRuntime } from "./gemini.ts";
|
|
12
|
+
import { GooseRuntime } from "./goose.ts";
|
|
13
|
+
import { OpenCodeRuntime } from "./opencode.ts";
|
|
14
|
+
import { PiRuntime } from "./pi.ts";
|
|
15
|
+
import { SaplingRuntime } from "./sapling.ts";
|
|
16
|
+
import type { AgentRuntime } from "./types.ts";
|
|
17
|
+
|
|
18
|
+
/** Registry of config-independent runtime adapters (name → factory). */
|
|
19
|
+
const runtimes = new Map<string, () => AgentRuntime>([
|
|
20
|
+
["aider", () => new AiderRuntime()],
|
|
21
|
+
["amp", () => new AmpRuntime()],
|
|
22
|
+
["claude", () => new ClaudeRuntime()],
|
|
23
|
+
["codex", () => new CodexRuntime()],
|
|
24
|
+
["copilot", () => new CopilotRuntime()],
|
|
25
|
+
["cursor", () => new CursorRuntime()],
|
|
26
|
+
["gemini", () => new GeminiRuntime()],
|
|
27
|
+
["goose", () => new GooseRuntime()],
|
|
28
|
+
["opencode", () => new OpenCodeRuntime()],
|
|
29
|
+
["pi", () => new PiRuntime()],
|
|
30
|
+
["sapling", () => new SaplingRuntime()],
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Return all registered runtime adapter instances.
|
|
35
|
+
*
|
|
36
|
+
* Used by callers that need to enumerate all runtimes (e.g. to build a
|
|
37
|
+
* dynamic list of known instruction file paths from each runtime's
|
|
38
|
+
* `instructionPath` property).
|
|
39
|
+
*
|
|
40
|
+
* @returns Array of one fresh instance per registered runtime.
|
|
41
|
+
*/
|
|
42
|
+
export function getAllRuntimes(): AgentRuntime[] {
|
|
43
|
+
return [
|
|
44
|
+
new AiderRuntime(),
|
|
45
|
+
new AmpRuntime(),
|
|
46
|
+
new ClaudeRuntime(),
|
|
47
|
+
new CodexRuntime(),
|
|
48
|
+
new CopilotRuntime(),
|
|
49
|
+
new CursorRuntime(),
|
|
50
|
+
new GeminiRuntime(),
|
|
51
|
+
new GooseRuntime(),
|
|
52
|
+
new OpenCodeRuntime(),
|
|
53
|
+
new PiRuntime(),
|
|
54
|
+
new SaplingRuntime(),
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Resolve a runtime adapter by name.
|
|
60
|
+
*
|
|
61
|
+
* Lookup order:
|
|
62
|
+
* 1. Explicit `name` argument (if provided)
|
|
63
|
+
* 2. `config.runtime.capabilities[capability]` (if capability provided)
|
|
64
|
+
* 3. `config.runtime.default` (if config is provided)
|
|
65
|
+
* 4. `"claude"` (hardcoded fallback)
|
|
66
|
+
*
|
|
67
|
+
* Special cases:
|
|
68
|
+
* - Pi runtime receives `config.runtime.pi` for model alias expansion.
|
|
69
|
+
*
|
|
70
|
+
* @param name - Runtime name to resolve (e.g. "claude"). Omit to use config default.
|
|
71
|
+
* @param config - Agentplate config for reading the default runtime.
|
|
72
|
+
* @param capability - Agent capability (e.g. "coordinator", "builder") for per-capability routing.
|
|
73
|
+
* @throws {Error} If the resolved runtime name is not registered.
|
|
74
|
+
* @returns A fresh AgentRuntime instance.
|
|
75
|
+
*/
|
|
76
|
+
export function getRuntime(
|
|
77
|
+
name?: string,
|
|
78
|
+
config?: AgentplateConfig,
|
|
79
|
+
capability?: string,
|
|
80
|
+
): AgentRuntime {
|
|
81
|
+
const capabilityRuntime =
|
|
82
|
+
capability && config?.runtime?.capabilities
|
|
83
|
+
? config.runtime.capabilities[capability]
|
|
84
|
+
: undefined;
|
|
85
|
+
const runtimeName = name ?? capabilityRuntime ?? config?.runtime?.default ?? "claude";
|
|
86
|
+
|
|
87
|
+
// Pi runtime needs config for model alias expansion.
|
|
88
|
+
if (runtimeName === "pi") {
|
|
89
|
+
return new PiRuntime(config?.runtime?.pi);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const factory = runtimes.get(runtimeName);
|
|
93
|
+
if (!factory) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`Unknown runtime: "${runtimeName}". Available: ${[...runtimes.keys()].join(", ")}`,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
return factory();
|
|
99
|
+
}
|