karajan-code 4.70.4 → 5.0.1
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/register-roles-skills.js +1 -1
- 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 +121 -55
- package/src/commands/review-gate.js +421 -341
- package/src/commands/skills.js +4 -1
- 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 +107 -26
- package/src/config/defaults.js +1 -0
- package/src/config/loader.js +34 -17
- package/src/config/role-resolver.js +1 -0
- package/src/config/schema.js +1 -0
- 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 +83 -13
- 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/handlers/rag-handler.js +9 -1
- 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 +71 -44
- package/src/rag/bootstrap-index.js +46 -54
- package/src/rag/indexer.js +9 -4
- package/src/rag/migrate.js +1 -1
- package/src/rag/onnx-fallback.js +2 -2
- package/src/rag/pid-lease.js +181 -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-cli.js +19 -1
- package/src/skills/atril-skills.js +5 -4
- package/src/skills/skills-search.js +72 -0
- package/src/skills/translate.js +62 -0
- 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
package/README.md
CHANGED
|
@@ -110,9 +110,9 @@ Tier B is the guarantee floor — hosts without hooks lose A, never B; C re-veri
|
|
|
110
110
|
|
|
111
111
|
Every chokepoint decision lands in a hash-chained log (`kj policy anchor` seals its head in git), and `kj policy report` turns that log into evidence of process: per rule, how often it warned, denied or was exempted, which denials are still open, which grants are alive, expired or renewed — so a rule "gains teeth" on data, not on a hunch, and a renewed exception is read for what it is: the policy asking to change.
|
|
112
112
|
|
|
113
|
-
##
|
|
113
|
+
## Unattended runs (CI and automation)
|
|
114
114
|
|
|
115
|
-
The classic multiagent pipeline
|
|
115
|
+
The classic multiagent pipeline (`kj run` and its family) was retired in 5.0: your agent does the work and Karajan governs it. In CI and scripts, pass `--non-interactive` (or `KJ_NON_INTERACTIVE=1`): safe gates auto-answer, FAIL findings stop with a real exit code. Moving from 4.x? See [MIGRATION-v5.md](MIGRATION-v5.md). `kj advanced` lists the full surface.
|
|
116
116
|
|
|
117
117
|
## v3 (historical)
|
|
118
118
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "karajan-code",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"description": "Local multi-agent coding orchestrator with TDD, SonarQube, and code review pipeline",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "AGPL-3.0",
|
|
@@ -94,30 +94,24 @@
|
|
|
94
94
|
"prepublishOnly": "node scripts/verify-pack.mjs",
|
|
95
95
|
"verify-quickstart": "node scripts/quickstart-gate.mjs"
|
|
96
96
|
},
|
|
97
|
-
"simple-git-hooks": {
|
|
98
|
-
"pre-commit": "npx lint-staged"
|
|
99
|
-
},
|
|
100
|
-
"lint-staged": {
|
|
101
|
-
"src/**/*.js": "eslint --max-warnings=999"
|
|
102
|
-
},
|
|
103
97
|
"overrides": {
|
|
104
98
|
"fast-uri": "^3.1.2"
|
|
105
99
|
},
|
|
106
100
|
"dependencies": {
|
|
107
101
|
"@babel/parser": "^7.29.7",
|
|
108
|
-
"@karajan-family/atril": "^1.
|
|
102
|
+
"@karajan-family/atril": "^1.3.0",
|
|
109
103
|
"@karajan-family/governance": "^0.1.0",
|
|
110
104
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
111
105
|
"@xterm/xterm": "^6.0.0",
|
|
112
|
-
"better-sqlite3": "^12.10.0",
|
|
113
106
|
"chokidar": "^5.0.0",
|
|
114
107
|
"commander": "^15.0.0",
|
|
108
|
+
"driver.js": "1.10.2",
|
|
115
109
|
"execa": "^9.6.0",
|
|
116
110
|
"express": "^5.1.0",
|
|
117
111
|
"express-rate-limit": "^8.5.0",
|
|
118
112
|
"helmet": "^8.1.0",
|
|
119
113
|
"js-yaml": "^4.2.0",
|
|
120
|
-
"karajan-core": "^1.
|
|
114
|
+
"karajan-core": "^1.7.0",
|
|
121
115
|
"karajan-rag": "^1.2.0",
|
|
122
116
|
"knip": "^6.15.0",
|
|
123
117
|
"madge": "^8.0.0",
|
|
@@ -133,18 +127,16 @@
|
|
|
133
127
|
},
|
|
134
128
|
"devDependencies": {
|
|
135
129
|
"@eslint/js": "^10.0.1",
|
|
136
|
-
"@vitest/coverage-v8": "^4.1.
|
|
130
|
+
"@vitest/coverage-v8": "^4.1.12",
|
|
137
131
|
"esbuild": "^0.28.0",
|
|
138
132
|
"eslint": "^10.4.1",
|
|
139
133
|
"eslint-plugin-import-x": "^4.16.2",
|
|
140
134
|
"eslint-plugin-node-security": "5.2.2",
|
|
141
135
|
"eslint-plugin-security": "^4.0.0",
|
|
142
136
|
"globals": "^17.6.0",
|
|
143
|
-
"lint-staged": "^17.0.7",
|
|
144
137
|
"postject": "^1.0.0-alpha.6",
|
|
145
138
|
"prettier": "^3.4.2",
|
|
146
|
-
"simple-git-hooks": "^2.13.1",
|
|
147
139
|
"smol-toml": "^1.7.0",
|
|
148
|
-
"vitest": "^4.1.
|
|
140
|
+
"vitest": "^4.1.12"
|
|
149
141
|
}
|
|
150
142
|
}
|
|
@@ -123,24 +123,9 @@ function renderEmptyState(title, text) {
|
|
|
123
123
|
|
|
124
124
|
// ---- Plan execution ----
|
|
125
125
|
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
// (editing inline if needed — see edit-in-place PR), and when happy
|
|
129
|
-
// clicks "Run plan", which spawns `kj run --plan <id>` as a detached
|
|
130
|
-
// child on the server. Status updates flow back through the plan JSON
|
|
131
|
-
// watcher without any manual refresh.
|
|
132
|
-
//
|
|
133
|
-
// We still expose a per-HU status PATCH endpoint for corrective moves
|
|
134
|
-
// (e.g. retry a failed HU manually), but it's no longer the primary
|
|
135
|
-
// action on the card.
|
|
126
|
+
// KJC-TSK-1319: the board no longer launches kj run (retired in 5.0); your
|
|
127
|
+
// agent does the work. A per-HU status PATCH stays for corrective moves.
|
|
136
128
|
|
|
137
|
-
/**
|
|
138
|
-
* Launch the pipeline over every plan of a project. Returns when the
|
|
139
|
-
* spawn ack is received — not when the run completes. Live progress is
|
|
140
|
-
* reflected automatically by the plans-dir watcher on the server, so
|
|
141
|
-
* the board re-renders without us having to poll.
|
|
142
|
-
* @param {string} projectId
|
|
143
|
-
*/
|
|
144
129
|
// Track the most recently launched planId so the "View log" button in
|
|
145
130
|
// the section header can re-open the viewer without the user hunting
|
|
146
131
|
// through plan ids.
|
|
@@ -249,6 +234,7 @@ function handleRoute() {
|
|
|
249
234
|
btn.classList.toggle('active', btn.dataset.view === currentView);
|
|
250
235
|
});
|
|
251
236
|
|
|
252
|
-
|
|
237
|
+
// KJC-TSK-1198: the view's promise, so the boot can wait for it before the tour.
|
|
238
|
+
return render();
|
|
253
239
|
}
|
|
254
240
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
<title>Karajan HU Board</title>
|
|
7
7
|
<link rel="stylesheet" href="/styles.css">
|
|
8
8
|
<link rel="stylesheet" href="/vendor/xterm/css/xterm.css">
|
|
9
|
+
<link rel="stylesheet" href="/vendor/driver/driver.css">
|
|
9
10
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='6' fill='%237c3aed'/><text x='16' y='22' text-anchor='middle' fill='white' font-size='16' font-family='sans-serif' font-weight='bold'>K</text></svg>">
|
|
10
11
|
</head>
|
|
11
12
|
<body>
|
|
@@ -25,7 +26,7 @@
|
|
|
25
26
|
<div class="header__controls">
|
|
26
27
|
<button class="control-btn" id="help-btn" title="What does each view do? Quick reference for Board / Graph / Dashboard / Sessions / Pipeline.">?</button>
|
|
27
28
|
<button class="control-btn" id="config-btn" title="Configurar Karajan (modelos, agentes, tope de iteraciones, …)">⚙</button>
|
|
28
|
-
<button class="control-btn" id="commands-btn" title="Launch a Karajan CLI command (kj
|
|
29
|
+
<button class="control-btn" id="commands-btn" title="Launch a Karajan CLI command (kj clean)">⚡</button>
|
|
29
30
|
<button class="control-btn" id="conversation-btn" title="Conversación con tu agente — la terminal real, dentro del board (ADR 0008)" onclick="openConversationPanel()">💬</button>
|
|
30
31
|
<button class="control-btn" id="restart-btn" title="Restart the HU Board server (preserves URL, the page reconnects automatically)">🔁</button>
|
|
31
32
|
<button class="control-btn" id="sync-btn" title="Re-scan disk for new batches">🔄</button>
|
|
@@ -72,6 +73,9 @@
|
|
|
72
73
|
<script src="/utils/story-detail-view.js"></script>
|
|
73
74
|
<script src="/utils/preflight-view.js"></script>
|
|
74
75
|
<script src="/utils/plan-rollup-view.js"></script>
|
|
76
|
+
<script src="/utils/shared-board-notice.js"></script>
|
|
77
|
+
<script src="/vendor/driver/driver.js.iife.js"></script>
|
|
78
|
+
<script src="/utils/board-tour.js"></script>
|
|
75
79
|
<script src="/utils/log-panel.js"></script>
|
|
76
80
|
<script src="/vendor/xterm/lib/xterm.js"></script>
|
|
77
81
|
<script src="/utils/terminal-panel.js"></script>
|
|
@@ -80,7 +84,6 @@
|
|
|
80
84
|
<script src="/utils/story-edit-form.js"></script>
|
|
81
85
|
<script src="/utils/config-editor.js"></script>
|
|
82
86
|
<script src="/utils/command-launcher.js"></script>
|
|
83
|
-
<script src="/utils/run-launcher.js"></script>
|
|
84
87
|
<script src="/utils/server-push.js"></script>
|
|
85
88
|
<script src="/app.js"></script>
|
|
86
89
|
<script src="/utils/init-listeners.js"></script>
|
|
@@ -374,6 +374,14 @@ a:hover {
|
|
|
374
374
|
color: var(--text-muted);
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
+
/* KJC-TSK-1313: the worktree lane working the card. */
|
|
378
|
+
.story-card__lane {
|
|
379
|
+
font-family: var(--font-mono);
|
|
380
|
+
font-size: 0.75rem;
|
|
381
|
+
color: var(--color-green);
|
|
382
|
+
overflow-wrap: anywhere;
|
|
383
|
+
}
|
|
384
|
+
|
|
377
385
|
.quality-bar {
|
|
378
386
|
display: inline-flex;
|
|
379
387
|
gap: 1px;
|
|
@@ -438,6 +446,24 @@ a:hover {
|
|
|
438
446
|
margin-top: 4px;
|
|
439
447
|
}
|
|
440
448
|
|
|
449
|
+
/* KJC-TSK-1324: ▶ starts a pending card in its own lane. */
|
|
450
|
+
.story-card__play {
|
|
451
|
+
margin-left: auto;
|
|
452
|
+
min-width: 28px;
|
|
453
|
+
min-height: 28px;
|
|
454
|
+
border: 1px solid var(--color-green);
|
|
455
|
+
border-radius: 6px;
|
|
456
|
+
background: transparent;
|
|
457
|
+
color: var(--color-green);
|
|
458
|
+
cursor: pointer;
|
|
459
|
+
}
|
|
460
|
+
.story-card__play:hover,
|
|
461
|
+
.story-card__play:focus-visible {
|
|
462
|
+
background: rgba(52, 211, 153, 0.15);
|
|
463
|
+
outline: 2px solid var(--color-green);
|
|
464
|
+
outline-offset: 1px;
|
|
465
|
+
}
|
|
466
|
+
|
|
441
467
|
/* ---- Story Detail Modal ---- */
|
|
442
468
|
.modal-backdrop {
|
|
443
469
|
position: fixed;
|
|
@@ -6,9 +6,6 @@
|
|
|
6
6
|
// Contents:
|
|
7
7
|
// - api(): thin JSON fetch wrapper used everywhere in the UI.
|
|
8
8
|
// - triggerSync(): POST /api/sync — fired on page load and the 🔄 button.
|
|
9
|
-
// - Standby banner: _standbyData state + refreshStandby() +
|
|
10
|
-
// _renderStandbyBanner() + _tickStandbyCountdowns() +
|
|
11
|
-
// startStandbyPolling() — KJC-TSK-0414 PR4.
|
|
12
9
|
// - Server-restart detector: pollServerVersion() + __versionBaseline +
|
|
13
10
|
// window.forceRefresh — KJC-TSK-0379.
|
|
14
11
|
//
|
|
@@ -22,77 +19,6 @@ async function api(path) {
|
|
|
22
19
|
return res.json();
|
|
23
20
|
}
|
|
24
21
|
|
|
25
|
-
// KJC-TSK-0414 PR4: standby banner — sesiones hibernadas pendientes de resume.
|
|
26
|
-
// Polleamos /api/standby cada 30s + tick countdown cada 1s.
|
|
27
|
-
let _standbyData = { sessions: [] };
|
|
28
|
-
let _standbyPollTimer = null;
|
|
29
|
-
let _standbyTickTimer = null;
|
|
30
|
-
|
|
31
|
-
function _fmtCountdown(ms) {
|
|
32
|
-
if (ms <= 0) return '✓ resume inmediato';
|
|
33
|
-
const s = Math.round(ms / 1000);
|
|
34
|
-
const h = Math.floor(s / 3600);
|
|
35
|
-
const m = Math.floor((s % 3600) / 60);
|
|
36
|
-
const sec = s % 60;
|
|
37
|
-
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(sec).padStart(2, '0')}`;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function refreshStandby() {
|
|
41
|
-
try {
|
|
42
|
-
const r = await api('/api/standby');
|
|
43
|
-
_standbyData = r || { sessions: [] };
|
|
44
|
-
_renderStandbyBanner();
|
|
45
|
-
} catch { /* silencioso — endpoint nuevo, podría no existir en versiones viejas */ }
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function _renderStandbyBanner() {
|
|
49
|
-
let banner = document.getElementById('standby-banner');
|
|
50
|
-
const sessions = _standbyData.sessions || [];
|
|
51
|
-
if (sessions.length === 0) {
|
|
52
|
-
if (banner) banner.remove();
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
if (!banner) {
|
|
56
|
-
banner = document.createElement('div');
|
|
57
|
-
banner.id = 'standby-banner';
|
|
58
|
-
banner.style.cssText = 'background:rgba(251,191,36,0.12);border-left:4px solid #fbbf24;padding:10px 16px;margin:0 0 12px;font-size:0.85rem';
|
|
59
|
-
const app = document.getElementById('app');
|
|
60
|
-
if (app?.parentNode) app.parentNode.insertBefore(banner, app);
|
|
61
|
-
}
|
|
62
|
-
const now = Date.now();
|
|
63
|
-
banner.innerHTML = `
|
|
64
|
-
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap">
|
|
65
|
-
<strong>💤 ${sessions.length} sesión(es) hibernadas:</strong>
|
|
66
|
-
${sessions.map((s) => {
|
|
67
|
-
const ms = new Date(s.cooldownUntil).getTime() - now;
|
|
68
|
-
const label = _fmtCountdown(ms);
|
|
69
|
-
const reason = s.reason || '?';
|
|
70
|
-
const planInfo = s.planId ? ` · ${s.planId}${s.huId ? '/' + s.huId : ''}` : '';
|
|
71
|
-
return `<span data-session="${esc(s.sessionId)}" data-cooldown="${esc(s.cooldownUntil)}" style="background:rgba(0,0,0,0.15);padding:4px 8px;border-radius:4px;font-family:monospace" title="${esc(reason)}${esc(planInfo)}">${esc(s.sessionId)} · <span class="standby-countdown">${label}</span></span>`;
|
|
72
|
-
}).join('')}
|
|
73
|
-
<span style="margin-left:auto;color:var(--text-muted);font-size:0.75rem">Auto-resume al llegar el cooldown (board) o <code>kj resume <id></code></span>
|
|
74
|
-
</div>
|
|
75
|
-
`;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function _tickStandbyCountdowns() {
|
|
79
|
-
const banner = document.getElementById('standby-banner');
|
|
80
|
-
if (!banner) return;
|
|
81
|
-
const now = Date.now();
|
|
82
|
-
for (const chip of banner.querySelectorAll('[data-cooldown]')) {
|
|
83
|
-
const target = new Date(chip.getAttribute('data-cooldown')).getTime();
|
|
84
|
-
const span = chip.querySelector('.standby-countdown');
|
|
85
|
-
if (span) span.textContent = _fmtCountdown(target - now);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function startStandbyPolling() {
|
|
90
|
-
if (_standbyPollTimer || _standbyTickTimer) return;
|
|
91
|
-
refreshStandby();
|
|
92
|
-
_standbyPollTimer = setInterval(refreshStandby, 30_000);
|
|
93
|
-
_standbyTickTimer = setInterval(_tickStandbyCountdowns, 1000);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
22
|
/**
|
|
97
23
|
* Trigger a full re-scan of disk data (hu-stories + sessions).
|
|
98
24
|
* Called on page load and via the sync button.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// KJC-TSK-1198 — guided tour of the HU Board (driver.js, served from the dependency at
|
|
2
|
+
// /vendor/driver). It starts by itself the first time; the help modal relaunches it.
|
|
3
|
+
//
|
|
4
|
+
// Classic script (no exports). Globals consumed: window.driver.js (driver.js.iife.js).
|
|
5
|
+
|
|
6
|
+
const BOARD_TOUR_SEEN_KEY = 'kj-board-tour-seen';
|
|
7
|
+
|
|
8
|
+
// [selector, title, description, what to say when it is not on screen (else the step is skipped)]
|
|
9
|
+
const BOARD_TOUR = [
|
|
10
|
+
['[data-view="dashboard"]', 'Dashboard', 'Every project the board knows, with its stats. Click a project to open it.'],
|
|
11
|
+
['#help-btn', 'Help', 'A quick reference of each view, and this tour again whenever you want it.'],
|
|
12
|
+
['#config-btn', 'Settings', 'Configure Karajan: models, agents, iteration cap.'],
|
|
13
|
+
['#commands-btn', 'Commands', 'Launch a kj command (kj clean) and watch its log.'],
|
|
14
|
+
['#conversation-btn', 'Conversation', 'Your agent’s real terminal, inside the board.'],
|
|
15
|
+
['#restart-btn', 'Restart', 'Restart the board server. The page reconnects by itself.'],
|
|
16
|
+
['#sync-btn', 'Re-scan', 'Read the plans and sessions on disk again when something new is missing.'],
|
|
17
|
+
['#force-refresh-btn', 'Clean reload', 'Wipe the browser caches and reload, for when the page looks stale.'],
|
|
18
|
+
['#project-nav', 'Project bar', 'Board is the kanban of HUs, Graph who blocks whom, Sessions every kj run, Governance the rules and decision log, Pipeline the live runs, RAG the index of the code, Wiki the project docs.',
|
|
19
|
+
'Open a project from the Dashboard: a bar appears with its views (Board, Graph, Sessions, Governance, Pipeline, RAG, Wiki).'],
|
|
20
|
+
['#preflight-panel', 'Project checks', 'Whether the project is ready to run: remote, clean tree, agents. Red means a blocker; open it to see why.'],
|
|
21
|
+
['#rag-panel', 'Search', 'Ask anything about the project’s plans, onboarding or code.'],
|
|
22
|
+
['#shared-board-notice [role="note"]', 'Shared board', 'These HUs are the team’s, from GitHub Issues: read-only here, changed with kj hu move.'],
|
|
23
|
+
['.kanban', 'Kanban', 'Pending, Running and Done. Click a card to see the HU in detail.'],
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const isOnScreen = (selector) => {
|
|
27
|
+
const el = document.querySelector(selector);
|
|
28
|
+
return Boolean(el) && el.offsetParent !== null;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/** The tour for what is on screen now: absent elements are skipped or explained in the middle. */
|
|
32
|
+
function boardTourSteps() {
|
|
33
|
+
return BOARD_TOUR.flatMap(([element, title, description, whenAbsent]) => {
|
|
34
|
+
if (isOnScreen(element)) return [{ element, popover: { title, description } }];
|
|
35
|
+
return whenAbsent ? [{ popover: { title, description: whenAbsent } }] : [];
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Run the whole tour; once closed or skipped, it does not start by itself again. */
|
|
40
|
+
function startBoardTour() {
|
|
41
|
+
const tour = window.driver.js.driver({
|
|
42
|
+
showProgress: true,
|
|
43
|
+
allowKeyboardControl: true,
|
|
44
|
+
steps: boardTourSteps(),
|
|
45
|
+
onDestroyed: () => {
|
|
46
|
+
try { localStorage.setItem(BOARD_TOUR_SEEN_KEY, '1'); }
|
|
47
|
+
catch (err) { console.warn('board tour: cannot remember it was seen:', err.message); }
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
tour.drive();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** First visit only. A browser that cannot remember it never gets it by itself. */
|
|
54
|
+
function maybeStartFirstTour() {
|
|
55
|
+
try {
|
|
56
|
+
if (localStorage.getItem(BOARD_TOUR_SEEN_KEY)) return false;
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
startBoardTour();
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
@@ -6,12 +6,11 @@
|
|
|
6
6
|
//
|
|
7
7
|
// Globals consumed from app.js / earlier scripts:
|
|
8
8
|
// - api(), esc(), renderEmptyState(), renderProjectPicker(),
|
|
9
|
-
// renderPreflightPanel(), renderPlanRollup(),
|
|
10
|
-
//
|
|
9
|
+
// renderPreflightPanel(), renderPlanRollup(),
|
|
10
|
+
// openGenericLogPanel(), humaniseProjectName(),
|
|
11
11
|
// resolveProjectMeta(), shortStoryId(), truncate(), scoreClass(),
|
|
12
12
|
// qualityBar(), timeAgo(), showError(), showConfirm(),
|
|
13
|
-
// showStoryDetail(), showOutcomeModal(),
|
|
14
|
-
// renameProjectModal()
|
|
13
|
+
// showStoryDetail(), showOutcomeModal(), renameProjectModal()
|
|
15
14
|
// - mutable: selectedProject, lastOpenedLog, projectNameCache,
|
|
16
15
|
// projectInitialsCache
|
|
17
16
|
// All lookups are runtime-bound; load order only needs board-view.js to live
|
|
@@ -35,10 +34,13 @@ async function renderBoard() {
|
|
|
35
34
|
return;
|
|
36
35
|
}
|
|
37
36
|
|
|
38
|
-
const [stories, projectCost] = await Promise.all([
|
|
37
|
+
const [stories, projectCost, lanes] = await Promise.all([
|
|
39
38
|
api(`/api/projects/${encodeURIComponent(selectedProject)}/stories`),
|
|
40
39
|
api(`/api/projects/${encodeURIComponent(selectedProject)}/cost`).catch(() => null),
|
|
40
|
+
api(`/api/lanes/${encodeURIComponent(selectedProject)}`).catch((err) => ({ byStory: {}, error: String(err.message || err) })),
|
|
41
41
|
]);
|
|
42
|
+
lanesByStory = lanes.byStory || {};
|
|
43
|
+
if (lanes.error) console.warn(`[board] worktree lanes unavailable: ${lanes.error}`);
|
|
42
44
|
const costSummary = projectCost ? formatProjectCostSummary(projectCost) : null;
|
|
43
45
|
// Φ0-G (KJC-TSK-0525): aggregated cache-hit badge at the project
|
|
44
46
|
// header, rendered alongside the project cost chip.
|
|
@@ -63,11 +65,9 @@ async function renderBoard() {
|
|
|
63
65
|
// columna "Failed" — vuelven a Pending con result=fail y badge ✗.
|
|
64
66
|
// status=failed legacy se trata como pending (migración lazy).
|
|
65
67
|
const columns = {
|
|
66
|
-
pending: stories.filter((s) =>
|
|
67
|
-
|
|
68
|
-
),
|
|
69
|
-
running: stories.filter((s) => ['coding', 'reviewing'].includes(s.status)),
|
|
70
|
-
done: stories.filter((s) => s.status === 'done'),
|
|
68
|
+
pending: stories.filter((s) => kanbanColumnOf(s.status) === 'pending'),
|
|
69
|
+
running: stories.filter((s) => kanbanColumnOf(s.status) === 'running'),
|
|
70
|
+
done: stories.filter((s) => kanbanColumnOf(s.status) === 'done'),
|
|
71
71
|
};
|
|
72
72
|
|
|
73
73
|
if (stories.length === 0) {
|
|
@@ -75,15 +75,8 @@ async function renderBoard() {
|
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
// "
|
|
79
|
-
//
|
|
80
|
-
// running (to avoid accidentally launching a second pipeline over a
|
|
81
|
-
// live one). Replaces the old "Mark as certified" button — the
|
|
82
|
-
// intermediate "certified" state was noise from the user's POV.
|
|
83
|
-
const awaitingCount = columns.pending.length;
|
|
84
|
-
const runningCount = columns.running.length;
|
|
85
|
-
const canRun = Boolean(selectedProject) && awaitingCount > 0 && runningCount === 0;
|
|
86
|
-
const isRunning = runningCount > 0;
|
|
78
|
+
// KJC-TSK-1351: no "⚙ N running" badge in the header. It counted the HUs the board's kj run
|
|
79
|
+
// could launch; with kj run retired the Running column's own count says how many are in progress.
|
|
87
80
|
|
|
88
81
|
// KJC-TSK-0403: 3 canonical lanes. Failed eliminado — HUs con
|
|
89
82
|
// result=fail aparecen en Pending con badge ✗.
|
|
@@ -106,32 +99,13 @@ async function renderBoard() {
|
|
|
106
99
|
<span class="section-header__count" title="stories">${stories.length} ${maggleText('board.stories', 'stories')}</span>
|
|
107
100
|
${costSummary ? `<span class="section-header__cost" title="${esc(costSummary.tooltip)}">💵 ${esc(costSummary.label)}</span>` : ''}
|
|
108
101
|
${cacheSummary ? `<span class="section-header__cache" title="${esc(cacheSummary.tooltip)}">${esc(cacheSummary.label)}</span>` : ''}
|
|
109
|
-
${isRunning ? `
|
|
110
|
-
<button id="running-badge-btn" class="section-header__badge"
|
|
111
|
-
style="margin-left:auto;padding:4px 10px;font-size:0.8rem;background:var(--color-yellow,#eab308);color:#000;border-radius:var(--radius-sm);font-weight:600;border:none;cursor:pointer;"
|
|
112
|
-
title="Abrir el log de la HU en marcha">
|
|
113
|
-
⚙ ${runningCount} ${maggleText('board.running', 'running')}…
|
|
114
|
-
</button>
|
|
115
|
-
<button id="stop-run-btn" class="control-btn"
|
|
116
|
-
style="padding:4px 10px;font-size:0.8rem;background:var(--color-red,#ef4444);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer;font-weight:600;"
|
|
117
|
-
title="${maggleText('board.stopTitle', 'Abortar todos los kj run en marcha de este proyecto (SIGTERM con escalado a SIGKILL tras 5s)')}">
|
|
118
|
-
${maggleText('board.stop', '⏹ Stop')}
|
|
119
|
-
</button>
|
|
120
|
-
` : ''}
|
|
121
102
|
${lastOpenedLog ? `
|
|
122
103
|
<button class="control-btn" id="view-log-btn"
|
|
123
|
-
style="
|
|
104
|
+
style="margin-left:auto;padding:6px 12px;font-size:0.85rem;background:var(--bg-primary);border:1px solid var(--border);color:var(--text);border-radius:var(--radius-sm);cursor:pointer;"
|
|
124
105
|
title="Re-open the log of the most recent run/command">
|
|
125
106
|
${maggleText('board.viewLog', '📜 View log')}
|
|
126
107
|
</button>
|
|
127
108
|
` : ''}
|
|
128
|
-
${canRun ? `
|
|
129
|
-
<button class="control-btn" id="run-plan-btn"
|
|
130
|
-
style="margin-left:auto;padding:6px 14px;font-size:0.9rem;background:var(--color-green);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer;font-weight:600;"
|
|
131
|
-
title="${maggleText('board.runTitle', 'Launch kj run --plan over every plan in this project')}">
|
|
132
|
-
${maggleText('board.run', '▶ Run plan')} (${awaitingCount} ${maggleText('board.story', 'HU')}${awaitingCount === 1 ? '' : 's'})
|
|
133
|
-
</button>
|
|
134
|
-
` : ''}
|
|
135
109
|
</div>
|
|
136
110
|
<div id="preflight-panel" class="preflight-panel" style="margin:8px 0;">
|
|
137
111
|
<div class="preflight-panel__loading" style="font-size:0.8rem;color:var(--text-muted);padding:6px 10px;">
|
|
@@ -150,24 +124,13 @@ async function renderBoard() {
|
|
|
150
124
|
</button>
|
|
151
125
|
<div id="rag-results" style="flex-basis:100%;font-size:0.8rem;color:var(--text-muted);"></div>
|
|
152
126
|
</div>
|
|
127
|
+
<div id="shared-board-notice"></div>
|
|
153
128
|
<div id="plan-rollup-banner"></div>
|
|
154
129
|
<div class="kanban">
|
|
155
130
|
${visibleColumns.map((c) => renderKanbanColumn(c.title, c.cls, c.rows)).join('')}
|
|
156
131
|
</div>
|
|
157
132
|
`;
|
|
158
133
|
|
|
159
|
-
if (canRun) {
|
|
160
|
-
document.getElementById('run-plan-btn').addEventListener('click', async () => {
|
|
161
|
-
// Pre-run safety: ask preflight if anything risky is missing
|
|
162
|
-
// (no remote, dirty tree, missing agents, etc.). The modal
|
|
163
|
-
// shows blockers + warnings in plain Spanish and the user
|
|
164
|
-
// can cancel or proceed anyway. If everything is green we
|
|
165
|
-
// skip the modal and run straight away.
|
|
166
|
-
const ok = await confirmRunWithPreflight(selectedProject);
|
|
167
|
-
if (!ok) return;
|
|
168
|
-
await runProject(selectedProject);
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
134
|
// Preflight panel is rendered AFTER the kanban so we can fetch
|
|
172
135
|
// it without blocking the initial paint. Replaces a placeholder
|
|
173
136
|
// div in-place (no full re-render).
|
|
@@ -176,6 +139,8 @@ async function renderBoard() {
|
|
|
176
139
|
// project has a stamped outcome. Non-blocking — if it fails the
|
|
177
140
|
// user just doesn't see the banner.
|
|
178
141
|
renderPlanRollup(selectedProject).catch(() => {});
|
|
142
|
+
// KJC-TSK-1197: explain the shared board when this project mirrors one.
|
|
143
|
+
renderSharedBoardNotice(selectedProject).catch((err) => console.warn('shared board notice:', err.message));
|
|
179
144
|
const viewLogBtn = document.getElementById('view-log-btn');
|
|
180
145
|
if (viewLogBtn && lastOpenedLog) {
|
|
181
146
|
viewLogBtn.addEventListener('click', () => openGenericLogPanel({
|
|
@@ -184,76 +149,9 @@ async function renderBoard() {
|
|
|
184
149
|
tailUrl: lastOpenedLog.tailUrl,
|
|
185
150
|
}));
|
|
186
151
|
}
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
// session) `lastOpenedLog` is null, so the "📜 View log" button isn't
|
|
191
|
-
// even rendered. Then the user has no way to see the live output.
|
|
192
|
-
//
|
|
193
|
-
// For a single-HU run the log lives at:
|
|
194
|
-
// ~/.karajan/hu-board-runs/<planId>--hu-<localHuId>.log
|
|
195
|
-
// which the /api/runs/:commandId/log endpoint serves when commandId
|
|
196
|
-
// matches the basename. The composite id stored in SQLite is
|
|
197
|
-
// "<projectId>::<localHuId>", so we strip the prefix to derive
|
|
198
|
-
// localHuId and concatenate with plan_id (already on the story row).
|
|
199
|
-
const runningBadgeBtn = document.getElementById('running-badge-btn');
|
|
200
|
-
if (runningBadgeBtn) {
|
|
201
|
-
runningBadgeBtn.addEventListener('click', async () => {
|
|
202
|
-
const running = stories.find((s) => s.status === 'coding' || s.status === 'reviewing');
|
|
203
|
-
if (!running) return;
|
|
204
|
-
const planId = running.plan_id;
|
|
205
|
-
if (!planId) {
|
|
206
|
-
await showError('Esta HU no tiene plan_id asociado, no puedo localizar su log.', { title: 'Sin log' });
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
const localHuId = running.id.includes('::') ? running.id.split('::').pop() : running.id;
|
|
210
|
-
const logBasename = `${planId}--hu-${localHuId}`;
|
|
211
|
-
const tailUrl = (offset) => `/api/runs/${encodeURIComponent(logBasename)}/log?offset=${offset || 0}`;
|
|
212
|
-
lastOpenedLog = { id: logBasename, label: `HU ${localHuId}`, tailUrl };
|
|
213
|
-
openGenericLogPanel({ id: logBasename, label: `HU ${localHuId}`, tailUrl });
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// KJC-TSK-0396: botón ⏹ Stop. Abortar todos los kj run vivos del
|
|
218
|
-
// proyecto. Si hay múltiples planes corriendo (caso raro pero
|
|
219
|
-
// posible), itera sobre cada planId único de las HUs running.
|
|
220
|
-
const stopRunBtn = document.getElementById('stop-run-btn');
|
|
221
|
-
if (stopRunBtn) {
|
|
222
|
-
stopRunBtn.addEventListener('click', async () => {
|
|
223
|
-
const runningHus = stories.filter((s) => s.status === 'coding' || s.status === 'reviewing');
|
|
224
|
-
const planIds = [...new Set(runningHus.map((s) => s.plan_id).filter(Boolean))];
|
|
225
|
-
if (planIds.length === 0) {
|
|
226
|
-
await showError('No hay plan_id asociado a las HUs en marcha.', { title: 'Sin runs' });
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
const ok = await showConfirm(
|
|
230
|
-
`Esto matará ${planIds.length === 1 ? 'el proceso' : `los ${planIds.length} procesos`} del orquestador en marcha y dejará las HUs en curso (coding/reviewing) en pending para que puedas relanzarlas.\n\n¿Seguro?`,
|
|
231
|
-
{ title: 'Abortar kj run', okLabel: 'Stop', cancelLabel: 'Cancelar', destructive: true }
|
|
232
|
-
);
|
|
233
|
-
if (!ok) return;
|
|
234
|
-
const summaries = [];
|
|
235
|
-
for (const planId of planIds) {
|
|
236
|
-
try {
|
|
237
|
-
const res = await fetch(`/api/runs/${encodeURIComponent(planId)}/stop`, {
|
|
238
|
-
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}',
|
|
239
|
-
});
|
|
240
|
-
const body = await res.json();
|
|
241
|
-
if (!res.ok) summaries.push(`${planId}: error ${body.error || res.status}`);
|
|
242
|
-
else summaries.push(`${planId}: ${body.stopped} SIGTERM, ${body.killed} SIGKILL, ${body.hu_reset_count || 0} HUs → pending`);
|
|
243
|
-
} catch (err) {
|
|
244
|
-
summaries.push(`${planId}: ${err.message}`);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
await fetch('/api/sync', { method: 'POST' }).catch(() => {});
|
|
248
|
-
await renderBoard();
|
|
249
|
-
if (summaries.some((s) => s.includes('SIGKILL') && !s.startsWith('0 ')) && summaries.some((s) => /\b[1-9]\d* SIGKILL/.test(s))) {
|
|
250
|
-
await showError(
|
|
251
|
-
`Algún proceso no respondió a SIGTERM en 5s y fue forzado con SIGKILL:\n\n${summaries.join('\n')}`,
|
|
252
|
-
{ title: 'Forzado con SIGKILL' }
|
|
253
|
-
);
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
}
|
|
152
|
+
// KJC-TSK-1351: the "⚙ N running" badge is a count. It used to open the log of a
|
|
153
|
+
// kj run, and ⏹ Stop killed it: with kj run retired an HU in progress is someone
|
|
154
|
+
// working on it, with no process or log of the board's to open or stop.
|
|
257
155
|
|
|
258
156
|
// RAG search panel (KJC-PCS-0049 Step 8) — input + scope + render hits.
|
|
259
157
|
const ragInput = document.getElementById('rag-query');
|
|
@@ -315,6 +213,25 @@ function renderKanbanColumn(title, cssClass, stories) {
|
|
|
315
213
|
`;
|
|
316
214
|
}
|
|
317
215
|
|
|
216
|
+
// KJC-TSK-1313: story id → the worktree lanes working it (GET /api/lanes/:project).
|
|
217
|
+
let lanesByStory = {};
|
|
218
|
+
|
|
219
|
+
/** KJC-TSK-1316: the lane's PR as a link (https only); a closed or merged one says so. */
|
|
220
|
+
function renderLanePr(pr) {
|
|
221
|
+
if (!pr || !/^https:\/\//.test(String(pr.url))) return '';
|
|
222
|
+
const state = pr.state === 'OPEN' ? '' : ` (${esc(String(pr.state).toLowerCase())})`;
|
|
223
|
+
return ` · <a href="${esc(pr.url)}" target="_blank" rel="noopener noreferrer" onclick="event.stopPropagation()">PR #${esc(String(pr.number))}</a>${state}`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** One badge per lane working the card: its name, branch and commits ahead of the base. */
|
|
227
|
+
function renderLaneBadges(story) {
|
|
228
|
+
return (lanesByStory[story.id] || []).map((l) => {
|
|
229
|
+
const ahead = typeof l.ahead === 'number' ? ` · ${l.ahead} ↑` : '';
|
|
230
|
+
const title = `carril ${l.slug} · ${l.path}`;
|
|
231
|
+
return `<span class="story-card__lane" title="${esc(title)}">🌿 ${esc(l.slug)} (${esc(l.branch)})${ahead}${renderLanePr(l.pr)}</span>`;
|
|
232
|
+
}).join('');
|
|
233
|
+
}
|
|
234
|
+
|
|
318
235
|
/**
|
|
319
236
|
* Renders a story card for the kanban board.
|
|
320
237
|
* @param {object} story
|
|
@@ -345,36 +262,15 @@ function renderStoryCard(story) {
|
|
|
345
262
|
// this on the card so the user edits the HU before trying to run.
|
|
346
263
|
const missingTestContract = testCount === 0 && ['pending', 'certified'].includes(story.status);
|
|
347
264
|
|
|
348
|
-
//
|
|
349
|
-
// - the HU is in a re-runnable lifecycle state (certified / failed /
|
|
350
|
-
// pending / done — KJC-TSK-0394 step 2 adds `done` so the user can
|
|
351
|
-
// replay a HU whose `result` was fail or partial without having to
|
|
352
|
-
// "reset to pending" first) AND
|
|
353
|
-
// - it has at least one acceptance test (otherwise the run would
|
|
354
|
-
// refuse) AND
|
|
355
|
-
// - it isn't currently coding/reviewing AND
|
|
356
|
-
// - KJC-BUG-0048: it has no unresolved blocked_by deps. The card
|
|
357
|
-
// already shows "⏳ waits for: ..." below the title; the ▶ button
|
|
358
|
-
// must match that gating or the user can launch a HU whose deps
|
|
359
|
-
// don't exist yet, hitting the missing-prereq path at runtime.
|
|
360
|
-
const canRunHu = ['certified', 'failed', 'pending', 'done'].includes(story.status)
|
|
361
|
-
&& testCount > 0
|
|
362
|
-
&& !['coding', 'reviewing'].includes(story.status)
|
|
363
|
-
&& blockedBy.length === 0;
|
|
364
|
-
|
|
265
|
+
// KJC-TSK-1319: the per-HU ▶ launched kj run, retired in 5.0.
|
|
365
266
|
return `
|
|
366
267
|
<div class="story-card" data-story-id="${esc(story.id)}" data-status="${esc(story.status || 'pending')}" onclick="showStoryDetail('${esc(story.id)}')">
|
|
367
268
|
<div class="story-card__id" title="${esc(story.id)}">
|
|
368
269
|
${esc(shortId)}
|
|
369
|
-
${canRunHu ? `
|
|
370
|
-
<button class="story-card__run-btn"
|
|
371
|
-
style="float:right;padding:1px 6px;font-size:0.7rem;background:var(--color-green);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer;"
|
|
372
|
-
title="Lanzar solo esta HU"
|
|
373
|
-
onclick="event.stopPropagation(); window.runSingleHuFromCard('${esc(story.id)}', '${esc(title.replace(/'/g, '''))}');">▶</button>
|
|
374
|
-
` : ''}
|
|
375
270
|
</div>
|
|
376
271
|
<div class="story-card__title">${esc(truncate(title, 100))}</div>
|
|
377
272
|
<div class="story-card__meta" style="gap:10px">
|
|
273
|
+
${renderLaneBadges(story)}
|
|
378
274
|
${acCount > 0 ? `<span title="${acCount} acceptance criteria">📋 ${acCount} AC${acCount === 1 ? '' : 's'}</span>` : ''}
|
|
379
275
|
${testCount > 0 ? `<span title="${testCount} acceptance tests declared">✅ ${testCount} test${testCount === 1 ? '' : 's'}</span>` : ''}
|
|
380
276
|
${story.spec_section ? `<span title="Implements SPEC ${esc(story.spec_section)}" style="font-family:var(--font-mono, monospace)">📖 §${esc(story.spec_section)}</span>` : ''}
|
|
@@ -415,11 +311,20 @@ function renderStoryCard(story) {
|
|
|
415
311
|
${renderResultBadge(computeEffectiveResult(story))}
|
|
416
312
|
<span class="story-card__time">${timeAgo(story.updated_at)}</span>
|
|
417
313
|
${renderOutcomeChip(story.outcome)}
|
|
314
|
+
${renderPlayButton(story)}
|
|
418
315
|
</div>
|
|
419
316
|
</div>
|
|
420
317
|
`;
|
|
421
318
|
}
|
|
422
319
|
|
|
320
|
+
// KJC-TSK-1324: ▶ starts a pending card without a lane: kj opens its lane and the panel coder
|
|
321
|
+
// writes it there (kj hu start --code). Review and commit stay with the person.
|
|
322
|
+
function renderPlayButton(story) {
|
|
323
|
+
if (story.status !== 'pending' || (lanesByStory[story.id] || []).length > 0) return '';
|
|
324
|
+
const hint = 'Start: open its lane and have your coder write it';
|
|
325
|
+
return `<button type="button" class="story-card__play" title="${hint}" aria-label="${esc(`${hint} (${story.id})`)}" onclick="startCardLane('${esc(story.id)}', event)">▶</button>`;
|
|
326
|
+
}
|
|
327
|
+
|
|
423
328
|
/**
|
|
424
329
|
* KJC-TSK-0394 step 5: result "efectivo" para la UI. Sobre el campo
|
|
425
330
|
* persistido `story.result` (si existe), o inferido del status legacy
|