karajan-code 3.1.0 → 3.3.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 (68) hide show
  1. package/README.md +12 -4
  2. package/package.json +6 -1
  3. package/packages/hu-board/package.json +3 -1
  4. package/packages/hu-board/public/index.html +1 -0
  5. package/packages/hu-board/public/styles.css +32 -0
  6. package/packages/hu-board/public/utils/board-view.js +21 -1
  7. package/packages/hu-board/public/utils/formatters.js +68 -0
  8. package/packages/hu-board/public/wiki.html +32 -0
  9. package/packages/hu-board/public/wiki.js +53 -0
  10. package/packages/hu-board/src/db.js +137 -0
  11. package/packages/hu-board/src/format.js +124 -0
  12. package/packages/hu-board/src/plan-mutations.js +4 -4
  13. package/packages/hu-board/src/routes/api.js +16 -1
  14. package/packages/hu-board/src/routes/rag.js +1 -1
  15. package/packages/hu-board/src/routes/wiki.js +43 -0
  16. package/packages/hu-board/src/run-tracker.js +1 -1
  17. package/packages/hu-board/src/server.js +4 -2
  18. package/packages/hu-board/src/sync.js +1 -1
  19. package/scripts/git-hooks/post-merge +13 -2
  20. package/src/agents/aider-agent.js +42 -1
  21. package/src/agents/claude-agent.js +14 -3
  22. package/src/agents/codex-agent.js +44 -7
  23. package/src/agents/gemini-agent.js +37 -1
  24. package/src/agents/opencode-agent.js +34 -1
  25. package/src/audit/ai-slop-findings.js +102 -0
  26. package/src/audit/deterministic-summary.js +18 -0
  27. package/src/brain/standby-scheduler.js +10 -92
  28. package/src/brain/standby-store.js +12 -168
  29. package/src/checks/ai-trash.js +34 -0
  30. package/src/checks/qmd.js +41 -0
  31. package/src/checks/squeezr.js +36 -0
  32. package/src/cli/register-meta.js +21 -0
  33. package/src/cli/register-pipeline.js +3 -0
  34. package/src/commands/audit.js +6 -4
  35. package/src/commands/board.js +34 -0
  36. package/src/commands/doctor.js +6 -0
  37. package/src/commands/init.js +73 -8
  38. package/src/commands/qmd.js +86 -0
  39. package/src/commands/resume.js +6 -45
  40. package/src/commands/run.js +6 -50
  41. package/src/git/hu-snapshot.js +10 -105
  42. package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +27 -0
  43. package/src/orchestrator/drivers/run-hu-batch.js +5 -1
  44. package/src/orchestrator/flow-runner.js +10 -2
  45. package/src/orchestrator/hu-sub-pipeline.js +82 -8
  46. package/src/plan/plan-hu-ops.js +16 -338
  47. package/src/plan/plan-id.js +4 -45
  48. package/src/plan/plan-validation.js +4 -82
  49. package/src/rag/vec-store.js +17 -253
  50. package/src/roles/audit-role.js +11 -1
  51. package/src/session/journal/summary-writer.js +55 -1
  52. package/src/utils/ai-trash.js +48 -0
  53. package/src/utils/atomic-write.js +6 -43
  54. package/src/utils/budget.js +38 -9
  55. package/src/utils/cli-ask-question.js +76 -0
  56. package/src/utils/doc-links.js +2 -0
  57. package/src/utils/os-detect.js +10 -0
  58. package/src/utils/paths.js +14 -123
  59. package/src/utils/port-check.js +6 -45
  60. package/src/utils/process.js +4 -188
  61. package/src/utils/qmd-collection.js +72 -0
  62. package/src/utils/qmd-detect.js +18 -0
  63. package/src/utils/qmd-install.js +37 -0
  64. package/src/utils/run-registry.js +9 -116
  65. package/src/utils/shared-paths.js +12 -41
  66. package/src/utils/squeezr-detect.js +18 -0
  67. package/src/utils/squeezr-install.js +37 -0
  68. package/src/utils/telemetry.js +43 -0
@@ -1,41 +1,12 @@
1
- /**
2
- * Team-shared HU Board paths — KJC-PRP-0002 / v2.31.0 PR1.
3
- *
4
- * The shared root lives INSIDE the project, not in `~/.karajan/`, so a team
5
- * can commit it to git (or sync via Drive/SyncThing) and share plans + HU
6
- * stories without each member regenerating them locally.
7
- *
8
- * <projectDir>/.karajan-shared/
9
- * plans/<planId>.json ← `kj plan share <planId>` copies here
10
- * hu-stories/<huId>.md ← reserved for PR2+ (board reads it)
11
- *
12
- * Keep the dir name in lockstep with the HU Board sync layer
13
- * (`packages/hu-board/src/sync.js`) — diverging the constant breaks the
14
- * watcher silently. One source of truth lives here.
15
- */
16
-
17
- import path from "node:path";
18
-
19
- export const SHARED_DIR_NAME = ".karajan-shared";
20
-
21
- export function getSharedRoot(projectDir) {
22
- return path.join(projectDir, SHARED_DIR_NAME);
23
- }
24
-
25
- export function getSharedPlansDir(projectDir) {
26
- return path.join(getSharedRoot(projectDir), "plans");
27
- }
28
-
29
- export function getSharedHuStoriesDir(projectDir) {
30
- return path.join(getSharedRoot(projectDir), "hu-stories");
31
- }
32
-
33
- /**
34
- * Quick check used by callers that decide whether a plan came from local
35
- * (`~/.karajan/plans/`) or from the shared dir. The HU Board uses this to
36
- * stamp a `source` column on each record so the UI can render a 🌐 badge.
37
- */
38
- export function isSharedPath(p) {
39
- if (!p) return false;
40
- return p.split(path.sep).includes(SHARED_DIR_NAME);
41
- }
1
+ // Shim — the real implementation now lives in @karajan/core/shared-paths.
2
+ // Kept here so CLI callers (src/**/*.js) keep importing
3
+ // "../utils/shared-paths.js" without churn during KJC-TSK-0511 (decouple
4
+ // @karajan/hu-board). Will be retired once every consumer migrates to
5
+ // the @karajan/core import.
6
+ export {
7
+ SHARED_DIR_NAME,
8
+ getSharedRoot,
9
+ getSharedPlansDir,
10
+ getSharedHuStoriesDir,
11
+ isSharedPath,
12
+ } from "@karajan/core/shared-paths";
@@ -0,0 +1,18 @@
1
+ import { runCommand } from "./process.js";
2
+
3
+ /**
4
+ * Detect whether Squeezr is installed and available.
5
+ * @returns {Promise<{ available: boolean, version: string|null }>}
6
+ */
7
+ export async function detectSqueezr() {
8
+ try {
9
+ const result = await runCommand("squeezr", ["--version"]);
10
+ if (result.exitCode === 0) {
11
+ const version = (result.stdout || "").trim() || null;
12
+ return { available: true, version };
13
+ }
14
+ return { available: false, version: null };
15
+ } catch { /* squeezr binary not found */
16
+ return { available: false, version: null };
17
+ }
18
+ }
@@ -0,0 +1,37 @@
1
+ import { runCommand } from "./process.js";
2
+ import { getInstallCommand } from "./os-detect.js";
3
+ import { detectSqueezr } from "./squeezr-detect.js";
4
+
5
+ /**
6
+ * Install Squeezr using the OS-appropriate command.
7
+ * @param {object} logger - Logger with info/warn methods
8
+ * @returns {Promise<{ ok: boolean, version: string|null, error: string|null }>}
9
+ */
10
+ export async function installSqueezr(logger) {
11
+ const command = getInstallCommand("squeezr");
12
+
13
+ logger.info("Installing Squeezr...");
14
+
15
+ try {
16
+ const result = await runCommand("sh", ["-c", command], { timeout: 120_000 });
17
+
18
+ if (result.exitCode !== 0) {
19
+ const error = (result.stderr || "").trim() || `exit code ${result.exitCode}`;
20
+ logger.warn(`Squeezr install failed: ${error}`);
21
+ return { ok: false, version: null, error };
22
+ }
23
+
24
+ const check = await detectSqueezr();
25
+ if (check.available) {
26
+ logger.info(`Squeezr ${check.version || ""} installed successfully.`);
27
+ return { ok: true, version: check.version, error: null };
28
+ }
29
+
30
+ logger.warn("Squeezr install command succeeded but squeezr binary not found in PATH.");
31
+ return { ok: false, version: null, error: "Binary not found after install" };
32
+ } catch (err) {
33
+ const error = err.message || String(err);
34
+ logger.warn(`Squeezr install failed: ${error}`);
35
+ return { ok: false, version: null, error };
36
+ }
37
+ }
@@ -26,6 +26,49 @@ export function buildTelemetryPayload(eventName, data = {}) {
26
26
  };
27
27
  }
28
28
 
29
+ /**
30
+ * Φ0-H (KJC-TSK-0526): aggregate cache-hit ratio per role from the budget
31
+ * summary the orchestrator already builds. Returns `{ cached_pct_coder,
32
+ * cached_pct_reviewer, cached_pct_total }` with values rounded to one
33
+ * decimal, or `null` per slot when there is no measurable input (rather
34
+ * than `0`, so the backend can distinguish "no cache" from "no signal").
35
+ *
36
+ * Provider-agnostic by design: the BudgetTracker entries already
37
+ * normalise `cached_tokens` for Anthropic (`cache_read_input_tokens`),
38
+ * OpenAI / Codex (`prompt_tokens_details.cached_tokens`), Gemini
39
+ * (`cachedContentTokenCount`) and the LiteLLM-routed providers
40
+ * (aider, opencode), so this helper does not need to know which
41
+ * provider produced the run.
42
+ *
43
+ * @param {{ breakdown_by_role?: object, total_cached_tokens?: number } | null | undefined} summary
44
+ * @returns {{ cached_pct_coder: number|null, cached_pct_reviewer: number|null, cached_pct_total: number|null }}
45
+ */
46
+ export function computeCachedPct(summary) {
47
+ const empty = { cached_pct_coder: null, cached_pct_reviewer: null, cached_pct_total: null };
48
+ if (!summary || typeof summary !== "object") return empty;
49
+ const ratio = (cached, tokensIn) => {
50
+ const c = Number(cached);
51
+ const t = Number(tokensIn);
52
+ if (!Number.isFinite(c) || !Number.isFinite(t) || t <= 0 || c < 0) return null;
53
+ return Math.round((c / t) * 1000) / 10;
54
+ };
55
+ const byRole = summary.breakdown_by_role || {};
56
+ const coder = byRole.coder || null;
57
+ const reviewer = byRole.reviewer || null;
58
+ let totalCached = 0;
59
+ let totalIn = 0;
60
+ for (const role of Object.keys(byRole)) {
61
+ const r = byRole[role];
62
+ totalCached += Number(r?.cached_tokens || 0);
63
+ totalIn += Number(r?.tokens_in || 0);
64
+ }
65
+ return {
66
+ cached_pct_coder: coder ? ratio(coder.cached_tokens, coder.tokens_in) : null,
67
+ cached_pct_reviewer: reviewer ? ratio(reviewer.cached_tokens, reviewer.tokens_in) : null,
68
+ cached_pct_total: ratio(totalCached, totalIn)
69
+ };
70
+ }
71
+
29
72
  /**
30
73
  * Send an anonymous telemetry event. Non-blocking, fire-and-forget.
31
74
  * Never throws, never blocks the pipeline.