@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,648 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command: ap inspect <agent-name>
|
|
3
|
+
*
|
|
4
|
+
* Deep per-agent inspection aggregating data from EventStore, SessionStore,
|
|
5
|
+
* MetricsStore, and tmux capture-pane.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readdir } from "node:fs/promises";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { Command } from "commander";
|
|
11
|
+
import { loadConfig } from "../config.ts";
|
|
12
|
+
import { ValidationError } from "../errors.ts";
|
|
13
|
+
import { createEventStore } from "../events/store.ts";
|
|
14
|
+
import { jsonOutput } from "../json.ts";
|
|
15
|
+
import { accent } from "../logging/color.ts";
|
|
16
|
+
import { formatDuration } from "../logging/format.ts";
|
|
17
|
+
import { renderHeader, separator, stateIconColored } from "../logging/theme.ts";
|
|
18
|
+
import { createMetricsStore } from "../metrics/store.ts";
|
|
19
|
+
import { openSessionStore } from "../sessions/compat.ts";
|
|
20
|
+
import type { AgentSession, StoredEvent, ToolStats } from "../types.ts";
|
|
21
|
+
import { TMUX_SOCKET } from "../worktree/tmux.ts";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Extract current file from most recent Edit/Write/Read tool_start event.
|
|
25
|
+
*/
|
|
26
|
+
function extractCurrentFile(events: StoredEvent[]): string | null {
|
|
27
|
+
// Scan backwards for tool_start events with Edit/Write/Read
|
|
28
|
+
const fileTools = ["Edit", "Write", "Read"];
|
|
29
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
30
|
+
const event = events[i];
|
|
31
|
+
if (
|
|
32
|
+
event &&
|
|
33
|
+
event.eventType === "tool_start" &&
|
|
34
|
+
event.toolName &&
|
|
35
|
+
fileTools.includes(event.toolName) &&
|
|
36
|
+
event.toolArgs
|
|
37
|
+
) {
|
|
38
|
+
try {
|
|
39
|
+
const args = JSON.parse(event.toolArgs) as Record<string, unknown>;
|
|
40
|
+
const filePath = (args.file_path as string) ?? (args.path as string);
|
|
41
|
+
if (filePath) {
|
|
42
|
+
return filePath;
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
// Failed to parse JSON, continue
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Summarize tool arguments for display (truncate long values).
|
|
54
|
+
*/
|
|
55
|
+
function summarizeArgs(toolArgs: string | null): string {
|
|
56
|
+
if (!toolArgs) return "";
|
|
57
|
+
try {
|
|
58
|
+
const parsed = JSON.parse(toolArgs) as Record<string, unknown>;
|
|
59
|
+
const entries = Object.entries(parsed)
|
|
60
|
+
.map(([key, value]) => {
|
|
61
|
+
const str = String(value);
|
|
62
|
+
return `${key}=${str.length > 40 ? `${str.slice(0, 37)}...` : str}`;
|
|
63
|
+
})
|
|
64
|
+
.join(", ");
|
|
65
|
+
return entries.length > 100 ? `${entries.slice(0, 97)}...` : entries;
|
|
66
|
+
} catch {
|
|
67
|
+
return toolArgs.length > 100 ? `${toolArgs.slice(0, 97)}...` : toolArgs;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Capture tmux pane output.
|
|
73
|
+
*/
|
|
74
|
+
async function captureTmux(sessionName: string, lines: number): Promise<string | null> {
|
|
75
|
+
try {
|
|
76
|
+
const proc = Bun.spawn(
|
|
77
|
+
["tmux", "-L", TMUX_SOCKET, "capture-pane", "-t", sessionName, "-p", "-S", `-${lines}`],
|
|
78
|
+
{
|
|
79
|
+
stdout: "pipe",
|
|
80
|
+
stderr: "pipe",
|
|
81
|
+
},
|
|
82
|
+
);
|
|
83
|
+
const exitCode = await proc.exited;
|
|
84
|
+
if (exitCode !== 0) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const output = await new Response(proc.stdout).text();
|
|
88
|
+
return output.trim();
|
|
89
|
+
} catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Parsed data from a headless agent's stdout.log NDJSON event stream. */
|
|
95
|
+
interface StdoutLogData {
|
|
96
|
+
toolCalls: Array<{
|
|
97
|
+
toolName: string;
|
|
98
|
+
argsSummary: string;
|
|
99
|
+
durationMs: number | null;
|
|
100
|
+
timestamp: string;
|
|
101
|
+
}>;
|
|
102
|
+
cumulativeInputTokens: number;
|
|
103
|
+
cumulativeOutputTokens: number;
|
|
104
|
+
cumulativeCacheReadTokens: number;
|
|
105
|
+
lastModel: string;
|
|
106
|
+
lastContextUtilization: number | null;
|
|
107
|
+
currentTurn: number;
|
|
108
|
+
isMidTool: boolean;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Find the most recent log directory for a headless agent.
|
|
113
|
+
* Looks under logsBaseDir/{agentName}/ and returns the last entry
|
|
114
|
+
* when sorted alphabetically (ISO timestamps sort = chronological).
|
|
115
|
+
*/
|
|
116
|
+
async function findLatestLogDir(logsBaseDir: string, agentName: string): Promise<string | null> {
|
|
117
|
+
const agentLogsDir = join(logsBaseDir, agentName);
|
|
118
|
+
try {
|
|
119
|
+
const entries = await readdir(agentLogsDir);
|
|
120
|
+
if (entries.length === 0) return null;
|
|
121
|
+
entries.sort();
|
|
122
|
+
const latest = entries[entries.length - 1];
|
|
123
|
+
if (!latest) return null;
|
|
124
|
+
return join(agentLogsDir, latest);
|
|
125
|
+
} catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Parse the last 200 lines of a headless agent's stdout.log NDJSON file.
|
|
132
|
+
*
|
|
133
|
+
* Extracts tool call activity and token usage from Sapling/Codex event streams.
|
|
134
|
+
* Handles partial lines and malformed JSON gracefully.
|
|
135
|
+
*
|
|
136
|
+
* @param logPath - Absolute path to stdout.log
|
|
137
|
+
* @returns Parsed data, or null if file missing or unreadable
|
|
138
|
+
*/
|
|
139
|
+
async function parseStdoutLog(logPath: string): Promise<StdoutLogData | null> {
|
|
140
|
+
const file = Bun.file(logPath);
|
|
141
|
+
if (!(await file.exists())) return null;
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const text = await file.text();
|
|
145
|
+
const allLines = text.split("\n");
|
|
146
|
+
// Tail last 200 lines for efficiency
|
|
147
|
+
const lines = allLines.length > 200 ? allLines.slice(-200) : allLines;
|
|
148
|
+
|
|
149
|
+
const toolCalls: StdoutLogData["toolCalls"] = [];
|
|
150
|
+
|
|
151
|
+
// Track pending tool_start events for durationMs matching.
|
|
152
|
+
// When tool_end arrives, pop the most recent pending entry with matching toolName.
|
|
153
|
+
const pendingTools: Array<{
|
|
154
|
+
toolName: string;
|
|
155
|
+
argsSummary: string;
|
|
156
|
+
timestamp: string;
|
|
157
|
+
}> = [];
|
|
158
|
+
|
|
159
|
+
let cumulativeInputTokens = 0;
|
|
160
|
+
let cumulativeOutputTokens = 0;
|
|
161
|
+
let cumulativeCacheReadTokens = 0;
|
|
162
|
+
let lastModel = "";
|
|
163
|
+
let lastContextUtilization: number | null = null;
|
|
164
|
+
let currentTurn = 0;
|
|
165
|
+
let lastEventType: string | null = null;
|
|
166
|
+
|
|
167
|
+
for (const line of lines) {
|
|
168
|
+
const trimmed = line.trim();
|
|
169
|
+
if (!trimmed) continue;
|
|
170
|
+
|
|
171
|
+
let event: Record<string, unknown>;
|
|
172
|
+
try {
|
|
173
|
+
event = JSON.parse(trimmed) as Record<string, unknown>;
|
|
174
|
+
} catch {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const type = typeof event.type === "string" ? event.type : null;
|
|
179
|
+
if (!type) continue;
|
|
180
|
+
|
|
181
|
+
lastEventType = type;
|
|
182
|
+
const timestamp =
|
|
183
|
+
typeof event.timestamp === "string" ? event.timestamp : new Date().toISOString();
|
|
184
|
+
|
|
185
|
+
if (type === "tool_start") {
|
|
186
|
+
const toolName = typeof event.toolName === "string" ? event.toolName : "unknown";
|
|
187
|
+
const argsSummary = typeof event.argsSummary === "string" ? event.argsSummary : "";
|
|
188
|
+
pendingTools.push({ toolName, argsSummary, timestamp });
|
|
189
|
+
} else if (type === "tool_end") {
|
|
190
|
+
const toolName = typeof event.toolName === "string" ? event.toolName : "";
|
|
191
|
+
const durationMs = typeof event.durationMs === "number" ? event.durationMs : null;
|
|
192
|
+
|
|
193
|
+
// Find and pop the most recent matching pending tool
|
|
194
|
+
let pendingIdx = -1;
|
|
195
|
+
for (let i = pendingTools.length - 1; i >= 0; i--) {
|
|
196
|
+
if (pendingTools[i]?.toolName === toolName) {
|
|
197
|
+
pendingIdx = i;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (pendingIdx >= 0) {
|
|
202
|
+
const pending = pendingTools[pendingIdx];
|
|
203
|
+
if (pending) {
|
|
204
|
+
pendingTools.splice(pendingIdx, 1);
|
|
205
|
+
toolCalls.push({
|
|
206
|
+
toolName: pending.toolName,
|
|
207
|
+
argsSummary: pending.argsSummary,
|
|
208
|
+
durationMs,
|
|
209
|
+
timestamp: pending.timestamp,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
} else if (type === "turn_start") {
|
|
214
|
+
const turn = typeof event.turn === "number" ? event.turn : currentTurn + 1;
|
|
215
|
+
currentTurn = turn;
|
|
216
|
+
} else if (type === "turn_end") {
|
|
217
|
+
const inputTokens = typeof event.inputTokens === "number" ? event.inputTokens : 0;
|
|
218
|
+
const outputTokens = typeof event.outputTokens === "number" ? event.outputTokens : 0;
|
|
219
|
+
const cacheReadTokens =
|
|
220
|
+
typeof event.cacheReadTokens === "number" ? event.cacheReadTokens : 0;
|
|
221
|
+
const model = typeof event.model === "string" ? event.model : "";
|
|
222
|
+
const ctxUtil =
|
|
223
|
+
typeof event.contextUtilization === "number" ? event.contextUtilization : null;
|
|
224
|
+
|
|
225
|
+
cumulativeInputTokens += inputTokens;
|
|
226
|
+
cumulativeOutputTokens += outputTokens;
|
|
227
|
+
cumulativeCacheReadTokens += cacheReadTokens;
|
|
228
|
+
if (model) lastModel = model;
|
|
229
|
+
if (ctxUtil !== null) lastContextUtilization = ctxUtil;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Any still-pending tool_starts are mid-execution — include them without durationMs
|
|
234
|
+
for (const pending of pendingTools) {
|
|
235
|
+
toolCalls.push({
|
|
236
|
+
toolName: pending.toolName,
|
|
237
|
+
argsSummary: pending.argsSummary,
|
|
238
|
+
durationMs: null,
|
|
239
|
+
timestamp: pending.timestamp,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
toolCalls,
|
|
245
|
+
cumulativeInputTokens,
|
|
246
|
+
cumulativeOutputTokens,
|
|
247
|
+
cumulativeCacheReadTokens,
|
|
248
|
+
lastModel,
|
|
249
|
+
lastContextUtilization,
|
|
250
|
+
currentTurn,
|
|
251
|
+
isMidTool: lastEventType === "tool_start",
|
|
252
|
+
};
|
|
253
|
+
} catch {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export interface InspectData {
|
|
259
|
+
session: AgentSession;
|
|
260
|
+
timeSinceLastActivity: number;
|
|
261
|
+
recentToolCalls: Array<{
|
|
262
|
+
toolName: string;
|
|
263
|
+
args: string;
|
|
264
|
+
durationMs: number | null;
|
|
265
|
+
timestamp: string;
|
|
266
|
+
}>;
|
|
267
|
+
currentFile: string | null;
|
|
268
|
+
toolStats: ToolStats[];
|
|
269
|
+
tokenUsage: {
|
|
270
|
+
inputTokens: number;
|
|
271
|
+
outputTokens: number;
|
|
272
|
+
cacheReadTokens: number;
|
|
273
|
+
cacheCreationTokens: number;
|
|
274
|
+
estimatedCostUsd: number | null;
|
|
275
|
+
modelUsed: string | null;
|
|
276
|
+
} | null;
|
|
277
|
+
tmuxOutput: string | null;
|
|
278
|
+
/** Turn progress for headless agents (populated from stdout.log). */
|
|
279
|
+
headlessTurnInfo: {
|
|
280
|
+
currentTurn: number;
|
|
281
|
+
contextUtilization: number | null;
|
|
282
|
+
isMidTool: boolean;
|
|
283
|
+
} | null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Gather all inspection data for an agent.
|
|
288
|
+
*/
|
|
289
|
+
export async function gatherInspectData(
|
|
290
|
+
root: string,
|
|
291
|
+
agentName: string,
|
|
292
|
+
opts: {
|
|
293
|
+
limit?: number;
|
|
294
|
+
noTmux?: boolean;
|
|
295
|
+
tmuxLines?: number;
|
|
296
|
+
} = {},
|
|
297
|
+
): Promise<InspectData> {
|
|
298
|
+
const agentplateDir = join(root, ".agentplate");
|
|
299
|
+
const { store } = openSessionStore(agentplateDir);
|
|
300
|
+
|
|
301
|
+
let session: AgentSession | null = null;
|
|
302
|
+
try {
|
|
303
|
+
session = store.getByName(agentName);
|
|
304
|
+
if (!session) {
|
|
305
|
+
throw new ValidationError(`Agent not found: ${agentName}`, {
|
|
306
|
+
field: "agent-name",
|
|
307
|
+
value: agentName,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const now = Date.now();
|
|
312
|
+
const timeSinceLastActivity = now - new Date(session.lastActivity).getTime();
|
|
313
|
+
|
|
314
|
+
// EventStore: recent tool calls and tool stats
|
|
315
|
+
let recentToolCalls: InspectData["recentToolCalls"] = [];
|
|
316
|
+
let currentFile: string | null = null;
|
|
317
|
+
let toolStats: ToolStats[] = [];
|
|
318
|
+
|
|
319
|
+
const eventsDbPath = join(agentplateDir, "events.db");
|
|
320
|
+
const eventsFile = Bun.file(eventsDbPath);
|
|
321
|
+
if (await eventsFile.exists()) {
|
|
322
|
+
const eventStore = createEventStore(eventsDbPath);
|
|
323
|
+
try {
|
|
324
|
+
// Get recent events for this agent
|
|
325
|
+
const events = eventStore.getByAgent(agentName, { limit: 200 });
|
|
326
|
+
|
|
327
|
+
// Extract current file from most recent Edit/Write/Read tool_start
|
|
328
|
+
currentFile = extractCurrentFile(events);
|
|
329
|
+
|
|
330
|
+
// Filter to tool_start events for recent tool calls display
|
|
331
|
+
const toolStartEvents = events.filter((e) => e.eventType === "tool_start");
|
|
332
|
+
const limit = opts.limit ?? 20;
|
|
333
|
+
recentToolCalls = toolStartEvents.slice(0, limit).map((event) => ({
|
|
334
|
+
toolName: event.toolName ?? "unknown",
|
|
335
|
+
args: summarizeArgs(event.toolArgs),
|
|
336
|
+
durationMs: event.toolDurationMs,
|
|
337
|
+
timestamp: event.createdAt,
|
|
338
|
+
}));
|
|
339
|
+
|
|
340
|
+
// Tool usage statistics
|
|
341
|
+
toolStats = eventStore.getToolStats({ agentName });
|
|
342
|
+
} finally {
|
|
343
|
+
eventStore.close();
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// MetricsStore: token usage
|
|
348
|
+
let tokenUsage: InspectData["tokenUsage"] = null;
|
|
349
|
+
const metricsDbPath = join(agentplateDir, "metrics.db");
|
|
350
|
+
const metricsFile = Bun.file(metricsDbPath);
|
|
351
|
+
if (await metricsFile.exists()) {
|
|
352
|
+
const metricsStore = createMetricsStore(metricsDbPath);
|
|
353
|
+
try {
|
|
354
|
+
const sessions = metricsStore.getSessionsByAgent(agentName);
|
|
355
|
+
const mostRecent = sessions[0];
|
|
356
|
+
if (mostRecent) {
|
|
357
|
+
tokenUsage = {
|
|
358
|
+
inputTokens: mostRecent.inputTokens,
|
|
359
|
+
outputTokens: mostRecent.outputTokens,
|
|
360
|
+
cacheReadTokens: mostRecent.cacheReadTokens,
|
|
361
|
+
cacheCreationTokens: mostRecent.cacheCreationTokens,
|
|
362
|
+
estimatedCostUsd: mostRecent.estimatedCostUsd,
|
|
363
|
+
modelUsed: mostRecent.modelUsed,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
} finally {
|
|
367
|
+
metricsStore.close();
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// tmux capture (skipped for headless agents where tmuxSession is empty)
|
|
372
|
+
let tmuxOutput: string | null = null;
|
|
373
|
+
if (!opts.noTmux && session.tmuxSession) {
|
|
374
|
+
const lines = opts.tmuxLines ?? 30;
|
|
375
|
+
tmuxOutput = await captureTmux(session.tmuxSession, lines);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Headless stdout.log fallback: parse NDJSON event stream for rich activity data.
|
|
379
|
+
// Used when tmuxSession is empty (headless agent: sapling, codex, etc.).
|
|
380
|
+
let headlessTurnInfo: InspectData["headlessTurnInfo"] = null;
|
|
381
|
+
if (session.tmuxSession === "") {
|
|
382
|
+
const logsBaseDir = join(agentplateDir, "logs");
|
|
383
|
+
const latestLogDir = await findLatestLogDir(logsBaseDir, agentName);
|
|
384
|
+
if (latestLogDir !== null) {
|
|
385
|
+
const stdoutData = await parseStdoutLog(join(latestLogDir, "stdout.log"));
|
|
386
|
+
if (stdoutData !== null) {
|
|
387
|
+
// Populate recentToolCalls from stdout.log when events.db had nothing.
|
|
388
|
+
if (recentToolCalls.length === 0 && stdoutData.toolCalls.length > 0) {
|
|
389
|
+
const limit = opts.limit ?? 20;
|
|
390
|
+
recentToolCalls = stdoutData.toolCalls.slice(0, limit).map((call) => ({
|
|
391
|
+
toolName: call.toolName,
|
|
392
|
+
args: call.argsSummary,
|
|
393
|
+
durationMs: call.durationMs,
|
|
394
|
+
timestamp: call.timestamp,
|
|
395
|
+
}));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Populate tokenUsage from turn_end events when metrics.db had nothing.
|
|
399
|
+
if (
|
|
400
|
+
tokenUsage === null &&
|
|
401
|
+
(stdoutData.cumulativeInputTokens > 0 || stdoutData.cumulativeOutputTokens > 0)
|
|
402
|
+
) {
|
|
403
|
+
tokenUsage = {
|
|
404
|
+
inputTokens: stdoutData.cumulativeInputTokens,
|
|
405
|
+
outputTokens: stdoutData.cumulativeOutputTokens,
|
|
406
|
+
cacheReadTokens: stdoutData.cumulativeCacheReadTokens,
|
|
407
|
+
cacheCreationTokens: 0,
|
|
408
|
+
estimatedCostUsd: null,
|
|
409
|
+
modelUsed: stdoutData.lastModel || null,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Always populate turn progress info for headless agents.
|
|
414
|
+
headlessTurnInfo = {
|
|
415
|
+
currentTurn: stdoutData.currentTurn,
|
|
416
|
+
contextUtilization: stdoutData.lastContextUtilization,
|
|
417
|
+
isMidTool: stdoutData.isMidTool,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Headless fallback: show recent events as live output when no tmux
|
|
424
|
+
if (!tmuxOutput && session.tmuxSession === "" && recentToolCalls.length > 0) {
|
|
425
|
+
const lines: string[] = ["[Headless agent — showing recent tool events]", ""];
|
|
426
|
+
for (const call of recentToolCalls.slice(0, 15)) {
|
|
427
|
+
const time = new Date(call.timestamp).toLocaleTimeString();
|
|
428
|
+
const dur = call.durationMs !== null ? `${call.durationMs}ms` : "pending";
|
|
429
|
+
lines.push(` [${time}] ${call.toolName.padEnd(15)} ${dur}`);
|
|
430
|
+
}
|
|
431
|
+
tmuxOutput = lines.join("\n");
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return {
|
|
435
|
+
session,
|
|
436
|
+
timeSinceLastActivity,
|
|
437
|
+
recentToolCalls,
|
|
438
|
+
currentFile,
|
|
439
|
+
toolStats,
|
|
440
|
+
tokenUsage,
|
|
441
|
+
tmuxOutput,
|
|
442
|
+
headlessTurnInfo,
|
|
443
|
+
};
|
|
444
|
+
} finally {
|
|
445
|
+
store.close();
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Print inspection data in human-readable format.
|
|
451
|
+
*/
|
|
452
|
+
export function printInspectData(data: InspectData): void {
|
|
453
|
+
const w = process.stdout.write.bind(process.stdout);
|
|
454
|
+
const { session } = data;
|
|
455
|
+
|
|
456
|
+
w(`\n${renderHeader(`Agent Inspection: ${accent(session.agentName)}`)}\n\n`);
|
|
457
|
+
|
|
458
|
+
// Agent state and metadata
|
|
459
|
+
w(`${stateIconColored(session.state)} State: ${session.state}\n`);
|
|
460
|
+
w(`Last activity: ${formatDuration(data.timeSinceLastActivity)} ago\n`);
|
|
461
|
+
w(`Task: ${accent(session.taskId)}\n`);
|
|
462
|
+
w(`Capability: ${session.capability}\n`);
|
|
463
|
+
w(`Branch: ${accent(session.branchName)}\n`);
|
|
464
|
+
if (session.parentAgent) {
|
|
465
|
+
w(`Parent: ${accent(session.parentAgent)} (depth: ${session.depth})\n`);
|
|
466
|
+
}
|
|
467
|
+
w(`Started: ${session.startedAt}\n`);
|
|
468
|
+
if (session.tmuxSession) {
|
|
469
|
+
w(`Tmux: ${accent(session.tmuxSession)}\n`);
|
|
470
|
+
} else if (session.pid !== null) {
|
|
471
|
+
w(`Process: PID ${accent(String(session.pid))} (headless)\n`);
|
|
472
|
+
}
|
|
473
|
+
w("\n");
|
|
474
|
+
|
|
475
|
+
// Current file
|
|
476
|
+
if (data.currentFile) {
|
|
477
|
+
w(`Current file: ${data.currentFile}\n\n`);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Headless turn progress
|
|
481
|
+
if (data.headlessTurnInfo) {
|
|
482
|
+
const { currentTurn, contextUtilization, isMidTool } = data.headlessTurnInfo;
|
|
483
|
+
w("Turn Progress\n");
|
|
484
|
+
w(`${separator()}\n`);
|
|
485
|
+
if (currentTurn > 0) {
|
|
486
|
+
w(` Current turn: ${currentTurn}\n`);
|
|
487
|
+
}
|
|
488
|
+
if (contextUtilization !== null) {
|
|
489
|
+
const pct = (contextUtilization * 100).toFixed(1);
|
|
490
|
+
w(` Context usage: ${pct}%\n`);
|
|
491
|
+
}
|
|
492
|
+
const status = isMidTool ? "executing tool" : "between turns";
|
|
493
|
+
w(` Status: ${status}\n`);
|
|
494
|
+
w("\n");
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Token usage
|
|
498
|
+
if (data.tokenUsage) {
|
|
499
|
+
w("Token Usage\n");
|
|
500
|
+
w(`${separator()}\n`);
|
|
501
|
+
w(` Input: ${data.tokenUsage.inputTokens.toLocaleString()}\n`);
|
|
502
|
+
w(` Output: ${data.tokenUsage.outputTokens.toLocaleString()}\n`);
|
|
503
|
+
w(` Cache read: ${data.tokenUsage.cacheReadTokens.toLocaleString()}\n`);
|
|
504
|
+
w(` Cache created: ${data.tokenUsage.cacheCreationTokens.toLocaleString()}\n`);
|
|
505
|
+
if (data.tokenUsage.estimatedCostUsd !== null) {
|
|
506
|
+
w(` Estimated cost: $${data.tokenUsage.estimatedCostUsd.toFixed(4)}\n`);
|
|
507
|
+
}
|
|
508
|
+
if (data.tokenUsage.modelUsed) {
|
|
509
|
+
w(` Model: ${data.tokenUsage.modelUsed}\n`);
|
|
510
|
+
}
|
|
511
|
+
w("\n");
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Tool usage statistics (top 10)
|
|
515
|
+
if (data.toolStats.length > 0) {
|
|
516
|
+
w("Tool Usage (Top 10)\n");
|
|
517
|
+
w(`${separator()}\n`);
|
|
518
|
+
const top10 = data.toolStats.slice(0, 10);
|
|
519
|
+
for (const stat of top10) {
|
|
520
|
+
const avgMs = stat.avgDurationMs.toFixed(0);
|
|
521
|
+
w(` ${stat.toolName.padEnd(20)} ${String(stat.count).padStart(6)} calls `);
|
|
522
|
+
w(`avg: ${String(avgMs).padStart(6)}ms max: ${stat.maxDurationMs}ms\n`);
|
|
523
|
+
}
|
|
524
|
+
w("\n");
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Recent tool calls
|
|
528
|
+
if (data.recentToolCalls.length > 0) {
|
|
529
|
+
w(`Recent Tool Calls (last ${data.recentToolCalls.length})\n`);
|
|
530
|
+
w(`${separator()}\n`);
|
|
531
|
+
for (const call of data.recentToolCalls) {
|
|
532
|
+
const time = new Date(call.timestamp).toLocaleTimeString();
|
|
533
|
+
const duration = call.durationMs !== null ? `${call.durationMs}ms` : "pending";
|
|
534
|
+
w(` [${time}] ${call.toolName.padEnd(15)} ${duration.padStart(10)}`);
|
|
535
|
+
if (call.args) {
|
|
536
|
+
w(` ${call.args}`);
|
|
537
|
+
}
|
|
538
|
+
w("\n");
|
|
539
|
+
}
|
|
540
|
+
w("\n");
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// tmux output (or headless fallback)
|
|
544
|
+
if (data.tmuxOutput) {
|
|
545
|
+
w(data.session.tmuxSession ? "Live Tmux Output\n" : "Recent Activity (headless)\n");
|
|
546
|
+
w(`${separator()}\n`);
|
|
547
|
+
w(`${data.tmuxOutput}\n`);
|
|
548
|
+
w(`${separator()}\n`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
interface InspectOpts {
|
|
553
|
+
json?: boolean;
|
|
554
|
+
follow?: boolean;
|
|
555
|
+
interval?: string;
|
|
556
|
+
limit?: string;
|
|
557
|
+
tmux?: boolean; // Commander: --no-tmux sets tmux=false
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
async function executeInspect(agentName: string, opts: InspectOpts): Promise<void> {
|
|
561
|
+
const json = opts.json ?? false;
|
|
562
|
+
const follow = opts.follow ?? false;
|
|
563
|
+
// Commander --no-tmux sets opts.tmux = false
|
|
564
|
+
const noTmux = opts.tmux === false;
|
|
565
|
+
|
|
566
|
+
const intervalStr = opts.interval;
|
|
567
|
+
const interval = intervalStr ? Number.parseInt(intervalStr, 10) : 3000;
|
|
568
|
+
if (Number.isNaN(interval) || interval < 500) {
|
|
569
|
+
throw new ValidationError("--interval must be a number >= 500 (milliseconds)", {
|
|
570
|
+
field: "interval",
|
|
571
|
+
value: intervalStr,
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const limitStr = opts.limit;
|
|
576
|
+
const limit = limitStr ? Number.parseInt(limitStr, 10) : 20;
|
|
577
|
+
if (Number.isNaN(limit) || limit < 1) {
|
|
578
|
+
throw new ValidationError("--limit must be a number >= 1", {
|
|
579
|
+
field: "limit",
|
|
580
|
+
value: limitStr,
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const cwd = process.cwd();
|
|
585
|
+
const config = await loadConfig(cwd);
|
|
586
|
+
const root = config.project.root;
|
|
587
|
+
|
|
588
|
+
if (follow) {
|
|
589
|
+
// Polling loop
|
|
590
|
+
while (true) {
|
|
591
|
+
// Clear screen
|
|
592
|
+
process.stdout.write("\x1b[2J\x1b[H");
|
|
593
|
+
const data = await gatherInspectData(root, agentName, {
|
|
594
|
+
limit,
|
|
595
|
+
noTmux,
|
|
596
|
+
tmuxLines: 30,
|
|
597
|
+
});
|
|
598
|
+
if (json) {
|
|
599
|
+
jsonOutput("inspect", data as unknown as Record<string, unknown>);
|
|
600
|
+
} else {
|
|
601
|
+
printInspectData(data);
|
|
602
|
+
}
|
|
603
|
+
await Bun.sleep(interval);
|
|
604
|
+
}
|
|
605
|
+
} else {
|
|
606
|
+
// Single snapshot
|
|
607
|
+
const data = await gatherInspectData(root, agentName, { limit, noTmux, tmuxLines: 30 });
|
|
608
|
+
if (json) {
|
|
609
|
+
jsonOutput("inspect", data as unknown as Record<string, unknown>);
|
|
610
|
+
} else {
|
|
611
|
+
printInspectData(data);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
export function createInspectCommand(): Command {
|
|
617
|
+
return new Command("inspect")
|
|
618
|
+
.description("Deep inspection of a single agent")
|
|
619
|
+
.argument("<agent-name>", "Agent name to inspect")
|
|
620
|
+
.option("--json", "Output as JSON")
|
|
621
|
+
.option("--follow", "Poll and refresh continuously")
|
|
622
|
+
.option("--interval <ms>", "Polling interval for --follow in milliseconds (default: 3000)")
|
|
623
|
+
.option("--limit <n>", "Number of recent tool calls to show (default: 20)")
|
|
624
|
+
.option("--no-tmux", "Skip tmux capture-pane")
|
|
625
|
+
.action(async (agentName: string, opts: InspectOpts) => {
|
|
626
|
+
await executeInspect(agentName, opts);
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
export async function inspectCommand(args: string[]): Promise<void> {
|
|
631
|
+
const cmd = createInspectCommand();
|
|
632
|
+
cmd.exitOverride();
|
|
633
|
+
try {
|
|
634
|
+
await cmd.parseAsync(args, { from: "user" });
|
|
635
|
+
} catch (err: unknown) {
|
|
636
|
+
if (err && typeof err === "object" && "code" in err) {
|
|
637
|
+
const code = (err as { code: string }).code;
|
|
638
|
+
if (code === "commander.helpDisplayed" || code === "commander.version") {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
if (code.startsWith("commander.")) {
|
|
642
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
643
|
+
throw new ValidationError(message, { field: "args" });
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
throw err;
|
|
647
|
+
}
|
|
648
|
+
}
|