@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,409 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import type { AgentplateConfig } from "../types.ts";
|
|
5
|
+
import { checkProviders } from "./providers.ts";
|
|
6
|
+
|
|
7
|
+
/** Build a minimal valid AgentplateConfig for testing. */
|
|
8
|
+
function makeConfig(overrides: Partial<AgentplateConfig> = {}): AgentplateConfig {
|
|
9
|
+
const tmp = tmpdir();
|
|
10
|
+
return {
|
|
11
|
+
project: {
|
|
12
|
+
name: "test-project",
|
|
13
|
+
root: tmp,
|
|
14
|
+
canonicalBranch: "main",
|
|
15
|
+
},
|
|
16
|
+
agents: {
|
|
17
|
+
manifestPath: join(tmp, ".agentplate", "agent-manifest.json"),
|
|
18
|
+
baseDir: join(tmp, ".agentplate", "agents"),
|
|
19
|
+
maxConcurrent: 5,
|
|
20
|
+
staggerDelayMs: 1000,
|
|
21
|
+
maxDepth: 2,
|
|
22
|
+
maxSessionsPerRun: 0,
|
|
23
|
+
maxAgentsPerLead: 5,
|
|
24
|
+
},
|
|
25
|
+
worktrees: {
|
|
26
|
+
baseDir: join(tmp, ".agentplate", "worktrees"),
|
|
27
|
+
},
|
|
28
|
+
taskTracker: {
|
|
29
|
+
backend: "auto",
|
|
30
|
+
enabled: false,
|
|
31
|
+
},
|
|
32
|
+
loam: {
|
|
33
|
+
enabled: false,
|
|
34
|
+
domains: [],
|
|
35
|
+
primeFormat: "markdown",
|
|
36
|
+
},
|
|
37
|
+
merge: {
|
|
38
|
+
aiResolveEnabled: false,
|
|
39
|
+
reimagineEnabled: false,
|
|
40
|
+
},
|
|
41
|
+
providers: {
|
|
42
|
+
anthropic: { type: "native" },
|
|
43
|
+
},
|
|
44
|
+
watchdog: {
|
|
45
|
+
tier0Enabled: false,
|
|
46
|
+
tier0IntervalMs: 30000,
|
|
47
|
+
tier1Enabled: false,
|
|
48
|
+
tier2Enabled: false,
|
|
49
|
+
staleThresholdMs: 300000,
|
|
50
|
+
zombieThresholdMs: 600000,
|
|
51
|
+
nudgeIntervalMs: 60000,
|
|
52
|
+
},
|
|
53
|
+
models: {},
|
|
54
|
+
logging: {
|
|
55
|
+
verbose: false,
|
|
56
|
+
redactSecrets: true,
|
|
57
|
+
},
|
|
58
|
+
...overrides,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Dummy agentplateDir — provider checks don't use the filesystem
|
|
63
|
+
const AGENTPLATE_DIR = join(tmpdir(), ".agentplate");
|
|
64
|
+
|
|
65
|
+
describe("checkProviders", () => {
|
|
66
|
+
test("all checks have required DoctorCheck fields", async () => {
|
|
67
|
+
const config = makeConfig();
|
|
68
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
69
|
+
|
|
70
|
+
expect(checks).toBeArray();
|
|
71
|
+
for (const check of checks) {
|
|
72
|
+
expect(typeof check.name).toBe("string");
|
|
73
|
+
expect(check.category).toBe("providers");
|
|
74
|
+
expect(["pass", "warn", "fail"]).toContain(check.status);
|
|
75
|
+
expect(typeof check.message).toBe("string");
|
|
76
|
+
if (check.details !== undefined) {
|
|
77
|
+
expect(check.details).toBeArray();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("providers-configured check", () => {
|
|
83
|
+
test("native-only config (no gateway) returns pass for providers-configured", async () => {
|
|
84
|
+
const config = makeConfig({
|
|
85
|
+
providers: { anthropic: { type: "native" } },
|
|
86
|
+
});
|
|
87
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
88
|
+
|
|
89
|
+
const check = checks.find((c) => c.name === "providers-configured");
|
|
90
|
+
expect(check).toBeDefined();
|
|
91
|
+
expect(check?.status).toBe("pass");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("empty providers returns warn for providers-configured", async () => {
|
|
95
|
+
const config = makeConfig({ providers: {} });
|
|
96
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
97
|
+
|
|
98
|
+
const check = checks.find((c) => c.name === "providers-configured");
|
|
99
|
+
expect(check).toBeDefined();
|
|
100
|
+
expect(check?.status).toBe("warn");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("providers-configured details list provider names and types", async () => {
|
|
104
|
+
const config = makeConfig({
|
|
105
|
+
providers: {
|
|
106
|
+
anthropic: { type: "native" },
|
|
107
|
+
openrouter: { type: "gateway", baseUrl: "http://127.0.0.1:19873" },
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
111
|
+
|
|
112
|
+
const check = checks.find((c) => c.name === "providers-configured");
|
|
113
|
+
expect(check?.status).toBe("pass");
|
|
114
|
+
expect(check?.details).toBeDefined();
|
|
115
|
+
expect(check?.details?.some((d) => d.includes("anthropic"))).toBe(true);
|
|
116
|
+
expect(check?.details?.some((d) => d.includes("openrouter"))).toBe(true);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe("provider-reachable-{name} check", () => {
|
|
121
|
+
test("gateway config triggers reachability check (warn path — no real server)", async () => {
|
|
122
|
+
const config = makeConfig({
|
|
123
|
+
providers: {
|
|
124
|
+
fake: {
|
|
125
|
+
type: "gateway",
|
|
126
|
+
// Use a port that is almost certainly not listening
|
|
127
|
+
baseUrl: "http://127.0.0.1:19873",
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
132
|
+
|
|
133
|
+
const check = checks.find((c) => c.name === "provider-reachable-fake");
|
|
134
|
+
expect(check).toBeDefined();
|
|
135
|
+
expect(check?.status).toBe("warn");
|
|
136
|
+
expect(check?.message).toContain("fake");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("reachability pass path — local HTTP server", async () => {
|
|
140
|
+
// Start a minimal Bun HTTP server on an ephemeral port
|
|
141
|
+
const server = Bun.serve({
|
|
142
|
+
port: 0, // OS assigns a free port
|
|
143
|
+
fetch() {
|
|
144
|
+
return new Response("ok");
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
const config = makeConfig({
|
|
150
|
+
providers: {
|
|
151
|
+
localtest: {
|
|
152
|
+
type: "gateway",
|
|
153
|
+
baseUrl: `http://127.0.0.1:${server.port}`,
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
158
|
+
|
|
159
|
+
const check = checks.find((c) => c.name === "provider-reachable-localtest");
|
|
160
|
+
expect(check).toBeDefined();
|
|
161
|
+
expect(check?.status).toBe("pass");
|
|
162
|
+
} finally {
|
|
163
|
+
await server.stop();
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("gateway without baseUrl skips reachability check", async () => {
|
|
168
|
+
const config = makeConfig({
|
|
169
|
+
providers: {
|
|
170
|
+
nourl: { type: "gateway" }, // no baseUrl
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
174
|
+
|
|
175
|
+
const reachCheck = checks.find((c) => c.name === "provider-reachable-nourl");
|
|
176
|
+
expect(reachCheck).toBeUndefined();
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe("provider-auth-token-{name} check", () => {
|
|
181
|
+
const ENV_KEY = "AGENTPLATE_TEST_FAKE_PROVIDER_TOKEN_XYZ";
|
|
182
|
+
|
|
183
|
+
beforeAll(() => {
|
|
184
|
+
// Ensure env var is unset before tests
|
|
185
|
+
delete process.env[ENV_KEY];
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
afterAll(() => {
|
|
189
|
+
delete process.env[ENV_KEY];
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("gateway with authTokenEnv warns when env var missing", async () => {
|
|
193
|
+
const config = makeConfig({
|
|
194
|
+
providers: {
|
|
195
|
+
testgateway: {
|
|
196
|
+
type: "gateway",
|
|
197
|
+
authTokenEnv: ENV_KEY,
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
202
|
+
|
|
203
|
+
const check = checks.find((c) => c.name === "provider-auth-token-testgateway");
|
|
204
|
+
expect(check).toBeDefined();
|
|
205
|
+
expect(check?.status).toBe("warn");
|
|
206
|
+
// Details must include the env var NAME, never a value
|
|
207
|
+
expect(check?.details?.some((d) => d.includes(ENV_KEY))).toBe(true);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("gateway with authTokenEnv passes when env var is set", async () => {
|
|
211
|
+
process.env[ENV_KEY] = "test-token-value";
|
|
212
|
+
|
|
213
|
+
const config = makeConfig({
|
|
214
|
+
providers: {
|
|
215
|
+
testgateway: {
|
|
216
|
+
type: "gateway",
|
|
217
|
+
authTokenEnv: ENV_KEY,
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
222
|
+
|
|
223
|
+
const check = checks.find((c) => c.name === "provider-auth-token-testgateway");
|
|
224
|
+
expect(check).toBeDefined();
|
|
225
|
+
expect(check?.status).toBe("pass");
|
|
226
|
+
// Details include the var name, not the value
|
|
227
|
+
expect(check?.details?.some((d) => d.includes(ENV_KEY))).toBe(true);
|
|
228
|
+
// Value must NOT appear in details
|
|
229
|
+
expect(check?.details?.some((d) => d.includes("test-token-value"))).toBe(false);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("native provider with no authTokenEnv skips auth-token check", async () => {
|
|
233
|
+
const config = makeConfig({
|
|
234
|
+
providers: { anthropic: { type: "native" } },
|
|
235
|
+
});
|
|
236
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
237
|
+
|
|
238
|
+
const authCheck = checks.find((c) => c.name?.startsWith("provider-auth-token-"));
|
|
239
|
+
expect(authCheck).toBeUndefined();
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe("tool-use-compat check", () => {
|
|
244
|
+
// Local HTTP server to avoid network calls to real openrouter.ai
|
|
245
|
+
let localServer: ReturnType<typeof Bun.serve>;
|
|
246
|
+
let localBaseUrl: string;
|
|
247
|
+
|
|
248
|
+
beforeAll(() => {
|
|
249
|
+
localServer = Bun.serve({
|
|
250
|
+
port: 0,
|
|
251
|
+
fetch() {
|
|
252
|
+
return new Response("ok");
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
localBaseUrl = `http://127.0.0.1:${localServer.port}`;
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
afterAll(async () => {
|
|
259
|
+
await localServer.stop();
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test("tool-heavy role with provider-prefixed model warns", async () => {
|
|
263
|
+
const config = makeConfig({
|
|
264
|
+
models: { builder: "openrouter/openai/gpt-4o" },
|
|
265
|
+
providers: {
|
|
266
|
+
openrouter: { type: "gateway", baseUrl: localBaseUrl },
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
270
|
+
|
|
271
|
+
const check = checks.find((c) => c.name === "tool-use-compat" && c.status === "warn");
|
|
272
|
+
expect(check).toBeDefined();
|
|
273
|
+
expect(check?.message).toContain("builder");
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test("non-tool-heavy role with provider-prefixed model does not warn", async () => {
|
|
277
|
+
// "lead" is not a tool-heavy role
|
|
278
|
+
const config = makeConfig({
|
|
279
|
+
models: { lead: "openrouter/openai/gpt-4o" },
|
|
280
|
+
providers: {
|
|
281
|
+
openrouter: { type: "gateway", baseUrl: localBaseUrl },
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
285
|
+
|
|
286
|
+
const warnChecks = checks.filter((c) => c.name === "tool-use-compat" && c.status === "warn");
|
|
287
|
+
expect(warnChecks.length).toBe(0);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test("no tool-heavy roles with prefixed models emits single pass", async () => {
|
|
291
|
+
const config = makeConfig({
|
|
292
|
+
models: { builder: "sonnet" }, // alias, not provider-prefixed
|
|
293
|
+
});
|
|
294
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
295
|
+
|
|
296
|
+
const passCheck = checks.find((c) => c.name === "tool-use-compat" && c.status === "pass");
|
|
297
|
+
expect(passCheck).toBeDefined();
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test("all three tool-heavy roles can trigger separate warns", async () => {
|
|
301
|
+
const config = makeConfig({
|
|
302
|
+
models: {
|
|
303
|
+
builder: "openrouter/openai/gpt-4o",
|
|
304
|
+
scout: "openrouter/openai/gpt-4o",
|
|
305
|
+
merger: "openrouter/openai/gpt-4o",
|
|
306
|
+
},
|
|
307
|
+
providers: {
|
|
308
|
+
openrouter: { type: "gateway", baseUrl: localBaseUrl },
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
312
|
+
|
|
313
|
+
const warnChecks = checks.filter((c) => c.name === "tool-use-compat" && c.status === "warn");
|
|
314
|
+
expect(warnChecks.length).toBe(3);
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
describe("model-provider-ref(s) check", () => {
|
|
319
|
+
// Local HTTP server to avoid network calls to real openrouter.ai
|
|
320
|
+
let localServer: ReturnType<typeof Bun.serve>;
|
|
321
|
+
let localBaseUrl: string;
|
|
322
|
+
|
|
323
|
+
beforeAll(() => {
|
|
324
|
+
localServer = Bun.serve({
|
|
325
|
+
port: 0,
|
|
326
|
+
fetch() {
|
|
327
|
+
return new Response("ok");
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
localBaseUrl = `http://127.0.0.1:${localServer.port}`;
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
afterAll(async () => {
|
|
334
|
+
await localServer.stop();
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test("model referencing unknown provider fails", async () => {
|
|
338
|
+
const config = makeConfig({
|
|
339
|
+
models: { builder: "unknownprovider/some-model" },
|
|
340
|
+
// unknownprovider not in providers
|
|
341
|
+
providers: { anthropic: { type: "native" } },
|
|
342
|
+
});
|
|
343
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
344
|
+
|
|
345
|
+
const check = checks.find((c) => c.name === "model-provider-ref" && c.status === "fail");
|
|
346
|
+
expect(check).toBeDefined();
|
|
347
|
+
expect(check?.message).toContain("unknownprovider");
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
test("model referencing defined provider passes", async () => {
|
|
351
|
+
const config = makeConfig({
|
|
352
|
+
models: { builder: "openrouter/openai/gpt-4o" },
|
|
353
|
+
providers: {
|
|
354
|
+
openrouter: { type: "gateway", baseUrl: localBaseUrl },
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
358
|
+
|
|
359
|
+
const check = checks.find((c) => c.name === "model-provider-ref" && c.status === "pass");
|
|
360
|
+
expect(check).toBeDefined();
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
test("no provider-prefixed models emits single pass named model-provider-refs", async () => {
|
|
364
|
+
const config = makeConfig({
|
|
365
|
+
models: { builder: "sonnet", scout: "haiku" },
|
|
366
|
+
});
|
|
367
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
368
|
+
|
|
369
|
+
const check = checks.find((c) => c.name === "model-provider-refs");
|
|
370
|
+
expect(check).toBeDefined();
|
|
371
|
+
expect(check?.status).toBe("pass");
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test("empty models emits single pass named model-provider-refs", async () => {
|
|
375
|
+
const config = makeConfig({ models: {} });
|
|
376
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
377
|
+
|
|
378
|
+
const check = checks.find((c) => c.name === "model-provider-refs");
|
|
379
|
+
expect(check).toBeDefined();
|
|
380
|
+
expect(check?.status).toBe("pass");
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
describe("gateway-api-key-reminder check", () => {
|
|
385
|
+
test("gateway present triggers api-key reminder warn", async () => {
|
|
386
|
+
const config = makeConfig({
|
|
387
|
+
providers: {
|
|
388
|
+
openrouter: { type: "gateway", baseUrl: "http://127.0.0.1:19873" },
|
|
389
|
+
},
|
|
390
|
+
});
|
|
391
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
392
|
+
|
|
393
|
+
const check = checks.find((c) => c.name === "gateway-api-key-reminder");
|
|
394
|
+
expect(check).toBeDefined();
|
|
395
|
+
expect(check?.status).toBe("warn");
|
|
396
|
+
expect(check?.message).toContain("ANTHROPIC_API_KEY");
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
test("no gateway providers — reminder is absent", async () => {
|
|
400
|
+
const config = makeConfig({
|
|
401
|
+
providers: { anthropic: { type: "native" } },
|
|
402
|
+
});
|
|
403
|
+
const checks = await checkProviders(config, AGENTPLATE_DIR);
|
|
404
|
+
|
|
405
|
+
const check = checks.find((c) => c.name === "gateway-api-key-reminder");
|
|
406
|
+
expect(check).toBeUndefined();
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
});
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import type { AgentplateConfig, ProviderConfig } from "../types.ts";
|
|
2
|
+
import type { DoctorCheck, DoctorCheckFn } from "./types.ts";
|
|
3
|
+
|
|
4
|
+
/** Roles that rely heavily on tool-use (function calling). */
|
|
5
|
+
const TOOL_HEAVY_ROLES = new Set(["builder", "scout", "merger"]);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Provider and multi-runtime configuration checks.
|
|
9
|
+
*
|
|
10
|
+
* Validates gateway provider reachability, auth tokens, model-provider references,
|
|
11
|
+
* and tool-use compatibility across configured runtimes.
|
|
12
|
+
*/
|
|
13
|
+
export const checkProviders: DoctorCheckFn = async (
|
|
14
|
+
config,
|
|
15
|
+
_agentplateDir,
|
|
16
|
+
): Promise<DoctorCheck[]> => {
|
|
17
|
+
const checks: DoctorCheck[] = [];
|
|
18
|
+
|
|
19
|
+
// Base check: at least one provider configured
|
|
20
|
+
checks.push(buildProvidersConfigured(config));
|
|
21
|
+
|
|
22
|
+
// Identify gateway providers
|
|
23
|
+
const gatewayEntries = Object.entries(config.providers).filter(([, p]) => p.type === "gateway");
|
|
24
|
+
|
|
25
|
+
// Check 1: provider-reachable-{name} — one per gateway provider with baseUrl
|
|
26
|
+
for (const [name, provider] of gatewayEntries) {
|
|
27
|
+
if (provider.baseUrl) {
|
|
28
|
+
checks.push(await checkProviderReachable(name, provider));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Check 2: provider-auth-token-{name} — one per gateway provider with authTokenEnv
|
|
33
|
+
for (const [name, provider] of gatewayEntries) {
|
|
34
|
+
if (provider.authTokenEnv) {
|
|
35
|
+
checks.push(buildProviderAuthToken(name, provider));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Check 3: tool-use-compat — one warn per tool-heavy role using a provider-prefixed model
|
|
40
|
+
checks.push(...buildToolUseCompat(config));
|
|
41
|
+
|
|
42
|
+
// Check 4: model-provider-ref(s) — one per provider-prefixed model, or single pass
|
|
43
|
+
checks.push(...buildModelProviderRefs(config));
|
|
44
|
+
|
|
45
|
+
// Check 5: gateway-api-key-reminder — only when gateway providers exist
|
|
46
|
+
if (gatewayEntries.length > 0) {
|
|
47
|
+
checks.push(buildGatewayApiKeyReminder());
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return checks;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Base check: verifies at least one provider is configured.
|
|
55
|
+
*/
|
|
56
|
+
function buildProvidersConfigured(config: AgentplateConfig): DoctorCheck {
|
|
57
|
+
const entries = Object.entries(config.providers);
|
|
58
|
+
|
|
59
|
+
if (entries.length > 0) {
|
|
60
|
+
return {
|
|
61
|
+
name: "providers-configured",
|
|
62
|
+
category: "providers",
|
|
63
|
+
status: "pass",
|
|
64
|
+
message: `${entries.length} provider${entries.length === 1 ? "" : "s"} configured`,
|
|
65
|
+
details: entries.map(([name, p]) => `${name} (${p.type})`),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
name: "providers-configured",
|
|
71
|
+
category: "providers",
|
|
72
|
+
status: "warn",
|
|
73
|
+
message: "No providers configured — add providers to config.yaml",
|
|
74
|
+
details: ["At least one native or gateway provider should be configured."],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Check 1: HTTP reachability of a gateway provider's baseUrl.
|
|
80
|
+
*
|
|
81
|
+
* Uses fetch() with a 5-second timeout. Any HTTP response (any status code)
|
|
82
|
+
* counts as reachable — only network errors or timeouts produce a warn.
|
|
83
|
+
*/
|
|
84
|
+
async function checkProviderReachable(
|
|
85
|
+
name: string,
|
|
86
|
+
provider: ProviderConfig,
|
|
87
|
+
): Promise<DoctorCheck> {
|
|
88
|
+
const baseUrl = provider.baseUrl as string; // caller guards baseUrl is defined
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
await fetch(baseUrl, {
|
|
92
|
+
method: "HEAD",
|
|
93
|
+
signal: AbortSignal.timeout(5000),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
name: `provider-reachable-${name}`,
|
|
98
|
+
category: "providers",
|
|
99
|
+
status: "pass",
|
|
100
|
+
message: `Gateway provider '${name}' is reachable`,
|
|
101
|
+
details: [baseUrl],
|
|
102
|
+
};
|
|
103
|
+
} catch (error) {
|
|
104
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
105
|
+
return {
|
|
106
|
+
name: `provider-reachable-${name}`,
|
|
107
|
+
category: "providers",
|
|
108
|
+
status: "warn",
|
|
109
|
+
message: `Gateway provider '${name}' is unreachable`,
|
|
110
|
+
details: [baseUrl, errorMsg],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Check 2: Validate that the auth token env var for a gateway provider is set.
|
|
117
|
+
*
|
|
118
|
+
* Reports the env var NAME in details — never the value.
|
|
119
|
+
*/
|
|
120
|
+
function buildProviderAuthToken(name: string, provider: ProviderConfig): DoctorCheck {
|
|
121
|
+
const envVar = provider.authTokenEnv as string; // caller guards authTokenEnv is defined
|
|
122
|
+
const value = process.env[envVar];
|
|
123
|
+
|
|
124
|
+
if (value && value.length > 0) {
|
|
125
|
+
return {
|
|
126
|
+
name: `provider-auth-token-${name}`,
|
|
127
|
+
category: "providers",
|
|
128
|
+
status: "pass",
|
|
129
|
+
message: `Auth token for provider '${name}' is set`,
|
|
130
|
+
details: [`Env var: ${envVar}`],
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
name: `provider-auth-token-${name}`,
|
|
136
|
+
category: "providers",
|
|
137
|
+
status: "warn",
|
|
138
|
+
message: `Auth token for provider '${name}' is missing`,
|
|
139
|
+
details: [`Env var: ${envVar}`, `Set ${envVar} to authenticate with this provider.`],
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Check 3: Tool-use compatibility for tool-heavy roles using non-Anthropic models.
|
|
145
|
+
*
|
|
146
|
+
* Tool-heavy roles (builder, scout, merger) rely on structured tool-use (function
|
|
147
|
+
* calling). Non-Anthropic models accessed via gateway providers may have different
|
|
148
|
+
* tool-use behavior. Emits one warn per affected role, or a single pass if none.
|
|
149
|
+
*/
|
|
150
|
+
function buildToolUseCompat(config: AgentplateConfig): DoctorCheck[] {
|
|
151
|
+
const checks: DoctorCheck[] = [];
|
|
152
|
+
|
|
153
|
+
for (const [role, model] of Object.entries(config.models)) {
|
|
154
|
+
if (!TOOL_HEAVY_ROLES.has(role)) continue;
|
|
155
|
+
if (model === undefined) continue;
|
|
156
|
+
if (!model.includes("/")) continue;
|
|
157
|
+
|
|
158
|
+
checks.push({
|
|
159
|
+
name: "tool-use-compat",
|
|
160
|
+
category: "providers",
|
|
161
|
+
status: "warn",
|
|
162
|
+
message: `models.${role} uses non-Anthropic model — tool-use compatibility not guaranteed`,
|
|
163
|
+
details: [
|
|
164
|
+
`Model: ${model}`,
|
|
165
|
+
"Tool use (function calling) behavior varies across providers.",
|
|
166
|
+
"Test agent behavior thoroughly before using in production.",
|
|
167
|
+
],
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (checks.length === 0) {
|
|
172
|
+
checks.push({
|
|
173
|
+
name: "tool-use-compat",
|
|
174
|
+
category: "providers",
|
|
175
|
+
status: "pass",
|
|
176
|
+
message: "No tool-heavy roles use non-Anthropic models",
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return checks;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Check 4: Validate that provider-prefixed model references point to configured providers.
|
|
185
|
+
*
|
|
186
|
+
* For each config.models entry containing '/' (provider-qualified), extracts the
|
|
187
|
+
* provider name and verifies it exists in config.providers. Emits one check per
|
|
188
|
+
* provider-prefixed model, or a single pass if no such models exist.
|
|
189
|
+
*/
|
|
190
|
+
function buildModelProviderRefs(config: AgentplateConfig): DoctorCheck[] {
|
|
191
|
+
const checks: DoctorCheck[] = [];
|
|
192
|
+
|
|
193
|
+
for (const [role, model] of Object.entries(config.models)) {
|
|
194
|
+
if (model === undefined) continue;
|
|
195
|
+
if (!model.includes("/")) continue;
|
|
196
|
+
|
|
197
|
+
const providerName = model.split("/")[0];
|
|
198
|
+
if (!providerName) continue;
|
|
199
|
+
|
|
200
|
+
if (config.providers[providerName]) {
|
|
201
|
+
checks.push({
|
|
202
|
+
name: "model-provider-ref",
|
|
203
|
+
category: "providers",
|
|
204
|
+
status: "pass",
|
|
205
|
+
message: `models.${role} references defined provider '${providerName}'`,
|
|
206
|
+
details: [`Model: ${model}`],
|
|
207
|
+
});
|
|
208
|
+
} else {
|
|
209
|
+
checks.push({
|
|
210
|
+
name: "model-provider-ref",
|
|
211
|
+
category: "providers",
|
|
212
|
+
status: "fail",
|
|
213
|
+
message: `models.${role} references undefined provider '${providerName}'`,
|
|
214
|
+
details: [
|
|
215
|
+
`Model: ${model}`,
|
|
216
|
+
`Provider '${providerName}' is not defined in config.yaml providers section.`,
|
|
217
|
+
`Add it: providers:\n ${providerName}:\n type: gateway\n baseUrl: https://...`,
|
|
218
|
+
],
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (checks.length === 0) {
|
|
224
|
+
checks.push({
|
|
225
|
+
name: "model-provider-refs",
|
|
226
|
+
category: "providers",
|
|
227
|
+
status: "pass",
|
|
228
|
+
message: "No provider-prefixed model references",
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return checks;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Check 5: Reminder about ANTHROPIC_API_KEY when gateway providers are configured.
|
|
237
|
+
*
|
|
238
|
+
* Agents spawned via gateway routes receive ANTHROPIC_API_KEY="" so they use the
|
|
239
|
+
* gateway instead of Anthropic directly. Any direct Anthropic API calls (e.g.,
|
|
240
|
+
* from merge/resolver.ts) require a separate key.
|
|
241
|
+
*/
|
|
242
|
+
function buildGatewayApiKeyReminder(): DoctorCheck {
|
|
243
|
+
return {
|
|
244
|
+
name: "gateway-api-key-reminder",
|
|
245
|
+
category: "providers",
|
|
246
|
+
status: "warn",
|
|
247
|
+
message:
|
|
248
|
+
"Gateway providers configured — agents using gateway routes will have ANTHROPIC_API_KEY set to empty string. Direct Anthropic API calls require a separate key.",
|
|
249
|
+
};
|
|
250
|
+
}
|