@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,285 @@
|
|
|
1
|
+
import { access, constants, mkdir, rm, stat } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import type { AgentManifest } from "../types.ts";
|
|
4
|
+
import type { DoctorCheck, DoctorCheckFn } from "./types.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Check if a path exists.
|
|
8
|
+
*/
|
|
9
|
+
async function pathExists(path: string): Promise<boolean> {
|
|
10
|
+
try {
|
|
11
|
+
await access(path, constants.F_OK);
|
|
12
|
+
return true;
|
|
13
|
+
} catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Directory structure checks.
|
|
20
|
+
* Validates that .agentplate/ and its subdirectories exist with correct permissions.
|
|
21
|
+
*/
|
|
22
|
+
export const checkStructure: DoctorCheckFn = async (
|
|
23
|
+
_config,
|
|
24
|
+
agentplateDir,
|
|
25
|
+
): Promise<DoctorCheck[]> => {
|
|
26
|
+
const checks: DoctorCheck[] = [];
|
|
27
|
+
|
|
28
|
+
// Check 1: .agentplate/ directory exists
|
|
29
|
+
const agentplateDirExists = await pathExists(agentplateDir);
|
|
30
|
+
checks.push({
|
|
31
|
+
name: ".agentplate/ directory",
|
|
32
|
+
category: "structure",
|
|
33
|
+
status: agentplateDirExists ? "pass" : "fail",
|
|
34
|
+
message: agentplateDirExists ? "Directory exists" : "Directory missing",
|
|
35
|
+
details: agentplateDirExists ? undefined : ["Run 'ap init' to create it"],
|
|
36
|
+
fixable: !agentplateDirExists,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// If .agentplate/ doesn't exist, bail early
|
|
40
|
+
if (!agentplateDirExists) {
|
|
41
|
+
return checks;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Check 2: Required files
|
|
45
|
+
const requiredFiles = ["config.yaml", "agent-manifest.json", "hooks.json", ".gitignore"];
|
|
46
|
+
const missingFiles: string[] = [];
|
|
47
|
+
|
|
48
|
+
for (const fileName of requiredFiles) {
|
|
49
|
+
const filePath = join(agentplateDir, fileName);
|
|
50
|
+
const exists = await pathExists(filePath);
|
|
51
|
+
if (!exists) {
|
|
52
|
+
missingFiles.push(fileName);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
checks.push({
|
|
57
|
+
name: "Required files",
|
|
58
|
+
category: "structure",
|
|
59
|
+
status: missingFiles.length === 0 ? "pass" : "fail",
|
|
60
|
+
message:
|
|
61
|
+
missingFiles.length === 0
|
|
62
|
+
? "All required files present"
|
|
63
|
+
: `Missing ${missingFiles.length} file(s)`,
|
|
64
|
+
details: missingFiles.length > 0 ? missingFiles : undefined,
|
|
65
|
+
fixable: missingFiles.length > 0,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Check 3: Required subdirectories
|
|
69
|
+
const requiredDirs = ["agent-defs", "agents", "worktrees", "specs", "logs"];
|
|
70
|
+
const missingDirs: string[] = [];
|
|
71
|
+
|
|
72
|
+
for (const dirName of requiredDirs) {
|
|
73
|
+
const dirPath = join(agentplateDir, dirName);
|
|
74
|
+
const exists = await pathExists(dirPath);
|
|
75
|
+
if (!exists) {
|
|
76
|
+
missingDirs.push(`${dirName}/`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
checks.push({
|
|
81
|
+
name: "Required subdirectories",
|
|
82
|
+
category: "structure",
|
|
83
|
+
status: missingDirs.length === 0 ? "pass" : "fail",
|
|
84
|
+
message:
|
|
85
|
+
missingDirs.length === 0
|
|
86
|
+
? "All required subdirectories present"
|
|
87
|
+
: `Missing ${missingDirs.length} subdirectory(ies)`,
|
|
88
|
+
details: missingDirs.length > 0 ? missingDirs : undefined,
|
|
89
|
+
fixable: missingDirs.length > 0,
|
|
90
|
+
fix:
|
|
91
|
+
missingDirs.length > 0
|
|
92
|
+
? async () => {
|
|
93
|
+
const actions: string[] = [];
|
|
94
|
+
for (const dir of missingDirs) {
|
|
95
|
+
const dirPath = join(agentplateDir, dir.replace(/\/$/, ""));
|
|
96
|
+
await mkdir(dirPath, { recursive: true });
|
|
97
|
+
actions.push(`Created missing directory: ${dir}`);
|
|
98
|
+
}
|
|
99
|
+
return actions;
|
|
100
|
+
}
|
|
101
|
+
: undefined,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Check 4: .gitignore contents — validate wildcard+whitelist model
|
|
105
|
+
const gitignorePath = join(agentplateDir, ".gitignore");
|
|
106
|
+
const expectedEntries = [
|
|
107
|
+
"*",
|
|
108
|
+
"!.gitignore",
|
|
109
|
+
"!config.yaml",
|
|
110
|
+
"!agent-manifest.json",
|
|
111
|
+
"!hooks.json",
|
|
112
|
+
"!groups.json",
|
|
113
|
+
"!agent-defs/",
|
|
114
|
+
"!agent-defs/**",
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const gitignoreContent = await Bun.file(gitignorePath).text();
|
|
119
|
+
const missingEntries = expectedEntries.filter((entry) => !gitignoreContent.includes(entry));
|
|
120
|
+
|
|
121
|
+
checks.push({
|
|
122
|
+
name: ".gitignore entries",
|
|
123
|
+
category: "structure",
|
|
124
|
+
status: missingEntries.length === 0 ? "pass" : "warn",
|
|
125
|
+
message:
|
|
126
|
+
missingEntries.length === 0
|
|
127
|
+
? "All expected entries present"
|
|
128
|
+
: `Missing ${missingEntries.length} entry(ies)`,
|
|
129
|
+
details: missingEntries.length > 0 ? missingEntries : undefined,
|
|
130
|
+
fixable: missingEntries.length > 0,
|
|
131
|
+
fix:
|
|
132
|
+
missingEntries.length > 0
|
|
133
|
+
? async () => {
|
|
134
|
+
const actions: string[] = [];
|
|
135
|
+
const content = await Bun.file(gitignorePath).text();
|
|
136
|
+
const suffix = content.endsWith("\n") ? "" : "\n";
|
|
137
|
+
await Bun.write(gitignorePath, `${content + suffix + missingEntries.join("\n")}\n`);
|
|
138
|
+
for (const entry of missingEntries) {
|
|
139
|
+
actions.push(`Added .gitignore entry: ${entry}`);
|
|
140
|
+
}
|
|
141
|
+
return actions;
|
|
142
|
+
}
|
|
143
|
+
: undefined,
|
|
144
|
+
});
|
|
145
|
+
} catch {
|
|
146
|
+
// .gitignore doesn't exist, already reported in required files check
|
|
147
|
+
checks.push({
|
|
148
|
+
name: ".gitignore entries",
|
|
149
|
+
category: "structure",
|
|
150
|
+
status: "fail",
|
|
151
|
+
message: "Cannot read .gitignore",
|
|
152
|
+
details: ["File is missing or unreadable"],
|
|
153
|
+
fixable: true,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Check 5: agent-defs/ contains .md files referenced by agent-manifest.json
|
|
158
|
+
try {
|
|
159
|
+
const manifestPath = join(agentplateDir, "agent-manifest.json");
|
|
160
|
+
const manifestContent = await Bun.file(manifestPath).text();
|
|
161
|
+
const manifest = JSON.parse(manifestContent) as AgentManifest;
|
|
162
|
+
|
|
163
|
+
const referencedFiles = new Set<string>();
|
|
164
|
+
for (const agentDef of Object.values(manifest.agents)) {
|
|
165
|
+
referencedFiles.add(agentDef.file);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const agentDefsDir = join(agentplateDir, "agent-defs");
|
|
169
|
+
const missingDefFiles: string[] = [];
|
|
170
|
+
|
|
171
|
+
for (const fileName of referencedFiles) {
|
|
172
|
+
const filePath = join(agentDefsDir, fileName);
|
|
173
|
+
const exists = await pathExists(filePath);
|
|
174
|
+
if (!exists) {
|
|
175
|
+
missingDefFiles.push(fileName);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
checks.push({
|
|
180
|
+
name: "Agent definition files",
|
|
181
|
+
category: "structure",
|
|
182
|
+
status: missingDefFiles.length === 0 ? "pass" : "fail",
|
|
183
|
+
message:
|
|
184
|
+
missingDefFiles.length === 0
|
|
185
|
+
? "All referenced .md files present"
|
|
186
|
+
: `Missing ${missingDefFiles.length} agent definition(s)`,
|
|
187
|
+
details: missingDefFiles.length > 0 ? missingDefFiles : undefined,
|
|
188
|
+
fixable: missingDefFiles.length > 0,
|
|
189
|
+
});
|
|
190
|
+
} catch (error) {
|
|
191
|
+
// Manifest missing or malformed, already reported or will be in config checks
|
|
192
|
+
checks.push({
|
|
193
|
+
name: "Agent definition files",
|
|
194
|
+
category: "structure",
|
|
195
|
+
status: "fail",
|
|
196
|
+
message: "Cannot validate agent definitions",
|
|
197
|
+
details: [
|
|
198
|
+
error instanceof Error ? error.message : "agent-manifest.json is missing or malformed",
|
|
199
|
+
],
|
|
200
|
+
fixable: false,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Check 6: No leftover files from failed init attempts
|
|
205
|
+
// Common temp files: .tmp, .bak, config.yaml~, etc.
|
|
206
|
+
try {
|
|
207
|
+
const entries = await Array.fromAsync(new Bun.Glob("*.{tmp,bak}").scan({ cwd: agentplateDir }));
|
|
208
|
+
const tempFiles = entries.filter((name) => name.endsWith(".tmp") || name.endsWith(".bak"));
|
|
209
|
+
|
|
210
|
+
checks.push({
|
|
211
|
+
name: "Leftover temp files",
|
|
212
|
+
category: "structure",
|
|
213
|
+
status: tempFiles.length === 0 ? "pass" : "warn",
|
|
214
|
+
message:
|
|
215
|
+
tempFiles.length === 0 ? "No temp files found" : `Found ${tempFiles.length} temp file(s)`,
|
|
216
|
+
details: tempFiles.length > 0 ? tempFiles : undefined,
|
|
217
|
+
fixable: tempFiles.length > 0,
|
|
218
|
+
fix:
|
|
219
|
+
tempFiles.length > 0
|
|
220
|
+
? async () => {
|
|
221
|
+
const actions: string[] = [];
|
|
222
|
+
for (const file of tempFiles) {
|
|
223
|
+
await rm(join(agentplateDir, file), { force: true });
|
|
224
|
+
actions.push(`Removed temp file: ${file}`);
|
|
225
|
+
}
|
|
226
|
+
return actions;
|
|
227
|
+
}
|
|
228
|
+
: undefined,
|
|
229
|
+
});
|
|
230
|
+
} catch {
|
|
231
|
+
// Ignore errors scanning for temp files
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Check 7: Stale lock files (older than 5 minutes)
|
|
235
|
+
try {
|
|
236
|
+
const lockEntries = await Array.fromAsync(new Bun.Glob("*.lock").scan({ cwd: agentplateDir }));
|
|
237
|
+
const now = Date.now();
|
|
238
|
+
const staleLockThresholdMs = 5 * 60 * 1000;
|
|
239
|
+
const staleLockFiles: string[] = [];
|
|
240
|
+
|
|
241
|
+
for (const lockFile of lockEntries) {
|
|
242
|
+
try {
|
|
243
|
+
const lockPath = join(agentplateDir, lockFile);
|
|
244
|
+
const stats = await stat(lockPath);
|
|
245
|
+
const ageMs = now - stats.mtimeMs;
|
|
246
|
+
if (ageMs > staleLockThresholdMs) {
|
|
247
|
+
staleLockFiles.push(lockFile);
|
|
248
|
+
}
|
|
249
|
+
} catch {
|
|
250
|
+
// ignore stat errors
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
checks.push({
|
|
255
|
+
name: "Stale lock files",
|
|
256
|
+
category: "structure",
|
|
257
|
+
status: staleLockFiles.length === 0 ? "pass" : "warn",
|
|
258
|
+
message:
|
|
259
|
+
staleLockFiles.length === 0
|
|
260
|
+
? "No stale lock files found"
|
|
261
|
+
: `Found ${staleLockFiles.length} stale lock file(s)`,
|
|
262
|
+
details: staleLockFiles.length > 0 ? staleLockFiles : undefined,
|
|
263
|
+
fixable: staleLockFiles.length > 0,
|
|
264
|
+
fix:
|
|
265
|
+
staleLockFiles.length > 0
|
|
266
|
+
? async () => {
|
|
267
|
+
const actions: string[] = [];
|
|
268
|
+
for (const file of staleLockFiles) {
|
|
269
|
+
try {
|
|
270
|
+
await rm(join(agentplateDir, file), { force: true });
|
|
271
|
+
actions.push(`Removed stale lock file: ${file}`);
|
|
272
|
+
} catch {
|
|
273
|
+
// ignore removal errors
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return actions;
|
|
277
|
+
}
|
|
278
|
+
: undefined,
|
|
279
|
+
});
|
|
280
|
+
} catch {
|
|
281
|
+
// ignore errors scanning for lock files
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return checks;
|
|
285
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AgentplateConfig } from "../types.ts";
|
|
2
|
+
|
|
3
|
+
// === Doctor (Health Checks) ===
|
|
4
|
+
|
|
5
|
+
/** Categories for doctor health checks. */
|
|
6
|
+
export type DoctorCategory =
|
|
7
|
+
| "dependencies"
|
|
8
|
+
| "structure"
|
|
9
|
+
| "config"
|
|
10
|
+
| "databases"
|
|
11
|
+
| "consistency"
|
|
12
|
+
| "agents"
|
|
13
|
+
| "merge"
|
|
14
|
+
| "logs"
|
|
15
|
+
| "version"
|
|
16
|
+
| "ecosystem"
|
|
17
|
+
| "providers"
|
|
18
|
+
| "watchdog"
|
|
19
|
+
| "serve";
|
|
20
|
+
|
|
21
|
+
/** Result of a single doctor health check. */
|
|
22
|
+
export interface DoctorCheck {
|
|
23
|
+
name: string;
|
|
24
|
+
category: DoctorCategory;
|
|
25
|
+
status: "pass" | "warn" | "fail";
|
|
26
|
+
message: string;
|
|
27
|
+
details?: string[];
|
|
28
|
+
/** Whether this check issues can be auto-fixed via --fix. */
|
|
29
|
+
fixable?: boolean;
|
|
30
|
+
/** Auto-fix closure — called when --fix flag is passed. Captures context at construction time. */
|
|
31
|
+
fix?: () => Promise<string[]> | string[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Signature for a doctor check function.
|
|
36
|
+
* Each check module exports a function matching this signature.
|
|
37
|
+
* Receives the loaded config and the absolute path to .agentplate/.
|
|
38
|
+
* Returns one or more check results.
|
|
39
|
+
*/
|
|
40
|
+
export type DoctorCheckFn = (
|
|
41
|
+
config: AgentplateConfig,
|
|
42
|
+
agentplateDir: string,
|
|
43
|
+
) => DoctorCheck[] | Promise<DoctorCheck[]>;
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdtemp } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { cleanupTempDir } from "../test-helpers.ts";
|
|
6
|
+
import type { AgentplateConfig } from "../types.ts";
|
|
7
|
+
import { checkCurrentVersion, checkVersion, checkVersionSync } from "./version.ts";
|
|
8
|
+
|
|
9
|
+
// Minimal config for testing
|
|
10
|
+
const mockConfig: AgentplateConfig = {
|
|
11
|
+
project: {
|
|
12
|
+
name: "test-project",
|
|
13
|
+
root: "/tmp/test",
|
|
14
|
+
canonicalBranch: "main",
|
|
15
|
+
},
|
|
16
|
+
agents: {
|
|
17
|
+
manifestPath: "/tmp/.agentplate/agent-manifest.json",
|
|
18
|
+
baseDir: "/tmp/.agentplate/agents",
|
|
19
|
+
maxConcurrent: 5,
|
|
20
|
+
staggerDelayMs: 1000,
|
|
21
|
+
maxDepth: 2,
|
|
22
|
+
maxSessionsPerRun: 0,
|
|
23
|
+
maxAgentsPerLead: 5,
|
|
24
|
+
},
|
|
25
|
+
worktrees: {
|
|
26
|
+
baseDir: "/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
|
+
};
|
|
59
|
+
|
|
60
|
+
describe("checkVersion", () => {
|
|
61
|
+
test("returns checks with category version", async () => {
|
|
62
|
+
const checks = await checkVersion(mockConfig, "/tmp/.agentplate");
|
|
63
|
+
|
|
64
|
+
expect(checks).toBeArray();
|
|
65
|
+
expect(checks.length).toBeGreaterThan(0);
|
|
66
|
+
|
|
67
|
+
for (const check of checks) {
|
|
68
|
+
expect(check.category).toBe("version");
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("includes version-current check", async () => {
|
|
73
|
+
const checks = await checkVersion(mockConfig, "/tmp/.agentplate");
|
|
74
|
+
|
|
75
|
+
const versionCheck = checks.find((c) => c.name === "version-current");
|
|
76
|
+
expect(versionCheck).toBeDefined();
|
|
77
|
+
expect(versionCheck?.status).toBeOneOf(["pass", "warn", "fail"]);
|
|
78
|
+
expect(versionCheck?.message).toContain("ap");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("includes package-json-sync check", async () => {
|
|
82
|
+
const checks = await checkVersion(mockConfig, "/tmp/.agentplate");
|
|
83
|
+
|
|
84
|
+
const syncCheck = checks.find((c) => c.name === "package-json-sync");
|
|
85
|
+
expect(syncCheck).toBeDefined();
|
|
86
|
+
expect(syncCheck?.status).toBeOneOf(["pass", "warn", "fail"]);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("version-current check reports version string", async () => {
|
|
90
|
+
const checks = await checkVersion(mockConfig, "/tmp/.agentplate");
|
|
91
|
+
|
|
92
|
+
const versionCheck = checks.find((c) => c.name === "version-current");
|
|
93
|
+
expect(versionCheck).toBeDefined();
|
|
94
|
+
|
|
95
|
+
if (versionCheck?.status === "pass") {
|
|
96
|
+
// Message should contain version in format "ap vX.Y.Z"
|
|
97
|
+
expect(versionCheck.message).toMatch(/ap v\d+\.\d+\.\d+/);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("package-json-sync check provides details", async () => {
|
|
102
|
+
const checks = await checkVersion(mockConfig, "/tmp/.agentplate");
|
|
103
|
+
|
|
104
|
+
const syncCheck = checks.find((c) => c.name === "package-json-sync");
|
|
105
|
+
expect(syncCheck).toBeDefined();
|
|
106
|
+
|
|
107
|
+
if (syncCheck?.status === "pass") {
|
|
108
|
+
// Should include version details
|
|
109
|
+
expect(syncCheck.details).toBeDefined();
|
|
110
|
+
expect(syncCheck.details?.length).toBeGreaterThan(0);
|
|
111
|
+
|
|
112
|
+
// Details should mention both package.json and src/version.ts
|
|
113
|
+
const detailsText = syncCheck.details?.join(" ");
|
|
114
|
+
expect(detailsText).toContain("package.json");
|
|
115
|
+
expect(detailsText).toContain("src/version.ts");
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("all checks have required DoctorCheck fields", async () => {
|
|
120
|
+
const checks = await checkVersion(mockConfig, "/tmp/.agentplate");
|
|
121
|
+
|
|
122
|
+
for (const check of checks) {
|
|
123
|
+
expect(check).toHaveProperty("name");
|
|
124
|
+
expect(check).toHaveProperty("category");
|
|
125
|
+
expect(check).toHaveProperty("status");
|
|
126
|
+
expect(check).toHaveProperty("message");
|
|
127
|
+
|
|
128
|
+
expect(typeof check.name).toBe("string");
|
|
129
|
+
expect(typeof check.message).toBe("string");
|
|
130
|
+
expect(["pass", "warn", "fail"]).toContain(check.status);
|
|
131
|
+
|
|
132
|
+
if (check.details !== undefined) {
|
|
133
|
+
expect(check.details).toBeArray();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (check.fixable !== undefined) {
|
|
137
|
+
expect(typeof check.fixable).toBe("boolean");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("checkCurrentVersion", () => {
|
|
144
|
+
test("passes against real repo root", async () => {
|
|
145
|
+
// Use the real agentplate repo root (two levels up from src/doctor/)
|
|
146
|
+
const toolRoot = join(import.meta.dir, "..", "..");
|
|
147
|
+
const check = await checkCurrentVersion(toolRoot);
|
|
148
|
+
expect(check.name).toBe("version-current");
|
|
149
|
+
expect(check.category).toBe("version");
|
|
150
|
+
expect(check.status).toBe("pass");
|
|
151
|
+
expect(check.message).toMatch(/ap v\d+\.\d+\.\d+/);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("fails for temp dir without version field", async () => {
|
|
155
|
+
const tempDir = await mkdtemp(join(tmpdir(), "version-test-"));
|
|
156
|
+
try {
|
|
157
|
+
// Write a package.json without a version field
|
|
158
|
+
await Bun.write(join(tempDir, "package.json"), JSON.stringify({ name: "test" }));
|
|
159
|
+
const check = await checkCurrentVersion(tempDir);
|
|
160
|
+
expect(check.name).toBe("version-current");
|
|
161
|
+
expect(check.status).toBe("fail");
|
|
162
|
+
expect(check.message).toContain("no version field");
|
|
163
|
+
} finally {
|
|
164
|
+
await cleanupTempDir(tempDir);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("fails for temp dir without package.json", async () => {
|
|
169
|
+
const tempDir = await mkdtemp(join(tmpdir(), "version-test-"));
|
|
170
|
+
try {
|
|
171
|
+
const check = await checkCurrentVersion(tempDir);
|
|
172
|
+
expect(check.name).toBe("version-current");
|
|
173
|
+
expect(check.status).toBe("fail");
|
|
174
|
+
} finally {
|
|
175
|
+
await cleanupTempDir(tempDir);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe("checkVersionSync", () => {
|
|
181
|
+
let tempDir: string;
|
|
182
|
+
|
|
183
|
+
beforeEach(async () => {
|
|
184
|
+
tempDir = await mkdtemp(join(tmpdir(), "version-sync-test-"));
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
afterEach(async () => {
|
|
188
|
+
await cleanupTempDir(tempDir);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("passes when versions match", async () => {
|
|
192
|
+
await Bun.write(
|
|
193
|
+
join(tempDir, "package.json"),
|
|
194
|
+
JSON.stringify({ name: "test", version: "1.2.3" }),
|
|
195
|
+
);
|
|
196
|
+
const { mkdir } = await import("node:fs/promises");
|
|
197
|
+
await mkdir(join(tempDir, "src"), { recursive: true });
|
|
198
|
+
await Bun.write(
|
|
199
|
+
join(tempDir, "src", "version.ts"),
|
|
200
|
+
'const VERSION = "1.2.3";\nexport { VERSION };\n',
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
const check = await checkVersionSync(tempDir);
|
|
204
|
+
expect(check.name).toBe("package-json-sync");
|
|
205
|
+
expect(check.category).toBe("version");
|
|
206
|
+
expect(check.status).toBe("pass");
|
|
207
|
+
expect(check.message).toContain("synchronized");
|
|
208
|
+
expect(check.details?.join(" ")).toContain("1.2.3");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("warns when versions mismatch", async () => {
|
|
212
|
+
await Bun.write(
|
|
213
|
+
join(tempDir, "package.json"),
|
|
214
|
+
JSON.stringify({ name: "test", version: "1.0.0" }),
|
|
215
|
+
);
|
|
216
|
+
const { mkdir } = await import("node:fs/promises");
|
|
217
|
+
await mkdir(join(tempDir, "src"), { recursive: true });
|
|
218
|
+
await Bun.write(
|
|
219
|
+
join(tempDir, "src", "version.ts"),
|
|
220
|
+
'const VERSION = "2.0.0";\nexport { VERSION };\n',
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
const check = await checkVersionSync(tempDir);
|
|
224
|
+
expect(check.name).toBe("package-json-sync");
|
|
225
|
+
expect(check.status).toBe("warn");
|
|
226
|
+
expect(check.message).toContain("mismatch");
|
|
227
|
+
expect(check.fixable).toBe(true);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test("warns when src/version.ts is missing", async () => {
|
|
231
|
+
await Bun.write(
|
|
232
|
+
join(tempDir, "package.json"),
|
|
233
|
+
JSON.stringify({ name: "test", version: "1.0.0" }),
|
|
234
|
+
);
|
|
235
|
+
// No src/version.ts created
|
|
236
|
+
|
|
237
|
+
const check = await checkVersionSync(tempDir);
|
|
238
|
+
expect(check.name).toBe("package-json-sync");
|
|
239
|
+
expect(check.status).toBe("warn");
|
|
240
|
+
});
|
|
241
|
+
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import type { DoctorCheck, DoctorCheckFn } from "./types.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Version compatibility checks.
|
|
6
|
+
* Validates agentplate CLI version, config schema version, database schema versions.
|
|
7
|
+
*/
|
|
8
|
+
export const checkVersion: DoctorCheckFn = async (
|
|
9
|
+
_config,
|
|
10
|
+
_agentplateDir,
|
|
11
|
+
): Promise<DoctorCheck[]> => {
|
|
12
|
+
const checks: DoctorCheck[] = [];
|
|
13
|
+
|
|
14
|
+
// Determine agentplate tool root (not the target project)
|
|
15
|
+
// import.meta.dir is src/doctor/, so go up two levels to repo root
|
|
16
|
+
const toolRoot = join(import.meta.dir, "..", "..");
|
|
17
|
+
|
|
18
|
+
// Check 1: version-current (read package.json)
|
|
19
|
+
const versionCheck = await checkCurrentVersion(toolRoot);
|
|
20
|
+
checks.push(versionCheck);
|
|
21
|
+
|
|
22
|
+
// Check 2: package-json-sync (compare package.json and src/index.ts)
|
|
23
|
+
const syncCheck = await checkVersionSync(toolRoot);
|
|
24
|
+
checks.push(syncCheck);
|
|
25
|
+
|
|
26
|
+
return checks;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Check that the current version can be determined from package.json.
|
|
31
|
+
* @internal Exported for testing.
|
|
32
|
+
*/
|
|
33
|
+
export async function checkCurrentVersion(toolRoot: string): Promise<DoctorCheck> {
|
|
34
|
+
try {
|
|
35
|
+
const packageJsonPath = join(toolRoot, "package.json");
|
|
36
|
+
const packageJson = (await Bun.file(packageJsonPath).json()) as { version?: string };
|
|
37
|
+
|
|
38
|
+
if (!packageJson.version) {
|
|
39
|
+
return {
|
|
40
|
+
name: "version-current",
|
|
41
|
+
category: "version",
|
|
42
|
+
status: "fail",
|
|
43
|
+
message: "Cannot determine version (package.json has no version field)",
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
name: "version-current",
|
|
49
|
+
category: "version",
|
|
50
|
+
status: "pass",
|
|
51
|
+
message: `ap v${packageJson.version}`,
|
|
52
|
+
};
|
|
53
|
+
} catch (error) {
|
|
54
|
+
return {
|
|
55
|
+
name: "version-current",
|
|
56
|
+
category: "version",
|
|
57
|
+
status: "fail",
|
|
58
|
+
message: "Cannot determine version",
|
|
59
|
+
details: [error instanceof Error ? error.message : String(error)],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Check that package.json version matches src/index.ts VERSION constant.
|
|
66
|
+
* @internal Exported for testing.
|
|
67
|
+
*/
|
|
68
|
+
export async function checkVersionSync(toolRoot: string): Promise<DoctorCheck> {
|
|
69
|
+
try {
|
|
70
|
+
// Read package.json version
|
|
71
|
+
const packageJsonPath = join(toolRoot, "package.json");
|
|
72
|
+
const packageJson = (await Bun.file(packageJsonPath).json()) as { version?: string };
|
|
73
|
+
const pkgVersion = packageJson.version;
|
|
74
|
+
|
|
75
|
+
if (!pkgVersion) {
|
|
76
|
+
return {
|
|
77
|
+
name: "package-json-sync",
|
|
78
|
+
category: "version",
|
|
79
|
+
status: "warn",
|
|
80
|
+
message: "Cannot verify version sync (package.json has no version)",
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Read src/version.ts and extract the VERSION constant (the single
|
|
85
|
+
// source of truth shared by every bundled bin).
|
|
86
|
+
const versionPath = join(toolRoot, "src", "version.ts");
|
|
87
|
+
const versionContent = await Bun.file(versionPath).text();
|
|
88
|
+
|
|
89
|
+
// Regex to find: const VERSION = "x.y.z" or export const VERSION = "x.y.z"
|
|
90
|
+
const versionRegex = /(?:export\s+)?const\s+VERSION\s*=\s*["']([^"']+)["']/;
|
|
91
|
+
const match = versionRegex.exec(versionContent);
|
|
92
|
+
|
|
93
|
+
if (!match?.[1]) {
|
|
94
|
+
return {
|
|
95
|
+
name: "package-json-sync",
|
|
96
|
+
category: "version",
|
|
97
|
+
status: "warn",
|
|
98
|
+
message: "Cannot find VERSION constant in src/version.ts",
|
|
99
|
+
details: [`package.json version: ${pkgVersion}`],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const indexVersion = match[1];
|
|
104
|
+
|
|
105
|
+
if (pkgVersion === indexVersion) {
|
|
106
|
+
return {
|
|
107
|
+
name: "package-json-sync",
|
|
108
|
+
category: "version",
|
|
109
|
+
status: "pass",
|
|
110
|
+
message: "Versions are synchronized",
|
|
111
|
+
details: [`package.json: ${pkgVersion}`, `src/version.ts: ${indexVersion}`],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
name: "package-json-sync",
|
|
117
|
+
category: "version",
|
|
118
|
+
status: "warn",
|
|
119
|
+
message: "Version mismatch between package.json and src/version.ts",
|
|
120
|
+
details: [`package.json: ${pkgVersion}`, `src/version.ts: ${indexVersion}`],
|
|
121
|
+
fixable: true,
|
|
122
|
+
};
|
|
123
|
+
} catch (error) {
|
|
124
|
+
return {
|
|
125
|
+
name: "package-json-sync",
|
|
126
|
+
category: "version",
|
|
127
|
+
status: "warn",
|
|
128
|
+
message: "Cannot verify version sync",
|
|
129
|
+
details: [error instanceof Error ? error.message : String(error)],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|