@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,132 @@
|
|
|
1
|
+
// KJC-PCS-0049 Step 3 — Chunkers for the RAG indexer.
|
|
2
|
+
// Each chunker is pure: takes text + opts, returns
|
|
3
|
+
// `[{ text, metadata: { source, kind, ...extras } }]`. The indexer
|
|
4
|
+
// (Step 4) hands every chunk to the embedder + vec store; the
|
|
5
|
+
// retriever (Step 5) reads metadata back to enrich results.
|
|
6
|
+
|
|
7
|
+
const DEFAULT_CHUNK_LIMIT = 800;
|
|
8
|
+
const DEFAULT_OVERLAP = 100;
|
|
9
|
+
|
|
10
|
+
// ---------- shared splitter ---------------------------------------
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Split a long string into overlapping windows. Used by every chunker
|
|
14
|
+
* that produces a single section too big for the embedder's context.
|
|
15
|
+
*/
|
|
16
|
+
function windowText(text, { limit = DEFAULT_CHUNK_LIMIT, overlap = DEFAULT_OVERLAP } = {}) {
|
|
17
|
+
if (text.length <= limit) return [text];
|
|
18
|
+
const step = Math.max(1, limit - overlap);
|
|
19
|
+
const out = [];
|
|
20
|
+
for (let i = 0; i < text.length; i += step) {
|
|
21
|
+
out.push(text.slice(i, i + limit));
|
|
22
|
+
if (i + limit >= text.length) break;
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ---------- markdown chunker (plans / onboarding briefs) ----------
|
|
28
|
+
|
|
29
|
+
const HEADING_RE = /^(#{1,6})\s+(.+)$/gm;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Chunk a markdown document by heading hierarchy. The headingPath
|
|
33
|
+
* metadata tracks ancestors so a retrieval result can show "from
|
|
34
|
+
* '# Plan' › '## Auth' › '### Login'".
|
|
35
|
+
*/
|
|
36
|
+
export function chunkMarkdown(text, { path = "<inline>", kind = "plan", limit = DEFAULT_CHUNK_LIMIT, overlap = DEFAULT_OVERLAP } = {}) {
|
|
37
|
+
const sections = [];
|
|
38
|
+
const stack = []; // [{ depth, title }]
|
|
39
|
+
let cursor = 0;
|
|
40
|
+
let pending = { headingPath: [], start: 0 };
|
|
41
|
+
HEADING_RE.lastIndex = 0;
|
|
42
|
+
let m;
|
|
43
|
+
while ((m = HEADING_RE.exec(text)) !== null) {
|
|
44
|
+
if (m.index > cursor) {
|
|
45
|
+
pending.body = text.slice(pending.start, m.index).trim();
|
|
46
|
+
if (pending.body) sections.push({ ...pending });
|
|
47
|
+
}
|
|
48
|
+
const depth = m[1].length;
|
|
49
|
+
const title = m[2].trim();
|
|
50
|
+
while (stack.length > 0 && stack[stack.length - 1].depth >= depth) stack.pop();
|
|
51
|
+
stack.push({ depth, title });
|
|
52
|
+
pending = { headingPath: stack.map((s) => s.title), start: HEADING_RE.lastIndex };
|
|
53
|
+
cursor = HEADING_RE.lastIndex;
|
|
54
|
+
}
|
|
55
|
+
const tail = text.slice(pending.start).trim();
|
|
56
|
+
if (tail) sections.push({ ...pending, body: tail });
|
|
57
|
+
const chunks = [];
|
|
58
|
+
for (const sec of sections) {
|
|
59
|
+
const pieces = windowText(sec.body, { limit, overlap });
|
|
60
|
+
for (const piece of pieces) {
|
|
61
|
+
chunks.push({ text: piece, metadata: { source: path, kind, headingPath: sec.headingPath } });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return chunks;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ---------- plan chunker (one chunk per HU) -----------------------
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Chunk a Karajan plan JSON. One chunk per HU; an extra chunk for the
|
|
71
|
+
* plan-level summary if present. The retriever uses hu_id to deep-link
|
|
72
|
+
* back to the board.
|
|
73
|
+
*/
|
|
74
|
+
export function chunkPlan(plan, { path = "<plan>", limit = DEFAULT_CHUNK_LIMIT } = {}) {
|
|
75
|
+
const out = [];
|
|
76
|
+
if (plan?.summary) {
|
|
77
|
+
const pieces = windowText(String(plan.summary), { limit });
|
|
78
|
+
for (const piece of pieces) {
|
|
79
|
+
out.push({ text: piece, metadata: { source: path, kind: "plan", section: "summary" } });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
for (const hu of plan?.hus || []) {
|
|
83
|
+
const parts = [hu.title, hu.description, hu.scope].filter(Boolean).join("\n\n");
|
|
84
|
+
if (!parts.trim()) continue;
|
|
85
|
+
for (const piece of windowText(parts, { limit })) {
|
|
86
|
+
out.push({ text: piece, metadata: { source: path, kind: "plan", hu_id: hu.id || null, title: hu.title || null } });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ---------- source chunker (export-symbol granularity) ------------
|
|
93
|
+
|
|
94
|
+
import { chunkSourceAST } from "./chunker-ast.js";
|
|
95
|
+
import { adapterForPath } from "../lang/registry.js";
|
|
96
|
+
|
|
97
|
+
const EXPORT_RE = /^export\s+(?:async\s+)?(?:function|class|const|let)\s+(\w+)/gm;
|
|
98
|
+
|
|
99
|
+
// KJC-TSK-0444 + KJC-PCS-0052 PR-B — dispatch por adapter cuando el path
|
|
100
|
+
// coincide con un lenguaje registrado que aporta su propio `chunkSource`
|
|
101
|
+
// (p.ej. Python regex top-level). Si no, mantiene el camino JS histórico:
|
|
102
|
+
// AST de @babel/parser primero, fallback a export-regex.
|
|
103
|
+
export function chunkSource(text, opts = {}) {
|
|
104
|
+
const ad = adapterForPath(opts.path);
|
|
105
|
+
if (ad?.chunkSource) return ad.chunkSource(text, opts);
|
|
106
|
+
const ast = chunkSourceAST(text, opts);
|
|
107
|
+
if (ast) return ast;
|
|
108
|
+
return chunkSourceRegex(text, opts);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function chunkSourceRegex(text, { path = "<src>", limit = DEFAULT_CHUNK_LIMIT, overlap = DEFAULT_OVERLAP } = {}) {
|
|
112
|
+
const marks = [];
|
|
113
|
+
EXPORT_RE.lastIndex = 0;
|
|
114
|
+
let m;
|
|
115
|
+
while ((m = EXPORT_RE.exec(text)) !== null) marks.push({ idx: m.index, symbol: m[1] });
|
|
116
|
+
if (marks.length === 0) {
|
|
117
|
+
return windowText(text, { limit, overlap })
|
|
118
|
+
.map((piece) => ({ text: piece, metadata: { source: path, kind: "code", symbol: null } }));
|
|
119
|
+
}
|
|
120
|
+
const out = [];
|
|
121
|
+
for (let i = 0; i < marks.length; i += 1) {
|
|
122
|
+
const start = marks[i].idx;
|
|
123
|
+
const end = i + 1 < marks.length ? marks[i + 1].idx : text.length;
|
|
124
|
+
const body = text.slice(start, end);
|
|
125
|
+
for (const piece of windowText(body, { limit, overlap })) {
|
|
126
|
+
out.push({ text: piece, metadata: { source: path, kind: "code", symbol: marks[i].symbol } });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export { chunkSourceRegex };
|
package/src/rag/eval.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// KJC-TSK-0483 — Retrieval-quality harness. Loads a golden set of queries
|
|
2
|
+
// (each with expected source paths and/or symbol names), runs the retriever
|
|
3
|
+
// against each, and reports recall@k + MRR per query and aggregated. Pure:
|
|
4
|
+
// the runner takes `runQuery` as a function so the CLI plugs in `query()`
|
|
5
|
+
// while tests can use a stub.
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
7
|
+
|
|
8
|
+
const DEFAULT_KS = [5, 10];
|
|
9
|
+
|
|
10
|
+
export function loadGoldenQueries(path) {
|
|
11
|
+
const data = JSON.parse(readFileSync(path, "utf8"));
|
|
12
|
+
if (!Array.isArray(data)) throw new Error(`golden queries: expected an array in ${path}`);
|
|
13
|
+
return data;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function hitMatchesExpected(hit, expected) {
|
|
17
|
+
if (!hit) return false;
|
|
18
|
+
const src = hit.source || "";
|
|
19
|
+
if (expected.sources?.some((s) => src.endsWith(s))) return true;
|
|
20
|
+
const sym = hit.metadata?.symbol;
|
|
21
|
+
if (sym && expected.symbols?.includes(sym)) return true;
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Per-query metrics. `recall@k` is binary — 1 if any expected match lands in
|
|
26
|
+
// the top-k, else 0. `rr` is the reciprocal of the first relevant rank (1 if
|
|
27
|
+
// the very first hit matches, 0 if none do). `firstRank` is null on miss so
|
|
28
|
+
// callers can render "—" without re-checking.
|
|
29
|
+
export function scoreQuery(hits, expected, ks = DEFAULT_KS) {
|
|
30
|
+
const recall = {};
|
|
31
|
+
for (const k of ks) {
|
|
32
|
+
recall[`@${k}`] = hits.slice(0, k).some((h) => hitMatchesExpected(h, expected)) ? 1 : 0;
|
|
33
|
+
}
|
|
34
|
+
let firstRank = null;
|
|
35
|
+
let rr = 0;
|
|
36
|
+
for (let i = 0; i < hits.length; i += 1) {
|
|
37
|
+
if (hitMatchesExpected(hits[i], expected)) { firstRank = i + 1; rr = 1 / firstRank; break; }
|
|
38
|
+
}
|
|
39
|
+
return { recall, rr, firstRank };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function aggregate(results, ks = DEFAULT_KS) {
|
|
43
|
+
const n = results.length || 1;
|
|
44
|
+
const recall = {};
|
|
45
|
+
for (const k of ks) {
|
|
46
|
+
recall[`@${k}`] = results.reduce((acc, r) => acc + (r.recall[`@${k}`] || 0), 0) / n;
|
|
47
|
+
}
|
|
48
|
+
const mrr = results.reduce((acc, r) => acc + (r.rr || 0), 0) / n;
|
|
49
|
+
return { n: results.length, recall, mrr };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function runEval(queries, runQuery, { topK = 10, ks = DEFAULT_KS } = {}) {
|
|
53
|
+
const results = [];
|
|
54
|
+
for (const q of queries) {
|
|
55
|
+
const hits = (await runQuery(q.query, topK)) || [];
|
|
56
|
+
const expected = { sources: q.expected_sources || [], symbols: q.expected_symbols || [] };
|
|
57
|
+
const scored = scoreQuery(hits, expected, ks);
|
|
58
|
+
results.push({ query: q.query, hits, ...scored });
|
|
59
|
+
}
|
|
60
|
+
return { results, aggregate: aggregate(results, ks) };
|
|
61
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Governed embedder (KJC-TSK-0682 — card filed by the karajan-rag session
|
|
3
|
+
* after its independent security audit closed the same risk there, H2).
|
|
4
|
+
* Chunks of project code must not travel to a cloud embedder without
|
|
5
|
+
* governance: local providers (ollama/onnx) pass through untouched; cloud
|
|
6
|
+
* providers (openai/voyage/cohere/mistral) require an EXPLICIT
|
|
7
|
+
* `rag.sensitivity: public` — the default (`internal`) blocks with an
|
|
8
|
+
* actionable error, never a silent downgrade. Even when allowed, every
|
|
9
|
+
* text is PII-redacted (karajan-rag's audited redactPII) before leaving
|
|
10
|
+
* the machine — defense in depth, not a substitute for the policy.
|
|
11
|
+
*/
|
|
12
|
+
import { redactPII } from "karajan-rag";
|
|
13
|
+
import { makeEmbedder } from "./embedders/factory.js";
|
|
14
|
+
|
|
15
|
+
const LOCAL_PROVIDERS = new Set(["ollama", "onnx"]);
|
|
16
|
+
const redact = (text) => redactPII(String(text)).text;
|
|
17
|
+
|
|
18
|
+
class RedactingEmbedder {
|
|
19
|
+
constructor(inner) { this.inner = inner; }
|
|
20
|
+
get dim() { return this.inner.dim; }
|
|
21
|
+
get model() { return this.inner.model; }
|
|
22
|
+
async embed(text) { return this.inner.embed(redact(text)); }
|
|
23
|
+
async embedBatch(texts) { return this.inner.embedBatch(texts.map(redact)); }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Drop-in replacement for makeEmbedder(config) at every kj call site. */
|
|
27
|
+
export function makeGovernedEmbedder(config) {
|
|
28
|
+
const provider = config?.rag?.embedder?.provider || "ollama";
|
|
29
|
+
if (LOCAL_PROVIDERS.has(provider)) return makeEmbedder(config);
|
|
30
|
+
|
|
31
|
+
const sensitivity = config?.rag?.sensitivity || "internal";
|
|
32
|
+
if (sensitivity !== "public") {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`embedder "${provider}" sends your code to a cloud provider, but rag.sensitivity is "${sensitivity}". `
|
|
35
|
+
+ `Use a local embedder (rag.embedder.provider: ollama | onnx) or declare rag.sensitivity: public in kj.config.yml.`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return new RedactingEmbedder(makeEmbedder(config));
|
|
39
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// KJC-PCS-0049 Step 4 — Indexer for the RAG pipeline. Wires the three
|
|
2
|
+
// previous modules together: chunker → embedder → vec store. The CLI
|
|
3
|
+
// (Step 6) drives this; Step 5's retriever reads what gets persisted
|
|
4
|
+
// here. A chokidar watcher version stays out of scope for v2.22.0 —
|
|
5
|
+
// indexProject() is on-demand from `kj rag index`, which keeps the
|
|
6
|
+
// failure mode simple (re-run to refresh).
|
|
7
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
8
|
+
import { readdir } from "node:fs/promises";
|
|
9
|
+
import { extname, isAbsolute, join, relative } from "node:path";
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
11
|
+
import { execa } from "execa";
|
|
12
|
+
|
|
13
|
+
import { chunkMarkdown, chunkPlan, chunkSource } from "./chunker.js";
|
|
14
|
+
import { insertChunk, deleteChunksBySource, findChunkByHash } from "./vec-store.js";
|
|
15
|
+
import { detectAdaptersForProject, buildMatchers, getAllCodeExtensions } from "../lang/registry.js";
|
|
16
|
+
|
|
17
|
+
// KJC-PCS-0052 PR-A — antes vivían dos consts hard-coded para JS aquí
|
|
18
|
+
// (CODE_EXT_RE + SKIP_SEGMENTS). Ahora vienen del registry de adapters,
|
|
19
|
+
// que se consulta una vez por proceso para el conjunto global y por
|
|
20
|
+
// projectDir cuando hace falta filtrar el walk.
|
|
21
|
+
const ALL_CODE_EXTENSIONS = new Set(getAllCodeExtensions());
|
|
22
|
+
const ONBOARDING_DIR = "onboarding", PLANS_DIR = "plans";
|
|
23
|
+
|
|
24
|
+
function detectKind(path) {
|
|
25
|
+
const ext = extname(path).toLowerCase();
|
|
26
|
+
const base = path.split("/").pop() || "";
|
|
27
|
+
if (ext === ".json" && (/^plan-.+\.json$/i.test(base) || path.includes(`/${PLANS_DIR}/`))) return "plan";
|
|
28
|
+
if (ext === ".md") return path.includes(`/${ONBOARDING_DIR}/`) ? "onboarding" : "plan";
|
|
29
|
+
if (ALL_CODE_EXTENSIONS.has(ext)) return "code";
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function chunksFor(path, kind) {
|
|
34
|
+
const text = readFileSync(path, "utf8");
|
|
35
|
+
if (kind === "plan" && extname(path) === ".json") {
|
|
36
|
+
try { return chunkPlan(JSON.parse(text), { path }); }
|
|
37
|
+
catch { return []; }
|
|
38
|
+
}
|
|
39
|
+
if (kind === "plan" || kind === "onboarding") {
|
|
40
|
+
return chunkMarkdown(text, { path, kind });
|
|
41
|
+
}
|
|
42
|
+
if (kind === "code") return chunkSource(text, { path });
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Index a single file. Returns `{ indexed, failed }`. Idempotent —
|
|
48
|
+
* pre-existing chunks for this `source` path are deleted before the
|
|
49
|
+
* fresh batch is written, so a second call against the same file
|
|
50
|
+
* leaves the store with exactly the latest chunks (no duplicates).
|
|
51
|
+
*/
|
|
52
|
+
export async function indexFile(path, { db, embedder, logger = console, project = null } = {}) {
|
|
53
|
+
if (!existsSync(path)) { logger.warn?.(`[rag-indexer] file not found: ${path}`); return { indexed: 0, failed: 0 }; }
|
|
54
|
+
const kind = detectKind(path);
|
|
55
|
+
if (!kind) { logger.warn?.(`[rag-indexer] unknown kind: ${path}`); return { indexed: 0, failed: 0 }; }
|
|
56
|
+
const chunks = chunksFor(path, kind);
|
|
57
|
+
if (!chunks.length) return { indexed: 0, failed: 0, skipped: 0 };
|
|
58
|
+
deleteChunksBySource(db, path);
|
|
59
|
+
let indexed = 0;
|
|
60
|
+
let failed = 0;
|
|
61
|
+
let skipped = 0;
|
|
62
|
+
for (const ch of chunks) {
|
|
63
|
+
// KJC-TSK-0484 — Skip re-embedding identical bodies for the same project.
|
|
64
|
+
// The chunks row is still rewritten under the new source so deletions of
|
|
65
|
+
// an alias don't orphan a row whose owner was the dedup target.
|
|
66
|
+
const contentHash = createHash("sha256").update(ch.text).digest("hex");
|
|
67
|
+
try {
|
|
68
|
+
const dup = findChunkByHash(db, contentHash, project);
|
|
69
|
+
if (dup) { skipped += 1; continue; }
|
|
70
|
+
const embedding = await embedder.embed(ch.text);
|
|
71
|
+
insertChunk(db, { source: path, kind: ch.metadata.kind || kind, text: ch.text, metadata: ch.metadata, embedding, project, contentHash });
|
|
72
|
+
indexed += 1;
|
|
73
|
+
} catch (err) {
|
|
74
|
+
failed += 1;
|
|
75
|
+
logger.warn?.(`[rag-indexer] embed failed for ${path} (${chunkLabel(ch)}): ${err.message}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const note = skipped ? `, ${skipped} dedup` : "";
|
|
79
|
+
logger.info?.(`[rag-indexer] ${relative(process.cwd(), path)} → ${indexed} chunk(s) (${failed} failed${note})`);
|
|
80
|
+
return { indexed, failed, skipped };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function chunkLabel(ch) {
|
|
84
|
+
return ch.metadata?.hu_id || ch.metadata?.symbol || ch.metadata?.headingPath?.join(" > ") || "block";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// KJC-PCS-0052 PR-B.2.4 — Activa el camino AST en chunkers cuyo adapter
|
|
88
|
+
// expone `prepare()` (Python, Rust). Promise.allSettled aísla cada grammar:
|
|
89
|
+
// si uno falla (red, wasm corrupto), el resto se carga y los .py/.rs sin
|
|
90
|
+
// AST listo caen al fallback regex en chunkSource. JS no tiene `prepare`
|
|
91
|
+
// porque chunker-ast.js usa @babel/parser síncrono (ya cargado).
|
|
92
|
+
export async function prepareAdapters(adapters, { logger = console } = {}) {
|
|
93
|
+
const tasks = adapters.filter((a) => typeof a.prepare === "function");
|
|
94
|
+
if (tasks.length === 0) return;
|
|
95
|
+
const results = await Promise.allSettled(tasks.map((a) => a.prepare()));
|
|
96
|
+
results.forEach((r, i) => {
|
|
97
|
+
if (r.status === "rejected") logger.warn?.(`[rag-indexer] adapter ${tasks[i].id} prepare failed: ${r.reason?.message || r.reason}`);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function listFiles(dir, predicate) {
|
|
102
|
+
const out = [];
|
|
103
|
+
async function walk(d) {
|
|
104
|
+
let ents; try { ents = await readdir(d, { withFileTypes: true }); } catch { return; }
|
|
105
|
+
for (const e of ents) {
|
|
106
|
+
const p = join(d, e.name);
|
|
107
|
+
if (e.isDirectory()) await walk(p); else if (e.isFile() && predicate(p)) out.push(p);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
await walk(dir); return out;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Index every Karajan-managed asset for the project: every plan-*.json
|
|
115
|
+
* under `<karajanHome>/plans/<slug>/`, the onboarding brief under
|
|
116
|
+
* `<karajanHome>/onboarding/`, and (optionally) source files under the
|
|
117
|
+
* projectDir. Sources are gated behind `withSources: true` because
|
|
118
|
+
* indexing tens of thousands of files is expensive — the CLI exposes
|
|
119
|
+
* it as `--with-sources`.
|
|
120
|
+
*/
|
|
121
|
+
export async function indexProject(projectDir, { db, embedder, karajanHome, logger = console, withSources = false } = {}) {
|
|
122
|
+
// KJC-TSK-0640: a full index must stamp HEAD too — without it,
|
|
123
|
+
// last_indexed_commit stayed null after the FIRST index, so the drift
|
|
124
|
+
// delta-update (maybeAutoUpdate) never engaged until a manual --since.
|
|
125
|
+
const totals = { indexed: 0, failed: 0, files: 0, head: null };
|
|
126
|
+
try {
|
|
127
|
+
const { stdout } = await execa("git", ["-C", projectDir, "rev-parse", "HEAD"]);
|
|
128
|
+
totals.head = stdout.trim();
|
|
129
|
+
} catch { /* not a git repo (or zero commits) — nothing to stamp */ }
|
|
130
|
+
const slug = projectDir.split("/").pop()?.replace(/[^a-zA-Z0-9._-]/g, "-").toLowerCase() || "project";
|
|
131
|
+
await prepareAdapters(detectAdaptersForProject(projectDir), { logger });
|
|
132
|
+
const planRoot = join(karajanHome, PLANS_DIR, slug);
|
|
133
|
+
if (existsSync(planRoot)) {
|
|
134
|
+
const plans = await listFiles(planRoot, (p) => /plan-.*\.json$/.test(p));
|
|
135
|
+
for (const p of plans) {
|
|
136
|
+
const r = await indexFile(p, { db, embedder, logger, project: slug });
|
|
137
|
+
totals.indexed += r.indexed; totals.failed += r.failed; totals.files += 1;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const onboarding = join(karajanHome, ONBOARDING_DIR, `${slug}.md`);
|
|
141
|
+
if (existsSync(onboarding)) {
|
|
142
|
+
const r = await indexFile(onboarding, { db, embedder, logger, project: slug });
|
|
143
|
+
totals.indexed += r.indexed; totals.failed += r.failed; totals.files += 1;
|
|
144
|
+
}
|
|
145
|
+
if (withSources) {
|
|
146
|
+
// KJC-BUG-0062: `.kj/worktrees/HU-*/` contains git worktrees Karajan spawns
|
|
147
|
+
// per HU during `kj run`; they hold complete copies of `src/` that
|
|
148
|
+
// duplicate every chunk and contaminate retrieval scores. `_diet` is the
|
|
149
|
+
// tests/_diet/ sandbox used by the test-diet audit harness — never user
|
|
150
|
+
// code. Both viven en COMMON_SKIP_SEGMENTS dentro del registry.
|
|
151
|
+
const matchers = buildMatchers(detectAdaptersForProject(projectDir));
|
|
152
|
+
const sources = await listFiles(projectDir, (p) => matchers.isCodeFile(p) && !matchers.shouldSkip(p));
|
|
153
|
+
for (const s of sources) {
|
|
154
|
+
const r = await indexFile(s, { db, embedder, logger, project: slug });
|
|
155
|
+
totals.indexed += r.indexed; totals.failed += r.failed; totals.files += 1;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return totals;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* KJC-TSK-0455 — Re-index only the files changed between `since` and HEAD.
|
|
163
|
+
* `git diff --name-status -z` drives the work: A/M re-embed, D drop chunks,
|
|
164
|
+
* R drops old + indexes new. Returns `{ indexed, failed, files, deleted, head }`;
|
|
165
|
+
* `head` lets callers persist via setLastIndexedCommit. Throws on diff failure
|
|
166
|
+
* (shallow clone, unknown ref) so the CLI can fall back to a full reindex.
|
|
167
|
+
*/
|
|
168
|
+
export async function indexProjectDelta(projectDir, { db, embedder, since, logger = console } = {}) {
|
|
169
|
+
const totals = { indexed: 0, failed: 0, files: 0, deleted: 0, head: null };
|
|
170
|
+
const slug = projectDir.split("/").pop()?.replace(/[^a-zA-Z0-9._-]/g, "-").toLowerCase() || "project";
|
|
171
|
+
const { stdout: head } = await execa("git", ["-C", projectDir, "rev-parse", "HEAD"]);
|
|
172
|
+
totals.head = head.trim();
|
|
173
|
+
if (totals.head === since) return totals;
|
|
174
|
+
const { stdout } = await execa("git", ["-C", projectDir, "diff", "--name-status", "-z", since, "HEAD"]);
|
|
175
|
+
const parts = stdout.split("\0").filter(Boolean);
|
|
176
|
+
const ops = []; // { kind: 'del'|'idx', p }
|
|
177
|
+
for (let i = 0; i < parts.length; i += 1) {
|
|
178
|
+
const s = parts[i];
|
|
179
|
+
if (s.startsWith("R")) { ops.push({ kind: "del", p: parts[++i] }, { kind: "idx", p: parts[++i] }); }
|
|
180
|
+
else if (s === "D") { ops.push({ kind: "del", p: parts[++i] }); }
|
|
181
|
+
else { ops.push({ kind: "idx", p: parts[++i] }); }
|
|
182
|
+
}
|
|
183
|
+
const adapters = detectAdaptersForProject(projectDir);
|
|
184
|
+
await prepareAdapters(adapters, { logger });
|
|
185
|
+
const matchers = buildMatchers(adapters);
|
|
186
|
+
for (const { kind, p } of ops) {
|
|
187
|
+
if (matchers.shouldSkip(p)) continue;
|
|
188
|
+
if (!matchers.isCodeFile(p)) continue;
|
|
189
|
+
const abs = isAbsolute(p) ? p : join(projectDir, p);
|
|
190
|
+
if (kind === "del") {
|
|
191
|
+
totals.deleted += deleteChunksBySource(db, abs);
|
|
192
|
+
logger.info?.(`[rag-indexer] delta delete ${relative(process.cwd(), abs)}`);
|
|
193
|
+
} else if (existsSync(abs)) {
|
|
194
|
+
const r = await indexFile(abs, { db, embedder, logger, project: slug });
|
|
195
|
+
totals.indexed += r.indexed; totals.failed += r.failed; totals.files += 1;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return totals;
|
|
199
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* library — the distilled engineering canon as its own RAG collection
|
|
3
|
+
* (KJC-TSK-0697). Markdown cards from `<pkg>/library/`, `~/.karajan/library/`
|
|
4
|
+
* and `<project>/.karajan/library/` index into the SAME global rag.db,
|
|
5
|
+
* isolated by project="library" + kind="library", so `kj rag query --library`
|
|
6
|
+
* reaches the canon and normal project queries never see it. The architect
|
|
7
|
+
* consults it to ground the greenfield alternative (KJC-TSK-0696).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
12
|
+
import { extname, join } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
import os from "node:os";
|
|
15
|
+
|
|
16
|
+
import { chunkMarkdown } from "./chunker.js";
|
|
17
|
+
import { insertChunk, deleteChunksBySource, findChunkByHash } from "./vec-store.js";
|
|
18
|
+
|
|
19
|
+
export const LIBRARY_PROJECT = "library";
|
|
20
|
+
|
|
21
|
+
const SHIPPED_LIBRARY_DIR = fileURLToPath(new URL("../../library", import.meta.url));
|
|
22
|
+
|
|
23
|
+
/** Existing library dirs, shipped canon first. */
|
|
24
|
+
export function libraryDirs({ pkgLibraryDir = SHIPPED_LIBRARY_DIR, home = os.homedir(), projectDir = process.cwd() } = {}) {
|
|
25
|
+
const candidates = [pkgLibraryDir, join(home, ".karajan", "library"), join(projectDir, ".karajan", "library")];
|
|
26
|
+
return candidates.filter((d) => existsSync(d));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Index every markdown card found in the library dirs. Idempotent: chunks
|
|
31
|
+
* re-index per source and identical bodies skip the embedder (content-hash
|
|
32
|
+
* dedup within the library project).
|
|
33
|
+
*/
|
|
34
|
+
export async function indexLibrary({ db, embedder, logger = console, pkgLibraryDir, home, projectDir } = {}) {
|
|
35
|
+
let indexed = 0, failed = 0, files = 0;
|
|
36
|
+
for (const dir of libraryDirs({ pkgLibraryDir, home, projectDir })) {
|
|
37
|
+
for (const name of readdirSync(dir)) {
|
|
38
|
+
if (extname(name).toLowerCase() !== ".md") continue;
|
|
39
|
+
const path = join(dir, name);
|
|
40
|
+
files += 1;
|
|
41
|
+
const chunks = chunkMarkdown(readFileSync(path, "utf8"), { path, kind: "library" });
|
|
42
|
+
const hashed = chunks.map((ch) => ({ ch, contentHash: createHash("sha256").update(ch.text).digest("hex") }));
|
|
43
|
+
// Unchanged card (every chunk body already known) → skip before the
|
|
44
|
+
// delete, so idempotent re-runs never touch the embedder.
|
|
45
|
+
if (hashed.length && hashed.every(({ contentHash }) => findChunkByHash(db, contentHash, LIBRARY_PROJECT))) continue;
|
|
46
|
+
deleteChunksBySource(db, path);
|
|
47
|
+
for (const { ch, contentHash } of hashed) {
|
|
48
|
+
try {
|
|
49
|
+
if (findChunkByHash(db, contentHash, LIBRARY_PROJECT)) continue;
|
|
50
|
+
const embedding = await embedder.embed(ch.text);
|
|
51
|
+
insertChunk(db, { source: path, kind: "library", text: ch.text, metadata: ch.metadata, embedding, project: LIBRARY_PROJECT, contentHash });
|
|
52
|
+
indexed += 1;
|
|
53
|
+
} catch (err) {
|
|
54
|
+
failed += 1;
|
|
55
|
+
logger.warn?.(`[rag-library] embed failed for ${path}: ${err.message}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (files) logger.info?.(`[rag-library] ${files} card(s) → ${indexed} chunk(s) (${failed} failed)`);
|
|
61
|
+
return { files, indexed, failed };
|
|
62
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// KJC-TSK-0436 (RAG Auto-Bootstrap PR2) — Capability check for Ollama
|
|
2
|
+
// bootstrap. Returns whether the host can reasonably run a local Ollama
|
|
3
|
+
// container: Docker installed, daemon reachable, and enough RAM
|
|
4
|
+
// (default >= 4 GB free). The caller (kj init) decides whether to
|
|
5
|
+
// bootstrap, warn, or skip.
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import { runCommand } from "../utils/process.js";
|
|
8
|
+
|
|
9
|
+
const DEFAULT_MIN_RAM_BYTES = 4 * 1024 * 1024 * 1024; // 4 GB free
|
|
10
|
+
const DEFAULT_TIMEOUT_MS = 5000;
|
|
11
|
+
|
|
12
|
+
export async function checkDockerAvailable(timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
13
|
+
const v = await runCommand("docker", ["--version"], { timeout: timeoutMs }).catch(() => ({ exitCode: 127 }));
|
|
14
|
+
if (v.exitCode !== 0) return { ok: false, reason: "docker:not-installed" };
|
|
15
|
+
const info = await runCommand("docker", ["info"], { timeout: timeoutMs }).catch(() => ({ exitCode: 1 }));
|
|
16
|
+
if (info.exitCode !== 0) return { ok: false, reason: "docker:daemon-unreachable" };
|
|
17
|
+
return { ok: true };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function checkRamCapacity(minBytes = DEFAULT_MIN_RAM_BYTES) {
|
|
21
|
+
const free = os.freemem();
|
|
22
|
+
const total = os.totalmem();
|
|
23
|
+
if (free < minBytes) {
|
|
24
|
+
return {
|
|
25
|
+
ok: false,
|
|
26
|
+
reason: `ram:insufficient (${formatGB(free)} free < ${formatGB(minBytes)} required)`,
|
|
27
|
+
freeBytes: free,
|
|
28
|
+
totalBytes: total,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return { ok: true, freeBytes: free, totalBytes: total };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Combined capability check. Returns `{ capable, reasons[] }`.
|
|
36
|
+
* Caller decides what to do (bootstrap, warn, skip).
|
|
37
|
+
*/
|
|
38
|
+
export async function checkOllamaCapability({ minRamBytes = DEFAULT_MIN_RAM_BYTES } = {}) {
|
|
39
|
+
const reasons = [];
|
|
40
|
+
const docker = await checkDockerAvailable();
|
|
41
|
+
if (!docker.ok) reasons.push(docker.reason);
|
|
42
|
+
const ram = checkRamCapacity(minRamBytes);
|
|
43
|
+
if (!ram.ok) reasons.push(ram.reason);
|
|
44
|
+
return { capable: reasons.length === 0, reasons, docker, ram };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function formatGB(bytes) {
|
|
48
|
+
return `${(bytes / (1024 ** 3)).toFixed(1)} GB`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Pull a model into the Ollama container. Uses `docker exec` so we do
|
|
53
|
+
* not depend on the host having `ollama` CLI installed. Wired by `kj
|
|
54
|
+
* init` after `ollamaUp`, and by `kj ollama pull <model>` (PR3).
|
|
55
|
+
*/
|
|
56
|
+
export async function pullOllamaModel(model, { containerName = "kj-ollama", timeoutMs = 10 * 60 * 1000 } = {}) {
|
|
57
|
+
if (!model || typeof model !== "string") throw new Error("pullOllamaModel: model is required");
|
|
58
|
+
return runCommand("docker", ["exec", containerName, "ollama", "pull", model], { timeout: timeoutMs });
|
|
59
|
+
}
|