@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,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kj audit --agent-readiness — score a repo for AI-agent readability.
|
|
3
|
+
*
|
|
4
|
+
* Static, deterministic, NO LLM call. Inspired by addyosmani/agentic-seo.
|
|
5
|
+
* Catches the failure modes that make repos invisible to Cursor /
|
|
6
|
+
* Cline / Aider / Claude Code:
|
|
7
|
+
* - no llms.txt → agents have to crawl the README
|
|
8
|
+
* - oversized doc pages → agents truncate or drop them
|
|
9
|
+
* - JS-only docs → agents can't render
|
|
10
|
+
* - broken heading hierarchy → agents misinfer structure
|
|
11
|
+
* - no robots allowlist → some agents skip silently
|
|
12
|
+
*
|
|
13
|
+
* Returns a 0-100 score + per-check detail + top remediation hints.
|
|
14
|
+
* Pure data transformation: filesystem reads in, analysis out.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import fs from "node:fs";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import { escapeRegExp } from "../utils/escape-regexp.js";
|
|
20
|
+
|
|
21
|
+
const PAGE_BYTE_LIMIT = 32 * 1024; // ≈ 8K tokens, the typical agent budget per fetch
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Catalogue of checks. Each check has:
|
|
25
|
+
* - id — stable identifier
|
|
26
|
+
* - weight — points contributed when ok
|
|
27
|
+
* - run — (rootDir) => { ok, hint, details }
|
|
28
|
+
*/
|
|
29
|
+
const CHECKS = [
|
|
30
|
+
{
|
|
31
|
+
id: "llms-txt-present",
|
|
32
|
+
weight: 25,
|
|
33
|
+
description: "llms.txt at repo root",
|
|
34
|
+
run(rootDir) {
|
|
35
|
+
const p = path.join(rootDir, "llms.txt");
|
|
36
|
+
const ok = fs.existsSync(p) && fs.statSync(p).isFile();
|
|
37
|
+
return ok
|
|
38
|
+
? { ok: true, hint: null, details: { path: p } }
|
|
39
|
+
: { ok: false, hint: "Add llms.txt at repo root with a single-fetch index of docs + commands.", details: {} };
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "llms-txt-valid",
|
|
44
|
+
weight: 10,
|
|
45
|
+
description: "llms.txt has at least one section heading and one link",
|
|
46
|
+
run(rootDir) {
|
|
47
|
+
const p = path.join(rootDir, "llms.txt");
|
|
48
|
+
if (!fs.existsSync(p)) return { ok: false, hint: "llms.txt missing — see llms-txt-present.", details: {} };
|
|
49
|
+
const text = safeRead(p);
|
|
50
|
+
const hasSection = /^##\s+/m.test(text);
|
|
51
|
+
const hasLink = /\[[^\]]+\]\([^)]+\)/.test(text);
|
|
52
|
+
const ok = hasSection && hasLink;
|
|
53
|
+
return ok
|
|
54
|
+
? { ok: true, hint: null, details: { hasSection, hasLink } }
|
|
55
|
+
: { ok: false, hint: "Add `## Section` headings and `[label](path)` links to llms.txt.", details: { hasSection, hasLink } };
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "robots-allowlist",
|
|
60
|
+
weight: 10,
|
|
61
|
+
description: "robots.txt explicitly Allows AI crawlers",
|
|
62
|
+
run(rootDir) {
|
|
63
|
+
// Check root or public/.
|
|
64
|
+
const candidates = ["robots.txt", "public/robots.txt", "static/robots.txt"];
|
|
65
|
+
const found = candidates.find((c) => fs.existsSync(path.join(rootDir, c)));
|
|
66
|
+
if (!found) return { ok: false, hint: "Add robots.txt with `User-agent: GPTBot|ClaudeBot|PerplexityBot\\nAllow: /` blocks.", details: {} };
|
|
67
|
+
const text = safeRead(path.join(rootDir, found));
|
|
68
|
+
const aiBots = ["GPTBot", "ClaudeBot", "PerplexityBot", "anthropic-ai", "Google-Extended"];
|
|
69
|
+
const allowed = aiBots.filter((bot) => new RegExp(`User-agent:\\s*${escapeRegExp(bot)}[\\s\\S]*?Allow:\\s*/`, "i").test(text));
|
|
70
|
+
const ok = allowed.length > 0;
|
|
71
|
+
return ok
|
|
72
|
+
? { ok: true, hint: null, details: { found, allowed } }
|
|
73
|
+
: { ok: false, hint: `robots.txt found at ${found} but no AI crawler explicitly Allowed. Add explicit User-agent: ClaudeBot blocks.`, details: { found, allowed } };
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: "page-token-budget",
|
|
78
|
+
weight: 15,
|
|
79
|
+
description: "every .md doc fits within ≈ 8K tokens (32 KB raw)",
|
|
80
|
+
run(rootDir) {
|
|
81
|
+
const docsDir = path.join(rootDir, "docs");
|
|
82
|
+
if (!fs.existsSync(docsDir)) {
|
|
83
|
+
// No docs/ at all is its own kind of agent-unfriendly, but
|
|
84
|
+
// not strictly a budget violation. Pass with a hint.
|
|
85
|
+
return { ok: true, hint: "No docs/ directory found. Consider adding one.", details: { docsDirPresent: false } };
|
|
86
|
+
}
|
|
87
|
+
const oversized = listMarkdownFiles(docsDir).filter((f) => fs.statSync(f).size > PAGE_BYTE_LIMIT);
|
|
88
|
+
if (oversized.length === 0) {
|
|
89
|
+
return { ok: true, hint: null, details: { oversizedCount: 0 } };
|
|
90
|
+
}
|
|
91
|
+
const sample = oversized.slice(0, 3).map((f) => `${path.relative(rootDir, f)} (${(fs.statSync(f).size / 1024).toFixed(1)} KB)`);
|
|
92
|
+
return {
|
|
93
|
+
ok: false,
|
|
94
|
+
hint: `Split or trim ${oversized.length} oversized doc(s): ${sample.join(", ")}.`,
|
|
95
|
+
details: { oversizedCount: oversized.length, sample },
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: "heading-hierarchy",
|
|
101
|
+
weight: 10,
|
|
102
|
+
description: "each .md has exactly one H1 and no skipped levels",
|
|
103
|
+
run(rootDir) {
|
|
104
|
+
const docsDir = path.join(rootDir, "docs");
|
|
105
|
+
if (!fs.existsSync(docsDir)) return { ok: true, hint: null, details: { docsDirPresent: false } };
|
|
106
|
+
const violations = [];
|
|
107
|
+
for (const f of listMarkdownFiles(docsDir)) {
|
|
108
|
+
const text = stripFencedCodeBlocks(safeRead(f));
|
|
109
|
+
// Count BOTH markdown `# ` and HTML `<h1>` headings — bilingual
|
|
110
|
+
// README-style files often use a centered `<h1 align="center">`
|
|
111
|
+
// banner, which is still semantically the document's H1.
|
|
112
|
+
const mdH1Count = (text.match(/^#\s+/gm) || []).length;
|
|
113
|
+
const htmlH1Count = (text.match(/<h1[\s>]/gi) || []).length;
|
|
114
|
+
const h1Count = mdH1Count + htmlH1Count;
|
|
115
|
+
if (h1Count !== 1) {
|
|
116
|
+
violations.push({ file: path.relative(rootDir, f), h1Count, mdH1Count, htmlH1Count });
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
// Check for skipped levels (#### without preceding ###, etc.)
|
|
120
|
+
const headings = [...text.matchAll(/^(#{1,6})\s/gm)].map((m) => m[1].length);
|
|
121
|
+
let prev = 0;
|
|
122
|
+
for (const lvl of headings) {
|
|
123
|
+
if (prev > 0 && lvl > prev + 1) {
|
|
124
|
+
violations.push({ file: path.relative(rootDir, f), reason: `H${prev}→H${lvl} skip` });
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
prev = lvl;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (violations.length === 0) {
|
|
131
|
+
return { ok: true, hint: null, details: { violationsCount: 0 } };
|
|
132
|
+
}
|
|
133
|
+
const sample = violations.slice(0, 3);
|
|
134
|
+
return {
|
|
135
|
+
ok: false,
|
|
136
|
+
hint: `Fix heading hierarchy in ${violations.length} doc(s): ${sample.map((v) => v.file).join(", ")}.`,
|
|
137
|
+
details: { violationsCount: violations.length, sample },
|
|
138
|
+
};
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: "agents-readme",
|
|
143
|
+
weight: 10,
|
|
144
|
+
description: "docs/agents/README.md as agent entry point",
|
|
145
|
+
run(rootDir) {
|
|
146
|
+
const p = path.join(rootDir, "docs/agents/README.md");
|
|
147
|
+
const ok = fs.existsSync(p);
|
|
148
|
+
return ok
|
|
149
|
+
? { ok: true, hint: null, details: { path: p } }
|
|
150
|
+
: { ok: false, hint: "Create docs/agents/README.md as the agent-facing index (links from llms.txt).", details: {} };
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: "skill-md-coverage",
|
|
155
|
+
weight: 20,
|
|
156
|
+
description: "at least one SKILL.md per declared CLI capability",
|
|
157
|
+
run(rootDir) {
|
|
158
|
+
const skillDir = path.join(rootDir, "docs/agents");
|
|
159
|
+
if (!fs.existsSync(skillDir)) return { ok: false, hint: "Create docs/agents/ with one SKILL.<command>.md per CLI capability.", details: {} };
|
|
160
|
+
const skillFiles = fs.readdirSync(skillDir).filter((f) => /^SKILL\..+\.md$/.test(f));
|
|
161
|
+
if (skillFiles.length === 0) {
|
|
162
|
+
return { ok: false, hint: "Add SKILL.<command>.md files documenting each CLI capability.", details: { skillFilesCount: 0 } };
|
|
163
|
+
}
|
|
164
|
+
// We don't enumerate the user's own CLI here (out of scope for a
|
|
165
|
+
// generic auditor). At least 1 SKILL.md is the minimum signal.
|
|
166
|
+
return { ok: true, hint: null, details: { skillFilesCount: skillFiles.length } };
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
function safeRead(p) {
|
|
172
|
+
try { return fs.readFileSync(p, "utf8"); } catch { return ""; }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Replace every fenced code block (```…``` or ~~~…~~~) with blank lines
|
|
177
|
+
* of the same line count so heading regexes don't accidentally match
|
|
178
|
+
* shell comments inside bash code blocks (`# install foo`). Preserving
|
|
179
|
+
* line counts keeps later byte-offset checks stable.
|
|
180
|
+
*/
|
|
181
|
+
function stripFencedCodeBlocks(text) {
|
|
182
|
+
return text.replaceAll(/^([ \t]*)(```|~~~)[^\n]*\n([\s\S]*?)\n\1\2[^\n]*$/gm, (match) => {
|
|
183
|
+
const lineCount = match.split("\n").length;
|
|
184
|
+
return "\n".repeat(lineCount - 1);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function listMarkdownFiles(dir, out = []) {
|
|
189
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
190
|
+
if (e.name.startsWith(".") || e.name === "node_modules") continue;
|
|
191
|
+
const p = path.join(dir, e.name);
|
|
192
|
+
if (e.isDirectory()) listMarkdownFiles(p, out);
|
|
193
|
+
else if (e.isFile() && e.name.endsWith(".md")) out.push(p);
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Run every agent-readiness check against `rootDir`. Returns a
|
|
200
|
+
* report with score, per-check results, and ranked remediation hints.
|
|
201
|
+
*
|
|
202
|
+
* @param {string} rootDir — absolute path to the repo root to audit
|
|
203
|
+
* @returns {{ score: number, checks: object[], topFixes: object[] }}
|
|
204
|
+
*/
|
|
205
|
+
export function runAgentReadiness(rootDir) {
|
|
206
|
+
const checks = [];
|
|
207
|
+
let earned = 0;
|
|
208
|
+
let possible = 0;
|
|
209
|
+
for (const c of CHECKS) {
|
|
210
|
+
possible += c.weight;
|
|
211
|
+
let result;
|
|
212
|
+
try {
|
|
213
|
+
result = c.run(rootDir);
|
|
214
|
+
} catch (err) {
|
|
215
|
+
result = { ok: false, hint: `Check threw: ${err.message}`, details: { error: err.message } };
|
|
216
|
+
}
|
|
217
|
+
if (result.ok) earned += c.weight;
|
|
218
|
+
checks.push({
|
|
219
|
+
id: c.id,
|
|
220
|
+
description: c.description,
|
|
221
|
+
weight: c.weight,
|
|
222
|
+
score: result.ok ? c.weight : 0,
|
|
223
|
+
ok: result.ok,
|
|
224
|
+
hint: result.hint,
|
|
225
|
+
details: result.details || {},
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
const score = possible === 0 ? 0 : Math.round((earned / possible) * 100);
|
|
229
|
+
const topFixes = checks
|
|
230
|
+
.filter((c) => !c.ok && c.hint)
|
|
231
|
+
.sort((a, b) => b.weight - a.weight)
|
|
232
|
+
.slice(0, 5)
|
|
233
|
+
.map((c, i) => ({
|
|
234
|
+
priority: i + 1,
|
|
235
|
+
checkId: c.id,
|
|
236
|
+
weightLost: c.weight,
|
|
237
|
+
action: c.hint,
|
|
238
|
+
}));
|
|
239
|
+
return { score, possible, earned, checks, topFixes };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Render the report in plain text for the human terminal.
|
|
244
|
+
*/
|
|
245
|
+
export function formatAgentReadinessReport({ score, checks, topFixes }, rootDir) {
|
|
246
|
+
const lines = [`Agent Readiness — ${rootDir}: ${score}/100`, ""];
|
|
247
|
+
for (const c of checks) {
|
|
248
|
+
const tick = c.ok ? "✓" : "✗";
|
|
249
|
+
const pts = `(+${c.score})`.padStart(6);
|
|
250
|
+
lines.push(` ${tick} ${c.description.padEnd(50)} ${pts}${c.hint ? " " + c.hint : ""}`);
|
|
251
|
+
}
|
|
252
|
+
if (topFixes.length > 0) {
|
|
253
|
+
lines.push("", "Top fixes:");
|
|
254
|
+
for (const f of topFixes) {
|
|
255
|
+
lines.push(` ${f.priority}. [${f.checkId}] ${f.action}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return lines.join("\n");
|
|
259
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// AI-slop tells detector — KJC-TSK-0503. Deterministic, offline,
|
|
2
|
+
// regex-based scanner over JS/TS source. Five categories:
|
|
3
|
+
// tautologic-comments, banner-separators, boilerplate-docstrings,
|
|
4
|
+
// magic-fallbacks, redundant-jsdoc. Score 100 = clean.
|
|
5
|
+
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
|
|
9
|
+
const FILE_EXTS = new Set([".js", ".mjs", ".cjs", ".ts", ".tsx", ".jsx"]);
|
|
10
|
+
const IGNORE_DIRS = new Set([
|
|
11
|
+
"node_modules", "dist", "build", "coverage",
|
|
12
|
+
".git", ".karajan", "public", ".next", ".nuxt", ".vercel", ".cache",
|
|
13
|
+
]);
|
|
14
|
+
const VERB = "(?:returns?|gets?|sets?|fetches?|loads?|saves?|creates?|builds?|makes?|computes?|handles?|checks?|validates?)";
|
|
15
|
+
const LINE_PATTERNS = {
|
|
16
|
+
"banner-separators": /\/[/*]\s*[=*\-_#~]{5,}/,
|
|
17
|
+
"magic-fallbacks": /(\?\?|\|\|)\s*(?:""|''|null|undefined)\s*(?:\)|;|,|$)/,
|
|
18
|
+
"redundant-jsdoc": new RegExp(`@param\\s+\\{[^}]+\\}\\s+(\\w+)\\s+(?:-\\s*)?(?:the\\s+)?\\1\\s+(?:parameter|argument|value)`, "i"),
|
|
19
|
+
};
|
|
20
|
+
const TAUT_RE = new RegExp(`^\\s*//\\s*${VERB}\\s+(?:the\\s+|a\\s+|an\\s+)?(\\w+)\\s*\\.?\\s*$`, "i");
|
|
21
|
+
const DOC_RE = new RegExp(`^\\s*/\\*\\*\\s*${VERB}\\s+(?:the\\s+|a\\s+|an\\s+)?(\\w+)\\s*\\.?\\s*\\*/\\s*$`, "i");
|
|
22
|
+
const DECL_RE = /^\s*(?:export\s+)?(?:default\s+)?(?:async\s+)?(?:function|const|let|var|class)\s+(\w+)/;
|
|
23
|
+
|
|
24
|
+
export const CATEGORIES = Object.freeze([
|
|
25
|
+
"tautologic-comments", "banner-separators", "boilerplate-docstrings",
|
|
26
|
+
"magic-fallbacks", "redundant-jsdoc",
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
function nextDecl(lines, from) {
|
|
30
|
+
for (let j = from; j < Math.min(from + 3, lines.length); j++) {
|
|
31
|
+
const m = lines[j]?.match(DECL_RE);
|
|
32
|
+
if (m) return m[1];
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const stem = (a, b) => { const al = a.toLowerCase(), bl = b.toLowerCase(); return al === bl || al.includes(bl) || bl.includes(al); };
|
|
37
|
+
|
|
38
|
+
export function scanText(relPath, text) {
|
|
39
|
+
const out = [];
|
|
40
|
+
const lines = text.split(/\r?\n/);
|
|
41
|
+
for (let i = 0; i < lines.length; i++) {
|
|
42
|
+
const line = lines[i];
|
|
43
|
+
const lineNo = i + 1;
|
|
44
|
+
for (const [category, re] of Object.entries(LINE_PATTERNS)) {
|
|
45
|
+
if (re.test(line)) out.push({ path: relPath, line: lineNo, category, snippet: line.trim().slice(0, 100) });
|
|
46
|
+
}
|
|
47
|
+
const t = line.match(TAUT_RE);
|
|
48
|
+
if (t) {
|
|
49
|
+
const decl = nextDecl(lines, i + 1);
|
|
50
|
+
if (decl && stem(decl, t[1])) out.push({ path: relPath, line: lineNo, category: "tautologic-comments", snippet: line.trim().slice(0, 100) });
|
|
51
|
+
}
|
|
52
|
+
const d = line.match(DOC_RE);
|
|
53
|
+
if (d) {
|
|
54
|
+
const decl = nextDecl(lines, i + 1);
|
|
55
|
+
if (decl && stem(decl, d[1])) out.push({ path: relPath, line: lineNo, category: "boilerplate-docstrings", snippet: line.trim().slice(0, 100) });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function listSourceFiles(root) {
|
|
62
|
+
const out = [];
|
|
63
|
+
const stack = [root];
|
|
64
|
+
while (stack.length) {
|
|
65
|
+
const dir = stack.pop();
|
|
66
|
+
let entries;
|
|
67
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { continue; }
|
|
68
|
+
for (const e of entries) {
|
|
69
|
+
const full = path.join(dir, e.name);
|
|
70
|
+
if (e.isDirectory()) {
|
|
71
|
+
if (IGNORE_DIRS.has(e.name) || e.name.startsWith(".")) continue;
|
|
72
|
+
stack.push(full);
|
|
73
|
+
} else if (FILE_EXTS.has(path.extname(e.name))) out.push(full);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function collectAiSlop(rootDir, opts = {}) {
|
|
80
|
+
const maxLOC = Number.isFinite(opts.maxLOC) ? opts.maxLOC : 200_000;
|
|
81
|
+
const maxWorst = Number.isFinite(opts.maxWorst) ? opts.maxWorst : 10;
|
|
82
|
+
try {
|
|
83
|
+
const findings = [];
|
|
84
|
+
let totalLOC = 0, filesScanned = 0;
|
|
85
|
+
for (const file of listSourceFiles(rootDir)) {
|
|
86
|
+
if (totalLOC >= maxLOC) break;
|
|
87
|
+
let content;
|
|
88
|
+
try { content = fs.readFileSync(file, "utf-8"); } catch { continue; }
|
|
89
|
+
findings.push(...scanText(path.relative(rootDir, file) || file, content));
|
|
90
|
+
totalLOC += content.split(/\r?\n/).length;
|
|
91
|
+
filesScanned++;
|
|
92
|
+
}
|
|
93
|
+
const byCategory = Object.fromEntries(CATEGORIES.map((c) => [c, 0]));
|
|
94
|
+
for (const f of findings) byCategory[f.category]++;
|
|
95
|
+
const density = totalLOC > 0 ? (findings.length / totalLOC) * 100 : 0;
|
|
96
|
+
const score = Math.max(0, Math.min(100, Math.round(100 - density * 100)));
|
|
97
|
+
const worst = findings.slice().sort((a, b) => (a.path === b.path ? a.line - b.line : a.path.localeCompare(b.path))).slice(0, maxWorst);
|
|
98
|
+
return { available: true, filesScanned, totalLOC, total: findings.length, score, byCategory, worst };
|
|
99
|
+
} catch (err) {
|
|
100
|
+
return { available: false, reason: err?.message || String(err) };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Resilient provider/model fallback for the `kj audit` CLI (KJC-BUG-0094).
|
|
2
|
+
// The CLI audit calls AuditRole.executeWithDeterministic directly, bypassing
|
|
3
|
+
// the orchestrator's recovery paths, so a dead configured model (e.g. an
|
|
4
|
+
// inherited "claude-fable-5") killed the command. The LLM phase now tries:
|
|
5
|
+
// configured provider+model → same provider default model → remaining known
|
|
6
|
+
// providers. First success wins; the deterministic context is reused.
|
|
7
|
+
import { resolveRole } from "../config.js";
|
|
8
|
+
import { AuditRole } from "../roles/audit-role.js";
|
|
9
|
+
|
|
10
|
+
const KNOWN_PROVIDERS = ["claude", "codex", "gemini"];
|
|
11
|
+
|
|
12
|
+
/** Ordered, de-duplicated fallback candidates for the audit role. */
|
|
13
|
+
export function buildAuditFallbackCandidates(config) {
|
|
14
|
+
const effective = resolveRole(config, "audit");
|
|
15
|
+
const primary = effective.provider || "claude";
|
|
16
|
+
const candidates = [{ provider: primary, model: effective.model ?? null }];
|
|
17
|
+
if (effective.model) candidates.push({ provider: primary, model: null });
|
|
18
|
+
for (const p of KNOWN_PROVIDERS) {
|
|
19
|
+
if (p !== primary) candidates.push({ provider: p, model: null });
|
|
20
|
+
}
|
|
21
|
+
const seen = new Set();
|
|
22
|
+
return candidates.filter((c) => {
|
|
23
|
+
const key = `${c.provider}::${c.model ?? ""}`;
|
|
24
|
+
return seen.has(key) ? false : (seen.add(key), true);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Pin the audit role to a provider/model. Sets BOTH the per-role override and
|
|
29
|
+
// `coder_options.model` (the audit role inherits its model from the coder
|
|
30
|
+
// bucket); a null model neutralizes both so the provider default is used.
|
|
31
|
+
export function withAuditProvider(config, provider, model) {
|
|
32
|
+
return {
|
|
33
|
+
...config,
|
|
34
|
+
roles: { ...(config?.roles || {}), audit: { ...(config?.roles?.audit || {}), provider, model: model ?? undefined } },
|
|
35
|
+
coder_options: { ...(config?.coder_options || {}), model: model ?? undefined },
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Run the audit LLM phase with automatic provider/model fallback. */
|
|
40
|
+
export async function runAuditWithFallback({
|
|
41
|
+
config, logger, roleInput, deterministicCtx, available, candidates, onFallback, createRole,
|
|
42
|
+
}) {
|
|
43
|
+
const makeRole = createRole || ((cfg) => new AuditRole({ config: cfg, logger }));
|
|
44
|
+
const all = candidates || buildAuditFallbackCandidates(config);
|
|
45
|
+
const usable = available ? all.filter((c) => available.includes(c.provider)) : all;
|
|
46
|
+
if (usable.length === 0) {
|
|
47
|
+
throw new Error("No available audit provider — install claude, codex, or gemini, or set roles.audit.provider");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let last = null;
|
|
51
|
+
let attempts = 0;
|
|
52
|
+
for (const cand of usable) {
|
|
53
|
+
attempts += 1;
|
|
54
|
+
if (attempts > 1) onFallback?.(cand, attempts);
|
|
55
|
+
const role = makeRole(withAuditProvider(config, cand.provider, cand.model));
|
|
56
|
+
const meta = { provider: cand.provider, model: cand.model ?? null };
|
|
57
|
+
try {
|
|
58
|
+
const result = await role.executeWithDeterministic(roleInput, deterministicCtx);
|
|
59
|
+
if (result?.ok) return { ...result, ...meta, attempts };
|
|
60
|
+
last = { ...result, ...meta };
|
|
61
|
+
} catch (err) {
|
|
62
|
+
last = { ok: false, result: { error: err.message, provider: cand.provider }, summary: `Audit failed: ${err.message}`, ...meta };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { ...last, attempts, exhausted: true };
|
|
66
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// KJC-TSK-0473 — Pure display helpers for audit history (diff + sparkline).
|
|
2
|
+
// No DB / native-module deps; safe to bundle into the SEA binary.
|
|
3
|
+
|
|
4
|
+
const SPARK_BARS = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"];
|
|
5
|
+
|
|
6
|
+
function scoreOf(c) {
|
|
7
|
+
if (!c || typeof c !== "object") return null;
|
|
8
|
+
return typeof c.score === "number" ? c.score : typeof c.percent === "number" ? c.percent : null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function computeHistoryDiff(currentHarness, previousRow) {
|
|
12
|
+
if (!currentHarness || typeof currentHarness.score !== "number") return null;
|
|
13
|
+
if (!previousRow || typeof previousRow.score !== "number") return { previousScore: null, firstRun: true };
|
|
14
|
+
let prevCats = {};
|
|
15
|
+
try { prevCats = previousRow.categories_json ? JSON.parse(previousRow.categories_json) || {} : {}; } catch { /* keep empty */ }
|
|
16
|
+
const currCats = currentHarness.categories || {};
|
|
17
|
+
const categoryDeltas = [];
|
|
18
|
+
for (const name of Object.keys(currCats)) {
|
|
19
|
+
const cur = scoreOf(currCats[name]);
|
|
20
|
+
const prev = scoreOf(prevCats[name]);
|
|
21
|
+
if (typeof cur === "number" && typeof prev === "number") {
|
|
22
|
+
categoryDeltas.push({ name, current: cur, previous: prev, delta: cur - prev });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const sorted = [...categoryDeltas].sort((a, b) => b.delta - a.delta);
|
|
26
|
+
const t = previousRow.timestamp ? Date.parse(previousRow.timestamp) : NaN;
|
|
27
|
+
const ageDays = Number.isNaN(t) ? null : Math.floor((Date.now() - t) / 86400000);
|
|
28
|
+
return {
|
|
29
|
+
previousScore: previousRow.score,
|
|
30
|
+
previousGrade: previousRow.grade || null,
|
|
31
|
+
previousTimestamp: previousRow.timestamp,
|
|
32
|
+
delta: currentHarness.score - previousRow.score,
|
|
33
|
+
categoryDeltas,
|
|
34
|
+
biggestImprovement: sorted.find((c) => c.delta > 0) || null,
|
|
35
|
+
biggestRegression: [...sorted].reverse().find((c) => c.delta < 0) || null,
|
|
36
|
+
baselineAgeDays: ageDays,
|
|
37
|
+
baselineStale: ageDays != null && ageDays > 30,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function formatHistoryDiff(diff) {
|
|
42
|
+
if (!diff) return "";
|
|
43
|
+
if (diff.firstRun) return "_First run for this project — no baseline yet._";
|
|
44
|
+
const arrow = diff.delta > 0 ? "↑" : diff.delta < 0 ? "↓" : "=";
|
|
45
|
+
const sign = diff.delta > 0 ? "+" : "";
|
|
46
|
+
const prevDate = (diff.previousTimestamp || "").slice(0, 10);
|
|
47
|
+
const lines = [`**Δ ${sign}${diff.delta} ${arrow} vs run anterior** (${prevDate}, ${diff.previousScore}/100 ${diff.previousGrade || ""})`];
|
|
48
|
+
if (diff.baselineStale) lines.push(`> ⚠ baseline antigua (${diff.baselineAgeDays} días) — considera ejecutar \`kj audit\` con más frecuencia.`);
|
|
49
|
+
if (diff.biggestImprovement) lines.push(`- Mayor mejora: **${diff.biggestImprovement.name}** (+${diff.biggestImprovement.delta})`);
|
|
50
|
+
if (diff.biggestRegression) lines.push(`- Mayor regresión: **${diff.biggestRegression.name}** (${diff.biggestRegression.delta})`);
|
|
51
|
+
return lines.join("\n");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function formatTrendSparkline(scores) {
|
|
55
|
+
const xs = (scores || []).map((r) => (typeof r === "number" ? r : r?.score)).filter((n) => typeof n === "number");
|
|
56
|
+
if (xs.length === 0) return "";
|
|
57
|
+
if (xs.length === 1) return `Score trend: ${xs[0]} (single run)`;
|
|
58
|
+
const min = Math.min(...xs), max = Math.max(...xs), range = max - min || 1;
|
|
59
|
+
const bars = xs.map((n) => SPARK_BARS[Math.min(SPARK_BARS.length - 1, Math.floor(((n - min) / range) * (SPARK_BARS.length - 1)))]).join("");
|
|
60
|
+
return `Score trend (last ${xs.length} runs): ${xs[0]} ${bars} ${xs[xs.length - 1]}`;
|
|
61
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// KJC-TSK-0472 — Per-project audit history (sqlite at .karajan/audit-history.db).
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import Database from "better-sqlite3";
|
|
5
|
+
|
|
6
|
+
const SCHEMA_VERSION = 1;
|
|
7
|
+
|
|
8
|
+
export function getAuditHistoryDbPath(projectDir) {
|
|
9
|
+
return path.join(projectDir, ".karajan", "audit-history.db");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function openAuditHistoryDb(projectDir) {
|
|
13
|
+
const dbPath = getAuditHistoryDbPath(projectDir);
|
|
14
|
+
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
15
|
+
const db = new Database(dbPath);
|
|
16
|
+
db.pragma("journal_mode = WAL");
|
|
17
|
+
migrate(db);
|
|
18
|
+
return db;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function migrate(db) {
|
|
22
|
+
const current = db.pragma("user_version", { simple: true });
|
|
23
|
+
if (current >= SCHEMA_VERSION) return;
|
|
24
|
+
db.exec(`
|
|
25
|
+
CREATE TABLE IF NOT EXISTS audits (
|
|
26
|
+
run_id TEXT PRIMARY KEY,
|
|
27
|
+
timestamp TEXT NOT NULL,
|
|
28
|
+
git_sha TEXT,
|
|
29
|
+
score INTEGER,
|
|
30
|
+
grade TEXT,
|
|
31
|
+
categories_json TEXT,
|
|
32
|
+
raw_report_path TEXT
|
|
33
|
+
);
|
|
34
|
+
CREATE TABLE IF NOT EXISTS findings (
|
|
35
|
+
run_id TEXT NOT NULL,
|
|
36
|
+
category TEXT,
|
|
37
|
+
check_id TEXT,
|
|
38
|
+
status TEXT,
|
|
39
|
+
weight REAL,
|
|
40
|
+
FOREIGN KEY(run_id) REFERENCES audits(run_id) ON DELETE CASCADE
|
|
41
|
+
);
|
|
42
|
+
CREATE INDEX IF NOT EXISTS idx_audits_timestamp ON audits(timestamp DESC);
|
|
43
|
+
CREATE INDEX IF NOT EXISTS idx_findings_run_id ON findings(run_id);
|
|
44
|
+
`);
|
|
45
|
+
db.pragma(`user_version = ${SCHEMA_VERSION}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// `harness` may be null (--no-harness / docker offline): row still inserted with NULL score.
|
|
49
|
+
export function recordAuditRun(db, { runId, timestamp, gitSha, harness, rawReportPath }) {
|
|
50
|
+
const categories = harness?.categories ? JSON.stringify(harness.categories) : null;
|
|
51
|
+
const checks = Array.isArray(harness?.checks) ? harness.checks : [];
|
|
52
|
+
const insertRun = db.prepare(`INSERT INTO audits (run_id, timestamp, git_sha, score, grade, categories_json, raw_report_path) VALUES (?, ?, ?, ?, ?, ?, ?)`);
|
|
53
|
+
const insertFinding = db.prepare(`INSERT INTO findings (run_id, category, check_id, status, weight) VALUES (?, ?, ?, ?, ?)`);
|
|
54
|
+
db.transaction(() => {
|
|
55
|
+
insertRun.run(runId, timestamp, gitSha || null, harness?.score ?? null, harness?.grade ?? null, categories, rawReportPath || null);
|
|
56
|
+
for (const c of checks) {
|
|
57
|
+
insertFinding.run(runId, c.category || null, c.name || c.id || null, c.passed ? "pass" : "fail", typeof c.weight === "number" ? c.weight : null);
|
|
58
|
+
}
|
|
59
|
+
})();
|
|
60
|
+
return { runId, inserted: 1 + checks.length };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function listRecentRuns(db, limit = 10) {
|
|
64
|
+
return db.prepare(`SELECT run_id, timestamp, git_sha, score, grade FROM audits ORDER BY timestamp DESC LIMIT ?`).all(limit);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// KJC-TSK-0473 — Fetch previous run (excluding current) for diff rendering.
|
|
68
|
+
export function getLatestPreviousRun(db, currentTimestamp) {
|
|
69
|
+
return db.prepare(`SELECT run_id, timestamp, git_sha, score, grade, categories_json FROM audits WHERE timestamp < ? AND score IS NOT NULL ORDER BY timestamp DESC LIMIT 1`).get(currentTimestamp || new Date().toISOString()) || null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// KJC-TSK-0473 — Score timeline (ASC) for sparkline rendering.
|
|
73
|
+
export function getRecentScores(db, limit = 10) {
|
|
74
|
+
const rows = db.prepare(`SELECT timestamp, score FROM audits WHERE score IS NOT NULL ORDER BY timestamp DESC LIMIT ?`).all(limit);
|
|
75
|
+
return rows.reverse();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function countRuns(db) {
|
|
79
|
+
return db.prepare(`SELECT COUNT(*) AS n FROM audits`).get().n;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function pruneOldRuns(db, keep = 50) {
|
|
83
|
+
const ids = db.prepare(`SELECT run_id FROM audits ORDER BY timestamp DESC LIMIT -1 OFFSET ?`).all(keep).map((r) => r.run_id);
|
|
84
|
+
if (ids.length === 0) return 0;
|
|
85
|
+
const ph = ids.map(() => "?").join(",");
|
|
86
|
+
db.transaction(() => { db.prepare(`DELETE FROM findings WHERE run_id IN (${ph})`).run(...ids); db.prepare(`DELETE FROM audits WHERE run_id IN (${ph})`).run(...ids); })();
|
|
87
|
+
return ids.length;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// High-level helper used by `kj audit`. Errors returned via `ok` so a broken db never breaks the audit.
|
|
91
|
+
export function persistAuditRun(projectDir, payload) {
|
|
92
|
+
try {
|
|
93
|
+
const db = openAuditHistoryDb(projectDir);
|
|
94
|
+
recordAuditRun(db, payload);
|
|
95
|
+
const n = countRuns(db);
|
|
96
|
+
db.close();
|
|
97
|
+
return { ok: true, total: n, pruneHint: n > 100 };
|
|
98
|
+
} catch (e) {
|
|
99
|
+
return { ok: false, error: e?.message || String(e) };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|