@karajan-family/code 4.21.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 +661 -0
- package/README.md +98 -0
- package/bin/karajan-mcp.js +2 -0
- package/bin/kj-rag-mcp.js +75 -0
- package/bin/kj-tail +361 -0
- package/bin/kj.js +2 -0
- package/docs/README.es.md +80 -0
- package/docs/karajan-code-logo-small.png +0 -0
- package/library/logical-clocks-lamport.md +40 -0
- package/package.json +140 -0
- package/packages/ai-trash/README.md +37 -0
- package/packages/ai-trash/bin/kj-trash.js +10 -0
- package/packages/ai-trash/package.json +43 -0
- package/packages/ai-trash/src/cli.js +200 -0
- package/packages/ai-trash/src/destructive-parser.js +74 -0
- package/packages/ai-trash/src/git-snapshot.js +128 -0
- package/packages/ai-trash/src/hook.js +137 -0
- package/packages/ai-trash/src/index.js +14 -0
- package/packages/ai-trash/src/install.js +73 -0
- package/packages/ai-trash/src/logger.js +60 -0
- package/packages/ai-trash/src/manifest.js +91 -0
- package/packages/ai-trash/src/permissions.js +26 -0
- package/packages/ai-trash/src/snapshot.js +66 -0
- package/packages/ai-trash/src/ulid.js +66 -0
- package/packages/hu-board/README.md +69 -0
- package/packages/hu-board/package.json +28 -0
- package/packages/hu-board/public/app.js +259 -0
- package/packages/hu-board/public/index.html +77 -0
- package/packages/hu-board/public/pipeline.css +132 -0
- package/packages/hu-board/public/pipeline.html +48 -0
- package/packages/hu-board/public/pipeline.js +200 -0
- package/packages/hu-board/public/rag.html +21 -0
- package/packages/hu-board/public/rag.js +39 -0
- package/packages/hu-board/public/styles.css +973 -0
- package/packages/hu-board/public/utils/api.js +139 -0
- package/packages/hu-board/public/utils/board-view.js +473 -0
- package/packages/hu-board/public/utils/command-launcher.js +213 -0
- package/packages/hu-board/public/utils/config-editor.js +181 -0
- package/packages/hu-board/public/utils/dashboard-view.js +103 -0
- package/packages/hu-board/public/utils/formatters.js +300 -0
- package/packages/hu-board/public/utils/graph-view.js +216 -0
- package/packages/hu-board/public/utils/hu-actions.js +224 -0
- package/packages/hu-board/public/utils/init-listeners.js +180 -0
- package/packages/hu-board/public/utils/log-panel.js +222 -0
- package/packages/hu-board/public/utils/modals.js +309 -0
- package/packages/hu-board/public/utils/plan-rollup-view.js +53 -0
- package/packages/hu-board/public/utils/preflight-view.js +89 -0
- package/packages/hu-board/public/utils/project-actions.js +143 -0
- package/packages/hu-board/public/utils/project-picker-view.js +139 -0
- package/packages/hu-board/public/utils/run-launcher.js +169 -0
- package/packages/hu-board/public/utils/server-push.js +252 -0
- package/packages/hu-board/public/utils/sessions-view.js +199 -0
- package/packages/hu-board/public/utils/story-detail-view.js +324 -0
- package/packages/hu-board/public/utils/story-edit-form.js +271 -0
- package/packages/hu-board/public/wiki.html +32 -0
- package/packages/hu-board/public/wiki.js +53 -0
- package/packages/hu-board/src/auth.js +92 -0
- package/packages/hu-board/src/boot-info.js +16 -0
- package/packages/hu-board/src/cleanup-zombies.js +145 -0
- package/packages/hu-board/src/command-runner.js +133 -0
- package/packages/hu-board/src/config-yaml.js +531 -0
- package/packages/hu-board/src/db.js +963 -0
- package/packages/hu-board/src/ephemeral-cleaner.js +196 -0
- package/packages/hu-board/src/event-bus.js +58 -0
- package/packages/hu-board/src/format.js +207 -0
- package/packages/hu-board/src/hu-zombie-reaper.js +103 -0
- package/packages/hu-board/src/journal-parser.js +180 -0
- package/packages/hu-board/src/plan-mutations.js +424 -0
- package/packages/hu-board/src/preflight.js +286 -0
- package/packages/hu-board/src/routes/api.js +1374 -0
- package/packages/hu-board/src/routes/pipeline.js +45 -0
- package/packages/hu-board/src/routes/rag.js +42 -0
- package/packages/hu-board/src/routes/wiki.js +43 -0
- package/packages/hu-board/src/run-tracker.js +121 -0
- package/packages/hu-board/src/server.js +438 -0
- package/packages/hu-board/src/sync.js +732 -0
- package/packages/hu-board/src/token-store.js +56 -0
- package/packages/hu-board/src/zombie-reaper.js +206 -0
- package/scripts/ai-attribution-guard.yml +84 -0
- package/scripts/audit-test-diet.mjs +151 -0
- package/scripts/build-sea.mjs +150 -0
- package/scripts/diet-baseline.mjs +170 -0
- package/scripts/dual-publish.mjs +67 -0
- package/scripts/esbuild-sea.config.mjs +273 -0
- package/scripts/git-hooks/post-merge +18 -0
- package/scripts/install-binary.ps1 +174 -0
- package/scripts/install-binary.sh +159 -0
- package/scripts/install-guard-all-repos.sh +85 -0
- package/scripts/install-kj.sh +193 -0
- package/scripts/install.js +901 -0
- package/scripts/install.sh +7 -0
- package/scripts/postinstall.js +117 -0
- package/scripts/protect-all-repos.sh +88 -0
- package/scripts/quickstart-gate.mjs +97 -0
- package/scripts/regen-arch-stats.sh +71 -0
- package/scripts/setup-multi-instance.sh +150 -0
- package/scripts/verify-pack.mjs +278 -0
- package/src/activity-log.js +59 -0
- package/src/agents/agy-agent.js +62 -0
- package/src/agents/aider-agent.js +78 -0
- package/src/agents/availability.js +52 -0
- package/src/agents/base-agent.js +134 -0
- package/src/agents/claude-agent.js +413 -0
- package/src/agents/codex-agent.js +112 -0
- package/src/agents/copilot-agent.js +65 -0
- package/src/agents/gemini-agent.js +85 -0
- package/src/agents/host-agent.js +38 -0
- package/src/agents/index.js +59 -0
- package/src/agents/kimi-agent.js +45 -0
- package/src/agents/model-registry.js +137 -0
- package/src/agents/opencode-agent.js +72 -0
- package/src/agents/qwen-agent.js +20 -0
- package/src/agents/resolve-bin.js +73 -0
- package/src/audit/agent-readiness.js +259 -0
- package/src/audit/ai-slop-findings.js +102 -0
- package/src/audit/audit-fallback.js +66 -0
- package/src/audit/audit-history-display.js +61 -0
- package/src/audit/audit-history.js +102 -0
- package/src/audit/basal-cost.js +281 -0
- package/src/audit/circular-deps.js +144 -0
- package/src/audit/dead-exports.js +186 -0
- package/src/audit/deterministic-summary.js +327 -0
- package/src/audit/harness-scorecard.js +100 -0
- package/src/audit/harness-section.js +81 -0
- package/src/audit/infra-findings.js +100 -0
- package/src/audit/injection-findings.js +114 -0
- package/src/audit/issue-filter.js +133 -0
- package/src/audit/osv-findings.js +167 -0
- package/src/audit/plan-adherence.js +224 -0
- package/src/audit/semgrep-findings.js +129 -0
- package/src/audit/sonar-findings.js +93 -0
- package/src/audit/webperf-input.js +133 -0
- package/src/autonomy/arbiter-role.js +71 -0
- package/src/autonomy/arbiter.js +22 -0
- package/src/autonomy/decider.js +18 -0
- package/src/autonomy/decision-resolver.js +58 -0
- package/src/autonomy/outcome-report.js +55 -0
- package/src/autonomy/policy.js +41 -0
- package/src/autonomy/stage-ask.js +30 -0
- package/src/bootstrap.js +276 -0
- package/src/brain/agent-error-classifier.js +116 -0
- package/src/brain/decision-tracker.js +125 -0
- package/src/brain/decisor.js +243 -0
- package/src/brain/solomon-consult.js +171 -0
- package/src/brain/standby-scheduler.js +8 -0
- package/src/brain/standby-store.js +12 -0
- package/src/brain/with-brain-recovery.js +235 -0
- package/src/budget/comparison.js +96 -0
- package/src/canvas/parse-md.js +238 -0
- package/src/canvas/schema.js +101 -0
- package/src/canvas/to-flat.js +85 -0
- package/src/canvas/validate.js +163 -0
- package/src/checks/ai-surface.js +79 -0
- package/src/checks/ai-trash.js +34 -0
- package/src/checks/binaries.js +207 -0
- package/src/checks/ci.js +105 -0
- package/src/checks/config-files.js +217 -0
- package/src/checks/dir-setup.js +103 -0
- package/src/checks/guarantee-level.js +38 -0
- package/src/checks/hardware.js +150 -0
- package/src/checks/harness-scorecard.js +41 -0
- package/src/checks/mcp-health.js +169 -0
- package/src/checks/method.js +73 -0
- package/src/checks/native-build.js +67 -0
- package/src/checks/node.js +70 -0
- package/src/checks/ollama.js +41 -0
- package/src/checks/orphans.js +77 -0
- package/src/checks/ports.js +188 -0
- package/src/checks/project-checks.js +245 -0
- package/src/checks/qmd.js +41 -0
- package/src/checks/rag-hooks.js +54 -0
- package/src/checks/release-check.js +102 -0
- package/src/checks/rtk.js +36 -0
- package/src/checks/runner.js +326 -0
- package/src/checks/skills.js +53 -0
- package/src/checks/sonar.js +67 -0
- package/src/checks/squeezr.js +36 -0
- package/src/checks/system.js +53 -0
- package/src/checks/tokens.js +172 -0
- package/src/checks/types.js +103 -0
- package/src/ci/dispatch.js +99 -0
- package/src/ci/index.js +3 -0
- package/src/ci/pr-diff.js +26 -0
- package/src/ci/repo.js +45 -0
- package/src/cli/_shared.js +30 -0
- package/src/cli/advanced-commands.js +61 -0
- package/src/cli/register-meta.js +772 -0
- package/src/cli/register-pipeline.js +335 -0
- package/src/cli/register-plan.js +224 -0
- package/src/cli/register-roles-skills.js +54 -0
- package/src/cli/register-sonar.js +28 -0
- package/src/cli/register-standby.js +26 -0
- package/src/cli.js +155 -0
- package/src/commands/advanced.js +31 -0
- package/src/commands/agent-run.js +63 -0
- package/src/commands/agents.js +105 -0
- package/src/commands/architect.js +123 -0
- package/src/commands/audit.js +430 -0
- package/src/commands/autorun.js +49 -0
- package/src/commands/board.js +458 -0
- package/src/commands/check.js +40 -0
- package/src/commands/clean.js +145 -0
- package/src/commands/code.js +45 -0
- package/src/commands/config.js +74 -0
- package/src/commands/discover.js +105 -0
- package/src/commands/doctor.js +251 -0
- package/src/commands/env.js +207 -0
- package/src/commands/harden.js +203 -0
- package/src/commands/hu.js +210 -0
- package/src/commands/identity.js +60 -0
- package/src/commands/init.js +950 -0
- package/src/commands/install-tools.js +432 -0
- package/src/commands/mutate.js +94 -0
- package/src/commands/ollama.js +44 -0
- package/src/commands/onboard.js +45 -0
- package/src/commands/plan/_shared.js +55 -0
- package/src/commands/plan/add-hu.js +23 -0
- package/src/commands/plan/delete.js +9 -0
- package/src/commands/plan/fix.js +99 -0
- package/src/commands/plan/generate.js +532 -0
- package/src/commands/plan/index.js +13 -0
- package/src/commands/plan/list.js +21 -0
- package/src/commands/plan/migrate-result.js +41 -0
- package/src/commands/plan/ready.js +46 -0
- package/src/commands/plan/remove-hu.js +17 -0
- package/src/commands/plan/share.js +85 -0
- package/src/commands/plan/show.js +81 -0
- package/src/commands/plan/unshare.js +41 -0
- package/src/commands/plan/validate.js +20 -0
- package/src/commands/plan.js +17 -0
- package/src/commands/policy.js +164 -0
- package/src/commands/privacy.js +45 -0
- package/src/commands/qmd.js +86 -0
- package/src/commands/rag-mcp.js +18 -0
- package/src/commands/rag.js +173 -0
- package/src/commands/report-issue.js +77 -0
- package/src/commands/report.js +469 -0
- package/src/commands/researcher.js +54 -0
- package/src/commands/resume.js +68 -0
- package/src/commands/review-gate.js +355 -0
- package/src/commands/review.js +89 -0
- package/src/commands/roles.js +116 -0
- package/src/commands/run.js +185 -0
- package/src/commands/scan.js +18 -0
- package/src/commands/skills.js +86 -0
- package/src/commands/sonar.js +53 -0
- package/src/commands/standby.js +117 -0
- package/src/commands/start.js +121 -0
- package/src/commands/status.js +36 -0
- package/src/commands/sync.js +103 -0
- package/src/commands/telemetry.js +70 -0
- package/src/commands/tournament.js +85 -0
- package/src/commands/triage.js +78 -0
- package/src/commands/undo.js +62 -0
- package/src/commands/watch.js +46 -0
- package/src/commands/webperf.js +162 -0
- package/src/commands/worktree.js +76 -0
- package/src/config/defaults.js +270 -0
- package/src/config/loader.js +204 -0
- package/src/config/overrides.js +220 -0
- package/src/config/role-resolver.js +171 -0
- package/src/config/schema.js +299 -0
- package/src/config/test-harness.js +78 -0
- package/src/config.js +34 -0
- package/src/domains/domain-loader.js +245 -0
- package/src/domains/domain-registry.js +140 -0
- package/src/domains/domain-synthesizer.js +129 -0
- package/src/environment/adr.js +45 -0
- package/src/environment/board-access.js +97 -0
- package/src/environment/board-select.js +59 -0
- package/src/environment/briefs.js +129 -0
- package/src/environment/issue-report.js +72 -0
- package/src/environment/mcp-wiring.js +44 -0
- package/src/environment/playbook.js +127 -0
- package/src/git/automation.js +334 -0
- package/src/git/hu-automation.js +171 -0
- package/src/git/hu-snapshot.js +10 -0
- package/src/golden/asserter.js +92 -0
- package/src/golden/loader.js +52 -0
- package/src/golden/runner.js +92 -0
- package/src/golden/schema.js +41 -0
- package/src/guards/intent-guard.js +161 -0
- package/src/guards/output-guard.js +149 -0
- package/src/guards/perf-guard.js +126 -0
- package/src/guards/policy-guard.js +90 -0
- package/src/guards/policy-resolver.js +85 -0
- package/src/guards/secret-redactor.js +71 -0
- package/src/harden/advisory.js +203 -0
- package/src/harden/alternatives.js +50 -0
- package/src/harden/check.js +81 -0
- package/src/harden/config-engine.js +128 -0
- package/src/harden/config-templates.js +122 -0
- package/src/harden/guidelines-engine.js +48 -0
- package/src/harden/guidelines-templates.js +36 -0
- package/src/harden/harden-engine.js +80 -0
- package/src/harden/harness-hooks.js +107 -0
- package/src/harden/hook-commands.js +25 -0
- package/src/harden/hook-templates.js +183 -0
- package/src/harden/interactive.js +76 -0
- package/src/harden/sentinel-hooks.js +721 -0
- package/src/harden/stack-roots.js +105 -0
- package/src/harden/test-on-push.js +38 -0
- package/src/harden/workflow-engine.js +116 -0
- package/src/harden/workflow-templates.js +285 -0
- package/src/hu/acceptance-runner.js +138 -0
- package/src/hu/auto-generator.js +358 -0
- package/src/hu/graph.js +71 -0
- package/src/hu/lazy-planner.js +109 -0
- package/src/hu/model-router.js +108 -0
- package/src/hu/parallel-executor.js +94 -0
- package/src/hu/split-validator.js +143 -0
- package/src/hu/splitting-detector.js +158 -0
- package/src/hu/splitting-generator.js +175 -0
- package/src/hu/store.js +392 -0
- package/src/hu/worktree-bootstrap.js +59 -0
- package/src/identity/bootstrap.js +48 -0
- package/src/identity/compare.js +38 -0
- package/src/identity/detect.js +51 -0
- package/src/identity/store.js +66 -0
- package/src/infrastructure/command-runner.js +44 -0
- package/src/infrastructure/environment.js +38 -0
- package/src/infrastructure/file-system-service.js +63 -0
- package/src/infrastructure/mocks.js +182 -0
- package/src/lang/chunk-go.js +94 -0
- package/src/lang/chunk-java.js +90 -0
- package/src/lang/chunk-python.js +88 -0
- package/src/lang/chunk-rust.js +102 -0
- package/src/lang/go.js +14 -0
- package/src/lang/java.js +16 -0
- package/src/lang/js.js +8 -0
- package/src/lang/python.js +14 -0
- package/src/lang/registry.js +79 -0
- package/src/lang/rust.js +14 -0
- package/src/lang/tree-sitter-loader.js +45 -0
- package/src/mcp/direct-role-runner.js +112 -0
- package/src/mcp/error-classifiers.js +66 -0
- package/src/mcp/handlers/direct-handlers.js +486 -0
- package/src/mcp/handlers/hu-handlers.js +93 -0
- package/src/mcp/handlers/management-handlers.js +265 -0
- package/src/mcp/handlers/rag-handler.js +52 -0
- package/src/mcp/handlers/run-handler.js +313 -0
- package/src/mcp/orphan-guard.js +131 -0
- package/src/mcp/preflight.js +47 -0
- package/src/mcp/progress.js +149 -0
- package/src/mcp/response-compressor.js +81 -0
- package/src/mcp/run-kj.js +135 -0
- package/src/mcp/server-handlers.js +79 -0
- package/src/mcp/server.js +66 -0
- package/src/mcp/shared-helpers.js +297 -0
- package/src/mcp/sovereignty-guard.js +112 -0
- package/src/mcp/suggest-handler.js +92 -0
- package/src/mcp/tool-arg-normalizers.js +16 -0
- package/src/mcp/tools.js +432 -0
- package/src/mutate/diff-scope.js +105 -0
- package/src/mutate/reviewer-signal.js +76 -0
- package/src/mutate/runner.js +72 -0
- package/src/mutate/tool-registry.js +90 -0
- package/src/onboarder/cache.js +24 -0
- package/src/onboarder/collectors/framework-multilang.js +64 -0
- package/src/onboarder/collectors/index.js +136 -0
- package/src/orchestrator/agent-fallback.js +81 -0
- package/src/orchestrator/brain-coordinator.js +186 -0
- package/src/orchestrator/ci-integration.js +139 -0
- package/src/orchestrator/config-init.js +510 -0
- package/src/orchestrator/direct-actions.js +217 -0
- package/src/orchestrator/drivers/error-recovery.js +287 -0
- package/src/orchestrator/drivers/init-context.js +227 -0
- package/src/orchestrator/drivers/iteration-loop.js +380 -0
- package/src/orchestrator/drivers/iteration-phases/coder-and-refactorer.js +69 -0
- package/src/orchestrator/drivers/iteration-phases/guards.js +84 -0
- package/src/orchestrator/drivers/iteration-phases/handle-approved.js +29 -0
- package/src/orchestrator/drivers/iteration-phases/quality-gates.js +114 -0
- package/src/orchestrator/drivers/iteration-phases/reviewer-gate.js +60 -0
- package/src/orchestrator/drivers/post-loop.js +383 -0
- package/src/orchestrator/drivers/pre-loop-phases/auto-hu-batch.js +136 -0
- package/src/orchestrator/drivers/pre-loop-phases/config-deprecations.js +47 -0
- package/src/orchestrator/drivers/pre-loop-phases/ensure-addyosmani-skills.js +84 -0
- package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +391 -0
- package/src/orchestrator/drivers/pre-loop.js +507 -0
- package/src/orchestrator/drivers/run-hu-batch.js +427 -0
- package/src/orchestrator/feedback-enrichment.js +140 -0
- package/src/orchestrator/feedback-queue.js +151 -0
- package/src/orchestrator/flow-control.js +170 -0
- package/src/orchestrator/flow-runner.js +364 -0
- package/src/orchestrator/fs-leak-detector.js +210 -0
- package/src/orchestrator/hu-scheduler.js +114 -0
- package/src/orchestrator/hu-sub-pipeline.js +616 -0
- package/src/orchestrator/integrations.js +81 -0
- package/src/orchestrator/iteration-gate.js +73 -0
- package/src/orchestrator/iteration-stages.js +9 -0
- package/src/orchestrator/parallel-limiter.js +74 -0
- package/src/orchestrator/pipeline-context.js +72 -0
- package/src/orchestrator/post-loop-stages.js +468 -0
- package/src/orchestrator/pre-loop-stages.js +11 -0
- package/src/orchestrator/preflight-checks.js +462 -0
- package/src/orchestrator/repair/repair-detector.js +117 -0
- package/src/orchestrator/repair/repair-runner.js +142 -0
- package/src/orchestrator/reviewer-fallback.js +45 -0
- package/src/orchestrator/role-output-compressor.js +189 -0
- package/src/orchestrator/session-journal.js +308 -0
- package/src/orchestrator/solomon-escalation.js +305 -0
- package/src/orchestrator/solomon-rules.js +212 -0
- package/src/orchestrator/stages/architect-stage.js +172 -0
- package/src/orchestrator/stages/coder-stage.js +480 -0
- package/src/orchestrator/stages/domain-curator-stage.js +82 -0
- package/src/orchestrator/stages/hu-reviewer-phases/detect-and-apply-splits.js +127 -0
- package/src/orchestrator/stages/hu-reviewer-stage.js +394 -0
- package/src/orchestrator/stages/perf-stage.js +140 -0
- package/src/orchestrator/stages/planner-stage.js +85 -0
- package/src/orchestrator/stages/rag-context-stage.js +77 -0
- package/src/orchestrator/stages/rag-preload-decisor.js +58 -0
- package/src/orchestrator/stages/research-stage.js +69 -0
- package/src/orchestrator/stages/reviewer-stage.js +422 -0
- package/src/orchestrator/stages/sonar-stage.js +421 -0
- package/src/orchestrator/stages/stage-classes.js +84 -0
- package/src/orchestrator/stages/stage-executor.js +155 -0
- package/src/orchestrator/stages/tdd-discipline-stage.js +56 -0
- package/src/orchestrator/stages/tool-judge-stage.js +53 -0
- package/src/orchestrator/stages/triage-stage.js +172 -0
- package/src/orchestrator/standby.js +71 -0
- package/src/orchestrator/verification-gate.js +183 -0
- package/src/orchestrator.js +22 -0
- package/src/plan/adr-generator.js +170 -0
- package/src/plan/adr-loader.js +60 -0
- package/src/plan/plan-executor.js +100 -0
- package/src/plan/plan-fixer.js +192 -0
- package/src/plan/plan-hu-ops.js +16 -0
- package/src/plan/plan-id.js +4 -0
- package/src/plan/plan-migrate-result.js +24 -0
- package/src/plan/plan-reviewer.js +236 -0
- package/src/plan/plan-schema.js +334 -0
- package/src/plan/plan-store.js +260 -0
- package/src/plan/plan-structural-pass.js +122 -0
- package/src/plan/plan-validation.js +4 -0
- package/src/plan/preflight-hu.js +105 -0
- package/src/plan/spec-detector.js +61 -0
- package/src/plan/tests-synthesizer.js +131 -0
- package/src/planning-game/adapter.js +108 -0
- package/src/planning-game/architect-adrs.js +45 -0
- package/src/planning-game/client.js +108 -0
- package/src/planning-game/decomposition.js +87 -0
- package/src/planning-game/pipeline-adapter.js +251 -0
- package/src/plugins/loader.js +67 -0
- package/src/policy/add.js +111 -0
- package/src/policy/decisions.js +42 -0
- package/src/policy/engine.js +82 -0
- package/src/policy/exceptions.js +60 -0
- package/src/privacy/onboarding.js +35 -0
- package/src/privacy/scan.js +120 -0
- package/src/prompts/arbiter.js +38 -0
- package/src/prompts/architect.js +103 -0
- package/src/prompts/audit.js +474 -0
- package/src/prompts/coder.js +287 -0
- package/src/prompts/diff-clip.js +27 -0
- package/src/prompts/discover.js +222 -0
- package/src/prompts/hu-reviewer.js +219 -0
- package/src/prompts/planner.js +312 -0
- package/src/prompts/prompt-layout.js +80 -0
- package/src/prompts/prompt-resolver.js +120 -0
- package/src/prompts/reviewer.js +75 -0
- package/src/prompts/rtk-snippet.js +25 -0
- package/src/prompts/spec-reviewer.js +66 -0
- package/src/prompts/split-signal.js +82 -0
- package/src/prompts/start-decision.js +52 -0
- package/src/prompts/tool-correctness-judge.js +31 -0
- package/src/prompts/triage.js +57 -0
- package/src/rag/auto-update.js +72 -0
- package/src/rag/chunker-ast.js +56 -0
- package/src/rag/chunker.js +132 -0
- package/src/rag/embedder.js +2 -0
- package/src/rag/embedders/_cloud-base.js +2 -0
- package/src/rag/embedders/cohere.js +2 -0
- package/src/rag/embedders/factory.js +2 -0
- package/src/rag/embedders/mistral.js +2 -0
- package/src/rag/embedders/onnx.js +2 -0
- package/src/rag/embedders/openai.js +2 -0
- package/src/rag/embedders/voyage.js +2 -0
- package/src/rag/eval.js +61 -0
- package/src/rag/governed-embedder.js +39 -0
- package/src/rag/indexer.js +199 -0
- package/src/rag/library.js +62 -0
- package/src/rag/ollama-capability.js +59 -0
- package/src/rag/ollama-manager.js +98 -0
- package/src/rag/onnx-fallback.js +69 -0
- package/src/rag/rerank.js +4 -0
- package/src/rag/retriever.js +2 -0
- package/src/rag/vec-store.js +17 -0
- package/src/rag/watcher.js +62 -0
- package/src/rag/where-parser.js +4 -0
- package/src/repeat-detector.js +88 -0
- package/src/review/card-first.js +99 -0
- package/src/review/card-verify.js +83 -0
- package/src/review/diff-generator.js +112 -0
- package/src/review/gate-gitignore.js +77 -0
- package/src/review/git-stash-helper.js +104 -0
- package/src/review/mutation-pregate.js +48 -0
- package/src/review/one-shot-review.js +122 -0
- package/src/review/parser.js +80 -0
- package/src/review/policy-gate.js +26 -0
- package/src/review/profiles.js +66 -0
- package/src/review/reviewer-fallback.js +125 -0
- package/src/review/schema.js +31 -0
- package/src/review/scope-filter.js +152 -0
- package/src/review/snapshot-diff.js +122 -0
- package/src/review/solomon-arbitration.js +113 -0
- package/src/review/sonar-pregate.js +63 -0
- package/src/review/tdd-discipline.js +90 -0
- package/src/review/tdd-policy.js +69 -0
- package/src/review/tests-with-code.js +33 -0
- package/src/review/verdict-store.js +87 -0
- package/src/review/workspace.js +21 -0
- package/src/roles/agent-role.js +194 -0
- package/src/roles/architect-role.js +77 -0
- package/src/roles/audit-role.js +258 -0
- package/src/roles/base-role.js +165 -0
- package/src/roles/coder-role.js +107 -0
- package/src/roles/commiter-role.js +110 -0
- package/src/roles/discover-role.js +81 -0
- package/src/roles/domain-curator-role.js +141 -0
- package/src/roles/hu-reviewer-role.js +64 -0
- package/src/roles/impeccable-role.js +85 -0
- package/src/roles/index.js +18 -0
- package/src/roles/karajan-brain-role.js +134 -0
- package/src/roles/onboarder-role.js +61 -0
- package/src/roles/perf-role.js +122 -0
- package/src/roles/planner-role.js +151 -0
- package/src/roles/refactorer-role.js +30 -0
- package/src/roles/repairer-role.js +181 -0
- package/src/roles/researcher-role.js +70 -0
- package/src/roles/reviewer-role.js +104 -0
- package/src/roles/security-role.js +73 -0
- package/src/roles/solomon-role.js +265 -0
- package/src/roles/sonar-role.js +70 -0
- package/src/roles/spec-reviewer-role.js +106 -0
- package/src/roles/tester-role.js +235 -0
- package/src/roles/tool-correctness-judge-role.js +78 -0
- package/src/roles/triage-role.js +99 -0
- package/src/session/journal/decisions-writer.js +200 -0
- package/src/session/journal/iteration-logger.js +194 -0
- package/src/session/journal/summary-writer.js +320 -0
- package/src/session/journal/tree-writer.js +164 -0
- package/src/session/journal/write-all.js +89 -0
- package/src/session/mutators.js +339 -0
- package/src/session/runtime-overrides.js +59 -0
- package/src/session/store.js +106 -0
- package/src/session-cleanup.js +116 -0
- package/src/skills/addyosmani-catalog.js +315 -0
- package/src/skills/addyosmani-role-map.js +139 -0
- package/src/skills/openskills-client.js +157 -0
- package/src/skills/semantic-detector.js +167 -0
- package/src/skills/skill-detector.js +385 -0
- package/src/skills/skill-loader.js +219 -0
- package/src/skills/skills-cache.js +128 -0
- package/src/sonar/api.js +128 -0
- package/src/sonar/cloud-scanner.js +78 -0
- package/src/sonar/config-resolver.js +88 -0
- package/src/sonar/credentials.js +60 -0
- package/src/sonar/discovery.js +124 -0
- package/src/sonar/enforcer.js +19 -0
- package/src/sonar/issue-filter.js +12 -0
- package/src/sonar/manager.js +181 -0
- package/src/sonar/project-key.js +120 -0
- package/src/sonar/scanner.js +307 -0
- package/src/sonar/token-bootstrap.js +205 -0
- package/src/sonar/token-recovery.js +67 -0
- package/src/spec-review/refine-loop.js +58 -0
- package/src/spec-review/run-spec-review.js +119 -0
- package/src/start/assessment.js +74 -0
- package/src/start/maturity.js +73 -0
- package/src/start/start-decider-role.js +79 -0
- package/src/start/sweep.js +157 -0
- package/src/sync/apply-canvas-patch.js +55 -0
- package/src/sync/detect-drift.js +113 -0
- package/src/tournament/crown.js +88 -0
- package/src/tournament/judge.js +150 -0
- package/src/tournament/run.js +142 -0
- package/src/tournament/scoreboard.js +124 -0
- package/src/tracker-bootstrap.js +44 -0
- package/src/types/agent.js +55 -0
- package/src/types/config.js +114 -0
- package/src/types/hu.js +47 -0
- package/src/types/index.js +38 -0
- package/src/types/policy.js +43 -0
- package/src/types/session.js +75 -0
- package/src/types/solomon.js +87 -0
- package/src/types/stage.js +68 -0
- package/src/utils/agent-detect.js +93 -0
- package/src/utils/ai-trash.js +48 -0
- package/src/utils/async-lock.js +31 -0
- package/src/utils/atomic-write.js +6 -0
- package/src/utils/banner.js +66 -0
- package/src/utils/binary-sources.js +70 -0
- package/src/utils/board-prompt-bridge.js +108 -0
- package/src/utils/budget.js +229 -0
- package/src/utils/cli-ask-question.js +93 -0
- package/src/utils/cli-progress.js +220 -0
- package/src/utils/cli-run-log.js +83 -0
- package/src/utils/cwd-confirm.js +39 -0
- package/src/utils/derive-project-name-from-cwd.js +119 -0
- package/src/utils/display/event-handlers.js +403 -0
- package/src/utils/display/formatters.js +132 -0
- package/src/utils/display/header.js +47 -0
- package/src/utils/display/resume-hint.js +41 -0
- package/src/utils/display/session-summary.js +118 -0
- package/src/utils/display/solomon.js +42 -0
- package/src/utils/display/spec-findings.js +44 -0
- package/src/utils/doc-links.js +29 -0
- package/src/utils/docker-install.js +34 -0
- package/src/utils/escape-regexp.js +15 -0
- package/src/utils/events.js +35 -0
- package/src/utils/fs.js +19 -0
- package/src/utils/garbage-collector.js +481 -0
- package/src/utils/git.js +279 -0
- package/src/utils/home-migration.js +87 -0
- package/src/utils/injection-guard.js +186 -0
- package/src/utils/install-hints.js +180 -0
- package/src/utils/json-extract.js +64 -0
- package/src/utils/locale.js +59 -0
- package/src/utils/logger.js +96 -0
- package/src/utils/managed-markers.js +93 -0
- package/src/utils/manifest-sniff.js +82 -0
- package/src/utils/messages.js +61 -0
- package/src/utils/model-selector.js +107 -0
- package/src/utils/os-detect.js +79 -0
- package/src/utils/paths.js +13 -0
- package/src/utils/pending-user-action.js +95 -0
- package/src/utils/port-check.js +6 -0
- package/src/utils/port-occupant.js +96 -0
- package/src/utils/pricing.js +29 -0
- package/src/utils/process.js +4 -0
- package/src/utils/project-detect.js +160 -0
- package/src/utils/prompt-project-name.js +35 -0
- package/src/utils/provider-env.js +116 -0
- package/src/utils/qmd-collection.js +72 -0
- package/src/utils/qmd-detect.js +18 -0
- package/src/utils/qmd-install.js +37 -0
- package/src/utils/quota-simulator.js +56 -0
- package/src/utils/rate-limit-detector.js +181 -0
- package/src/utils/repo-cleaner.js +79 -0
- package/src/utils/retry.js +88 -0
- package/src/utils/role-assigner.js +137 -0
- package/src/utils/role-env.js +58 -0
- package/src/utils/rtk-detect.js +18 -0
- package/src/utils/rtk-install.js +38 -0
- package/src/utils/rtk-wrapper.js +87 -0
- package/src/utils/run-context.js +75 -0
- package/src/utils/run-log.js +217 -0
- package/src/utils/run-registry.js +9 -0
- package/src/utils/shared-paths.js +12 -0
- package/src/utils/squeezr-detect.js +21 -0
- package/src/utils/squeezr-install.js +37 -0
- package/src/utils/stack-detect.js +135 -0
- package/src/utils/stall-detector.js +159 -0
- package/src/utils/status-dashboard.js +254 -0
- package/src/utils/task-file.js +109 -0
- package/src/utils/telemetry.js +124 -0
- package/src/utils/templates-root.js +61 -0
- package/src/utils/tool-call-extractor.js +56 -0
- package/src/utils/tool-governor.js +116 -0
- package/src/utils/tool-installer.js +93 -0
- package/src/utils/update-check.js +280 -0
- package/src/utils/vector-store-orphans.js +89 -0
- package/src/utils/welcome.js +47 -0
- package/src/utils/with-timeout.js +42 -0
- package/src/utils/wizard.js +47 -0
- package/src/webperf/cwv-gate.js +94 -0
- package/src/webperf/devtools-detect.js +112 -0
- package/src/webperf/scanner.js +209 -0
- package/src/webperf/webperf-role-integration.js +66 -0
- package/templates/coder-rules.md +45 -0
- package/templates/docker-compose.sonar.yml +60 -0
- package/templates/domains/example-dental.md +57 -0
- package/templates/kj.config.yml +128 -0
- package/templates/review-rules.md +17 -0
- package/templates/roles/architect/anthropic.md +74 -0
- package/templates/roles/architect/default.md +72 -0
- package/templates/roles/architect/google.md +61 -0
- package/templates/roles/architect/openai.md +60 -0
- package/templates/roles/architect/opencode.md +59 -0
- package/templates/roles/architect.md +76 -0
- package/templates/roles/audit.md +68 -0
- package/templates/roles/coder/anthropic.md +81 -0
- package/templates/roles/coder/default.md +78 -0
- package/templates/roles/coder/google.md +87 -0
- package/templates/roles/coder/openai.md +68 -0
- package/templates/roles/coder/opencode.md +58 -0
- package/templates/roles/coder.md +152 -0
- package/templates/roles/commiter.md +44 -0
- package/templates/roles/discover.md +167 -0
- package/templates/roles/domain-curator.md +52 -0
- package/templates/roles/hu-reviewer.md +236 -0
- package/templates/roles/impeccable-design.md +87 -0
- package/templates/roles/impeccable.md +125 -0
- package/templates/roles/karajan-brain.md +95 -0
- package/templates/roles/onboarder.md +45 -0
- package/templates/roles/planner/anthropic.md +59 -0
- package/templates/roles/planner/default.md +57 -0
- package/templates/roles/planner/google.md +75 -0
- package/templates/roles/planner/openai.md +55 -0
- package/templates/roles/planner/opencode.md +62 -0
- package/templates/roles/planner.md +61 -0
- package/templates/roles/refactorer.md +48 -0
- package/templates/roles/repairer.md +103 -0
- package/templates/roles/researcher.md +41 -0
- package/templates/roles/reviewer/anthropic.md +61 -0
- package/templates/roles/reviewer/default.md +62 -0
- package/templates/roles/reviewer/google.md +63 -0
- package/templates/roles/reviewer/openai.md +57 -0
- package/templates/roles/reviewer/opencode.md +42 -0
- package/templates/roles/reviewer-paranoid.md +38 -0
- package/templates/roles/reviewer-relaxed.md +34 -0
- package/templates/roles/reviewer-strict.md +37 -0
- package/templates/roles/reviewer.md +62 -0
- package/templates/roles/security.md +54 -0
- package/templates/roles/solomon/anthropic.md +71 -0
- package/templates/roles/solomon/default.md +83 -0
- package/templates/roles/solomon/google.md +61 -0
- package/templates/roles/solomon/openai.md +53 -0
- package/templates/roles/solomon/opencode.md +55 -0
- package/templates/roles/solomon.md +83 -0
- package/templates/roles/sonar.md +49 -0
- package/templates/roles/spec-reviewer.md +47 -0
- package/templates/roles/tester.md +50 -0
- package/templates/roles/triage.md +69 -0
- package/templates/skills/kj-architect.md +45 -0
- package/templates/skills/kj-audit.md +63 -0
- package/templates/skills/kj-board.md +16 -0
- package/templates/skills/kj-code.md +51 -0
- package/templates/skills/kj-csv-transform.md +40 -0
- package/templates/skills/kj-data-report.md +44 -0
- package/templates/skills/kj-discover.md +24 -0
- package/templates/skills/kj-rag-query.md +50 -0
- package/templates/skills/kj-review.md +47 -0
- package/templates/skills/kj-run.md +73 -0
- package/templates/skills/kj-security.md +49 -0
- package/templates/skills/kj-sonar.md +41 -0
- package/templates/skills/kj-sql-analysis.md +40 -0
- package/templates/skills/kj-test.md +40 -0
- package/templates/workflows/automerge.yml +30 -0
- package/templates/workflows/houston-override.yml +46 -0
- package/templates/workflows/kj-ci-gateway.yml +58 -0
- package/vendor/tree-sitter-grammars/README.md +21 -0
- package/vendor/tree-sitter-grammars/go.wasm +0 -0
- package/vendor/tree-sitter-grammars/java.wasm +0 -0
- package/vendor/tree-sitter-grammars/python.wasm +0 -0
- package/vendor/tree-sitter-grammars/rust.wasm +0 -0
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import { BaseAgent } from "./base-agent.js";
|
|
2
|
+
import { resolveBin } from "./resolve-bin.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Safely parse a JSON line, returning null on failure.
|
|
6
|
+
*/
|
|
7
|
+
function tryParseJson(line) {
|
|
8
|
+
try {
|
|
9
|
+
return JSON.parse(line);
|
|
10
|
+
} catch { return null; }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Extract a human-readable error message from Claude's raw NDJSON stderr output.
|
|
15
|
+
* Claude emits system init, api_retry, assistant error, and result lines.
|
|
16
|
+
* We extract just the meaningful error text, discarding the JSON noise.
|
|
17
|
+
*/
|
|
18
|
+
export function sanitizeClaudeError(raw) {
|
|
19
|
+
if (!raw) return "Unknown error";
|
|
20
|
+
const lines = raw.split("\n").filter(Boolean);
|
|
21
|
+
|
|
22
|
+
// Look for a result line with the actual error
|
|
23
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
24
|
+
const obj = tryParseJson(lines[i]);
|
|
25
|
+
if (!obj) continue;
|
|
26
|
+
if (obj.type === "result" && obj.result) return String(obj.result);
|
|
27
|
+
if (obj.type === "assistant" && obj.message?.content) {
|
|
28
|
+
const text = obj.message.content
|
|
29
|
+
.filter(b => b.type === "text" && b.text)
|
|
30
|
+
.map(b => b.text)
|
|
31
|
+
.join(" ");
|
|
32
|
+
if (text) return text;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// If no JSON parsed, return first non-JSON line or truncated raw
|
|
37
|
+
for (const line of lines) {
|
|
38
|
+
if (!line.startsWith("{")) return line.slice(0, 200);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return raw.slice(0, 200);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Try to extract a result string from a parsed JSON object.
|
|
46
|
+
* Returns the result string or null if the object is not a result message.
|
|
47
|
+
*/
|
|
48
|
+
function extractResultText(obj) {
|
|
49
|
+
if (obj.type === "result" && obj.result) {
|
|
50
|
+
return typeof obj.result === "string" ? obj.result : JSON.stringify(obj.result);
|
|
51
|
+
}
|
|
52
|
+
if (obj.result && typeof obj.result === "string") {
|
|
53
|
+
return obj.result;
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Collect text parts from an assistant message's content blocks.
|
|
60
|
+
*/
|
|
61
|
+
function collectAssistantText(obj) {
|
|
62
|
+
if (obj.type !== "assistant" || !obj.message?.content) return [];
|
|
63
|
+
return obj.message.content
|
|
64
|
+
.filter(block => block.type === "text" && block.text)
|
|
65
|
+
.map(block => block.text);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Extract usage metrics from stream-json/json NDJSON output.
|
|
70
|
+
* Looks for the "result" line which contains total_cost_usd,
|
|
71
|
+
* usage.input_tokens/output_tokens, cache_read_input_tokens,
|
|
72
|
+
* cache_creation_input_tokens, and modelUsage.
|
|
73
|
+
*
|
|
74
|
+
* `cached_tokens` aggregates both cache_read_input_tokens (tokens served
|
|
75
|
+
* from Anthropic's automatic prompt cache) and cache_creation_input_tokens
|
|
76
|
+
* (tokens just written to the cache during this call). The sum represents
|
|
77
|
+
* the total volume of input that benefited from prompt-caching machinery.
|
|
78
|
+
*
|
|
79
|
+
* Returns an object with tokens_in, tokens_out, cached_tokens, cost_usd,
|
|
80
|
+
* model or null if not found.
|
|
81
|
+
*/
|
|
82
|
+
function extractUsageFromStreamJson(raw) {
|
|
83
|
+
const events = parseClaudeEvents(raw);
|
|
84
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
85
|
+
const obj = events[i];
|
|
86
|
+
if (obj.type !== "result") continue;
|
|
87
|
+
|
|
88
|
+
const tokens_in = obj.usage?.input_tokens ?? 0;
|
|
89
|
+
const tokens_out = obj.usage?.output_tokens ?? 0;
|
|
90
|
+
const cache_read = obj.usage?.cache_read_input_tokens ?? 0;
|
|
91
|
+
const cache_creation = obj.usage?.cache_creation_input_tokens ?? 0;
|
|
92
|
+
const cached_tokens = cache_read + cache_creation;
|
|
93
|
+
const cost_usd = obj.total_cost_usd ?? undefined;
|
|
94
|
+
const modelUsage = obj.modelUsage;
|
|
95
|
+
const model = modelUsage ? Object.keys(modelUsage)[0] || null : null;
|
|
96
|
+
|
|
97
|
+
return { tokens_in, tokens_out, cached_tokens, cost_usd, model };
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Normalize Claude's output payload to a flat array of event objects.
|
|
104
|
+
*
|
|
105
|
+
* Claude 2.x supports two on-disk shapes depending on the output-format flag:
|
|
106
|
+
*
|
|
107
|
+
* --output-format stream-json → NDJSON, one event JSON per line
|
|
108
|
+
* --output-format json → a single JSON array with every event
|
|
109
|
+
*
|
|
110
|
+
* The legacy path assumed NDJSON and silently dropped the `json` variant
|
|
111
|
+
* (split("\n") returned one unparseable "line" containing the whole array,
|
|
112
|
+
* extract returned empty, caller fell back to the raw string — which made
|
|
113
|
+
* `kj plan` save the whole stream as `approach` with zero HUs). Handle both
|
|
114
|
+
* shapes here so callers see a uniform list of events regardless of flag.
|
|
115
|
+
*/
|
|
116
|
+
function parseClaudeEvents(raw) {
|
|
117
|
+
const trimmed = (raw || "").trim();
|
|
118
|
+
if (!trimmed) return [];
|
|
119
|
+
// `--output-format json` → single JSON array.
|
|
120
|
+
if (trimmed.startsWith("[")) {
|
|
121
|
+
try {
|
|
122
|
+
const arr = JSON.parse(trimmed);
|
|
123
|
+
if (Array.isArray(arr)) return arr.filter((o) => o && typeof o === "object");
|
|
124
|
+
} catch { /* fall through to NDJSON */ }
|
|
125
|
+
}
|
|
126
|
+
// `--output-format stream-json` → NDJSON, one event per line.
|
|
127
|
+
const events = [];
|
|
128
|
+
for (const line of trimmed.split("\n")) {
|
|
129
|
+
const obj = tryParseJson(line);
|
|
130
|
+
if (obj && typeof obj === "object") events.push(obj);
|
|
131
|
+
}
|
|
132
|
+
return events;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Extract the final text result from Claude's output (either stream-json
|
|
137
|
+
* NDJSON or single-array json — see parseClaudeEvents). We look for the
|
|
138
|
+
* `result` message first (holds the final text); if missing, concatenate
|
|
139
|
+
* every assistant text block encountered.
|
|
140
|
+
*/
|
|
141
|
+
function extractTextFromStreamJson(raw) {
|
|
142
|
+
const events = parseClaudeEvents(raw);
|
|
143
|
+
// Walk from the end — the final `result` event is the last one.
|
|
144
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
145
|
+
const result = extractResultText(events[i]);
|
|
146
|
+
if (result) return result;
|
|
147
|
+
}
|
|
148
|
+
// Fallback: accumulate every assistant text block in order.
|
|
149
|
+
const parts = [];
|
|
150
|
+
for (const obj of events) parts.push(...collectAssistantText(obj));
|
|
151
|
+
return parts.join("") || raw;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Create a wrapping onOutput that parses stream-json lines and forwards
|
|
156
|
+
* meaningful content (assistant text, tool usage) to the original callback.
|
|
157
|
+
*/
|
|
158
|
+
function summarizeToolCall(name, input = {}) {
|
|
159
|
+
// Produce concise human-readable action line from tool_use block
|
|
160
|
+
const rel = (p) => String(p || "").replace(process.cwd() + "/", "");
|
|
161
|
+
switch (name) {
|
|
162
|
+
case "Read": return `Read ${rel(input.file_path)}`;
|
|
163
|
+
case "Write": return `Write ${rel(input.file_path)}`;
|
|
164
|
+
case "Edit": return `Edit ${rel(input.file_path)}`;
|
|
165
|
+
case "MultiEdit": return `MultiEdit ${rel(input.file_path)} (${(input.edits || []).length} edits)`;
|
|
166
|
+
case "NotebookEdit": return `NotebookEdit ${rel(input.notebook_path)}`;
|
|
167
|
+
case "Glob": return `Glob ${input.pattern || ""}`.trim();
|
|
168
|
+
case "Grep": return `Grep "${(input.pattern || "").slice(0, 60)}"${input.path ? ` in ${rel(input.path)}` : ""}`;
|
|
169
|
+
case "Bash": {
|
|
170
|
+
const cmd = String(input.command || "").replaceAll(/\s+/g, " ").slice(0, 100);
|
|
171
|
+
return `Bash $ ${cmd}`;
|
|
172
|
+
}
|
|
173
|
+
case "TodoWrite": return `Todo ${(input.todos || []).length} items`;
|
|
174
|
+
case "WebFetch": return `WebFetch ${input.url || ""}`;
|
|
175
|
+
case "WebSearch": return `WebSearch "${(input.query || "").slice(0, 60)}"`;
|
|
176
|
+
case "Task": return `Agent: ${input.subagent_type || "?"} — ${(input.description || "").slice(0, 60)}`;
|
|
177
|
+
default: return `${name}${Object.keys(input).length > 0 ? " (…)" : ""}`;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Translate a Claude stream-json event into a human-readable summary.
|
|
183
|
+
*
|
|
184
|
+
* Returns a `{ line, kind }` object for the cli-progress reporter to
|
|
185
|
+
* display, OR `null` to suppress (the event is structural noise).
|
|
186
|
+
*
|
|
187
|
+
* Whitelist semantics on purpose: pre-v2.7.5 the filter ONLY recognised
|
|
188
|
+
* `assistant` + `result` and forwarded everything else as raw JSON,
|
|
189
|
+
* which dumped 6 KB of `system/init` + `rate_limit_event` to the user
|
|
190
|
+
* on every `kj plan`. Now we recognise every event type Claude can
|
|
191
|
+
* emit and either condense it to one line or drop it. Unknown types
|
|
192
|
+
* are dropped silently unless DEBUG_KJ_AGENT is set.
|
|
193
|
+
*/
|
|
194
|
+
export function translateStreamJsonEvent(obj) {
|
|
195
|
+
switch (obj.type) {
|
|
196
|
+
case "system": {
|
|
197
|
+
// The init frame announces session id + model. Useful as a single
|
|
198
|
+
// status line on first connect; the rate_limit subtype on a
|
|
199
|
+
// healthy session is pure noise — suppress.
|
|
200
|
+
if (obj.subtype === "init") {
|
|
201
|
+
const model = typeof obj.model === "string" ? obj.model : "?";
|
|
202
|
+
return { line: `Claude session ready (model: ${model.replace(/\[.*\]/, "")})`, kind: "text" };
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
case "rate_limit_event": {
|
|
207
|
+
const status = obj.rate_limit_info?.status;
|
|
208
|
+
// Only surface when actually throttled — "allowed" is the happy
|
|
209
|
+
// path and surfaces every few seconds during normal runs.
|
|
210
|
+
if (status && status !== "allowed") {
|
|
211
|
+
return { line: `Rate limit: ${status}`, kind: "text" };
|
|
212
|
+
}
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
case "user": {
|
|
216
|
+
// Echoes of the previous tool's output coming back into the
|
|
217
|
+
// conversation. Adding a line per echo would double the noise of
|
|
218
|
+
// the corresponding tool_use line, which we already render.
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
case "assistant": {
|
|
222
|
+
const content = obj.message?.content;
|
|
223
|
+
if (!Array.isArray(content)) return null;
|
|
224
|
+
// Each content block becomes a separate line — we return them
|
|
225
|
+
// through the caller's emit-many path below.
|
|
226
|
+
return { _multi: true, blocks: content };
|
|
227
|
+
}
|
|
228
|
+
case "result": {
|
|
229
|
+
const summary = typeof obj.result === "string"
|
|
230
|
+
? obj.result.split("\n")[0].slice(0, 120)
|
|
231
|
+
: "result received";
|
|
232
|
+
return { line: `done: ${summary}` };
|
|
233
|
+
}
|
|
234
|
+
default: {
|
|
235
|
+
if (process.env.DEBUG_KJ_AGENT) {
|
|
236
|
+
return { line: `[debug] unknown event: ${obj.type}` };
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function createStreamJsonFilter(onOutput) {
|
|
244
|
+
if (!onOutput) return null;
|
|
245
|
+
return ({ stream, line }) => {
|
|
246
|
+
let obj;
|
|
247
|
+
try {
|
|
248
|
+
obj = JSON.parse(line);
|
|
249
|
+
} catch {
|
|
250
|
+
// Not JSON — forward verbatim ONLY if it's short and not a partial
|
|
251
|
+
// JSON chunk (some agents print stderr diagnostics through the
|
|
252
|
+
// same channel). Anything that smells like JSON-debris gets
|
|
253
|
+
// dropped to keep the CLI clean.
|
|
254
|
+
const trimmed = line.trim();
|
|
255
|
+
if (trimmed && !trimmed.startsWith("{") && !trimmed.startsWith("[") && trimmed.length < 200) {
|
|
256
|
+
onOutput({ stream, line: trimmed });
|
|
257
|
+
}
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const summary = translateStreamJsonEvent(obj);
|
|
262
|
+
if (!summary) return;
|
|
263
|
+
|
|
264
|
+
// Multi-block assistant payload: one line per content block.
|
|
265
|
+
if (summary._multi) {
|
|
266
|
+
for (const block of summary.blocks) {
|
|
267
|
+
if (block.type === "text" && block.text) {
|
|
268
|
+
const firstLine = block.text.split("\n")[0].trim().slice(0, 120);
|
|
269
|
+
if (firstLine) onOutput({ stream, line: firstLine, kind: "text" });
|
|
270
|
+
} else if (block.type === "tool_use") {
|
|
271
|
+
onOutput({ stream, line: summarizeToolCall(block.name, block.input), kind: "tool" });
|
|
272
|
+
} else if (block.type === "thinking" && block.thinking) {
|
|
273
|
+
onOutput({ stream, line: `thinking: ${block.thinking.slice(0, 80).replaceAll(/\s+/g, " ")}…`, kind: "thinking" });
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
onOutput({ stream, line: summary.line, kind: summary.kind });
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Build clean execa options for Claude subprocess.
|
|
285
|
+
*
|
|
286
|
+
* Three critical fixes for running `claude -p` from Node.js:
|
|
287
|
+
*
|
|
288
|
+
* 1. Strip CLAUDECODE env var — Claude Code 2.x sets this to block nested
|
|
289
|
+
* sessions. The spawned `claude -p` is a separate non-interactive
|
|
290
|
+
* invocation, not a true nested session.
|
|
291
|
+
*
|
|
292
|
+
* 2. Detach stdin (stdin: "ignore") — When launched from Node.js (which is
|
|
293
|
+
* how Claude Code / Karajan MCP runs), the child inherits the parent's
|
|
294
|
+
* stdin. `claude -p` then blocks waiting to read from a stdin that the
|
|
295
|
+
* parent is already consuming. Ignoring stdin prevents the hang.
|
|
296
|
+
*
|
|
297
|
+
* 3. Claude Code 2.x writes all structured output (stream-json, json) to
|
|
298
|
+
* stderr, NOT stdout. The agent must read from stderr for the actual
|
|
299
|
+
* response data.
|
|
300
|
+
*/
|
|
301
|
+
function cleanExecaOpts(extra = {}) {
|
|
302
|
+
const { CLAUDECODE: _CLAUDECODE, ...env } = process.env;
|
|
303
|
+
// PAR-H (KJC-TSK-0631): extra.env ADDS to the inherited env (lane slot
|
|
304
|
+
// vars) instead of replacing it wholesale.
|
|
305
|
+
const { env: extraEnv, ...rest } = extra;
|
|
306
|
+
return { env: extraEnv ? { ...env, ...extraEnv } : env, stdin: "ignore", ...rest };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Pick the best raw output from a claude subprocess result.
|
|
311
|
+
* Claude 2.x sends structured output to stderr; stdout is often empty.
|
|
312
|
+
*/
|
|
313
|
+
function pickOutput(res) {
|
|
314
|
+
return res.stdout || res.stderr || "";
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Default tools to allow for Claude subprocess.
|
|
319
|
+
* Since claude -p runs non-interactively (stdin: "ignore"), it cannot ask for
|
|
320
|
+
* permission approval. Without --allowedTools, it blocks waiting for approval
|
|
321
|
+
* that never comes.
|
|
322
|
+
*/
|
|
323
|
+
const ALLOWED_TOOLS = [
|
|
324
|
+
"Read", "Write", "Edit", "Bash", "Glob", "Grep"
|
|
325
|
+
];
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Base CLI args for a task (Φ1-D, KJC-PCS-0057). When the role provides the
|
|
329
|
+
* prompt split in stable/volatile buckets, the stable block travels via
|
|
330
|
+
* --append-system-prompt — the CLI places cache breakpoints on the system
|
|
331
|
+
* block, so Anthropic's prompt cache hits on it across iterations — and only
|
|
332
|
+
* the volatile block goes in the -p message. Without the split, legacy
|
|
333
|
+
* single-prompt behavior is unchanged.
|
|
334
|
+
*/
|
|
335
|
+
function buildPromptArgs(task) {
|
|
336
|
+
if (task.stablePrompt && task.volatilePrompt) {
|
|
337
|
+
return ["-p", task.volatilePrompt, "--append-system-prompt", task.stablePrompt, "--allowedTools", ...ALLOWED_TOOLS];
|
|
338
|
+
}
|
|
339
|
+
return ["-p", task.prompt, "--allowedTools", ...ALLOWED_TOOLS];
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export class ClaudeAgent extends BaseAgent {
|
|
343
|
+
async runTask(task) {
|
|
344
|
+
const role = task.role || "coder";
|
|
345
|
+
const model = this.getRoleModel(role);
|
|
346
|
+
const result = await this._runTaskExec(task, model, role);
|
|
347
|
+
if (!result.ok && model && this.isModelNotSupportedError(result)) {
|
|
348
|
+
this.logger?.warn(`Claude model "${model}" not supported — retrying with agent default`);
|
|
349
|
+
return this._runTaskExec(task, null, role);
|
|
350
|
+
}
|
|
351
|
+
return result;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async reviewTask(task) {
|
|
355
|
+
const role = task.role || "reviewer";
|
|
356
|
+
const model = this.getRoleModel(role);
|
|
357
|
+
const result = await this._reviewTaskExec(task, model);
|
|
358
|
+
if (!result.ok && model && this.isModelNotSupportedError(result)) {
|
|
359
|
+
this.logger?.warn(`Claude model "${model}" not supported — retrying with agent default`);
|
|
360
|
+
return this._reviewTaskExec(task, null);
|
|
361
|
+
}
|
|
362
|
+
return result;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
async _runTaskExec(task, model, _role) {
|
|
366
|
+
const args = buildPromptArgs(task);
|
|
367
|
+
if (model) args.push("--model", model);
|
|
368
|
+
// PL-C (KJC-TSK-0735): el rol del subproceso viaja en KJ_POLICY_ROLE —
|
|
369
|
+
// si su harness corre hooks (claude -p), el tier A evalúa la policy con
|
|
370
|
+
// el rol que ACTÚA. El rol del ORQUESTADOR es autoritativo: task.env no
|
|
371
|
+
// puede spoofearlo (catch de codex: sería un bypass de reglas por rol).
|
|
372
|
+
const roleEnv = task.role ? { ...task.env, KJ_POLICY_ROLE: task.role } : task.env;
|
|
373
|
+
|
|
374
|
+
// Use stream-json when onOutput is provided to get real-time feedback
|
|
375
|
+
if (task.onOutput) {
|
|
376
|
+
args.push("--output-format", "stream-json", "--verbose");
|
|
377
|
+
const streamFilter = createStreamJsonFilter(task.onOutput);
|
|
378
|
+
const res = await this.runCommand(resolveBin("claude"), args, cleanExecaOpts({
|
|
379
|
+
onOutput: streamFilter,
|
|
380
|
+
silenceTimeoutMs: task.silenceTimeoutMs,
|
|
381
|
+
timeout: task.timeoutMs,
|
|
382
|
+
env: roleEnv,
|
|
383
|
+
cwd: task.cwd
|
|
384
|
+
}));
|
|
385
|
+
const raw = pickOutput(res);
|
|
386
|
+
const output = extractTextFromStreamJson(raw);
|
|
387
|
+
const usage = extractUsageFromStreamJson(raw);
|
|
388
|
+
return { ok: res.exitCode === 0, output, error: res.exitCode === 0 ? "" : sanitizeClaudeError(raw), exitCode: res.exitCode, ...usage };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Without streaming, use json output to get structured response via stderr
|
|
392
|
+
args.push("--output-format", "json");
|
|
393
|
+
const res = await this.runCommand(resolveBin("claude"), args, cleanExecaOpts({ env: roleEnv, cwd: task.cwd }));
|
|
394
|
+
const raw = pickOutput(res);
|
|
395
|
+
const output = extractTextFromStreamJson(raw);
|
|
396
|
+
const usage = extractUsageFromStreamJson(raw);
|
|
397
|
+
return { ok: res.exitCode === 0, output, error: res.exitCode === 0 ? "" : sanitizeClaudeError(raw), exitCode: res.exitCode, ...usage };
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async _reviewTaskExec(task, model) {
|
|
401
|
+
const args = [...buildPromptArgs(task), "--output-format", "stream-json", "--verbose"];
|
|
402
|
+
if (model) args.push("--model", model);
|
|
403
|
+
const res = await this.runCommand(resolveBin("claude"), args, cleanExecaOpts({
|
|
404
|
+
onOutput: task.onOutput,
|
|
405
|
+
silenceTimeoutMs: task.silenceTimeoutMs,
|
|
406
|
+
timeout: task.timeoutMs,
|
|
407
|
+
env: task.env
|
|
408
|
+
}));
|
|
409
|
+
const raw = pickOutput(res);
|
|
410
|
+
const usage = extractUsageFromStreamJson(raw);
|
|
411
|
+
return { ok: res.exitCode === 0, output: raw, error: res.exitCode === 0 ? "" : raw, exitCode: res.exitCode, ...usage };
|
|
412
|
+
}
|
|
413
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { BaseAgent } from "./base-agent.js";
|
|
2
|
+
import { resolveBin } from "./resolve-bin.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Try to extract an OpenAI-style `usage` JSON block from codex stdout.
|
|
6
|
+
*
|
|
7
|
+
* OpenAI's Chat Completions API exposes prompt-cache hits via
|
|
8
|
+
* `usage.prompt_tokens_details.cached_tokens` (since Aug 2024). Codex
|
|
9
|
+
* CLI doesn't surface this in its default footer, but newer flags or a
|
|
10
|
+
* wrapper may emit a JSON `usage` line. We scan stdout line-by-line and
|
|
11
|
+
* accept the first parseable object that carries `prompt_tokens`.
|
|
12
|
+
*
|
|
13
|
+
* Returns `{tokens_in, tokens_out, cached_tokens}` or null when absent.
|
|
14
|
+
*/
|
|
15
|
+
function extractJsonUsage(stdout) {
|
|
16
|
+
for (const line of stdout.split("\n")) {
|
|
17
|
+
const trimmed = line.trim();
|
|
18
|
+
if (!trimmed.startsWith("{")) continue;
|
|
19
|
+
let obj;
|
|
20
|
+
try { obj = JSON.parse(trimmed); } catch { continue; }
|
|
21
|
+
const usage = obj?.usage ?? (typeof obj?.prompt_tokens === "number" ? obj : null);
|
|
22
|
+
if (!usage || typeof usage.prompt_tokens !== "number") continue;
|
|
23
|
+
return {
|
|
24
|
+
tokens_in: usage.prompt_tokens,
|
|
25
|
+
tokens_out: usage.completion_tokens ?? 0,
|
|
26
|
+
cached_tokens: usage.prompt_tokens_details?.cached_tokens ?? 0
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Extract token usage from Codex CLI stdout. Two shapes are supported:
|
|
34
|
+
*
|
|
35
|
+
* 1. OpenAI-style JSON `usage` block (preferred):
|
|
36
|
+
* `{"usage":{"prompt_tokens":N,"completion_tokens":M,
|
|
37
|
+
* "prompt_tokens_details":{"cached_tokens":C}}}`
|
|
38
|
+
* → `{tokens_in:N, tokens_out:M, cached_tokens:C}`.
|
|
39
|
+
*
|
|
40
|
+
* 2. Legacy "tokens used\n<number>" footer (codex's default total,
|
|
41
|
+
* no cache visibility) → `{tokens_in:0, tokens_out, cached_tokens:0}`.
|
|
42
|
+
*
|
|
43
|
+
* Returns null when neither shape is found.
|
|
44
|
+
*/
|
|
45
|
+
function extractCodexTokens(stdout) {
|
|
46
|
+
if (!stdout) return null;
|
|
47
|
+
const json = extractJsonUsage(stdout);
|
|
48
|
+
if (json) return json;
|
|
49
|
+
const match = stdout.match(/tokens?\s+used\s*\n\s*([\d,]+)/i);
|
|
50
|
+
if (!match) return null;
|
|
51
|
+
const total = Number(match[1].replaceAll(/,/g, ""));
|
|
52
|
+
if (!Number.isFinite(total) || total <= 0) return null;
|
|
53
|
+
return { tokens_in: 0, tokens_out: total, cached_tokens: 0 };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export class CodexAgent extends BaseAgent {
|
|
57
|
+
async runTask(task) {
|
|
58
|
+
const role = task.role || "coder";
|
|
59
|
+
const model = this.getRoleModel(role);
|
|
60
|
+
const result = await this._exec(task, model, role);
|
|
61
|
+
if (!result.ok && model && this.isModelNotSupportedError(result)) {
|
|
62
|
+
this.logger?.warn(`Codex model "${model}" not supported — retrying with agent default`);
|
|
63
|
+
return this._exec(task, null, role);
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async reviewTask(task) {
|
|
69
|
+
const role = task.role || "reviewer";
|
|
70
|
+
const model = this.getRoleModel(role);
|
|
71
|
+
const result = await this._exec(task, model, role);
|
|
72
|
+
if (!result.ok && model && this.isModelNotSupportedError(result)) {
|
|
73
|
+
this.logger?.warn(`Codex model "${model}" not supported — retrying with agent default`);
|
|
74
|
+
return this._exec(task, null, role);
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async _exec(task, model, role) {
|
|
80
|
+
// KJC-BUG-0143 (campo, issue #1471): codex exec >= 0.146 rechaza
|
|
81
|
+
// --full-auto ("unexpected argument"); el auto-approve moderno para un
|
|
82
|
+
// coder que escribe es --sandbox workspace-write. Los CLIs antiguos no
|
|
83
|
+
// conocen --sandbox: si lo rechazan, se reintenta con el flag legacy.
|
|
84
|
+
const autoApprove = role !== "reviewer" && this.isAutoApproveEnabled(role);
|
|
85
|
+
const res = await this._execOnce(task, model, role, { autoApprove, legacyAutoFlag: false });
|
|
86
|
+
if (!res.ok && autoApprove && /unexpected argument '--sandbox'/.test(res.error || "")) {
|
|
87
|
+
this.logger?.warn("codex CLI sin --sandbox — reintentando con el flag legacy --full-auto");
|
|
88
|
+
return this._execOnce(task, model, role, { autoApprove, legacyAutoFlag: true });
|
|
89
|
+
}
|
|
90
|
+
return res;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async _execOnce(task, model, role, { autoApprove, legacyAutoFlag }) {
|
|
94
|
+
const args = ["exec", "--skip-git-repo-check"];
|
|
95
|
+
if (model) args.push("--model", model);
|
|
96
|
+
if (autoApprove) {
|
|
97
|
+
if (legacyAutoFlag) args.push("--full-auto");
|
|
98
|
+
else args.push("--sandbox", "workspace-write");
|
|
99
|
+
}
|
|
100
|
+
args.push("-");
|
|
101
|
+
const res = await this.runCommand(resolveBin("codex"), args, {
|
|
102
|
+
onOutput: task.onOutput,
|
|
103
|
+
silenceTimeoutMs: task.silenceTimeoutMs,
|
|
104
|
+
timeout: task.timeoutMs,
|
|
105
|
+
input: task.prompt,
|
|
106
|
+
// TOR-A (KJC-TSK-0723): tournament lanes run the coder INSIDE the lane.
|
|
107
|
+
cwd: task.cwd
|
|
108
|
+
});
|
|
109
|
+
const usage = extractCodexTokens(res.stdout);
|
|
110
|
+
return { ok: res.exitCode === 0, output: res.stdout, error: res.stderr, exitCode: res.exitCode, ...usage };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { BaseAgent } from "./base-agent.js";
|
|
2
|
+
import { resolveBin } from "./resolve-bin.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* GitHub Copilot CLI (@github/copilot, binary `copilot`) — KJC-TSK-0666.
|
|
6
|
+
* The seventh built-in agent, and the user-chosen third AI for solomon
|
|
7
|
+
* arbitration (gemini tier retired, Qwen OAuth free tier discontinued).
|
|
8
|
+
*
|
|
9
|
+
* Verified live against copilot 1.0.73:
|
|
10
|
+
* - `-p <prompt>` runs non-interactive; copilot has NO stdin prompt channel
|
|
11
|
+
* (stdin is treated as context, spawning an agentic run) — so the prompt
|
|
12
|
+
* travels as an argument. Same documented E2BIG limit as claude-agent
|
|
13
|
+
* (KJC-BUG-0121) for multi-hundred-KB diffs.
|
|
14
|
+
* - `--output-format json` emits JSONL; the final text lives in the
|
|
15
|
+
* `assistant.message` event's `data.content`, run metadata in `result`.
|
|
16
|
+
* - Default model routing is automatic and varies per run (haiku, gpt-5-mini
|
|
17
|
+
* observed) — pin it per role via `roles.<role>.model` when neutrality
|
|
18
|
+
* matters (e.g. a gemini-family model for solomon).
|
|
19
|
+
*/
|
|
20
|
+
export class CopilotAgent extends BaseAgent {
|
|
21
|
+
async runTask(task) {
|
|
22
|
+
// Coder mode IS an agentic run: file edits and shell need permissions.
|
|
23
|
+
return this._exec(task, this.getRoleModel(task.role || "coder"), ["--allow-all-tools"]);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async reviewTask(task) {
|
|
27
|
+
// Review/arbitration answers from the prompt alone — no tool grants, and
|
|
28
|
+
// an explicit no-tools instruction so copilot never stalls waiting for
|
|
29
|
+
// an approval that headless mode cannot give.
|
|
30
|
+
return this._exec(
|
|
31
|
+
{ ...task, prompt: `Do NOT use any tools. Answer directly from this prompt only.\n\n${task.prompt}` },
|
|
32
|
+
this.getRoleModel(task.role || "reviewer"),
|
|
33
|
+
[],
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async _exec(task, model, extraArgs) {
|
|
38
|
+
const args = ["-p", task.prompt, "--output-format", "json", "--no-color", ...extraArgs];
|
|
39
|
+
if (model) args.push("--model", model);
|
|
40
|
+
const res = await this.runCommand(resolveBin("copilot"), args, {
|
|
41
|
+
onOutput: task.onOutput,
|
|
42
|
+
silenceTimeoutMs: task.silenceTimeoutMs,
|
|
43
|
+
timeout: task.timeoutMs
|
|
44
|
+
});
|
|
45
|
+
const output = extractCopilotOutput(res.stdout);
|
|
46
|
+
return { ok: res.exitCode === 0, output: output ?? res.stdout, error: res.stderr, exitCode: res.exitCode };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Last `assistant.message` content from copilot's JSONL stream (null if none). */
|
|
51
|
+
export function extractCopilotOutput(stdout) {
|
|
52
|
+
if (!stdout) return null;
|
|
53
|
+
let content = null;
|
|
54
|
+
for (const line of stdout.split("\n")) {
|
|
55
|
+
const t = line.trim();
|
|
56
|
+
if (!t.startsWith("{")) continue;
|
|
57
|
+
try {
|
|
58
|
+
const obj = JSON.parse(t);
|
|
59
|
+
if (obj?.type === "assistant.message" && typeof obj?.data?.content === "string") {
|
|
60
|
+
content = obj.data.content;
|
|
61
|
+
}
|
|
62
|
+
} catch { /* non-JSON noise between events */ }
|
|
63
|
+
}
|
|
64
|
+
return content;
|
|
65
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { BaseAgent } from "./base-agent.js";
|
|
2
|
+
import { resolveBin } from "./resolve-bin.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Extract Gemini's `usageMetadata` from stdout, including
|
|
6
|
+
* `cachedContentTokenCount` (Gemini's context-caching API metric,
|
|
7
|
+
* https://ai.google.dev/api/caching).
|
|
8
|
+
*
|
|
9
|
+
* The gemini CLI emits `usageMetadata` as part of its JSON response
|
|
10
|
+
* shape (today: only `--output-format json`, used in reviewTask; in
|
|
11
|
+
* plain runTask mode no usage is surfaced). When absent, returns null
|
|
12
|
+
* so the caller leaves `cached_tokens` undefined (= unmeasured,
|
|
13
|
+
* distinct from a measured 0).
|
|
14
|
+
*/
|
|
15
|
+
function extractGeminiUsage(text) {
|
|
16
|
+
if (!text) return null;
|
|
17
|
+
const candidates = [];
|
|
18
|
+
for (const line of text.split("\n")) {
|
|
19
|
+
const t = line.trim();
|
|
20
|
+
if (t.startsWith("{") || t.startsWith("[")) candidates.push(t);
|
|
21
|
+
}
|
|
22
|
+
candidates.push(text.trim());
|
|
23
|
+
for (const candidate of candidates) {
|
|
24
|
+
let obj;
|
|
25
|
+
try { obj = JSON.parse(candidate); } catch { continue; }
|
|
26
|
+
const meta = obj?.usageMetadata
|
|
27
|
+
?? obj?.usage_metadata
|
|
28
|
+
?? obj?.response?.usageMetadata;
|
|
29
|
+
if (!meta || typeof meta.promptTokenCount !== "number") continue;
|
|
30
|
+
return {
|
|
31
|
+
tokens_in: meta.promptTokenCount,
|
|
32
|
+
tokens_out: meta.candidatesTokenCount ?? 0,
|
|
33
|
+
cached_tokens: meta.cachedContentTokenCount ?? 0
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class GeminiAgent extends BaseAgent {
|
|
40
|
+
// Overridden by forks with the same headless interface (QwenAgent).
|
|
41
|
+
get cliBin() { return "gemini"; }
|
|
42
|
+
get spawnEnv() { return { GEMINI_CLI_TRUST_WORKSPACE: "true" }; }
|
|
43
|
+
|
|
44
|
+
async runTask(task) {
|
|
45
|
+
const role = task.role || "coder";
|
|
46
|
+
const model = this.getRoleModel(role);
|
|
47
|
+
const result = await this._exec(task, model, "run");
|
|
48
|
+
if (!result.ok && model && this.isModelNotSupportedError(result)) {
|
|
49
|
+
this.logger?.warn(`${this.cliBin} model "${model}" not supported — retrying with agent default`);
|
|
50
|
+
return this._exec(task, null, "run");
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async reviewTask(task) {
|
|
56
|
+
const role = task.role || "reviewer";
|
|
57
|
+
const model = this.getRoleModel(role);
|
|
58
|
+
const result = await this._exec(task, model, "review");
|
|
59
|
+
if (!result.ok && model && this.isModelNotSupportedError(result)) {
|
|
60
|
+
this.logger?.warn(`${this.cliBin} model "${model}" not supported — retrying with agent default`);
|
|
61
|
+
return this._exec(task, null, "review");
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async _exec(task, model, mode) {
|
|
67
|
+
// KJC-BUG-0121: the prompt NEVER travels as a CLI argument — a solomon
|
|
68
|
+
// prompt embeds the full diff, and large diffs blow past the kernel's
|
|
69
|
+
// per-argument limit (E2BIG). gemini reads the prompt from stdin in
|
|
70
|
+
// headless mode. Same bug's layer 2: headless gemini refuses untrusted
|
|
71
|
+
// workspaces unless GEMINI_CLI_TRUST_WORKSPACE is set.
|
|
72
|
+
const args = [];
|
|
73
|
+
if (mode === "review") args.push("--output-format", "json");
|
|
74
|
+
if (model) args.push("--model", model);
|
|
75
|
+
const res = await this.runCommand(resolveBin(this.cliBin), args, {
|
|
76
|
+
input: task.prompt,
|
|
77
|
+
env: this.spawnEnv,
|
|
78
|
+
onOutput: task.onOutput,
|
|
79
|
+
silenceTimeoutMs: task.silenceTimeoutMs,
|
|
80
|
+
timeout: task.timeoutMs
|
|
81
|
+
});
|
|
82
|
+
const usage = extractGeminiUsage(res.stdout) ?? extractGeminiUsage(res.stderr);
|
|
83
|
+
return { ok: res.exitCode === 0, output: res.stdout, error: res.stderr, exitCode: res.exitCode, ...usage };
|
|
84
|
+
}
|
|
85
|
+
}
|