karajan-code 3.0.0 → 3.2.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.
Files changed (104) hide show
  1. package/README.md +14 -6
  2. package/docs/README.es.md +1 -1
  3. package/package.json +8 -2
  4. package/packages/hu-board/package.json +3 -1
  5. package/packages/hu-board/public/app.js +81 -4025
  6. package/packages/hu-board/public/index.html +21 -0
  7. package/packages/hu-board/public/styles.css +32 -0
  8. package/packages/hu-board/public/utils/api.js +139 -0
  9. package/packages/hu-board/public/utils/board-view.js +462 -0
  10. package/packages/hu-board/public/utils/command-launcher.js +213 -0
  11. package/packages/hu-board/public/utils/config-editor.js +181 -0
  12. package/packages/hu-board/public/utils/dashboard-view.js +103 -0
  13. package/packages/hu-board/public/utils/formatters.js +273 -0
  14. package/packages/hu-board/public/utils/graph-view.js +216 -0
  15. package/packages/hu-board/public/utils/hu-actions.js +224 -0
  16. package/packages/hu-board/public/utils/init-listeners.js +180 -0
  17. package/packages/hu-board/public/utils/log-panel.js +222 -0
  18. package/packages/hu-board/public/utils/modals.js +290 -0
  19. package/packages/hu-board/public/utils/plan-rollup-view.js +53 -0
  20. package/packages/hu-board/public/utils/preflight-view.js +89 -0
  21. package/packages/hu-board/public/utils/project-actions.js +143 -0
  22. package/packages/hu-board/public/utils/project-picker-view.js +139 -0
  23. package/packages/hu-board/public/utils/run-launcher.js +169 -0
  24. package/packages/hu-board/public/utils/server-push.js +252 -0
  25. package/packages/hu-board/public/utils/sessions-view.js +199 -0
  26. package/packages/hu-board/public/utils/story-detail-view.js +324 -0
  27. package/packages/hu-board/public/utils/story-edit-form.js +271 -0
  28. package/packages/hu-board/public/wiki.html +32 -0
  29. package/packages/hu-board/public/wiki.js +53 -0
  30. package/packages/hu-board/src/config-yaml.js +14 -0
  31. package/packages/hu-board/src/db.js +93 -2
  32. package/packages/hu-board/src/format.js +79 -0
  33. package/packages/hu-board/src/plan-mutations.js +4 -4
  34. package/packages/hu-board/src/routes/api.js +43 -10
  35. package/packages/hu-board/src/routes/rag.js +1 -1
  36. package/packages/hu-board/src/routes/wiki.js +43 -0
  37. package/packages/hu-board/src/run-tracker.js +1 -1
  38. package/packages/hu-board/src/server.js +4 -2
  39. package/packages/hu-board/src/sync.js +1 -1
  40. package/scripts/audit-test-diet.mjs +151 -0
  41. package/scripts/git-hooks/post-merge +13 -2
  42. package/src/audit/ai-slop-findings.js +102 -0
  43. package/src/audit/deterministic-summary.js +18 -0
  44. package/src/audit/harness-scorecard.js +52 -3
  45. package/src/audit/harness-section.js +6 -2
  46. package/src/brain/standby-scheduler.js +10 -92
  47. package/src/brain/standby-store.js +12 -168
  48. package/src/checks/ai-trash.js +34 -0
  49. package/src/checks/qmd.js +41 -0
  50. package/src/checks/runner.js +10 -0
  51. package/src/checks/squeezr.js +36 -0
  52. package/src/cli/register-meta.js +36 -2
  53. package/src/cli/register-pipeline.js +3 -0
  54. package/src/cli/register-plan.js +10 -1
  55. package/src/commands/audit.js +6 -4
  56. package/src/commands/board.js +34 -0
  57. package/src/commands/clean.js +62 -0
  58. package/src/commands/doctor.js +6 -0
  59. package/src/commands/init.js +73 -8
  60. package/src/commands/plan/generate.js +9 -3
  61. package/src/commands/qmd.js +86 -0
  62. package/src/commands/resume.js +6 -45
  63. package/src/commands/run.js +6 -50
  64. package/src/commands/sync.js +30 -2
  65. package/src/config/defaults.js +5 -0
  66. package/src/config/schema.js +2 -0
  67. package/src/git/hu-snapshot.js +10 -105
  68. package/src/orchestrator/config-init.js +1 -0
  69. package/src/orchestrator/drivers/iteration-phases/quality-gates.js +23 -0
  70. package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +16 -0
  71. package/src/orchestrator/drivers/run-hu-batch.js +5 -1
  72. package/src/orchestrator/hu-sub-pipeline.js +52 -8
  73. package/src/orchestrator/stages/coder-stage.js +8 -2
  74. package/src/orchestrator/stages/tdd-discipline-stage.js +56 -0
  75. package/src/orchestrator/stages/tool-judge-stage.js +53 -0
  76. package/src/plan/plan-hu-ops.js +16 -338
  77. package/src/plan/plan-id.js +4 -45
  78. package/src/plan/plan-validation.js +4 -82
  79. package/src/prompts/tool-correctness-judge.js +31 -0
  80. package/src/rag/vec-store.js +17 -253
  81. package/src/review/git-stash-helper.js +104 -0
  82. package/src/review/tdd-discipline.js +90 -0
  83. package/src/roles/audit-role.js +11 -1
  84. package/src/roles/tool-correctness-judge-role.js +78 -0
  85. package/src/session/mutators.js +4 -0
  86. package/src/sync/apply-canvas-patch.js +55 -0
  87. package/src/utils/ai-trash.js +48 -0
  88. package/src/utils/atomic-write.js +6 -43
  89. package/src/utils/cli-ask-question.js +76 -0
  90. package/src/utils/doc-links.js +2 -0
  91. package/src/utils/os-detect.js +10 -0
  92. package/src/utils/paths.js +14 -111
  93. package/src/utils/port-check.js +6 -45
  94. package/src/utils/process.js +4 -188
  95. package/src/utils/qmd-collection.js +72 -0
  96. package/src/utils/qmd-detect.js +18 -0
  97. package/src/utils/qmd-install.js +37 -0
  98. package/src/utils/repo-cleaner.js +79 -0
  99. package/src/utils/run-registry.js +9 -116
  100. package/src/utils/shared-paths.js +12 -41
  101. package/src/utils/squeezr-detect.js +18 -0
  102. package/src/utils/squeezr-install.js +37 -0
  103. package/src/utils/tool-call-extractor.js +56 -0
  104. package/src/utils/vector-store-orphans.js +89 -0
@@ -26,6 +26,7 @@
26
26
  <button class="nav-btn" data-view="sessions" title="Every kj run that has touched the board, newest first. Filter by project; click a session to inspect its iterations, commits and checkpoints.">Sessions</button>
27
27
  <a class="nav-btn" href="/pipeline.html" title="Live observability of pipeline runs (Karajan v2.7+): stage timings, agent calls, decision logs.">Pipeline</a>
28
28
  <a class="nav-btn" href="/rag.html" title="RAG retrieval-quality dashboard: chunk counts, embedder provider, db size.">RAG</a>
29
+ <a class="nav-btn" href="/wiki.html" title="Search the per-project QMD semantic wiki (docs, plans, reviews).">Wiki</a>
29
30
  <select class="project-select" id="project-select">
30
31
  <option value="">All Projects</option>
31
32
  </select>
@@ -51,6 +52,26 @@
51
52
  <div class="modal" id="modal-content"></div>
52
53
  </div>
53
54
 
55
+ <script src="/utils/formatters.js"></script>
56
+ <script src="/utils/modals.js"></script>
57
+ <script src="/utils/api.js"></script>
58
+ <script src="/utils/sessions-view.js"></script>
59
+ <script src="/utils/board-view.js"></script>
60
+ <script src="/utils/dashboard-view.js"></script>
61
+ <script src="/utils/graph-view.js"></script>
62
+ <script src="/utils/project-picker-view.js"></script>
63
+ <script src="/utils/story-detail-view.js"></script>
64
+ <script src="/utils/preflight-view.js"></script>
65
+ <script src="/utils/plan-rollup-view.js"></script>
66
+ <script src="/utils/log-panel.js"></script>
67
+ <script src="/utils/hu-actions.js"></script>
68
+ <script src="/utils/project-actions.js"></script>
69
+ <script src="/utils/story-edit-form.js"></script>
70
+ <script src="/utils/config-editor.js"></script>
71
+ <script src="/utils/command-launcher.js"></script>
72
+ <script src="/utils/run-launcher.js"></script>
73
+ <script src="/utils/server-push.js"></script>
54
74
  <script src="/app.js"></script>
75
+ <script src="/utils/init-listeners.js"></script>
55
76
  </body>
56
77
  </html>
@@ -244,6 +244,17 @@ a:hover {
244
244
  font-family: var(--font-mono);
245
245
  }
246
246
 
247
+ .section-header__cost {
248
+ font-size: 0.8rem;
249
+ color: var(--text-muted);
250
+ font-family: var(--font-mono);
251
+ padding: 2px 8px;
252
+ border: 1px solid var(--border);
253
+ border-radius: var(--radius-sm);
254
+ background: var(--bg-secondary, transparent);
255
+ cursor: help;
256
+ }
257
+
247
258
  /* ---- Kanban Board ---- */
248
259
  .kanban {
249
260
  display: grid;
@@ -341,6 +352,12 @@ a:hover {
341
352
  .story-card__score--ok { color: var(--color-yellow); }
342
353
  .story-card__score--bad { color: var(--color-red); }
343
354
 
355
+ .story-card__cost {
356
+ font-family: var(--font-mono);
357
+ font-size: 0.75rem;
358
+ color: var(--text-muted);
359
+ }
360
+
344
361
  .quality-bar {
345
362
  display: inline-flex;
346
363
  gap: 1px;
@@ -939,3 +956,18 @@ body.scoped-mode .header__nav a[href="/pipeline.html"] {
939
956
  .rag-bar__label { color: var(--text-secondary, #cbd5e1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
940
957
  .rag-bar__fill { display: block; height: 14px; background: linear-gradient(90deg, #7c3aed, #a78bfa); border-radius: 3px; }
941
958
  .rag-bar__value { text-align: right; color: var(--text-primary, #f8fafc); font-variant-numeric: tabular-nums; }
959
+
960
+ /* KJC-TSK-0508 — Wiki panel (QMD semantic search) */
961
+ .wiki-search { padding: 1rem; }
962
+ .wiki-form { display: grid; grid-template-columns: 1fr 220px auto auto; gap: .5rem; align-items: center; }
963
+ .wiki-form input[type="search"], .wiki-form input[type="text"] { padding: .5rem .75rem; border-radius: 6px; border: 1px solid var(--border, #334155); background: var(--bg-secondary, #1e1e2e); color: var(--text-primary, #f8fafc); font-size: .9rem; }
964
+ .wiki-fast { display: flex; align-items: center; gap: .35rem; font-size: .85rem; color: var(--text-secondary, #94a3b8); }
965
+ .wiki-hint { margin: .5rem 0 0; font-size: .8rem; color: var(--text-secondary, #94a3b8); font-style: italic; }
966
+ .wiki-results { display: grid; gap: .75rem; padding: 0 1rem 1rem; }
967
+ .wiki-hit { background: var(--bg-secondary, #1e1e2e); border-radius: 8px; padding: .9rem 1rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
968
+ .wiki-hit__head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: .4rem; }
969
+ .wiki-hit__src { font-family: ui-monospace, monospace; font-size: .85rem; color: #a78bfa; }
970
+ .wiki-hit__score { font-size: .75rem; color: var(--text-secondary, #94a3b8); font-variant-numeric: tabular-nums; }
971
+ .wiki-hit__snip { margin: 0; font-family: ui-monospace, monospace; font-size: .82rem; line-height: 1.45; color: var(--text-primary, #f8fafc); white-space: pre-wrap; word-break: break-word; }
972
+ .wiki-code { background: #0f172a; color: #e2e8f0; padding: .5rem .75rem; border-radius: 6px; font-family: ui-monospace, monospace; font-size: .85rem; margin: .5rem 0; }
973
+ @media (max-width: 720px) { .wiki-form { grid-template-columns: 1fr; } }
@@ -0,0 +1,139 @@
1
+ // KJC-TSK-0501 — API + polling helpers extracted from app.js (step 3/8).
2
+ // Loaded as a classic script before app.js (after modals.js) — all
3
+ // top-level declarations hoist to global scope so app.js consumes them
4
+ // transparently.
5
+ //
6
+ // Contents:
7
+ // - api(): thin JSON fetch wrapper used everywhere in the UI.
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
+ // - Server-restart detector: pollServerVersion() + __versionBaseline +
13
+ // window.forceRefresh — KJC-TSK-0379.
14
+ //
15
+ // Dependencies: `esc()` from formatters.js (already global).
16
+ // `setInterval(pollServerVersion, 30_000)` + initial call fire at the
17
+ // bottom of the file so the detector starts as soon as the script loads.
18
+
19
+ async function api(path) {
20
+ const res = await fetch(path);
21
+ if (!res.ok) throw new Error(`API error: ${res.status}`);
22
+ return res.json();
23
+ }
24
+
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 &lt;id&gt;</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
+ /**
97
+ * Trigger a full re-scan of disk data (hu-stories + sessions).
98
+ * Called on page load and via the sync button.
99
+ */
100
+ async function triggerSync() {
101
+ try {
102
+ await fetch('/api/sync', { method: 'POST' });
103
+ } catch { /* ignore — board may not support sync yet */ }
104
+ }
105
+
106
+ // --- Server-restart detector (KJC-TSK-0379) ---------------------------
107
+ // Poll /api/version every 30s. First response anchors the baseline; if
108
+ // `boot_time` later differs, the server restarted (likely after a `kj
109
+ // board start`) and the client reloads to pick up any new HTML/JS
110
+ // served with `Cache-Control: no-store`. Avoids the "I killed the
111
+ // server but the browser keeps showing stale UI" pain.
112
+ let __versionBaseline = null;
113
+ async function pollServerVersion() {
114
+ try {
115
+ const res = await api('/version');
116
+ if (!__versionBaseline) { __versionBaseline = res; return; }
117
+ if (res.boot_time !== __versionBaseline.boot_time) {
118
+ console.info('[hu-board] server restart detected, reloading...');
119
+ window.forceRefresh();
120
+ }
121
+ } catch (_) { /* network blip; try next tick */ }
122
+ }
123
+ setInterval(pollServerVersion, 30_000);
124
+ pollServerVersion();
125
+
126
+ // Manual escape hatch: bound to the 🔄 button in the header. Wipes all
127
+ // caches the browser may be holding (Cache API + sessionStorage) and
128
+ // hard-reloads. Useful when the user thinks something is wrong even
129
+ // though the version-poll hasn't fired.
130
+ window.forceRefresh = async function forceRefresh() {
131
+ try {
132
+ if ('caches' in window) {
133
+ const keys = await caches.keys();
134
+ await Promise.all(keys.map((k) => caches.delete(k)));
135
+ }
136
+ sessionStorage.clear();
137
+ } catch (_) { /* best effort */ }
138
+ window.location.reload();
139
+ };