@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,281 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { execFile } from "node:child_process";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { getKarajanHome } from "../utils/paths.js";
|
|
6
|
+
import { sniffManifests, summariseStack } from "../utils/manifest-sniff.js";
|
|
7
|
+
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
|
|
10
|
+
const SOURCE_EXTENSIONS = new Set([
|
|
11
|
+
".js", ".mjs", ".cjs", ".ts", ".mts", ".cts", ".tsx", ".jsx",
|
|
12
|
+
".py", ".rb", ".go", ".rs", ".java", ".kt", ".swift", ".c", ".cpp", ".h",
|
|
13
|
+
".vue", ".svelte", ".astro", ".php", ".cs", ".sh"
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
const EXCLUDE_DIRS = new Set([
|
|
17
|
+
"node_modules", ".git", "dist", "build", "coverage", ".next",
|
|
18
|
+
".nuxt", ".output", "__pycache__", ".cache", ".parcel-cache"
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
function slugify(projectDir) {
|
|
22
|
+
return path.basename(projectDir).replaceAll(/[^a-zA-Z0-9_-]/g, "_");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function walkSourceFiles(dir) {
|
|
26
|
+
// Use git ls-files for speed (instant vs recursive walk)
|
|
27
|
+
try {
|
|
28
|
+
const { stdout } = await execFileAsync("git", ["ls-files", "--cached", "--others", "--exclude-standard"], { cwd: dir, encoding: "utf8", timeout: 5000 });
|
|
29
|
+
return stdout.trim().split("\n")
|
|
30
|
+
.filter(f => f && SOURCE_EXTENSIONS.has(path.extname(f)) && !EXCLUDE_DIRS.has(f.split("/")[0]))
|
|
31
|
+
.map(f => path.join(dir, f));
|
|
32
|
+
} catch {
|
|
33
|
+
// Fallback to recursive walk if not a git repo
|
|
34
|
+
const files = [];
|
|
35
|
+
let entries;
|
|
36
|
+
try {
|
|
37
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
38
|
+
} catch { /* directory not readable */
|
|
39
|
+
return files;
|
|
40
|
+
}
|
|
41
|
+
for (const entry of entries) {
|
|
42
|
+
if (EXCLUDE_DIRS.has(entry.name)) continue;
|
|
43
|
+
const full = path.join(dir, entry.name);
|
|
44
|
+
if (entry.isDirectory()) {
|
|
45
|
+
files.push(...await walkSourceFiles(full));
|
|
46
|
+
} else if (SOURCE_EXTENSIONS.has(path.extname(entry.name))) {
|
|
47
|
+
files.push(full);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return files;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function countLines(filePath) {
|
|
55
|
+
try {
|
|
56
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
57
|
+
return content.split("\n").length;
|
|
58
|
+
} catch {
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function countDependenciesFromStack(stack) {
|
|
64
|
+
// Suma deps + devDeps de TODOS los manifests detectados (multi-stack).
|
|
65
|
+
// Si no hay manifests, devuelve ceros — coherente con repos sin paquete.
|
|
66
|
+
let deps = 0;
|
|
67
|
+
let devDeps = 0;
|
|
68
|
+
for (const m of stack.manifests) {
|
|
69
|
+
deps += m.deps;
|
|
70
|
+
devDeps += m.devDeps;
|
|
71
|
+
}
|
|
72
|
+
return { dependencies: deps, devDependencies: devDeps, total: deps + devDeps };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function runDepcheck(projectDir) {
|
|
76
|
+
return new Promise((resolve) => {
|
|
77
|
+
execFile("npx", ["depcheck", "--json"], { cwd: projectDir, timeout: 30000 }, (err, stdout) => {
|
|
78
|
+
if (err && !stdout) {
|
|
79
|
+
resolve({ unused: [], note: "depcheck not available or failed" });
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const result = JSON.parse(stdout);
|
|
84
|
+
const unused = [
|
|
85
|
+
...(result.dependencies || []),
|
|
86
|
+
...(result.devDependencies || [])
|
|
87
|
+
];
|
|
88
|
+
resolve({ unused });
|
|
89
|
+
} catch {
|
|
90
|
+
resolve({ unused: [], note: "depcheck output not parseable" });
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Regexes hoisted to module scope so they're compiled once instead of per file.
|
|
97
|
+
// Naming: *Re catches static/syntactic patterns; we still rely on the import set
|
|
98
|
+
// being a superset (false negative > false positive in audit context).
|
|
99
|
+
const EXPORT_RE = /export\s+(?:default\s+)?(?:function|class|const|let|var|async\s+function)\s+(\w+)/g;
|
|
100
|
+
const NAMED_EXPORT_RE = /export\s*\{([^}]+)\}/g;
|
|
101
|
+
const STATIC_NAMED_IMPORT_RE = /import\s+\{([^}]+)\}\s+from/g;
|
|
102
|
+
const DEFAULT_IMPORT_RE = /import\s+(\w+)\s+from/g;
|
|
103
|
+
// Namespace import (`import * as ns from <path>`) — knip-equivalent: mark
|
|
104
|
+
// the target file as fully-consumed because we cannot statically know
|
|
105
|
+
// which `ns.X` accesses happen.
|
|
106
|
+
const NAMESPACE_IMPORT_RE = /import\s+\*\s+as\s+\w+\s+from\s+["']([^"']+)["']/g;
|
|
107
|
+
// Any dynamic `await import(<path>)` — destructured, late-bound, or
|
|
108
|
+
// member-accessed. Conservatively mark the target as fully-consumed:
|
|
109
|
+
// trying to enumerate downstream destructuring/member-access patterns is
|
|
110
|
+
// fragile (the same test may use parenthesised assignment with no const,
|
|
111
|
+
// or bind first and `.foo`-access later). A false-negative here (one
|
|
112
|
+
// extra "live" export we can't prove dead) is always cheaper than a
|
|
113
|
+
// false-positive in audit context.
|
|
114
|
+
const DYNAMIC_IMPORT_RE = /\bawait\s+import\s*\(\s*["']([^"']+)["']\s*\)/g;
|
|
115
|
+
// Re-export bridges (`export { x } from <path>` and `export * from <path>`)
|
|
116
|
+
// — both consume from the target file (the re-exporter is itself another
|
|
117
|
+
// file consumer downstream).
|
|
118
|
+
const RE_EXPORT_NAMED_RE = /export\s*\{([^}]+)\}\s*from\s*["']([^"']+)["']/g;
|
|
119
|
+
const RE_EXPORT_STAR_RE = /export\s*\*\s*from\s*["']([^"']+)["']/g;
|
|
120
|
+
// Export preceded by a JSDoc block with `@internal` is opt-out: documented as
|
|
121
|
+
// dynamic-import-only or test-fixture surface that the regex passes can't see.
|
|
122
|
+
const INTERNAL_EXPORT_RE = /\/\*\*[\s\S]*?@internal[\s\S]*?\*\/\s*export\s+(?:default\s+)?(?:function|class|const|let|var|async\s+function)\s+(\w+)/g;
|
|
123
|
+
|
|
124
|
+
function resolveImportSpecifier(fromFile, specifier) {
|
|
125
|
+
// Only resolve project-relative paths; bare specifiers (npm packages) and
|
|
126
|
+
// node: builtins are not in our exportMap and don't matter here.
|
|
127
|
+
if (!specifier.startsWith(".") && !specifier.startsWith("/")) return null;
|
|
128
|
+
const base = path.resolve(path.dirname(fromFile), specifier);
|
|
129
|
+
// Caller will probe both with and without explicit extensions.
|
|
130
|
+
return base.endsWith(".js") || base.endsWith(".mjs") || base.endsWith(".cjs")
|
|
131
|
+
|| base.endsWith(".ts") || base.endsWith(".tsx")
|
|
132
|
+
? base
|
|
133
|
+
: `${base}.js`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Strip every string literal (template, double, single) from the content
|
|
137
|
+
// before scanning for export declarations. Test files routinely embed
|
|
138
|
+
// sample source code inside fixtures (template literals for plugin
|
|
139
|
+
// modules, double-quoted diff lines for prompt tests, etc.). At top level
|
|
140
|
+
// `export ...` is a statement, never an expression, so it can never
|
|
141
|
+
// legitimately appear inside a quoted string in real code — meaning the
|
|
142
|
+
// strip is safe for export detection. We do NOT use this stripped view
|
|
143
|
+
// for import detection: the import regexes anchor on `import {...} from`
|
|
144
|
+
// regardless of the path string, but the namespace/dynamic-import passes
|
|
145
|
+
// need the actual path inside the quotes to resolve the target module.
|
|
146
|
+
function stripStringLiterals(content) {
|
|
147
|
+
return content
|
|
148
|
+
.replaceAll(/`[^`]*`/g, "``")
|
|
149
|
+
.replaceAll(/"(?:\\.|[^"\\])*"/g, '""')
|
|
150
|
+
.replaceAll(/'(?:\\.|[^'\\])*'/g, "''");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function findDeadExports(sourceFiles) {
|
|
154
|
+
const exportMap = new Map(); // file -> [exportName]
|
|
155
|
+
const internalExportsByFile = new Map(); // file -> Set<exportName>
|
|
156
|
+
const importedNames = new Set();
|
|
157
|
+
const fullyConsumedFiles = new Set(); // resolved file paths reached via `import *` or bound dynamic import
|
|
158
|
+
|
|
159
|
+
for (const file of sourceFiles) {
|
|
160
|
+
let content;
|
|
161
|
+
try {
|
|
162
|
+
content = await fs.readFile(file, "utf8");
|
|
163
|
+
} catch {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
const exportContent = stripStringLiterals(content);
|
|
167
|
+
|
|
168
|
+
const exports = [];
|
|
169
|
+
for (const match of exportContent.matchAll(EXPORT_RE)) {
|
|
170
|
+
exports.push(match[1]);
|
|
171
|
+
}
|
|
172
|
+
for (const match of exportContent.matchAll(NAMED_EXPORT_RE)) {
|
|
173
|
+
const names = match[1].split(",").map(n => n.trim().split(/\s+as\s+/).pop().trim());
|
|
174
|
+
exports.push(...names);
|
|
175
|
+
}
|
|
176
|
+
if (exports.length > 0) {
|
|
177
|
+
exportMap.set(file, exports);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Collect `@internal`-tagged exports up front so the dead-pass can skip them.
|
|
181
|
+
const internalSet = new Set();
|
|
182
|
+
for (const match of exportContent.matchAll(INTERNAL_EXPORT_RE)) {
|
|
183
|
+
internalSet.add(match[1]);
|
|
184
|
+
}
|
|
185
|
+
if (internalSet.size > 0) {
|
|
186
|
+
internalExportsByFile.set(file, internalSet);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
for (const match of content.matchAll(STATIC_NAMED_IMPORT_RE)) {
|
|
190
|
+
const names = match[1].split(",").map(n => n.trim().split(/\s+as\s+/)[0].trim());
|
|
191
|
+
for (const name of names) importedNames.add(name);
|
|
192
|
+
}
|
|
193
|
+
for (const match of content.matchAll(DEFAULT_IMPORT_RE)) {
|
|
194
|
+
importedNames.add(match[1]);
|
|
195
|
+
}
|
|
196
|
+
for (const match of content.matchAll(NAMESPACE_IMPORT_RE)) {
|
|
197
|
+
const resolved = resolveImportSpecifier(file, match[1]);
|
|
198
|
+
if (resolved) fullyConsumedFiles.add(resolved);
|
|
199
|
+
}
|
|
200
|
+
for (const match of content.matchAll(DYNAMIC_IMPORT_RE)) {
|
|
201
|
+
const resolved = resolveImportSpecifier(file, match[1]);
|
|
202
|
+
if (resolved) fullyConsumedFiles.add(resolved);
|
|
203
|
+
}
|
|
204
|
+
for (const match of content.matchAll(RE_EXPORT_NAMED_RE)) {
|
|
205
|
+
const names = match[1].split(",").map(n => n.trim().split(/\s+as\s+/)[0].trim());
|
|
206
|
+
for (const name of names) importedNames.add(name);
|
|
207
|
+
}
|
|
208
|
+
for (const match of content.matchAll(RE_EXPORT_STAR_RE)) {
|
|
209
|
+
const resolved = resolveImportSpecifier(file, match[1]);
|
|
210
|
+
if (resolved) fullyConsumedFiles.add(resolved);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const dead = [];
|
|
215
|
+
for (const [file, exports] of exportMap) {
|
|
216
|
+
if (fullyConsumedFiles.has(file)) continue; // module imported via * or bound await import — can't tell which exports are used
|
|
217
|
+
const internalSet = internalExportsByFile.get(file);
|
|
218
|
+
for (const name of exports) {
|
|
219
|
+
if (internalSet?.has(name)) continue; // explicitly opted out via @internal
|
|
220
|
+
if (!importedNames.has(name)) {
|
|
221
|
+
dead.push({ file, name });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return dead;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export async function measureBasalCost(projectDir) {
|
|
229
|
+
const sourceFiles = await walkSourceFiles(projectDir);
|
|
230
|
+
let totalLines = 0;
|
|
231
|
+
for (const f of sourceFiles) {
|
|
232
|
+
totalLines += await countLines(f);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const stack = summariseStack(await sniffManifests(projectDir));
|
|
236
|
+
const depInfo = countDependenciesFromStack(stack);
|
|
237
|
+
// depcheck solo aplica a Node; en stacks puros no-Node lo saltamos.
|
|
238
|
+
const hasNode = stack.manifests.some((m) => m.stack === "node");
|
|
239
|
+
const depcheck = hasNode ? await runDepcheck(projectDir) : { unused: [], note: "depcheck skipped (no package.json)" };
|
|
240
|
+
const deadExports = await findDeadExports(sourceFiles);
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
totalLines,
|
|
244
|
+
totalFiles: sourceFiles.length,
|
|
245
|
+
dependencies: depInfo,
|
|
246
|
+
unusedDependencies: depcheck,
|
|
247
|
+
deadExports,
|
|
248
|
+
stack
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function auditDir() {
|
|
253
|
+
return path.join(getKarajanHome(), "audits");
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export async function loadPreviousAudit(projectDir) {
|
|
257
|
+
const file = path.join(auditDir(), slugify(projectDir), "latest.json");
|
|
258
|
+
try {
|
|
259
|
+
return JSON.parse(await fs.readFile(file, "utf8"));
|
|
260
|
+
} catch {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export async function saveAuditSnapshot(projectDir, metrics) {
|
|
266
|
+
const dir = path.join(auditDir(), slugify(projectDir));
|
|
267
|
+
await fs.mkdir(dir, { recursive: true });
|
|
268
|
+
const snapshot = { ...metrics, timestamp: new Date().toISOString() };
|
|
269
|
+
await fs.writeFile(path.join(dir, "latest.json"), JSON.stringify(snapshot, null, 2));
|
|
270
|
+
return snapshot;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export function computeGrowthDelta(current, previous) {
|
|
274
|
+
if (!previous) return null;
|
|
275
|
+
return {
|
|
276
|
+
lines: current.totalLines - previous.totalLines,
|
|
277
|
+
files: current.totalFiles - previous.totalFiles,
|
|
278
|
+
deps: current.dependencies.total - (previous.dependencies?.total ?? 0),
|
|
279
|
+
since: previous.timestamp || null
|
|
280
|
+
};
|
|
281
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// Circular import-deps collector for `kj audit` (KJC-TSK — v2.17).
|
|
2
|
+
//
|
|
3
|
+
// Deterministic, stack-aware. Uses madge programmatically; activates only
|
|
4
|
+
// when the project's stack has JS/TS sources. Anything else returns
|
|
5
|
+
// { available: false, reason }.
|
|
6
|
+
//
|
|
7
|
+
// Same best-effort shape as sonar-findings / osv-findings / semgrep-findings:
|
|
8
|
+
// - missing dep or timeout returns { available: false, reason }
|
|
9
|
+
// - findings are passed through audit/issue-filter.js so the user can
|
|
10
|
+
// suppress known cycles via config.audit.false_positives or inline
|
|
11
|
+
// `// karajan-audit-ignore: madge:circular-import` markers.
|
|
12
|
+
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
import fs from "node:fs/promises";
|
|
15
|
+
import { filterFalsePositives } from "./issue-filter.js";
|
|
16
|
+
|
|
17
|
+
// madge is loaded dynamically: in the SEA standalone binary it is excluded
|
|
18
|
+
// from the bundle (see scripts/esbuild-sea.config.mjs) and a require()
|
|
19
|
+
// failure here cleanly degrades to available:false, while npm installs work
|
|
20
|
+
// normally. Keeping the import inside collectCircularDeps() also avoids
|
|
21
|
+
// paying the load cost for non-JS projects.
|
|
22
|
+
|
|
23
|
+
const SCAN_TIMEOUT_MS = 60_000;
|
|
24
|
+
const MAX_CYCLES_REPORTED = 50;
|
|
25
|
+
|
|
26
|
+
// Extensions madge will follow. Python/Go/etc. are not supported and we
|
|
27
|
+
// short-circuit on stack detection — listing only JS/TS variants here is a
|
|
28
|
+
// safety net so we never accidentally scan unrelated files.
|
|
29
|
+
const JS_TS_EXTENSIONS = ["js", "mjs", "cjs", "jsx", "ts", "tsx"];
|
|
30
|
+
|
|
31
|
+
function hasJsTsStack(stack) {
|
|
32
|
+
if (!stack) return false;
|
|
33
|
+
const lang = (stack.language || "").toLowerCase();
|
|
34
|
+
if (lang === "javascript" || lang === "typescript") return true;
|
|
35
|
+
const langs = Array.isArray(stack.languages) ? stack.languages : [];
|
|
36
|
+
return langs.some((l) => /^(javascript|typescript|jsx?|tsx?)$/i.test(String(l)));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function findTsConfig(projectDir) {
|
|
40
|
+
for (const name of ["tsconfig.json", "jsconfig.json"]) {
|
|
41
|
+
const p = path.join(projectDir, name);
|
|
42
|
+
try { await fs.access(p); return p; } catch { /* not present */ }
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function pickEntrypoint(projectDir) {
|
|
48
|
+
// madge needs an entrypoint. We prefer common conventions; if none
|
|
49
|
+
// exists we fall back to scanning `src/` which covers ~95% of JS/TS
|
|
50
|
+
// projects we've seen.
|
|
51
|
+
return path.join(projectDir, "src");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Collect circular-import findings for a project.
|
|
56
|
+
*
|
|
57
|
+
* @param {string} projectDir
|
|
58
|
+
* @param {object} stack - shape returned by detectProjectStack()
|
|
59
|
+
* @param {object} [config] - kj config (for config.audit.false_positives)
|
|
60
|
+
* @param {object} [logger]
|
|
61
|
+
* @returns {Promise<{available: boolean, reason?: string, total?: number, cycles?: object[], suppressed?: object[]}>}
|
|
62
|
+
*/
|
|
63
|
+
export async function collectCircularDeps(projectDir, stack, config = {}, logger = null) {
|
|
64
|
+
if (!hasJsTsStack(stack)) {
|
|
65
|
+
return { available: false, reason: "no JS/TS sources detected — circular-dep scan skipped" };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const entry = pickEntrypoint(projectDir);
|
|
69
|
+
try { await fs.access(entry); } catch {
|
|
70
|
+
return { available: false, reason: `entrypoint ${path.relative(projectDir, entry)} not found` };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let madge;
|
|
74
|
+
try {
|
|
75
|
+
({ default: madge } = await import("madge"));
|
|
76
|
+
} catch (err) {
|
|
77
|
+
return { available: false, reason: `madge not loadable (${err?.message || err}). In the SEA binary install karajan-code via npm to enable.` };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const tsConfig = await findTsConfig(projectDir);
|
|
81
|
+
const opts = {
|
|
82
|
+
fileExtensions: JS_TS_EXTENSIONS,
|
|
83
|
+
excludeRegExp: [/node_modules/, /\.test\.[jt]sx?$/, /\.spec\.[jt]sx?$/, /dist\//, /build\//, /coverage\//],
|
|
84
|
+
detectiveOptions: { es6: { mixedImports: true } },
|
|
85
|
+
};
|
|
86
|
+
if (tsConfig) opts.tsConfig = tsConfig;
|
|
87
|
+
|
|
88
|
+
let cycles;
|
|
89
|
+
try {
|
|
90
|
+
const scan = await Promise.race([
|
|
91
|
+
madge(entry, opts),
|
|
92
|
+
new Promise((_, rej) => setTimeout(() => rej(new Error("madge timeout")), SCAN_TIMEOUT_MS)),
|
|
93
|
+
]);
|
|
94
|
+
cycles = scan.circular() || [];
|
|
95
|
+
} catch (err) {
|
|
96
|
+
logger?.warn?.(`audit/circular-deps: madge failed (${err?.message || err})`);
|
|
97
|
+
return { available: false, reason: `madge run failed: ${err?.message || err}` };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// madge returns paths relative to the entry dir; normalize to paths
|
|
101
|
+
// relative to projectDir so they match how the rest of the audit (Sonar,
|
|
102
|
+
// Semgrep) reports locations and so config.audit.false_positives can
|
|
103
|
+
// target them with familiar globs like "src/".
|
|
104
|
+
const relPrefix = path.relative(projectDir, entry);
|
|
105
|
+
const normalizedCycles = cycles.map((cycle) =>
|
|
106
|
+
cycle.map((p) => (relPrefix ? path.join(relPrefix, p) : p)),
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const issues = normalizedCycles.slice(0, MAX_CYCLES_REPORTED).map((cycle, idx) => ({
|
|
110
|
+
tool: "madge",
|
|
111
|
+
rule: "circular-import",
|
|
112
|
+
component: cycle[0],
|
|
113
|
+
path: cycle[0],
|
|
114
|
+
line: 1,
|
|
115
|
+
severity: cycle.length >= 4 ? "MAJOR" : "MINOR",
|
|
116
|
+
message: `Circular import chain (${cycle.length} files): ${cycle.join(" → ")} → ${cycle[0]}`,
|
|
117
|
+
cycle,
|
|
118
|
+
index: idx,
|
|
119
|
+
}));
|
|
120
|
+
|
|
121
|
+
const extraRules = (config?.audit?.false_positives || []).filter((r) => r.tool === "madge");
|
|
122
|
+
const { kept, suppressed } = filterFalsePositives(issues, { extraRules, projectRoot: projectDir });
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
available: true,
|
|
126
|
+
total: kept.length,
|
|
127
|
+
suppressedCount: suppressed.length,
|
|
128
|
+
cycles: kept,
|
|
129
|
+
suppressed,
|
|
130
|
+
truncated: normalizedCycles.length > MAX_CYCLES_REPORTED ? normalizedCycles.length - MAX_CYCLES_REPORTED : 0,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Group cycles by severity for the deterministic summary renderer.
|
|
136
|
+
*/
|
|
137
|
+
export function groupCyclesBySeverity(cycles) {
|
|
138
|
+
const groups = { MAJOR: [], MINOR: [] };
|
|
139
|
+
for (const c of cycles || []) {
|
|
140
|
+
const sev = c.severity || "MINOR";
|
|
141
|
+
if (groups[sev]) groups[sev].push(c);
|
|
142
|
+
}
|
|
143
|
+
return groups;
|
|
144
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// Dead exports collector for `kj audit` (KJC-TSK v2.17, code-quality dim).
|
|
2
|
+
//
|
|
3
|
+
// Wraps knip via subprocess. Reports unused exports + unused files —
|
|
4
|
+
// findings consumed deterministically by the LLM phase so the coder
|
|
5
|
+
// stops shipping new code without noticing dead code piling up.
|
|
6
|
+
//
|
|
7
|
+
// Same best-effort pattern as madge / semgrep / osv / sonar:
|
|
8
|
+
// - missing project signal (no package.json, no JS/TS) → available:false
|
|
9
|
+
// - missing knip binary → available:false (degrades cleanly in SEA)
|
|
10
|
+
// - timeout / parse error → available:false with reason
|
|
11
|
+
//
|
|
12
|
+
// Findings pass through audit/issue-filter.js so users can suppress
|
|
13
|
+
// false positives via config.audit.false_positives (tool="knip") or
|
|
14
|
+
// inline `// karajan-audit-ignore: knip:<ruleId>`.
|
|
15
|
+
|
|
16
|
+
import { execFile } from "node:child_process";
|
|
17
|
+
import { promisify } from "node:util";
|
|
18
|
+
import { createRequire } from "node:module";
|
|
19
|
+
import path from "node:path";
|
|
20
|
+
import fs from "node:fs/promises";
|
|
21
|
+
import { filterFalsePositives } from "./issue-filter.js";
|
|
22
|
+
|
|
23
|
+
const execFileAsync = promisify(execFile);
|
|
24
|
+
|
|
25
|
+
const SCAN_TIMEOUT_MS = 120_000;
|
|
26
|
+
const MAX_DEAD_EXPORTS_REPORTED = 100;
|
|
27
|
+
const MAX_UNUSED_FILES_REPORTED = 50;
|
|
28
|
+
|
|
29
|
+
function hasJsTsStack(stack) {
|
|
30
|
+
if (!stack) return false;
|
|
31
|
+
const lang = (stack.language || "").toLowerCase();
|
|
32
|
+
if (lang === "javascript" || lang === "typescript") return true;
|
|
33
|
+
const langs = Array.isArray(stack.languages) ? stack.languages : [];
|
|
34
|
+
return langs.some((l) => /^(javascript|typescript|jsx?|tsx?)$/i.test(String(l)));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function hasPackageJson(projectDir) {
|
|
38
|
+
try { await fs.access(path.join(projectDir, "package.json")); return true; } catch { return false; }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Resolve the knip binary path relative to this package's node_modules.
|
|
43
|
+
* In SEA builds knip is `external` and require.resolve throws — we let
|
|
44
|
+
* the caller catch it and return available:false.
|
|
45
|
+
*
|
|
46
|
+
* knip restricts its subpath exports to "." and "./session", so we can't
|
|
47
|
+
* directly resolve "knip/bin/knip.js" or even "knip/package.json".
|
|
48
|
+
* Instead we resolve "knip" (the main entry, dist/index.js), walk up to
|
|
49
|
+
* its package root, and locate bin/knip.js from there.
|
|
50
|
+
*/
|
|
51
|
+
function resolveKnipBin() {
|
|
52
|
+
const require = createRequire(import.meta.url);
|
|
53
|
+
const entry = require.resolve("knip"); // e.g. .../node_modules/knip/dist/index.js
|
|
54
|
+
// entry is .../node_modules/knip/dist/index.js — the package root is two levels up
|
|
55
|
+
const knipRoot = path.dirname(path.dirname(entry));
|
|
56
|
+
return path.join(knipRoot, "bin", "knip.js");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Collect dead-exports findings for a project.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} projectDir
|
|
63
|
+
* @param {object} stack
|
|
64
|
+
* @param {object} [config]
|
|
65
|
+
* @param {object} [logger]
|
|
66
|
+
* @returns {Promise<{available: boolean, reason?: string, total?: number, exports?: object[], files?: object[], suppressed?: object[]}>}
|
|
67
|
+
*/
|
|
68
|
+
export async function collectDeadExports(projectDir, stack, config = {}, logger = null) {
|
|
69
|
+
if (!hasJsTsStack(stack)) {
|
|
70
|
+
return { available: false, reason: "no JS/TS sources detected — dead-export scan skipped" };
|
|
71
|
+
}
|
|
72
|
+
if (!(await hasPackageJson(projectDir))) {
|
|
73
|
+
return { available: false, reason: "no package.json found — knip needs one to detect entry points" };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let knipBin;
|
|
77
|
+
try { knipBin = resolveKnipBin(); } catch (err) {
|
|
78
|
+
return { available: false, reason: `knip binary not resolvable (${err?.message || err}). In the SEA binary install karajan-code via npm to enable.` };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let raw;
|
|
82
|
+
try {
|
|
83
|
+
const { stdout } = await execFileAsync(
|
|
84
|
+
process.execPath,
|
|
85
|
+
[knipBin, "--reporter", "json", "--no-progress", "--no-exit-code"],
|
|
86
|
+
{ cwd: projectDir, timeout: SCAN_TIMEOUT_MS, maxBuffer: 16 * 1024 * 1024 },
|
|
87
|
+
);
|
|
88
|
+
raw = stdout;
|
|
89
|
+
} catch (err) {
|
|
90
|
+
// knip exits non-zero when issues are found; with --no-exit-code that
|
|
91
|
+
// should not happen, but old configs might still trigger it. If the
|
|
92
|
+
// process produced JSON on stdout we still try to parse it.
|
|
93
|
+
raw = err?.stdout || "";
|
|
94
|
+
if (!raw) {
|
|
95
|
+
logger?.warn?.(`audit/dead-exports: knip failed (${err?.message || err})`);
|
|
96
|
+
return { available: false, reason: `knip run failed: ${err?.shortMessage || err?.message || err}` };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let report;
|
|
101
|
+
try {
|
|
102
|
+
report = JSON.parse(raw);
|
|
103
|
+
} catch (err) {
|
|
104
|
+
return { available: false, reason: `knip output not parseable as JSON (${err?.message || err})` };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// knip --reporter json shape: { files: [paths], issues: [{ file, exports?, types?, ... }], ... }
|
|
108
|
+
const unusedFiles = Array.isArray(report.files) ? report.files : [];
|
|
109
|
+
const fileIssues = Array.isArray(report.issues) ? report.issues : [];
|
|
110
|
+
|
|
111
|
+
const deadExportIssues = [];
|
|
112
|
+
for (const fi of fileIssues) {
|
|
113
|
+
const file = fi.file || fi.filePath || "";
|
|
114
|
+
if (!file) continue;
|
|
115
|
+
for (const ex of fi.exports || []) {
|
|
116
|
+
deadExportIssues.push({
|
|
117
|
+
tool: "knip",
|
|
118
|
+
rule: "unused-exports",
|
|
119
|
+
component: file,
|
|
120
|
+
path: file,
|
|
121
|
+
line: ex.line || 1,
|
|
122
|
+
col: ex.col,
|
|
123
|
+
name: ex.name,
|
|
124
|
+
severity: "MINOR",
|
|
125
|
+
message: `Unused export \`${ex.name}\` in ${file}`,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
for (const t of fi.types || []) {
|
|
129
|
+
deadExportIssues.push({
|
|
130
|
+
tool: "knip",
|
|
131
|
+
rule: "unused-types",
|
|
132
|
+
component: file,
|
|
133
|
+
path: file,
|
|
134
|
+
line: t.line || 1,
|
|
135
|
+
name: t.name,
|
|
136
|
+
severity: "MINOR",
|
|
137
|
+
message: `Unused type \`${t.name}\` in ${file}`,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const unusedFileIssues = unusedFiles.map((f) => ({
|
|
143
|
+
tool: "knip",
|
|
144
|
+
rule: "unused-files",
|
|
145
|
+
component: f,
|
|
146
|
+
path: f,
|
|
147
|
+
line: 1,
|
|
148
|
+
severity: "MAJOR",
|
|
149
|
+
message: `Unused file: ${f}`,
|
|
150
|
+
}));
|
|
151
|
+
|
|
152
|
+
const allIssues = [
|
|
153
|
+
...deadExportIssues.slice(0, MAX_DEAD_EXPORTS_REPORTED),
|
|
154
|
+
...unusedFileIssues.slice(0, MAX_UNUSED_FILES_REPORTED),
|
|
155
|
+
];
|
|
156
|
+
const extraRules = (config?.audit?.false_positives || []).filter((r) => r.tool === "knip");
|
|
157
|
+
const { kept, suppressed } = filterFalsePositives(allIssues, { extraRules, projectRoot: projectDir });
|
|
158
|
+
|
|
159
|
+
const keptExports = kept.filter((i) => i.rule === "unused-exports" || i.rule === "unused-types");
|
|
160
|
+
const keptFiles = kept.filter((i) => i.rule === "unused-files");
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
available: true,
|
|
164
|
+
total: kept.length,
|
|
165
|
+
suppressedCount: suppressed.length,
|
|
166
|
+
exports: keptExports,
|
|
167
|
+
files: keptFiles,
|
|
168
|
+
suppressed,
|
|
169
|
+
truncated: {
|
|
170
|
+
exports: deadExportIssues.length > MAX_DEAD_EXPORTS_REPORTED ? deadExportIssues.length - MAX_DEAD_EXPORTS_REPORTED : 0,
|
|
171
|
+
files: unusedFiles.length > MAX_UNUSED_FILES_REPORTED ? unusedFiles.length - MAX_UNUSED_FILES_REPORTED : 0,
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Group dead-export issues by severity for the deterministic summary.
|
|
178
|
+
*/
|
|
179
|
+
export function groupDeadExportsBySeverity(items) {
|
|
180
|
+
const groups = { MAJOR: [], MINOR: [] };
|
|
181
|
+
for (const i of items || []) {
|
|
182
|
+
const sev = i.severity || "MINOR";
|
|
183
|
+
if (groups[sev]) groups[sev].push(i);
|
|
184
|
+
}
|
|
185
|
+
return groups;
|
|
186
|
+
}
|