@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,13 @@
|
|
|
1
|
+
// Barrel: re-export all plan sub-command handlers.
|
|
2
|
+
export { planGenerateCommand } from "./generate.js";
|
|
3
|
+
export { planListCommand } from "./list.js";
|
|
4
|
+
export { planShowCommand } from "./show.js";
|
|
5
|
+
export { planReadyCommand } from "./ready.js";
|
|
6
|
+
export { planValidateCommand } from "./validate.js";
|
|
7
|
+
export { planDeleteCommand } from "./delete.js";
|
|
8
|
+
export { planAddHuCommand } from "./add-hu.js";
|
|
9
|
+
export { planRemoveHuCommand } from "./remove-hu.js";
|
|
10
|
+
export { planMigrateResultCommand } from "./migrate-result.js";
|
|
11
|
+
export { planFixCommand } from "./fix.js";
|
|
12
|
+
export { planShareCommand } from "./share.js";
|
|
13
|
+
export { planUnshareCommand } from "./unshare.js";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kj plan list
|
|
3
|
+
*/
|
|
4
|
+
export async function planListCommand({ config }) {
|
|
5
|
+
const { listPlans } = await import("../../plan/plan-store.js");
|
|
6
|
+
const projectDir = config.projectDir || process.cwd();
|
|
7
|
+
const plans = await listPlans(projectDir);
|
|
8
|
+
|
|
9
|
+
if (plans.length === 0) {
|
|
10
|
+
console.log("No plans found. Create one with: kj plan \"your task\"");
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
console.log(`Plans for ${projectDir}:\n`);
|
|
15
|
+
for (const p of plans) {
|
|
16
|
+
const husInfo = p.huCount > 0 ? ` (${p.huCount} HUs)` : "";
|
|
17
|
+
const status = p.status !== "draft" ? ` [${p.status}]` : "";
|
|
18
|
+
console.log(` ${p.planId} ${(p.name || p.task || "").slice(0, 50)}${husInfo}${status}`);
|
|
19
|
+
console.log(` Created: ${p.createdAt}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kj plan migrate-result [--dry-run] — KJC-TSK-0394 step 4.
|
|
3
|
+
* Siembra `result` en las HUs de los plan files existentes,
|
|
4
|
+
* deducido del status legacy. Idempotente.
|
|
5
|
+
*/
|
|
6
|
+
import { listPlans, loadPlan, savePlan } from "../../plan/plan-store.js";
|
|
7
|
+
import { migratePlanResult } from "../../plan/plan-migrate-result.js";
|
|
8
|
+
|
|
9
|
+
export async function planMigrateResultCommand({ config, flags = {} }) {
|
|
10
|
+
const projectDir = config.projectDir || process.cwd();
|
|
11
|
+
const dryRun = !!flags.dryRun;
|
|
12
|
+
|
|
13
|
+
const plans = await listPlans(projectDir);
|
|
14
|
+
if (plans.length === 0) {
|
|
15
|
+
console.log("No hay plans que migrar en este proyecto.");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let totalPlans = 0;
|
|
20
|
+
let totalHus = 0;
|
|
21
|
+
for (const meta of plans) {
|
|
22
|
+
const plan = await loadPlan(projectDir, meta.planId);
|
|
23
|
+
if (!plan) continue;
|
|
24
|
+
const { plan: migrated, changes } = migratePlanResult(plan);
|
|
25
|
+
if (changes.length === 0) continue;
|
|
26
|
+
totalPlans += 1;
|
|
27
|
+
totalHus += changes.length;
|
|
28
|
+
const summary = changes
|
|
29
|
+
.map((c) => `${c.huId}→${c.result ?? "null"}`)
|
|
30
|
+
.join(", ");
|
|
31
|
+
console.log(`${dryRun ? "[dry-run] " : ""}${meta.planId}: ${changes.length} HU(s) — ${summary}`);
|
|
32
|
+
if (!dryRun) await savePlan(projectDir, migrated);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (totalPlans === 0) {
|
|
36
|
+
console.log("Todos los plans ya tienen `result` sembrado. Nada que hacer.");
|
|
37
|
+
} else {
|
|
38
|
+
const prefix = dryRun ? "[dry-run] " : "";
|
|
39
|
+
console.log(`${prefix}Migrados ${totalHus} HU(s) en ${totalPlans} plan(s).`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kj plan ready <planId>
|
|
3
|
+
*/
|
|
4
|
+
export async function planReadyCommand({ config, planId }) {
|
|
5
|
+
const { loadPlan, savePlan } = await import("../../plan/plan-store.js");
|
|
6
|
+
const { certifyAllHus } = await import("../../plan/plan-hu-ops.js");
|
|
7
|
+
const { validatePlan } = await import("../../plan/plan-schema.js");
|
|
8
|
+
const projectDir = config.projectDir || process.cwd();
|
|
9
|
+
|
|
10
|
+
const plan = await loadPlan(projectDir, planId);
|
|
11
|
+
if (!plan) { console.error(`Plan not found: ${planId}`); process.exitCode = 1; return; }
|
|
12
|
+
|
|
13
|
+
const validation = validatePlan(plan);
|
|
14
|
+
if (!validation.valid) {
|
|
15
|
+
console.error("Plan validation failed:");
|
|
16
|
+
for (const err of validation.errors) console.error(` - ${err}`);
|
|
17
|
+
process.exitCode = 1;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (plan.hus.length === 0) {
|
|
22
|
+
console.error("Cannot approve a plan with 0 HUs.");
|
|
23
|
+
process.exitCode = 1;
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Tests-first gate: refuse to certify HUs that have no acceptance_tests.
|
|
28
|
+
// A "ready" plan must have an executable test contract per HU, otherwise
|
|
29
|
+
// the coder has nothing to aim for. Flag each offender, then bail so the
|
|
30
|
+
// user can edit the plan (board ✎ Edit or `kj plan add-hu`) and retry.
|
|
31
|
+
const missing = plan.hus.filter((h) => !Array.isArray(h.acceptance_tests) || h.acceptance_tests.length === 0);
|
|
32
|
+
if (missing.length > 0) {
|
|
33
|
+
console.error(`Cannot mark ready: ${missing.length}/${plan.hus.length} HU(s) have no acceptance_tests:`);
|
|
34
|
+
for (const h of missing) console.error(` - ${h.id} ${(h.title || "").slice(0, 60)}`);
|
|
35
|
+
console.error("");
|
|
36
|
+
console.error("Tests-first flow requires a test contract per HU. Edit each HU on the board");
|
|
37
|
+
console.error("(http://localhost:4000) via the ✎ Edit button, or re-run `kj plan` to regenerate.");
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const count = certifyAllHus(plan);
|
|
43
|
+
await savePlan(projectDir, plan);
|
|
44
|
+
console.log(`✅ ${count} HUs certified. Plan status: ready`);
|
|
45
|
+
console.log(`Run: kj run --plan ${planId} "${(plan.task || "").slice(0, 40)}..."`);
|
|
46
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kj plan remove-hu <planId> <huId>
|
|
3
|
+
*/
|
|
4
|
+
export async function planRemoveHuCommand({ config, planId, huId }) {
|
|
5
|
+
const { loadPlan, savePlan } = await import("../../plan/plan-store.js");
|
|
6
|
+
const { removeHu } = await import("../../plan/plan-hu-ops.js");
|
|
7
|
+
const projectDir = config.projectDir || process.cwd();
|
|
8
|
+
|
|
9
|
+
const plan = await loadPlan(projectDir, planId);
|
|
10
|
+
if (!plan) { console.error(`Plan not found: ${planId}`); process.exitCode = 1; return; }
|
|
11
|
+
|
|
12
|
+
const ok = removeHu(plan, huId);
|
|
13
|
+
if (!ok) { console.error(`HU not found: ${huId}`); process.exitCode = 1; return; }
|
|
14
|
+
|
|
15
|
+
await savePlan(projectDir, plan);
|
|
16
|
+
console.log(`Removed: ${huId}`);
|
|
17
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kj plan share <planId>
|
|
3
|
+
*
|
|
4
|
+
* Promotes a local plan (under `~/.karajan/plans/<projectSlug>/`) to the
|
|
5
|
+
* project's shared dir (`<projectDir>/.karajan-shared/plans/`) so teammates
|
|
6
|
+
* pulling the repo see the same HUs in their HU Board. KJC-PRP-0002 / PR1.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from "node:fs/promises";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { writeJsonAtomic } from "../../utils/atomic-write.js";
|
|
12
|
+
import { getSharedPlansDir } from "../../utils/shared-paths.js";
|
|
13
|
+
import { loadPlan } from "../../plan/plan-store.js";
|
|
14
|
+
|
|
15
|
+
function parseIdList(raw) {
|
|
16
|
+
if (!raw) return null;
|
|
17
|
+
const ids = String(raw).split(",").map((s) => s.trim()).filter(Boolean);
|
|
18
|
+
return ids.length ? ids : null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function planShareCommand({ config, planId, logger, only, exclude }) {
|
|
22
|
+
const log = logger || console;
|
|
23
|
+
const projectDir = config.projectDir || process.cwd();
|
|
24
|
+
|
|
25
|
+
if (!planId) {
|
|
26
|
+
log.error?.("kj plan share <planId> — planId is required");
|
|
27
|
+
process.exitCode = 1;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const onlyIds = parseIdList(only);
|
|
32
|
+
const excludeIds = parseIdList(exclude);
|
|
33
|
+
if (onlyIds && excludeIds) {
|
|
34
|
+
log.error?.("kj plan share — --only and --exclude are mutually exclusive");
|
|
35
|
+
process.exitCode = 1;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const plan = await loadPlan(projectDir, planId);
|
|
40
|
+
if (!plan) {
|
|
41
|
+
log.error?.(`Plan not found: ${planId}`);
|
|
42
|
+
process.exitCode = 1;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// KJC-PRP-0002 PR4: optional per-HU filter. Lets a dev share a curated
|
|
47
|
+
// subset of HUs without splitting the plan or hand-editing the JSON.
|
|
48
|
+
let hus = plan.hus || [];
|
|
49
|
+
let filterSummary = "";
|
|
50
|
+
if (onlyIds) {
|
|
51
|
+
hus = hus.filter((h) => onlyIds.includes(h.id));
|
|
52
|
+
filterSummary = ` [only: ${onlyIds.join(",")}]`;
|
|
53
|
+
} else if (excludeIds) {
|
|
54
|
+
hus = hus.filter((h) => !excludeIds.includes(h.id));
|
|
55
|
+
filterSummary = ` [excluding: ${excludeIds.join(",")}]`;
|
|
56
|
+
}
|
|
57
|
+
if ((onlyIds || excludeIds) && hus.length === 0) {
|
|
58
|
+
log.error?.(`No HUs left after applying filter — aborting share.`);
|
|
59
|
+
process.exitCode = 1;
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const sharedDir = getSharedPlansDir(projectDir);
|
|
64
|
+
await fs.mkdir(sharedDir, { recursive: true });
|
|
65
|
+
const targetFile = path.join(sharedDir, `${plan.planId}.json`);
|
|
66
|
+
|
|
67
|
+
// Stamp a `shared: true` marker so consumers (board sync, audit, etc.)
|
|
68
|
+
// can tell promoted copies from local ones without inspecting the path.
|
|
69
|
+
const sharedPlan = {
|
|
70
|
+
...plan,
|
|
71
|
+
hus,
|
|
72
|
+
shared: true,
|
|
73
|
+
sharedAt: new Date().toISOString(),
|
|
74
|
+
};
|
|
75
|
+
if (onlyIds || excludeIds) {
|
|
76
|
+
sharedPlan.sharedFilter = onlyIds
|
|
77
|
+
? { only: onlyIds }
|
|
78
|
+
: { exclude: excludeIds };
|
|
79
|
+
}
|
|
80
|
+
await writeJsonAtomic(targetFile, sharedPlan);
|
|
81
|
+
|
|
82
|
+
const rel = path.relative(projectDir, targetFile);
|
|
83
|
+
log.info?.(`✓ Shared plan ${plan.planId} → ${rel}${filterSummary}`);
|
|
84
|
+
log.info?.(` Commit '.karajan-shared/' so your team picks it up.`);
|
|
85
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { formatHuTable } from "./_shared.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* kj plan show <planId>
|
|
5
|
+
*/
|
|
6
|
+
export async function planShowCommand({ config, planId }) {
|
|
7
|
+
const { loadPlan } = await import("../../plan/plan-store.js");
|
|
8
|
+
const projectDir = config.projectDir || process.cwd();
|
|
9
|
+
const plan = await loadPlan(projectDir, planId);
|
|
10
|
+
|
|
11
|
+
if (!plan) {
|
|
12
|
+
console.error(`Plan not found: ${planId}`);
|
|
13
|
+
process.exitCode = 1;
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
console.log(`Plan: ${plan.planId}`);
|
|
18
|
+
console.log(`Task: ${plan.task}`);
|
|
19
|
+
console.log(`Name: ${plan.name || "—"}`);
|
|
20
|
+
console.log(`Status: ${plan.status}`);
|
|
21
|
+
console.log(`Version: ${plan.version}`);
|
|
22
|
+
console.log(`Created: ${plan.createdAt}`);
|
|
23
|
+
|
|
24
|
+
if (plan.approach) {
|
|
25
|
+
console.log(`\n## Approach\n${plan.approach}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (plan.hus?.length > 0) {
|
|
29
|
+
console.log(`\n## HUs (${plan.hus.length})`);
|
|
30
|
+
console.log(formatHuTable(plan.hus));
|
|
31
|
+
} else {
|
|
32
|
+
console.log("\nNo HUs defined. Add with: kj plan add-hu " + planId);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Reviewer findings (PR D). When the planner ran with the plan
|
|
36
|
+
// reviewer pass enabled, plan.review carries advisory findings —
|
|
37
|
+
// missing HUs, missing deps, scope overlaps, parallelisable
|
|
38
|
+
// groups, order issues. Each section only renders when non-empty
|
|
39
|
+
// so a clean plan stays clean on screen.
|
|
40
|
+
if (plan.review) {
|
|
41
|
+
const r = plan.review;
|
|
42
|
+
const sections = [];
|
|
43
|
+
if (r.missing_hus?.length) {
|
|
44
|
+
sections.push(`### Missing HUs (${r.missing_hus.length})`);
|
|
45
|
+
for (const m of r.missing_hus) {
|
|
46
|
+
sections.push(`- §${m.spec_section}: ${m.rationale}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (r.missing_dependencies?.length) {
|
|
50
|
+
sections.push(`\n### Missing dependencies (${r.missing_dependencies.length})`);
|
|
51
|
+
for (const d of r.missing_dependencies) {
|
|
52
|
+
sections.push(`- ${d.from} should depend on ${d.on} — ${d.rationale}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (r.scope_overlaps?.length) {
|
|
56
|
+
sections.push(`\n### Scope overlaps (${r.scope_overlaps.length})`);
|
|
57
|
+
for (const o of r.scope_overlaps) {
|
|
58
|
+
sections.push(`- ${o.between.join(" ↔ ")}: ${o.rationale}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (r.parallelisable_groups?.length) {
|
|
62
|
+
sections.push(`\n### Parallelisable groups (${r.parallelisable_groups.length})`);
|
|
63
|
+
for (const g of r.parallelisable_groups) {
|
|
64
|
+
sections.push(`- ${g.hus.join(" + ")}${g.rationale ? ` — ${g.rationale}` : ""}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (r.order_issues?.length) {
|
|
68
|
+
sections.push(`\n### Order issues (${r.order_issues.length})`);
|
|
69
|
+
for (const o of r.order_issues) {
|
|
70
|
+
sections.push(`- ${o.hus.join(", ")} (${o.issue}): ${o.rationale}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (sections.length > 0) {
|
|
74
|
+
console.log("\n## Reviewer findings");
|
|
75
|
+
if (r.summary) console.log(`_${r.summary}_\n`);
|
|
76
|
+
console.log(sections.join("\n"));
|
|
77
|
+
} else if (r.summary) {
|
|
78
|
+
console.log(`\n## Reviewer findings\n_${r.summary}_`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kj plan unshare <planId>
|
|
3
|
+
*
|
|
4
|
+
* Inverse of `kj plan share`. Removes the plan from the project's shared dir
|
|
5
|
+
* (`<projectDir>/.karajan-shared/plans/`). The per-user copy at
|
|
6
|
+
* `~/.karajan/plans/<projectSlug>/<planId>.json` stays untouched, so the dev
|
|
7
|
+
* keeps the plan locally while the team stops seeing it. KJC-PRP-0002 / PR3.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from "node:fs/promises";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { getSharedPlansDir } from "../../utils/shared-paths.js";
|
|
13
|
+
|
|
14
|
+
export async function planUnshareCommand({ config, planId, logger }) {
|
|
15
|
+
const log = logger || console;
|
|
16
|
+
const projectDir = config.projectDir || process.cwd();
|
|
17
|
+
|
|
18
|
+
if (!planId) {
|
|
19
|
+
log.error?.("kj plan unshare <planId> — planId is required");
|
|
20
|
+
process.exitCode = 1;
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const sharedDir = getSharedPlansDir(projectDir);
|
|
25
|
+
const targetFile = path.join(sharedDir, `${planId}.json`);
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
await fs.unlink(targetFile);
|
|
29
|
+
} catch (err) {
|
|
30
|
+
if (err.code === "ENOENT") {
|
|
31
|
+
log.error?.(`Plan not shared: ${planId} (no file at .karajan-shared/plans/)`);
|
|
32
|
+
process.exitCode = 1;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const rel = path.relative(projectDir, targetFile);
|
|
39
|
+
log.info?.(`✓ Unshared plan ${planId} (removed ${rel})`);
|
|
40
|
+
log.info?.(` Commit '.karajan-shared/' so your team stops seeing it.`);
|
|
41
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kj plan validate <planId>
|
|
3
|
+
*/
|
|
4
|
+
export async function planValidateCommand({ config, planId }) {
|
|
5
|
+
const { loadPlan } = await import("../../plan/plan-store.js");
|
|
6
|
+
const { validatePlan } = await import("../../plan/plan-schema.js");
|
|
7
|
+
const projectDir = config.projectDir || process.cwd();
|
|
8
|
+
|
|
9
|
+
const plan = await loadPlan(projectDir, planId);
|
|
10
|
+
if (!plan) { console.error(`Plan not found: ${planId}`); process.exitCode = 1; return; }
|
|
11
|
+
|
|
12
|
+
const result = validatePlan(plan);
|
|
13
|
+
if (result.valid) {
|
|
14
|
+
console.log(`✅ Plan ${planId} is valid (${plan.hus.length} HUs, status: ${plan.status})`);
|
|
15
|
+
} else {
|
|
16
|
+
console.error(`❌ Plan ${planId} has ${result.errors.length} error(s):`);
|
|
17
|
+
for (const err of result.errors) console.error(` - ${err}`);
|
|
18
|
+
process.exitCode = 1;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Thin shim. The plan command was split into one file per sub-command
|
|
2
|
+
// under `./plan/`. This file remains so existing callers that import
|
|
3
|
+
// `./commands/plan.js` (src/cli.js, tests) keep working without churn.
|
|
4
|
+
export {
|
|
5
|
+
planGenerateCommand,
|
|
6
|
+
planListCommand,
|
|
7
|
+
planShowCommand,
|
|
8
|
+
planReadyCommand,
|
|
9
|
+
planValidateCommand,
|
|
10
|
+
planDeleteCommand,
|
|
11
|
+
planAddHuCommand,
|
|
12
|
+
planRemoveHuCommand,
|
|
13
|
+
planMigrateResultCommand,
|
|
14
|
+
planFixCommand,
|
|
15
|
+
planShareCommand,
|
|
16
|
+
planUnshareCommand,
|
|
17
|
+
} from "./plan/index.js";
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `kj policy` — PL-A parte 3b (KJC-TSK-0733, épica KJC-PCS-0074). El motor
|
|
3
|
+
* en modo WARN: eval de una tool call y check del diff staged contra
|
|
4
|
+
* `.karajan/policy.yml`. En PL-A nada bloquea salvo un fichero de policy
|
|
5
|
+
* inválido (exit 1: declarar lo inaplicable es error del fichero, no aviso).
|
|
6
|
+
* `eval --strict` devuelve exit 2 en deny — el contrato para los adaptadores
|
|
7
|
+
* de hooks de PL-B. Los dientes de check llegan en PL-B/PL-C.
|
|
8
|
+
*/
|
|
9
|
+
import { execFile } from "node:child_process";
|
|
10
|
+
import { promisify } from "node:util";
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
12
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { checkStagedDiff, evalToolCall, loadPolicy } from "../policy/engine.js";
|
|
15
|
+
import { loadStandingExceptions, recordPolicyException } from "../policy/exceptions.js";
|
|
16
|
+
import { verifyDecisionChain } from "@karajan-family/governance";
|
|
17
|
+
|
|
18
|
+
const execFileAsync = promisify(execFile);
|
|
19
|
+
|
|
20
|
+
async function stagedFacts(projectDir, gitFn, range = null) {
|
|
21
|
+
const run =
|
|
22
|
+
gitFn ||
|
|
23
|
+
(async (args) => (await execFileAsync("git", args, { cwd: projectDir, maxBuffer: 16 * 1024 * 1024 })).stdout);
|
|
24
|
+
// PL-C (KJC-TSK-0735): en CI no hay staged — con --range se evalúa
|
|
25
|
+
// base...head, el MISMO motor sobre el diff del PR (tier C del ADR 0001).
|
|
26
|
+
const base = range ? ["diff", range] : ["diff", "--cached"];
|
|
27
|
+
const files = (await run([...base, "--name-only"]))
|
|
28
|
+
.split("\n")
|
|
29
|
+
.map((s) => s.trim())
|
|
30
|
+
.filter(Boolean);
|
|
31
|
+
let net = 0;
|
|
32
|
+
for (const line of (await run([...base, "--numstat"])).split("\n")) {
|
|
33
|
+
const [a, r] = line.trim().split(/\s+/);
|
|
34
|
+
if (a && a !== "-") net += Number(a) || 0;
|
|
35
|
+
if (r && r !== "-") net -= Number(r) || 0;
|
|
36
|
+
}
|
|
37
|
+
return { files, netLinesAdded: net };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function policyCommand({ action, config = {}, flags = {}, logger = console, deps = {} }) {
|
|
41
|
+
const projectDir = config?.projectDir || process.cwd();
|
|
42
|
+
const { policy, errors } = loadPolicy({ projectDir, deps });
|
|
43
|
+
if (errors.length > 0) {
|
|
44
|
+
for (const e of errors) logger.error?.(`✗ ${e}`);
|
|
45
|
+
logger.error?.("policy: el fichero declara lo que el motor no puede aplicar — corrígelo (una regla que miente es peor que ninguna)");
|
|
46
|
+
return 1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// GOV-C2 (KJC-TSK-0749): anclaje temporal sin blockchain — verificar la
|
|
50
|
+
// cadena ENTERA y sellar su head-hash en un fichero TRACKEADO por git:
|
|
51
|
+
// reescribir el log de ayer exige reescribir el repo de ayer. Un anchor
|
|
52
|
+
// previo con más entradas que el log actual = truncado ⇒ no se re-sella.
|
|
53
|
+
if (action === "anchor") {
|
|
54
|
+
let raw;
|
|
55
|
+
try {
|
|
56
|
+
raw = readFileSync(join(projectDir, ".karajan", "policy-decisions.jsonl"), "utf8");
|
|
57
|
+
} catch {
|
|
58
|
+
logger.info?.("policy anchor: sin decisiones que anclar — el log nace con el primer deny/excepción/commit-allow");
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
62
|
+
// Fichero vacío o solo whitespace = sin decisiones (catch de codex:
|
|
63
|
+
// lines.at(-1) undefined reventaba el hash en vez de salir limpio).
|
|
64
|
+
if (lines.length === 0) {
|
|
65
|
+
logger.info?.("policy anchor: sin decisiones que anclar — el log nace con el primer deny/excepción/commit-allow");
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
const chain = verifyDecisionChain(lines);
|
|
69
|
+
if (!chain.ok) {
|
|
70
|
+
logger.error?.(`✗ policy anchor: cadena rota en la entrada ${chain.at} (${chain.reason}) — el log ha sido manipulado; NO se sella`);
|
|
71
|
+
return 1;
|
|
72
|
+
}
|
|
73
|
+
const anchorFile = join(projectDir, ".karajan", "policy-anchor.json");
|
|
74
|
+
try {
|
|
75
|
+
const prev = JSON.parse(readFileSync(anchorFile, "utf8"));
|
|
76
|
+
if (Number.isFinite(prev.length) && prev.length > chain.length) {
|
|
77
|
+
logger.error?.(`✗ policy anchor: el log retrocede (anchor previo=${prev.length} entradas, actual=${chain.length}) — truncado tras el último sello; NO se re-sella`);
|
|
78
|
+
return 1;
|
|
79
|
+
}
|
|
80
|
+
} catch { /* sin anchor previo (o ilegible): primer sello */ }
|
|
81
|
+
const head = createHash("sha256").update(lines.at(-1), "utf8").digest("hex");
|
|
82
|
+
writeFileSync(anchorFile, `${JSON.stringify({ head, length: chain.length, ts: new Date().toISOString() }, null, 2)}\n`, "utf8");
|
|
83
|
+
logger.info?.(`✓ policy anchor: cadena íntegra (${chain.length} decisiones) — head ${head.slice(0, 12)} sellado en .karajan/policy-anchor.json; committéalo para anclarlo en la historia de git`);
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// GOV-B (KJC-TSK-0746): conceder una excepción PERMANENTE con el modelo
|
|
88
|
+
// probatorio completo — quién (identidad), regla exacta, justificación en
|
|
89
|
+
// el momento y caducidad obligatoria. Los defaults.* del consumidor son
|
|
90
|
+
// inexcepcionables: no se conceden ni desde aquí.
|
|
91
|
+
if (action === "grant") {
|
|
92
|
+
const { rule, until, reason } = flags;
|
|
93
|
+
if (!rule || !until || !reason?.trim()) {
|
|
94
|
+
logger.error?.("policy grant: --rule, --until (ISO) y --reason son obligatorios — una excepción sin quién/por qué/hasta cuándo no es una excepción, es un agujero");
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
// Inexcepcionable = inexcepcionable TAMBIÉN al conceder (catch de codex):
|
|
98
|
+
// defaults.*, cualquier cap con class security, y una policy inválida
|
|
99
|
+
// (sin policy legible no se puede probar que la regla NO es security).
|
|
100
|
+
const m = /^roles\.([^.]+)\.(write|shell)/.exec(rule);
|
|
101
|
+
if (rule.startsWith("defaults.") || errors.length > 0 || (m && policy.roles?.[m[1]]?.[m[2]]?.class === "security")) {
|
|
102
|
+
logger.error?.(`policy grant: "${rule}" es inexcepcionable (default del proyecto o clase security) o la policy no es verificable — no se concede, ni con razón`);
|
|
103
|
+
return 1;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
// GOV-E (KJC-TSK-0750, idea del lector): la renovación es SEÑAL — una
|
|
107
|
+
// regla re-concedida N veces es la política real pidiendo que la
|
|
108
|
+
// cambien por su cauce. Se cuenta contra TODAS las permanentes previas
|
|
109
|
+
// (vencidas incluidas: precisamente esas son la sedimentación).
|
|
110
|
+
const previous = loadStandingExceptions(projectDir).standing.filter((e) => e.rule_id === rule).length;
|
|
111
|
+
const rec = recordPolicyException({
|
|
112
|
+
projectDir,
|
|
113
|
+
entry: { rule_id: rule, justification: reason.trim(), scopeKind: "permanente", expiresAt: until },
|
|
114
|
+
});
|
|
115
|
+
logger.info?.(`✓ excepción permanente registrada: [${rec.rule_id}] hasta ${rec.expiresAt} — concedida por ${rec.who?.git ?? "?"} (${rec.who?.grade ?? "?"})`);
|
|
116
|
+
if (previous >= 1) {
|
|
117
|
+
logger.warn?.(`⚠ ${previous + 1}ª concesión sobre esta regla — una excepción que se renueva ya no es una excepción: considera cambiar la política por su cauce (PR a .karajan/policy.yml)`);
|
|
118
|
+
}
|
|
119
|
+
return 0;
|
|
120
|
+
} catch (err) {
|
|
121
|
+
logger.error?.(`policy grant: ${err.message}`);
|
|
122
|
+
return 1;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (action === "eval") {
|
|
127
|
+
let input;
|
|
128
|
+
try {
|
|
129
|
+
input = flags.input ? JSON.parse(flags.input) : {};
|
|
130
|
+
} catch {
|
|
131
|
+
logger.error?.("policy eval: --input debe ser JSON válido");
|
|
132
|
+
return 1;
|
|
133
|
+
}
|
|
134
|
+
const verdict = evalToolCall(policy, { role: flags.role || "coder", tool: flags.tool, input, root: projectDir });
|
|
135
|
+
logger.info?.(JSON.stringify(verdict));
|
|
136
|
+
return verdict.decision === "deny" && flags.strict ? 2 : 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// check — staged por defecto, base...head con --range (CI). Warn salvo
|
|
140
|
+
// --strict (PL-C): con --strict, una violación enforcement=deny devuelve
|
|
141
|
+
// exit 2 nombrando la regla — el contrato merge-blocking del tier C.
|
|
142
|
+
const facts = await stagedFacts(projectDir, deps.gitFn, flags.range || null);
|
|
143
|
+
const violations = checkStagedDiff(policy, { role: flags.role || "coder", ...facts });
|
|
144
|
+
const hard = flags.strict ? violations.filter((v) => v.enforcement === "deny") : [];
|
|
145
|
+
if (flags.json) {
|
|
146
|
+
logger.info?.(JSON.stringify({ mode: flags.strict ? "strict" : "warn", violations }));
|
|
147
|
+
return hard.length > 0 ? 2 : 0;
|
|
148
|
+
}
|
|
149
|
+
if (violations.length === 0) {
|
|
150
|
+
logger.info?.("policy check: limpio");
|
|
151
|
+
return 0;
|
|
152
|
+
}
|
|
153
|
+
for (const v of violations) {
|
|
154
|
+
const where = v.file ? ` (${v.file})` : "";
|
|
155
|
+
const mark = flags.strict && v.enforcement === "deny" ? "✗" : "⚠";
|
|
156
|
+
logger.warn?.(`${mark} policy [${v.rule_id}] ${v.reason}${where}`);
|
|
157
|
+
}
|
|
158
|
+
if (hard.length > 0) {
|
|
159
|
+
logger.error?.(`policy check: ${hard.length} violación(es) con enforcement=deny — el merge no procede (--strict)`);
|
|
160
|
+
return 2;
|
|
161
|
+
}
|
|
162
|
+
logger.warn?.(`policy check: ${violations.length} aviso(s) — check es modo warn, no bloquea; los deny los aplican kj review --staged y el pre-commit (PL-B)`);
|
|
163
|
+
return 0;
|
|
164
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `kj privacy scan` (KJC-TSK-0704) — audit an outbound boundary before it
|
|
3
|
+
* ships: files/dirs or the staged diff's ADDED lines. Denylist blocks
|
|
4
|
+
* (exit 1), generic PII warns; findings always come out masked.
|
|
5
|
+
*/
|
|
6
|
+
import { runCommand } from "../utils/process.js";
|
|
7
|
+
import { loadPrivacyList, scanPaths, scanText, privacyConfigPath } from "../privacy/scan.js";
|
|
8
|
+
|
|
9
|
+
const addedLines = (diff) =>
|
|
10
|
+
diff.split("\n").filter((l) => l.startsWith("+") && !l.startsWith("+++")).map((l) => l.slice(1)).join("\n");
|
|
11
|
+
|
|
12
|
+
export async function privacyScanCommand({ paths = [], flags = {}, logger = console } = {}) {
|
|
13
|
+
const list = loadPrivacyList();
|
|
14
|
+
let findings;
|
|
15
|
+
if (flags.staged) {
|
|
16
|
+
const res = await runCommand("git", ["diff", "--cached", "--no-ext-diff", "--unified=0"]);
|
|
17
|
+
findings = scanText(addedLines(res.stdout || ""), { list, source: "<staged diff>" });
|
|
18
|
+
} else {
|
|
19
|
+
if (paths.length === 0) {
|
|
20
|
+
logger.error?.("kj privacy scan: pass one or more paths, or --staged");
|
|
21
|
+
process.exitCode = 2;
|
|
22
|
+
return { ok: false, findings: [] };
|
|
23
|
+
}
|
|
24
|
+
findings = scanPaths(paths, { list });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const blocks = findings.filter((f) => f.severity === "block");
|
|
28
|
+
const warns = findings.filter((f) => f.severity === "warn");
|
|
29
|
+
const ok = blocks.length === 0;
|
|
30
|
+
process.exitCode = ok ? 0 : 1;
|
|
31
|
+
// --json keeps stdout machine-clean: exactly one JSON document, no prose.
|
|
32
|
+
if (flags.json) {
|
|
33
|
+
process.stdout.write(`${JSON.stringify({ ok, blocks: blocks.length, warns: warns.length, findings })}\n`);
|
|
34
|
+
return { ok, findings };
|
|
35
|
+
}
|
|
36
|
+
for (const f of blocks) logger.error?.(`✗ BLOCK [${f.type}] ${f.source}:${f.line} → ${f.masked}`);
|
|
37
|
+
for (const f of warns) logger.warn?.(`⚠ warn [${f.type}] ${f.source}:${f.line} → ${f.masked}`);
|
|
38
|
+
if (!list.present) {
|
|
39
|
+
logger.info?.(`hint: no personal denylist found — create ${privacyConfigPath()} (personal: [...], allow: [...]) so YOUR data blocks, not just warns`);
|
|
40
|
+
}
|
|
41
|
+
logger.info?.(ok
|
|
42
|
+
? `privacy scan: clean of denylist hits (${warns.length} generic warning(s))`
|
|
43
|
+
: `privacy scan: ${blocks.length} personal-data hit(s) — this must not ship`);
|
|
44
|
+
return { ok, findings };
|
|
45
|
+
}
|