@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,579 @@
|
|
|
1
|
+
// Claude Code runtime adapter for agentplate's AgentRuntime interface.
|
|
2
|
+
// Pure extraction — no new behavior. All implementation delegates to existing code.
|
|
3
|
+
// Phase 0: file exists and compiles. Callers are not rewired until Phase 2.
|
|
4
|
+
|
|
5
|
+
import { mkdir } from "node:fs/promises";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { deployHooks } from "../agents/hooks-deployer.ts";
|
|
8
|
+
import { estimateCost } from "../metrics/pricing.ts";
|
|
9
|
+
import { parseTranscriptUsage } from "../metrics/transcript.ts";
|
|
10
|
+
import type { ResolvedModel } from "../types.ts";
|
|
11
|
+
import type {
|
|
12
|
+
AgentEvent,
|
|
13
|
+
AgentRuntime,
|
|
14
|
+
DirectSpawnOpts,
|
|
15
|
+
HooksDef,
|
|
16
|
+
OverlayContent,
|
|
17
|
+
ReadyState,
|
|
18
|
+
SpawnOpts,
|
|
19
|
+
TranscriptSummary,
|
|
20
|
+
} from "./types.ts";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Claude Code runtime adapter.
|
|
24
|
+
*
|
|
25
|
+
* Implements AgentRuntime for the `claude` CLI (Anthropic's Claude Code).
|
|
26
|
+
* All methods delegate to existing agentplate subsystems — this adapter
|
|
27
|
+
* only provides the runtime-agnostic interface layer.
|
|
28
|
+
*
|
|
29
|
+
* Phase 0: file exists, compiles, and exports the class.
|
|
30
|
+
* Phase 2 will rewire callers (sling.ts, coordinator.ts, etc.) to use this adapter.
|
|
31
|
+
*/
|
|
32
|
+
export class ClaudeRuntime implements AgentRuntime {
|
|
33
|
+
/** Unique identifier for this runtime. */
|
|
34
|
+
readonly id = "claude";
|
|
35
|
+
|
|
36
|
+
/** Stability level. Claude Code is the primary runtime. */
|
|
37
|
+
readonly stability = "stable" as const;
|
|
38
|
+
|
|
39
|
+
/** Relative path to the instruction file within a worktree. */
|
|
40
|
+
readonly instructionPath = ".claude/CLAUDE.md";
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Build the shell command string to spawn an interactive Claude Code agent.
|
|
44
|
+
*
|
|
45
|
+
* Maps SpawnOpts to the `claude` CLI flags:
|
|
46
|
+
* - `model` → `--model <model>`
|
|
47
|
+
* - `permissionMode` → `--permission-mode <mode>`
|
|
48
|
+
* - "bypass" maps to "bypassPermissions"
|
|
49
|
+
* - "ask" maps to "default"
|
|
50
|
+
* - `appendSystemPrompt` → `--append-system-prompt '<escaped>'`
|
|
51
|
+
*
|
|
52
|
+
* The returned string is passed directly to tmux as the initial command.
|
|
53
|
+
* The `cwd` and `env` fields of SpawnOpts are handled by the tmux session
|
|
54
|
+
* creator, not embedded in the command string.
|
|
55
|
+
*
|
|
56
|
+
* @param opts - Spawn options (model, permissionMode, appendSystemPrompt)
|
|
57
|
+
* @returns Shell command string suitable for tmux new-session -c
|
|
58
|
+
*/
|
|
59
|
+
buildSpawnCommand(opts: SpawnOpts): string {
|
|
60
|
+
const permMode = opts.permissionMode === "bypass" ? "bypassPermissions" : "default";
|
|
61
|
+
let cmd = `claude --model ${opts.model} --permission-mode ${permMode}`;
|
|
62
|
+
|
|
63
|
+
if (opts.appendSystemPromptFile) {
|
|
64
|
+
// Read from file at shell expansion time — avoids tmux IPC message size
|
|
65
|
+
// limits (~8-16KB) that cause "command too long" errors when large agent
|
|
66
|
+
// definitions are inlined. The $(cat ...) expands inside the tmux pane's
|
|
67
|
+
// shell, so the tmux IPC message only carries the short command string.
|
|
68
|
+
const escaped = opts.appendSystemPromptFile.replace(/'/g, "'\\''");
|
|
69
|
+
cmd += ` --append-system-prompt "$(cat '${escaped}')"`;
|
|
70
|
+
} else if (opts.appendSystemPrompt) {
|
|
71
|
+
// Single-quote the content for safe shell expansion.
|
|
72
|
+
// POSIX single-quoted strings cannot contain single quotes, so escape
|
|
73
|
+
// them using the standard technique: end quote, escaped quote, start quote.
|
|
74
|
+
const escaped = opts.appendSystemPrompt.replace(/'/g, "'\\''");
|
|
75
|
+
cmd += ` --append-system-prompt '${escaped}'`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return cmd;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Build the argv array for a headless one-shot Claude invocation.
|
|
83
|
+
*
|
|
84
|
+
* Returns an argv array suitable for `Bun.spawn()`. The `--print` flag
|
|
85
|
+
* causes Claude Code to run the prompt and exit, writing output to stdout.
|
|
86
|
+
*
|
|
87
|
+
* Used by merge/resolver.ts (AI-assisted conflict resolution) and
|
|
88
|
+
* watchdog/triage.ts (AI-assisted failure classification).
|
|
89
|
+
*
|
|
90
|
+
* @param prompt - The prompt to pass via `-p`
|
|
91
|
+
* @param model - Optional model override (omit to use Claude Code's default)
|
|
92
|
+
* @returns Argv array for Bun.spawn
|
|
93
|
+
*/
|
|
94
|
+
buildPrintCommand(prompt: string, model?: string): string[] {
|
|
95
|
+
const cmd = ["claude", "--print", "-p", prompt];
|
|
96
|
+
if (model !== undefined) {
|
|
97
|
+
cmd.push("--model", model);
|
|
98
|
+
}
|
|
99
|
+
return cmd;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Deploy per-agent instructions and guards to a worktree.
|
|
104
|
+
*
|
|
105
|
+
* For Claude Code this means writes to the worktree's `.claude/` directory:
|
|
106
|
+
* 1. `CLAUDE.md` — the agent's task-specific overlay (generated by ap sling).
|
|
107
|
+
* Skipped when overlay is undefined (hooks-only deployment for coordinator/supervisor/monitor).
|
|
108
|
+
* 2. `settings.local.json` — Claude Code hooks for security guards
|
|
109
|
+
*
|
|
110
|
+
* The `overlay.content` is written verbatim when provided. The hooks are generated by
|
|
111
|
+
* `deployHooks()` from `src/agents/hooks-deployer.ts`.
|
|
112
|
+
*
|
|
113
|
+
* @param worktreePath - Absolute path to the agent's git worktree
|
|
114
|
+
* @param overlay - Overlay content to write as CLAUDE.md, or undefined for hooks-only deployment
|
|
115
|
+
* @param hooks - Hook definition used by deployHooks
|
|
116
|
+
* @throws {AgentError} If the hooks template is missing or writes fail
|
|
117
|
+
*/
|
|
118
|
+
async deployConfig(
|
|
119
|
+
worktreePath: string,
|
|
120
|
+
overlay: OverlayContent | undefined,
|
|
121
|
+
hooks: HooksDef,
|
|
122
|
+
): Promise<void> {
|
|
123
|
+
if (overlay) {
|
|
124
|
+
const claudeDir = join(worktreePath, ".claude");
|
|
125
|
+
await mkdir(claudeDir, { recursive: true });
|
|
126
|
+
|
|
127
|
+
const claudeMdPath = join(claudeDir, "CLAUDE.md");
|
|
128
|
+
await Bun.write(claudeMdPath, overlay.content);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Always deploy hooks — headless Claude Code DOES dispatch settings.local.json
|
|
132
|
+
// PreToolUse hooks (verified empirically against `claude -p --output-format stream-json`).
|
|
133
|
+
// The original design (agentplate-1c32 / docs/headless-hooks-design.md Q6) wrongly assumed
|
|
134
|
+
// hooks don't fire in headless mode and skipped deployment, leaving headless agents with
|
|
135
|
+
// none of the destructive-command guards. agentplate-e24b reverses that: in headless mode
|
|
136
|
+
// we deploy a settings.local.json containing only PreToolUse security guards (path boundary,
|
|
137
|
+
// capability blocks, bash danger patterns, tracker close, lead close gate). The other
|
|
138
|
+
// hook types are dropped because they have headless equivalents already wired up
|
|
139
|
+
// (initial stdin prompt, serve mail injection loop, stream-json event capture).
|
|
140
|
+
await deployHooks(
|
|
141
|
+
hooks.worktreePath,
|
|
142
|
+
hooks.agentName,
|
|
143
|
+
hooks.capability,
|
|
144
|
+
hooks.qualityGates,
|
|
145
|
+
hooks.isHeadless ?? false,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Detect Claude Code TUI readiness from a tmux pane content snapshot.
|
|
151
|
+
*
|
|
152
|
+
* Uses the same heuristics as `waitForTuiReady()` in `src/worktree/tmux.ts`,
|
|
153
|
+
* but operates on a pre-captured pane string rather than polling tmux directly.
|
|
154
|
+
* The caller is responsible for capturing pane content and acting on the result
|
|
155
|
+
* (e.g. sending "Enter" to dismiss a trust dialog).
|
|
156
|
+
*
|
|
157
|
+
* Detection phases:
|
|
158
|
+
* - Trust dialog: "trust this folder" detected → `{ phase: "dialog", action: "Enter" }`
|
|
159
|
+
* - Ready: prompt indicator (❯ or 'Try "') AND status bar ("bypass permissions")
|
|
160
|
+
* both present → `{ phase: "ready" }`
|
|
161
|
+
* - Otherwise → `{ phase: "loading" }`
|
|
162
|
+
*
|
|
163
|
+
* @param paneContent - Captured tmux pane content to analyze
|
|
164
|
+
* @returns Current readiness phase
|
|
165
|
+
*/
|
|
166
|
+
detectReady(paneContent: string): ReadyState {
|
|
167
|
+
// Claude Code v2.1.71+ shows a dedicated bypass confirmation screen.
|
|
168
|
+
// It already contains both a prompt marker and the phrase "bypass permissions",
|
|
169
|
+
// so it must be detected before the normal ready heuristics.
|
|
170
|
+
if (
|
|
171
|
+
paneContent.includes("WARNING: Claude Code running in Bypass Permissions mode") &&
|
|
172
|
+
paneContent.includes("1. No, exit") &&
|
|
173
|
+
paneContent.includes("2. Yes, I accept")
|
|
174
|
+
) {
|
|
175
|
+
return { phase: "dialog", action: "type:2" };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Trust dialog takes precedence — it replaces the normal TUI temporarily.
|
|
179
|
+
// The caller should send the action key to dismiss it.
|
|
180
|
+
if (paneContent.includes("trust this folder")) {
|
|
181
|
+
return { phase: "dialog", action: "Enter" };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Phase 1: prompt indicator confirms Claude Code has started.
|
|
185
|
+
// ❯ is the claude prompt character; 'Try "' appears in the welcome banner.
|
|
186
|
+
const hasPrompt = paneContent.includes("\u276f") || paneContent.includes('Try "');
|
|
187
|
+
|
|
188
|
+
// Phase 2: status bar text confirms full TUI render.
|
|
189
|
+
// Only match 'bypass permissions' — 'shift+tab' appears in ALL Claude Code sessions
|
|
190
|
+
// regardless of permission mode and would cause false-positive ready detection.
|
|
191
|
+
const hasStatusBar = paneContent.includes("bypass permissions");
|
|
192
|
+
|
|
193
|
+
if (hasPrompt && hasStatusBar) {
|
|
194
|
+
return { phase: "ready" };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return { phase: "loading" };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Parse a Claude Code transcript JSONL file into normalized token usage.
|
|
202
|
+
*
|
|
203
|
+
* Reads the JSONL file at `path` and aggregates token usage across all
|
|
204
|
+
* assistant turns. Returns null if the file does not exist or cannot be read.
|
|
205
|
+
*
|
|
206
|
+
* Delegates to `parseTranscriptUsage()` and `estimateCost()` from
|
|
207
|
+
* `src/metrics/transcript.ts`. The `estimatedCostUsd` is computed but
|
|
208
|
+
* not exposed here because `TranscriptSummary` only carries the three
|
|
209
|
+
* core fields (inputTokens, outputTokens, model). Cost data is available
|
|
210
|
+
* via `src/metrics/transcript.ts` directly for callers that need it.
|
|
211
|
+
*
|
|
212
|
+
* @param path - Absolute path to the transcript JSONL file
|
|
213
|
+
* @returns Aggregated token usage, or null if unavailable
|
|
214
|
+
*/
|
|
215
|
+
async parseTranscript(path: string): Promise<TranscriptSummary | null> {
|
|
216
|
+
const file = Bun.file(path);
|
|
217
|
+
if (!(await file.exists())) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
const usage = await parseTranscriptUsage(path);
|
|
223
|
+
// estimateCost is called to validate the model is recognized,
|
|
224
|
+
// though the result is not surfaced in TranscriptSummary.
|
|
225
|
+
if (usage.modelUsed !== null) {
|
|
226
|
+
estimateCost(usage);
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
inputTokens: usage.inputTokens,
|
|
230
|
+
outputTokens: usage.outputTokens,
|
|
231
|
+
model: usage.modelUsed ?? "",
|
|
232
|
+
};
|
|
233
|
+
} catch {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Build the argv array for Bun.spawn() to launch a Claude Code agent in headless mode.
|
|
240
|
+
*
|
|
241
|
+
* Returns the exact flags Multica uses for headless Claude Code sessions:
|
|
242
|
+
* `-p --output-format stream-json --input-format stream-json --verbose
|
|
243
|
+
* --strict-mcp-config --permission-mode bypassPermissions [--model <m>]`
|
|
244
|
+
*
|
|
245
|
+
* Claude Code reads `.claude/CLAUDE.md` from cwd automatically — do NOT
|
|
246
|
+
* pass `--append-system-prompt` or consume `opts.instructionPath`.
|
|
247
|
+
* The initial stdin prompt is the caller's responsibility.
|
|
248
|
+
*
|
|
249
|
+
* @param opts - Direct spawn options; only `model` is consumed
|
|
250
|
+
* @returns Argv array for Bun.spawn — do not shell-interpolate
|
|
251
|
+
*/
|
|
252
|
+
buildDirectSpawn(opts: DirectSpawnOpts): string[] {
|
|
253
|
+
const argv = [
|
|
254
|
+
"claude",
|
|
255
|
+
"-p",
|
|
256
|
+
"--output-format",
|
|
257
|
+
"stream-json",
|
|
258
|
+
"--input-format",
|
|
259
|
+
"stream-json",
|
|
260
|
+
"--verbose",
|
|
261
|
+
"--strict-mcp-config",
|
|
262
|
+
"--permission-mode",
|
|
263
|
+
"bypassPermissions",
|
|
264
|
+
];
|
|
265
|
+
if (opts.model !== undefined) {
|
|
266
|
+
argv.push("--model", opts.model);
|
|
267
|
+
}
|
|
268
|
+
// Phase 1 (agentplate-b835): emit --resume on follow-up spawns. Mirrors
|
|
269
|
+
// multica/server/pkg/agent/claude.go:434 (positional). Empty string and
|
|
270
|
+
// null are treated as "no resume" — only non-empty strings activate it.
|
|
271
|
+
if (typeof opts.resumeSessionId === "string" && opts.resumeSessionId.length > 0) {
|
|
272
|
+
argv.push("--resume", opts.resumeSessionId);
|
|
273
|
+
}
|
|
274
|
+
return argv;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Parse stream-json stdout from a Claude Code headless subprocess into typed AgentEvent objects.
|
|
279
|
+
*
|
|
280
|
+
* Reads the ReadableStream from Bun.spawn() stdout, buffers partial lines,
|
|
281
|
+
* and yields a typed AgentEvent for each complete JSON line. Malformed lines
|
|
282
|
+
* and unknown message types are silently skipped.
|
|
283
|
+
*
|
|
284
|
+
* Adjacent assistant_text deltas are coalesced into a single assistant_message
|
|
285
|
+
* event using a batching window. The batch flushes on timer expiry, size cap,
|
|
286
|
+
* any non-text event, or stream end — whichever comes first.
|
|
287
|
+
*
|
|
288
|
+
* Event mapping (Claude stream-json → AgentEvent):
|
|
289
|
+
* - assistant/text → buffered; emitted as one assistant_message per batch
|
|
290
|
+
* - assistant/tool_use → { type: "tool_use", callId, name, input }
|
|
291
|
+
* - assistant/thinking → (skipped)
|
|
292
|
+
* - user/tool_result → { type: "tool_result", toolUseId, content }
|
|
293
|
+
* - system → { type: "status", sessionId, subtype }
|
|
294
|
+
* - result → { type: "result", sessionId, result, isError, durationMs, numTurns }
|
|
295
|
+
*
|
|
296
|
+
* @param stream - ReadableStream<Uint8Array> from Bun.spawn stdout
|
|
297
|
+
* @param opts - Optional hooks and tuning:
|
|
298
|
+
* - `onSessionId` is invoked once, synchronously, on the first event that carries a
|
|
299
|
+
* non-empty `sessionId`. Consumer errors are swallowed so they cannot crash the parser.
|
|
300
|
+
* - `flushIntervalMs` — Max ms to buffer text before emitting (default 500).
|
|
301
|
+
* - `flushSizeBytes` — Max UTF-8 byte size of a text batch (default 4096).
|
|
302
|
+
* @yields Parsed AgentEvent objects in emission order
|
|
303
|
+
*/
|
|
304
|
+
async *parseEvents(
|
|
305
|
+
stream: ReadableStream<Uint8Array>,
|
|
306
|
+
opts?: {
|
|
307
|
+
onSessionId?: (sessionId: string) => void;
|
|
308
|
+
flushIntervalMs?: number;
|
|
309
|
+
flushSizeBytes?: number;
|
|
310
|
+
},
|
|
311
|
+
): AsyncIterable<AgentEvent> {
|
|
312
|
+
const flushIntervalMs = opts?.flushIntervalMs ?? 500;
|
|
313
|
+
const flushSizeBytes = opts?.flushSizeBytes ?? 4096;
|
|
314
|
+
|
|
315
|
+
const reader = stream.getReader();
|
|
316
|
+
const decoder = new TextDecoder();
|
|
317
|
+
let buffer = "";
|
|
318
|
+
let sessionIdPinned = false;
|
|
319
|
+
|
|
320
|
+
// Batch state for adjacent assistant_text deltas.
|
|
321
|
+
let pendingText: string[] = [];
|
|
322
|
+
let pendingByteSize = 0;
|
|
323
|
+
let pendingStartTs: string | null = null;
|
|
324
|
+
let pendingModel: string | undefined;
|
|
325
|
+
let pendingUsage: unknown;
|
|
326
|
+
|
|
327
|
+
// Returns batched assistant_message event and resets state; null when buffer is empty.
|
|
328
|
+
const flushText = (): AgentEvent | null => {
|
|
329
|
+
if (pendingText.length === 0) return null;
|
|
330
|
+
const text = pendingText.join("");
|
|
331
|
+
const event: AgentEvent = {
|
|
332
|
+
type: "assistant_message",
|
|
333
|
+
timestamp: pendingStartTs ?? new Date().toISOString(),
|
|
334
|
+
text,
|
|
335
|
+
};
|
|
336
|
+
if (pendingModel !== undefined) event.model = pendingModel;
|
|
337
|
+
if (pendingUsage !== undefined) event.usage = pendingUsage;
|
|
338
|
+
pendingText = [];
|
|
339
|
+
pendingByteSize = 0;
|
|
340
|
+
pendingStartTs = null;
|
|
341
|
+
pendingModel = undefined;
|
|
342
|
+
pendingUsage = undefined;
|
|
343
|
+
return event;
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
// Sync generator: parses one JSON line, yielding AgentEvents in order.
|
|
347
|
+
// Mutates batch state via closure — do not call concurrently.
|
|
348
|
+
function* processLine(line: string): Generator<AgentEvent> {
|
|
349
|
+
const trimmed = line.trim();
|
|
350
|
+
if (!trimmed) return;
|
|
351
|
+
|
|
352
|
+
let msg: Record<string, unknown>;
|
|
353
|
+
try {
|
|
354
|
+
msg = JSON.parse(trimmed) as Record<string, unknown>;
|
|
355
|
+
} catch {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const timestamp = new Date().toISOString();
|
|
360
|
+
|
|
361
|
+
if (msg.type === "assistant") {
|
|
362
|
+
const message =
|
|
363
|
+
typeof msg.message === "object" && msg.message !== null
|
|
364
|
+
? (msg.message as Record<string, unknown>)
|
|
365
|
+
: null;
|
|
366
|
+
if (!message) return;
|
|
367
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
368
|
+
const model = typeof message.model === "string" ? message.model : undefined;
|
|
369
|
+
const usage = message.usage !== undefined ? message.usage : undefined;
|
|
370
|
+
|
|
371
|
+
for (const block of content) {
|
|
372
|
+
if (typeof block !== "object" || block === null) continue;
|
|
373
|
+
const b = block as Record<string, unknown>;
|
|
374
|
+
if (b.type === "text") {
|
|
375
|
+
const text = typeof b.text === "string" ? b.text : String(b.text);
|
|
376
|
+
const textByteSize = new TextEncoder().encode(text).byteLength;
|
|
377
|
+
|
|
378
|
+
// Size-cap: if appending would exceed cap and buffer is non-empty, flush first.
|
|
379
|
+
if (pendingByteSize > 0 && pendingByteSize + textByteSize > flushSizeBytes) {
|
|
380
|
+
const ev = flushText();
|
|
381
|
+
if (ev) yield ev;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Record the start-of-batch timestamp on the first fragment.
|
|
385
|
+
if (pendingByteSize === 0) pendingStartTs = timestamp;
|
|
386
|
+
|
|
387
|
+
pendingText.push(text);
|
|
388
|
+
pendingByteSize += textByteSize;
|
|
389
|
+
// Latest contributing message wins for model/usage.
|
|
390
|
+
if (model !== undefined) pendingModel = model;
|
|
391
|
+
if (usage !== undefined) pendingUsage = usage;
|
|
392
|
+
|
|
393
|
+
// Immediate flush when a single fragment meets or exceeds the size cap.
|
|
394
|
+
if (pendingByteSize >= flushSizeBytes) {
|
|
395
|
+
const ev = flushText();
|
|
396
|
+
if (ev) yield ev;
|
|
397
|
+
}
|
|
398
|
+
} else if (b.type === "tool_use") {
|
|
399
|
+
// Non-text block: flush pending text first to preserve in-order delivery.
|
|
400
|
+
const ev = flushText();
|
|
401
|
+
if (ev) yield ev;
|
|
402
|
+
yield {
|
|
403
|
+
type: "tool_use",
|
|
404
|
+
timestamp,
|
|
405
|
+
callId: b.id,
|
|
406
|
+
name: b.name,
|
|
407
|
+
input: b.input,
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
// thinking and other block types → skip
|
|
411
|
+
}
|
|
412
|
+
} else if (msg.type === "user") {
|
|
413
|
+
const message =
|
|
414
|
+
typeof msg.message === "object" && msg.message !== null
|
|
415
|
+
? (msg.message as Record<string, unknown>)
|
|
416
|
+
: null;
|
|
417
|
+
if (!message) return;
|
|
418
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
419
|
+
|
|
420
|
+
const flushEv = flushText();
|
|
421
|
+
if (flushEv) yield flushEv;
|
|
422
|
+
|
|
423
|
+
for (const block of content) {
|
|
424
|
+
if (typeof block !== "object" || block === null) continue;
|
|
425
|
+
const b = block as Record<string, unknown>;
|
|
426
|
+
if (b.type === "tool_result") {
|
|
427
|
+
yield {
|
|
428
|
+
type: "tool_result",
|
|
429
|
+
timestamp,
|
|
430
|
+
toolUseId: b.tool_use_id,
|
|
431
|
+
content: b.content,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
} else if (msg.type === "system") {
|
|
436
|
+
const flushEv = flushText();
|
|
437
|
+
if (flushEv) yield flushEv;
|
|
438
|
+
yield { type: "status", timestamp, sessionId: msg.session_id, subtype: msg.subtype };
|
|
439
|
+
} else if (msg.type === "result") {
|
|
440
|
+
const flushEv = flushText();
|
|
441
|
+
if (flushEv) yield flushEv;
|
|
442
|
+
yield {
|
|
443
|
+
type: "result",
|
|
444
|
+
timestamp,
|
|
445
|
+
sessionId: msg.session_id,
|
|
446
|
+
result: msg.result,
|
|
447
|
+
isError: msg.is_error,
|
|
448
|
+
durationMs: msg.duration_ms,
|
|
449
|
+
numTurns: msg.num_turns,
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const maybePinSession = (event: AgentEvent): void => {
|
|
455
|
+
if (sessionIdPinned || !opts?.onSessionId) return;
|
|
456
|
+
const sid = event.sessionId;
|
|
457
|
+
if (typeof sid !== "string" || sid.length === 0) return;
|
|
458
|
+
sessionIdPinned = true;
|
|
459
|
+
try {
|
|
460
|
+
opts.onSessionId(sid);
|
|
461
|
+
} catch {
|
|
462
|
+
// Consumer errors must not crash the parser.
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
try {
|
|
467
|
+
// Use the inferred return type of reader.read() to stay compatible with
|
|
468
|
+
// both the standard Web Streams API and Bun's slightly divergent typings.
|
|
469
|
+
type ReadResult = Awaited<ReturnType<typeof reader.read>>;
|
|
470
|
+
type Race = { kind: "read"; result: ReadResult } | { kind: "timeout" };
|
|
471
|
+
|
|
472
|
+
let readPromise = reader.read();
|
|
473
|
+
|
|
474
|
+
while (true) {
|
|
475
|
+
if (pendingText.length > 0 && pendingStartTs !== null) {
|
|
476
|
+
// Race the next read chunk against the flush timer.
|
|
477
|
+
const elapsed = Date.now() - new Date(pendingStartTs).getTime();
|
|
478
|
+
const remaining = Math.max(0, flushIntervalMs - elapsed);
|
|
479
|
+
|
|
480
|
+
let timerId: ReturnType<typeof setTimeout> | undefined;
|
|
481
|
+
const wrappedRead: Promise<Race> = readPromise.then((result) => ({
|
|
482
|
+
kind: "read" as const,
|
|
483
|
+
result,
|
|
484
|
+
}));
|
|
485
|
+
const wrappedTimer = new Promise<Race>((resolve) => {
|
|
486
|
+
timerId = setTimeout(() => resolve({ kind: "timeout" }), remaining);
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
const winner = await Promise.race([wrappedRead, wrappedTimer]);
|
|
490
|
+
if (timerId !== undefined) clearTimeout(timerId);
|
|
491
|
+
|
|
492
|
+
if (winner.kind === "timeout") {
|
|
493
|
+
const ev = flushText();
|
|
494
|
+
if (ev) yield ev;
|
|
495
|
+
continue; // readPromise still in flight — re-enter loop without refreshing
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const result = winner.result;
|
|
499
|
+
if (result.done) break;
|
|
500
|
+
buffer += decoder.decode(result.value, { stream: true });
|
|
501
|
+
const lines = buffer.split("\n");
|
|
502
|
+
buffer = lines.pop() ?? "";
|
|
503
|
+
for (const line of lines) {
|
|
504
|
+
for (const event of processLine(line)) {
|
|
505
|
+
maybePinSession(event);
|
|
506
|
+
yield event;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
readPromise = reader.read();
|
|
510
|
+
} else {
|
|
511
|
+
const result = await readPromise;
|
|
512
|
+
if (result.done) break;
|
|
513
|
+
buffer += decoder.decode(result.value, { stream: true });
|
|
514
|
+
const lines = buffer.split("\n");
|
|
515
|
+
// Last element is either empty or an incomplete line — keep in buffer.
|
|
516
|
+
buffer = lines.pop() ?? "";
|
|
517
|
+
for (const line of lines) {
|
|
518
|
+
for (const event of processLine(line)) {
|
|
519
|
+
maybePinSession(event);
|
|
520
|
+
yield event;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
readPromise = reader.read();
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Flush remaining buffer on clean stream end (no trailing newline).
|
|
528
|
+
if (buffer.trim()) {
|
|
529
|
+
for (const event of processLine(buffer)) {
|
|
530
|
+
maybePinSession(event);
|
|
531
|
+
yield event;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Drain any remaining buffered text after the read loop exits.
|
|
536
|
+
const finalEv = flushText();
|
|
537
|
+
if (finalEv) yield finalEv;
|
|
538
|
+
} finally {
|
|
539
|
+
reader.releaseLock();
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Build runtime-specific environment variables for model/provider routing.
|
|
545
|
+
*
|
|
546
|
+
* Returns the provider environment variables from the resolved model.
|
|
547
|
+
* For Anthropic native: may include ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL.
|
|
548
|
+
* For gateway providers: may include gateway-specific auth and routing vars.
|
|
549
|
+
*
|
|
550
|
+
* Returns an empty object if the resolved model has no provider env vars.
|
|
551
|
+
* Callers (sling.ts, coordinator.ts) merge this with AGENTPLATE_AGENT_NAME
|
|
552
|
+
* and AGENTPLATE_WORKTREE_PATH before passing to createSession().
|
|
553
|
+
*
|
|
554
|
+
* @param model - Resolved model with optional provider env vars
|
|
555
|
+
* @returns Environment variable map (may be empty)
|
|
556
|
+
*/
|
|
557
|
+
buildEnv(model: ResolvedModel): Record<string, string> {
|
|
558
|
+
return model.env ?? {};
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* Return the Claude Code transcript directory for a given project root.
|
|
563
|
+
*
|
|
564
|
+
* Claude Code stores session transcripts at ~/.claude/projects/<projectKey>/
|
|
565
|
+
* where <projectKey> is the project root path with "/" replaced by "-".
|
|
566
|
+
*
|
|
567
|
+
* @param projectRoot - Absolute path to the project root
|
|
568
|
+
* @returns Absolute path to the transcript directory, or null if HOME is unavailable
|
|
569
|
+
*/
|
|
570
|
+
getTranscriptDir(projectRoot: string): string | null {
|
|
571
|
+
const home = process.env.HOME ?? "";
|
|
572
|
+
if (home.length === 0) return null;
|
|
573
|
+
const projectKey = projectRoot.replace(/\//g, "-");
|
|
574
|
+
return join(home, ".claude", "projects", projectKey);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/** Singleton instance for use in callers that do not need DI. */
|
|
579
|
+
export const claudeRuntime = new ClaudeRuntime();
|