@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,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kj plan add-hu <planId> --title "..." --type sw --deps hu_xxx
|
|
3
|
+
*/
|
|
4
|
+
export async function planAddHuCommand({ config, planId, title, type, deps, scope }) {
|
|
5
|
+
const { loadPlan, savePlan } = await import("../../plan/plan-store.js");
|
|
6
|
+
const { addHu } = 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 hu = addHu(plan, {
|
|
13
|
+
title: title || "New HU",
|
|
14
|
+
task_type: type || "sw",
|
|
15
|
+
scope: scope || null,
|
|
16
|
+
blocked_by: deps ? deps.split(",").map(d => d.trim()) : []
|
|
17
|
+
});
|
|
18
|
+
const { creatorLabel } = await import("../hu.js");
|
|
19
|
+
hu.created_by = creatorLabel(); // KJC-TSK-0661: provenance stamp
|
|
20
|
+
|
|
21
|
+
await savePlan(projectDir, plan);
|
|
22
|
+
console.log(`Added: ${hu.id} — ${hu.title}`);
|
|
23
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kj plan delete <planId>
|
|
3
|
+
*/
|
|
4
|
+
export async function planDeleteCommand({ config, planId }) {
|
|
5
|
+
const { deletePlan } = await import("../../plan/plan-store.js");
|
|
6
|
+
const projectDir = config.projectDir || process.cwd();
|
|
7
|
+
const ok = await deletePlan(projectDir, planId);
|
|
8
|
+
console.log(ok ? `Deleted: ${planId}` : `Not found: ${planId}`);
|
|
9
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// KJC-TSK-0402: relanzar el self-fix loop sobre un plan ya generado,
|
|
2
|
+
// opcionalmente con feedback humano que guía al reviewer hacia los
|
|
3
|
+
// puntos débiles que la primera pasada no detectó. NO regenera desde
|
|
4
|
+
// cero — preserva el trabajo previo.
|
|
5
|
+
|
|
6
|
+
import { createAgent } from "../../agents/index.js";
|
|
7
|
+
import { assertAgentsAvailable } from "../../agents/availability.js";
|
|
8
|
+
import { resolveRole } from "../../config.js";
|
|
9
|
+
import { createCliProgressReporter } from "../../utils/cli-progress.js";
|
|
10
|
+
import { reviewPlan, findingsCount, findingsCountByType } from "../../plan/plan-reviewer.js";
|
|
11
|
+
import { applyReviewerFeedback, applyFixerPatch } from "../../plan/plan-fixer.js";
|
|
12
|
+
import { runStructuralPass } from "../../plan/plan-structural-pass.js";
|
|
13
|
+
import { loadPlan, savePlan } from "../../plan/plan-store.js";
|
|
14
|
+
|
|
15
|
+
export async function planFixCommand({ config, planId, prompt, logger, json }) {
|
|
16
|
+
const projectDir = config.projectDir || process.cwd();
|
|
17
|
+
const plan = await loadPlan(projectDir, planId);
|
|
18
|
+
if (!plan) throw new Error(`Plan no encontrado: ${planId}`);
|
|
19
|
+
if (!Array.isArray(plan.hus) || plan.hus.length === 0) {
|
|
20
|
+
throw new Error(`Plan ${planId} no tiene HUs.`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const before = { hus: plan.hus.length, deps: plan.hus.reduce((a, h) => a + (h.blocked_by?.length || 0), 0) };
|
|
24
|
+
|
|
25
|
+
const plannerRole = resolveRole(config, "planner");
|
|
26
|
+
await assertAgentsAvailable([plannerRole.provider]);
|
|
27
|
+
const planner = createAgent(plannerRole.provider, config, logger);
|
|
28
|
+
const silenceTimeoutMs = Number(config?.session?.max_agent_silence_minutes) > 0
|
|
29
|
+
? Math.round(Number(config.session.max_agent_silence_minutes) * 60 * 1000) : undefined;
|
|
30
|
+
const timeoutMs = Number(config?.session?.max_planner_minutes) > 0
|
|
31
|
+
? Math.round(Number(config.session.max_planner_minutes) * 60 * 1000) : undefined;
|
|
32
|
+
|
|
33
|
+
// Reviewer con prompt opcional del usuario inyectado como contexto extra.
|
|
34
|
+
// El prompt se prepende al task original para que el reviewer LLM lo lea
|
|
35
|
+
// antes de la HU list.
|
|
36
|
+
const enrichedTask = prompt
|
|
37
|
+
? `${plan.task || ""}\n\n## USER FEEDBACK (priorizar estos puntos)\n${prompt}\n`
|
|
38
|
+
: plan.task || "";
|
|
39
|
+
|
|
40
|
+
const reviewProgress = createCliProgressReporter({ role: "plan-fix:reviewer", quiet: Boolean(json) });
|
|
41
|
+
const review = await reviewPlan({
|
|
42
|
+
agent: planner, task: enrichedTask, hus: plan.hus,
|
|
43
|
+
onOutput: reviewProgress.onOutput, silenceTimeoutMs, timeoutMs,
|
|
44
|
+
});
|
|
45
|
+
reviewProgress.finish(review.ok ? "done" : "failed");
|
|
46
|
+
if (!review.ok) throw new Error(`Reviewer falló: ${review.error}`);
|
|
47
|
+
plan.review = review.findings;
|
|
48
|
+
let count = findingsCount(review.findings);
|
|
49
|
+
if (!json) process.stderr.write(`Reviewer encontró ${count} item(s).\n`);
|
|
50
|
+
|
|
51
|
+
// Self-fix loop con smart convergence guard (mismo que generate.js).
|
|
52
|
+
const maxIter = 4;
|
|
53
|
+
for (let i = 1; i <= maxIter && count > 0; i++) {
|
|
54
|
+
const husSnapshot = JSON.parse(JSON.stringify(plan.hus));
|
|
55
|
+
const reviewSnapshot = JSON.parse(JSON.stringify(plan.review));
|
|
56
|
+
const fixProgress = createCliProgressReporter({ role: `plan-fix:iter ${i}`, quiet: Boolean(json) });
|
|
57
|
+
const fix = await applyReviewerFeedback({
|
|
58
|
+
agent: planner, task: enrichedTask, hus: plan.hus, findings: plan.review,
|
|
59
|
+
onOutput: fixProgress.onOutput, silenceTimeoutMs, timeoutMs,
|
|
60
|
+
});
|
|
61
|
+
if (!fix.ok) { fixProgress.finish("failed"); break; }
|
|
62
|
+
const ops = applyFixerPatch(plan, fix.patch);
|
|
63
|
+
if (ops.added + ops.depsAdded + ops.archived === 0) { fixProgress.finish("done"); break; }
|
|
64
|
+
const review2 = await reviewPlan({
|
|
65
|
+
agent: planner, task: enrichedTask, hus: plan.hus,
|
|
66
|
+
onOutput: fixProgress.onOutput, silenceTimeoutMs, timeoutMs,
|
|
67
|
+
});
|
|
68
|
+
if (!review2.ok) { plan.hus = husSnapshot; plan.review = reviewSnapshot; fixProgress.finish("failed"); break; }
|
|
69
|
+
const b = findingsCountByType(reviewSnapshot);
|
|
70
|
+
const a = findingsCountByType(review2.findings);
|
|
71
|
+
const accept = a.priority < b.priority || (a.priority === b.priority && a.total <= b.total);
|
|
72
|
+
if (!accept) { plan.hus = husSnapshot; plan.review = reviewSnapshot; fixProgress.finish("reverted"); break; }
|
|
73
|
+
plan.review = review2.findings;
|
|
74
|
+
count = a.total;
|
|
75
|
+
fixProgress.finish("done");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Structural integrity pass determinístico al final.
|
|
79
|
+
const { fixes: structuralFixes } = runStructuralPass(plan);
|
|
80
|
+
if (structuralFixes.length > 0) {
|
|
81
|
+
if (!plan.review) plan.review = {};
|
|
82
|
+
plan.review.structural_fixes = structuralFixes;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
await savePlan(projectDir, plan);
|
|
86
|
+
const after = { hus: plan.hus.length, deps: plan.hus.reduce((a, h) => a + (h.blocked_by?.length || 0), 0) };
|
|
87
|
+
|
|
88
|
+
if (json) {
|
|
89
|
+
console.log(JSON.stringify({ planId, before, after, structuralFixes }, null, 2));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const diffHus = after.hus - before.hus;
|
|
93
|
+
const diffDeps = after.deps - before.deps;
|
|
94
|
+
const structuralSummary = structuralFixes.length > 0
|
|
95
|
+
? `, ${structuralFixes.length} structural fix(es)` : "";
|
|
96
|
+
console.log(
|
|
97
|
+
`kj plan fix ${planId}: ${diffHus >= 0 ? "+" : ""}${diffHus} HUs, ${diffDeps >= 0 ? "+" : ""}${diffDeps} deps${structuralSummary}. Findings: ${count} restantes.`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
import { createAgent } from "../../agents/index.js";
|
|
2
|
+
import { assertAgentsAvailable } from "../../agents/availability.js";
|
|
3
|
+
import { resolveRole } from "../../config.js";
|
|
4
|
+
import { buildPlannerPrompt } from "../../prompts/planner.js";
|
|
5
|
+
import { parseMaybeJsonString } from "../../review/parser.js";
|
|
6
|
+
import { createCliProgressReporter } from "../../utils/cli-progress.js";
|
|
7
|
+
import { runAutoGC, summarizeGC } from "../../utils/garbage-collector.js";
|
|
8
|
+
import { promptProjectName } from "../../utils/prompt-project-name.js";
|
|
9
|
+
import { deriveProjectNameFromCwd } from "../../utils/derive-project-name-from-cwd.js";
|
|
10
|
+
import { applyReviewerFeedback, applyFixerPatch } from "../../plan/plan-fixer.js";
|
|
11
|
+
import { runStructuralPass } from "../../plan/plan-structural-pass.js";
|
|
12
|
+
import { recommendModelsForHu, complexityFromTaskType } from "../../hu/model-router.js";
|
|
13
|
+
import { withBrainRecovery } from "../../brain/with-brain-recovery.js";
|
|
14
|
+
import { buildStandbyState } from "../../brain/standby-store.js";
|
|
15
|
+
import { printResumeHint } from "../../utils/display/resume-hint.js";
|
|
16
|
+
import { runSpecReview } from "../../spec-review/run-spec-review.js";
|
|
17
|
+
import { prependPreflightHu } from "../../plan/preflight-hu.js";
|
|
18
|
+
import { readCachedBrief } from "../../onboarder/cache.js";
|
|
19
|
+
import { formatPlan, formatHuTable } from "./_shared.js";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* kj plan "task" — generate plan + HUs
|
|
23
|
+
*/
|
|
24
|
+
export async function planGenerateCommand({ task, config, logger, json, context, flags = {}, sourceCanvasPath = null }) {
|
|
25
|
+
const { withCliRunLog } = await import("../../utils/cli-run-log.js");
|
|
26
|
+
return withCliRunLog("plan", { projectDir: config?.projectDir, logger }, async ({ runLog }) => {
|
|
27
|
+
return planGenerateImpl({ task, config, logger, json, context, runLog, flags, sourceCanvasPath });
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function planGenerateImpl({ task, config, logger, json, context, runLog, flags = {}, sourceCanvasPath = null }) {
|
|
32
|
+
// Auto-GC: prune orphan plans (project dir gone) + old finalised plans/
|
|
33
|
+
// sessions/HU batches before we start. Silent unless something was
|
|
34
|
+
// removed; one-liner summary on stderr in that case so --json stays
|
|
35
|
+
// untouched. Skipped in test env.
|
|
36
|
+
if (!process.env.VITEST && process.env.NODE_ENV !== "test") {
|
|
37
|
+
try {
|
|
38
|
+
const gcResult = await runAutoGC();
|
|
39
|
+
const line = summarizeGC(gcResult);
|
|
40
|
+
if (line && !json) process.stderr.write(`${line}\n`);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
logger?.warn?.(`Auto-GC failed (non-blocking): ${err.message}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Spec-reviewer pre-planner audit (KJC-PCS-0048). Static import —
|
|
47
|
+
// role runs every invocation (modulo bypass); no lazy-load benefit.
|
|
48
|
+
const reviewResult = await runSpecReview({
|
|
49
|
+
spec: task, config, logger, flags,
|
|
50
|
+
askQuestion: json ? null : (typeof flags?.askQuestion === "function" ? flags.askQuestion : null),
|
|
51
|
+
});
|
|
52
|
+
if (!reviewResult.proceed) {
|
|
53
|
+
logger?.info?.("Aborted by user after spec review.");
|
|
54
|
+
return { ok: false, aborted: true, reason: "spec-review-cancelled" };
|
|
55
|
+
}
|
|
56
|
+
if (reviewResult.refined && reviewResult.finalSpec) {
|
|
57
|
+
task = reviewResult.finalSpec; // downstream runs the refined spec
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// KJC-TSK-0384 PR 3: --use-onboarding folds the cached Architecture Brief
|
|
61
|
+
// from `kj onboard` into the planner's context. Silent when no brief is
|
|
62
|
+
// cached so users without one don't see noise; loud warn when they passed
|
|
63
|
+
// the flag explicitly but the cache is empty so the missed wiring surfaces.
|
|
64
|
+
let resolvedContext = context;
|
|
65
|
+
if (flags?.useOnboarding) {
|
|
66
|
+
const brief = await readCachedBrief(config?.projectDir || process.cwd());
|
|
67
|
+
if (brief.found) {
|
|
68
|
+
resolvedContext = [
|
|
69
|
+
resolvedContext, brief.content ? `## Architecture Brief (from kj onboard)\n\n${brief.content}` : null,
|
|
70
|
+
].filter(Boolean).join("\n\n");
|
|
71
|
+
runLog.logText(`[planner] onboarding brief injected from ${brief.path}`);
|
|
72
|
+
} else {
|
|
73
|
+
logger?.warn?.(`--use-onboarding passed but no brief at ${brief.path}. Run 'kj onboard' first.`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const plannerRole = resolveRole(config, "planner");
|
|
78
|
+
await assertAgentsAvailable([plannerRole.provider]);
|
|
79
|
+
runLog.logText(`[planner] provider=${plannerRole.provider}`);
|
|
80
|
+
|
|
81
|
+
const planner = createAgent(plannerRole.provider, config, logger);
|
|
82
|
+
const prompt = buildPlannerPrompt({ task, context: resolvedContext });
|
|
83
|
+
const silenceTimeoutMs = Number(config?.session?.max_agent_silence_minutes) > 0
|
|
84
|
+
? Math.round(Number(config.session.max_agent_silence_minutes) * 60 * 1000)
|
|
85
|
+
: undefined;
|
|
86
|
+
const timeoutMs = Number(config?.session?.max_planner_minutes) > 0
|
|
87
|
+
? Math.round(Number(config.session.max_planner_minutes) * 60 * 1000)
|
|
88
|
+
: undefined;
|
|
89
|
+
|
|
90
|
+
// Live progress to stderr so the user sees what the planner is doing
|
|
91
|
+
// instead of staring at a silent shell for 30 s – 3 min. `--json` mode
|
|
92
|
+
// stays quiet so stdout remains a pure JSON document.
|
|
93
|
+
// Sub-progress por fase (KJC outputs limpios): cada llamada al
|
|
94
|
+
// agente arranca su propio "[planner:fase] starting → done" para
|
|
95
|
+
// que el usuario no piense que "[planner] done" significa "el
|
|
96
|
+
// comando entero terminó" — sigue habiendo tests-synth, reviewer,
|
|
97
|
+
// self-fix iter N por delante.
|
|
98
|
+
const progress = createCliProgressReporter({ role: "planner:initial", quiet: Boolean(json) });
|
|
99
|
+
let result;
|
|
100
|
+
// `kj plan` has no pipeline session, so synthesise a standby id
|
|
101
|
+
// (`plan_<stamp>`). If the planner hits a quota cap, withBrainRecovery
|
|
102
|
+
// persists it and `kj standby resume` re-spawns via the saved argv.
|
|
103
|
+
const plannerSessionState = buildStandbyState({
|
|
104
|
+
session: { id: `plan_${new Date().toISOString().replaceAll(/[:.]/g, "-")}` },
|
|
105
|
+
config,
|
|
106
|
+
});
|
|
107
|
+
try {
|
|
108
|
+
// KJC-TSK-0413: planner inicial pasa por Brain Recovery — clasifica
|
|
109
|
+
// 401/429/5xx/SILENCED/etc y decide retry/standby/hibernate/abort.
|
|
110
|
+
result = await withBrainRecovery({
|
|
111
|
+
agent: planner,
|
|
112
|
+
taskArgs: { prompt, role: "planner", silenceTimeoutMs, timeoutMs, onOutput: progress.onOutput },
|
|
113
|
+
role: "planner",
|
|
114
|
+
logger,
|
|
115
|
+
sessionState: plannerSessionState,
|
|
116
|
+
});
|
|
117
|
+
progress.finish(result.ok ? "done" : (result.action || "failed"));
|
|
118
|
+
} catch (err) {
|
|
119
|
+
progress.finish("failed");
|
|
120
|
+
throw err;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!result.ok && result.action === "hibernate") {
|
|
124
|
+
// Quota cap: Brain Recovery persisted a standby snapshot. Stop
|
|
125
|
+
// cleanly with a resume hint instead of throwing a generic error.
|
|
126
|
+
const hibResult = {
|
|
127
|
+
ok: false, hibernated: true,
|
|
128
|
+
sessionId: plannerSessionState?.sessionId || null,
|
|
129
|
+
standbyFile: result.standbyFile || null,
|
|
130
|
+
};
|
|
131
|
+
if (!json) printResumeHint(hibResult);
|
|
132
|
+
process.exitCode = 1;
|
|
133
|
+
return hibResult;
|
|
134
|
+
}
|
|
135
|
+
if (!result.ok) throw new Error(result.error || result.output || "Planner failed");
|
|
136
|
+
|
|
137
|
+
const parsed = parseMaybeJsonString(result.output);
|
|
138
|
+
|
|
139
|
+
// Create v2 plan with HUs from planner steps
|
|
140
|
+
const { createPlanV2 } = await import("../../plan/plan-schema.js");
|
|
141
|
+
const { addHu } = await import("../../plan/plan-hu-ops.js");
|
|
142
|
+
const { savePlan } = await import("../../plan/plan-store.js");
|
|
143
|
+
const { deriveProjectName, classifyTaskType } = await import("../../hu/auto-generator.js");
|
|
144
|
+
|
|
145
|
+
const projectDir = config.projectDir || process.cwd();
|
|
146
|
+
const plan = createPlanV2(task);
|
|
147
|
+
// Default project name resolution, in priority order:
|
|
148
|
+
// 1. cwd signals — package.json "name" → git remote basename →
|
|
149
|
+
// directory basename. This is what the user actually expects:
|
|
150
|
+
// if they ran `git init` in `~/ws_demo/weather-dashboard` and
|
|
151
|
+
// then asked Karajan to plan, the default name should reflect
|
|
152
|
+
// THAT directory, not the words of the SPEC document.
|
|
153
|
+
// 2. Fallback: derive from the task text (legacy heuristic). Used
|
|
154
|
+
// when none of the cwd signals yields anything (e.g. running
|
|
155
|
+
// from a tmp dir, no git, no package.json — typical for
|
|
156
|
+
// ad-hoc planning sessions).
|
|
157
|
+
// The user is still prompted to confirm / override on TTY runs;
|
|
158
|
+
// non-interactive runs (--json, --yes, board-spawned subprocesses)
|
|
159
|
+
// accept the default silently.
|
|
160
|
+
const defaultName = deriveProjectNameFromCwd(projectDir) || deriveProjectName(task);
|
|
161
|
+
const canPrompt = process.stdin.isTTY && process.stdout.isTTY
|
|
162
|
+
&& !json && !flags?.yes && flags?.interactive !== false;
|
|
163
|
+
plan.name = canPrompt ? await promptProjectName(defaultName) : defaultName;
|
|
164
|
+
plan.approach = parsed?.approach || (typeof parsed === "string" ? parsed : (typeof result.output === "string" ? result.output : null));
|
|
165
|
+
plan.risks = parsed?.risks || [];
|
|
166
|
+
plan.outOfScope = parsed?.outOfScope || [];
|
|
167
|
+
// KJC-TSK-0348: persist the SPEC.canvas.md path so `kj sync --apply`
|
|
168
|
+
// can later locate it and append drift notes. Only set when the plan
|
|
169
|
+
// actually came from a Canvas SPEC; flat-task plans stay unstamped.
|
|
170
|
+
if (typeof sourceCanvasPath === "string" && sourceCanvasPath.length > 0) {
|
|
171
|
+
plan.sourceCanvasPath = sourceCanvasPath;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Convert planner steps → HUs, carrying the structured acceptance_tests
|
|
175
|
+
// the LLM was asked to emit. If the LLM fell back to legacy behaviour
|
|
176
|
+
// (omitted tests or returned an empty array) we DO NOT inject a fake
|
|
177
|
+
// suite-wide placeholder — that was the pre-v2.7.5 lie. Instead we
|
|
178
|
+
// stamp `acceptance_tests: []` and the board / CLI flag the HU as
|
|
179
|
+
// "missing test contract" so the user can either regenerate or edit
|
|
180
|
+
// the tests manually before executing.
|
|
181
|
+
const { normaliseAcceptanceTests } = await import("../../plan/plan-schema.js");
|
|
182
|
+
const steps = parsed?.steps || [];
|
|
183
|
+
let stepsMissingTests = 0;
|
|
184
|
+
|
|
185
|
+
// Two-pass HU creation (KJC-TSK-0382 follow-up):
|
|
186
|
+
// Pass 1 — create every HU with `blocked_by: []` and remember the
|
|
187
|
+
// mapping `<symbolic id from planner> -> <stable hu id>`.
|
|
188
|
+
// Pass 2 — translate the planner's `dependencies: [symbolicId, …]`
|
|
189
|
+
// into the corresponding `blocked_by: [huId, …]`.
|
|
190
|
+
// The split is needed because `addHu` assigns the stable id at insertion
|
|
191
|
+
// time, so a forward reference (step B depends on step A whose hu id
|
|
192
|
+
// doesn't exist yet) would otherwise be impossible to resolve.
|
|
193
|
+
const symbolicToHuId = new Map();
|
|
194
|
+
const stepDeps = []; // parallel array: stepDeps[i] = array of symbolic dep ids
|
|
195
|
+
const stepReuse = []; // parallel array: stepReuse[i] = array of symbolic reuse ids (KJC-BUG-0044 / P3)
|
|
196
|
+
for (const step of steps) {
|
|
197
|
+
const desc = typeof step === "string" ? step : step.description || step.title || JSON.stringify(step);
|
|
198
|
+
const rawTests = typeof step === "object" ? step.acceptance_tests : null;
|
|
199
|
+
const tests = normaliseAcceptanceTests(rawTests);
|
|
200
|
+
if (tests.length === 0) stepsMissingTests += 1;
|
|
201
|
+
// Humanización IDs: preservar el id simbólico del planner ("INFRA-001",
|
|
202
|
+
// "AUTH-SIGNUP", etc.) como short_id del HU. La clave canónica
|
|
203
|
+
// sigue siendo el `hu.id` largo (`hu_<planId>_<NNN>`); el short_id
|
|
204
|
+
// solo da una etiqueta amigable que la CLI muestra y acepta como
|
|
205
|
+
// referencia en `kj run --hu INFRA-001`.
|
|
206
|
+
const symbolicId = typeof step === "object" && typeof step.id === "string" && step.id.trim()
|
|
207
|
+
? step.id.trim() : null;
|
|
208
|
+
const taskType = classifyTaskType(desc);
|
|
209
|
+
// KJC-TSK-0405 step 2: asigna coder_model + reviewer_model
|
|
210
|
+
// automáticamente según complexity inferido del task_type. El
|
|
211
|
+
// planner puede emitir `step.complexity` (trivial/simple/medium/complex
|
|
212
|
+
// o score 1-5) para override más fino. Cross-provider reviewer
|
|
213
|
+
// (claude↔codex) por defecto.
|
|
214
|
+
const coderProvider = config?.roles?.coder?.provider || config?.coder || "claude";
|
|
215
|
+
const complexity = (typeof step === "object" && step.complexity)
|
|
216
|
+
? step.complexity
|
|
217
|
+
: complexityFromTaskType(taskType);
|
|
218
|
+
const models = recommendModelsForHu({ complexity, coderProvider, config });
|
|
219
|
+
const hu = addHu(plan, {
|
|
220
|
+
title: desc.slice(0, 80),
|
|
221
|
+
task_type: taskType,
|
|
222
|
+
scope: desc,
|
|
223
|
+
blocked_by: [],
|
|
224
|
+
acceptance_tests: tests,
|
|
225
|
+
short_id: symbolicId,
|
|
226
|
+
spec_section: typeof step === "object" && typeof step.spec_section === "string" ? step.spec_section.trim() || null : null,
|
|
227
|
+
coder_model: models.coder_model,
|
|
228
|
+
coder_provider: models.coder_provider,
|
|
229
|
+
reviewer_model: models.reviewer_model,
|
|
230
|
+
reviewer_provider: models.reviewer_provider,
|
|
231
|
+
});
|
|
232
|
+
// KJC-TSK-0661: provenance stamp (addHu builds its object explicitly,
|
|
233
|
+
// so this goes on the returned reference, not in huData).
|
|
234
|
+
hu.created_by = "planner";
|
|
235
|
+
if (symbolicId) symbolicToHuId.set(symbolicId, hu.id);
|
|
236
|
+
const deps = typeof step === "object" && Array.isArray(step.dependencies) ? step.dependencies : [];
|
|
237
|
+
stepDeps.push(deps);
|
|
238
|
+
const reuse = typeof step === "object" && Array.isArray(step.reuse) ? step.reuse : [];
|
|
239
|
+
stepReuse.push(reuse);
|
|
240
|
+
}
|
|
241
|
+
// Pass 2: resolve dependencies + reuse. Unknown symbolic ids are dropped
|
|
242
|
+
// silently (the plan-reviewer pass will flag them as gaps if they matter).
|
|
243
|
+
for (let i = 0; i < plan.hus.length; i++) {
|
|
244
|
+
const symbolicDeps = stepDeps[i] || [];
|
|
245
|
+
const resolvedDeps = symbolicDeps
|
|
246
|
+
.map((s) => typeof s === "string" ? symbolicToHuId.get(s.trim()) : null)
|
|
247
|
+
.filter(Boolean);
|
|
248
|
+
if (resolvedDeps.length > 0) plan.hus[i].blocked_by = resolvedDeps;
|
|
249
|
+
const symbolicReuse = stepReuse[i] || [];
|
|
250
|
+
const resolvedReuse = symbolicReuse
|
|
251
|
+
.map((s) => typeof s === "string" ? symbolicToHuId.get(s.trim()) : null)
|
|
252
|
+
.filter(Boolean);
|
|
253
|
+
if (resolvedReuse.length > 0) plan.hus[i].reuse = resolvedReuse;
|
|
254
|
+
}
|
|
255
|
+
// Tests-synthesizer pass (v2.7.5): the first planner call asks for
|
|
256
|
+
// acceptance_tests inline, but Claude / Codex frequently skip that
|
|
257
|
+
// sub-instruction on dense specs. If any HU came back without
|
|
258
|
+
// tests, run a focused second pass that ONLY synthesizes tests for
|
|
259
|
+
// those HUs. This is ON by default — the user is paying a CLI
|
|
260
|
+
// subscription, not per-token, so we optimise for plan quality, not
|
|
261
|
+
// round-trip cost. If `--no-tests-synth` (or `--quick`) is set, we
|
|
262
|
+
// skip and warn loudly that `kj run --plan` will refuse to execute.
|
|
263
|
+
const skipSynth = Boolean(flags?.noTestsSynth || flags?.quick);
|
|
264
|
+
if (stepsMissingTests > 0 && !skipSynth) {
|
|
265
|
+
runLog.logText(
|
|
266
|
+
`[planner] ${stepsMissingTests}/${steps.length} HUs missing acceptance_tests — running tests-synthesizer pass`
|
|
267
|
+
);
|
|
268
|
+
// Sub-progress reporter por fase: el usuario ve cada fase como su
|
|
269
|
+
// propio "[planner:X] starting → done" en vez de un solo
|
|
270
|
+
// "[planner] done" que oculta que aún hay trabajo en cola.
|
|
271
|
+
const synthProgress = createCliProgressReporter({ role: "planner:tests-synth", quiet: Boolean(json) });
|
|
272
|
+
const { synthesizeMissingTests } = await import("../../plan/tests-synthesizer.js");
|
|
273
|
+
const husNeedingTests = plan.hus
|
|
274
|
+
.filter((h) => !Array.isArray(h.acceptance_tests) || h.acceptance_tests.length === 0)
|
|
275
|
+
.map((h) => ({ id: h.id, title: h.title, scope: h.scope }));
|
|
276
|
+
const synthResult = await synthesizeMissingTests({
|
|
277
|
+
agent: planner,
|
|
278
|
+
task,
|
|
279
|
+
husNeedingTests,
|
|
280
|
+
onOutput: synthProgress.onOutput,
|
|
281
|
+
silenceTimeoutMs,
|
|
282
|
+
timeoutMs,
|
|
283
|
+
});
|
|
284
|
+
synthProgress.finish(synthResult.ok ? "done" : "failed");
|
|
285
|
+
if (synthResult.ok) {
|
|
286
|
+
let patched = 0;
|
|
287
|
+
for (const hu of plan.hus) {
|
|
288
|
+
const tests = synthResult.tests[hu.id];
|
|
289
|
+
if (tests && tests.length > 0) {
|
|
290
|
+
hu.acceptance_tests = tests;
|
|
291
|
+
hu.updatedAt = new Date().toISOString();
|
|
292
|
+
patched += 1;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
stepsMissingTests = plan.hus.filter((h) => !h.acceptance_tests?.length).length;
|
|
296
|
+
runLog.logText(
|
|
297
|
+
`[planner] tests-synthesizer patched ${patched}/${husNeedingTests.length} HUs (${stepsMissingTests} still missing)`
|
|
298
|
+
);
|
|
299
|
+
} else {
|
|
300
|
+
runLog.logText(`[planner] tests-synthesizer failed: ${synthResult.error}`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (stepsMissingTests > 0) {
|
|
305
|
+
runLog.logText(
|
|
306
|
+
`[planner] WARN: ${stepsMissingTests}/${steps.length} HUs still have no acceptance_tests after the synthesizer pass. `
|
|
307
|
+
+ `Edit them on the board (✎ Edit) or re-run \`kj plan\`. \`kj run --plan\` will refuse to execute these HUs.`
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Plan reviewer pass (PR D). Asks five closed-ended questions
|
|
312
|
+
// about the just-generated plan: missing HUs, missing deps, scope
|
|
313
|
+
// overlap, parallelisable groups, order issues. Output is purely
|
|
314
|
+
// advisory — we surface the findings to the user and they decide
|
|
315
|
+
// whether to apply them. Skipped when --no-plan-review or --quick.
|
|
316
|
+
const skipReview = Boolean(flags?.noPlanReview || flags?.quick);
|
|
317
|
+
if (plan.hus.length > 0 && !skipReview) {
|
|
318
|
+
runLog.logText("[planner] running plan reviewer pass");
|
|
319
|
+
const reviewProgress = createCliProgressReporter({ role: "planner:reviewer", quiet: Boolean(json) });
|
|
320
|
+
const { reviewPlan, findingsCount, findingsCountByType } = await import("../../plan/plan-reviewer.js");
|
|
321
|
+
const review = await reviewPlan({
|
|
322
|
+
agent: planner,
|
|
323
|
+
task,
|
|
324
|
+
hus: plan.hus,
|
|
325
|
+
onOutput: reviewProgress.onOutput,
|
|
326
|
+
silenceTimeoutMs,
|
|
327
|
+
timeoutMs,
|
|
328
|
+
});
|
|
329
|
+
reviewProgress.finish(review.ok ? "done" : "failed");
|
|
330
|
+
if (review.ok) {
|
|
331
|
+
plan.review = review.findings;
|
|
332
|
+
const count = findingsCount(review.findings);
|
|
333
|
+
runLog.logText(
|
|
334
|
+
count > 0
|
|
335
|
+
? `[planner] reviewer surfaced ${count} item(s): ${review.findings.summary || "(see plan.review for details)"}`
|
|
336
|
+
: `[planner] reviewer found nothing actionable: ${review.findings.summary || "OK"}`
|
|
337
|
+
);
|
|
338
|
+
// KJC-BUG-0045 / P4: self-fix loop. The reviewer is flag-only by
|
|
339
|
+
// contract; when it surfaces actionable issues, ask the planner to
|
|
340
|
+
// PATCH the plan, apply in-process, and re-review. Loop until 0
|
|
341
|
+
// findings or maxIterations. Skippable with --no-plan-fixer/--quick.
|
|
342
|
+
const skipFixer = Boolean(flags?.noPlanFixer || flags?.quick);
|
|
343
|
+
// KJC-BUG-0054: bumped 2 → 4 — el guard antiguo cortaba el loop
|
|
344
|
+
// demasiado pronto cuando había muchos hallazgos. Con el guard
|
|
345
|
+
// inteligente (priority vs secondary) podemos permitir más
|
|
346
|
+
// iteraciones sin riesgo de divergir indefinidamente, porque
|
|
347
|
+
// cada iter que NO reduce priority ni total se revierte.
|
|
348
|
+
const maxFixerIterations = 4;
|
|
349
|
+
if (count > 0 && !skipFixer) {
|
|
350
|
+
for (let i = 1; i <= maxFixerIterations; i++) {
|
|
351
|
+
const currentCount = findingsCount(plan.review);
|
|
352
|
+
if (currentCount === 0) break;
|
|
353
|
+
runLog.logText(`[planner] self-fix iter ${i}/${maxFixerIterations} — patching ${currentCount} issue(s)`);
|
|
354
|
+
const fixProgress = createCliProgressReporter({ role: `planner:self-fix iter ${i}`, quiet: Boolean(json) });
|
|
355
|
+
// KJC-BUG-0046 / P5: snapshot plan + review BEFORE applying the patch
|
|
356
|
+
// so we can revert if the iteration regresses (newCount > currentCount).
|
|
357
|
+
// Dogfooding 2026-05-12: GRETA Plan 2 iter 2 went 10 → 17 findings
|
|
358
|
+
// because the fixer's deletions created dangling references the
|
|
359
|
+
// reviewer then flagged. Without the guard, the loop made the plan
|
|
360
|
+
// strictly worse than before iter 2 started.
|
|
361
|
+
const husSnapshot = JSON.parse(JSON.stringify(plan.hus));
|
|
362
|
+
const reviewSnapshot = JSON.parse(JSON.stringify(plan.review));
|
|
363
|
+
const fix = await applyReviewerFeedback({
|
|
364
|
+
agent: planner, task, hus: plan.hus, findings: plan.review,
|
|
365
|
+
onOutput: fixProgress.onOutput, silenceTimeoutMs, timeoutMs,
|
|
366
|
+
});
|
|
367
|
+
if (!fix.ok) {
|
|
368
|
+
fixProgress.finish("failed");
|
|
369
|
+
runLog.logText(`[planner] self-fix iter ${i} failed (non-blocking): ${fix.error}`);
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
const ops = applyFixerPatch(plan, fix.patch);
|
|
373
|
+
runLog.logText(`[planner] self-fix iter ${i} applied: +${ops.added} HU, +${ops.depsAdded} dep, ${ops.archived} archived`);
|
|
374
|
+
if (ops.added + ops.depsAdded + ops.archived === 0) {
|
|
375
|
+
fixProgress.finish("done");
|
|
376
|
+
runLog.logText(`[planner] self-fix iter ${i} empty patch — stopping`);
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
const review2 = await reviewPlan({
|
|
380
|
+
agent: planner, task, hus: plan.hus,
|
|
381
|
+
onOutput: fixProgress.onOutput, silenceTimeoutMs, timeoutMs,
|
|
382
|
+
});
|
|
383
|
+
if (!review2.ok) {
|
|
384
|
+
fixProgress.finish("failed");
|
|
385
|
+
runLog.logText(`[planner] re-review after iter ${i} failed: ${review2.error}`);
|
|
386
|
+
plan.hus = husSnapshot;
|
|
387
|
+
plan.review = reviewSnapshot;
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
390
|
+
// KJC-BUG-0054: smart convergence guard. El guard antiguo
|
|
391
|
+
// (newCount > currentCount → revert) tiraba iteraciones
|
|
392
|
+
// VÁLIDAS que arreglaban un ciclo o una HU faltante pero
|
|
393
|
+
// a cambio creaban deps menores. Ahora distinguimos:
|
|
394
|
+
// - priority = cycles + missing_hus (CRÍTICO, no ignorable)
|
|
395
|
+
// - secondary = missing_deps + overlaps + order non-circular
|
|
396
|
+
//
|
|
397
|
+
// Reglas (en orden):
|
|
398
|
+
// 1. priority bajó → ACEPTAR (aunque secondary suba — el plan es ejecutable)
|
|
399
|
+
// 2. priority igual + total bajó → ACEPTAR
|
|
400
|
+
// 3. priority igual + total igual → ACEPTAR (lateral move, da chance)
|
|
401
|
+
// 4. priority subió → REVERT
|
|
402
|
+
// 5. priority igual + total subió → REVERT (regresión genuina)
|
|
403
|
+
const before = findingsCountByType(reviewSnapshot);
|
|
404
|
+
const after = findingsCountByType(review2.findings);
|
|
405
|
+
const newCount = after.total;
|
|
406
|
+
const accept =
|
|
407
|
+
after.priority < before.priority
|
|
408
|
+
|| (after.priority === before.priority && after.total <= before.total);
|
|
409
|
+
if (!accept) {
|
|
410
|
+
const reason = after.priority > before.priority
|
|
411
|
+
? `priority ${before.priority}→${after.priority}`
|
|
412
|
+
: `total ${before.total}→${after.total} (priority unchanged at ${after.priority})`;
|
|
413
|
+
plan.hus = husSnapshot;
|
|
414
|
+
plan.review = reviewSnapshot;
|
|
415
|
+
fixProgress.finish("reverted");
|
|
416
|
+
runLog.logText(`[planner] self-fix iter ${i} regressed (${reason}) — reverted, stopping`);
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
plan.review = review2.findings;
|
|
420
|
+
fixProgress.finish("done");
|
|
421
|
+
const deltaSummary = after.priority < before.priority
|
|
422
|
+
? `priority ${before.priority}→${after.priority}, total ${before.total}→${after.total}`
|
|
423
|
+
: `total ${before.total}→${after.total}`;
|
|
424
|
+
runLog.logText(`[planner] after self-fix iter ${i}: ${newCount} issue(s) (${deltaSummary})`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
} else {
|
|
428
|
+
runLog.logText(`[planner] reviewer pass failed (non-blocking): ${review.error}`);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// KJC-TSK-0399: deterministic structural integrity pass — cycles,
|
|
433
|
+
// orphan refs, missing short_id. Runs after self-fix loop because
|
|
434
|
+
// the LLM is bad at graph problems. Always runs (cheap, no network).
|
|
435
|
+
if (plan.hus.length > 0) {
|
|
436
|
+
const { fixes } = runStructuralPass(plan);
|
|
437
|
+
if (fixes.length > 0) {
|
|
438
|
+
if (!plan.review) plan.review = {};
|
|
439
|
+
plan.review.structural_fixes = fixes;
|
|
440
|
+
const byKind = fixes.reduce((a, f) => ({ ...a, [f.kind]: (a[f.kind] || 0) + 1 }), {});
|
|
441
|
+
runLog.logText(`[planner] structural pass: ${fixes.length} fix(es) — ${JSON.stringify(byKind)}`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// KJC-TSK-0397: inject a [PREFLIGHT-000] HU at the top of the plan so
|
|
446
|
+
// every functional HU gates on a clean environment (deps + build + tests
|
|
447
|
+
// + lint + git clean, plus cloud auth when applicable). Idempotent: a
|
|
448
|
+
// plan that already has a preflight HU is left untouched. Disable with
|
|
449
|
+
// `--no-preflight-hu`.
|
|
450
|
+
if (flags.preflightHu !== false) {
|
|
451
|
+
await prependPreflightHu(plan, projectDir);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const planId = await savePlan(projectDir, plan);
|
|
455
|
+
const { plansDir } = await import("../../plan/plan-store.js");
|
|
456
|
+
const path = await import("node:path");
|
|
457
|
+
const planPath = path.join(plansDir(projectDir), `${planId}.json`);
|
|
458
|
+
|
|
459
|
+
runLog.logText(`[planner] finished — hus=${plan.hus.length} plan=${planId}`);
|
|
460
|
+
|
|
461
|
+
if (json) {
|
|
462
|
+
console.log(JSON.stringify(plan, null, 2));
|
|
463
|
+
return { ok: true, planId, ref: plan.alias || planId, huCount: plan.hus.length };
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (parsed?.approach) {
|
|
467
|
+
console.log(formatPlan(parsed));
|
|
468
|
+
} else if (plan.approach) {
|
|
469
|
+
console.log(plan.approach);
|
|
470
|
+
}
|
|
471
|
+
console.log(`\n## HUs (${plan.hus.length})`);
|
|
472
|
+
console.log(formatHuTable(plan.hus));
|
|
473
|
+
// Tell the user what just happened and what their two real options are.
|
|
474
|
+
// Pre-v2.7.5 we printed Review / Approve / Execute as three lines, which
|
|
475
|
+
// confused everyone:
|
|
476
|
+
// - "Review" sounded like an action; `kj plan show` is read-only.
|
|
477
|
+
// - "Approve" exposed the internal `kj plan ready` gate that the board
|
|
478
|
+
// workflow now bypasses (the board's "Run plan" button calls
|
|
479
|
+
// `kj run --plan` directly and the tests-first gate is enforced there).
|
|
480
|
+
// Now we just say: inspect, then run. The board is the recommended UI.
|
|
481
|
+
console.log(`\nPlan saved: ${planId} (${plan.hus.length} HUs, status: ${plan.status})`);
|
|
482
|
+
console.log(` → ${planPath}`);
|
|
483
|
+
// Reviewer findings: print a short summary so the user knows
|
|
484
|
+
// at-a-glance whether the plan needs attention. Full details live
|
|
485
|
+
// in `plan.review` and `kj plan show <id>` renders them.
|
|
486
|
+
if (plan.review) {
|
|
487
|
+
const r = plan.review;
|
|
488
|
+
const counts = [
|
|
489
|
+
r.missing_hus?.length ? `${r.missing_hus.length} missing HU(s)` : null,
|
|
490
|
+
r.missing_dependencies?.length ? `${r.missing_dependencies.length} missing dep(s)` : null,
|
|
491
|
+
r.scope_overlaps?.length ? `${r.scope_overlaps.length} overlap(s)` : null,
|
|
492
|
+
r.order_issues?.length ? `${r.order_issues.length} order issue(s)` : null,
|
|
493
|
+
].filter(Boolean);
|
|
494
|
+
if (counts.length === 0) {
|
|
495
|
+
console.log(`Reviewer: ✓ ${r.summary || "no issues"}`);
|
|
496
|
+
} else {
|
|
497
|
+
console.log(`Reviewer flagged: ${counts.join(", ")} — see \`kj plan show ${planId}\``);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
// Humanización IDs: si el plan tiene alias, lo preferimos como
|
|
501
|
+
// referencia visible (más legible). Fallback al planId.
|
|
502
|
+
const ref = plan.alias || planId;
|
|
503
|
+
console.log("");
|
|
504
|
+
console.log(`Inspect: kj plan show ${ref}`);
|
|
505
|
+
console.log(`Run: kj run --plan ${ref}`);
|
|
506
|
+
console.log(` (or click ▶ Run plan on the board, if running)`);
|
|
507
|
+
|
|
508
|
+
// UX boost for HU-bearing plans: auto-start the HU Board (same pattern as
|
|
509
|
+
// `kj run` auto-HU generation) so the user can inspect the 12-HU plan in a
|
|
510
|
+
// browser instead of squinting at the ASCII table. The board reads
|
|
511
|
+
// ~/.kj/plans/ directly, so this plan is visible immediately. Never in
|
|
512
|
+
// vitest / test mode, and never when `--json` is active.
|
|
513
|
+
if (plan.hus.length > 0 && !process.env.VITEST && process.env.NODE_ENV !== "test") {
|
|
514
|
+
try {
|
|
515
|
+
const { startBoard, renderBoardBanner } = await import("../board.js");
|
|
516
|
+
const { projectSlug: slugFor } = await import("../../plan/plan-store.js");
|
|
517
|
+
const boardPort = config?.hu_board?.port ?? 4000;
|
|
518
|
+
// `projectSlug` scopes the board URL to this project's view
|
|
519
|
+
// (`#board/<slug>`) instead of the global "All projects" dashboard.
|
|
520
|
+
// The hash route opens the board pre-filtered to that project.
|
|
521
|
+
const boardResult = await startBoard(boardPort, { projectSlug: slugFor(projectDir) });
|
|
522
|
+
const status = boardResult.alreadyRunning ? "already running" : "started";
|
|
523
|
+
console.log(renderBoardBanner({ url: boardResult.url, status, projectName: plan.name }));
|
|
524
|
+
} catch (err) {
|
|
525
|
+
// Non-blocking: plan is saved, only the visualiser failed.
|
|
526
|
+
logger?.warn?.(`HU Board auto-start failed (non-blocking): ${err.message}`);
|
|
527
|
+
console.log(`\nVisualize: kj board start (http://localhost:${config?.hu_board?.port ?? 4000})`);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
// KJC-TSK-0574: surface planId/ref so `kj autorun` can chain plan → run.
|
|
531
|
+
return { ok: true, planId, ref, huCount: plan.hus.length };
|
|
532
|
+
}
|