karajan-code 4.70.4 → 5.0.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/README.md +2 -2
- package/package.json +6 -14
- package/packages/hu-board/package.json +1 -1
- package/packages/hu-board/public/app.js +4 -18
- package/packages/hu-board/public/index.html +5 -2
- package/packages/hu-board/public/styles.css +26 -0
- package/packages/hu-board/public/utils/api.js +0 -74
- package/packages/hu-board/public/utils/board-tour.js +62 -0
- package/packages/hu-board/public/utils/board-view.js +49 -144
- package/packages/hu-board/public/utils/command-launcher.js +8 -70
- package/packages/hu-board/public/utils/formatters.js +10 -0
- package/packages/hu-board/public/utils/governance-view.js +13 -1
- package/packages/hu-board/public/utils/hu-actions.js +17 -68
- package/packages/hu-board/public/utils/init-listeners.js +7 -8
- package/packages/hu-board/public/utils/log-panel.js +3 -16
- package/packages/hu-board/public/utils/maggle-mode.js +2 -12
- package/packages/hu-board/public/utils/modals.js +7 -0
- package/packages/hu-board/public/utils/project-picker-view.js +1 -1
- package/packages/hu-board/public/utils/server-push.js +3 -22
- package/packages/hu-board/public/utils/shared-board-notice.js +33 -0
- package/packages/hu-board/public/utils/story-detail-view.js +5 -19
- package/packages/hu-board/src/cleanup-zombies.js +0 -1
- package/packages/hu-board/src/command-runner.js +36 -25
- package/packages/hu-board/src/db-connection.js +282 -0
- package/packages/hu-board/src/db-paths.js +113 -0
- package/packages/hu-board/src/db-queries.js +188 -0
- package/packages/hu-board/src/db-tombstones.js +51 -0
- package/packages/hu-board/src/db.js +28 -619
- package/packages/hu-board/src/ephemeral-cleaner.js +2 -3
- package/packages/hu-board/src/hu-zombie-reaper.js +5 -1
- package/packages/hu-board/src/plan-mutations.js +3 -153
- package/packages/hu-board/src/plans-service.js +55 -0
- package/packages/hu-board/src/routes/api/commands.js +122 -0
- package/packages/hu-board/src/routes/api/plans.js +55 -0
- package/packages/hu-board/src/routes/api/projects.js +267 -0
- package/packages/hu-board/src/routes/api/prompts.js +135 -0
- package/packages/hu-board/src/routes/api/sessions.js +95 -0
- package/packages/hu-board/src/routes/api/stories.js +183 -0
- package/packages/hu-board/src/routes/api/system.js +241 -0
- package/packages/hu-board/src/routes/api/tombstones.js +38 -0
- package/packages/hu-board/src/routes/api.js +24 -1371
- package/packages/hu-board/src/routes/governance.js +23 -1
- package/packages/hu-board/src/routes/lanes.js +40 -0
- package/packages/hu-board/src/routes/rag.js +4 -3
- package/packages/hu-board/src/server.js +20 -34
- package/packages/hu-board/src/sync-files.js +318 -0
- package/packages/hu-board/src/sync-project.js +99 -0
- package/packages/hu-board/src/sync.js +22 -566
- package/packages/hu-board/src/zombie-reaper.js +1 -1
- package/scripts/dual-publish.mjs +19 -3
- package/scripts/postinstall.js +15 -2
- package/scripts/verify-pack-mcp.mjs +11 -7
- package/scripts/verify-pack.mjs +4 -4
- package/src/audit/audit-history.js +3 -2
- package/src/audit/dead-exports.js +68 -59
- package/src/audit/deterministic-summary.js +33 -13
- package/src/audit/npm-signatures.js +41 -0
- package/src/audit/sbom.js +35 -0
- package/src/checks/agent-token.js +112 -0
- package/src/checks/dir-setup.js +34 -1
- package/src/checks/native-build.js +21 -39
- package/src/checks/runner.js +128 -142
- package/src/cli/advanced-commands.js +6 -6
- package/src/cli/register-meta.js +42 -82
- package/src/cli/register-pipeline.js +8 -143
- package/src/cli/register-plan.js +9 -100
- package/src/cli/retired-commands.js +49 -0
- package/src/cli.js +18 -5
- package/src/commands/agent-run.js +40 -26
- package/src/commands/audit.js +127 -124
- package/src/commands/board.js +5 -9
- package/src/commands/check.js +29 -16
- package/src/commands/doctor.js +2 -0
- package/src/commands/env.js +176 -150
- package/src/commands/go.js +71 -53
- package/src/commands/harden.js +90 -81
- package/src/commands/hu-start.js +52 -0
- package/src/commands/hu.js +288 -221
- package/src/commands/identity.js +111 -78
- package/src/commands/init-atril.js +47 -0
- package/src/commands/init.js +11 -40
- package/src/commands/install-tools.js +70 -85
- package/src/commands/main-guard.js +78 -0
- package/src/commands/policy.js +226 -184
- package/src/commands/qmd.js +27 -21
- package/src/commands/rag.js +75 -48
- package/src/commands/review-gate.js +421 -341
- package/src/commands/start.js +4 -5
- package/src/commands/steward.js +96 -47
- package/src/commands/tournament.js +46 -37
- package/src/commands/worktree.js +94 -25
- package/src/config/loader.js +34 -17
- package/src/config/secrets.js +36 -0
- package/src/config.js +1 -0
- package/src/environment/adr.js +16 -2
- package/src/git/karajan-label.js +20 -8
- package/src/guards/main-guard.js +54 -0
- package/src/harden/hook-templates.js +16 -1
- package/src/harden/sentinel/posttooluse.mjs +18 -6
- package/src/harden/sentinel/pretooluse-sentinel.mjs +64 -36
- package/src/harden/sentinel/sentinel-lib.mjs +129 -26
- package/src/harden/sentinel/sentinel-shell.mjs +5 -16
- package/src/harden/sentinel/sessionstart.mjs +3 -2
- package/src/harden/sentinel/stop.mjs +5 -4
- package/src/harden/workflow-templates.js +1 -1
- package/src/hu/board-lookup.js +29 -0
- package/src/hu/gh-identity.js +23 -0
- package/src/hu/github-board.js +4 -2
- package/src/hu/lane-prune.js +15 -7
- package/src/hu/lane-setup.js +65 -0
- package/src/hu/lanes.js +66 -0
- package/src/hu/team-board-mirror.js +3 -2
- package/src/infrastructure/command-runner.js +1 -1
- package/src/infrastructure/file-system-service.js +1 -1
- package/src/mcp/handlers/direct-handlers.js +2 -292
- package/src/mcp/handlers/hu-handlers.js +1 -35
- package/src/mcp/handlers/management-handlers.js +2 -42
- package/src/mcp/server-handlers.js +16 -22
- package/src/mcp/tools.js +1 -196
- package/src/plan/lane-plans.js +54 -0
- package/src/plan/plan-store.js +7 -4
- package/src/policy/add.js +26 -12
- package/src/policy/adr-number-check.js +31 -0
- package/src/prompts/audit.js +299 -339
- package/src/prompts/card-context.js +10 -5
- package/src/prompts/reviewer.js +5 -2
- package/src/rag/auto-update.js +56 -43
- package/src/rag/bootstrap-index.js +28 -54
- package/src/rag/migrate.js +1 -1
- package/src/rag/onnx-fallback.js +2 -2
- package/src/rag/pid-lease.js +70 -0
- package/src/rag/project-store.js +2 -12
- package/src/review/card-first.js +57 -46
- package/src/review/gate-gitignore.js +23 -4
- package/src/review/gate-hook.js +12 -4
- package/src/review/one-shot-review.js +45 -35
- package/src/review/publish-pr.js +14 -2
- package/src/review/repo-docs.js +73 -0
- package/src/review/repo-stack.js +59 -0
- package/src/review/reviewer-fallback.js +19 -4
- package/src/review/seal-commit.js +29 -0
- package/src/review/sonar-pregate.js +59 -44
- package/src/review/third-party-review.js +90 -16
- package/src/review/third-party-size.js +43 -0
- package/src/review/third-party-skills.js +54 -0
- package/src/review/verify-pr.js +1 -24
- package/src/roles/audit-role.js +106 -125
- package/src/skills/atril-skills.js +5 -4
- package/src/sonar/scanner.js +116 -101
- package/src/steward/invariants.js +5 -4
- package/src/steward/phantom-sweep.js +66 -0
- package/src/tournament/judge.js +60 -53
- package/src/utils/gh-account.js +37 -0
- package/src/utils/git.js +0 -13
- package/src/utils/home-migration.js +7 -1
- package/src/utils/main-checkout.js +53 -0
- package/src/utils/qmd-collection.js +3 -1
- package/src/utils/welcome.js +3 -3
- package/templates/{skills → atril/commands}/kj-architect.md +5 -0
- package/templates/{skills → atril/commands}/kj-audit.md +5 -0
- package/templates/{skills → atril/commands}/kj-board.md +5 -0
- package/templates/{skills → atril/commands}/kj-code.md +5 -0
- package/templates/{skills → atril/commands}/kj-csv-transform.md +5 -0
- package/templates/{skills → atril/commands}/kj-data-report.md +5 -0
- package/templates/{skills → atril/commands}/kj-discover.md +5 -0
- package/templates/{skills → atril/commands}/kj-rag-query.md +5 -0
- package/templates/{skills → atril/commands}/kj-review.md +5 -0
- package/templates/{skills → atril/commands}/kj-security.md +5 -0
- package/templates/{skills → atril/commands}/kj-sonar.md +5 -0
- package/templates/{skills → atril/commands}/kj-sql-analysis.md +5 -0
- package/templates/{skills → atril/commands}/kj-test.md +5 -0
- package/packages/hu-board/public/utils/run-launcher.js +0 -169
- package/packages/hu-board/src/run-tracker.js +0 -121
- package/src/activity-log.js +0 -59
- package/src/agents/host-agent.js +0 -38
- package/src/agents/model-registry.js +0 -142
- package/src/audit/plan-adherence.js +0 -224
- package/src/autonomy/arbiter-role.js +0 -71
- package/src/autonomy/arbiter.js +0 -22
- package/src/autonomy/decider.js +0 -18
- package/src/autonomy/decision-resolver.js +0 -58
- package/src/autonomy/outcome-report.js +0 -55
- package/src/autonomy/stage-ask.js +0 -30
- package/src/brain/decision-tracker.js +0 -125
- package/src/brain/decisor.js +0 -243
- package/src/brain/solomon-consult.js +0 -171
- package/src/brain/standby-scheduler.js +0 -8
- package/src/budget/comparison.js +0 -96
- package/src/canvas/parse-md.js +0 -238
- package/src/canvas/schema.js +0 -101
- package/src/canvas/to-flat.js +0 -85
- package/src/canvas/validate.js +0 -163
- package/src/ci/dispatch.js +0 -99
- package/src/ci/index.js +0 -3
- package/src/ci/pr-diff.js +0 -26
- package/src/cli/register-standby.js +0 -26
- package/src/commands/architect.js +0 -111
- package/src/commands/autorun.js +0 -49
- package/src/commands/researcher.js +0 -54
- package/src/commands/resume.js +0 -72
- package/src/commands/review.js +0 -89
- package/src/commands/run.js +0 -185
- package/src/commands/standby.js +0 -117
- package/src/commands/status.js +0 -36
- package/src/commands/triage.js +0 -78
- package/src/commands/undo.js +0 -62
- package/src/domains/domain-loader.js +0 -245
- package/src/domains/domain-registry.js +0 -140
- package/src/domains/domain-synthesizer.js +0 -129
- package/src/git/automation.js +0 -433
- package/src/git/coder-commit-guard.js +0 -40
- package/src/git/hu-automation.js +0 -243
- package/src/git/hu-snapshot.js +0 -10
- package/src/git/symlink-targets.js +0 -57
- package/src/golden/asserter.js +0 -92
- package/src/golden/loader.js +0 -52
- package/src/golden/runner.js +0 -92
- package/src/golden/schema.js +0 -41
- package/src/guards/duplicate-members.js +0 -86
- package/src/guards/intent-guard.js +0 -161
- package/src/guards/perf-guard.js +0 -126
- package/src/guards/policy-guard.js +0 -90
- package/src/guards/policy-resolver.js +0 -85
- package/src/guards/secret-redactor.js +0 -71
- package/src/hu/acceptance-runner.js +0 -138
- package/src/hu/lazy-planner.js +0 -109
- package/src/hu/parallel-executor.js +0 -94
- package/src/hu/routing-metrics.js +0 -17
- package/src/hu/split-validator.js +0 -143
- package/src/hu/splitting-detector.js +0 -158
- package/src/hu/splitting-generator.js +0 -175
- package/src/hu/store.js +0 -394
- package/src/infrastructure/mocks.js +0 -182
- package/src/mcp/handlers/run-handler.js +0 -318
- package/src/mcp/sovereignty-guard.js +0 -113
- package/src/mcp/tool-arg-normalizers.js +0 -16
- package/src/mutate/reviewer-signal.js +0 -76
- package/src/orchestrator/agent-fallback.js +0 -81
- package/src/orchestrator/brain-coordinator.js +0 -186
- package/src/orchestrator/ci-integration.js +0 -139
- package/src/orchestrator/config-init.js +0 -426
- package/src/orchestrator/direct-actions.js +0 -217
- package/src/orchestrator/drivers/error-recovery.js +0 -287
- package/src/orchestrator/drivers/init-context.js +0 -238
- package/src/orchestrator/drivers/iteration-loop.js +0 -382
- package/src/orchestrator/drivers/iteration-phases/coder-and-refactorer.js +0 -69
- package/src/orchestrator/drivers/iteration-phases/guards.js +0 -91
- package/src/orchestrator/drivers/iteration-phases/handle-approved.js +0 -29
- package/src/orchestrator/drivers/iteration-phases/quality-gates.js +0 -125
- package/src/orchestrator/drivers/iteration-phases/reviewer-gate.js +0 -62
- package/src/orchestrator/drivers/post-loop.js +0 -404
- package/src/orchestrator/drivers/pre-loop-phases/auto-hu-batch.js +0 -137
- package/src/orchestrator/drivers/pre-loop-phases/config-deprecations.js +0 -47
- package/src/orchestrator/drivers/pre-loop-phases/ensure-addyosmani-skills.js +0 -84
- package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +0 -393
- package/src/orchestrator/drivers/pre-loop.js +0 -524
- package/src/orchestrator/drivers/run-hu-batch.js +0 -468
- package/src/orchestrator/feedback-enrichment.js +0 -140
- package/src/orchestrator/feedback-queue.js +0 -151
- package/src/orchestrator/flow-control.js +0 -170
- package/src/orchestrator/flow-runner.js +0 -365
- package/src/orchestrator/fs-leak-detector.js +0 -228
- package/src/orchestrator/hu-scheduler.js +0 -114
- package/src/orchestrator/hu-sub-pipeline.js +0 -642
- package/src/orchestrator/iteration-gate.js +0 -73
- package/src/orchestrator/iteration-stages.js +0 -9
- package/src/orchestrator/parallel-limiter.js +0 -74
- package/src/orchestrator/pipeline-context.js +0 -72
- package/src/orchestrator/post-loop-stages.js +0 -468
- package/src/orchestrator/pre-loop-stages.js +0 -11
- package/src/orchestrator/preflight-checks.js +0 -487
- package/src/orchestrator/repair/repair-detector.js +0 -117
- package/src/orchestrator/repair/repair-runner.js +0 -142
- package/src/orchestrator/reviewer-fallback.js +0 -45
- package/src/orchestrator/role-output-compressor.js +0 -189
- package/src/orchestrator/session-journal.js +0 -308
- package/src/orchestrator/solomon-escalation.js +0 -331
- package/src/orchestrator/solomon-rules.js +0 -212
- package/src/orchestrator/stages/acceptance-coverage-stage.js +0 -47
- package/src/orchestrator/stages/architect-stage.js +0 -172
- package/src/orchestrator/stages/coder-stage.js +0 -506
- package/src/orchestrator/stages/domain-curator-stage.js +0 -82
- package/src/orchestrator/stages/format-stage.js +0 -61
- package/src/orchestrator/stages/hu-reviewer-phases/detect-and-apply-splits.js +0 -127
- package/src/orchestrator/stages/hu-reviewer-stage.js +0 -419
- package/src/orchestrator/stages/perf-stage.js +0 -140
- package/src/orchestrator/stages/planner-stage.js +0 -85
- package/src/orchestrator/stages/rag-context-stage.js +0 -80
- package/src/orchestrator/stages/rag-preload-decisor.js +0 -58
- package/src/orchestrator/stages/research-stage.js +0 -69
- package/src/orchestrator/stages/reviewer-stage.js +0 -431
- package/src/orchestrator/stages/sonar-stage.js +0 -439
- package/src/orchestrator/stages/stage-classes.js +0 -84
- package/src/orchestrator/stages/stage-executor.js +0 -155
- package/src/orchestrator/stages/suite-green-stage.js +0 -62
- package/src/orchestrator/stages/tdd-discipline-stage.js +0 -56
- package/src/orchestrator/stages/tool-judge-stage.js +0 -53
- package/src/orchestrator/stages/triage-stage.js +0 -172
- package/src/orchestrator/standby.js +0 -71
- package/src/orchestrator/verification-gate.js +0 -183
- package/src/orchestrator.js +0 -22
- package/src/plan/adr-generator.js +0 -170
- package/src/plan/adr-loader.js +0 -60
- package/src/plan/plan-executor.js +0 -100
- package/src/plan/plan-validation.js +0 -4
- package/src/prompts/acceptance-coverage.js +0 -32
- package/src/prompts/arbiter.js +0 -38
- package/src/prompts/architect.js +0 -103
- package/src/prompts/hu-reviewer.js +0 -244
- package/src/prompts/tool-correctness-judge.js +0 -31
- package/src/prompts/triage.js +0 -57
- package/src/rag/embedder.js +0 -2
- package/src/rag/embedders/_cloud-base.js +0 -2
- package/src/rag/embedders/cohere.js +0 -2
- package/src/rag/embedders/mistral.js +0 -2
- package/src/rag/embedders/onnx.js +0 -2
- package/src/rag/embedders/openai.js +0 -2
- package/src/rag/embedders/voyage.js +0 -2
- package/src/rag/rerank.js +0 -4
- package/src/rag/where-parser.js +0 -4
- package/src/repeat-detector.js +0 -88
- package/src/review/diff-generator.js +0 -112
- package/src/review/git-stash-helper.js +0 -104
- package/src/review/schema.js +0 -31
- package/src/review/scope-filter.js +0 -152
- package/src/review/snapshot-diff.js +0 -122
- package/src/review/tdd-discipline.js +0 -90
- package/src/review/tdd-policy.js +0 -69
- package/src/roles/acceptance-coverage-role.js +0 -78
- package/src/roles/architect-role.js +0 -77
- package/src/roles/coder-role.js +0 -113
- package/src/roles/commiter-role.js +0 -113
- package/src/roles/domain-curator-role.js +0 -134
- package/src/roles/hu-reviewer-role.js +0 -64
- package/src/roles/impeccable-role.js +0 -85
- package/src/roles/index.js +0 -18
- package/src/roles/karajan-brain-role.js +0 -134
- package/src/roles/perf-role.js +0 -122
- package/src/roles/planner-role.js +0 -151
- package/src/roles/refactorer-role.js +0 -30
- package/src/roles/repairer-role.js +0 -181
- package/src/roles/researcher-role.js +0 -70
- package/src/roles/reviewer-role.js +0 -115
- package/src/roles/security-role.js +0 -73
- package/src/roles/solomon-role.js +0 -271
- package/src/roles/sonar-role.js +0 -72
- package/src/roles/tester-role.js +0 -235
- package/src/roles/tool-correctness-judge-role.js +0 -78
- package/src/roles/triage-role.js +0 -99
- package/src/session/journal/decisions-writer.js +0 -200
- package/src/session/journal/iteration-logger.js +0 -194
- package/src/session/journal/summary-writer.js +0 -320
- package/src/session/journal/tree-writer.js +0 -164
- package/src/session/journal/write-all.js +0 -89
- package/src/session-cleanup.js +0 -116
- package/src/skills/semantic-detector.js +0 -167
- package/src/skills/skill-detector.js +0 -385
- package/src/sonar/cloud-scanner.js +0 -78
- package/src/tracker-bootstrap.js +0 -44
- package/src/types/index.js +0 -38
- package/src/types/policy.js +0 -43
- package/src/types/solomon.js +0 -87
- package/src/utils/async-lock.js +0 -31
- package/src/utils/budget.js +0 -229
- package/src/utils/cwd-confirm.js +0 -39
- package/src/utils/display/event-handlers.js +0 -403
- package/src/utils/display/formatters.js +0 -132
- package/src/utils/display/header.js +0 -47
- package/src/utils/display/session-summary.js +0 -118
- package/src/utils/display/solomon.js +0 -42
- package/src/utils/pricing.js +0 -29
- package/src/utils/quota-simulator.js +0 -56
- package/src/utils/role-assigner.js +0 -137
- package/src/utils/rtk-wrapper.js +0 -87
- package/src/utils/run-registry.js +0 -9
- package/src/utils/status-dashboard.js +0 -254
- package/src/utils/tool-call-extractor.js +0 -56
- package/src/webperf/devtools-detect.js +0 -112
- package/src/webperf/webperf-role-integration.js +0 -66
- package/templates/skills/kj-run.md +0 -73
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Native
|
|
2
|
+
* Native module check (KJC-BUG-0092, KJC-TSK-1353).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* WARN in `kj doctor` with the exact remedy instead of a cryptic runtime crash.
|
|
4
|
+
* node-pty is kj's only native module since better-sqlite3 left (KJC-TSK-1353:
|
|
5
|
+
* every SQLite store runs on node:sqlite, built into Node). It is optional: it
|
|
6
|
+
* powers the HU Board's terminal. Where it needs its install script, pnpm
|
|
7
|
+
* (ERR_PNPM_IGNORED_BUILDS) and npm 12 (allowScripts) block it by default, so
|
|
8
|
+
* `kj doctor` warns with the exact remedy instead of a broken terminal.
|
|
10
9
|
*/
|
|
11
10
|
|
|
12
11
|
import { execFileSync } from "node:child_process";
|
|
@@ -32,19 +31,7 @@ function defaultNpmMajor() {
|
|
|
32
31
|
* allowScripts covers it; reinstalling blocks it again. Approve, then rebuild, in kj's dir.
|
|
33
32
|
*/
|
|
34
33
|
export const npmAllowScriptsRemedy = (pkgDir = OWN_PKG_DIR) =>
|
|
35
|
-
`cd "${pkgDir}" && npm install-scripts approve
|
|
36
|
-
|
|
37
|
-
// The addon is installed but its native build is missing (a missing package needs a reinstall, not this).
|
|
38
|
-
const UNBUILT_ADDON = /bindings file|better_sqlite3\.node/;
|
|
39
|
-
|
|
40
|
-
/** A missing better-sqlite3 addon becomes an error that says how to build it; any other error passes as it is. */
|
|
41
|
-
export function explainNativeLoadError(err, { npmMajor = defaultNpmMajor, pkgDir = OWN_PKG_DIR } = {}) {
|
|
42
|
-
if (!UNBUILT_ADDON.test(String(err?.message ?? err))) return err;
|
|
43
|
-
const remedy = (npmMajor() ?? 0) >= 12
|
|
44
|
-
? `npm 12 skipped its install script (allowScripts); build it with: ${npmAllowScriptsRemedy(pkgDir)}`
|
|
45
|
-
: "run kj doctor for the exact fix on this install";
|
|
46
|
-
return new Error(`better-sqlite3 native addon is not built, so the RAG cannot open its index — ${remedy}`, { cause: err });
|
|
47
|
-
}
|
|
34
|
+
`cd "${pkgDir}" && npm install-scripts approve node-pty && npm rebuild node-pty`;
|
|
48
35
|
|
|
49
36
|
/** pnpm lays packages out under a `.pnpm` virtual store and symlinks them in. */
|
|
50
37
|
function isPnpmInstall() {
|
|
@@ -54,50 +41,45 @@ function isPnpmInstall() {
|
|
|
54
41
|
);
|
|
55
42
|
}
|
|
56
43
|
|
|
57
|
-
/**
|
|
58
|
-
async function
|
|
59
|
-
|
|
60
|
-
new Database(":memory:").close();
|
|
44
|
+
/** Load node-pty's native binding. */
|
|
45
|
+
async function defaultLoadPty() {
|
|
46
|
+
await import("node-pty");
|
|
61
47
|
}
|
|
62
48
|
|
|
63
|
-
|
|
49
|
+
const NO_TERMINAL = "the HU Board terminal is unavailable (everything else works)";
|
|
50
|
+
|
|
51
|
+
export function createNativeBuildCheck({ loadPty = defaultLoadPty, isPnpm = isPnpmInstall, npmMajor = defaultNpmMajor, pkgDir = OWN_PKG_DIR } = {}) {
|
|
64
52
|
return {
|
|
65
53
|
name: "native-build",
|
|
66
|
-
label: "native modules (
|
|
54
|
+
label: "native modules (node-pty)",
|
|
67
55
|
strategy: STRATEGY.MANUAL,
|
|
68
56
|
async detect() {
|
|
69
57
|
try {
|
|
70
|
-
await
|
|
71
|
-
return {
|
|
72
|
-
ok: true,
|
|
73
|
-
severity: "info",
|
|
74
|
-
detail: "better-sqlite3 native addon loads — DB-backed commands available",
|
|
75
|
-
};
|
|
58
|
+
await loadPty();
|
|
59
|
+
return { ok: true, severity: "info", detail: "node-pty loads — the HU Board terminal is available" };
|
|
76
60
|
} catch (err) {
|
|
77
61
|
const firstLine = String(err?.message || err).split("\n")[0];
|
|
78
62
|
if (isPnpm()) {
|
|
79
63
|
return {
|
|
80
64
|
ok: false,
|
|
81
65
|
severity: "warn",
|
|
82
|
-
detail:
|
|
83
|
-
fix: "Approve the build then reinstall: `pnpm approve-builds
|
|
66
|
+
detail: `node-pty native build was skipped — pnpm blocks dependency build scripts by default; ${NO_TERMINAL}`,
|
|
67
|
+
fix: "Approve the build then reinstall: `pnpm approve-builds node-pty` — or install with npm: `npm i -g @karajan-family/code`",
|
|
84
68
|
};
|
|
85
69
|
}
|
|
86
70
|
if ((npmMajor() ?? 0) >= 12) {
|
|
87
71
|
return {
|
|
88
72
|
ok: false,
|
|
89
73
|
severity: "warn",
|
|
90
|
-
detail: `
|
|
74
|
+
detail: `node-pty failed to load: ${firstLine} — npm 12 skips dependency install scripts that allowScripts does not cover; ${NO_TERMINAL}`,
|
|
91
75
|
fix: npmAllowScriptsRemedy(pkgDir),
|
|
92
76
|
};
|
|
93
77
|
}
|
|
94
78
|
return {
|
|
95
79
|
ok: false,
|
|
96
80
|
severity: "warn",
|
|
97
|
-
// KJC-BUG-0118: the standalone (curl) binary ships without native
|
|
98
|
-
|
|
99
|
-
// npm install) instead of implying the install is broken.
|
|
100
|
-
detail: `better-sqlite3 failed to load: ${firstLine} — DB-backed features (RAG, board, MCP) unavailable; the standalone binary does not bundle native modules`,
|
|
81
|
+
// KJC-BUG-0118: the standalone (curl) binary ships without native modules by design.
|
|
82
|
+
detail: `node-pty failed to load: ${firstLine} — ${NO_TERMINAL}; the standalone binary does not bundle native modules`,
|
|
101
83
|
fix: "Reinstall to rebuild native modules: `npm i -g @karajan-family/code`",
|
|
102
84
|
};
|
|
103
85
|
}
|
package/src/checks/runner.js
CHANGED
|
@@ -51,162 +51,25 @@ export async function runChecks(checks, ctx, options = {}) {
|
|
|
51
51
|
|
|
52
52
|
const { config } = ctx;
|
|
53
53
|
const startTotal = Date.now();
|
|
54
|
-
const entries =
|
|
54
|
+
const entries = checks.map((check) => entryFor(check, config));
|
|
55
55
|
const overrides = {};
|
|
56
56
|
|
|
57
|
-
// Build applicable entries (skipped checks noted up front).
|
|
58
|
-
// A check.applies(config) may return:
|
|
59
|
-
// - true / false — boolean form (back-compat).
|
|
60
|
-
// - an object { applies: boolean, reason?: string } — when false, `reason`
|
|
61
|
-
// is shown verbatim so users see WHY (e.g. "sonarqube.enabled is false
|
|
62
|
-
// in kj.config.yml") instead of the opaque "Not applicable" message
|
|
63
|
-
// that confused users debugging their config.
|
|
64
|
-
for (const check of checks) {
|
|
65
|
-
if (typeof check.applies !== "function") {
|
|
66
|
-
entries.push({ check, status: null, detail: null, runMs: 0, detectResult: null, remediated: false });
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
const verdict = check.applies(config);
|
|
70
|
-
const isObj = verdict && typeof verdict === "object" && "applies" in verdict;
|
|
71
|
-
const applies = isObj ? Boolean(verdict.applies) : Boolean(verdict);
|
|
72
|
-
if (applies) {
|
|
73
|
-
entries.push({ check, status: null, detail: null, runMs: 0, detectResult: null, remediated: false });
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
const reason = (isObj && typeof verdict.reason === "string" && verdict.reason.trim())
|
|
77
|
-
? verdict.reason.trim()
|
|
78
|
-
: "Not applicable for current configuration";
|
|
79
|
-
entries.push({
|
|
80
|
-
check,
|
|
81
|
-
status: STATUS.SKIPPED,
|
|
82
|
-
detail: reason,
|
|
83
|
-
runMs: 0,
|
|
84
|
-
detectResult: null,
|
|
85
|
-
remediated: false,
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
57
|
// Phase 1: detect in parallel with timeout.
|
|
90
58
|
const detectBase = Date.now();
|
|
91
|
-
await Promise.all(
|
|
92
|
-
entries.map(async (entry) => {
|
|
93
|
-
if (entry.status === STATUS.SKIPPED) return;
|
|
94
|
-
const start = Date.now();
|
|
95
|
-
// Per-check override: checks that spawn slow tools (e.g. `npx <pkg>` cold
|
|
96
|
-
// start) declare detectTimeoutMs so the blanket pipeline timeout doesn't
|
|
97
|
-
// kill them. Falls back to the pipeline-wide timeoutMs.
|
|
98
|
-
const detectTimeoutMs = Number.isFinite(entry.check.detectTimeoutMs)
|
|
99
|
-
? entry.check.detectTimeoutMs
|
|
100
|
-
: timeoutMs;
|
|
101
|
-
try {
|
|
102
|
-
const result = await withTimeout(entry.check.detect({ config, signal }), detectTimeoutMs, entry.check.name);
|
|
103
|
-
entry.detectResult = result;
|
|
104
|
-
entry.runMs = Date.now() - start;
|
|
105
|
-
entry.status = resolveStatusFromDetect(entry.check, result);
|
|
106
|
-
entry.detail = result.detail;
|
|
107
|
-
// KJC-BUG-0049: si el check es degradable y falló (status FAIL/WARN tras
|
|
108
|
-
// resolveStatusFromDetect), aplicar disables al overrides y degradar a WARN.
|
|
109
|
-
// Así el preflight no aborta — la feature opcional se desactiva con aviso.
|
|
110
|
-
if (!result.ok && entry.check.degradable && (entry.status === STATUS.FAIL || entry.status === STATUS.WARN)) {
|
|
111
|
-
for (const dotPath of entry.check.degradable.disables || []) {
|
|
112
|
-
setDotPath(overrides, dotPath, false);
|
|
113
|
-
}
|
|
114
|
-
entry.status = STATUS.WARN;
|
|
115
|
-
entry.detail = `${result.detail} → ${entry.check.degradable.warn}`;
|
|
116
|
-
entry.degraded = true;
|
|
117
|
-
}
|
|
118
|
-
} catch (err) {
|
|
119
|
-
entry.runMs = Date.now() - start;
|
|
120
|
-
if (err instanceof TimeoutError) {
|
|
121
|
-
entry.detectResult = { ok: false, severity: "warn", detail: `Timeout after ${detectTimeoutMs}ms` };
|
|
122
|
-
// A timeout on a degradable (optional) check must not block the
|
|
123
|
-
// pipeline: degrade to WARN and disable its feature flags, same as
|
|
124
|
-
// a regular failure (KJC-BUG-0049 semantics).
|
|
125
|
-
if (entry.check.degradable) {
|
|
126
|
-
for (const dotPath of entry.check.degradable.disables || []) {
|
|
127
|
-
setDotPath(overrides, dotPath, false);
|
|
128
|
-
}
|
|
129
|
-
entry.status = STATUS.WARN;
|
|
130
|
-
entry.detail = `${entry.detectResult.detail} → ${entry.check.degradable.warn}`;
|
|
131
|
-
entry.degraded = true;
|
|
132
|
-
} else {
|
|
133
|
-
entry.status = STATUS.TIMEOUT;
|
|
134
|
-
entry.detail = entry.detectResult.detail;
|
|
135
|
-
}
|
|
136
|
-
} else {
|
|
137
|
-
entry.detectResult = { ok: false, severity: "fail", detail: `Detection error: ${err.message}` };
|
|
138
|
-
entry.status = STATUS.FAIL;
|
|
139
|
-
entry.detail = entry.detectResult.detail;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
})
|
|
143
|
-
);
|
|
59
|
+
await Promise.all(entries.map((entry) => detectEntry(entry, { config, signal, timeoutMs, overrides })));
|
|
144
60
|
logger.info?.(`Detect phase done in ${Date.now() - detectBase}ms`);
|
|
145
61
|
|
|
146
|
-
// Phase 2: remediate sequentially (only in "fix" mode, only failing checks).
|
|
147
62
|
if (mode !== "check-only") {
|
|
63
|
+
// Phase 2: remediate sequentially (only in "fix" mode, only failing checks).
|
|
148
64
|
for (const entry of entries) {
|
|
149
|
-
if (
|
|
150
|
-
const { check, detectResult } = entry;
|
|
151
|
-
if (check.strategy === STRATEGY.PROMPT && !yes) {
|
|
152
|
-
const approved = await onConfirm(check.describe || `Fix: ${check.name}`);
|
|
153
|
-
if (!approved) {
|
|
154
|
-
logger.info?.(`User declined remediation for ${check.name}`);
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
const start = Date.now();
|
|
159
|
-
try {
|
|
160
|
-
const rem = await withTimeout(
|
|
161
|
-
check.remediate({ config, extra: detectResult.extra, signal, logger }),
|
|
162
|
-
remediateTimeoutMs,
|
|
163
|
-
`${check.name}:remediate`
|
|
164
|
-
);
|
|
165
|
-
entry.remediated = rem.fixed === true;
|
|
166
|
-
entry.runMs += Date.now() - start;
|
|
167
|
-
if (rem.fixed) {
|
|
168
|
-
if (rem.changes) mergeOverrides(overrides, rem.changes);
|
|
169
|
-
entry.detail = rem.detail;
|
|
170
|
-
entry.status = STATUS.FIXED;
|
|
171
|
-
} else {
|
|
172
|
-
entry.detail = rem.detail || entry.detail;
|
|
173
|
-
entry.status = STATUS.FAIL;
|
|
174
|
-
}
|
|
175
|
-
} catch (err) {
|
|
176
|
-
entry.runMs += Date.now() - start;
|
|
177
|
-
const msg = err instanceof TimeoutError ? `Remediation timeout after ${remediateTimeoutMs}ms` : `Remediation error: ${err.message}`;
|
|
178
|
-
entry.detail = msg;
|
|
179
|
-
entry.status = STATUS.FAIL;
|
|
180
|
-
logger.warn?.(`Remediation failed for ${check.name}: ${msg}`);
|
|
181
|
-
}
|
|
65
|
+
if (shouldAttemptRemediation(entry)) await remediateEntry(entry, { config, signal, logger, yes, onConfirm, remediateTimeoutMs, overrides });
|
|
182
66
|
}
|
|
183
|
-
|
|
184
67
|
// Phase 3: re-verify fixed checks using the config merged with any
|
|
185
68
|
// runtime overrides applied by remediations. Without this merge, a check
|
|
186
69
|
// that rebinds to a new port (override) would re-verify against the old
|
|
187
70
|
// config and wrongly report failure.
|
|
188
71
|
const verifyConfig = mergeConfigAndOverrides(config, overrides);
|
|
189
|
-
await Promise.all(
|
|
190
|
-
entries.filter((e) => e.remediated).map(async (entry) => {
|
|
191
|
-
const start = Date.now();
|
|
192
|
-
try {
|
|
193
|
-
const result = await withTimeout(
|
|
194
|
-
entry.check.detect({ config: verifyConfig, signal }),
|
|
195
|
-
Number.isFinite(entry.check.detectTimeoutMs) ? entry.check.detectTimeoutMs : timeoutMs,
|
|
196
|
-
`${entry.check.name}:reverify`
|
|
197
|
-
);
|
|
198
|
-
entry.runMs += Date.now() - start;
|
|
199
|
-
if (!result.ok) {
|
|
200
|
-
entry.status = STATUS.FAIL;
|
|
201
|
-
entry.detail = `${entry.detail} (but re-verify failed: ${result.detail})`;
|
|
202
|
-
}
|
|
203
|
-
} catch (err) {
|
|
204
|
-
entry.runMs += Date.now() - start;
|
|
205
|
-
entry.status = STATUS.FAIL;
|
|
206
|
-
entry.detail = `${entry.detail} (re-verify error: ${err.message})`;
|
|
207
|
-
}
|
|
208
|
-
})
|
|
209
|
-
);
|
|
72
|
+
await Promise.all(entries.filter((e) => e.remediated).map((entry) => reverifyEntry(entry, { verifyConfig, signal, timeoutMs })));
|
|
210
73
|
}
|
|
211
74
|
|
|
212
75
|
// Flatten into reports.
|
|
@@ -234,6 +97,129 @@ export async function runChecks(checks, ctx, options = {}) {
|
|
|
234
97
|
};
|
|
235
98
|
}
|
|
236
99
|
|
|
100
|
+
// Build an applicable entry (skipped checks noted up front).
|
|
101
|
+
// A check.applies(config) may return:
|
|
102
|
+
// - true / false — boolean form (back-compat).
|
|
103
|
+
// - an object { applies: boolean, reason?: string } — when false, `reason`
|
|
104
|
+
// is shown verbatim so users see WHY (e.g. "sonarqube.enabled is false
|
|
105
|
+
// in kj.config.yml") instead of the opaque "Not applicable" message
|
|
106
|
+
// that confused users debugging their config.
|
|
107
|
+
function entryFor(check, config) {
|
|
108
|
+
const entry = { check, status: null, detail: null, runMs: 0, detectResult: null, remediated: false };
|
|
109
|
+
if (typeof check.applies !== "function") return entry;
|
|
110
|
+
const verdict = check.applies(config);
|
|
111
|
+
const isObj = verdict && typeof verdict === "object" && "applies" in verdict;
|
|
112
|
+
const applies = isObj ? Boolean(verdict.applies) : Boolean(verdict);
|
|
113
|
+
if (applies) return entry;
|
|
114
|
+
const reason = (isObj && typeof verdict.reason === "string" && verdict.reason.trim())
|
|
115
|
+
? verdict.reason.trim()
|
|
116
|
+
: "Not applicable for current configuration";
|
|
117
|
+
return { ...entry, status: STATUS.SKIPPED, detail: reason };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Per-check override: checks that spawn slow tools (e.g. `npx <pkg>` cold
|
|
121
|
+
// start) declare detectTimeoutMs so the blanket pipeline timeout doesn't
|
|
122
|
+
// kill them. Falls back to the pipeline-wide timeoutMs.
|
|
123
|
+
const detectTimeoutOf = (check, timeoutMs) => (Number.isFinite(check.detectTimeoutMs) ? check.detectTimeoutMs : timeoutMs);
|
|
124
|
+
|
|
125
|
+
// KJC-BUG-0049: an optional (degradable) check that fails or times out does not
|
|
126
|
+
// block the pipeline: its feature flags are disabled and it degrades to WARN.
|
|
127
|
+
function degrade(entry, overrides, detail) {
|
|
128
|
+
for (const dotPath of entry.check.degradable.disables || []) {
|
|
129
|
+
setDotPath(overrides, dotPath, false);
|
|
130
|
+
}
|
|
131
|
+
entry.status = STATUS.WARN;
|
|
132
|
+
entry.detail = `${detail} → ${entry.check.degradable.warn}`;
|
|
133
|
+
entry.degraded = true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function detectEntry(entry, { config, signal, timeoutMs, overrides }) {
|
|
137
|
+
if (entry.status === STATUS.SKIPPED) return;
|
|
138
|
+
const start = Date.now();
|
|
139
|
+
const detectTimeoutMs = detectTimeoutOf(entry.check, timeoutMs);
|
|
140
|
+
try {
|
|
141
|
+
const result = await withTimeout(entry.check.detect({ config, signal }), detectTimeoutMs, entry.check.name);
|
|
142
|
+
entry.detectResult = result;
|
|
143
|
+
entry.runMs = Date.now() - start;
|
|
144
|
+
entry.status = resolveStatusFromDetect(entry.check, result);
|
|
145
|
+
entry.detail = result.detail;
|
|
146
|
+
// Así el preflight no aborta — la feature opcional se desactiva con aviso.
|
|
147
|
+
if (!result.ok && entry.check.degradable && (entry.status === STATUS.FAIL || entry.status === STATUS.WARN)) {
|
|
148
|
+
degrade(entry, overrides, result.detail);
|
|
149
|
+
}
|
|
150
|
+
} catch (err) {
|
|
151
|
+
entry.runMs = Date.now() - start;
|
|
152
|
+
if (!(err instanceof TimeoutError)) {
|
|
153
|
+
entry.detectResult = { ok: false, severity: "fail", detail: `Detection error: ${err.message}` };
|
|
154
|
+
entry.status = STATUS.FAIL;
|
|
155
|
+
entry.detail = entry.detectResult.detail;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
entry.detectResult = { ok: false, severity: "warn", detail: `Timeout after ${detectTimeoutMs}ms` };
|
|
159
|
+
if (entry.check.degradable) {
|
|
160
|
+
degrade(entry, overrides, entry.detectResult.detail);
|
|
161
|
+
} else {
|
|
162
|
+
entry.status = STATUS.TIMEOUT;
|
|
163
|
+
entry.detail = entry.detectResult.detail;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function remediateEntry(entry, { config, signal, logger, yes, onConfirm, remediateTimeoutMs, overrides }) {
|
|
169
|
+
const { check, detectResult } = entry;
|
|
170
|
+
if (check.strategy === STRATEGY.PROMPT && !yes) {
|
|
171
|
+
const approved = await onConfirm(check.describe || `Fix: ${check.name}`);
|
|
172
|
+
if (!approved) {
|
|
173
|
+
logger.info?.(`User declined remediation for ${check.name}`);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const start = Date.now();
|
|
178
|
+
try {
|
|
179
|
+
const rem = await withTimeout(
|
|
180
|
+
check.remediate({ config, extra: detectResult.extra, signal, logger }),
|
|
181
|
+
remediateTimeoutMs,
|
|
182
|
+
`${check.name}:remediate`
|
|
183
|
+
);
|
|
184
|
+
entry.remediated = rem.fixed === true;
|
|
185
|
+
entry.runMs += Date.now() - start;
|
|
186
|
+
if (rem.fixed) {
|
|
187
|
+
if (rem.changes) mergeOverrides(overrides, rem.changes);
|
|
188
|
+
entry.detail = rem.detail;
|
|
189
|
+
entry.status = STATUS.FIXED;
|
|
190
|
+
} else {
|
|
191
|
+
entry.detail = rem.detail || entry.detail;
|
|
192
|
+
entry.status = STATUS.FAIL;
|
|
193
|
+
}
|
|
194
|
+
} catch (err) {
|
|
195
|
+
entry.runMs += Date.now() - start;
|
|
196
|
+
const msg = err instanceof TimeoutError ? `Remediation timeout after ${remediateTimeoutMs}ms` : `Remediation error: ${err.message}`;
|
|
197
|
+
entry.detail = msg;
|
|
198
|
+
entry.status = STATUS.FAIL;
|
|
199
|
+
logger.warn?.(`Remediation failed for ${check.name}: ${msg}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function reverifyEntry(entry, { verifyConfig, signal, timeoutMs }) {
|
|
204
|
+
const start = Date.now();
|
|
205
|
+
try {
|
|
206
|
+
const result = await withTimeout(
|
|
207
|
+
entry.check.detect({ config: verifyConfig, signal }),
|
|
208
|
+
detectTimeoutOf(entry.check, timeoutMs),
|
|
209
|
+
`${entry.check.name}:reverify`
|
|
210
|
+
);
|
|
211
|
+
entry.runMs += Date.now() - start;
|
|
212
|
+
if (!result.ok) {
|
|
213
|
+
entry.status = STATUS.FAIL;
|
|
214
|
+
entry.detail = `${entry.detail} (but re-verify failed: ${result.detail})`;
|
|
215
|
+
}
|
|
216
|
+
} catch (err) {
|
|
217
|
+
entry.runMs += Date.now() - start;
|
|
218
|
+
entry.status = STATUS.FAIL;
|
|
219
|
+
entry.detail = `${entry.detail} (re-verify error: ${err.message})`;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
237
223
|
function shouldAttemptRemediation(entry) {
|
|
238
224
|
if (entry.status === STATUS.SKIPPED) return false;
|
|
239
225
|
if (entry.status === STATUS.OK) return false;
|
|
@@ -13,9 +13,9 @@ export const CORE_COMMANDS = [
|
|
|
13
13
|
// gets — it is the first command a new project ever runs.
|
|
14
14
|
"bootstrap",
|
|
15
15
|
"init",
|
|
16
|
-
"
|
|
16
|
+
"hu",
|
|
17
|
+
"review",
|
|
17
18
|
"plan",
|
|
18
|
-
"status",
|
|
19
19
|
"doctor",
|
|
20
20
|
"harden",
|
|
21
21
|
"config",
|
|
@@ -31,11 +31,11 @@ export const META_COMMANDS = ["advanced", "help"];
|
|
|
31
31
|
* so a newly-registered command can never silently vanish from `kj advanced`.
|
|
32
32
|
*/
|
|
33
33
|
export const ADVANCED_GROUPS = [
|
|
34
|
-
{ title: "
|
|
35
|
-
{ title: "Análisis
|
|
34
|
+
{ title: "Agentes y revisión", commands: ["code", "solomon", "agent", "scan", "tournament"] },
|
|
35
|
+
{ title: "Análisis previo", commands: ["discover", "spec", "onboard", "brief"] },
|
|
36
36
|
{ title: "Búsqueda / RAG", commands: ["rag", "qmd", "watch", "doc"] },
|
|
37
|
-
{ title: "Calidad / auditoría", commands: ["audit", "check", "mutate", "webperf", "sonar", "privacy", "release", "policy", "rules", "rulesync", "claims", "steward", "pr-size"] },
|
|
38
|
-
{ title: "Sesión / board", commands: ["
|
|
37
|
+
{ title: "Calidad / auditoría", commands: ["audit", "check", "mutate", "webperf", "sonar", "privacy", "release", "policy", "rules", "rulesync", "claims", "steward", "pr-size", "main-guard"] },
|
|
38
|
+
{ title: "Sesión / board", commands: ["report", "board", "adr", "worktree", "sentinel", "identity", "security", "handoff"] },
|
|
39
39
|
{ title: "Infra / setup", commands: ["install-tools", "ollama", "skills", "roles", "agents", "env"] },
|
|
40
40
|
{ title: "Mantenimiento", commands: ["clean", "sync", "telemetry", "report-issue"] },
|
|
41
41
|
];
|
package/src/cli/register-meta.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { discoverCommand } from "../commands/discover.js";
|
|
2
2
|
import { specRefineCommand } from "../commands/spec-refine.js";
|
|
3
|
-
import {
|
|
4
|
-
import { researcherCommand } from "../commands/researcher.js";
|
|
5
|
-
import { architectCommand } from "../commands/architect.js";
|
|
3
|
+
import { writeSbom } from "../audit/sbom.js";
|
|
6
4
|
import { onboardCommand } from "../commands/onboard.js";
|
|
7
5
|
import { startCommand } from "../commands/start.js";
|
|
8
6
|
import { goCommand } from "../commands/go.js";
|
|
@@ -19,10 +17,8 @@ import { qmdQueryCommand } from "../commands/qmd.js";
|
|
|
19
17
|
import { ragMcpCommand } from "../commands/rag-mcp.js";
|
|
20
18
|
import { watchStartCommand, watchStopCommand, watchStatusCommand } from "../commands/watch.js";
|
|
21
19
|
import { auditCommand } from "../commands/audit.js";
|
|
22
|
-
import { resumeCommand } from "../commands/resume.js";
|
|
23
20
|
import { boardCommand } from "../commands/board.js";
|
|
24
21
|
import { webperfCommand } from "../commands/webperf.js";
|
|
25
|
-
import { undoCommand } from "../commands/undo.js";
|
|
26
22
|
import { syncCommand } from "../commands/sync.js";
|
|
27
23
|
import { cleanCommand } from "../commands/clean.js";
|
|
28
24
|
import { checkCommand } from "../commands/check.js";
|
|
@@ -106,54 +102,6 @@ export function registerMeta(program, { pkgVersion }) {
|
|
|
106
102
|
});
|
|
107
103
|
});
|
|
108
104
|
|
|
109
|
-
program
|
|
110
|
-
.command("triage")
|
|
111
|
-
.description("Classify task complexity and recommend pipeline roles")
|
|
112
|
-
.argument("[task]", "Task description (REQUIRED — provide as argument or via --task-file)")
|
|
113
|
-
.option("--task-file <path>", "Read the task from a file (e.g. .md)")
|
|
114
|
-
.option("--triage <name>", "Override triage agent")
|
|
115
|
-
.option("--triage-model <name>", "Override triage model")
|
|
116
|
-
.option("--json", "Output raw JSON")
|
|
117
|
-
.action(async (task, flags) => {
|
|
118
|
-
await withConfig(pkgVersion, "triage", flags, async ({ config, logger }) => {
|
|
119
|
-
const { resolveTaskInput } = await import("../utils/task-file.js");
|
|
120
|
-
const resolvedTask = await resolveTaskInput({ task, taskFile: flags.taskFile, projectDir: config.projectDir, logger, indexFirstBytes: config.docs?.indexFirstBytes });
|
|
121
|
-
await triageCommand({ task: resolvedTask, config, logger, json: flags.json });
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
program
|
|
126
|
-
.command("researcher")
|
|
127
|
-
.description("Research codebase for a task (files, patterns, constraints)")
|
|
128
|
-
.argument("[task]", "Task description (REQUIRED — provide as argument or via --task-file)")
|
|
129
|
-
.option("--task-file <path>", "Read the task from a file (e.g. .md)")
|
|
130
|
-
.option("--researcher <name>", "Override researcher agent")
|
|
131
|
-
.option("--researcher-model <name>", "Override researcher model")
|
|
132
|
-
.action(async (task, flags) => {
|
|
133
|
-
await withConfig(pkgVersion, "researcher", flags, async ({ config, logger }) => {
|
|
134
|
-
const { resolveTaskInput } = await import("../utils/task-file.js");
|
|
135
|
-
const resolvedTask = await resolveTaskInput({ task, taskFile: flags.taskFile, projectDir: config.projectDir, logger, indexFirstBytes: config.docs?.indexFirstBytes });
|
|
136
|
-
await researcherCommand({ task: resolvedTask, config, logger });
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
program
|
|
141
|
-
.command("architect")
|
|
142
|
-
.description("Design solution architecture (layers, patterns, contracts)")
|
|
143
|
-
.argument("[task]", "Task description (REQUIRED — provide as argument or via --task-file)")
|
|
144
|
-
.option("--task-file <path>", "Read the task from a file (e.g. .md)")
|
|
145
|
-
.option("--architect <name>", "Override architect agent")
|
|
146
|
-
.option("--architect-model <name>", "Override architect model")
|
|
147
|
-
.option("--context <text>", "Additional context (e.g. researcher output)")
|
|
148
|
-
.option("--json", "Output raw JSON")
|
|
149
|
-
.action(async (task, flags) => {
|
|
150
|
-
await withConfig(pkgVersion, "architect", flags, async ({ config, logger }) => {
|
|
151
|
-
const { resolveTaskInput } = await import("../utils/task-file.js");
|
|
152
|
-
const resolvedTask = await resolveTaskInput({ task, taskFile: flags.taskFile, projectDir: config.projectDir, logger, indexFirstBytes: config.docs?.indexFirstBytes });
|
|
153
|
-
await architectCommand({ task: resolvedTask, config, logger, context: flags.context, json: flags.json });
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
|
|
157
105
|
program
|
|
158
106
|
.command("onboard")
|
|
159
107
|
.description("Analyze an existing codebase (brownfield) and produce an Architecture Brief")
|
|
@@ -317,7 +265,7 @@ export function registerMeta(program, { pkgVersion }) {
|
|
|
317
265
|
// beats ordering a practice an agent can narrate without doing.
|
|
318
266
|
const wt = program.command("worktree").description("Isolated task lanes (git worktrees) for the host agent");
|
|
319
267
|
wt.command("start <slug>")
|
|
320
|
-
.description("Create a lane: worktree + feat/<slug> branch + dependency bootstrap")
|
|
268
|
+
.description("Create a lane: worktree + feat/<slug> branch from origin/<base> + dependency bootstrap")
|
|
321
269
|
.action(async (slug, flags) => {
|
|
322
270
|
await withConfig(pkgVersion, "worktree", flags, async ({ config }) => {
|
|
323
271
|
await worktreeCommand({ config, action: "start", args: [slug], flags });
|
|
@@ -325,14 +273,16 @@ export function registerMeta(program, { pkgVersion }) {
|
|
|
325
273
|
});
|
|
326
274
|
wt.command("list")
|
|
327
275
|
.description("List the active lanes")
|
|
276
|
+
.option("--json", "Each lane with its commits ahead of the base and the cards its branch names")
|
|
277
|
+
.option("--prs", "With --json, each lane's PR (one gh call)")
|
|
328
278
|
.action(async (flags) => {
|
|
329
279
|
await withConfig(pkgVersion, "worktree", flags, async ({ config }) => {
|
|
330
280
|
await worktreeCommand({ config, action: "list", flags });
|
|
331
281
|
});
|
|
332
282
|
});
|
|
333
283
|
wt.command("done <slug>")
|
|
334
|
-
.option("--force", "Remove the lane even with uncommitted changes")
|
|
335
|
-
.description("Remove a merged
|
|
284
|
+
.option("--force", "Remove the lane even with uncommitted changes, and delete its branch even if not merged")
|
|
285
|
+
.description("Remove a lane and delete its branch when merged into the base (refuses uncommitted changes without --force)")
|
|
336
286
|
.action(async (slug, flags) => {
|
|
337
287
|
await withConfig(pkgVersion, "worktree", flags, async ({ config }) => {
|
|
338
288
|
await worktreeCommand({ config, action: "done", args: [slug], flags });
|
|
@@ -372,6 +322,16 @@ export function registerMeta(program, { pkgVersion }) {
|
|
|
372
322
|
await huCommand({ config, action: "move", args: [id, status], flags });
|
|
373
323
|
});
|
|
374
324
|
});
|
|
325
|
+
// KJC-TSK-1372 (#2853): one step to start a task: card, lane, running.
|
|
326
|
+
hu.command("start <id> [slug]")
|
|
327
|
+
.description("Start a card: open its lane feat/<id>-<slug> from origin/<base> and move it to running (slug from the title if omitted)")
|
|
328
|
+
.option("--code", "Then have the panel coder write the card in the lane (kj code --card)")
|
|
329
|
+
.option("--json", "Machine-readable output")
|
|
330
|
+
.action(async (id, slug, flags) => {
|
|
331
|
+
await withConfig(pkgVersion, "hu", flags, async ({ config }) => {
|
|
332
|
+
await huCommand({ config, action: "start", args: [id, slug].filter(Boolean), flags });
|
|
333
|
+
});
|
|
334
|
+
});
|
|
375
335
|
hu.command("list")
|
|
376
336
|
.option("--json", "Machine-readable output")
|
|
377
337
|
.action(async (flags) => {
|
|
@@ -801,6 +761,19 @@ export function registerMeta(program, { pkgVersion }) {
|
|
|
801
761
|
});
|
|
802
762
|
});
|
|
803
763
|
|
|
764
|
+
// KJC-TSK-1369 (ADR 0037): the audit job on every push to the protected branch.
|
|
765
|
+
program.command("main-guard")
|
|
766
|
+
.description("Audit a push to the protected branch: merges without their required checks, and the protection (exit 1 with a markdown report)")
|
|
767
|
+
.requiredOption("--repo <owner/repo>", "GitHub repository")
|
|
768
|
+
.requiredOption("--before <sha>", "The branch tip before the push")
|
|
769
|
+
.requiredOption("--after <sha>", "The branch tip after the push")
|
|
770
|
+
.requiredOption("--require <checks...>", "Required checks that every merge must have passed")
|
|
771
|
+
.option("--branch <name>", "Protected branch", "main")
|
|
772
|
+
.action(async (flags) => {
|
|
773
|
+
const { mainGuardCommand } = await import("../commands/main-guard.js");
|
|
774
|
+
await mainGuardCommand({ flags });
|
|
775
|
+
});
|
|
776
|
+
|
|
804
777
|
const rag = program.command("rag").description("Retrieval-augmented search over Karajan plans, onboarding briefs and project code");
|
|
805
778
|
rag.command("index")
|
|
806
779
|
.description("Index plans + onboarding (and optionally project sources) into the local vector store")
|
|
@@ -947,8 +920,21 @@ export function registerMeta(program, { pkgVersion }) {
|
|
|
947
920
|
.option("--deterministic-only", "Skip the LLM analysis entirely. Print/persist only the deterministic findings (basalCost, sonar, stack, growth-delta, webperf). Zero tokens spent. Compatible with --report-file and --json.")
|
|
948
921
|
.option("--security", "Focused deterministic security pass: prompt-injection scan over the agent-context surface (CLAUDE.md/AGENTS.md/templates/.rulesync/.karajan) + OSV + Semgrep + Sonar. Skips basal-cost, webperf, madge, knip, ai-slop, harness and the LLM — zero tokens. Designed for agents to self-invoke when a task touches sensitive surface (KJC-TSK-0695).")
|
|
949
922
|
.option("-y, --yes", "Auto-confirm the 'Continue with LLM analysis?' prompt. Useful in scripts that want the full audit non-interactively. CI/non-TTY paths already auto-confirm without this flag.")
|
|
923
|
+
.option("--sbom <file>", "Only write the CycloneDX SBOM of the npm project's production dependencies to <file> (from package-lock.json) and stop. KJC-TSK-1255.")
|
|
950
924
|
.action(async (task, flags) => {
|
|
951
925
|
await withConfig(pkgVersion, "audit", flags, async ({ config, logger }) => {
|
|
926
|
+
if (flags.sbom) {
|
|
927
|
+
try {
|
|
928
|
+
const res = await writeSbom({ projectDir: config.projectDir || process.cwd(), file: flags.sbom });
|
|
929
|
+
if (flags.json) console.log(JSON.stringify(res));
|
|
930
|
+
else console.log(`SBOM (CycloneDX, ${res.components} components): ${res.file}`);
|
|
931
|
+
} catch (err) {
|
|
932
|
+
if (flags.json) console.log(JSON.stringify({ error: err.message }));
|
|
933
|
+
else logger.error(err.message);
|
|
934
|
+
process.exitCode = 1;
|
|
935
|
+
}
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
952
938
|
let resolvedTask = task;
|
|
953
939
|
if (flags.taskFile) {
|
|
954
940
|
const { readTaskFile } = await import("../utils/task-file.js");
|
|
@@ -997,21 +983,6 @@ export function registerMeta(program, { pkgVersion }) {
|
|
|
997
983
|
});
|
|
998
984
|
});
|
|
999
985
|
|
|
1000
|
-
program
|
|
1001
|
-
.command("resume")
|
|
1002
|
-
.description("Resume a paused session")
|
|
1003
|
-
.argument("<sessionId>")
|
|
1004
|
-
.option("--answer <text>", "Answer to the question that caused the pause")
|
|
1005
|
-
.option("--non-interactive", "Agents/CI: auto-answer prompts with their safe defaults; stop when there is none. Also enabled by KJ_NON_INTERACTIVE=1.")
|
|
1006
|
-
.option("--json", "Output JSON only")
|
|
1007
|
-
.action(async (sessionId, flags) => {
|
|
1008
|
-
await withConfig(pkgVersion, "resume", flags, async ({ config, logger }) => {
|
|
1009
|
-
const res = await resumeCommand({ sessionId, answer: flags.answer, config, logger, flags });
|
|
1010
|
-
// KJC-TSK-0674 (issue #1289): same contract as kj run — an aborted
|
|
1011
|
-
// resume must be observable by CI.
|
|
1012
|
-
if (res?.aborted) process.exitCode = 1;
|
|
1013
|
-
});
|
|
1014
|
-
});
|
|
1015
986
|
|
|
1016
987
|
program
|
|
1017
988
|
.command("update")
|
|
@@ -1061,17 +1032,6 @@ export function registerMeta(program, { pkgVersion }) {
|
|
|
1061
1032
|
});
|
|
1062
1033
|
});
|
|
1063
1034
|
|
|
1064
|
-
program
|
|
1065
|
-
.command("undo")
|
|
1066
|
-
.description("Revert last pipeline run")
|
|
1067
|
-
.option("--hard", "Discard all changes (default: soft reset, keeps changes staged)")
|
|
1068
|
-
.action(async (flags) => {
|
|
1069
|
-
await withConfig(pkgVersion, "undo", flags, async ({ logger }) => {
|
|
1070
|
-
const result = await undoCommand({ hard: !!flags.hard, logger });
|
|
1071
|
-
if (!result.ok) process.exit(1);
|
|
1072
|
-
});
|
|
1073
|
-
});
|
|
1074
|
-
|
|
1075
1035
|
program
|
|
1076
1036
|
.command("sync")
|
|
1077
1037
|
.description("Detect drift between code and the latest plan. Read-only by default; --apply writes a patch back to the Canvas.")
|