@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,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token persistence for HU Board (KJC-TSK-0355).
|
|
3
|
+
*
|
|
4
|
+
* Stored at `~/.karajan/hu-board/token` (or `$KJ_HOME/hu-board/token`)
|
|
5
|
+
* with mode 0600 — single-line, hex-encoded, 32 bytes of randomness.
|
|
6
|
+
* Idempotent: read existing token if file exists, otherwise generate
|
|
7
|
+
* + persist a new one.
|
|
8
|
+
*
|
|
9
|
+
* The token is ONLY enforced when the server binds to a non-loopback
|
|
10
|
+
* interface. See `auth.js` for the full threat model.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { randomBytes } from "node:crypto";
|
|
14
|
+
import { mkdirSync, readFileSync, writeFileSync, chmodSync, existsSync } from "node:fs";
|
|
15
|
+
import { dirname, join } from "node:path";
|
|
16
|
+
|
|
17
|
+
const TOKEN_BYTES = 32;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Compute the on-disk token path. Delegates to db.js::getKjHome which
|
|
21
|
+
* (post-KJC-TSK-0420) honours KARAJAN_HOME > KJ_HOME > VITEST > default.
|
|
22
|
+
* @returns {string}
|
|
23
|
+
*/
|
|
24
|
+
import { getKjHome } from "./db.js";
|
|
25
|
+
export function getTokenPath() {
|
|
26
|
+
return join(getKjHome(), "hu-board", "token");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Read the persisted token if it exists; otherwise generate a fresh
|
|
31
|
+
* one, write it (mode 0600), and return it. Returns `null` only if
|
|
32
|
+
* the filesystem operation itself fails (caller decides whether that
|
|
33
|
+
* disables auth or aborts startup).
|
|
34
|
+
*
|
|
35
|
+
* @returns {string|null}
|
|
36
|
+
*/
|
|
37
|
+
export function getOrCreateToken() {
|
|
38
|
+
const tokenPath = getTokenPath();
|
|
39
|
+
try {
|
|
40
|
+
if (existsSync(tokenPath)) {
|
|
41
|
+
const raw = readFileSync(tokenPath, "utf8").trim();
|
|
42
|
+
if (raw.length >= 16) return raw;
|
|
43
|
+
// Corrupt or empty file — overwrite below.
|
|
44
|
+
}
|
|
45
|
+
const token = randomBytes(TOKEN_BYTES).toString("hex");
|
|
46
|
+
mkdirSync(dirname(tokenPath), { recursive: true });
|
|
47
|
+
writeFileSync(tokenPath, token, { mode: 0o600 });
|
|
48
|
+
try { chmodSync(tokenPath, 0o600); } catch { /* best-effort on Windows */ }
|
|
49
|
+
return token;
|
|
50
|
+
} catch (err) {
|
|
51
|
+
// Don't crash the board over this — just disable auth and let the
|
|
52
|
+
// caller log a warning.
|
|
53
|
+
console.warn(`[token-store] failed to manage token file at ${tokenPath}: ${err.message}`);
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zombie session reaper for the HU Board (board-polish #2,
|
|
3
|
+
* KJC dogfooding 2026-05-07).
|
|
4
|
+
*
|
|
5
|
+
* Why this exists
|
|
6
|
+
* ===============
|
|
7
|
+
*
|
|
8
|
+
* Sessions in the SQLite db can stay "in flight" forever when the
|
|
9
|
+
* orchestrator process dies between iterations: their `status` stays
|
|
10
|
+
* `coding` / `stalled` / `paused`, the UI keeps surfacing them as
|
|
11
|
+
* actionable, and `kj board start` weeks later still pops a
|
|
12
|
+
* "Karajan needs an answer" modal for a checkpoint nobody owns
|
|
13
|
+
* anymore.
|
|
14
|
+
*
|
|
15
|
+
* Concretely, on 2026-05-07 the user opened the board on a fresh
|
|
16
|
+
* `kj board start` and saw the popup of an HU from 2026-04-27 — 9
|
|
17
|
+
* days dead, status still `paused`. The fix per his explicit
|
|
18
|
+
* mandate:
|
|
19
|
+
*
|
|
20
|
+
* "No puede quedarse nada zombie. Debe haber algun sistema que lo
|
|
21
|
+
* compruebe al arrancar y mate esos zombies."
|
|
22
|
+
*
|
|
23
|
+
* Thresholds (per his decision in the same session):
|
|
24
|
+
* - status in (coding | stalled) and >= 6h since updated_at → reap
|
|
25
|
+
* - status === paused and >= 24h since updated_at → reap
|
|
26
|
+
* - everything else → skip
|
|
27
|
+
*
|
|
28
|
+
* What "reap" means: write `status: "failed"` BOTH to the DB row
|
|
29
|
+
* AND to the underlying `~/.karajan/sessions/<id>/session.json`,
|
|
30
|
+
* append a structured checkpoint of the form
|
|
31
|
+
* { stage: 'zombie-reap', reason: <reason>, at: <iso> }
|
|
32
|
+
* so the audit trail survives. The session row stays in place
|
|
33
|
+
* (we don't DELETE — the user can still inspect what happened).
|
|
34
|
+
*
|
|
35
|
+
* Why update the JSON on disk too
|
|
36
|
+
* -------------------------------
|
|
37
|
+
*
|
|
38
|
+
* The board's `fullScan()` reads every session.json on startup and
|
|
39
|
+
* upserts the row, OVERWRITING any DB-only mutation. If the reaper
|
|
40
|
+
* only updated the DB, the next reboot would reset the status back
|
|
41
|
+
* to `paused`/`stalled` because the JSON still says so. Persisting
|
|
42
|
+
* to disk makes the reap permanent on the first reboot — the user
|
|
43
|
+
* sees the cleanup once, and never again.
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
import fs from "node:fs";
|
|
47
|
+
import path from "node:path";
|
|
48
|
+
|
|
49
|
+
const DEFAULT_CODING_HOURS = 6;
|
|
50
|
+
const DEFAULT_PAUSED_HOURS = 24;
|
|
51
|
+
const CODING_LIKE_STATUSES = new Set(["coding", "stalled", "running", "in_progress"]);
|
|
52
|
+
const PAUSED_STATUSES = new Set(["paused", "checkpoint", "awaiting_answer"]);
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Decide whether a single session row qualifies as zombie at `now`.
|
|
56
|
+
* Pure function — no I/O. Used by both the reaper itself and the
|
|
57
|
+
* unit tests.
|
|
58
|
+
*
|
|
59
|
+
* @param {{ id: string, status: string, updated_at?: string|null,
|
|
60
|
+
* created_at?: string|null }} session
|
|
61
|
+
* @param {{ now: number, codingHours: number, pausedHours: number }} ctx
|
|
62
|
+
* @returns {{ zombie: boolean, reason?: string }}
|
|
63
|
+
*/
|
|
64
|
+
export function classifyZombie(session, ctx) {
|
|
65
|
+
if (!session || !session.status) return { zombie: false };
|
|
66
|
+
const status = String(session.status).toLowerCase();
|
|
67
|
+
// Reference timestamp: prefer updated_at (the daemon writes that on
|
|
68
|
+
// every checkpoint sync), fall back to created_at (in case the row
|
|
69
|
+
// was inserted but never touched again).
|
|
70
|
+
const refIso = session.updated_at || session.created_at;
|
|
71
|
+
if (!refIso) return { zombie: false };
|
|
72
|
+
const refTs = new Date(refIso).getTime();
|
|
73
|
+
if (Number.isNaN(refTs)) return { zombie: false };
|
|
74
|
+
const ageHours = (ctx.now - refTs) / 3_600_000;
|
|
75
|
+
if (ageHours <= 0) return { zombie: false };
|
|
76
|
+
if (CODING_LIKE_STATUSES.has(status) && ageHours >= ctx.codingHours) {
|
|
77
|
+
return { zombie: true, reason: `${status} ${ageHours.toFixed(1)}h inactive (>= ${ctx.codingHours}h)` };
|
|
78
|
+
}
|
|
79
|
+
if (PAUSED_STATUSES.has(status) && ageHours >= ctx.pausedHours) {
|
|
80
|
+
return { zombie: true, reason: `${status} ${ageHours.toFixed(1)}h inactive (>= ${ctx.pausedHours}h)` };
|
|
81
|
+
}
|
|
82
|
+
return { zombie: false };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Filter a list of session rows down to the zombie ones. Pure.
|
|
87
|
+
*
|
|
88
|
+
* @param {Array<object>} sessions
|
|
89
|
+
* @param {{ now?: number, codingHours?: number, pausedHours?: number }} [opts]
|
|
90
|
+
* @returns {Array<{ session: object, reason: string }>}
|
|
91
|
+
*/
|
|
92
|
+
export function findZombieSessions(sessions, opts = {}) {
|
|
93
|
+
const ctx = {
|
|
94
|
+
now: opts.now ?? Date.now(),
|
|
95
|
+
codingHours: opts.codingHours ?? DEFAULT_CODING_HOURS,
|
|
96
|
+
pausedHours: opts.pausedHours ?? DEFAULT_PAUSED_HOURS,
|
|
97
|
+
};
|
|
98
|
+
const out = [];
|
|
99
|
+
for (const session of sessions || []) {
|
|
100
|
+
const verdict = classifyZombie(session, ctx);
|
|
101
|
+
if (verdict.zombie) out.push({ session, reason: verdict.reason });
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Append a `zombie-reap` checkpoint to a JSON-encoded checkpoints
|
|
108
|
+
* blob (the on-disk format). Returns the new JSON string.
|
|
109
|
+
*
|
|
110
|
+
* Defensive about malformed input: if the existing blob isn't a
|
|
111
|
+
* JSON array, replaces it with a fresh array containing the new
|
|
112
|
+
* entry — never throws.
|
|
113
|
+
*/
|
|
114
|
+
export function appendZombieCheckpoint(existingJson, reason, isoTimestamp) {
|
|
115
|
+
let arr = [];
|
|
116
|
+
if (existingJson) {
|
|
117
|
+
try {
|
|
118
|
+
const parsed = JSON.parse(existingJson);
|
|
119
|
+
if (Array.isArray(parsed)) arr = parsed;
|
|
120
|
+
} catch { /* fall through with empty arr */ }
|
|
121
|
+
}
|
|
122
|
+
arr.push({ stage: "zombie-reap", reason, at: isoTimestamp });
|
|
123
|
+
return JSON.stringify(arr);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Default location of the on-disk session JSON files. Delegates to
|
|
128
|
+
* db.js::getKjHome (KJC-TSK-0420): KARAJAN_HOME > KJ_HOME > VITEST > default.
|
|
129
|
+
* @returns {string}
|
|
130
|
+
*/
|
|
131
|
+
import { getKjHome } from "./db.js";
|
|
132
|
+
function getSessionsDir() {
|
|
133
|
+
return path.join(getKjHome(), "sessions");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Persist `status: "failed"` and the new zombie-reap checkpoint to
|
|
138
|
+
* the on-disk session.json. This stops the next `fullScan()` from
|
|
139
|
+
* resurrecting the zombi.
|
|
140
|
+
*
|
|
141
|
+
* Safe-by-default: if the file is missing, malformed, or the write
|
|
142
|
+
* fails, this returns `false` and the caller logs a warning. We
|
|
143
|
+
* never throw and we never block the daemon's startup.
|
|
144
|
+
*
|
|
145
|
+
* @param {string} sessionId
|
|
146
|
+
* @param {string} reason
|
|
147
|
+
* @param {string} reapedAtIso
|
|
148
|
+
* @param {{ sessionsDir?: string }} [opts]
|
|
149
|
+
* @returns {boolean} true when the file was rewritten successfully
|
|
150
|
+
*/
|
|
151
|
+
export function markSessionFailedOnDisk(sessionId, reason, reapedAtIso, opts = {}) {
|
|
152
|
+
const dir = opts.sessionsDir || getSessionsDir();
|
|
153
|
+
const file = path.join(dir, sessionId, "session.json");
|
|
154
|
+
let raw;
|
|
155
|
+
try { raw = fs.readFileSync(file, "utf8"); } catch { return false; }
|
|
156
|
+
let parsed;
|
|
157
|
+
try { parsed = JSON.parse(raw); } catch { return false; }
|
|
158
|
+
if (!parsed || typeof parsed !== "object") return false;
|
|
159
|
+
parsed.status = "failed";
|
|
160
|
+
parsed.updated_at = reapedAtIso;
|
|
161
|
+
parsed.checkpoints = Array.isArray(parsed.checkpoints) ? parsed.checkpoints : [];
|
|
162
|
+
parsed.checkpoints.push({ stage: "zombie-reap", reason, at: reapedAtIso });
|
|
163
|
+
try {
|
|
164
|
+
fs.writeFileSync(file, JSON.stringify(parsed, null, 2));
|
|
165
|
+
return true;
|
|
166
|
+
} catch { return false; }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Reap every zombie session in the DB AND in the underlying
|
|
171
|
+
* session.json files. Side-effecting. Returns the list of reaped
|
|
172
|
+
* sessions so the server can log them.
|
|
173
|
+
*
|
|
174
|
+
* @param {object} deps
|
|
175
|
+
* @param {object} deps.db better-sqlite3 Database handle
|
|
176
|
+
* @param {object} [deps.opts] threshold + sessionsDir overrides
|
|
177
|
+
* @param {Function} [deps.now] () => number, for tests
|
|
178
|
+
* @returns {Array<{ id: string, status_before: string, reason: string, disk_persisted: boolean }>}
|
|
179
|
+
*/
|
|
180
|
+
export function reapZombieSessions({ db, opts = {}, now = () => Date.now() }) {
|
|
181
|
+
if (!db) return [];
|
|
182
|
+
const allSessions = db.prepare(
|
|
183
|
+
"SELECT id, status, updated_at, created_at, checkpoints FROM sessions WHERE status NOT IN ('failed', 'approved', 'cancelled', 'done')",
|
|
184
|
+
).all();
|
|
185
|
+
const zombies = findZombieSessions(allSessions, { ...opts, now: now() });
|
|
186
|
+
if (zombies.length === 0) return [];
|
|
187
|
+
const update = db.prepare(
|
|
188
|
+
"UPDATE sessions SET status = 'failed', checkpoints = ?, updated_at = ? WHERE id = ?",
|
|
189
|
+
);
|
|
190
|
+
const reapedAt = new Date(now()).toISOString();
|
|
191
|
+
const reaped = [];
|
|
192
|
+
for (const { session, reason } of zombies) {
|
|
193
|
+
const newCheckpoints = appendZombieCheckpoint(session.checkpoints, reason, reapedAt);
|
|
194
|
+
update.run(newCheckpoints, reapedAt, session.id);
|
|
195
|
+
const diskPersisted = markSessionFailedOnDisk(session.id, reason, reapedAt, {
|
|
196
|
+
sessionsDir: opts.sessionsDir,
|
|
197
|
+
});
|
|
198
|
+
reaped.push({
|
|
199
|
+
id: session.id,
|
|
200
|
+
status_before: session.status,
|
|
201
|
+
reason,
|
|
202
|
+
disk_persisted: diskPersisted,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
return reaped;
|
|
206
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# AI Attribution Guard — GitHub Action
|
|
2
|
+
#
|
|
3
|
+
# Copy this file to .github/workflows/ai-attribution-guard.yml in any repo.
|
|
4
|
+
# Blocks PRs that contain AI attribution comments or Co-Authored-By lines.
|
|
5
|
+
# No dependencies, no Node.js required. Pure bash + grep.
|
|
6
|
+
|
|
7
|
+
name: AI Attribution Guard
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
pull_request:
|
|
11
|
+
types: [opened, synchronize, reopened]
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
scan:
|
|
15
|
+
name: Scan for AI attribution
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
with:
|
|
20
|
+
fetch-depth: 0
|
|
21
|
+
|
|
22
|
+
- name: Check PR diff for AI attribution
|
|
23
|
+
# KJC-BUG-0072 — never interpolate ${{ github.* }} directly inside a
|
|
24
|
+
# `run:` block on pull_request workflows. Pin to env vars instead.
|
|
25
|
+
env:
|
|
26
|
+
BASE_REF: ${{ github.base_ref }}
|
|
27
|
+
run: |
|
|
28
|
+
DIFF=$(git diff "origin/${BASE_REF}...HEAD" \
|
|
29
|
+
-- ':!*.png' ':!*.jpg' ':!*.svg' ':!*.ico' \
|
|
30
|
+
':!*.lock' ':!package-lock.json' ':!yarn.lock' \
|
|
31
|
+
':!*.min.js' ':!*.min.css' \
|
|
32
|
+
':!.githooks/*' \
|
|
33
|
+
| grep '^+' \
|
|
34
|
+
| grep -v '^+++' \
|
|
35
|
+
|| true)
|
|
36
|
+
|
|
37
|
+
[ -z "$DIFF" ] && echo "No diff to scan" && exit 0
|
|
38
|
+
|
|
39
|
+
# Attribution patterns (not functional references like provider: "claude")
|
|
40
|
+
ATTR='(Generated|Created|Written|Made|Built|Authored|Assisted|Powered|Produced) (by|with|using|via) .*(Claude|Anthropic|OpenAI|ChatGPT|GPT-[0-9]|Copilot|Gemini|Cursor|Windsurf|Codeium)'
|
|
41
|
+
COAUTH='Co-Authored-By:.*\b(Claude|Anthropic|OpenAI|ChatGPT|Copilot|Gemini|Cursor|Windsurf|Codeium)\b'
|
|
42
|
+
COMMENT='(//|#|/\*|\*|<!--|--)\s*.*(Claude|Anthropic|OpenAI|ChatGPT|Copilot|Gemini).*(generated|assisted|helped|created|wrote|built|made)'
|
|
43
|
+
|
|
44
|
+
FOUND=""
|
|
45
|
+
for PAT in "$ATTR" "$COAUTH" "$COMMENT"; do
|
|
46
|
+
MATCH=$(echo "$DIFF" | grep -i -E "$PAT" || true)
|
|
47
|
+
[ -n "$MATCH" ] && FOUND="${FOUND}${MATCH}\n"
|
|
48
|
+
done
|
|
49
|
+
|
|
50
|
+
if [ -n "$FOUND" ]; then
|
|
51
|
+
echo "::error::AI attribution detected in PR diff"
|
|
52
|
+
echo -e "$FOUND"
|
|
53
|
+
echo ""
|
|
54
|
+
echo "Remove AI attribution comments/lines and push again."
|
|
55
|
+
exit 1
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
echo "AI attribution guard: clean"
|
|
59
|
+
|
|
60
|
+
- name: Check commit messages
|
|
61
|
+
# KJC-BUG-0072 — same hardening as the diff scan: env-pin the ref.
|
|
62
|
+
env:
|
|
63
|
+
BASE_REF: ${{ github.base_ref }}
|
|
64
|
+
run: |
|
|
65
|
+
COMMITS=$(git log --format='%s%n%b' "origin/${BASE_REF}...HEAD")
|
|
66
|
+
|
|
67
|
+
COAUTH='Co-Authored-By:.*\b(Claude|Anthropic|OpenAI|ChatGPT|Copilot|Gemini|Cursor|Windsurf|Codeium)\b'
|
|
68
|
+
ATTR='(Generated|Created|Written|Built|Authored) (by|with|using) .*(Claude|Anthropic|OpenAI|ChatGPT|Copilot|Gemini|Cursor|Windsurf|Codeium)'
|
|
69
|
+
|
|
70
|
+
FOUND=""
|
|
71
|
+
for PAT in "$COAUTH" "$ATTR"; do
|
|
72
|
+
MATCH=$(echo "$COMMITS" | grep -i -E "$PAT" || true)
|
|
73
|
+
[ -n "$MATCH" ] && FOUND="${FOUND}${MATCH}\n"
|
|
74
|
+
done
|
|
75
|
+
|
|
76
|
+
if [ -n "$FOUND" ]; then
|
|
77
|
+
echo "::error::AI attribution detected in commit messages"
|
|
78
|
+
echo -e "$FOUND"
|
|
79
|
+
echo ""
|
|
80
|
+
echo "Amend commits to remove AI attribution and force-push."
|
|
81
|
+
exit 1
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
echo "Commit messages: clean"
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* scripts/audit-test-diet.mjs — semantic loss-of-meaning auditor (KJC-TSK-0345).
|
|
4
|
+
* Categories: empty-no-expect, skipped-pending, imports-orphan, deprecated-export,
|
|
5
|
+
* subsumed-candidate. Each finding carries confidence (high|low). Complements
|
|
6
|
+
* tests/_diet/audit.mjs (which classifies by LOC bucket only).
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
|
|
12
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const DEFAULT_ROOT = resolve(HERE, "..");
|
|
14
|
+
const SKIP_DIRS = new Set(["_diet", "fixtures", "node_modules"]);
|
|
15
|
+
|
|
16
|
+
export function walkTests(rootDir, testsSubdir = "tests") {
|
|
17
|
+
const out = [];
|
|
18
|
+
const base = join(rootDir, testsSubdir);
|
|
19
|
+
if (!existsSync(base)) return out;
|
|
20
|
+
const stack = [base];
|
|
21
|
+
while (stack.length) {
|
|
22
|
+
for (const e of readdirSync(stack.pop(), { withFileTypes: true })) {
|
|
23
|
+
const p = join(e.parentPath ?? base, e.name);
|
|
24
|
+
if (e.isDirectory() && !SKIP_DIRS.has(e.name)) stack.push(p);
|
|
25
|
+
else if (e.isFile() && /\.test\.(m?js|ts)$/.test(e.name)) out.push(p);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function parseImports(source) {
|
|
32
|
+
const imports = [];
|
|
33
|
+
const re = /^[ \t]*import\s+(?:([\w*\s{},]+?)\s+from\s+)?['"]([^'"]+)['"]/gm;
|
|
34
|
+
let m;
|
|
35
|
+
while ((m = re.exec(source)) !== null) {
|
|
36
|
+
const named = [];
|
|
37
|
+
const braced = (m[1] || "").match(/\{([^}]+)\}/);
|
|
38
|
+
if (braced) for (const part of braced[1].split(",")) {
|
|
39
|
+
const name = part.trim().split(/\s+as\s+/)[0].trim();
|
|
40
|
+
if (name) named.push(name);
|
|
41
|
+
}
|
|
42
|
+
imports.push({ specifier: m[2], named });
|
|
43
|
+
}
|
|
44
|
+
return imports;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function resolveSrcImport(testFile, specifier, rootDir) {
|
|
48
|
+
if (!specifier.startsWith(".") && !specifier.startsWith("#")) return null;
|
|
49
|
+
const base = specifier.startsWith("#") ? resolve(rootDir, specifier.replace(/^#/, "src/")) : resolve(dirname(testFile), specifier);
|
|
50
|
+
for (const c of [base, `${base}.js`, `${base}.mjs`, join(base, "index.js")]) {
|
|
51
|
+
if (existsSync(c) && statSync(c).isFile()) return c;
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function extractItNames(source) {
|
|
57
|
+
const names = new Set();
|
|
58
|
+
for (const m of source.matchAll(/\b(?:it|test)(?:\.(?:only|skip|todo))?\s*\(\s*['"`]([^'"`]+)['"`]/g)) names.add(m[1]);
|
|
59
|
+
return names;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function extractExports(source) {
|
|
63
|
+
const names = new Set();
|
|
64
|
+
for (const m of source.matchAll(/export\s+(?:async\s+)?(?:function|class|const|let|var)\s+([A-Za-z_$][\w$]*)/g)) names.add(m[1]);
|
|
65
|
+
for (const m of source.matchAll(/export\s*\{([^}]+)\}/g)) for (const part of m[1].split(",")) {
|
|
66
|
+
const name = part.trim().split(/\s+as\s+/).pop().trim();
|
|
67
|
+
if (name) names.add(name);
|
|
68
|
+
}
|
|
69
|
+
if (/export\s+default\b/.test(source)) names.add("default");
|
|
70
|
+
return names;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function auditFile(testFile, rootDir) {
|
|
74
|
+
const source = readFileSync(testFile, "utf-8");
|
|
75
|
+
const rel = relative(rootDir, testFile);
|
|
76
|
+
const findings = [];
|
|
77
|
+
const hasIt = /\b(?:it|test)\s*\(/.test(source);
|
|
78
|
+
if (hasIt && !/\bexpect\s*\(/.test(source)) findings.push({ category: "empty-no-expect", confidence: "high", detail: "it()/test() blocks with no expect()" });
|
|
79
|
+
if (/\b(?:it|test|describe)\.skip\s*\(|\bxit\s*\(|\bxtest\s*\(|\b(?:it|test)\.todo\s*\(/.test(source)) {
|
|
80
|
+
findings.push({ category: "skipped-pending", confidence: "high", detail: "uses .skip / xit / xtest / .todo" });
|
|
81
|
+
}
|
|
82
|
+
const srcImports = [];
|
|
83
|
+
for (const imp of parseImports(source)) {
|
|
84
|
+
if (!imp.specifier.startsWith(".") && !imp.specifier.startsWith("#")) continue;
|
|
85
|
+
const resolved = resolveSrcImport(testFile, imp.specifier, rootDir);
|
|
86
|
+
if (!resolved) { findings.push({ category: "imports-orphan", confidence: "high", detail: `cannot resolve '${imp.specifier}'` }); continue; }
|
|
87
|
+
if (imp.named.length) {
|
|
88
|
+
const missing = imp.named.filter((n) => !extractExports(readFileSync(resolved, "utf-8")).has(n));
|
|
89
|
+
if (missing.length) findings.push({ category: "deprecated-export", confidence: "low", detail: `${imp.specifier} no longer exports: ${missing.join(", ")}` });
|
|
90
|
+
}
|
|
91
|
+
srcImports.push({ resolved: relative(rootDir, resolved), named: imp.named });
|
|
92
|
+
}
|
|
93
|
+
return { path: rel, srcImports, findings, itNames: extractItNames(source) };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function findSubsumed(perFile) {
|
|
97
|
+
const bySrc = new Map();
|
|
98
|
+
for (const f of perFile) for (const imp of f.srcImports) {
|
|
99
|
+
if (!bySrc.has(imp.resolved)) bySrc.set(imp.resolved, []);
|
|
100
|
+
bySrc.get(imp.resolved).push({ path: f.path, named: new Set(imp.named), itNames: f.itNames });
|
|
101
|
+
}
|
|
102
|
+
const out = [];
|
|
103
|
+
for (const [src, consumers] of bySrc) {
|
|
104
|
+
if (consumers.length < 2) continue;
|
|
105
|
+
const sorted = [...consumers].sort((a, b) => a.named.size - b.named.size);
|
|
106
|
+
const narrow = sorted[0]; const broad = sorted[sorted.length - 1];
|
|
107
|
+
if (narrow.path === broad.path || narrow.named.size === 0 || broad.named.size === 0) continue;
|
|
108
|
+
if (!(narrow.named.size < broad.named.size && [...narrow.named].every((n) => broad.named.has(n)))) continue;
|
|
109
|
+
// Require ≥50% of narrow's it() names to also appear in broad's. Imports
|
|
110
|
+
// alone are a false-positive trap: two tests can share helpers and assert
|
|
111
|
+
// on disjoint behavior (e.g. tests/budget.test.js vs tests/utils/budget.test.js).
|
|
112
|
+
if (narrow.itNames.size === 0) continue;
|
|
113
|
+
const overlap = [...narrow.itNames].filter((n) => broad.itNames.has(n)).length;
|
|
114
|
+
if (overlap / narrow.itNames.size < 0.5) continue;
|
|
115
|
+
out.push({ category: "subsumed-candidate", confidence: "low", path: narrow.path, detail: `${overlap}/${narrow.itNames.size} it() names also in ${broad.path}; subset of imports from ${src}` });
|
|
116
|
+
}
|
|
117
|
+
return out;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function auditTestDiet(rootDir = DEFAULT_ROOT) {
|
|
121
|
+
const files = walkTests(rootDir);
|
|
122
|
+
const perFile = files.map((f) => auditFile(f, rootDir));
|
|
123
|
+
const findings = [];
|
|
124
|
+
for (const f of perFile) for (const x of f.findings) findings.push({ path: f.path, ...x });
|
|
125
|
+
for (const s of findSubsumed(perFile)) findings.push(s);
|
|
126
|
+
return { generatedAt: new Date().toISOString(), rootDir, totals: { filesAudited: files.length, findings: findings.length }, findings };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function renderMarkdown(report) {
|
|
130
|
+
const byCat = new Map();
|
|
131
|
+
for (const f of report.findings) {
|
|
132
|
+
if (!byCat.has(f.category)) byCat.set(f.category, []);
|
|
133
|
+
byCat.get(f.category).push(f);
|
|
134
|
+
}
|
|
135
|
+
const lines = [`# Semantic test-diet audit`, ``, `Generated: ${report.generatedAt}`, `Files audited: ${report.totals.filesAudited}`, `Findings: ${report.totals.findings}`, ``];
|
|
136
|
+
for (const [cat, items] of byCat) {
|
|
137
|
+
lines.push(`## ${cat} (${items.length})`, ``);
|
|
138
|
+
for (const it of items) lines.push(`- \`${it.path}\` — ${it.detail} (confidence: ${it.confidence})`);
|
|
139
|
+
lines.push(``);
|
|
140
|
+
}
|
|
141
|
+
return lines.join("\n");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
145
|
+
const report = auditTestDiet(DEFAULT_ROOT);
|
|
146
|
+
const outDir = join(DEFAULT_ROOT, "audit");
|
|
147
|
+
if (!existsSync(outDir)) mkdirSync(outDir, { recursive: true });
|
|
148
|
+
writeFileSync(join(outDir, "test-diet-semantic.json"), JSON.stringify(report, null, 2));
|
|
149
|
+
writeFileSync(join(outDir, "test-diet-semantic.md"), renderMarkdown(report));
|
|
150
|
+
console.log(`audit: ${report.totals.filesAudited} files, ${report.totals.findings} findings -> audit/test-diet-semantic.{json,md}`);
|
|
151
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* build-sea.mjs — Build a Node.js Single Executable Application (SEA) binary.
|
|
5
|
+
*
|
|
6
|
+
* Steps:
|
|
7
|
+
* 1. esbuild bundles src/cli.js -> dist/kj-bundle.cjs (CJS, all deps inlined)
|
|
8
|
+
* 2. Generate SEA preparation blob via node --experimental-sea-config
|
|
9
|
+
* 3. Copy the current node binary to dist/kj (or dist/kj.exe on Windows)
|
|
10
|
+
* 4. Inject the blob into the binary with postject
|
|
11
|
+
* 5. (macOS only) Re-sign the binary with ad-hoc signature
|
|
12
|
+
*
|
|
13
|
+
* Requirements (installed as devDependencies — run `npm ci` first):
|
|
14
|
+
* - esbuild
|
|
15
|
+
* - postject
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* node scripts/build-sea.mjs
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { execSync } from "node:child_process";
|
|
22
|
+
import { cpSync, mkdirSync, writeFileSync, statSync, readFileSync } from "node:fs";
|
|
23
|
+
import path from "node:path";
|
|
24
|
+
import { fileURLToPath } from "node:url";
|
|
25
|
+
|
|
26
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
27
|
+
const __dirname = path.dirname(__filename);
|
|
28
|
+
const ROOT = path.resolve(__dirname, "..");
|
|
29
|
+
const DIST = path.join(ROOT, "dist");
|
|
30
|
+
|
|
31
|
+
const isWindows = process.platform === "win32";
|
|
32
|
+
const isMac = process.platform === "darwin";
|
|
33
|
+
const binaryName = isWindows ? "kj.exe" : "kj";
|
|
34
|
+
const binaryPath = path.join(DIST, binaryName);
|
|
35
|
+
|
|
36
|
+
function run(cmd, opts = {}) {
|
|
37
|
+
console.log(` $ ${cmd}`);
|
|
38
|
+
execSync(cmd, { cwd: ROOT, stdio: "inherit", ...opts });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function formatBytes(bytes) {
|
|
42
|
+
const mb = bytes / (1024 * 1024);
|
|
43
|
+
return `${mb.toFixed(1)} MB`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function main() {
|
|
47
|
+
try {
|
|
48
|
+
console.log("\n=== Karajan Code — SEA Build ===\n");
|
|
49
|
+
|
|
50
|
+
// ── Step 0: Prepare dist directory ──────────────────────────────
|
|
51
|
+
mkdirSync(DIST, { recursive: true });
|
|
52
|
+
|
|
53
|
+
// ── Step 1: Bundle with esbuild ─────────────────────────────────
|
|
54
|
+
console.log("[1/5] Bundling with esbuild...");
|
|
55
|
+
const { seaBuildOptions } = await import("./esbuild-sea.config.mjs");
|
|
56
|
+
const esbuild = await import("esbuild");
|
|
57
|
+
await esbuild.build(seaBuildOptions);
|
|
58
|
+
console.log(" -> dist/kj-bundle.cjs created.\n");
|
|
59
|
+
|
|
60
|
+
// ── Step 2: Generate SEA blob ───────────────────────────────────
|
|
61
|
+
console.log("[2/5] Generating SEA preparation blob...");
|
|
62
|
+
// Templates travel as SEA assets (KJC-BUG-0104): the binary ships no
|
|
63
|
+
// templates/ directory, so kj init / role prompts / onboard resolved
|
|
64
|
+
// paths like $HOME/templates/skills and hit ENOENT. The runtime
|
|
65
|
+
// (src/utils/templates-root.js) reads the index asset and extracts
|
|
66
|
+
// the files to ~/.karajan/embedded-templates/<version>/ on first use.
|
|
67
|
+
const { collectTemplateFiles } = await import("./esbuild-sea.config.mjs");
|
|
68
|
+
const pkgVersion = JSON.parse(
|
|
69
|
+
readFileSync(path.join(ROOT, "package.json"), "utf8"),
|
|
70
|
+
).version;
|
|
71
|
+
const templateFiles = collectTemplateFiles();
|
|
72
|
+
writeFileSync(
|
|
73
|
+
path.join(DIST, "templates-index.json"),
|
|
74
|
+
JSON.stringify({ version: pkgVersion, files: templateFiles }),
|
|
75
|
+
);
|
|
76
|
+
// Absolute paths: node resolves asset paths against the CWD (not the
|
|
77
|
+
// config file), so anything relative is ambiguous.
|
|
78
|
+
const assets = { "templates-index.json": path.join(DIST, "templates-index.json") };
|
|
79
|
+
for (const rel of templateFiles) {
|
|
80
|
+
assets[`templates/${rel}`] = path.join(ROOT, "templates", rel);
|
|
81
|
+
}
|
|
82
|
+
console.log(` -> ${templateFiles.length} template assets embedded.`);
|
|
83
|
+
const seaConfig = {
|
|
84
|
+
main: "dist/kj-bundle.cjs",
|
|
85
|
+
output: "dist/sea-prep.blob",
|
|
86
|
+
disableExperimentalSEAWarning: true,
|
|
87
|
+
assets,
|
|
88
|
+
};
|
|
89
|
+
writeFileSync(
|
|
90
|
+
path.join(DIST, "sea-config.json"),
|
|
91
|
+
JSON.stringify(seaConfig, null, 2),
|
|
92
|
+
);
|
|
93
|
+
run("node --experimental-sea-config dist/sea-config.json");
|
|
94
|
+
console.log(" -> dist/sea-prep.blob created.\n");
|
|
95
|
+
|
|
96
|
+
// ── Step 3: Copy node binary ────────────────────────────────────
|
|
97
|
+
console.log("[3/5] Copying node binary...");
|
|
98
|
+
cpSync(process.execPath, binaryPath);
|
|
99
|
+
console.log(` -> ${binaryName} copied.\n`);
|
|
100
|
+
|
|
101
|
+
// ── Step 3.5: Remove signature on macOS (required before postject)
|
|
102
|
+
if (isMac) {
|
|
103
|
+
console.log("[3.5] Removing macOS code signature...");
|
|
104
|
+
run(`codesign --remove-signature ${binaryPath}`);
|
|
105
|
+
console.log(" -> Signature removed.\n");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ── Step 4: Inject blob with postject ───────────────────────────
|
|
109
|
+
console.log("[4/5] Injecting SEA blob with postject...");
|
|
110
|
+
// On macOS the blob MUST land as section NODE_SEA_BLOB inside a segment
|
|
111
|
+
// named NODE_SEA. Without --macho-segment-name postject writes it to a
|
|
112
|
+
// default segment the SEA loader never inspects, so the binary segfaults
|
|
113
|
+
// on startup (KJC-BUG-0096, nodejs/postject#76). The flag is Mach-O only —
|
|
114
|
+
// ELF (Linux) and PE (Windows) don't take it — so add it only on macOS.
|
|
115
|
+
const postjectCmd = [
|
|
116
|
+
`npx postject ${binaryPath}`,
|
|
117
|
+
"NODE_SEA_BLOB dist/sea-prep.blob",
|
|
118
|
+
"--sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2",
|
|
119
|
+
...(isMac ? ["--macho-segment-name NODE_SEA"] : []),
|
|
120
|
+
].join(" ");
|
|
121
|
+
run(postjectCmd);
|
|
122
|
+
console.log(" -> Blob injected.\n");
|
|
123
|
+
|
|
124
|
+
// ── Step 4.5: Re-sign on macOS ──────────────────────────────────
|
|
125
|
+
if (isMac) {
|
|
126
|
+
console.log("[4.5] Re-signing binary (ad-hoc)...");
|
|
127
|
+
run(`codesign --sign - ${binaryPath}`);
|
|
128
|
+
console.log(" -> Binary signed.\n");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ── Done ────────────────────────────────────────────────────────
|
|
132
|
+
const stat = statSync(binaryPath);
|
|
133
|
+
console.log("[5/5] Done!");
|
|
134
|
+
console.log(` Binary: ${binaryPath}`);
|
|
135
|
+
console.log(` Size: ${formatBytes(stat.size)}`);
|
|
136
|
+
console.log("");
|
|
137
|
+
} catch (err) {
|
|
138
|
+
console.error("\n=== SEA Build FAILED ===\n");
|
|
139
|
+
console.error(err.message || err);
|
|
140
|
+
console.error(
|
|
141
|
+
"\nNote: esbuild may fail to bundle certain ESM-only dependencies",
|
|
142
|
+
);
|
|
143
|
+
console.error(
|
|
144
|
+
"or native modules. Check the error above for details.\n",
|
|
145
|
+
);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
main();
|