@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,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* detectStackRoots — find every language root in a (possibly monorepo) tree
|
|
3
|
+
* for `kj harden` (KJC-TSK-0561).
|
|
4
|
+
*
|
|
5
|
+
* Walks the project up to a shallow depth and returns the shallowest
|
|
6
|
+
* directory where each language appears, e.g. a fullstack repo yields
|
|
7
|
+
* `[{dir:"frontend",language:"javascript"},{dir:"backend",language:"python"}]`
|
|
8
|
+
* so lint/format config is seeded on each side. Universal config (handled by
|
|
9
|
+
* the caller) stays at the project root. Noise dirs are never descended.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
13
|
+
import { join, relative } from "node:path";
|
|
14
|
+
|
|
15
|
+
const IGNORE_DIRS = new Set([
|
|
16
|
+
"node_modules",
|
|
17
|
+
"dist",
|
|
18
|
+
"build",
|
|
19
|
+
"coverage",
|
|
20
|
+
"vendor",
|
|
21
|
+
"target",
|
|
22
|
+
"__pycache__",
|
|
23
|
+
"venv",
|
|
24
|
+
// Auxiliary code that isn't the app to harden (KJC-TSK-0564): demo/fixture
|
|
25
|
+
// dirs ship their own throwaway manifests and must not seed lint/CI config.
|
|
26
|
+
"examples",
|
|
27
|
+
"example",
|
|
28
|
+
"fixtures",
|
|
29
|
+
"samples",
|
|
30
|
+
"third_party",
|
|
31
|
+
"testdata",
|
|
32
|
+
"__fixtures__",
|
|
33
|
+
]);
|
|
34
|
+
const DEFAULT_MAX_DEPTH = 2;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Minimal glob for harden scope flags — no dynamic RegExp (eslint security).
|
|
38
|
+
* Matches on exact equality, directory prefix (`wrappers` ⊇ `wrappers/python`)
|
|
39
|
+
* and `*` wildcards anchored at both ends unless the `*` is leading/trailing.
|
|
40
|
+
*/
|
|
41
|
+
export function matchesGlob(value, pattern) {
|
|
42
|
+
if (value === pattern || value.startsWith(`${pattern}/`)) return true;
|
|
43
|
+
if (!pattern.includes("*")) return false;
|
|
44
|
+
const pieces = pattern.split("*");
|
|
45
|
+
let idx = 0;
|
|
46
|
+
for (let i = 0; i < pieces.length; i += 1) {
|
|
47
|
+
const piece = pieces[i];
|
|
48
|
+
if (!piece) continue;
|
|
49
|
+
const at = value.indexOf(piece, idx);
|
|
50
|
+
if (at === -1 || (i === 0 && at !== 0)) return false;
|
|
51
|
+
idx = at + piece.length;
|
|
52
|
+
}
|
|
53
|
+
return pattern.endsWith("*") || value.length === idx;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Identify the language of a single directory by its manifest files. */
|
|
57
|
+
export function languageAt(dir) {
|
|
58
|
+
if (existsSync(join(dir, "package.json"))) {
|
|
59
|
+
if (existsSync(join(dir, "tsconfig.json"))) return "typescript";
|
|
60
|
+
try {
|
|
61
|
+
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
|
|
62
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
63
|
+
if (deps.typescript || deps["ts-node"]) return "typescript";
|
|
64
|
+
} catch {
|
|
65
|
+
/* unreadable package.json → treat as plain JS */
|
|
66
|
+
}
|
|
67
|
+
return "javascript";
|
|
68
|
+
}
|
|
69
|
+
if (["pyproject.toml", "requirements.txt", "setup.py"].some((f) => existsSync(join(dir, f)))) return "python";
|
|
70
|
+
if (existsSync(join(dir, "go.mod"))) return "go";
|
|
71
|
+
if (existsSync(join(dir, "composer.json"))) return "php";
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function detectStackRoots(projectDir, { maxDepth = DEFAULT_MAX_DEPTH, only = [], exclude = [] } = {}) {
|
|
76
|
+
const found = [];
|
|
77
|
+
function walk(dir, depth) {
|
|
78
|
+
const language = languageAt(dir);
|
|
79
|
+
if (language) found.push({ dir: relative(projectDir, dir) || ".", language, depth });
|
|
80
|
+
if (depth >= maxDepth) return;
|
|
81
|
+
let entries;
|
|
82
|
+
try {
|
|
83
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
84
|
+
} catch {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
for (const e of entries) {
|
|
88
|
+
if (e.isDirectory() && !e.name.startsWith(".") && !IGNORE_DIRS.has(e.name)) {
|
|
89
|
+
walk(join(dir, e.name), depth + 1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
walk(projectDir, 0);
|
|
94
|
+
|
|
95
|
+
// Keep only the shallowest directory per language.
|
|
96
|
+
const byLanguage = new Map();
|
|
97
|
+
for (const f of found) {
|
|
98
|
+
const cur = byLanguage.get(f.language);
|
|
99
|
+
if (!cur || f.depth < cur.depth) byLanguage.set(f.language, f);
|
|
100
|
+
}
|
|
101
|
+
let roots = [...byLanguage.values()].map(({ dir, language }) => ({ dir, language }));
|
|
102
|
+
if (only.length) roots = roots.filter((r) => only.some((o) => matchesGlob(r.dir, o)));
|
|
103
|
+
if (exclude.length) roots = roots.filter((r) => !exclude.some((g) => matchesGlob(r.dir, g)));
|
|
104
|
+
return roots.sort((a, b) => a.dir.localeCompare(b.dir));
|
|
105
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test-on-push policy (KJC-TSK-0647) — the full suite on every push is
|
|
3
|
+
* redundant when CI already runs it per PR, and a source of false reds
|
|
4
|
+
* (suites unstable under local parallelism). Field-reproduced.
|
|
5
|
+
*
|
|
6
|
+
* Policy: a CI workflow that runs tests ⇒ pre-push drops its test command
|
|
7
|
+
* (identity guard and chaining stay). Repos can force it back with
|
|
8
|
+
* `"kj": { "harden": { "test_on_push": true } }` in package.json. Repos
|
|
9
|
+
* without CI keep tests on push — there it IS the last safety net.
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
|
|
14
|
+
const TEST_STEP = /\b(npm (run )?test|vitest|jest|pytest|go test|phpunit|cargo test)\b/;
|
|
15
|
+
|
|
16
|
+
export function ciRunsTests(projectDir) {
|
|
17
|
+
const dir = join(projectDir, ".github", "workflows");
|
|
18
|
+
if (!existsSync(dir)) return false;
|
|
19
|
+
try {
|
|
20
|
+
return readdirSync(dir)
|
|
21
|
+
.filter((f) => /\.ya?ml$/.test(f))
|
|
22
|
+
.some((f) => TEST_STEP.test(readFileSync(join(dir, f), "utf8")));
|
|
23
|
+
} catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function resolveTestOnPush({ projectDir, repoCfg = {}, testCmd = null }) {
|
|
29
|
+
if (!testCmd) return { testCmd: null, reason: null };
|
|
30
|
+
if (repoCfg.test_on_push === true) return { testCmd, reason: null };
|
|
31
|
+
if (ciRunsTests(projectDir)) {
|
|
32
|
+
return {
|
|
33
|
+
testCmd: null,
|
|
34
|
+
reason: "CI already runs the suite per PR — pre-push keeps only the identity guard (opt back in with kj.harden.test_on_push: true in package.json)",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return { testCmd, reason: null };
|
|
38
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* workflow-engine — seeds CI quality workflows for `kj harden` (KJC-TSK-0557).
|
|
3
|
+
*
|
|
4
|
+
* Seed-if-absent into `.github/workflows/`: a workflow the user already wrote
|
|
5
|
+
* (no kj:managed marker) is left untouched and reported "skipped". kj-managed
|
|
6
|
+
* workflows refresh in place. Markers ride in YAML `#` comments.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
import { dirname } from "node:path";
|
|
14
|
+
|
|
15
|
+
import { upsertManagedBlock } from "../utils/managed-markers.js";
|
|
16
|
+
import { extraWorkflowsFor, mutationWorkflowFor, qualityWorkflowFor, policyWorkflowFor, WORKFLOWS } from "./workflow-templates.js";
|
|
17
|
+
|
|
18
|
+
// La versión del kj que corre harden — es la que se pinea en el fallback
|
|
19
|
+
// npx del workflow de policy (jamás @latest en CI). Lectura LAZY: en el
|
|
20
|
+
// bundle SEA no hay package.json junto al módulo y una lectura eager a
|
|
21
|
+
// nivel de módulo tumbaba el binario entero al cargar (ENOENT /package.json).
|
|
22
|
+
let _ownPkg;
|
|
23
|
+
function ownPkg() {
|
|
24
|
+
if (_ownPkg === undefined) {
|
|
25
|
+
try {
|
|
26
|
+
_ownPkg = JSON.parse(
|
|
27
|
+
readFileSync(join(dirname(fileURLToPath(import.meta.url)), "..", "..", "package.json"), "utf8"),
|
|
28
|
+
);
|
|
29
|
+
} catch {
|
|
30
|
+
_ownPkg = null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return _ownPkg;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const BLOCK_VERSION = 1;
|
|
37
|
+
const WORKFLOWS_DIR = join(".github", "workflows");
|
|
38
|
+
|
|
39
|
+
/** A package.json that ships code (not private, has bin or main) ⇒ publishable. */
|
|
40
|
+
export function isPublishableNpm(projectDir) {
|
|
41
|
+
const pkgPath = join(projectDir, "package.json");
|
|
42
|
+
if (!existsSync(pkgPath)) return false;
|
|
43
|
+
try {
|
|
44
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
45
|
+
return pkg.private !== true && Boolean(pkg.bin || pkg.main);
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Lockfile-detected package manager (KJC-BUG-0131): npm ci kills pnpm/yarn CI. */
|
|
52
|
+
export function detectPackageManager(projectDir) {
|
|
53
|
+
if (existsSync(join(projectDir, "pnpm-lock.yaml"))) return "pnpm";
|
|
54
|
+
if (existsSync(join(projectDir, "yarn.lock"))) return "yarn";
|
|
55
|
+
return "npm";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// KJC-BUG-0137: the JS quality workflow only carries a lint step when the
|
|
59
|
+
// project HAS a lint script — and then it enforces (no --if-present).
|
|
60
|
+
function hasLintScript(projectDir) {
|
|
61
|
+
try {
|
|
62
|
+
return Boolean(JSON.parse(readFileSync(join(projectDir, "package.json"), "utf8")).scripts?.lint);
|
|
63
|
+
} catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function installWorkflows({
|
|
69
|
+
projectDir = process.cwd(),
|
|
70
|
+
language = null,
|
|
71
|
+
profile = "standard",
|
|
72
|
+
mutation = false,
|
|
73
|
+
dryRun = false,
|
|
74
|
+
kjVersion = null,
|
|
75
|
+
} = {}) {
|
|
76
|
+
const dir = join(projectDir, WORKFLOWS_DIR);
|
|
77
|
+
const pm = detectPackageManager(projectDir);
|
|
78
|
+
const quality = qualityWorkflowFor(language, pm, hasLintScript(projectDir));
|
|
79
|
+
const extras = extraWorkflowsFor({ profile, publishable: isPublishableNpm(projectDir), pm });
|
|
80
|
+
const mut = mutation ? mutationWorkflowFor(language, pm) : null;
|
|
81
|
+
// PL-C: el tier C solo existe donde el proyecto DECLARA policy. Versión a
|
|
82
|
+
// pinear: la plumbeada por el CLI (SEA) o la propia; "latest" es último
|
|
83
|
+
// recurso inalcanzable en los caminos reales.
|
|
84
|
+
const pinned = kjVersion ?? ownPkg()?.version ?? "latest";
|
|
85
|
+
const policy = existsSync(join(projectDir, ".karajan", "policy.yml"))
|
|
86
|
+
? { file: "kj-policy.yml", blockId: "wf-policy", body: policyWorkflowFor(pinned, ownPkg()?.name ?? "karajan-code") }
|
|
87
|
+
: null;
|
|
88
|
+
const all = [...WORKFLOWS, ...(quality ? [quality] : []), ...extras, ...(mut ? [mut] : []), ...(policy ? [policy] : [])];
|
|
89
|
+
const results = [];
|
|
90
|
+
for (const wf of all) {
|
|
91
|
+
const target = join(dir, wf.file);
|
|
92
|
+
const label = join(WORKFLOWS_DIR, wf.file);
|
|
93
|
+
const exists = existsSync(target);
|
|
94
|
+
const source = exists ? readFileSync(target, "utf8") : "";
|
|
95
|
+
if (exists && !source.includes(`kj:managed:${wf.blockId}`)) {
|
|
96
|
+
results.push({ file: label, action: "skipped" });
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const { content, action } = upsertManagedBlock({
|
|
100
|
+
source,
|
|
101
|
+
blockId: wf.blockId,
|
|
102
|
+
version: BLOCK_VERSION,
|
|
103
|
+
body: wf.body,
|
|
104
|
+
style: "hash",
|
|
105
|
+
});
|
|
106
|
+
if (!dryRun && action !== "unchanged") {
|
|
107
|
+
mkdirSync(dir, { recursive: true });
|
|
108
|
+
// Newline final SIEMPRE: prettier (y POSIX) lo exigen y el bloque
|
|
109
|
+
// gestionado no lo garantiza — sin esto, el format-check del propio
|
|
110
|
+
// pre-commit rechaza el fichero recién generado.
|
|
111
|
+
writeFileSync(target, content.endsWith("\n") ? content : `${content}\n`);
|
|
112
|
+
}
|
|
113
|
+
results.push({ file: label, action });
|
|
114
|
+
}
|
|
115
|
+
return { dryRun, workflows: results };
|
|
116
|
+
}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI workflow templates for `kj harden` (KJC-TSK-0557).
|
|
3
|
+
*
|
|
4
|
+
* Generic, language-agnostic GitHub Actions that `kj harden` seeds into a
|
|
5
|
+
* target repo's `.github/workflows/`. Bodies are plain strings (NOT template
|
|
6
|
+
* literals) so the `${{ … }}` GitHub expressions survive verbatim.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Blocks AI self-attribution in PR commit messages. Pure bash + grep, no Node.
|
|
10
|
+
// Pins github.base_ref to an env var (never interpolated into the run block).
|
|
11
|
+
// KJC-BUG-0136 (issue #1374): actions pinned to full commit SHAs — a
|
|
12
|
+
// mutable tag lets its owner run arbitrary code inside EVERY hardened
|
|
13
|
+
// repo's CI, with access to the workflow's secrets; 5 of this project's 7
|
|
14
|
+
// audit findings came from files kj itself generated. The version rides in
|
|
15
|
+
// a comment (the practice kj audit demands). To bump one:
|
|
16
|
+
// gh api repos/<owner>/<repo>/commits/<tag> --jq .sha
|
|
17
|
+
export const PINNED_ACTIONS = {
|
|
18
|
+
checkout: "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4",
|
|
19
|
+
setupNode: "actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4",
|
|
20
|
+
setupPython: "actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5",
|
|
21
|
+
setupGo: "actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5",
|
|
22
|
+
setupPhp: "shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const NO_AI_ATTRIBUTION_WORKFLOW = [
|
|
26
|
+
"name: Block AI attribution",
|
|
27
|
+
"on:",
|
|
28
|
+
" pull_request:",
|
|
29
|
+
" branches: [main]",
|
|
30
|
+
"permissions:",
|
|
31
|
+
" contents: read",
|
|
32
|
+
"jobs:",
|
|
33
|
+
" scan:",
|
|
34
|
+
" runs-on: ubuntu-latest",
|
|
35
|
+
" steps:",
|
|
36
|
+
` - uses: ${PINNED_ACTIONS.checkout}`,
|
|
37
|
+
" with:",
|
|
38
|
+
" fetch-depth: 0",
|
|
39
|
+
" - name: Scan commit messages for AI attribution",
|
|
40
|
+
" env:",
|
|
41
|
+
" BASE_REF: ${{ github.base_ref }}",
|
|
42
|
+
" run: |",
|
|
43
|
+
' msgs=$(git log --format=%B "origin/${BASE_REF}...HEAD" || true)',
|
|
44
|
+
" pat='co-authored-by:.*(claude|anthropic|openai|chatgpt|gpt-[0-9]|copilot|gemini|cursor|windsurf|codeium|deepseek)'",
|
|
45
|
+
" pat=\"$pat|(generated|written|authored|assisted|powered) (by|with|using) .*(claude|anthropic|openai|copilot|gemini)\"",
|
|
46
|
+
' if printf "%s" "$msgs" | grep -qiE "$pat"; then',
|
|
47
|
+
' echo "::error::AI attribution detected in commit messages"; exit 1',
|
|
48
|
+
" fi",
|
|
49
|
+
' echo "AI attribution scan: clean"',
|
|
50
|
+
].join("\n");
|
|
51
|
+
|
|
52
|
+
/** Universal workflows (every stack). style:hash ⇒ marker in YAML comments. */
|
|
53
|
+
export const WORKFLOWS = [
|
|
54
|
+
{ file: "kj-no-ai-attribution.yml", blockId: "wf-no-ai", body: NO_AI_ATTRIBUTION_WORKFLOW },
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
// PL-C (KJC-TSK-0735) — tier C del ADR 0001: re-check merge-blocking en CI
|
|
58
|
+
// (cubre el hook local manipulado). Solo se siembra con policy declarada;
|
|
59
|
+
// npx PINEADO a la versión del kj que corrió harden — jamás @latest en CI.
|
|
60
|
+
export const policyWorkflowFor = (kjVersion, kjName = "karajan-code") => [
|
|
61
|
+
"name: Policy",
|
|
62
|
+
"on:",
|
|
63
|
+
" pull_request:",
|
|
64
|
+
"permissions:",
|
|
65
|
+
" contents: read",
|
|
66
|
+
"jobs:",
|
|
67
|
+
" policy-check:",
|
|
68
|
+
" runs-on: ubuntu-latest",
|
|
69
|
+
" steps:",
|
|
70
|
+
` - uses: ${PINNED_ACTIONS.checkout}`,
|
|
71
|
+
" with:",
|
|
72
|
+
" fetch-depth: 0",
|
|
73
|
+
` - uses: ${PINNED_ACTIONS.setupNode}`,
|
|
74
|
+
" with:",
|
|
75
|
+
" node-version: 22",
|
|
76
|
+
" - name: kj policy check (strict) on the PR diff",
|
|
77
|
+
" env:",
|
|
78
|
+
" BASE_REF: ${{ github.base_ref }}",
|
|
79
|
+
" run: |",
|
|
80
|
+
` if [ -f bin/kj.js ]; then npm ci --ignore-scripts && KJ="node bin/kj.js"; else KJ="npx --yes ${kjName}@${kjVersion}"; fi`,
|
|
81
|
+
' $KJ policy check --range "origin/${BASE_REF}...HEAD" --strict',
|
|
82
|
+
"", // línea en blanco pre-marcador: prettier la exige tras un block scalar
|
|
83
|
+
].join("\n");
|
|
84
|
+
|
|
85
|
+
const header = (steps) =>
|
|
86
|
+
[
|
|
87
|
+
"name: Quality",
|
|
88
|
+
"on:",
|
|
89
|
+
" pull_request:",
|
|
90
|
+
" branches: [main]",
|
|
91
|
+
"jobs:",
|
|
92
|
+
" quality:",
|
|
93
|
+
" runs-on: ubuntu-latest",
|
|
94
|
+
" steps:",
|
|
95
|
+
` - uses: ${PINNED_ACTIONS.checkout}`,
|
|
96
|
+
...steps,
|
|
97
|
+
].join("\n");
|
|
98
|
+
|
|
99
|
+
// Per-package-manager commands (KJC-BUG-0131, issue #1330): `npm ci` in a
|
|
100
|
+
// pnpm/yarn repo dies with EUSAGE (no package-lock.json). corepack ships with
|
|
101
|
+
// Node 22 and resolves the right pnpm/yarn from `packageManager` — no
|
|
102
|
+
// third-party action. Yarn scripts run through `npm run` (scripts don't touch
|
|
103
|
+
// the lockfile). Lint ENFORCES — never --if-present: a step that passes when
|
|
104
|
+
// its script is missing protects nothing (KJC-BUG-0137, issue #1357); the
|
|
105
|
+
// engine omits the step instead when the project has no lint script.
|
|
106
|
+
export const PM_COMMANDS = {
|
|
107
|
+
npm: { setup: [], install: "npm ci", lint: "npm run -s lint", test: "npm test" },
|
|
108
|
+
pnpm: {
|
|
109
|
+
setup: [" - run: corepack enable"],
|
|
110
|
+
install: "pnpm install --frozen-lockfile",
|
|
111
|
+
lint: "pnpm run -s lint",
|
|
112
|
+
test: "pnpm test",
|
|
113
|
+
},
|
|
114
|
+
yarn: {
|
|
115
|
+
setup: [" - run: corepack enable"],
|
|
116
|
+
install: "yarn install --frozen-lockfile",
|
|
117
|
+
lint: "npm run -s lint",
|
|
118
|
+
test: "yarn test",
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const nodeSetupSteps = (pm) => [
|
|
123
|
+
` - uses: ${PINNED_ACTIONS.setupNode}`,
|
|
124
|
+
" with:",
|
|
125
|
+
" node-version: 22",
|
|
126
|
+
...(PM_COMMANDS[pm] || PM_COMMANDS.npm).setup,
|
|
127
|
+
` - run: ${(PM_COMMANDS[pm] || PM_COMMANDS.npm).install}`,
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
const QUALITY_BY_LANGUAGE = {
|
|
131
|
+
python: header([
|
|
132
|
+
` - uses: ${PINNED_ACTIONS.setupPython}`,
|
|
133
|
+
" with:",
|
|
134
|
+
" python-version: '3.12'",
|
|
135
|
+
" - run: pip install ruff pytest",
|
|
136
|
+
" - run: ruff check .",
|
|
137
|
+
" - run: pytest",
|
|
138
|
+
]),
|
|
139
|
+
go: header([
|
|
140
|
+
` - uses: ${PINNED_ACTIONS.setupGo}`,
|
|
141
|
+
" with:",
|
|
142
|
+
" go-version: stable",
|
|
143
|
+
" - run: go vet ./...",
|
|
144
|
+
" - run: go test ./...",
|
|
145
|
+
]),
|
|
146
|
+
php: header([
|
|
147
|
+
` - uses: ${PINNED_ACTIONS.setupPhp}`,
|
|
148
|
+
" with:",
|
|
149
|
+
" php-version: '8.3'",
|
|
150
|
+
" - run: composer install --no-interaction --no-progress",
|
|
151
|
+
" - run: vendor/bin/phpstan analyse --no-progress",
|
|
152
|
+
" - run: vendor/bin/phpunit",
|
|
153
|
+
]),
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Stack-aware Quality workflow entry, or null for an unknown language.
|
|
157
|
+
* `lint:false` drops the JS lint step entirely (project has no lint script,
|
|
158
|
+
* KJC-BUG-0137) — mirroring the local hook, which also omits it.
|
|
159
|
+
*/
|
|
160
|
+
export function qualityWorkflowFor(language, pm = "npm", lint = true) {
|
|
161
|
+
const c = PM_COMMANDS[pm] || PM_COMMANDS.npm;
|
|
162
|
+
const body =
|
|
163
|
+
language === "javascript" || language === "typescript"
|
|
164
|
+
? header([...nodeSetupSteps(pm), ...(lint ? [` - run: ${c.lint}`] : []), ` - run: ${c.test}`])
|
|
165
|
+
: QUALITY_BY_LANGUAGE[language];
|
|
166
|
+
return body ? { file: "kj-quality.yml", blockId: "wf-quality", body } : null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Caps the net LOC delta of a PR (strict profile only — it is opinionated).
|
|
170
|
+
export const SHRINK_BUDGET_WORKFLOW = [
|
|
171
|
+
"name: Shrink budget",
|
|
172
|
+
"on:",
|
|
173
|
+
" pull_request:",
|
|
174
|
+
" branches: [main]",
|
|
175
|
+
"permissions:",
|
|
176
|
+
" contents: read",
|
|
177
|
+
"jobs:",
|
|
178
|
+
" budget:",
|
|
179
|
+
" runs-on: ubuntu-latest",
|
|
180
|
+
' env: { LOC_LIMIT: "200" }',
|
|
181
|
+
" steps:",
|
|
182
|
+
` - uses: ${PINNED_ACTIONS.checkout}`,
|
|
183
|
+
" with:",
|
|
184
|
+
" fetch-depth: 0",
|
|
185
|
+
" - name: Net LOC delta within budget",
|
|
186
|
+
" env:",
|
|
187
|
+
" BASE_REF: ${{ github.base_ref }}",
|
|
188
|
+
" run: |",
|
|
189
|
+
" d=$(git diff --numstat \"origin/${BASE_REF}...HEAD\" -- . ':!**/*.md' ':!*.lock' ':!package-lock.json' || true)",
|
|
190
|
+
' a=$(printf "%s\\n" "$d" | awk \'$1!="-"{s+=$1}END{print s+0}\')',
|
|
191
|
+
' r=$(printf "%s\\n" "$d" | awk \'$2!="-"{s+=$2}END{print s+0}\')',
|
|
192
|
+
' net=$((a - r)); echo "net=$net (limit=$LOC_LIMIT)"',
|
|
193
|
+
' if [ "$net" -gt "$LOC_LIMIT" ]; then echo "::error::PR adds $net net lines (limit $LOC_LIMIT)"; exit 1; fi',
|
|
194
|
+
].join("\n");
|
|
195
|
+
|
|
196
|
+
// Packs the tarball and installs it clean — only for publishable npm packages.
|
|
197
|
+
// `npm pack`/`npm install <tarball>` need no lockfile, so only the dependency
|
|
198
|
+
// install step is package-manager-aware.
|
|
199
|
+
export const packSmokeWorkflow = (pm = "npm") =>
|
|
200
|
+
[
|
|
201
|
+
"name: Pack smoke",
|
|
202
|
+
"on:",
|
|
203
|
+
" pull_request:",
|
|
204
|
+
" branches: [main]",
|
|
205
|
+
"permissions:",
|
|
206
|
+
" contents: read",
|
|
207
|
+
"jobs:",
|
|
208
|
+
" pack-smoke:",
|
|
209
|
+
" runs-on: ubuntu-latest",
|
|
210
|
+
" steps:",
|
|
211
|
+
` - uses: ${PINNED_ACTIONS.checkout}`,
|
|
212
|
+
...nodeSetupSteps(pm),
|
|
213
|
+
" - name: Pack and install the tarball clean",
|
|
214
|
+
" run: |",
|
|
215
|
+
" tgz=$(npm pack --silent)",
|
|
216
|
+
" dir=$(mktemp -d)",
|
|
217
|
+
' npm install --prefix "$dir" "$PWD/$tgz" --no-audit --no-fund',
|
|
218
|
+
' echo "Tarball installs clean: $tgz"',
|
|
219
|
+
].join("\n");
|
|
220
|
+
|
|
221
|
+
// Opt-in nightly/manual mutation audit (KJC-TSK-0589). NEVER a PR gate: it runs
|
|
222
|
+
// on a weekly schedule + manual dispatch, and the mutate step is non-blocking
|
|
223
|
+
// (continue-on-error) so a red run never nags. Only stacks whose `kj mutate`
|
|
224
|
+
// yields a JSON report (stryker/mutmut/infection) get a workflow — no silent
|
|
225
|
+
// scaffold for tools that can't report yet.
|
|
226
|
+
const MUTATION_TOOLCHAIN = {
|
|
227
|
+
javascript: "node", // resolved per package manager in mutationWorkflowFor
|
|
228
|
+
python: [
|
|
229
|
+
` - uses: ${PINNED_ACTIONS.setupPython}`,
|
|
230
|
+
" with:",
|
|
231
|
+
" python-version: '3.12'",
|
|
232
|
+
" - run: pip install mutmut pytest",
|
|
233
|
+
],
|
|
234
|
+
php: [
|
|
235
|
+
` - uses: ${PINNED_ACTIONS.setupPhp}`,
|
|
236
|
+
" with:",
|
|
237
|
+
" php-version: '8.3'",
|
|
238
|
+
" - run: composer install --no-interaction --no-progress",
|
|
239
|
+
],
|
|
240
|
+
};
|
|
241
|
+
MUTATION_TOOLCHAIN.typescript = MUTATION_TOOLCHAIN.javascript;
|
|
242
|
+
|
|
243
|
+
/** Opt-in mutation workflow entry, or null for a stack without a JSON report. */
|
|
244
|
+
export function mutationWorkflowFor(language, pm = "npm") {
|
|
245
|
+
const entry = MUTATION_TOOLCHAIN[language];
|
|
246
|
+
if (!entry) return null;
|
|
247
|
+
const c = PM_COMMANDS[pm] || PM_COMMANDS.npm;
|
|
248
|
+
const toolchain = entry === "node" ? [...c.setup, ` - run: ${c.install}`] : entry;
|
|
249
|
+
const body = [
|
|
250
|
+
"name: Mutation (nightly)",
|
|
251
|
+
"on:",
|
|
252
|
+
" schedule:",
|
|
253
|
+
" - cron: '0 4 * * 1'",
|
|
254
|
+
" workflow_dispatch:",
|
|
255
|
+
"permissions:",
|
|
256
|
+
" contents: read",
|
|
257
|
+
"jobs:",
|
|
258
|
+
" mutation:",
|
|
259
|
+
" runs-on: ubuntu-latest",
|
|
260
|
+
" steps:",
|
|
261
|
+
` - uses: ${PINNED_ACTIONS.checkout}`,
|
|
262
|
+
" with:",
|
|
263
|
+
" fetch-depth: 0",
|
|
264
|
+
` - uses: ${PINNED_ACTIONS.setupNode}`,
|
|
265
|
+
" with:",
|
|
266
|
+
" node-version: 22",
|
|
267
|
+
...toolchain,
|
|
268
|
+
" - name: Mutation testing (advisory, never blocks a PR)",
|
|
269
|
+
" continue-on-error: true",
|
|
270
|
+
" run: npx --yes --package karajan-code kj mutate --since HEAD~1",
|
|
271
|
+
].join("\n");
|
|
272
|
+
return { file: "kj-mutation.yml", blockId: "wf-mutation", body };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** Conditional workflows: shrink-budget on strict, pack-smoke when publishable. */
|
|
276
|
+
export function extraWorkflowsFor({ profile = "standard", publishable = false, pm = "npm" } = {}) {
|
|
277
|
+
const extras = [];
|
|
278
|
+
if (profile === "strict") {
|
|
279
|
+
extras.push({ file: "kj-shrink-budget.yml", blockId: "wf-shrink", body: SHRINK_BUDGET_WORKFLOW });
|
|
280
|
+
}
|
|
281
|
+
if (publishable) {
|
|
282
|
+
extras.push({ file: "kj-pack-smoke.yml", blockId: "wf-pack-smoke", body: packSmokeWorkflow(pm) });
|
|
283
|
+
}
|
|
284
|
+
return extras;
|
|
285
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HU Acceptance Test Runner.
|
|
3
|
+
* Executes acceptance_tests commands for an HU and returns structured results.
|
|
4
|
+
* Brain uses this to determine if an HU is done (all pass) or needs fixing (with diagnostics).
|
|
5
|
+
*/
|
|
6
|
+
import { runCommand } from "../utils/process.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Run a single acceptance test command.
|
|
10
|
+
* @param {string} cmd - Shell command to execute
|
|
11
|
+
* @param {string} cwd - Working directory
|
|
12
|
+
* @param {number} [timeoutMs=30000] - Timeout per test
|
|
13
|
+
* @returns {Promise<{cmd: string, passed: boolean, output: string, exitCode: number}>}
|
|
14
|
+
*/
|
|
15
|
+
async function runSingleTest(cmd, cwd, timeoutMs = 30000, env = null) {
|
|
16
|
+
try {
|
|
17
|
+
const result = await runCommand("bash", ["-c", cmd], {
|
|
18
|
+
timeout: timeoutMs,
|
|
19
|
+
cwd,
|
|
20
|
+
// PAR-H (KJC-TSK-0631): lane env (KJ_LANE_SLOT / KJ_PORT_OFFSET) so
|
|
21
|
+
// tests that start services can offset their ports. execa merges
|
|
22
|
+
// this on top of process.env.
|
|
23
|
+
...(env ? { env } : {})
|
|
24
|
+
});
|
|
25
|
+
const output = (result.stdout || "") + (result.stderr || "");
|
|
26
|
+
return {
|
|
27
|
+
cmd,
|
|
28
|
+
passed: result.exitCode === 0,
|
|
29
|
+
output: output.slice(-500), // last 500 chars for diagnostics
|
|
30
|
+
exitCode: result.exitCode
|
|
31
|
+
};
|
|
32
|
+
} catch (err) {
|
|
33
|
+
return {
|
|
34
|
+
cmd,
|
|
35
|
+
passed: false,
|
|
36
|
+
output: err.message?.slice(-500) || "Command timed out or crashed",
|
|
37
|
+
exitCode: -1
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Run all acceptance tests for an HU.
|
|
44
|
+
*
|
|
45
|
+
* Accepts two shapes:
|
|
46
|
+
* - Legacy: plain string shell command.
|
|
47
|
+
* - v2.7.5: { type: "shell" | "gherkin", content, file? }
|
|
48
|
+
*
|
|
49
|
+
* Shell entries execute immediately (exit code 0 = pass). Gherkin entries
|
|
50
|
+
* cannot be executed directly — that's Phase 3 of tests-first where the
|
|
51
|
+
* tester role translates them into real code tests. Until then, we report
|
|
52
|
+
* them as "pending" and skip rather than failing the whole gate.
|
|
53
|
+
*
|
|
54
|
+
* @param {Array<string|object>} tests - Array of shell commands OR structured entries
|
|
55
|
+
* @param {string} cwd - Working directory
|
|
56
|
+
* @returns {Promise<{allPassed: boolean, results: object[], summary: string, diagnostics: string|null, pending: number}>}
|
|
57
|
+
*/
|
|
58
|
+
export async function runAcceptanceTests(tests, cwd, { env = null } = {}) {
|
|
59
|
+
if (!tests || tests.length === 0) {
|
|
60
|
+
return { allPassed: false, results: [], summary: "No acceptance tests defined", diagnostics: null, pending: 0 };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const results = [];
|
|
64
|
+
let pending = 0;
|
|
65
|
+
for (const entry of tests) {
|
|
66
|
+
let type = "shell";
|
|
67
|
+
let cmd;
|
|
68
|
+
if (typeof entry === "string") {
|
|
69
|
+
cmd = entry;
|
|
70
|
+
} else if (entry && typeof entry === "object" && typeof entry.content === "string") {
|
|
71
|
+
type = entry.type === "gherkin" ? "gherkin" : "shell";
|
|
72
|
+
cmd = entry.content;
|
|
73
|
+
} else {
|
|
74
|
+
results.push({ cmd: JSON.stringify(entry), passed: false, output: "Malformed acceptance_test entry", exitCode: -2, type: "invalid" });
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (type === "gherkin") {
|
|
78
|
+
// Gherkin entries need a translator (Phase 3). Mark them pending
|
|
79
|
+
// so the coder/tester sees they haven't been validated yet —
|
|
80
|
+
// better than silently passing (would lie) or hard-failing
|
|
81
|
+
// (would block the whole pipeline).
|
|
82
|
+
pending += 1;
|
|
83
|
+
results.push({
|
|
84
|
+
cmd,
|
|
85
|
+
passed: false,
|
|
86
|
+
output: "Gherkin test not yet executable (awaits tester translation — Phase 3)",
|
|
87
|
+
exitCode: 0,
|
|
88
|
+
type: "gherkin",
|
|
89
|
+
pending: true,
|
|
90
|
+
});
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const result = await runSingleTest(cmd, cwd, 30000, env);
|
|
94
|
+
results.push({ ...result, type: "shell" });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const executedResults = results.filter((r) => !r.pending);
|
|
98
|
+
const passed = executedResults.filter((r) => r.passed);
|
|
99
|
+
const failed = executedResults.filter((r) => !r.passed);
|
|
100
|
+
const allPassed = failed.length === 0;
|
|
101
|
+
|
|
102
|
+
const shellSummary = `${passed.length}/${executedResults.length} shell tests passed`;
|
|
103
|
+
const summary = pending > 0
|
|
104
|
+
? `${shellSummary}, ${pending} gherkin test(s) pending translation`
|
|
105
|
+
: shellSummary;
|
|
106
|
+
|
|
107
|
+
let diagnostics = null;
|
|
108
|
+
if (!allPassed) {
|
|
109
|
+
diagnostics = failed.map((f) =>
|
|
110
|
+
`FAIL: ${f.cmd}\n exit=${f.exitCode}\n output: ${f.output.trim().split("\n").slice(-5).join("\n ")}`
|
|
111
|
+
).join("\n\n");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return { allPassed, results, summary, diagnostics, pending };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Build a concrete diagnostic prompt for Brain to send to the coder.
|
|
119
|
+
* Reads the failed test outputs and produces actionable instructions.
|
|
120
|
+
* @param {object[]} failedResults - Array of failed test results
|
|
121
|
+
* @returns {string} Prompt for the coder with concrete fix instructions
|
|
122
|
+
*/
|
|
123
|
+
export function buildDiagnosticPrompt(failedResults) {
|
|
124
|
+
if (!failedResults || failedResults.length === 0) return "";
|
|
125
|
+
const lines = ["The following acceptance tests FAILED. Fix each one:", ""];
|
|
126
|
+
for (const f of failedResults) {
|
|
127
|
+
lines.push(`❌ Command: ${f.cmd}`);
|
|
128
|
+
lines.push(` Exit code: ${f.exitCode}`);
|
|
129
|
+
const lastLines = f.output.trim().split("\n").slice(-8);
|
|
130
|
+
lines.push(` Last output:`);
|
|
131
|
+
for (const l of lastLines) {
|
|
132
|
+
lines.push(` ${l}`);
|
|
133
|
+
}
|
|
134
|
+
lines.push("");
|
|
135
|
+
}
|
|
136
|
+
lines.push("Fix ALL failing tests. Run each command yourself to verify before finishing.");
|
|
137
|
+
return lines.join("\n");
|
|
138
|
+
}
|