@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,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semgrep SAST collector for `kj audit` (KJC-TSK-0366).
|
|
3
|
+
*
|
|
4
|
+
* Wraps `semgrep --config auto --json` and feeds structured static-
|
|
5
|
+
* analysis findings into the audit prompt. Equivalent to the SAST half
|
|
6
|
+
* of `snyk code` but free for OSS, no account, runs locally. Semgrep
|
|
7
|
+
* has 2000+ built-in rules across JS/TS/Python/Go/etc. covering OWASP
|
|
8
|
+
* Top 10, taint analysis, hardcoded secrets, regex misuse and language-
|
|
9
|
+
* specific anti-patterns.
|
|
10
|
+
*
|
|
11
|
+
* Same best-effort pattern as sonar-findings.js and osv-findings.js:
|
|
12
|
+
* - Missing binary or timeout returns {available: false, reason}.
|
|
13
|
+
* - 120s timeout (semgrep can be slow on large repos with --config auto).
|
|
14
|
+
* - Severity-grouped (ERROR / WARNING / INFO) and capped at
|
|
15
|
+
* MAX_SEMGREP_FINDINGS_IN_PROMPT (50) total entries with overflow
|
|
16
|
+
* markers.
|
|
17
|
+
* - The LLM is told these are deterministic and asked to fold them
|
|
18
|
+
* into the `security` dimension using the rule ID as the rule field.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { runGoverned, jobsCap } from "../utils/tool-governor.js";
|
|
22
|
+
|
|
23
|
+
const SCAN_TIMEOUT_MS = 120_000;
|
|
24
|
+
const SEVERITY_ORDER = ["ERROR", "WARNING", "INFO"];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Run semgrep on the project and return a structured result.
|
|
28
|
+
*
|
|
29
|
+
* @param {string} projectDir - absolute path to the project root
|
|
30
|
+
* @param {object} [logger]
|
|
31
|
+
* @returns {Promise<{available: boolean, reason?: string, total?: number, findings?: object[]}>}
|
|
32
|
+
*/
|
|
33
|
+
export async function collectSemgrepFindings(projectDir, logger = null) {
|
|
34
|
+
// KJC-BUG-0122: a full `--config auto` scan must NEVER fire from the unit
|
|
35
|
+
// suite — on machines with the complete stack installed (the product
|
|
36
|
+
// default since v4.1.2) parallel vitest workers each launched a real
|
|
37
|
+
// whole-repo scan: load average 50, orphaned semgrep-core processes.
|
|
38
|
+
// E2E tests that genuinely want the real scan opt in explicitly.
|
|
39
|
+
if (process.env.VITEST && process.env.KJ_ALLOW_REAL_SCANS !== "1") {
|
|
40
|
+
return { available: false, reason: "real scans disabled under the test runner (set KJ_ALLOW_REAL_SCANS=1 to opt in)" };
|
|
41
|
+
}
|
|
42
|
+
let stdout;
|
|
43
|
+
try {
|
|
44
|
+
// KJC-TSK-0668: governed run — machine-wide single-flight, --jobs capped
|
|
45
|
+
// at half the CPUs (semgrep grabs 14 by default), nice +10, and a timeout
|
|
46
|
+
// that kills the whole process tree (no more orphaned semgrep-core).
|
|
47
|
+
const { stdout: out } = await runGoverned(
|
|
48
|
+
"semgrep",
|
|
49
|
+
["--config", "auto", "--json", "--quiet", `--jobs=${jobsCap()}`, `--timeout=${Math.floor(SCAN_TIMEOUT_MS / 1000)}`, projectDir || "."],
|
|
50
|
+
{ cwd: projectDir, timeout: SCAN_TIMEOUT_MS + 5000, maxBuffer: 32 * 1024 * 1024 }
|
|
51
|
+
);
|
|
52
|
+
stdout = out;
|
|
53
|
+
} catch (err) {
|
|
54
|
+
if (err.code === "ENOENT") {
|
|
55
|
+
if (logger?.warn) logger.warn("semgrep not found in PATH — install via 'pipx install semgrep' or 'brew install semgrep'");
|
|
56
|
+
return { available: false, reason: "semgrep not installed" };
|
|
57
|
+
}
|
|
58
|
+
if (err.killed) {
|
|
59
|
+
if (logger?.warn) logger.warn(`semgrep timed out after ${SCAN_TIMEOUT_MS}ms`);
|
|
60
|
+
return { available: false, reason: `semgrep timed out after ${SCAN_TIMEOUT_MS}ms` };
|
|
61
|
+
}
|
|
62
|
+
// semgrep exits 1 when it finds blocking rules (ERROR severity), but
|
|
63
|
+
// still emits a valid JSON report on stdout. Rescue that case.
|
|
64
|
+
if (typeof err.stdout === "string" && err.stdout.trim().length > 0) {
|
|
65
|
+
stdout = err.stdout;
|
|
66
|
+
} else {
|
|
67
|
+
if (logger?.warn) logger.warn(`semgrep failed: ${err.message}`);
|
|
68
|
+
return { available: false, reason: `semgrep failed: ${err.message}` };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let parsed;
|
|
73
|
+
try {
|
|
74
|
+
parsed = JSON.parse(stdout);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
if (logger?.warn) logger.warn(`semgrep output not parseable: ${err.message}`);
|
|
77
|
+
return { available: false, reason: "semgrep output not parseable as JSON" };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return { available: true, ...parseSemgrepOutput(parsed, projectDir) };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function relativizeSemgrepPath(p, projectDir) {
|
|
84
|
+
return p.startsWith(projectDir + "/") ? p.slice(projectDir.length + 1) : p;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Convert semgrep's JSON shape into the flat findings list the prompt
|
|
89
|
+
* builder consumes. Semgrep emits results[] with rule id, severity,
|
|
90
|
+
* path, start/end line, message, fix hints.
|
|
91
|
+
*
|
|
92
|
+
* @param {object} raw - parsed semgrep --json output
|
|
93
|
+
* @param {string} [projectDir] - used to make paths repo-relative
|
|
94
|
+
* @returns {{total: number, findings: object[]}}
|
|
95
|
+
*/
|
|
96
|
+
export function parseSemgrepOutput(raw, projectDir = "") {
|
|
97
|
+
const results = Array.isArray(raw?.results) ? raw.results : [];
|
|
98
|
+
const findings = [];
|
|
99
|
+
for (const r of results) {
|
|
100
|
+
const file = r.path && projectDir
|
|
101
|
+
? relativizeSemgrepPath(r.path, projectDir)
|
|
102
|
+
: r.path || "";
|
|
103
|
+
findings.push({
|
|
104
|
+
rule: r.check_id || "(unknown)",
|
|
105
|
+
severity: String(r.extra?.severity || "INFO").toUpperCase(),
|
|
106
|
+
file,
|
|
107
|
+
line: r.start?.line ?? 0,
|
|
108
|
+
endLine: r.end?.line ?? 0,
|
|
109
|
+
message: r.extra?.message || "",
|
|
110
|
+
fix: r.extra?.fix || r.extra?.fix_regex || null,
|
|
111
|
+
cwe: r.extra?.metadata?.cwe || null,
|
|
112
|
+
owasp: r.extra?.metadata?.owasp || null,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return { total: findings.length, findings };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Group findings by severity in canonical order (ERROR → WARNING → INFO).
|
|
120
|
+
*/
|
|
121
|
+
export function groupFindingsBySeverity(findings) {
|
|
122
|
+
const groups = Object.fromEntries(SEVERITY_ORDER.map(s => [s, []]));
|
|
123
|
+
for (const f of findings || []) {
|
|
124
|
+
const sev = (f.severity || "INFO").toUpperCase();
|
|
125
|
+
if (groups[sev]) groups[sev].push(f);
|
|
126
|
+
else groups.INFO.push(f);
|
|
127
|
+
}
|
|
128
|
+
return groups;
|
|
129
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sonar findings collector for `kj audit` (KJC-TSK-0361).
|
|
3
|
+
*
|
|
4
|
+
* The audit pipeline already pays for SonarQube to exist (kj run + the
|
|
5
|
+
* SonarStage drive a full scan and gate the pipeline on its result). When
|
|
6
|
+
* the user runs `kj audit`, we don't want to pay for *another* full scan
|
|
7
|
+
* (~1-3 min) — we want to read whatever Sonar already knows. This module
|
|
8
|
+
* does exactly that: ping the configured Sonar host, and if it's
|
|
9
|
+
* reachable, fetch the current open-issue list + quality-gate status via
|
|
10
|
+
* the existing sonar/api.js helpers. Both results are best-effort: a
|
|
11
|
+
* sonar-down host or a never-scanned project quietly returns
|
|
12
|
+
* `{available: false, reason: "..."}` and the audit continues without
|
|
13
|
+
* the section.
|
|
14
|
+
*
|
|
15
|
+
* Why this matters: the LLM auditor was previously asked to find issues
|
|
16
|
+
* by reading source code, but Sonar already produces them with rule IDs
|
|
17
|
+
* (squid:S1234), severity, and line precision. Feeding those in lets the
|
|
18
|
+
* LLM cross-reference its own findings (high-confidence overlap) and
|
|
19
|
+
* focus its tokens on patterns Sonar can't see (architecture, naming,
|
|
20
|
+
* API design).
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { isSonarReachable } from "../sonar/manager.js";
|
|
24
|
+
import { getOpenIssues, getQualityGateStatus } from "../sonar/api.js";
|
|
25
|
+
import { canResolveSonarProjectKey } from "../sonar/project-key.js";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Collect deterministic Sonar findings for the audit prompt.
|
|
29
|
+
*
|
|
30
|
+
* @param {object} config - resolved Karajan config (must have config.sonarqube.host)
|
|
31
|
+
* @param {object} [logger] - optional logger for warn-level traces
|
|
32
|
+
* @returns {Promise<{available: boolean, reason?: string, issues?: object[], total?: number, qualityGate?: object}>}
|
|
33
|
+
*/
|
|
34
|
+
export async function collectSonarFindings(config, logger = null) {
|
|
35
|
+
const sonarConfig = config?.sonarqube || {};
|
|
36
|
+
const host = sonarConfig.host || "http://localhost:9000";
|
|
37
|
+
const healthcheckSeconds = sonarConfig.timeouts?.healthcheckSeconds ?? 5;
|
|
38
|
+
|
|
39
|
+
// Ping first — avoids surfacing a 30-second timeout into audit runtime
|
|
40
|
+
// when Docker is simply down or the user opted out of Sonar.
|
|
41
|
+
const reachable = await isSonarReachable(host, healthcheckSeconds).catch(() => false);
|
|
42
|
+
if (!reachable) {
|
|
43
|
+
if (logger?.warn) logger.warn(`sonar audit input skipped: host ${host} not reachable`);
|
|
44
|
+
return { available: false, reason: `sonar host ${host} not reachable` };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// No git remote and no explicit project_key → silent skip. This is the
|
|
48
|
+
// typical `kj run` in a fresh /tmp/... folder. Reporting it as a
|
|
49
|
+
// warning treats it like an error (which it isn't — sonar simply
|
|
50
|
+
// doesn't apply to a remoteless repo).
|
|
51
|
+
if (!(await canResolveSonarProjectKey(config))) {
|
|
52
|
+
return {
|
|
53
|
+
available: false,
|
|
54
|
+
reason: "not applicable: no git remote and no sonarqube.project_key configured",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let issuesResult;
|
|
59
|
+
try {
|
|
60
|
+
issuesResult = await getOpenIssues(config);
|
|
61
|
+
} catch (err) {
|
|
62
|
+
if (logger?.warn) logger.warn(`sonar audit input: getOpenIssues failed: ${err?.message || err}`);
|
|
63
|
+
return { available: false, reason: `getOpenIssues failed: ${err?.message || err}` };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let qualityGate = null;
|
|
67
|
+
try {
|
|
68
|
+
const qg = await getQualityGateStatus(config);
|
|
69
|
+
if (qg?.ok) qualityGate = { status: qg.status, conditions: qg.conditions };
|
|
70
|
+
} catch { /* gate is optional context */ }
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
available: true,
|
|
74
|
+
issues: issuesResult.issues || [],
|
|
75
|
+
total: issuesResult.total || 0,
|
|
76
|
+
qualityGate,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Group Sonar issues by severity, preserving Sonar's canonical ordering.
|
|
82
|
+
* Used by the prompt builder to render the findings section.
|
|
83
|
+
*/
|
|
84
|
+
export function groupIssuesBySeverity(issues) {
|
|
85
|
+
const order = ["BLOCKER", "CRITICAL", "MAJOR", "MINOR", "INFO"];
|
|
86
|
+
const groups = Object.fromEntries(order.map(s => [s, []]));
|
|
87
|
+
for (const issue of issues || []) {
|
|
88
|
+
const sev = issue.severity || "INFO";
|
|
89
|
+
if (groups[sev]) groups[sev].push(issue);
|
|
90
|
+
else groups.INFO.push(issue);
|
|
91
|
+
}
|
|
92
|
+
return groups;
|
|
93
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebPerf input collector for `kj audit` (KJC-TSK-0360).
|
|
3
|
+
*
|
|
4
|
+
* Two modes:
|
|
5
|
+
* - "static-hints": no live measurement available, but the project is
|
|
6
|
+
* frontend so the LLM should look for frontend-perf patterns in the
|
|
7
|
+
* source (bundle bloat, render-blocking, image opt, lazy loading).
|
|
8
|
+
* This is the common path because `kj audit` runs against a code
|
|
9
|
+
* tree, not a live preview server.
|
|
10
|
+
* - "cwv-result": a Core Web Vitals measurement was supplied. Three
|
|
11
|
+
* possible sources, in priority order:
|
|
12
|
+
* 1. `config.webperf.lastResult` — inline {metrics, thresholds, ...}
|
|
13
|
+
* (rare; legacy / programmatic injection)
|
|
14
|
+
* 2. `config.webperf.lastResultPath` — file path written by
|
|
15
|
+
* `kj webperf` (KJC-TSK-0149). This is the standard wiring.
|
|
16
|
+
* 3. `~/.karajan/webperf/<projectSlug>/last.json` — fallback
|
|
17
|
+
* discovery when the user ran `kj webperf` but didn't patch
|
|
18
|
+
* their kj.config.yml.
|
|
19
|
+
* evaluateCwv from src/webperf/cwv-gate.js produces a structured
|
|
20
|
+
* verdict the prompt renders verbatim.
|
|
21
|
+
*
|
|
22
|
+
* The "live measurement" path is intentionally NOT wired here: the
|
|
23
|
+
* audit's LLM sub-agent is forbidden from MCP tool use (SUBAGENT_PREAMBLE
|
|
24
|
+
* in src/prompts/audit.js), and spawning Chrome / launching a preview
|
|
25
|
+
* server during a read-only audit is wrong layer. `kj webperf` does
|
|
26
|
+
* the measurement; `kj audit` consumes the persisted result.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import fs from "node:fs";
|
|
30
|
+
import path from "node:path";
|
|
31
|
+
import { evaluateCwv, CWV_THRESHOLDS } from "../webperf/cwv-gate.js";
|
|
32
|
+
import { getWebperfDir } from "../utils/paths.js";
|
|
33
|
+
|
|
34
|
+
// Delegated to src/utils/paths.js (KJC-TSK-0420) — respects
|
|
35
|
+
// KARAJAN_HOME / KJ_HOME (with deprecation warning) / VITEST tmp.
|
|
36
|
+
function webperfHome() { return getWebperfDir(); }
|
|
37
|
+
|
|
38
|
+
function projectSlug(projectDir) {
|
|
39
|
+
return path.basename(projectDir || process.cwd()).replaceAll(/[^a-zA-Z0-9_-]/g, "_");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Try the three discovery paths in priority order. Returns the parsed
|
|
44
|
+
* scan object (shape produced by src/webperf/scanner.js) or null.
|
|
45
|
+
*/
|
|
46
|
+
function loadPersistedScan(webperfConfig, projectDir) {
|
|
47
|
+
// 1. Inline result on the config (back-compat)
|
|
48
|
+
if (webperfConfig.lastResult && typeof webperfConfig.lastResult === "object" && webperfConfig.lastResult.metrics) {
|
|
49
|
+
return webperfConfig.lastResult;
|
|
50
|
+
}
|
|
51
|
+
// 2. Explicit file path on the config
|
|
52
|
+
const candidates = [];
|
|
53
|
+
if (webperfConfig.lastResultPath) candidates.push(webperfConfig.lastResultPath);
|
|
54
|
+
// 3. Fallback: ~/.karajan/webperf/<slug>/last.json
|
|
55
|
+
if (projectDir) {
|
|
56
|
+
candidates.push(path.join(webperfHome(), projectSlug(projectDir), "last.json"));
|
|
57
|
+
}
|
|
58
|
+
for (const candidate of candidates) {
|
|
59
|
+
try {
|
|
60
|
+
const raw = fs.readFileSync(candidate, "utf8");
|
|
61
|
+
const parsed = JSON.parse(raw);
|
|
62
|
+
if (parsed && typeof parsed === "object" && parsed.metrics) return parsed;
|
|
63
|
+
} catch { /* missing file or unparseable — try next */ }
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Decide what (if anything) to feed the audit prompt about web perf.
|
|
70
|
+
*
|
|
71
|
+
* @param {object|null} stack - detectProjectStack output (null when unknown)
|
|
72
|
+
* @param {object} [config] - resolved Karajan config (config.webperf may carry a stale CWV result)
|
|
73
|
+
* @returns {{available: boolean, mode?: 'static-hints'|'cwv-result', cwv?: object, reason?: string}}
|
|
74
|
+
*/
|
|
75
|
+
export function collectWebPerfInput(stack, config = {}) {
|
|
76
|
+
const webperfConfig = config?.webperf || {};
|
|
77
|
+
const projectDir = config?.projectDir;
|
|
78
|
+
const persisted = loadPersistedScan(webperfConfig, projectDir);
|
|
79
|
+
|
|
80
|
+
if (persisted) {
|
|
81
|
+
const verdict = persisted.cwv || evaluateCwv(persisted.metrics, persisted.thresholds || webperfConfig.thresholds);
|
|
82
|
+
return {
|
|
83
|
+
available: true,
|
|
84
|
+
mode: "cwv-result",
|
|
85
|
+
url: persisted.url || webperfConfig.url || null,
|
|
86
|
+
capturedAt: persisted.capturedAt || null,
|
|
87
|
+
cwv: verdict,
|
|
88
|
+
thresholds: persisted.thresholds || webperfConfig.thresholds || CWV_THRESHOLDS,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// No live measurement → static hints only when the project actually has
|
|
93
|
+
// a frontend layer to audit. Backend-only projects get nothing.
|
|
94
|
+
const isFrontendish = !stack || stack.isFrontend === true || stack.isFullstack === true;
|
|
95
|
+
if (!isFrontendish) {
|
|
96
|
+
return { available: false, reason: "project is backend-only — no frontend-perf hints to give" };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return { available: true, mode: "static-hints" };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Render a CWV verdict (from collectWebPerfInput's cwv-result mode) as
|
|
104
|
+
* concise prompt-friendly markdown. Kept independent from the prompt
|
|
105
|
+
* builder so it stays unit-testable in isolation.
|
|
106
|
+
*/
|
|
107
|
+
export function formatCwvVerdict(input) {
|
|
108
|
+
if (!input || input.mode !== "cwv-result" || !input.cwv) return null;
|
|
109
|
+
const { cwv, url, capturedAt } = input;
|
|
110
|
+
const lines = [];
|
|
111
|
+
if (url) lines.push(`- Target URL: ${url}`);
|
|
112
|
+
if (capturedAt) lines.push(`- Captured: ${capturedAt}`);
|
|
113
|
+
lines.push(`- Verdict: ${cwv.pass ? "PASS" : "FAIL"}`);
|
|
114
|
+
if (Object.keys(cwv.scores || {}).length) {
|
|
115
|
+
lines.push("- Scores:");
|
|
116
|
+
for (const [metric, score] of Object.entries(cwv.scores)) {
|
|
117
|
+
lines.push(` - ${metric.toUpperCase()}: ${score.value} (${score.rating})`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (cwv.blocking?.length) {
|
|
121
|
+
lines.push("- Blocking metrics (above poor threshold):");
|
|
122
|
+
for (const b of cwv.blocking) {
|
|
123
|
+
lines.push(` - ${b.metric.toUpperCase()} ${b.value} > ${b.threshold}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (cwv.advisory?.length) {
|
|
127
|
+
lines.push("- Advisory metrics (between good and poor):");
|
|
128
|
+
for (const a of cwv.advisory) {
|
|
129
|
+
lines.push(` - ${a.metric.toUpperCase()} ${a.value} > good=${a.threshold}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return lines.join("\n");
|
|
133
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArbiterRole — the autonomous decision authority that resolves agent conflicts
|
|
3
|
+
* by picking the least-bad verdict (KJC-TSK-0573, épica KJC-PCS-0062).
|
|
4
|
+
*
|
|
5
|
+
* Extends AgentRole (so it inherits Brain recovery / standby) following the
|
|
6
|
+
* TriageRole pattern: a cheap decisor model emits a closed-set verdict, and any
|
|
7
|
+
* parse failure degrades to BLOCK_AND_CONTINUE — the pipeline never crashes and
|
|
8
|
+
* never blocks on the Arbiter.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { AgentRole } from "../roles/agent-role.js";
|
|
12
|
+
import { extractFirstJson } from "../utils/json-extract.js";
|
|
13
|
+
import { buildArbiterPrompt, VERDICTS } from "../prompts/arbiter.js";
|
|
14
|
+
|
|
15
|
+
const CONSERVATIVE = "BLOCK_AND_CONTINUE";
|
|
16
|
+
const VERDICT_SET = new Set(VERDICTS);
|
|
17
|
+
|
|
18
|
+
function clampConfidence(value) {
|
|
19
|
+
const n = Number(value);
|
|
20
|
+
return Number.isFinite(n) ? Math.min(1, Math.max(0, n)) : 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class ArbiterRole extends AgentRole {
|
|
24
|
+
constructor(opts) {
|
|
25
|
+
super({ ...opts, name: "arbiter" });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async buildPrompt(input) {
|
|
29
|
+
return { prompt: buildArbiterPrompt(input) };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
parseOutput(raw) {
|
|
33
|
+
return extractFirstJson(raw);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
buildSuccessResult(parsed, provider) {
|
|
37
|
+
const verdict = VERDICT_SET.has(parsed?.verdict) ? parsed.verdict : CONSERVATIVE;
|
|
38
|
+
return {
|
|
39
|
+
verdict,
|
|
40
|
+
confidence: clampConfidence(parsed?.confidence),
|
|
41
|
+
rationale: String(parsed?.rationale || "").trim() || "no rationale",
|
|
42
|
+
defect: parsed?.defect ? String(parsed.defect).trim() : null,
|
|
43
|
+
directive: parsed?.directive ? String(parsed.directive).trim() : null,
|
|
44
|
+
provider,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
buildSummary(parsed) {
|
|
49
|
+
return `Arbiter: ${VERDICT_SET.has(parsed?.verdict) ? parsed.verdict : CONSERVATIVE}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Never crash on garbage: degrade to the conservative verdict so the pipeline
|
|
53
|
+
// moves on instead of aborting (ok:true keeps the run alive).
|
|
54
|
+
handleParseNull(agentResult, provider) {
|
|
55
|
+
return this.degraded(agentResult, provider, "unstructured output");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
handleParseError(err, agentResult, provider) {
|
|
59
|
+
return this.degraded(agentResult, provider, err?.message || "parse error");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
degraded(agentResult, provider, why) {
|
|
63
|
+
this.logger?.warn?.(`[arbiter] ${why} → ${CONSERVATIVE} (conservative)`);
|
|
64
|
+
return {
|
|
65
|
+
ok: true,
|
|
66
|
+
result: { verdict: CONSERVATIVE, confidence: 0, rationale: `degraded: ${why}`, defect: null, directive: null, provider },
|
|
67
|
+
summary: `Arbiter: ${CONSERVATIVE} (degraded)`,
|
|
68
|
+
usage: agentResult.usage,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createArbiter — adapt ArbiterRole to the function the decision-resolver
|
|
3
|
+
* injects (KJC-TSK-0573). The resolver calls `arbiter(req)` and expects
|
|
4
|
+
* `{ choice, rationale, ... }`; here `choice` is the Arbiter's verdict, which
|
|
5
|
+
* the resolver then validates against the caller's options (AUTO-A).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { ArbiterRole } from "./arbiter-role.js";
|
|
9
|
+
|
|
10
|
+
export function createArbiter({ config = {}, logger = console, createAgentFn = null } = {}) {
|
|
11
|
+
return async function arbiter(req) {
|
|
12
|
+
const role = new ArbiterRole({ config, logger, createAgentFn });
|
|
13
|
+
const { result } = await role.execute(req);
|
|
14
|
+
return {
|
|
15
|
+
choice: result.verdict,
|
|
16
|
+
rationale: result.rationale,
|
|
17
|
+
confidence: result.confidence,
|
|
18
|
+
defect: result.defect,
|
|
19
|
+
directive: result.directive,
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* buildDecider — wire the active autonomy level to a ready decision resolver
|
|
3
|
+
* (KJC-TSK-0572, design in docs/specs/autonomous-delivery.md §5).
|
|
4
|
+
*
|
|
5
|
+
* interactive → the human `ask`; assisted/autonomous → the Arbiter (AUTO-B).
|
|
6
|
+
* Consumers (spec-review, reviewer, checkpoint…) call `resolve(...)` instead of
|
|
7
|
+
* prompting directly, so the autonomy level decides who answers in one place.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createDecisionResolver } from "./decision-resolver.js";
|
|
11
|
+
import { resolveAutonomy } from "./policy.js";
|
|
12
|
+
import { createArbiter } from "./arbiter.js";
|
|
13
|
+
|
|
14
|
+
export function buildDecider({ config = {}, flags = {}, ask = null, env = process.env, logger = console, createAgentFn = null } = {}) {
|
|
15
|
+
const autonomy = resolveAutonomy({ config, flags, env });
|
|
16
|
+
const arbiter = autonomy === "interactive" ? null : createArbiter({ config, logger, createAgentFn });
|
|
17
|
+
return { autonomy, resolve: createDecisionResolver({ autonomy, ask, arbiter, logger }) };
|
|
18
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* autonomy/decision-resolver — the single choke point for every pipeline
|
|
3
|
+
* question (KJC-TSK-0572, design in docs/specs/autonomous-delivery.md §5).
|
|
4
|
+
*
|
|
5
|
+
* Today the "ask a human" logic is scattered (spec-review, checkpoint,
|
|
6
|
+
* reviewer, board-bridge). This unifies it: callers ask `resolveDecision`
|
|
7
|
+
* instead of prompting directly, and the autonomy level decides who answers —
|
|
8
|
+
* the human (`ask`) or the Arbiter (`arbiter`, injected by AUTO-B).
|
|
9
|
+
*
|
|
10
|
+
* INVARIANT: in `autonomous` mode the resolver NEVER returns a null choice and
|
|
11
|
+
* NEVER blocks — if the Arbiter can't decide it degrades to the option flagged
|
|
12
|
+
* `conservative` (or the first option). Decisions are always traced.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { RISK, usesArbiter } from "./policy.js";
|
|
16
|
+
|
|
17
|
+
/** Pick the safe fallback: the option flagged conservative, else the first. */
|
|
18
|
+
function conservativeOf(options) {
|
|
19
|
+
return options.find((o) => o.conservative) ?? options[0] ?? null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function decision(choice, rationale, source) {
|
|
23
|
+
return { choice: choice ?? null, rationale, source };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Build a resolver bound to an autonomy level and its two answer sources.
|
|
28
|
+
*
|
|
29
|
+
* @param {object} opts
|
|
30
|
+
* @param {string} opts.autonomy - interactive | assisted | autonomous
|
|
31
|
+
* @param {(question: string, context?: object) => Promise<string|null>} [opts.ask] - human prompt
|
|
32
|
+
* @param {(input: object) => Promise<{choice: string, rationale?: string}>} [opts.arbiter] - AUTO-B
|
|
33
|
+
* @returns {(req: object) => Promise<{choice, rationale, source}>}
|
|
34
|
+
*/
|
|
35
|
+
export function createDecisionResolver({ autonomy = "interactive", ask = null, arbiter = null, logger = console } = {}) {
|
|
36
|
+
return async function resolveDecision({ question, options = [], context = null, risk = RISK.LOW } = {}) {
|
|
37
|
+
const fallback = conservativeOf(options);
|
|
38
|
+
|
|
39
|
+
// Human path: interactive, or assisted with a high-risk decision.
|
|
40
|
+
if (!usesArbiter(autonomy, risk)) {
|
|
41
|
+
const answer = ask ? await ask(question, context) : null;
|
|
42
|
+
return decision(answer, "human", "human");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Arbiter path: must always yield a valid choice, never block.
|
|
46
|
+
if (!arbiter) return decision(fallback?.value, "no arbiter available → conservative", "fallback");
|
|
47
|
+
let verdict;
|
|
48
|
+
try {
|
|
49
|
+
verdict = await arbiter({ question, options, context, risk });
|
|
50
|
+
} catch (err) {
|
|
51
|
+
logger.warn?.(`decision-resolver: arbiter failed (${err.message}) → conservative`);
|
|
52
|
+
return decision(fallback?.value, "arbiter error → conservative", "fallback");
|
|
53
|
+
}
|
|
54
|
+
const valid = verdict && options.some((o) => o.value === verdict.choice);
|
|
55
|
+
if (!valid) return decision(fallback?.value, "arbiter gave no valid choice → conservative", "fallback");
|
|
56
|
+
return decision(verdict.choice, verdict.rationale ?? "arbiter", "arbiter");
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* outcome-report — the auditable "meets the ask, with known defects" report
|
|
3
|
+
* for an autonomous run (KJC-TSK-0577, design in docs/specs/autonomous-delivery.md §10).
|
|
4
|
+
*
|
|
5
|
+
* Pure: builds a serializable outcome from the run results + Arbiter decisions,
|
|
6
|
+
* and renders it as lines (or the structure is emitted as --json). The point is
|
|
7
|
+
* that an imperfect-but-delivered result is explicit and auditable: which HUs
|
|
8
|
+
* met the ask, what the Arbiter decided, and what debt remains.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {object} opts
|
|
13
|
+
* @param {string} [opts.planRef]
|
|
14
|
+
* @param {boolean} opts.delivered - the run met the minimum ask
|
|
15
|
+
* @param {Array<{id, title?, met: boolean, note?}>} [opts.hus]
|
|
16
|
+
* @param {Array<{verdict, rationale, hu?, defect?}>} [opts.decisions] - Arbiter log
|
|
17
|
+
*/
|
|
18
|
+
export function buildOutcome({ planRef = null, delivered = false, hus = [], decisions = [] } = {}) {
|
|
19
|
+
const met = hus.filter((h) => h.met).length;
|
|
20
|
+
const residualDefects = [
|
|
21
|
+
...hus.filter((h) => !h.met).map((h) => ({ hu: h.id, detail: h.note || "acceptance criteria not fully met" })),
|
|
22
|
+
...decisions.filter((d) => d.defect).map((d) => ({ hu: d.hu ?? null, detail: d.defect })),
|
|
23
|
+
];
|
|
24
|
+
return {
|
|
25
|
+
verdict: delivered ? "DELIVERED" : "INCOMPLETE",
|
|
26
|
+
planRef,
|
|
27
|
+
hus: { total: hus.length, met, unmet: hus.length - met },
|
|
28
|
+
decisions,
|
|
29
|
+
residualDefects,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Render an outcome (from buildOutcome) as human-readable lines. */
|
|
34
|
+
export function formatOutcomeReport(outcome) {
|
|
35
|
+
const lines = [
|
|
36
|
+
`kj autorun — outcome: ${outcome.verdict}`,
|
|
37
|
+
` plan: ${outcome.planRef ?? "?"}`,
|
|
38
|
+
` HUs: ${outcome.hus.met}/${outcome.hus.total} met the ask`,
|
|
39
|
+
];
|
|
40
|
+
if (outcome.decisions?.length) {
|
|
41
|
+
lines.push(" Decisions (Arbiter — least-bad calls):");
|
|
42
|
+
for (const d of outcome.decisions) {
|
|
43
|
+
lines.push(` • ${d.verdict}${d.hu ? ` [${d.hu}]` : ""} — ${d.rationale}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (outcome.residualDefects?.length) {
|
|
47
|
+
lines.push(" Known defects (delivered with debt):");
|
|
48
|
+
for (const def of outcome.residualDefects) {
|
|
49
|
+
lines.push(` • ${def.hu ? `[${def.hu}] ` : ""}${def.detail}`);
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
lines.push(" No residual defects recorded.");
|
|
53
|
+
}
|
|
54
|
+
return lines;
|
|
55
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* autonomy/policy — autonomy levels for Autonomous Delivery (KJC-TSK-0572,
|
|
3
|
+
* épica KJC-PCS-0062, design in docs/specs/autonomous-delivery.md §4).
|
|
4
|
+
*
|
|
5
|
+
* One configurable axis decides who resolves a pipeline question:
|
|
6
|
+
* interactive — the human (current behaviour, default)
|
|
7
|
+
* assisted — the human only for high-risk decisions, the Arbiter otherwise
|
|
8
|
+
* autonomous — always the Arbiter; never asks a human, never blocks
|
|
9
|
+
*
|
|
10
|
+
* Precedence (most specific wins): CLI flags → env → config → default.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export const AUTONOMY_LEVELS = Object.freeze(["interactive", "assisted", "autonomous"]);
|
|
14
|
+
|
|
15
|
+
/** Risk of a decision — drives whether `assisted` escalates to the human. */
|
|
16
|
+
export const RISK = Object.freeze({ LOW: "low", HIGH: "high" });
|
|
17
|
+
|
|
18
|
+
const DEFAULT_LEVEL = "interactive";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the active autonomy level. `--autonomous` is sugar for the
|
|
22
|
+
* `autonomous` level; `--autonomy <level>` sets it explicitly. An unknown
|
|
23
|
+
* value degrades to the safe default rather than throwing.
|
|
24
|
+
*/
|
|
25
|
+
export function resolveAutonomy({ config = {}, flags = {}, env = {} } = {}) {
|
|
26
|
+
const raw =
|
|
27
|
+
flags.autonomy ??
|
|
28
|
+
(flags.autonomous ? "autonomous" : null) ??
|
|
29
|
+
env.KJ_AUTONOMY ??
|
|
30
|
+
config.autonomy ??
|
|
31
|
+
DEFAULT_LEVEL;
|
|
32
|
+
const level = String(raw).toLowerCase();
|
|
33
|
+
return AUTONOMY_LEVELS.includes(level) ? level : DEFAULT_LEVEL;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** True when the level should route decisions to the Arbiter for `risk`. */
|
|
37
|
+
export function usesArbiter(level, risk = RISK.LOW) {
|
|
38
|
+
if (level === "autonomous") return true;
|
|
39
|
+
if (level === "assisted") return risk !== RISK.HIGH;
|
|
40
|
+
return false; // interactive
|
|
41
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createAutonomyAskQuestion — make the stages' question function non-blocking in
|
|
3
|
+
* autonomous mode (KJC-TSK-0576, AUTO-E, design in docs/specs/autonomous-delivery.md §9).
|
|
4
|
+
*
|
|
5
|
+
* In `autonomous` no stage asks a human or routes to the HU Board: the wrapper
|
|
6
|
+
* answers in-process with the least-bad "keep going" choice, so the run never
|
|
7
|
+
* pauses or aborts mid-loop. interactive and assisted keep the human prompts
|
|
8
|
+
* (baseAsk) unchanged.
|
|
9
|
+
*
|
|
10
|
+
* The answer is chosen per stage so the EXISTING branches proceed (verified
|
|
11
|
+
* against the stage code — no stage logic is touched):
|
|
12
|
+
* - architect clarification: null → the stage continues best-effort (a truthy
|
|
13
|
+
* answer would force a wasteful architect re-run).
|
|
14
|
+
* - everything else (Solomon escalation, checkpoint, …): "continue" → parsed
|
|
15
|
+
* as free-text guidance → the stage continues instead of pausing/aborting.
|
|
16
|
+
*/
|
|
17
|
+
export function createAutonomyAskQuestion({ baseAsk = null, autonomy = "interactive", logger = console } = {}) {
|
|
18
|
+
if (autonomy !== "autonomous") return baseAsk;
|
|
19
|
+
const auto = async (_question, context = {}) => {
|
|
20
|
+
const stage = context?.stage ?? null;
|
|
21
|
+
logger?.info?.(`[autonomy] ${stage ? `${stage}: ` : ""}auto-continue (no human in the loop)`);
|
|
22
|
+
return stage === "architect" ? null : "continue";
|
|
23
|
+
};
|
|
24
|
+
auto.interactive = false;
|
|
25
|
+
// Marker so the orchestrator can tell an autonomous (unattended) run from an
|
|
26
|
+
// interactive one even though both carry a truthy askQuestion — the wall-clock
|
|
27
|
+
// backstop relies on it (KJC-TSK-0575, AUTO-D).
|
|
28
|
+
auto.unattended = true;
|
|
29
|
+
return auto;
|
|
30
|
+
}
|