@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,219 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
const SKILL_DIRS = [".agent/skills", ".claude/skills"];
|
|
5
|
+
const SKILL_FILE = "SKILL.md";
|
|
6
|
+
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Extract the `type` field from SKILL.md frontmatter.
|
|
10
|
+
* Returns "technical" (default) or "domain".
|
|
11
|
+
* @param {string} content — raw SKILL.md content
|
|
12
|
+
* @returns {{type: string, body: string}}
|
|
13
|
+
*/
|
|
14
|
+
function parseSkillType(content) {
|
|
15
|
+
const match = content.match(FRONTMATTER_RE);
|
|
16
|
+
if (!match) return { type: "technical", body: content };
|
|
17
|
+
|
|
18
|
+
const frontmatter = match[1];
|
|
19
|
+
const body = match[2];
|
|
20
|
+
|
|
21
|
+
// Simple regex extraction — avoid full YAML parser for a single field
|
|
22
|
+
const typeMatch = frontmatter.match(/^type:\s*(.+)$/m);
|
|
23
|
+
const type = typeMatch ? typeMatch[1].trim().toLowerCase() : "technical";
|
|
24
|
+
|
|
25
|
+
return { type, body };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Scans known skill directories for SKILL.md files.
|
|
30
|
+
* @param {string} projectDir — absolute path to the project root
|
|
31
|
+
* @param {{type?: string}} [options] — filter by type: "technical" (default), "domain", or undefined (all)
|
|
32
|
+
* @returns {Promise<Array<{name: string, content: string, type: string}>>}
|
|
33
|
+
*/
|
|
34
|
+
export async function loadAvailableSkills(projectDir, options = {}) {
|
|
35
|
+
const skills = [];
|
|
36
|
+
const filterType = options.type || null;
|
|
37
|
+
|
|
38
|
+
for (const rel of SKILL_DIRS) {
|
|
39
|
+
const dir = join(projectDir, rel);
|
|
40
|
+
let entries;
|
|
41
|
+
try {
|
|
42
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
43
|
+
} catch {
|
|
44
|
+
continue; // directory does not exist — skip
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
for (const entry of entries) {
|
|
48
|
+
if (!entry.isDirectory()) continue;
|
|
49
|
+
const skillPath = join(dir, entry.name, SKILL_FILE);
|
|
50
|
+
try {
|
|
51
|
+
const raw = await readFile(skillPath, "utf-8");
|
|
52
|
+
const { type, body } = parseSkillType(raw);
|
|
53
|
+
|
|
54
|
+
// Filter by type if specified
|
|
55
|
+
if (filterType && type !== filterType) continue;
|
|
56
|
+
|
|
57
|
+
// For backward compat: existing callers get content without frontmatter for domain skills,
|
|
58
|
+
// but full content for technical skills (they never had frontmatter before)
|
|
59
|
+
const content = type === "domain" ? body.trim() : raw;
|
|
60
|
+
skills.push({ name: entry.name, content, type });
|
|
61
|
+
} catch {
|
|
62
|
+
// no SKILL.md in this subdirectory — skip
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return skills;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Builds a prompt section from loaded skills.
|
|
72
|
+
*
|
|
73
|
+
* For Anthropic (Claude Sonnet 4.5+), skills are delivered as Agent Skills
|
|
74
|
+
* natively (loaded by the runtime from SKILL.md) — duplicating their full
|
|
75
|
+
* content in the prompt wastes tokens and risks drifting from the canonical
|
|
76
|
+
* source. When `provider === "anthropic"`, this function emits a lightweight
|
|
77
|
+
* reference list; Claude will load the SKILL.md files on demand.
|
|
78
|
+
*
|
|
79
|
+
* For every other provider, the full SKILL.md body is inlined (previous
|
|
80
|
+
* behaviour preserved).
|
|
81
|
+
*
|
|
82
|
+
* When `role` is supplied, skills are filtered to those relevant to the role
|
|
83
|
+
* (e.g. a reviewer only sees `*-code-review`, `*-security-*`, and general
|
|
84
|
+
* patterns; see ROLE_SKILL_PATTERNS). Setting role to null/undefined keeps
|
|
85
|
+
* the legacy behavior of injecting every skill (useful for the coder role,
|
|
86
|
+
* which benefits from broad context).
|
|
87
|
+
*
|
|
88
|
+
* @param {Array<{name: string, content: string}>} skills
|
|
89
|
+
* @param {{ provider?: string, role?: string }} [options]
|
|
90
|
+
* @returns {string} prompt section or empty string
|
|
91
|
+
*/
|
|
92
|
+
export function buildSkillSection(skills, options = {}) {
|
|
93
|
+
if (!skills || skills.length === 0) return "";
|
|
94
|
+
|
|
95
|
+
const filtered = options.role ? filterSkillsForRole(skills, options.role) : skills;
|
|
96
|
+
if (filtered.length === 0) return "";
|
|
97
|
+
|
|
98
|
+
const providerSlug = normalize(options.provider);
|
|
99
|
+
|
|
100
|
+
if (providerSlug === "anthropic") {
|
|
101
|
+
return buildReferenceSection(filtered);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return buildInlineSection(filtered);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Role → substring patterns that skills must match to be included. A skill
|
|
109
|
+
* matches the role if its name contains ANY of the role's patterns OR if it
|
|
110
|
+
* appears in the role's explicit allowlist. Coder is omitted on purpose: it
|
|
111
|
+
* receives everything.
|
|
112
|
+
*
|
|
113
|
+
* The patterns target the skill NAMING convention shipped by openskills.cc
|
|
114
|
+
* where skills are typically named like `react-patterns`, `java-code-review`,
|
|
115
|
+
* `pytest-patterns`, `owasp-top-10`, etc.
|
|
116
|
+
*/
|
|
117
|
+
/**
|
|
118
|
+
* Role → filter config.
|
|
119
|
+
*
|
|
120
|
+
* Two modes:
|
|
121
|
+
* - `includes` present: skill name must contain ANY include AND NOT contain
|
|
122
|
+
* any exclude. `allow` bypasses both checks (explicit allowlist by name).
|
|
123
|
+
* - `includes` absent (or empty) + `excludes` present: skill passes unless
|
|
124
|
+
* its name contains an exclude pattern. This is the "accept broad, trim
|
|
125
|
+
* obvious mismatches" mode used by architect.
|
|
126
|
+
*/
|
|
127
|
+
const ROLE_SKILL_PATTERNS = {
|
|
128
|
+
reviewer: {
|
|
129
|
+
includes: ["code-review", "security", "lint", "style", "antipatterns", "owasp"],
|
|
130
|
+
excludes: [],
|
|
131
|
+
allow: ["sql-analysis"],
|
|
132
|
+
},
|
|
133
|
+
architect: {
|
|
134
|
+
// Architect sees everything except test-framework patterns. Architecture
|
|
135
|
+
// decisions touch every domain, so keep the filter permissive.
|
|
136
|
+
includes: null,
|
|
137
|
+
excludes: ["pytest", "vitest", "jest", "playwright", "cypress"],
|
|
138
|
+
allow: [],
|
|
139
|
+
},
|
|
140
|
+
tester: {
|
|
141
|
+
includes: ["test", "pytest", "vitest", "jest", "playwright", "cypress"],
|
|
142
|
+
excludes: [],
|
|
143
|
+
allow: [],
|
|
144
|
+
},
|
|
145
|
+
security: {
|
|
146
|
+
includes: ["security", "owasp", "sast"],
|
|
147
|
+
excludes: [],
|
|
148
|
+
allow: [],
|
|
149
|
+
},
|
|
150
|
+
planner: {
|
|
151
|
+
// Planner gets broad stack context except test frameworks.
|
|
152
|
+
includes: null,
|
|
153
|
+
excludes: ["pytest", "vitest", "jest", "playwright", "cypress"],
|
|
154
|
+
allow: [],
|
|
155
|
+
},
|
|
156
|
+
solomon: {
|
|
157
|
+
includes: ["security", "code-review"],
|
|
158
|
+
excludes: [],
|
|
159
|
+
allow: [],
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Filter skills for a given role using the ROLE_SKILL_PATTERNS map. Unknown
|
|
165
|
+
* roles get ALL skills (conservative default for the coder role and any
|
|
166
|
+
* future addition). Matching is case-insensitive substring.
|
|
167
|
+
*
|
|
168
|
+
* @param {Array<{name: string}>} skills
|
|
169
|
+
* @param {string} role
|
|
170
|
+
* @returns {Array} filtered array
|
|
171
|
+
*/
|
|
172
|
+
export function filterSkillsForRole(skills, role) {
|
|
173
|
+
const cfg = ROLE_SKILL_PATTERNS[role];
|
|
174
|
+
if (!cfg) return skills; // unknown role (e.g. coder) → no filter
|
|
175
|
+
const includes = Array.isArray(cfg.includes) ? cfg.includes.map((s) => s.toLowerCase()) : null;
|
|
176
|
+
const excludes = (cfg.excludes || []).map((s) => s.toLowerCase());
|
|
177
|
+
const allow = new Set((cfg.allow || []).map((s) => s.toLowerCase()));
|
|
178
|
+
return skills.filter((s) => {
|
|
179
|
+
const name = (s.name || "").toLowerCase();
|
|
180
|
+
if (allow.has(name)) return true;
|
|
181
|
+
if (excludes.some((pat) => name.includes(pat))) return false;
|
|
182
|
+
if (!includes || includes.length === 0) return true; // exclude-only mode
|
|
183
|
+
return includes.some((pat) => name.includes(pat));
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function buildInlineSection(skills) {
|
|
188
|
+
const header = [
|
|
189
|
+
"## Domain Skills",
|
|
190
|
+
"",
|
|
191
|
+
"The following domain-specific knowledge is available for this task:"
|
|
192
|
+
].join("\n");
|
|
193
|
+
|
|
194
|
+
const blocks = skills.map(
|
|
195
|
+
(s) => `### ${s.name}\n${s.content}`
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
return [header, ...blocks].join("\n\n");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function buildReferenceSection(skills) {
|
|
202
|
+
const names = skills.map((s) => s.name).join(", ");
|
|
203
|
+
return [
|
|
204
|
+
"## Domain Skills",
|
|
205
|
+
"",
|
|
206
|
+
`Skills available natively via your Agent Skills capability: ${names}.`,
|
|
207
|
+
"Each skill's SKILL.md is already loaded in your runtime; invoke its guidance as needed without re-reading the full content."
|
|
208
|
+
].join("\n");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function normalize(value) {
|
|
212
|
+
if (!value) return null;
|
|
213
|
+
const lc = String(value).toLowerCase();
|
|
214
|
+
if (lc === "claude" || lc === "anthropic") return "anthropic";
|
|
215
|
+
if (lc === "codex" || lc === "openai") return "openai";
|
|
216
|
+
if (lc === "gemini" || lc === "google") return "google";
|
|
217
|
+
if (lc === "opencode") return "opencode";
|
|
218
|
+
return lc;
|
|
219
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local cache for installed OpenSkills, backed by `~/.karajan/skills-cache/`.
|
|
3
|
+
*
|
|
4
|
+
* The cache records WHEN each skill was last installed so we can skip
|
|
5
|
+
* redundant `npx openskills install X` calls during the 7-day TTL window
|
|
6
|
+
* (each call can take 10-30s even when the skill is already present).
|
|
7
|
+
*
|
|
8
|
+
* This is intentionally simple: one directory per skill name with a
|
|
9
|
+
* `meta.json` describing the last install time. Skill content is NOT copied
|
|
10
|
+
* to the cache — OpenSkills owns the real content on disk; we only track
|
|
11
|
+
* freshness.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import fs from "node:fs/promises";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import { getKarajanHome } from "../utils/paths.js";
|
|
17
|
+
|
|
18
|
+
const CACHE_DIR_NAME = "skills-cache";
|
|
19
|
+
const DEFAULT_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
20
|
+
|
|
21
|
+
export function getCacheRoot() {
|
|
22
|
+
return path.join(getKarajanHome(), CACHE_DIR_NAME);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function sanitize(name) {
|
|
26
|
+
// Allow safe skill name characters, replace everything else with "_".
|
|
27
|
+
return String(name || "").replaceAll(/[^A-Za-z0-9._-]/g, "_");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function metaPath(name) {
|
|
31
|
+
return path.join(getCacheRoot(), sanitize(name), "meta.json");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Read cached metadata for a skill. Returns null if missing or unreadable.
|
|
36
|
+
* @param {string} name
|
|
37
|
+
* @returns {Promise<{cachedAt: string, source?: string}|null>}
|
|
38
|
+
*/
|
|
39
|
+
export async function getCachedMeta(name) {
|
|
40
|
+
if (!name) return null;
|
|
41
|
+
try {
|
|
42
|
+
const raw = await fs.readFile(metaPath(name), "utf8");
|
|
43
|
+
const parsed = JSON.parse(raw);
|
|
44
|
+
if (typeof parsed?.cachedAt !== "string") return null;
|
|
45
|
+
return parsed;
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* True iff the skill was cached more recently than `ttlMs` ago.
|
|
53
|
+
* @param {string} name
|
|
54
|
+
* @param {number} [ttlMs=DEFAULT_TTL_MS]
|
|
55
|
+
* @returns {Promise<boolean>}
|
|
56
|
+
*/
|
|
57
|
+
export async function isFresh(name, ttlMs = DEFAULT_TTL_MS) {
|
|
58
|
+
const meta = await getCachedMeta(name);
|
|
59
|
+
if (!meta) return false;
|
|
60
|
+
const cachedTime = Date.parse(meta.cachedAt);
|
|
61
|
+
if (Number.isNaN(cachedTime)) return false;
|
|
62
|
+
return Date.now() - cachedTime < ttlMs;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Record that a skill was just (re-)installed. Creates the cache subdir if
|
|
67
|
+
* needed.
|
|
68
|
+
* @param {string} name
|
|
69
|
+
* @param {{ source?: string }} [opts]
|
|
70
|
+
* @returns {Promise<void>}
|
|
71
|
+
*/
|
|
72
|
+
export async function recordInstall(name, opts = {}) {
|
|
73
|
+
if (!name) return;
|
|
74
|
+
const dir = path.join(getCacheRoot(), sanitize(name));
|
|
75
|
+
await fs.mkdir(dir, { recursive: true });
|
|
76
|
+
const meta = {
|
|
77
|
+
name,
|
|
78
|
+
cachedAt: new Date().toISOString(),
|
|
79
|
+
...(opts.source ? { source: opts.source } : {}),
|
|
80
|
+
};
|
|
81
|
+
await fs.writeFile(path.join(dir, "meta.json"), JSON.stringify(meta, null, 2), "utf8");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Remove all cached skill metadata (does not touch real installed skills,
|
|
86
|
+
* only clears the freshness tracker).
|
|
87
|
+
* @returns {Promise<{removed: number}>}
|
|
88
|
+
*/
|
|
89
|
+
export async function clearCache() {
|
|
90
|
+
const root = getCacheRoot();
|
|
91
|
+
let removed = 0;
|
|
92
|
+
try {
|
|
93
|
+
const entries = await fs.readdir(root, { withFileTypes: true });
|
|
94
|
+
for (const entry of entries) {
|
|
95
|
+
if (!entry.isDirectory()) continue;
|
|
96
|
+
await fs.rm(path.join(root, entry.name), { recursive: true, force: true });
|
|
97
|
+
removed++;
|
|
98
|
+
}
|
|
99
|
+
} catch { /* cache dir does not exist — nothing to clear */ }
|
|
100
|
+
return { removed };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* List cached skill names with their cachedAt timestamp, sorted oldest first.
|
|
105
|
+
* @returns {Promise<Array<{name: string, cachedAt: string, fresh: boolean}>>}
|
|
106
|
+
*/
|
|
107
|
+
export async function listCached(ttlMs = DEFAULT_TTL_MS) {
|
|
108
|
+
const root = getCacheRoot();
|
|
109
|
+
const out = [];
|
|
110
|
+
let entries;
|
|
111
|
+
try {
|
|
112
|
+
entries = await fs.readdir(root, { withFileTypes: true });
|
|
113
|
+
} catch {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
for (const entry of entries) {
|
|
117
|
+
if (!entry.isDirectory()) continue;
|
|
118
|
+
const meta = await getCachedMeta(entry.name);
|
|
119
|
+
if (!meta) continue;
|
|
120
|
+
const cachedTime = Date.parse(meta.cachedAt);
|
|
121
|
+
const fresh = !Number.isNaN(cachedTime) && (Date.now() - cachedTime < ttlMs);
|
|
122
|
+
out.push({ name: meta.name || entry.name, cachedAt: meta.cachedAt, fresh });
|
|
123
|
+
}
|
|
124
|
+
out.sort((a, b) => a.cachedAt.localeCompare(b.cachedAt));
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export { DEFAULT_TTL_MS };
|
package/src/sonar/api.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { runCommand } from "../utils/process.js";
|
|
2
|
+
import { withRetry } from "../utils/retry.js";
|
|
3
|
+
import { resolveSonarProjectKey } from "./project-key.js";
|
|
4
|
+
import { resolveSonarToken } from "./config-resolver.js";
|
|
5
|
+
import { filterFalsePositives } from "./issue-filter.js";
|
|
6
|
+
import { recoverSonarToken } from "./token-recovery.js";
|
|
7
|
+
|
|
8
|
+
class SonarApiError extends Error {
|
|
9
|
+
constructor(message, { url, httpStatus, hint } = {}) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "SonarApiError";
|
|
12
|
+
this.url = url;
|
|
13
|
+
this.httpStatus = httpStatus;
|
|
14
|
+
this.hint = hint;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function tokenFromConfig(config) {
|
|
19
|
+
return resolveSonarToken(config) || "";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function parseHttpResponse(stdout) {
|
|
23
|
+
const lines = stdout.split("\n");
|
|
24
|
+
const httpCode = Number.parseInt(lines.pop(), 10) || 0;
|
|
25
|
+
const body = lines.join("\n");
|
|
26
|
+
return { httpCode, body };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function sonarFetchOnce(config, urlPath, { _retriedAfterRecovery = false, logger = null } = {}) {
|
|
30
|
+
const token = tokenFromConfig(config);
|
|
31
|
+
const url = `${config.sonarqube.host}${urlPath}`;
|
|
32
|
+
const res = await runCommand("curl", ["-s", "-w", "\n%{http_code}", "-u", `${token}:`, url]);
|
|
33
|
+
|
|
34
|
+
if (res.exitCode !== 0) {
|
|
35
|
+
const err = new SonarApiError(
|
|
36
|
+
`SonarQube is not reachable at ${config.sonarqube.host}. Check that SonarQube is running ('kj sonar start').`,
|
|
37
|
+
{ url, hint: "Run 'kj sonar start' or verify Docker is running." }
|
|
38
|
+
);
|
|
39
|
+
err.httpStatus = 503;
|
|
40
|
+
throw err;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const { httpCode, body } = parseHttpResponse(res.stdout);
|
|
44
|
+
|
|
45
|
+
if (httpCode === 401) {
|
|
46
|
+
// KJC-BUG-0057 (v2.19.2): try to bootstrap a fresh token via the Sonar
|
|
47
|
+
// REST API and retry ONCE. If recovery still fails — or this was already
|
|
48
|
+
// the retry — surface the actionable error like before.
|
|
49
|
+
if (!_retriedAfterRecovery) {
|
|
50
|
+
const recovery = await recoverSonarToken(config, logger);
|
|
51
|
+
if (recovery.ok) {
|
|
52
|
+
return sonarFetchOnce(config, urlPath, { _retriedAfterRecovery: true, logger });
|
|
53
|
+
}
|
|
54
|
+
throw new SonarApiError(
|
|
55
|
+
`SonarQube authentication failed (HTTP 401) and auto-recovery failed: ${recovery.reason}. Regenerate the token manually with 'kj init' or save admin user/password in ~/.karajan/sonar-credentials.json.`,
|
|
56
|
+
{ url, httpStatus: 401, hint: "Run 'kj init' to regenerate the SonarQube token, or save admin credentials in ~/.karajan/sonar-credentials.json so Karajan can re-bootstrap automatically." }
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
throw new SonarApiError(
|
|
60
|
+
`SonarQube authentication failed (HTTP 401) even after auto-recovery. The newly generated token is also being rejected — the Sonar instance may be in an inconsistent state.`,
|
|
61
|
+
{ url, httpStatus: 401, hint: "Inspect the Sonar UI directly; consider 'kj sonar restart'." }
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (httpCode >= 400) {
|
|
66
|
+
const err = new SonarApiError(
|
|
67
|
+
`SonarQube API returned HTTP ${httpCode} for ${url}.`,
|
|
68
|
+
{ url, httpStatus: httpCode }
|
|
69
|
+
);
|
|
70
|
+
err.httpStatus = httpCode;
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return body;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function sonarFetch(config, urlPath) {
|
|
78
|
+
const maxAttempts = config.sonarqube?.max_scan_retries ?? 3;
|
|
79
|
+
return withRetry(
|
|
80
|
+
() => sonarFetchOnce(config, urlPath),
|
|
81
|
+
{ maxAttempts, initialBackoffMs: 2000, maxBackoffMs: 15000 }
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function getQualityGateStatus(config, projectKey = null) {
|
|
86
|
+
const effectiveProjectKey = await resolveSonarProjectKey(config, { projectKey });
|
|
87
|
+
const body = await sonarFetch(config, `/api/qualitygates/project_status?projectKey=${effectiveProjectKey}`);
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const parsed = JSON.parse(body);
|
|
91
|
+
return { ok: true, status: parsed.projectStatus?.status || "ERROR", conditions: parsed.projectStatus?.conditions || [], raw: parsed };
|
|
92
|
+
} catch { /* SonarQube response is not valid JSON */
|
|
93
|
+
return { ok: false, status: "ERROR", conditions: [], raw: body };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function getOpenIssues(config, projectKey = null) {
|
|
98
|
+
const effectiveProjectKey = await resolveSonarProjectKey(config, { projectKey });
|
|
99
|
+
const body = await sonarFetch(config, `/api/issues/search?projectKeys=${effectiveProjectKey}&statuses=OPEN`);
|
|
100
|
+
|
|
101
|
+
let parsedIssues = [];
|
|
102
|
+
let parsedRaw = body;
|
|
103
|
+
try {
|
|
104
|
+
const parsed = JSON.parse(body);
|
|
105
|
+
parsedIssues = parsed.issues || [];
|
|
106
|
+
parsedRaw = parsed;
|
|
107
|
+
} catch { /* SonarQube response is not valid JSON */ }
|
|
108
|
+
|
|
109
|
+
// KJC-TSK-0416: pre-filtro de falsos positivos antes de devolver al
|
|
110
|
+
// caller. El coder, audit y scan consumen lo que pasa el filtro;
|
|
111
|
+
// suppressed se devuelve aparte para observabilidad.
|
|
112
|
+
// v2.17.0: accepts both legacy config.sonar.false_positives (no `tool`
|
|
113
|
+
// field, implicit "sonar") and the new generalised config.audit.false_positives.
|
|
114
|
+
const sonarLegacy = (config?.sonar?.false_positives || []).map((r) => ({ tool: "sonar", ...r }));
|
|
115
|
+
const auditRules = (config?.audit?.false_positives || []).filter((r) => (r.tool || "sonar") === "sonar");
|
|
116
|
+
const taggedIssues = parsedIssues.map((i) => ({ ...i, tool: "sonar" }));
|
|
117
|
+
const { kept, suppressed } = filterFalsePositives(taggedIssues, {
|
|
118
|
+
extraRules: [...sonarLegacy, ...auditRules],
|
|
119
|
+
projectRoot: config?.projectDir,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
total: kept.length,
|
|
124
|
+
issues: kept,
|
|
125
|
+
suppressed,
|
|
126
|
+
raw: parsedRaw,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { runCommand } from "../utils/process.js";
|
|
2
|
+
import { resolveSonarProjectKey } from "./project-key.js";
|
|
3
|
+
|
|
4
|
+
function buildCloudScannerArgs(projectKey, config) {
|
|
5
|
+
const sc = config.sonarcloud || {};
|
|
6
|
+
const scanner = sc.scanner || {};
|
|
7
|
+
const host = sc.host || "https://sonarcloud.io";
|
|
8
|
+
const organization = process.env.KJ_SONARCLOUD_ORG || sc.organization;
|
|
9
|
+
|
|
10
|
+
const args = [
|
|
11
|
+
`-Dsonar.host.url=${host}`,
|
|
12
|
+
`-Dsonar.projectKey=${projectKey}`
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
// Token passed via SONAR_TOKEN env var, not CLI args (invisible in `ps aux`)
|
|
16
|
+
if (organization) args.push(`-Dsonar.organization=${organization}`);
|
|
17
|
+
if (scanner.sources) args.push(`-Dsonar.sources=${scanner.sources}`);
|
|
18
|
+
if (scanner.exclusions) args.push(`-Dsonar.exclusions=${scanner.exclusions}`);
|
|
19
|
+
if (scanner.test_inclusions) args.push(`-Dsonar.test.inclusions=${scanner.test_inclusions}`);
|
|
20
|
+
|
|
21
|
+
return args;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function runSonarCloudScan(config, projectKey = null) {
|
|
25
|
+
const sc = config.sonarcloud || {};
|
|
26
|
+
const token = process.env.KJ_SONARCLOUD_TOKEN || sc.token;
|
|
27
|
+
const organization = sc.organization || process.env.KJ_SONARCLOUD_ORG;
|
|
28
|
+
|
|
29
|
+
if (!token) {
|
|
30
|
+
return {
|
|
31
|
+
ok: false,
|
|
32
|
+
projectKey: null,
|
|
33
|
+
stdout: "",
|
|
34
|
+
stderr: "SonarCloud token not configured. Set sonarcloud.token in kj.config.yml or KJ_SONARCLOUD_TOKEN env var.",
|
|
35
|
+
exitCode: 1
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!organization) {
|
|
40
|
+
return {
|
|
41
|
+
ok: false,
|
|
42
|
+
projectKey: null,
|
|
43
|
+
stdout: "",
|
|
44
|
+
stderr: "SonarCloud organization not configured. Set sonarcloud.organization in kj.config.yml or KJ_SONARCLOUD_ORG env var.",
|
|
45
|
+
exitCode: 1
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let effectiveProjectKey;
|
|
50
|
+
try {
|
|
51
|
+
effectiveProjectKey = projectKey || sc.project_key || await resolveSonarProjectKey(config, { projectKey });
|
|
52
|
+
} catch (error) {
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
projectKey: null,
|
|
56
|
+
stdout: "",
|
|
57
|
+
stderr: error?.message || String(error),
|
|
58
|
+
exitCode: 1
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const scannerTimeout = 15 * 60 * 1000;
|
|
63
|
+
const args = buildCloudScannerArgs(effectiveProjectKey, config);
|
|
64
|
+
|
|
65
|
+
// Pass token via process env, not CLI args — invisible in `ps aux`
|
|
66
|
+
const env = { ...process.env };
|
|
67
|
+
if (token) env.SONAR_TOKEN = token;
|
|
68
|
+
|
|
69
|
+
const result = await runCommand("npx", ["@sonar/scan", ...args], { timeout: scannerTimeout, env });
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
ok: result.exitCode === 0,
|
|
73
|
+
projectKey: effectiveProjectKey,
|
|
74
|
+
stdout: result.stdout,
|
|
75
|
+
stderr: result.stderr,
|
|
76
|
+
exitCode: result.exitCode
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized SonarQube configuration resolution.
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for host URL, token, and admin credentials.
|
|
5
|
+
* Consumers: scanner.js, preflight-checks.js, api.js.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { loadSonarCredentials } from "./credentials.js";
|
|
9
|
+
|
|
10
|
+
const DEFAULT_HOST = "http://localhost:9000";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Normalize SonarQube host URL for API calls (replace Docker-internal hostname).
|
|
14
|
+
* @param {string|undefined} rawHost - Raw host from config or env
|
|
15
|
+
* @returns {string} Normalized host URL without trailing slash
|
|
16
|
+
*/
|
|
17
|
+
export function resolveSonarHost(rawHost) {
|
|
18
|
+
const host = String(rawHost || DEFAULT_HOST)
|
|
19
|
+
.replaceAll(/host\.docker\.internal/g, "localhost")
|
|
20
|
+
.replace(/\/+$/, "");
|
|
21
|
+
return host;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Resolve SonarQube auth token from (in priority order):
|
|
26
|
+
* 1. KJ_SONAR_TOKEN env var
|
|
27
|
+
* 2. config.sonarqube.token
|
|
28
|
+
* 3. SONAR_TOKEN env var
|
|
29
|
+
*
|
|
30
|
+
* For async resolution (including credentials file), use resolveSonarTokenAsync.
|
|
31
|
+
*
|
|
32
|
+
* @param {object} [config={}] - Karajan config object
|
|
33
|
+
* @returns {string|null} Token string or null if none found
|
|
34
|
+
*/
|
|
35
|
+
export function resolveSonarToken(config = {}) {
|
|
36
|
+
const token =
|
|
37
|
+
process.env.KJ_SONAR_TOKEN ||
|
|
38
|
+
config.sonarqube?.token ||
|
|
39
|
+
process.env.SONAR_TOKEN ||
|
|
40
|
+
null;
|
|
41
|
+
return token || null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Async version of resolveSonarToken that also checks the credentials file
|
|
46
|
+
* for previously generated tokens.
|
|
47
|
+
*
|
|
48
|
+
* @param {object} [config={}] - Karajan config object
|
|
49
|
+
* @returns {Promise<string|null>} Token string or null if none found
|
|
50
|
+
*/
|
|
51
|
+
export async function resolveSonarTokenAsync(config = {}) {
|
|
52
|
+
const syncToken = resolveSonarToken(config);
|
|
53
|
+
if (syncToken) return syncToken;
|
|
54
|
+
|
|
55
|
+
// Check credentials file for persisted token
|
|
56
|
+
const fileCreds = (await loadSonarCredentials()) || {};
|
|
57
|
+
return fileCreds.token || null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Resolve SonarQube admin credentials from (in priority order):
|
|
62
|
+
* 1. Env vars: KJ_SONAR_ADMIN_USER / KJ_SONAR_ADMIN_PASSWORD
|
|
63
|
+
* 2. Config: sonarqube.admin_user / sonarqube.admin_password
|
|
64
|
+
* 3. Credentials file: ~/.karajan/sonar-credentials.json
|
|
65
|
+
*
|
|
66
|
+
* Returns all candidate passwords (de-duped) so callers can try each.
|
|
67
|
+
*
|
|
68
|
+
* @param {object} [config={}] - Karajan config object
|
|
69
|
+
* @returns {Promise<{ user: string|null, passwords: string[] }>}
|
|
70
|
+
*/
|
|
71
|
+
export async function resolveSonarCredentials(config = {}) {
|
|
72
|
+
const fileCreds = (await loadSonarCredentials()) || {};
|
|
73
|
+
|
|
74
|
+
const user =
|
|
75
|
+
process.env.KJ_SONAR_ADMIN_USER ||
|
|
76
|
+
config.sonarqube?.admin_user ||
|
|
77
|
+
fileCreds.user ||
|
|
78
|
+
"admin"; // Default for fresh SonarQube installs
|
|
79
|
+
|
|
80
|
+
const candidates = [
|
|
81
|
+
process.env.KJ_SONAR_ADMIN_PASSWORD,
|
|
82
|
+
config.sonarqube?.admin_password,
|
|
83
|
+
fileCreds.password,
|
|
84
|
+
"admin", // Default password for fresh SonarQube installs (zero-config)
|
|
85
|
+
].filter(Boolean);
|
|
86
|
+
|
|
87
|
+
return { user, passwords: [...new Set(candidates)] };
|
|
88
|
+
}
|