@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,950 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { getConfigPath, getProjectConfigPath, loadConfig, writeConfig } from "../config.js";
|
|
5
|
+
import { sonarUp, checkVmMaxMapCount } from "../sonar/manager.js";
|
|
6
|
+
import { ollamaUp, waitForOllamaReady, normalizeOllamaConfig } from "../rag/ollama-manager.js";
|
|
7
|
+
import { checkOllamaCapability, pullOllamaModel } from "../rag/ollama-capability.js";
|
|
8
|
+
import { exists, ensureDir } from "../utils/fs.js";
|
|
9
|
+
import { ensureContractBlockPresent } from "../review/gate-gitignore.js";
|
|
10
|
+
import { getKarajanHome } from "../utils/paths.js";
|
|
11
|
+
import { getTemplatesRoot } from "../utils/templates-root.js";
|
|
12
|
+
import { detectAvailableAgents } from "../utils/agent-detect.js";
|
|
13
|
+
import { createWizard, isTTY } from "../utils/wizard.js";
|
|
14
|
+
import { runCommand } from "../utils/process.js";
|
|
15
|
+
import { getInstallCommand } from "../utils/os-detect.js";
|
|
16
|
+
import { detectOsLocale, SUPPORTED_LANGUAGES } from "../utils/locale.js";
|
|
17
|
+
import { buildTelemetryPayload, sendTelemetryEvent } from "../utils/telemetry.js";
|
|
18
|
+
import { detectRtk } from "../utils/rtk-detect.js";
|
|
19
|
+
import { installRtk } from "../utils/rtk-install.js";
|
|
20
|
+
import { detectSqueezr } from "../utils/squeezr-detect.js";
|
|
21
|
+
import { installSqueezr } from "../utils/squeezr-install.js";
|
|
22
|
+
import { detectAiTrash, installAiTrashHook } from "../utils/ai-trash.js";
|
|
23
|
+
import { detectQmd } from "../utils/qmd-detect.js";
|
|
24
|
+
import { installQmd } from "../utils/qmd-install.js";
|
|
25
|
+
import { registerQmdCollections } from "../utils/qmd-collection.js";
|
|
26
|
+
import { isGitRepo } from "../harden/harden-engine.js";
|
|
27
|
+
import { hardenCommand } from "./harden.js";
|
|
28
|
+
import { detectProjectStack } from "../utils/stack-detect.js";
|
|
29
|
+
import { bootstrapSonarToken } from "../sonar/token-bootstrap.js";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Roles asked individually after coder/reviewer. Each entry:
|
|
33
|
+
* - key: config.roles.<key>.provider
|
|
34
|
+
* - pipelineKey: config.pipeline.<key>.enabled (omit when not gated by a flag)
|
|
35
|
+
* - default: which role's CLI to inherit ("coder" | "reviewer")
|
|
36
|
+
* - allowDisable: true if "off" is a valid choice
|
|
37
|
+
*
|
|
38
|
+
* Order matters — keep the most-impactful first so the wizard never hides
|
|
39
|
+
* critical roles behind a long tail of opt-ins.
|
|
40
|
+
*/
|
|
41
|
+
const PER_ROLE_QUESTIONS = [
|
|
42
|
+
{ key: "planner", pipelineKey: "planner", default: "coder", allowDisable: true, label: "Planner (decomposes complex tasks into HUs)" },
|
|
43
|
+
{ key: "researcher", pipelineKey: "researcher", default: "coder", allowDisable: true, label: "Researcher (explores codebase before coding)" },
|
|
44
|
+
{ key: "architect", pipelineKey: "architect", default: "coder", allowDisable: true, label: "Architect (designs solution + contracts)" },
|
|
45
|
+
{ key: "tester", pipelineKey: "tester", default: "coder", allowDisable: false, label: "Tester (verifies tests + coverage)" },
|
|
46
|
+
{ key: "security", pipelineKey: "security", default: "coder", allowDisable: false, label: "Security (OWASP audit)" },
|
|
47
|
+
{ key: "solomon", pipelineKey: "solomon", default: "reviewer", allowDisable: false, label: "Solomon (judge AI for dilemmas)" },
|
|
48
|
+
{ key: "refactorer", pipelineKey: "refactorer", default: "coder", allowDisable: true, label: "Refactorer (clarity passes)" },
|
|
49
|
+
{ key: "impeccable", pipelineKey: "impeccable", default: "coder", allowDisable: true, label: "Impeccable (UI/UX audit — frontend only)" },
|
|
50
|
+
{ key: "perf", pipelineKey: "perf", default: "coder", allowDisable: true, label: "Perf (Core Web Vitals gate — frontend only)" },
|
|
51
|
+
{ key: "hu_reviewer", pipelineKey: "hu_reviewer", default: "reviewer", allowDisable: true, label: "HU Reviewer (mandatory user-story certification)" },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
// ---- Sub-asks ----
|
|
55
|
+
|
|
56
|
+
async function askAgents(wizard, available, config, logger) {
|
|
57
|
+
if (available.length === 1) {
|
|
58
|
+
const only = available[0].name;
|
|
59
|
+
logger.info(`Only one agent available: ${only}. Using it for all roles.\n`);
|
|
60
|
+
config.coder = only;
|
|
61
|
+
config.reviewer = only;
|
|
62
|
+
config.roles.coder.provider = only;
|
|
63
|
+
config.roles.reviewer.provider = only;
|
|
64
|
+
return { coder: only, reviewer: only };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const agentOptions = available.map((a) => ({
|
|
68
|
+
label: `${a.name} (${a.version})`,
|
|
69
|
+
value: a.name,
|
|
70
|
+
available: true,
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
const coder = await wizard.select("Select default CODER agent:", agentOptions);
|
|
74
|
+
config.coder = coder;
|
|
75
|
+
config.roles.coder.provider = coder;
|
|
76
|
+
logger.info(` -> Coder: ${coder}`);
|
|
77
|
+
|
|
78
|
+
const reviewer = await wizard.select("Select default REVIEWER agent:", agentOptions);
|
|
79
|
+
config.reviewer = reviewer;
|
|
80
|
+
config.roles.reviewer.provider = reviewer;
|
|
81
|
+
logger.info(` -> Reviewer: ${reviewer}`);
|
|
82
|
+
|
|
83
|
+
return { coder, reviewer };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* For each non-coder/non-reviewer role, ask: use coder default | use reviewer
|
|
88
|
+
* default | pick a specific CLI | (when allowed) disable. The choice is
|
|
89
|
+
* persisted as `config.roles.<role>.provider` (null means "inherit from
|
|
90
|
+
* coder" at runtime). Pipeline-flag-gated roles also flip
|
|
91
|
+
* `config.pipeline.<role>.enabled`.
|
|
92
|
+
*
|
|
93
|
+
* `available.length === 1` short-circuits: every role uses the only CLI
|
|
94
|
+
* available, no point asking.
|
|
95
|
+
*/
|
|
96
|
+
async function askPerRoleProviders(wizard, available, config, { coder, reviewer }, logger, ask = true) {
|
|
97
|
+
if (available.length <= 1) return;
|
|
98
|
+
|
|
99
|
+
// Make sure containers exist for every role/pipeline key we touch —
|
|
100
|
+
// older configs may have a sparser shape than DEFAULTS.
|
|
101
|
+
config.roles = config.roles || {};
|
|
102
|
+
config.pipeline = config.pipeline || {};
|
|
103
|
+
|
|
104
|
+
// KJC-TSK-0571: per-role provider selection is advanced. By default every
|
|
105
|
+
// role inherits its provider from coder/reviewer — a newcomer has no context
|
|
106
|
+
// to pick an agent for "refactorer" or "perf". Only init the containers.
|
|
107
|
+
if (!ask) {
|
|
108
|
+
for (const role of PER_ROLE_QUESTIONS) {
|
|
109
|
+
config.roles[role.key] = config.roles[role.key] || { provider: null, model: null };
|
|
110
|
+
if (role.pipelineKey) config.pipeline[role.pipelineKey] = config.pipeline[role.pipelineKey] || { enabled: false };
|
|
111
|
+
}
|
|
112
|
+
logger.info(" Agent routing: every role inherits from your coder/reviewer (default).");
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
logger.info("");
|
|
117
|
+
logger.info("Configure provider per role (Enter accepts the default):");
|
|
118
|
+
|
|
119
|
+
for (const role of PER_ROLE_QUESTIONS) {
|
|
120
|
+
config.roles[role.key] = config.roles[role.key] || { provider: null, model: null };
|
|
121
|
+
if (role.pipelineKey) {
|
|
122
|
+
config.pipeline[role.pipelineKey] = config.pipeline[role.pipelineKey] || { enabled: false };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const defaultLabel = role.default === "coder" ? `coder (${coder})` : `reviewer (${reviewer})`;
|
|
126
|
+
const baseOptions = [
|
|
127
|
+
{ label: `Use ${defaultLabel} — default`, value: "__default__", available: true },
|
|
128
|
+
];
|
|
129
|
+
for (const a of available) {
|
|
130
|
+
if ((role.default === "coder" && a.name === coder) || (role.default === "reviewer" && a.name === reviewer)) continue;
|
|
131
|
+
baseOptions.push({ label: a.name, value: a.name, available: true });
|
|
132
|
+
}
|
|
133
|
+
if (role.allowDisable) {
|
|
134
|
+
baseOptions.push({ label: "Disable role", value: "__disabled__", available: true });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const choice = await wizard.select(` ${role.label}:`, baseOptions);
|
|
138
|
+
|
|
139
|
+
if (choice === "__disabled__") {
|
|
140
|
+
if (role.pipelineKey) config.pipeline[role.pipelineKey].enabled = false;
|
|
141
|
+
logger.info(` -> ${role.key}: disabled`);
|
|
142
|
+
} else if (choice === "__default__") {
|
|
143
|
+
// Inherit from coder/reviewer at runtime — leave provider null.
|
|
144
|
+
config.roles[role.key].provider = null;
|
|
145
|
+
if (role.pipelineKey) config.pipeline[role.pipelineKey].enabled = true;
|
|
146
|
+
logger.info(` -> ${role.key}: ${defaultLabel}`);
|
|
147
|
+
} else {
|
|
148
|
+
config.roles[role.key].provider = choice;
|
|
149
|
+
if (role.pipelineKey) config.pipeline[role.pipelineKey].enabled = true;
|
|
150
|
+
logger.info(` -> ${role.key}: ${choice}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* KJC-TSK-0415: Plan B — fallback chain. Cuando el provider primario se
|
|
157
|
+
* queda sin créditos (QUOTA_EXHAUSTED_DAILY/MONTHLY) y el cooldown excede
|
|
158
|
+
* max_wait_hours, Brain switchea al fallback en vez de hibernar.
|
|
159
|
+
*
|
|
160
|
+
* Solo pregunta si hay >= 2 providers disponibles (sin alternativas reales
|
|
161
|
+
* no tiene sentido el fallback).
|
|
162
|
+
*/
|
|
163
|
+
async function askFallbackChain(wizard, available, config, { coder, reviewer }, logger) {
|
|
164
|
+
if (available.length < 2) return;
|
|
165
|
+
|
|
166
|
+
logger.info("");
|
|
167
|
+
logger.info("Plan B — automatic fallback when a provider runs out of credits:");
|
|
168
|
+
const enable = await wizard.confirm(
|
|
169
|
+
" Configure a fallback? (default 12h; if the cooldown exceeds it, switch to the alternate provider)",
|
|
170
|
+
true
|
|
171
|
+
);
|
|
172
|
+
if (!enable) return;
|
|
173
|
+
|
|
174
|
+
const maxHoursRaw = await wizard.input(" Max wait before falling back (hours):", "12");
|
|
175
|
+
const maxHours = Number.parseFloat(maxHoursRaw) || 12;
|
|
176
|
+
|
|
177
|
+
config.roles = config.roles || {};
|
|
178
|
+
const KEY_ROLES = [
|
|
179
|
+
{ key: "coder", label: "coder", primary: coder },
|
|
180
|
+
{ key: "reviewer", label: "reviewer", primary: reviewer },
|
|
181
|
+
{ key: "planner", label: "planner", primary: coder },
|
|
182
|
+
];
|
|
183
|
+
for (const role of KEY_ROLES) {
|
|
184
|
+
config.roles[role.key] = config.roles[role.key] || {};
|
|
185
|
+
const opts = [{ label: "No fallback (hibernate)", value: "__none__", available: true }];
|
|
186
|
+
for (const a of available) {
|
|
187
|
+
if (a.name === role.primary) continue;
|
|
188
|
+
opts.push({ label: a.name, value: a.name, available: true });
|
|
189
|
+
}
|
|
190
|
+
const choice = await wizard.select(` Fallback for ${role.label} when ${role.primary} runs out of credits:`, opts);
|
|
191
|
+
if (choice === "__none__") {
|
|
192
|
+
delete config.roles[role.key].fallback;
|
|
193
|
+
logger.info(` -> ${role.key}: no fallback (will hibernate)`);
|
|
194
|
+
} else {
|
|
195
|
+
config.roles[role.key].fallback = { provider: choice, max_wait_hours: maxHours };
|
|
196
|
+
logger.info(` -> ${role.key}: ${role.primary} → ${choice} (after ${maxHours}h)`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function askMethodology(wizard, config, logger) {
|
|
202
|
+
const methodology = await wizard.select("Development methodology:", [
|
|
203
|
+
{ label: "TDD (test-driven development)", value: "tdd", available: true },
|
|
204
|
+
{ label: "Standard (no TDD enforcement)", value: "standard", available: true },
|
|
205
|
+
]);
|
|
206
|
+
config.development.methodology = methodology;
|
|
207
|
+
config.development.require_test_changes = methodology === "tdd";
|
|
208
|
+
logger.info(` -> Methodology: ${methodology}`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function askLanguages(wizard, config, logger) {
|
|
212
|
+
const detectedLocale = detectOsLocale();
|
|
213
|
+
const allLangEntries = Object.entries(SUPPORTED_LANGUAGES).map(([code, name]) => ({
|
|
214
|
+
label: `${name} (${code})`,
|
|
215
|
+
value: code,
|
|
216
|
+
available: true,
|
|
217
|
+
}));
|
|
218
|
+
const languageOptions = [
|
|
219
|
+
...allLangEntries.filter((o) => o.value === detectedLocale),
|
|
220
|
+
...allLangEntries.filter((o) => o.value !== detectedLocale),
|
|
221
|
+
];
|
|
222
|
+
|
|
223
|
+
const pipelineLang = await wizard.select("Language for agent messages and reports:", languageOptions);
|
|
224
|
+
config.language = pipelineLang;
|
|
225
|
+
logger.info(` -> Pipeline language: ${pipelineLang}`);
|
|
226
|
+
|
|
227
|
+
const huLangOptions = [
|
|
228
|
+
...allLangEntries.filter((o) => o.value === pipelineLang),
|
|
229
|
+
...allLangEntries.filter((o) => o.value !== pipelineLang),
|
|
230
|
+
];
|
|
231
|
+
const huLang = await wizard.select("Language for user stories (HUs):", huLangOptions);
|
|
232
|
+
config.hu_language = huLang;
|
|
233
|
+
logger.info(` -> User-story language: ${huLang}`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function askGitAutomation(wizard, config, logger) {
|
|
237
|
+
config.git = config.git || {};
|
|
238
|
+
const autoCommit = await wizard.confirm("Auto-commit per user story? (recommended for `kj run` so commits show up in git log)", false);
|
|
239
|
+
config.git.auto_commit = autoCommit;
|
|
240
|
+
logger.info(` -> auto_commit: ${autoCommit}`);
|
|
241
|
+
|
|
242
|
+
const autoPush = await wizard.confirm("Auto-push to remote after each commit?", false);
|
|
243
|
+
config.git.auto_push = autoPush;
|
|
244
|
+
logger.info(` -> auto_push: ${autoPush}`);
|
|
245
|
+
|
|
246
|
+
const autoPr = await wizard.confirm("Auto-open a Pull Request when the run finishes?", false);
|
|
247
|
+
config.git.auto_pr = autoPr;
|
|
248
|
+
logger.info(` -> auto_pr: ${autoPr}`);
|
|
249
|
+
|
|
250
|
+
// Branch prefix only worth asking when auto_commit is on; otherwise leave default.
|
|
251
|
+
if (autoCommit) {
|
|
252
|
+
const prefix = await wizard.ask("Branch prefix (e.g. feat/, kj/, ai/) [feat/]: ");
|
|
253
|
+
config.git.branch_prefix = (prefix && prefix.trim()) || "feat/";
|
|
254
|
+
logger.info(` -> branch_prefix: ${config.git.branch_prefix}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function askBoardSecurity(wizard, config, logger) {
|
|
259
|
+
if (!config.hu_board?.enabled) return;
|
|
260
|
+
|
|
261
|
+
const bindChoice = await wizard.select(
|
|
262
|
+
"HU Board bind host:",
|
|
263
|
+
[
|
|
264
|
+
{ label: "127.0.0.1 (loopback only — recommended for personal laptops)", value: "127.0.0.1", available: true },
|
|
265
|
+
{ label: "0.0.0.0 (LAN exposure — token auth auto-enforced for non-loopback peers)", value: "0.0.0.0", available: true },
|
|
266
|
+
],
|
|
267
|
+
);
|
|
268
|
+
config.hu_board.bind = bindChoice;
|
|
269
|
+
logger.info(` -> HU Board bind: ${bindChoice}`);
|
|
270
|
+
|
|
271
|
+
if (bindChoice === "0.0.0.0") {
|
|
272
|
+
logger.info(" A secret token will be auto-generated on first start at ~/.karajan/hu-board/token (mode 0600).");
|
|
273
|
+
logger.info(" Open the URL printed by `kj board start` to grab it.");
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const portInput = await wizard.ask("HU Board port [4000]: ");
|
|
277
|
+
const port = Number.parseInt(portInput, 10);
|
|
278
|
+
config.hu_board.port = Number.isFinite(port) && port > 0 && port < 65536 ? port : 4000;
|
|
279
|
+
logger.info(` -> HU Board port: ${config.hu_board.port}`);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Explicit, opt-in telemetry consent. We do NOT inherit a "Karajan-installed
|
|
284
|
+
* users want to help" assumption — silence and missing config keys both mean
|
|
285
|
+
* "no". The prompt is English to match the rest of the wizard (KJC-BUG-0088).
|
|
286
|
+
* See src/utils/telemetry.js and the
|
|
287
|
+
* "Privacy & Telemetry" section of README.md for what gets sent.
|
|
288
|
+
*/
|
|
289
|
+
async function askTelemetry(wizard, config, logger) {
|
|
290
|
+
// English to stay consistent with the rest of the wizard (KJC-BUG-0088); the
|
|
291
|
+
// user's chosen pipeline/HU language drives `kj run` output, not this prompt.
|
|
292
|
+
const t = {
|
|
293
|
+
question: "Help improve Karajan by sending anonymous telemetry to the project?",
|
|
294
|
+
detail: " (version, OS, commands, pipeline duration — no code, no tasks, no personal data)",
|
|
295
|
+
previewIntro: " Example of the exact payload sent when you run `kj run` (inspect more with `kj telemetry preview`):",
|
|
296
|
+
enabled: " -> Telemetry: enabled (thank you!)",
|
|
297
|
+
disabled: " -> Telemetry: disabled",
|
|
298
|
+
};
|
|
299
|
+
logger.info(t.detail);
|
|
300
|
+
const example = buildTelemetryPayload("cli_command", { version: "x.y.z", command: "run" });
|
|
301
|
+
logger.info(t.previewIntro);
|
|
302
|
+
logger.info(` ${JSON.stringify(example)}`);
|
|
303
|
+
const enabled = await wizard.confirm(t.question, false);
|
|
304
|
+
config.telemetry = enabled;
|
|
305
|
+
logger.info(enabled ? t.enabled : t.disabled);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async function runWizard(config, logger) {
|
|
309
|
+
const agents = await detectAvailableAgents();
|
|
310
|
+
const available = agents.filter((a) => a.available);
|
|
311
|
+
const unavailable = agents.filter((a) => !a.available);
|
|
312
|
+
|
|
313
|
+
logger.info("\n=== Karajan Code Setup Wizard ===\n");
|
|
314
|
+
|
|
315
|
+
if (available.length === 0) {
|
|
316
|
+
logger.warn("No AI agents detected. Install at least one:");
|
|
317
|
+
for (const agent of unavailable) {
|
|
318
|
+
logger.warn(` ${agent.name}: ${agent.install}`);
|
|
319
|
+
}
|
|
320
|
+
logger.info("\nGenerating config with defaults (claude). You can change it later in kj.config.yml.\n");
|
|
321
|
+
return config;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
logger.info("Detected agents:");
|
|
325
|
+
for (const agent of available) {
|
|
326
|
+
logger.info(` OK ${agent.name} (${agent.version})`);
|
|
327
|
+
}
|
|
328
|
+
for (const agent of unavailable) {
|
|
329
|
+
logger.info(` MISS ${agent.name}`);
|
|
330
|
+
}
|
|
331
|
+
logger.info("");
|
|
332
|
+
|
|
333
|
+
const wizard = createWizard();
|
|
334
|
+
try {
|
|
335
|
+
const { coder, reviewer } = await askAgents(wizard, available, config, logger);
|
|
336
|
+
|
|
337
|
+
const enableTriage = await wizard.confirm("Enable triage (auto-pick a lighter pipeline for simple tasks)?", false);
|
|
338
|
+
config.pipeline.triage = config.pipeline.triage || {};
|
|
339
|
+
config.pipeline.triage.enabled = enableTriage;
|
|
340
|
+
logger.info(` -> Triage: ${enableTriage ? "enabled" : "disabled"}`);
|
|
341
|
+
|
|
342
|
+
const enableSonar = await wizard.confirm("Enable SonarQube analysis?", true);
|
|
343
|
+
config.sonarqube.enabled = enableSonar;
|
|
344
|
+
logger.info(` -> SonarQube: ${enableSonar ? "enabled" : "disabled"}`);
|
|
345
|
+
|
|
346
|
+
const enableHuBoard = await wizard.confirm("Enable the HU Board — a local web UI to track user stories (HUs)?", false);
|
|
347
|
+
config.hu_board = config.hu_board || {};
|
|
348
|
+
config.hu_board.enabled = enableHuBoard;
|
|
349
|
+
if (enableHuBoard) {
|
|
350
|
+
config.hu_board.port = 4000;
|
|
351
|
+
config.hu_board.auto_start = true;
|
|
352
|
+
}
|
|
353
|
+
logger.info(` -> HU Board: ${enableHuBoard ? "enabled (auto-start on kj run)" : "disabled"}`);
|
|
354
|
+
|
|
355
|
+
// KJC-TSK-0571: per-role providers + fallback chains are advanced and
|
|
356
|
+
// collapse a newcomer's first run by ~12 questions. Ask one gate; default
|
|
357
|
+
// off → every role inherits from coder/reviewer.
|
|
358
|
+
const advancedRouting =
|
|
359
|
+
available.length > 1 &&
|
|
360
|
+
(await wizard.confirm(
|
|
361
|
+
"Configure advanced agent routing (a different AI per role + fallback chains)? Most users don't need this.",
|
|
362
|
+
false
|
|
363
|
+
));
|
|
364
|
+
await askPerRoleProviders(wizard, available, config, { coder, reviewer }, logger, advancedRouting);
|
|
365
|
+
if (advancedRouting) {
|
|
366
|
+
await askFallbackChain(wizard, available, config, { coder, reviewer }, logger);
|
|
367
|
+
}
|
|
368
|
+
await askMethodology(wizard, config, logger);
|
|
369
|
+
await askLanguages(wizard, config, logger);
|
|
370
|
+
|
|
371
|
+
logger.info("");
|
|
372
|
+
logger.info("Git automation:");
|
|
373
|
+
await askGitAutomation(wizard, config, logger);
|
|
374
|
+
|
|
375
|
+
// Iteration gate (KJC-TSK-0628): opt-in step mode, default no.
|
|
376
|
+
config.session = config.session || {};
|
|
377
|
+
const stepMode = await wizard.confirm(
|
|
378
|
+
"Pause after each iteration with a report and ask before continuing (step mode)?",
|
|
379
|
+
false,
|
|
380
|
+
);
|
|
381
|
+
config.session.iteration_gate = stepMode;
|
|
382
|
+
logger.info(` -> iteration_gate: ${stepMode}`);
|
|
383
|
+
|
|
384
|
+
if (enableHuBoard) {
|
|
385
|
+
logger.info("");
|
|
386
|
+
logger.info("HU Board security:");
|
|
387
|
+
await askBoardSecurity(wizard, config, logger);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
logger.info("");
|
|
391
|
+
logger.info("Privacy:");
|
|
392
|
+
await askTelemetry(wizard, config, logger);
|
|
393
|
+
|
|
394
|
+
logger.info("");
|
|
395
|
+
} finally {
|
|
396
|
+
wizard.close();
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return config;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Backwards-compatible alias for `writeConfig`. Pre-KJC-BUG-0036 this
|
|
404
|
+
* wrapper was the *only* writer that stripped `sonarqube.enabled`
|
|
405
|
+
* before serialising — we needed it because the global `writeConfig`
|
|
406
|
+
* would happily persist the wizard's transient hint. KJC-BUG-0036
|
|
407
|
+
* moved that strip (and the equivalent `_deprecated` strip) into
|
|
408
|
+
* `writeConfig` itself, so this is now a thin alias kept around to
|
|
409
|
+
* keep callers and tests stable. Prefer `writeConfig` in new code.
|
|
410
|
+
*
|
|
411
|
+
* @param {string} configPath
|
|
412
|
+
* @param {object} config
|
|
413
|
+
*/
|
|
414
|
+
export async function writeInitConfig(configPath, config) {
|
|
415
|
+
await writeConfig(configPath, config);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
async function handleConfigSetup({ config, configExists, interactive, configPath, logger }) {
|
|
419
|
+
if (configExists && interactive) {
|
|
420
|
+
const wizard = createWizard();
|
|
421
|
+
try {
|
|
422
|
+
const reconfigure = await wizard.confirm("Config already exists. Reconfigure?", false);
|
|
423
|
+
if (reconfigure) {
|
|
424
|
+
const updated = await runWizard(config, logger);
|
|
425
|
+
await writeInitConfig(configPath, updated);
|
|
426
|
+
logger.info(`Updated ${configPath}`);
|
|
427
|
+
} else {
|
|
428
|
+
logger.info("Keeping existing config.");
|
|
429
|
+
}
|
|
430
|
+
} finally {
|
|
431
|
+
wizard.close();
|
|
432
|
+
}
|
|
433
|
+
} else if (!configExists && interactive) {
|
|
434
|
+
const updated = await runWizard(config, logger);
|
|
435
|
+
await writeInitConfig(configPath, updated);
|
|
436
|
+
logger.info(`Created ${configPath}`);
|
|
437
|
+
} else if (!configExists) {
|
|
438
|
+
await writeInitConfig(configPath, config);
|
|
439
|
+
logger.info(`Created ${configPath}`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
async function ensureReviewRules(reviewRulesPath, logger) {
|
|
444
|
+
if (await exists(reviewRulesPath)) return;
|
|
445
|
+
await fs.writeFile(
|
|
446
|
+
reviewRulesPath,
|
|
447
|
+
"# Review Rules\n\n- Focus on security, correctness, and test coverage.\n",
|
|
448
|
+
"utf8"
|
|
449
|
+
);
|
|
450
|
+
logger.info("Created .karajan/review-rules.md");
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
async function ensureCoderRules(coderRulesPath, logger) {
|
|
454
|
+
if (await exists(coderRulesPath)) return;
|
|
455
|
+
const templatePath = path.join(getTemplatesRoot(), "coder-rules.md");
|
|
456
|
+
let content;
|
|
457
|
+
try {
|
|
458
|
+
content = await fs.readFile(templatePath, "utf8");
|
|
459
|
+
} catch { /* template not found, use inline default */
|
|
460
|
+
content = [
|
|
461
|
+
"# Coder Rules",
|
|
462
|
+
"",
|
|
463
|
+
"## File modification safety",
|
|
464
|
+
"",
|
|
465
|
+
"- NEVER overwrite existing files entirely. Always make targeted, minimal edits.",
|
|
466
|
+
"- After each edit, verify with `git diff` that ONLY the intended lines changed.",
|
|
467
|
+
"- Do not modify code unrelated to the task.",
|
|
468
|
+
""
|
|
469
|
+
].join("\n");
|
|
470
|
+
}
|
|
471
|
+
await fs.writeFile(coderRulesPath, content, "utf8");
|
|
472
|
+
logger.info("Created .karajan/coder-rules.md");
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
async function setupSonarQube(config, logger, { interactive } = {}) {
|
|
476
|
+
if (config.sonarqube?.enabled === false) {
|
|
477
|
+
logger.info("SonarQube disabled — skipping container setup.");
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Check Docker first — if not available, disable sonar gracefully
|
|
482
|
+
const dockerCheck = await runCommand("docker", ["--version"]);
|
|
483
|
+
if (dockerCheck.exitCode !== 0) {
|
|
484
|
+
logger.info("Docker not found — disabling SonarQube. Install Docker to enable it later.");
|
|
485
|
+
config.sonarqube.enabled = false;
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const vmCheck = await checkVmMaxMapCount(os.platform());
|
|
490
|
+
if (!vmCheck.ok) {
|
|
491
|
+
logger.warn(`vm.max_map_count check failed: ${vmCheck.reason}`);
|
|
492
|
+
if (vmCheck.fix) {
|
|
493
|
+
logger.warn(`Fix: ${vmCheck.fix}`);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const sonar = await sonarUp();
|
|
498
|
+
if (sonar.exitCode !== 0) {
|
|
499
|
+
logger.info(`SonarQube could not be started — disabling for now. You can enable it later with 'kj sonar start'.`);
|
|
500
|
+
config.sonarqube.enabled = false;
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
logger.info("SonarQube container started");
|
|
505
|
+
|
|
506
|
+
// KJC-TSK-0367: try to bootstrap the analysis token automatically. The
|
|
507
|
+
// bootstrapper logs in with admin/admin, rotates the password if it's
|
|
508
|
+
// still the default, and POSTs to /api/user_tokens/generate. On 401 or
|
|
509
|
+
// any other failure, we fall back to the manual flow that was the only
|
|
510
|
+
// option before this card.
|
|
511
|
+
if (interactive) {
|
|
512
|
+
logger.info("Bootstrapping SonarQube token automatically...");
|
|
513
|
+
const result = await bootstrapSonarToken({
|
|
514
|
+
host: config.sonarqube.host || "http://localhost:9000",
|
|
515
|
+
configPath: getConfigPath(),
|
|
516
|
+
});
|
|
517
|
+
if (result.ok) {
|
|
518
|
+
config.sonarqube.token = result.token;
|
|
519
|
+
logger.info(` -> Sonar token saved to ${result.savedTo}`);
|
|
520
|
+
if (result.passwordRotated) {
|
|
521
|
+
logger.info(` -> Sonar admin password rotated. New value at ${result.passwordSavedTo}`);
|
|
522
|
+
} else if (result.passwordRotationError) {
|
|
523
|
+
// KJC-BUG-0035: rotation failed but token generation succeeded.
|
|
524
|
+
// admin/admin is still live — surface it loudly so the user can
|
|
525
|
+
// fix it manually before someone else exploits it.
|
|
526
|
+
logger.warn(` -> SonarQube admin password NOT rotated: ${result.passwordRotationError}`);
|
|
527
|
+
logger.warn(" admin/admin remains active. Rotate it manually at http://localhost:9000/account/security");
|
|
528
|
+
}
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
logger.warn(`Could not auto-generate Sonar token: ${result.reason}`);
|
|
532
|
+
logger.info("Falling back to manual flow:");
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
logger.info("");
|
|
536
|
+
logger.info("To configure the SonarQube token manually:");
|
|
537
|
+
logger.info(" 1. Open http://localhost:9000");
|
|
538
|
+
logger.info(" 2. Log in (default credentials: admin / admin)");
|
|
539
|
+
logger.info(" 3. Go to: My Account > Security > Generate Token");
|
|
540
|
+
logger.info(" 4. Name: karajan-cli, Type: Global Analysis Token");
|
|
541
|
+
logger.info(" 5. Set the token in ~/.karajan/kj.config.yml under sonarqube.token");
|
|
542
|
+
logger.info(' or export KJ_SONAR_TOKEN="<your-token>"');
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
async function scaffoldCiGateway(config, flags, logger) {
|
|
546
|
+
if (!config.ci?.enabled && !flags.scaffoldCi) return;
|
|
547
|
+
const projectDir = process.cwd();
|
|
548
|
+
const workflowDir = path.join(projectDir, ".github", "workflows");
|
|
549
|
+
await ensureDir(workflowDir);
|
|
550
|
+
|
|
551
|
+
const templatesDir = path.join(getTemplatesRoot(), "workflows");
|
|
552
|
+
const workflows = ["kj-ci-gateway.yml", "automerge.yml", "houston-override.yml"];
|
|
553
|
+
|
|
554
|
+
for (const wf of workflows) {
|
|
555
|
+
const destPath = path.join(workflowDir, wf);
|
|
556
|
+
if (await exists(destPath)) {
|
|
557
|
+
logger.info(`${wf} already exists — skipping`);
|
|
558
|
+
} else {
|
|
559
|
+
const srcPath = path.join(templatesDir, wf);
|
|
560
|
+
try {
|
|
561
|
+
const content = await fs.readFile(srcPath, "utf8");
|
|
562
|
+
await fs.writeFile(destPath, content, "utf8");
|
|
563
|
+
logger.info(`Created ${path.relative(projectDir, destPath)}`);
|
|
564
|
+
} catch (err) {
|
|
565
|
+
logger.warn(`Could not scaffold ${wf}: ${err.message}`);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
logger.info("");
|
|
571
|
+
logger.info("Karajan CI Gateway scaffolded. Next steps:");
|
|
572
|
+
logger.info(" 1. Create a GitHub App named 'kj-reviewer' with pull_request write permissions");
|
|
573
|
+
logger.info(" 2. Install the App on your repository");
|
|
574
|
+
logger.info(" 3. Add secrets: KJ_CI_APP_ID and KJ_CI_PRIVATE_KEY");
|
|
575
|
+
logger.info(" 4. Push the workflow files and enable 'kj run --enable-ci'");
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// KJC-TSK-0436 — RAG embedder bootstrap. Default-on, opt-out with
|
|
579
|
+
// `--no-ollama`. Skipped automatically when the host can't run it
|
|
580
|
+
// (no Docker, daemon down, < 4 GB free RAM) — a one-line warning
|
|
581
|
+
// tells the user how to wire an external embedder instead.
|
|
582
|
+
async function bootstrapOllama({ flags, config, logger, interactive }) {
|
|
583
|
+
// Commander maps `--no-ollama` to `flags.ollama=false`.
|
|
584
|
+
const skip = flags?.noOllama === true || flags?.ollama === false;
|
|
585
|
+
if (skip) {
|
|
586
|
+
logger.info("Ollama bootstrap skipped (--no-ollama). RAG embedder will be unavailable until you configure one.");
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
const cap = await checkOllamaCapability();
|
|
590
|
+
if (!cap.capable) {
|
|
591
|
+
logger.warn(`Ollama bootstrap skipped: ${cap.reasons.join("; ")}.`);
|
|
592
|
+
logger.warn(" Wire an external embedder via `config.rag.embedder` or rerun `kj init` after fixing the cause.");
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
const result = await ollamaUp(config?.rag?.embedder || null);
|
|
596
|
+
if (result.exitCode !== 0) {
|
|
597
|
+
logger.warn(`Ollama bootstrap failed: ${result.stderr || result.stdout}`);
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
if (result.reusedHost) {
|
|
601
|
+
logger.info(`Reusing existing Ollama at ${result.reusedHost}.`);
|
|
602
|
+
} else {
|
|
603
|
+
logger.info("Ollama container started. Waiting for /api/tags...");
|
|
604
|
+
const ollamaCfg = normalizeOllamaConfig(config?.rag?.embedder || {});
|
|
605
|
+
const ready = await waitForOllamaReady(ollamaCfg.host, ollamaCfg.timeouts.readyMs);
|
|
606
|
+
if (!ready) {
|
|
607
|
+
logger.warn(`Ollama did not become ready within ${ollamaCfg.timeouts.readyMs} ms. Continuing — pull the model manually with \`kj ollama pull nomic-embed-text\`.`);
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
const model = config?.rag?.embedder?.model || "nomic-embed-text";
|
|
612
|
+
logger.info(`Pulling embedder model ${model} (first run downloads ~270 MB)...`);
|
|
613
|
+
const pull = await pullOllamaModel(model, { containerName: config?.rag?.embedder?.container_name });
|
|
614
|
+
if (pull.exitCode === 0) {
|
|
615
|
+
logger.info(` -> ${model} ready. RAG embedder available.`);
|
|
616
|
+
if (interactive) logger.info(" -> Enable pre-loop retrieval with `yq -i '.rag.preload.enabled = true' " + getConfigPath() + "`");
|
|
617
|
+
} else {
|
|
618
|
+
logger.warn(`Model pull failed: ${pull.stderr || pull.stdout}. Retry with \`kj ollama pull ${model}\`.`);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
async function installSkills(logger, interactive) {
|
|
623
|
+
const projectDir = process.cwd();
|
|
624
|
+
const commandsDir = path.join(projectDir, ".claude", "commands");
|
|
625
|
+
const skillsTemplateDir = path.join(getTemplatesRoot(), "skills");
|
|
626
|
+
|
|
627
|
+
let doInstall = true;
|
|
628
|
+
if (interactive) {
|
|
629
|
+
const wizard = createWizard();
|
|
630
|
+
try {
|
|
631
|
+
doInstall = await wizard.confirm("Install Karajan skills as slash commands (/kj-code, /kj-review, etc.)?", true);
|
|
632
|
+
} finally {
|
|
633
|
+
wizard.close();
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (!doInstall) {
|
|
638
|
+
logger.info("Skills installation skipped.");
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
await ensureDir(commandsDir);
|
|
643
|
+
|
|
644
|
+
let installed = 0;
|
|
645
|
+
try {
|
|
646
|
+
const files = await fs.readdir(skillsTemplateDir);
|
|
647
|
+
for (const file of files) {
|
|
648
|
+
if (!file.endsWith(".md")) continue;
|
|
649
|
+
const src = path.join(skillsTemplateDir, file);
|
|
650
|
+
const dest = path.join(commandsDir, file);
|
|
651
|
+
if (await exists(dest)) {
|
|
652
|
+
logger.info(` ${file} already exists — skipping`);
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
const content = await fs.readFile(src, "utf8");
|
|
656
|
+
await fs.writeFile(dest, content, "utf8");
|
|
657
|
+
installed += 1;
|
|
658
|
+
}
|
|
659
|
+
} catch (err) {
|
|
660
|
+
logger.warn(`Could not install skills: ${err.message}`);
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if (installed > 0) {
|
|
665
|
+
logger.info(`Installed ${installed} Karajan skill(s) in .claude/commands/`);
|
|
666
|
+
logger.info("Available as slash commands: /kj-run, /kj-code, /kj-review, /kj-test, /kj-security, /kj-discover, /kj-architect, /kj-sonar, /kj-rag-query");
|
|
667
|
+
} else {
|
|
668
|
+
logger.info("All skills already installed.");
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const GITIGNORE_ENTRIES = [
|
|
673
|
+
{ pattern: ".kj/", comment: "Karajan runtime (logs, worktrees)" },
|
|
674
|
+
{ pattern: ".agent/", comment: "Agent skills (OpenSkills)" },
|
|
675
|
+
{ pattern: ".scannerwork/", comment: "SonarQube scanner temp" },
|
|
676
|
+
{ pattern: ".reviews/", comment: "Legacy review artifacts" },
|
|
677
|
+
];
|
|
678
|
+
|
|
679
|
+
async function ensureGitignoreEntries(projectDir, logger) {
|
|
680
|
+
const gitignorePath = path.join(projectDir, ".gitignore");
|
|
681
|
+
let content;
|
|
682
|
+
try {
|
|
683
|
+
content = await fs.readFile(gitignorePath, "utf8");
|
|
684
|
+
} catch {
|
|
685
|
+
// No .gitignore yet — will create one
|
|
686
|
+
}
|
|
687
|
+
if (!content) content = "";
|
|
688
|
+
|
|
689
|
+
const missing = GITIGNORE_ENTRIES.filter(e => !content.includes(e.pattern));
|
|
690
|
+
if (missing.length > 0) {
|
|
691
|
+
const block = [
|
|
692
|
+
"",
|
|
693
|
+
"# Karajan Code",
|
|
694
|
+
...missing.map(e => e.pattern)
|
|
695
|
+
].join("\n") + "\n";
|
|
696
|
+
await fs.appendFile(gitignorePath, block, "utf8");
|
|
697
|
+
logger.info(`Added to .gitignore: ${missing.map(e => e.pattern).join(", ")}`);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// KJC-BUG-0123 (issue #1268): the .karajan entries were missing entirely —
|
|
701
|
+
// and they must land as the CONTRACT BLOCK (`.karajan/*` + re-includes),
|
|
702
|
+
// never a bare `.karajan/` dir-exclude, or git cannot re-include the
|
|
703
|
+
// review-gate/hooks/adrs the whole team inherits (KJC-TSK-0646).
|
|
704
|
+
const res = await ensureContractBlockPresent(projectDir);
|
|
705
|
+
if (res.changed) logger.info("Added the .karajan contract block to .gitignore (verdicts stay local; gate/hooks/adrs tracked)");
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* KJC-TSK-0395: decide where the config is written based on the flags
|
|
710
|
+
* and (when interactive) a wizard prompt. Returns { configPath, scope }.
|
|
711
|
+
* Throws when both --global and --local are passed (mutually exclusive).
|
|
712
|
+
*/
|
|
713
|
+
export async function resolveConfigScope({ flags, interactive }) {
|
|
714
|
+
if (flags?.global && flags?.local) {
|
|
715
|
+
throw new Error("Cannot pass both --global and --local — pick one scope for the config.");
|
|
716
|
+
}
|
|
717
|
+
if (flags?.global) return { configPath: getConfigPath(), scope: "global" };
|
|
718
|
+
if (flags?.local) return { configPath: getProjectConfigPath(process.cwd()), scope: "local" };
|
|
719
|
+
if (interactive) {
|
|
720
|
+
const globalPath = getConfigPath();
|
|
721
|
+
// KJC-BUG-0087: on a true first run (no global config yet) there is no
|
|
722
|
+
// meaningful choice — set up the global config and skip a cold question a
|
|
723
|
+
// newcomer has no context to answer. Only when a global already exists is
|
|
724
|
+
// "override just for this project?" an actual decision worth asking.
|
|
725
|
+
if (!(await exists(globalPath))) {
|
|
726
|
+
return { configPath: globalPath, scope: "global" };
|
|
727
|
+
}
|
|
728
|
+
const wizard = createWizard();
|
|
729
|
+
try {
|
|
730
|
+
const choice = await wizard.select(
|
|
731
|
+
"You already have a global Karajan config. For this project, do you want to…",
|
|
732
|
+
[
|
|
733
|
+
{ value: "global", label: "Use the global config (your default for every project)" },
|
|
734
|
+
{ value: "local", label: `Override it just for this project (${getProjectConfigPath(process.cwd())})` },
|
|
735
|
+
]
|
|
736
|
+
);
|
|
737
|
+
return { configPath: choice === "local" ? getProjectConfigPath(process.cwd()) : globalPath, scope: choice };
|
|
738
|
+
} finally {
|
|
739
|
+
wizard.close();
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
// Non-interactive + no flags → legacy behaviour: global. Keeps CI scripts working.
|
|
743
|
+
return { configPath: getConfigPath(), scope: "global" };
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
export async function initCommand({ logger, flags = {} }) {
|
|
747
|
+
// AB-B (KJC-TSK-0656): with --json, stdout is a machine contract — every
|
|
748
|
+
// human log moves to stderr so the only stdout line is the summary object.
|
|
749
|
+
if (flags.json) {
|
|
750
|
+
const toStderr = (...args) => console.error(...args);
|
|
751
|
+
logger = { ...logger, info: toStderr, warn: toStderr, error: toStderr };
|
|
752
|
+
}
|
|
753
|
+
const karajanHome = getKarajanHome();
|
|
754
|
+
await ensureDir(karajanHome);
|
|
755
|
+
logger.info(`Ensured ${karajanHome} exists`);
|
|
756
|
+
|
|
757
|
+
const interactive = flags.noInteractive !== true && isTTY();
|
|
758
|
+
// AB-B (KJC-TSK-0656): the agent is the UI. When there is no TTY the
|
|
759
|
+
// wizard silently used defaults — say so, so a brain (or a CI log
|
|
760
|
+
// reader) knows WHY nothing was asked and which flags override what.
|
|
761
|
+
if (!interactive && flags.noInteractive !== true) {
|
|
762
|
+
logger.info("No TTY detected — running non-interactive with defaults (pass flags to override; see kj init --help)");
|
|
763
|
+
}
|
|
764
|
+
const { configPath, scope } = await resolveConfigScope({ flags, interactive });
|
|
765
|
+
logger.info(`Config scope: ${scope} → ${configPath}`);
|
|
766
|
+
const karajanDir = path.join(process.cwd(), ".karajan");
|
|
767
|
+
await ensureDir(karajanDir);
|
|
768
|
+
const reviewRulesPath = path.resolve(karajanDir, "review-rules.md");
|
|
769
|
+
const coderRulesPath = path.resolve(karajanDir, "coder-rules.md");
|
|
770
|
+
|
|
771
|
+
const { config } = await loadConfig();
|
|
772
|
+
// KJC-BUG-0087: ask "reconfigure?" only when a config exists AT THE CHOSEN
|
|
773
|
+
// scope path — not when the global cascade has one. Picking local scope in a
|
|
774
|
+
// fresh dir must not report the unrelated global config as "already exists".
|
|
775
|
+
const configExists = await exists(configPath);
|
|
776
|
+
|
|
777
|
+
// Auto-detect language from OS locale for non-interactive mode
|
|
778
|
+
if (!interactive && !configExists) {
|
|
779
|
+
const detectedLocale = detectOsLocale();
|
|
780
|
+
config.language = detectedLocale;
|
|
781
|
+
config.hu_language = detectedLocale;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
await handleConfigSetup({ config, configExists, interactive, configPath, logger });
|
|
785
|
+
await ensureReviewRules(reviewRulesPath, logger);
|
|
786
|
+
await ensureCoderRules(coderRulesPath, logger);
|
|
787
|
+
await ensureGitignoreEntries(process.cwd(), logger);
|
|
788
|
+
await installSkills(logger, interactive);
|
|
789
|
+
await bootstrapOllama({ flags, config, logger, interactive });
|
|
790
|
+
|
|
791
|
+
// Auto-detect project stack and preconfigure
|
|
792
|
+
const stack = await detectProjectStack(process.cwd());
|
|
793
|
+
if (stack.frameworks.length > 0 || stack.language) {
|
|
794
|
+
const parts = [];
|
|
795
|
+
if (stack.frameworks.length > 0) parts.push(stack.frameworks.join(" + "));
|
|
796
|
+
if (stack.language) parts.push(stack.language);
|
|
797
|
+
const stackType = stack.isFullstack ? "fullstack" : stack.isFrontend ? "frontend" : stack.isBackend ? "backend" : "unknown";
|
|
798
|
+
logger.info(`Detected stack: ${parts.join(" / ")} (${stackType})`);
|
|
799
|
+
|
|
800
|
+
if (stack.suggestions.impeccable) {
|
|
801
|
+
config.impeccable = config.impeccable || {};
|
|
802
|
+
config.impeccable.enabled = true;
|
|
803
|
+
logger.info(" -> Impeccable design audit enabled (frontend detected)");
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
if (stack.suggestions.skills.length > 0) {
|
|
807
|
+
logger.info(` -> Suggested skills: ${stack.suggestions.skills.join(", ")}`);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
if (stack.language && !configExists) {
|
|
811
|
+
config.development = config.development || {};
|
|
812
|
+
config.development.methodology = config.development.methodology || "tdd";
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
await writeInitConfig(configPath, config);
|
|
816
|
+
} else {
|
|
817
|
+
logger.info("No project stack detected — using default configuration.");
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// Check RTK availability — auto-install if missing (opt-out: --no-rtk)
|
|
821
|
+
const skipRtk = flags?.noRtk === true || flags?.rtk === false;
|
|
822
|
+
if (skipRtk) {
|
|
823
|
+
logger.info("RTK setup skipped (--no-rtk). Bash outputs will not be token-optimized.");
|
|
824
|
+
} else {
|
|
825
|
+
const rtk = await detectRtk();
|
|
826
|
+
if (rtk.available) {
|
|
827
|
+
logger.info(`RTK ${rtk.version || ""} detected.`);
|
|
828
|
+
} else {
|
|
829
|
+
const installResult = await installRtk(logger);
|
|
830
|
+
if (!installResult.ok) {
|
|
831
|
+
logger.warn("RTK install failed — Bash outputs will not be token-optimized.");
|
|
832
|
+
logger.warn(` Manual install: ${getInstallCommand("rtk")}`);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// Check Squeezr availability — auto-install if missing (opt-out: --no-squeezr)
|
|
838
|
+
const skipSqueezr = flags?.noSqueezr === true || flags?.squeezr === false;
|
|
839
|
+
if (skipSqueezr) {
|
|
840
|
+
logger.info("Squeezr setup skipped (--no-squeezr). Tool outputs will not be compressed.");
|
|
841
|
+
} else {
|
|
842
|
+
const squeezr = await detectSqueezr();
|
|
843
|
+
if (squeezr.available) {
|
|
844
|
+
logger.info(`Squeezr ${squeezr.version || ""} detected.`);
|
|
845
|
+
} else {
|
|
846
|
+
const installResult = await installSqueezr(logger);
|
|
847
|
+
if (!installResult.ok) {
|
|
848
|
+
logger.warn("Squeezr install failed — tool outputs will not be compressed.");
|
|
849
|
+
logger.warn(` Manual install: ${getInstallCommand("squeezr")}`);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// Check ai-trash availability — register PreToolUse hook if binary present
|
|
855
|
+
// (opt-out: --no-ai-trash). The binary itself ships with karajan-code.
|
|
856
|
+
const skipAiTrash = flags?.noAiTrash === true || flags?.aiTrash === false;
|
|
857
|
+
if (skipAiTrash) {
|
|
858
|
+
logger.info("ai-trash setup skipped (--no-ai-trash). Destructive ops will run unprotected.");
|
|
859
|
+
} else {
|
|
860
|
+
const aiTrash = await detectAiTrash();
|
|
861
|
+
if (aiTrash.available) {
|
|
862
|
+
await installAiTrashHook(logger);
|
|
863
|
+
} else {
|
|
864
|
+
logger.warn("ai-trash kj-trash binary not found — destructive ops unprotected.");
|
|
865
|
+
logger.warn(" Install karajan-code globally (npm i -g karajan-code) so kj-trash is on PATH.");
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// Check QMD availability — auto-install if missing and register project
|
|
870
|
+
// collections (docs/, .reviews/, .karajan/plans/). Opt-out: --no-qmd.
|
|
871
|
+
const skipQmd = flags?.noQmd === true || flags?.qmd === false;
|
|
872
|
+
if (skipQmd) {
|
|
873
|
+
logger.info("QMD setup skipped (--no-qmd). Semantic wiki indexing disabled.");
|
|
874
|
+
} else {
|
|
875
|
+
const qmd = await detectQmd();
|
|
876
|
+
if (qmd.available) {
|
|
877
|
+
logger.info(`QMD ${qmd.version || ""} detected.`);
|
|
878
|
+
} else {
|
|
879
|
+
const installResult = await installQmd(logger);
|
|
880
|
+
if (!installResult.ok) {
|
|
881
|
+
logger.warn("QMD install failed — semantic wiki unavailable.");
|
|
882
|
+
logger.warn(` Manual install: ${getInstallCommand("qmd")}`);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
const recheck = await detectQmd();
|
|
886
|
+
if (recheck.available) {
|
|
887
|
+
await registerQmdCollections(process.cwd(), logger);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
await setupSonarQube(config, logger, { interactive });
|
|
892
|
+
await scaffoldCiGateway(config, flags, logger);
|
|
893
|
+
|
|
894
|
+
// Install the quality harness (git hooks, lint/format/commit config, CI
|
|
895
|
+
// quality gates, agent guidelines) through the SAME engine as `kj harden`,
|
|
896
|
+
// so init and harden never drift. Opt-out: --no-harden. Needs a git repo.
|
|
897
|
+
const skipHarden = flags?.noHarden === true || flags?.harden === false;
|
|
898
|
+
if (skipHarden) {
|
|
899
|
+
logger.info("Quality harness skipped (--no-harden).");
|
|
900
|
+
} else if (isGitRepo(process.cwd())) {
|
|
901
|
+
logger.info("Installing quality harness (kj harden)...");
|
|
902
|
+
await hardenCommand({ projectDir: process.cwd(), logger });
|
|
903
|
+
} else {
|
|
904
|
+
logger.info("Not a git repository — run `kj harden` after `git init` to add the quality harness.");
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// Persist any changes setupSonarQube made (token, etc.) back to disk.
|
|
908
|
+
// Use writeInitConfig so the deprecated `sonarqube.enabled` key —
|
|
909
|
+
// which setupSonarQube still mutates as an in-memory hint — never
|
|
910
|
+
// reaches the YAML file.
|
|
911
|
+
await writeInitConfig(configPath, config);
|
|
912
|
+
|
|
913
|
+
// Telemetry: anonymous install event (non-blocking)
|
|
914
|
+
const { readFileSync } = await import("node:fs");
|
|
915
|
+
const { fileURLToPath } = await import("node:url");
|
|
916
|
+
try {
|
|
917
|
+
// TSK-0340: use the already-static `path` default import for resolve/dirname.
|
|
918
|
+
const pkgPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../package.json");
|
|
919
|
+
const version = JSON.parse(readFileSync(pkgPath, "utf8")).version;
|
|
920
|
+
sendTelemetryEvent("install", { version }, config).catch(() => {});
|
|
921
|
+
} catch { /* non-blocking */ }
|
|
922
|
+
|
|
923
|
+
// AB-B (KJC-TSK-0656): machine contract for brains — one JSON object with
|
|
924
|
+
// what init actually did, instead of parsing the human log.
|
|
925
|
+
if (flags.json) {
|
|
926
|
+
console.log(JSON.stringify({
|
|
927
|
+
ok: true, configPath, scope, interactive,
|
|
928
|
+
skipped: ["ollama", "rtk", "squeezr", "qmd", "harden"].filter((t) => flags[t] === false),
|
|
929
|
+
}));
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// Clear close so a first-time user knows setup finished and what to do next,
|
|
934
|
+
// instead of being left at the end of a wall of detection logs (KJC-BUG-0088).
|
|
935
|
+
logger.info("");
|
|
936
|
+
logger.info("✓ Karajan is set up in this project.");
|
|
937
|
+
logger.info(' Next: kj run "describe what to build or fix"');
|
|
938
|
+
logger.info(" More: kj --help · kj doctor (check your setup)");
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// Internal exports for unit testing (KJC-TSK-0367)
|
|
942
|
+
export const __test__ = {
|
|
943
|
+
askAgents,
|
|
944
|
+
askPerRoleProviders,
|
|
945
|
+
askMethodology,
|
|
946
|
+
askLanguages,
|
|
947
|
+
askGitAutomation,
|
|
948
|
+
askBoardSecurity,
|
|
949
|
+
PER_ROLE_QUESTIONS,
|
|
950
|
+
};
|