@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,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git automation helpers for the pipeline.
|
|
3
|
+
* Extracted from orchestrator.js for testability and reuse.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { addCheckpoint } from "../session/store.js";
|
|
7
|
+
import { stampStagedVerdict } from "../review/verdict-store.js";
|
|
8
|
+
import {
|
|
9
|
+
ensureGitRepo,
|
|
10
|
+
currentBranch,
|
|
11
|
+
hasCommits,
|
|
12
|
+
fetchBase,
|
|
13
|
+
syncBaseBranch,
|
|
14
|
+
ensureBranchUpToDateWithBase,
|
|
15
|
+
createBranch,
|
|
16
|
+
buildBranchName,
|
|
17
|
+
commitAll,
|
|
18
|
+
pushBranch,
|
|
19
|
+
createPullRequest,
|
|
20
|
+
listPendingPaths,
|
|
21
|
+
hasRemote
|
|
22
|
+
} from "../utils/git.js";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Karajan writes / updates several internal directories during a pipeline
|
|
26
|
+
* run (the workspace scaffold + the session journal). When they're the
|
|
27
|
+
* ONLY thing pending at finalization, the post-loop commit shouldn't
|
|
28
|
+
* inherit the user task's `feat:` message — it should be a clear
|
|
29
|
+
* `chore: scaffold karajan workspace` so `git log --oneline` reads:
|
|
30
|
+
*
|
|
31
|
+
* 2d770aa docs: add JSDoc comment to index.js
|
|
32
|
+
* abc1234 chore: scaffold karajan workspace
|
|
33
|
+
* 8a95ef0 initial
|
|
34
|
+
*
|
|
35
|
+
* A path counts as scaffolding when it lives at one of these prefixes
|
|
36
|
+
* OR is the project's root `.gitignore` (we extend it to include
|
|
37
|
+
* stack-specific entries during triage).
|
|
38
|
+
*/
|
|
39
|
+
const KARAJAN_SCAFFOLD_PREFIXES = [
|
|
40
|
+
".karajan/",
|
|
41
|
+
".reviews/",
|
|
42
|
+
".kj/",
|
|
43
|
+
".agent/",
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
function isKarajanScaffoldPath(p) {
|
|
47
|
+
if (p === ".gitignore") return true;
|
|
48
|
+
return KARAJAN_SCAFFOLD_PREFIXES.some((prefix) => p.startsWith(prefix));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {string[]} paths
|
|
53
|
+
* @returns {boolean} true when every pending path is a Karajan internal
|
|
54
|
+
*/
|
|
55
|
+
export function pendingPathsAreOnlyKarajanScaffold(paths) {
|
|
56
|
+
if (!Array.isArray(paths) || paths.length === 0) return false;
|
|
57
|
+
return paths.every(isKarajanScaffoldPath);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Map the taskType (as classified by triage) to the matching
|
|
62
|
+
* Conventional Commits prefix. When triage hasn't run or the type is
|
|
63
|
+
* unknown we default to `feat:` (the pre-fix behaviour).
|
|
64
|
+
*/
|
|
65
|
+
const TASK_TYPE_TO_PREFIX = {
|
|
66
|
+
doc: "docs",
|
|
67
|
+
"add-tests": "test",
|
|
68
|
+
refactor: "refactor",
|
|
69
|
+
infra: "chore",
|
|
70
|
+
sw: "feat",
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
function prefixForTaskType(taskType) {
|
|
74
|
+
return TASK_TYPE_TO_PREFIX[taskType] || "feat";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function commitMessageFromTask(task, taskType = null) {
|
|
78
|
+
const clean = String(task || "")
|
|
79
|
+
.replaceAll(/\s+/g, " ")
|
|
80
|
+
.trim();
|
|
81
|
+
const prefix = prefixForTaskType(taskType);
|
|
82
|
+
return `${prefix}: ${clean.slice(0, 72) || "karajan update"}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Build the message used by `finalizeGitAutomation` for the post-loop
|
|
87
|
+
* commit. Two paths:
|
|
88
|
+
* - When the only pending changes are Karajan scaffold files
|
|
89
|
+
* (.gitignore, .karajan/, .reviews/, ...), use a fixed
|
|
90
|
+
* `chore: scaffold karajan workspace` message. This avoids the
|
|
91
|
+
* "duplicate feat:" git log seen when triage extends .gitignore
|
|
92
|
+
* for a freshly detected stack and the coder already committed
|
|
93
|
+
* the user-visible change.
|
|
94
|
+
* - Otherwise, fall back to `commitMessageFromTask(task, taskType)`,
|
|
95
|
+
* respecting the type for the prefix.
|
|
96
|
+
*/
|
|
97
|
+
export function buildFinalizeCommitMessage({ task, taskType, pendingPaths }) {
|
|
98
|
+
if (pendingPathsAreOnlyKarajanScaffold(pendingPaths)) {
|
|
99
|
+
return "chore: scaffold karajan workspace";
|
|
100
|
+
}
|
|
101
|
+
return commitMessageFromTask(task, taskType);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function prepareGitAutomation({ config, task, logger, session }) {
|
|
105
|
+
const enabled = config.git.auto_commit || config.git.auto_push || config.git.auto_pr;
|
|
106
|
+
if (!enabled) return { enabled: false };
|
|
107
|
+
|
|
108
|
+
if (!(await ensureGitRepo())) {
|
|
109
|
+
throw new Error("Git automation requested but current directory is not a git repository");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const baseBranch = config.base_branch;
|
|
113
|
+
const autoRebase = config.git.auto_rebase !== false;
|
|
114
|
+
const repoHasCommits = await hasCommits();
|
|
115
|
+
|
|
116
|
+
// New repo without commits: create branch directly (no fetch/sync possible)
|
|
117
|
+
if (!repoHasCommits) {
|
|
118
|
+
const created = buildBranchName(config.git.branch_prefix || "feat/", task);
|
|
119
|
+
// git checkout -b works even without commits (creates orphan-like branch on first commit)
|
|
120
|
+
await createBranch(created);
|
|
121
|
+
logger.info(`New repo — created working branch: ${created}`);
|
|
122
|
+
await addCheckpoint(session, { stage: "git-prep", branch: created, created: true, newRepo: true });
|
|
123
|
+
return { enabled: true, branch: created, baseBranch, autoRebase };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
await fetchBase(baseBranch).catch(() => {
|
|
127
|
+
// No remote — skip fetch (new project without push)
|
|
128
|
+
logger.info("No remote configured — skipping fetch");
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
let branch;
|
|
132
|
+
try {
|
|
133
|
+
branch = await currentBranch();
|
|
134
|
+
} catch {
|
|
135
|
+
// HEAD exists but branch detection failed — unusual, treat as main
|
|
136
|
+
branch = baseBranch;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (branch === baseBranch) {
|
|
140
|
+
await syncBaseBranch({ baseBranch, autoRebase }).catch(() => {
|
|
141
|
+
// No remote tracking — skip sync for new projects
|
|
142
|
+
});
|
|
143
|
+
const created = buildBranchName(config.git.branch_prefix || "feat/", task);
|
|
144
|
+
await createBranch(created);
|
|
145
|
+
branch = created;
|
|
146
|
+
logger.info(`Created working branch: ${branch}`);
|
|
147
|
+
await addCheckpoint(session, { stage: "git-prep", branch, created: true });
|
|
148
|
+
} else {
|
|
149
|
+
await ensureBranchUpToDateWithBase({ branch, baseBranch, autoRebase }).catch(() => {
|
|
150
|
+
// No remote tracking — skip rebase for new projects
|
|
151
|
+
logger.info("No remote tracking — skipping rebase check");
|
|
152
|
+
});
|
|
153
|
+
await addCheckpoint(session, { stage: "git-prep", branch, created: false });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return { enabled: true, branch, baseBranch, autoRebase };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function buildPrBody({ task: _task, stageResults }) {
|
|
160
|
+
const sections = ["Created by Karajan Code."];
|
|
161
|
+
|
|
162
|
+
const approach = stageResults?.planner?.approach;
|
|
163
|
+
if (approach) {
|
|
164
|
+
sections.push("", "## Approach", approach);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const steps = stageResults?.planner?.steps;
|
|
168
|
+
if (steps?.length) {
|
|
169
|
+
sections.push("", "## Steps");
|
|
170
|
+
for (let i = 0; i < steps.length; i++) {
|
|
171
|
+
sections.push(`${i + 1}. ${steps[i]}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const triageSubtasks = stageResults?.triage?.subtasks;
|
|
176
|
+
const shouldDecompose = stageResults?.triage?.shouldDecompose;
|
|
177
|
+
const pendingSubtasks = shouldDecompose && triageSubtasks?.length > 1 ? triageSubtasks.slice(1) : [];
|
|
178
|
+
if (pendingSubtasks.length > 0) {
|
|
179
|
+
sections.push("", "## Pending subtasks", "This PR addresses part of a larger task. The following subtasks were identified but not included:");
|
|
180
|
+
for (const subtask of pendingSubtasks) {
|
|
181
|
+
sections.push(`- [ ] ${subtask}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return sections.join("\n");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Create an early PR after the first coder iteration (Karajan CI flow).
|
|
190
|
+
* Commits, pushes, and creates a PR before the reviewer runs.
|
|
191
|
+
* Returns { prNumber, prUrl, commits } or null if nothing to commit.
|
|
192
|
+
*/
|
|
193
|
+
export async function earlyPrCreation({ gitCtx, task, logger, session, stageResults = null }) {
|
|
194
|
+
if (!gitCtx?.enabled) return null;
|
|
195
|
+
|
|
196
|
+
const commitMsg = commitMessageFromTask(task);
|
|
197
|
+
const commitResult = await commitAll(commitMsg);
|
|
198
|
+
if (!commitResult.committed) {
|
|
199
|
+
logger.info("earlyPrCreation: no changes to commit");
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const commits = commitResult.commit ? [commitResult.commit] : [];
|
|
204
|
+
await addCheckpoint(session, { stage: "ci-commit", committed: true });
|
|
205
|
+
|
|
206
|
+
await pushBranch(gitCtx.branch);
|
|
207
|
+
await addCheckpoint(session, { stage: "ci-push", branch: gitCtx.branch });
|
|
208
|
+
logger.info(`Pushed branch for early PR: ${gitCtx.branch}`);
|
|
209
|
+
|
|
210
|
+
const body = buildPrBody({ task, stageResults });
|
|
211
|
+
const prUrl = await createPullRequest({
|
|
212
|
+
baseBranch: gitCtx.baseBranch,
|
|
213
|
+
branch: gitCtx.branch,
|
|
214
|
+
title: commitMessageFromTask(task),
|
|
215
|
+
body
|
|
216
|
+
});
|
|
217
|
+
await addCheckpoint(session, { stage: "ci-pr", branch: gitCtx.branch, pr: prUrl });
|
|
218
|
+
logger.info(`Early PR created: ${prUrl}`);
|
|
219
|
+
|
|
220
|
+
// Extract PR number from URL (e.g. https://github.com/owner/repo/pull/42)
|
|
221
|
+
const prNumber = Number.parseInt(prUrl.split("/").pop(), 10) || null;
|
|
222
|
+
return { prNumber, prUrl, commits };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Incremental push after each coder iteration (Karajan CI flow).
|
|
227
|
+
* Commits and pushes without creating a new PR.
|
|
228
|
+
*/
|
|
229
|
+
export async function incrementalPush({ gitCtx, task, logger, session }) {
|
|
230
|
+
if (!gitCtx?.enabled) return null;
|
|
231
|
+
|
|
232
|
+
const commitMsg = commitMessageFromTask(task);
|
|
233
|
+
const commitResult = await commitAll(commitMsg);
|
|
234
|
+
if (!commitResult.committed) {
|
|
235
|
+
logger.info("incrementalPush: no changes to commit");
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const commits = commitResult.commit ? [commitResult.commit] : [];
|
|
240
|
+
await addCheckpoint(session, { stage: "ci-incremental-commit", committed: true });
|
|
241
|
+
|
|
242
|
+
await pushBranch(gitCtx.branch);
|
|
243
|
+
await addCheckpoint(session, { stage: "ci-incremental-push", branch: gitCtx.branch });
|
|
244
|
+
logger.info(`Incremental push: ${gitCtx.branch}`);
|
|
245
|
+
|
|
246
|
+
return { commits };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export async function finalizeGitAutomation({ config, gitCtx, task, logger, session, stageResults = null }) {
|
|
250
|
+
if (!gitCtx?.enabled) return { git: "disabled", commits: [] };
|
|
251
|
+
|
|
252
|
+
// Take a snapshot of pending paths BEFORE staging so we can decide
|
|
253
|
+
// whether what's about to be committed is just Karajan scaffolding
|
|
254
|
+
// (in which case we use `chore: scaffold karajan workspace`) or a
|
|
255
|
+
// real, user-visible change (in which case we honour the task's
|
|
256
|
+
// taskType for the Conventional Commits prefix).
|
|
257
|
+
const taskType = stageResults?.triage?.taskType ?? null;
|
|
258
|
+
let pendingPaths = [];
|
|
259
|
+
if (config.git.auto_commit) {
|
|
260
|
+
try {
|
|
261
|
+
pendingPaths = await listPendingPaths();
|
|
262
|
+
} catch { /* best-effort — fall back to default message */ }
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const commitMsg =
|
|
266
|
+
config.git.commit_message
|
|
267
|
+
|| buildFinalizeCommitMessage({ task, taskType, pendingPaths });
|
|
268
|
+
|
|
269
|
+
let committed = false;
|
|
270
|
+
const commits = [];
|
|
271
|
+
if (config.git.auto_commit) {
|
|
272
|
+
// ENV-F1: this path only runs after the pipeline's reviewer approved,
|
|
273
|
+
// so stamp that verdict for the staged diff — the v4 pre-commit gate
|
|
274
|
+
// (when the repo opted in) accepts the pipeline's own commit.
|
|
275
|
+
const commitResult = await commitAll(commitMsg, null, {
|
|
276
|
+
beforeCommit: () => stampStagedVerdict({
|
|
277
|
+
projectDir: config?.projectDir || process.cwd(),
|
|
278
|
+
reviewer: config?.reviewer || "pipeline-reviewer",
|
|
279
|
+
summary: `kj run session ${session?.id || ""}: reviewer approved`.trim(),
|
|
280
|
+
}),
|
|
281
|
+
});
|
|
282
|
+
committed = commitResult.committed;
|
|
283
|
+
if (commitResult.commit) {
|
|
284
|
+
commits.push(commitResult.commit);
|
|
285
|
+
}
|
|
286
|
+
await addCheckpoint(session, { stage: "git-commit", committed });
|
|
287
|
+
logger.info(committed ? "Committed changes" : "No changes to commit");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// KJC-BUG-0112: without an `origin` remote (the quickstart scenario:
|
|
291
|
+
// fresh folder + git init) push/PR automation has nothing to talk to —
|
|
292
|
+
// fetchBase used to throw here and escalate to Solomon on EVERY
|
|
293
|
+
// iteration, burning the whole budget on an expected condition.
|
|
294
|
+
const remoteAvailable = (config.git.auto_push || config.git.auto_pr)
|
|
295
|
+
? await hasRemote()
|
|
296
|
+
: false;
|
|
297
|
+
if ((config.git.auto_push || config.git.auto_pr) && !remoteAvailable) {
|
|
298
|
+
logger.info("No remote configured — skipping push/PR automation");
|
|
299
|
+
await addCheckpoint(session, { stage: "git-push", skipped: "no-remote" });
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (remoteAvailable && (config.git.auto_push || config.git.auto_pr)) {
|
|
303
|
+
await fetchBase(gitCtx.baseBranch);
|
|
304
|
+
await ensureBranchUpToDateWithBase({
|
|
305
|
+
branch: gitCtx.branch,
|
|
306
|
+
baseBranch: gitCtx.baseBranch,
|
|
307
|
+
autoRebase: gitCtx.autoRebase
|
|
308
|
+
});
|
|
309
|
+
await addCheckpoint(session, { stage: "git-rebase-check", branch: gitCtx.branch });
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (remoteAvailable && (config.git.auto_push || config.git.auto_pr)) {
|
|
313
|
+
await pushBranch(gitCtx.branch);
|
|
314
|
+
await addCheckpoint(session, { stage: "git-push", branch: gitCtx.branch });
|
|
315
|
+
logger.info(`Pushed branch: ${gitCtx.branch}`);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
let prUrl = session.ci_pr_url || null;
|
|
319
|
+
if (remoteAvailable && config.git.auto_pr && !prUrl) {
|
|
320
|
+
const body = buildPrBody({ task, stageResults });
|
|
321
|
+
prUrl = await createPullRequest({
|
|
322
|
+
baseBranch: gitCtx.baseBranch,
|
|
323
|
+
branch: gitCtx.branch,
|
|
324
|
+
title: commitMessageFromTask(task),
|
|
325
|
+
body
|
|
326
|
+
});
|
|
327
|
+
await addCheckpoint(session, { stage: "git-pr", branch: gitCtx.branch, pr: prUrl });
|
|
328
|
+
logger.info("Pull request created");
|
|
329
|
+
} else if (prUrl) {
|
|
330
|
+
logger.info(`PR already exists (CI flow): ${prUrl}`);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return { committed, branch: gitCtx.branch, prUrl, pr: prUrl, commits };
|
|
334
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-HU git automation: branch, commit, push, PR per HU story.
|
|
3
|
+
* Called from the HU sub-pipeline wrapper in orchestrator.js.
|
|
4
|
+
*/
|
|
5
|
+
import { commitAll, pushBranch, createPullRequest, hasChanges } from "../utils/git.js";
|
|
6
|
+
import { runCommand } from "../utils/process.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Build a git-safe branch name for an HU story.
|
|
10
|
+
* @param {string} prefix - e.g. "feat/"
|
|
11
|
+
* @param {object} story - HU story {id, title}
|
|
12
|
+
* @returns {string}
|
|
13
|
+
*/
|
|
14
|
+
export function buildHuBranchName(prefix, story) {
|
|
15
|
+
const baseSlug = String(story.title || story.id || "hu")
|
|
16
|
+
.toLowerCase()
|
|
17
|
+
.replaceAll(/[^a-z0-9]+/g, "-")
|
|
18
|
+
.replaceAll(/^-+|-+$/g, "")
|
|
19
|
+
.slice(0, 40);
|
|
20
|
+
return `${prefix}${story.id}-${baseSlug}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the base branch for a given HU based on its dependencies.
|
|
25
|
+
* If it has no blocked_by, uses config.base_branch.
|
|
26
|
+
* If it has parents, uses the last-created parent branch (assumes parents ran first).
|
|
27
|
+
*
|
|
28
|
+
* @param {object} story - the HU story
|
|
29
|
+
* @param {Map<string,string>} huBranches - map of huId → branchName (already created)
|
|
30
|
+
* @param {string} baseBranch - config.base_branch fallback
|
|
31
|
+
* @returns {string} base branch name
|
|
32
|
+
*/
|
|
33
|
+
export function resolveHuBase(story, huBranches, baseBranch) {
|
|
34
|
+
const parents = story.blocked_by || [];
|
|
35
|
+
if (parents.length === 0) return baseBranch;
|
|
36
|
+
// Walk parents in reverse order of declaration to find the most recent one
|
|
37
|
+
for (let i = parents.length - 1; i >= 0; i--) {
|
|
38
|
+
const parentBranch = huBranches.get(parents[i]);
|
|
39
|
+
if (parentBranch) return parentBranch;
|
|
40
|
+
}
|
|
41
|
+
return baseBranch;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Resolve to an actually-existing local ref. Tries the preferred branch
|
|
46
|
+
* first, then `main` and `master`, then plain `HEAD` as last resort.
|
|
47
|
+
*
|
|
48
|
+
* Why: N6 plan-flow dogfooding (2026-05-07) on a `git init -q` repo
|
|
49
|
+
* with `init.defaultBranch=master` produced 7 identical warnings:
|
|
50
|
+
* `failed to create branch feat/...-001 from main:
|
|
51
|
+
* fatal: 'main' is not a commit and a branch can't be created`.
|
|
52
|
+
* Every HU then fell back to running on the original branch, which
|
|
53
|
+
* defeats the per-HU isolation the sub-pipeline was supposed to give.
|
|
54
|
+
*
|
|
55
|
+
* The configured base_branch is honoured when it exists; otherwise we
|
|
56
|
+
* pick the most likely default. HEAD always exists once the repo has
|
|
57
|
+
* any commit, so the fallback never throws past the gate.
|
|
58
|
+
*
|
|
59
|
+
* @param {string} preferred
|
|
60
|
+
* @returns {Promise<string>}
|
|
61
|
+
*/
|
|
62
|
+
async function resolveExistingBranchRef(preferred) {
|
|
63
|
+
const candidates = [preferred, "main", "master", "HEAD"]
|
|
64
|
+
.filter((c, i, arr) => c && arr.indexOf(c) === i); // unique, truthy
|
|
65
|
+
for (const ref of candidates) {
|
|
66
|
+
const probe = await runCommand("git", ["rev-parse", "--verify", "--quiet", ref], {});
|
|
67
|
+
if (probe.exitCode === 0) return ref;
|
|
68
|
+
}
|
|
69
|
+
return preferred; // give up; caller will surface the original error
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Create a branch for an HU starting from its resolved base.
|
|
74
|
+
* Returns the branch name created (or null if git automation is disabled).
|
|
75
|
+
*
|
|
76
|
+
* @param {object} params
|
|
77
|
+
* @param {object} params.story
|
|
78
|
+
* @param {Map} params.huBranches
|
|
79
|
+
* @param {object} params.config
|
|
80
|
+
* @param {object} params.logger
|
|
81
|
+
* @returns {Promise<string|null>}
|
|
82
|
+
*/
|
|
83
|
+
export async function prepareHuBranch({ story, huBranches, config, logger }) {
|
|
84
|
+
if (!config.git?.auto_commit && !config.git?.auto_push && !config.git?.auto_pr) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const baseBranch = resolveHuBase(story, huBranches, config.base_branch || "main");
|
|
88
|
+
const effectiveBase = await resolveExistingBranchRef(baseBranch);
|
|
89
|
+
if (effectiveBase !== baseBranch) {
|
|
90
|
+
logger.info(`HU git: configured base '${baseBranch}' not found locally — using '${effectiveBase}' instead`);
|
|
91
|
+
}
|
|
92
|
+
const prefix = config.git?.branch_prefix || "feat/";
|
|
93
|
+
const branchName = buildHuBranchName(prefix, story);
|
|
94
|
+
|
|
95
|
+
// Checkout from the resolved base. Use `git checkout -B` to overwrite if rerun.
|
|
96
|
+
const res = await runCommand("git", ["checkout", "-B", branchName, effectiveBase], {});
|
|
97
|
+
if (res.exitCode !== 0) {
|
|
98
|
+
logger.warn(`HU git: failed to create branch ${branchName} from ${effectiveBase}: ${res.stderr}`);
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
huBranches.set(story.id, branchName);
|
|
102
|
+
logger.info(`HU ${story.id}: branch '${branchName}' from '${effectiveBase}'`);
|
|
103
|
+
return branchName;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* After an HU is approved, commit its changes and optionally push + create PR.
|
|
108
|
+
*
|
|
109
|
+
* @param {object} params
|
|
110
|
+
* @param {object} params.story
|
|
111
|
+
* @param {string} params.branchName
|
|
112
|
+
* @param {object} params.config
|
|
113
|
+
* @param {object} params.logger
|
|
114
|
+
* @returns {Promise<{committed: boolean, pushed: boolean, prUrl: string|null}>}
|
|
115
|
+
*/
|
|
116
|
+
export async function finalizeHuCommit({ story, branchName, config, logger, cwd = null }) {
|
|
117
|
+
const result = { committed: false, pushed: false, prUrl: null };
|
|
118
|
+
if (!branchName) return result;
|
|
119
|
+
|
|
120
|
+
const changed = await hasChanges(cwd);
|
|
121
|
+
if (!changed) {
|
|
122
|
+
logger.info(`HU ${story.id}: no changes to commit`);
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const title = story.title || story.id;
|
|
127
|
+
const commitMsg = `feat(${story.id}): ${title}`;
|
|
128
|
+
if (config.git?.auto_commit) {
|
|
129
|
+
const commitRes = await commitAll(commitMsg, cwd);
|
|
130
|
+
if (commitRes) {
|
|
131
|
+
result.committed = true;
|
|
132
|
+
logger.info(`HU ${story.id}: committed on '${branchName}'`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (config.git?.auto_push && result.committed) {
|
|
137
|
+
try {
|
|
138
|
+
await pushBranch(branchName, cwd);
|
|
139
|
+
result.pushed = true;
|
|
140
|
+
logger.info(`HU ${story.id}: pushed '${branchName}'`);
|
|
141
|
+
} catch (err) {
|
|
142
|
+
logger.warn(`HU ${story.id}: push failed: ${err.message}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (config.git?.auto_pr && result.pushed) {
|
|
147
|
+
try {
|
|
148
|
+
const prBody = [
|
|
149
|
+
`## HU ${story.id}: ${title}`,
|
|
150
|
+
"",
|
|
151
|
+
story.certified?.text || "",
|
|
152
|
+
"",
|
|
153
|
+
"### Acceptance criteria",
|
|
154
|
+
...(story.acceptance_criteria || []).map(c => `- ${c}`)
|
|
155
|
+
].join("\n");
|
|
156
|
+
const url = await createPullRequest({
|
|
157
|
+
baseBranch: config.base_branch || "main",
|
|
158
|
+
branch: branchName,
|
|
159
|
+
title: commitMsg,
|
|
160
|
+
body: prBody,
|
|
161
|
+
cwd
|
|
162
|
+
});
|
|
163
|
+
result.prUrl = url;
|
|
164
|
+
logger.info(`HU ${story.id}: PR created ${url}`);
|
|
165
|
+
} catch (err) {
|
|
166
|
+
logger.warn(`HU ${story.id}: PR creation failed: ${err.message}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Shim de compatibilidad: hu-snapshot vive ahora en karajan-core.
|
|
2
|
+
// Mantenemos esta ruta para que callers internos del CLI (orchestrator,
|
|
3
|
+
// runners) sigan funcionando sin tocar imports.
|
|
4
|
+
export {
|
|
5
|
+
snapshotRefForHu,
|
|
6
|
+
createHuSnapshot,
|
|
7
|
+
hasHuSnapshot,
|
|
8
|
+
restoreHuSnapshot,
|
|
9
|
+
removeHuSnapshot,
|
|
10
|
+
} from "karajan-core/hu-snapshot";
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Golden tasks asserter (KJC-TSK-0374). Parses a `summary.md` produced
|
|
3
|
+
* by a real `kj run` and compares it against a GoldenTask's structural
|
|
4
|
+
* expectations. Pure: no I/O, no subprocess. Subprocess driver lands
|
|
5
|
+
* in PR-3a; the 3 fixtures + baseline + spec land in PR-3b.
|
|
6
|
+
*
|
|
7
|
+
* Three of the five assertion families live here (the ones derivable
|
|
8
|
+
* from summary.md alone): expected_commits_min, expected_audit_status,
|
|
9
|
+
* expected_plan_adherence_min. expected_test_files and
|
|
10
|
+
* allowed_loc_range need filesystem access — PR-3a wires those.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { escapeRegExp } from "../utils/escape-regexp.js";
|
|
14
|
+
|
|
15
|
+
const COMMIT_LINE = /^- `([^`]+)`\s+(.*)$/;
|
|
16
|
+
const SCORE_LINE = /^\*\*Score\*\*:\s*(\d+)\s*\/\s*100/m;
|
|
17
|
+
const AUDIT_ROW = /^\|\s*`audit`\s*\|\s*([^|]+?)\s*\|/m;
|
|
18
|
+
|
|
19
|
+
function extractSection(content, heading) {
|
|
20
|
+
const re = new RegExp(`^## ${escapeRegExp(heading)}\\s*$([\\s\\S]*?)(?=^## |\\Z)`, "m");
|
|
21
|
+
const m = content.match(re);
|
|
22
|
+
return m ? m[1].trim() : "";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function parseCommits(content) {
|
|
26
|
+
const section = extractSection(content, "Commits");
|
|
27
|
+
if (!section || /No commits/.test(section)) return [];
|
|
28
|
+
return section
|
|
29
|
+
.split("\n")
|
|
30
|
+
.map((line) => line.match(COMMIT_LINE))
|
|
31
|
+
.filter(Boolean)
|
|
32
|
+
.map(([, hash, message]) => ({ hash, message }));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function parseAuditStatus(content) {
|
|
36
|
+
const m = content.match(AUDIT_ROW);
|
|
37
|
+
if (!m) return null;
|
|
38
|
+
const cell = m[1];
|
|
39
|
+
if (/✅|pass/i.test(cell)) return "pass";
|
|
40
|
+
if (/❌|fail/i.test(cell)) return "fail";
|
|
41
|
+
if (/⚠|warn/i.test(cell)) return "warning";
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function parsePlanAdherence(content) {
|
|
46
|
+
const section = extractSection(content, "Plan adherence");
|
|
47
|
+
if (!section) return null;
|
|
48
|
+
const m = section.match(SCORE_LINE);
|
|
49
|
+
if (!m) return null;
|
|
50
|
+
const score = Number(m[1]);
|
|
51
|
+
return Number.isFinite(score) ? score : null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function parseSummary(content) {
|
|
55
|
+
const text = String(content || "");
|
|
56
|
+
return {
|
|
57
|
+
commits: parseCommits(text),
|
|
58
|
+
auditStatus: parseAuditStatus(text),
|
|
59
|
+
planAdherenceScore: parsePlanAdherence(text),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function assertFromSummary(task, parsed) {
|
|
64
|
+
const failures = [];
|
|
65
|
+
|
|
66
|
+
const fail = (kind, expected, actual, message) => failures.push({ kind, expected, actual, message });
|
|
67
|
+
|
|
68
|
+
if (parsed.commits.length < task.expected_commits_min) {
|
|
69
|
+
fail("commits_min", `>= ${task.expected_commits_min}`, parsed.commits.length,
|
|
70
|
+
`expected at least ${task.expected_commits_min} commit(s); got ${parsed.commits.length}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (parsed.auditStatus !== task.expected_audit_status) {
|
|
74
|
+
const got = parsed.auditStatus === null ? "no audit row in summary" : `"${parsed.auditStatus}"`;
|
|
75
|
+
fail("audit_status", task.expected_audit_status, parsed.auditStatus,
|
|
76
|
+
`expected audit "${task.expected_audit_status}"; got ${got}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (typeof task.expected_plan_adherence_min === "number") {
|
|
80
|
+
const score = parsed.planAdherenceScore;
|
|
81
|
+
const min = task.expected_plan_adherence_min;
|
|
82
|
+
if (score === null) {
|
|
83
|
+
fail("plan_adherence_missing", `>= ${min}`, null,
|
|
84
|
+
`expected plan adherence >= ${min}; summary has no Plan adherence section`);
|
|
85
|
+
} else if (score < min) {
|
|
86
|
+
fail("plan_adherence_below", `>= ${min}`, score,
|
|
87
|
+
`expected plan adherence >= ${min}; got ${score}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return { ok: failures.length === 0, failures };
|
|
92
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Golden tasks loader (KJC-TSK-0374). Reads + validates JSON files
|
|
3
|
+
* under a directory; aggregates errors; rejects duplicate ids.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from "node:fs/promises";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { safeParseGoldenTask } from "./schema.js";
|
|
9
|
+
|
|
10
|
+
export async function loadGoldenTasks(dir) {
|
|
11
|
+
const entries = await fs.readdir(dir, { withFileTypes: true }).catch((err) => {
|
|
12
|
+
throw new Error(`golden tasks directory not readable: ${dir} (${err.code || err.message})`);
|
|
13
|
+
});
|
|
14
|
+
const jsonFiles = entries
|
|
15
|
+
.filter((e) => e.isFile() && e.name.endsWith(".json"))
|
|
16
|
+
.map((e) => path.join(dir, e.name))
|
|
17
|
+
.sort();
|
|
18
|
+
|
|
19
|
+
const tasks = [];
|
|
20
|
+
const errors = [];
|
|
21
|
+
const seenIds = new Map();
|
|
22
|
+
|
|
23
|
+
for (const filePath of jsonFiles) {
|
|
24
|
+
let raw;
|
|
25
|
+
try {
|
|
26
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
27
|
+
raw = JSON.parse(content);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
errors.push(`${path.basename(filePath)}: invalid JSON (${err.message})`);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const result = safeParseGoldenTask(raw);
|
|
33
|
+
if (!result.ok) {
|
|
34
|
+
const summary = result.issues.map((iss) => ` - ${iss.path || "(root)"}: ${iss.message}`).join("\n");
|
|
35
|
+
errors.push(`${path.basename(filePath)}:\n${summary}`);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const existing = seenIds.get(result.task.id);
|
|
39
|
+
if (existing) {
|
|
40
|
+
errors.push(`${path.basename(filePath)}: duplicate id "${result.task.id}" (also in ${existing})`);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
seenIds.set(result.task.id, path.basename(filePath));
|
|
44
|
+
tasks.push(result.task);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (errors.length > 0) {
|
|
48
|
+
throw new Error(`golden tasks failed validation:\n${errors.join("\n")}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return { tasks };
|
|
52
|
+
}
|