open-multi-agent-kit 0.78.6 → 0.78.7

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 (169) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +26 -0
  3. package/MATURITY.md +2 -2
  4. package/README.md +19 -4
  5. package/dist/brand/night-city.theme.json +80 -0
  6. package/dist/brand/palette.d.ts +73 -177
  7. package/dist/brand/palette.js +115 -39
  8. package/dist/brand/rust-forge.theme.json +216 -0
  9. package/dist/brand/theme-compiled.d.ts +61 -0
  10. package/dist/brand/theme-compiled.js +241 -0
  11. package/dist/brand/theme.js +50 -37
  12. package/dist/cli/release-promotion-gate.d.ts +8 -1
  13. package/dist/cli/release-promotion-gate.js +20 -9
  14. package/dist/cli/root.d.ts +1 -0
  15. package/dist/cli/root.js +2 -1
  16. package/dist/cli/theme/index.d.ts +11 -2
  17. package/dist/cli/theme/index.js +6 -1
  18. package/dist/cli/theme/oklab-quantize.d.ts +29 -0
  19. package/dist/cli/theme/oklab-quantize.js +105 -0
  20. package/dist/cli/theme/render-table.d.ts +59 -0
  21. package/dist/cli/theme/render-table.js +112 -0
  22. package/dist/cli/theme/status-frame.d.ts +10 -0
  23. package/dist/cli/theme/status-frame.js +22 -0
  24. package/dist/cli/theme/terminal-capability.d.ts +6 -1
  25. package/dist/cli/theme/terminal-capability.js +20 -4
  26. package/dist/cli/theme/theme-doc.d.ts +22 -0
  27. package/dist/cli/theme/theme-doc.js +97 -0
  28. package/dist/cli/theme/theme-registry.js +39 -18
  29. package/dist/cli/theme/tier-explain.d.ts +21 -0
  30. package/dist/cli/theme/tier-explain.js +60 -0
  31. package/dist/cli/ui/green-rain-renderer.js +3 -2
  32. package/dist/cli/ui/neon-grid-renderer.js +4 -3
  33. package/dist/cli/ui/rust-forge-renderer.js +17 -3
  34. package/dist/cli/ui/system24-renderer.d.ts +18 -1
  35. package/dist/cli/ui/system24-renderer.js +39 -16
  36. package/dist/cli/v2/chat-repl.js +3 -1
  37. package/dist/cli/v2/cli-v2-skeleton.d.ts +6 -0
  38. package/dist/cli/v2/cli-v2-skeleton.js +121 -9
  39. package/dist/cli/v2/interactive-prompt.d.ts +1 -0
  40. package/dist/cli/v2/interactive-prompt.js +6 -2
  41. package/dist/commands/chat/core.js +2 -2
  42. package/dist/commands/chat/runtime.js +2 -0
  43. package/dist/commands/chat/utils.js +12 -4
  44. package/dist/commands/cockpit/render.js +102 -64
  45. package/dist/commands/cockpit/utils.d.ts +2 -0
  46. package/dist/commands/design.js +18 -16
  47. package/dist/commands/doctor/checks.d.ts +0 -1
  48. package/dist/commands/doctor/checks.js +1 -36
  49. package/dist/commands/doctor/report.js +0 -11
  50. package/dist/commands/init/content.d.ts +1 -1
  51. package/dist/commands/init/content.js +3 -11
  52. package/dist/commands/init.js +2 -2
  53. package/dist/commands/mcp/config.d.ts +32 -0
  54. package/dist/commands/mcp/config.js +264 -0
  55. package/dist/commands/mcp/doctor-fix.d.ts +12 -0
  56. package/dist/commands/mcp/doctor-fix.js +261 -0
  57. package/dist/commands/mcp/doctor.d.ts +55 -0
  58. package/dist/commands/mcp/doctor.js +401 -0
  59. package/dist/commands/mcp/list.d.ts +1 -0
  60. package/dist/commands/mcp/list.js +56 -0
  61. package/dist/commands/mcp/shared.d.ts +47 -0
  62. package/dist/commands/mcp/shared.js +192 -0
  63. package/dist/commands/mcp/test.d.ts +5 -0
  64. package/dist/commands/mcp/test.js +457 -0
  65. package/dist/commands/mcp.d.ts +8 -104
  66. package/dist/commands/mcp.js +5 -1603
  67. package/dist/contracts/proof.d.ts +1 -1
  68. package/dist/goal/control-loop.d.ts +15 -0
  69. package/dist/goal/control-loop.js +39 -5
  70. package/dist/hud/render.d.ts +2 -0
  71. package/dist/hud/render.js +7 -1
  72. package/dist/mcp/secret-scanner.d.ts +6 -0
  73. package/dist/mcp/secret-scanner.js +44 -11
  74. package/dist/memory/graph-delta-log.d.ts +137 -0
  75. package/dist/memory/graph-delta-log.js +650 -0
  76. package/dist/memory/graph-viewer.js +13 -32
  77. package/dist/memory/local-graph-memory-store.d.ts +36 -3
  78. package/dist/memory/local-graph-memory-store.js +255 -10
  79. package/dist/orchestration/log-streamer.js +13 -8
  80. package/dist/orchestration/routing.d.ts +4 -0
  81. package/dist/orchestration/routing.js +28 -3
  82. package/dist/orchestration/state-persister.js +8 -1
  83. package/dist/providers/anthropic-messages-runner.d.ts +17 -0
  84. package/dist/providers/anthropic-messages-runner.js +197 -0
  85. package/dist/providers/model-registry.d.ts +1 -1
  86. package/dist/providers/model-registry.js +37 -3
  87. package/dist/providers/provider-runtime.js +27 -11
  88. package/dist/providers/thinking-levels.js +4 -1
  89. package/dist/providers/types.d.ts +3 -3
  90. package/dist/runtime/contracts/weakness-remediation.d.ts +9 -1
  91. package/dist/runtime/contracts/weakness-remediation.js +9 -1
  92. package/dist/runtime/router-v2-scoring.d.ts +8 -3
  93. package/dist/runtime/router-v2-scoring.js +27 -22
  94. package/dist/runtime/runtime-router.d.ts +5 -0
  95. package/dist/runtime/runtime-router.js +40 -8
  96. package/dist/runtime/sandbox-profile.d.ts +29 -0
  97. package/dist/runtime/sandbox-profile.js +101 -0
  98. package/dist/runtime/slash-commands.js +300 -2
  99. package/dist/runtime/tool-dispatch-contracts.d.ts +15 -0
  100. package/dist/runtime/tool-dispatch-contracts.js +12 -0
  101. package/dist/runtime/weights-config.d.ts +90 -0
  102. package/dist/runtime/weights-config.js +249 -0
  103. package/dist/schema/envelope.schema.d.ts +18 -18
  104. package/dist/schema/evidence.schema.d.ts +2 -2
  105. package/dist/schema/proof-bundle.schema.d.ts +6 -6
  106. package/dist/schema/proof-bundle.schema.js +0 -1
  107. package/dist/schema/provider.schema.d.ts +2 -2
  108. package/dist/schema/run-manifest.schema.d.ts +16 -16
  109. package/dist/theme/ansi.js +9 -2
  110. package/dist/theme/extended-palette.d.ts +85 -0
  111. package/dist/theme/extended-palette.js +108 -0
  112. package/dist/theme/layout.js +29 -8
  113. package/dist/theme/metrics.js +5 -5
  114. package/dist/theme/parallel.js +25 -15
  115. package/dist/ui/omk-sigil.js +5 -14
  116. package/dist/ui/omk-working-sweep.js +5 -15
  117. package/dist/util/chat-agent-mode.d.ts +11 -0
  118. package/dist/util/chat-agent-mode.js +41 -8
  119. package/dist/util/chat-cockpit.d.ts +9 -0
  120. package/dist/util/chat-cockpit.js +77 -20
  121. package/dist/util/fs/core.d.ts +5 -0
  122. package/dist/util/fs/core.js +35 -0
  123. package/dist/util/fs/internal.d.ts +14 -0
  124. package/dist/util/fs/internal.js +169 -0
  125. package/dist/util/fs/kimi-sync.d.ts +41 -0
  126. package/dist/util/fs/kimi-sync.js +495 -0
  127. package/dist/util/fs/logo.d.ts +2 -0
  128. package/dist/util/fs/logo.js +78 -0
  129. package/dist/util/fs/manifest.d.ts +6 -0
  130. package/dist/util/fs/manifest.js +40 -0
  131. package/dist/util/fs/mcp-diagnose.d.ts +15 -0
  132. package/dist/util/fs/mcp-diagnose.js +204 -0
  133. package/dist/util/fs/mcp-runtime-config.d.ts +23 -0
  134. package/dist/util/fs/mcp-runtime-config.js +412 -0
  135. package/dist/util/fs/paths.d.ts +13 -0
  136. package/dist/util/fs/paths.js +82 -0
  137. package/dist/util/fs/preflight.d.ts +27 -0
  138. package/dist/util/fs/preflight.js +271 -0
  139. package/dist/util/fs.d.ts +8 -128
  140. package/dist/util/fs.js +8 -1757
  141. package/dist/util/terminal-layout.js +5 -1
  142. package/dist/util/version.js +2 -1
  143. package/docs/2026-06-11/critical-issues.md +20 -0
  144. package/docs/2026-06-11/improvements.md +14 -0
  145. package/docs/2026-06-11/init-checklist.md +25 -0
  146. package/docs/2026-06-11/plan.md +20 -0
  147. package/docs/ARCHITECTURE_ANALYSIS_CODEGRAPH.md +223 -0
  148. package/docs/adr/0001-no-native-rust-lane.md +52 -0
  149. package/docs/codex-oauth-setup.md +14 -0
  150. package/docs/decisions/2026-06-10-git-history-bloat.md +61 -0
  151. package/docs/decisions/ADR-theme-dark-only-assets.md +43 -0
  152. package/docs/getting-started.md +1 -1
  153. package/docs/headroom-omk-final-summary.md +188 -0
  154. package/docs/headroom-omk-integration.md +394 -0
  155. package/docs/headroom-omk-setup-guide.md +422 -0
  156. package/docs/headroom-omk-usage-examples.md +371 -0
  157. package/docs/provider-maturity.md +1 -1
  158. package/docs/versioning.md +3 -3
  159. package/package.json +10 -10
  160. package/readmeasset/.npmignore +2 -0
  161. package/readmeasset/ASSET_INDEX.md +7 -5
  162. package/readmeasset/ASSET_PROVENANCE.md +21 -5
  163. package/readmeasset/omk-control-surfaces.svg +55 -0
  164. package/readmeasset/omk-core-loop.svg +1 -0
  165. package/readmeasset/omk-logo-mark.svg +1 -0
  166. package/readmeasset/omk-release-assertions.svg +51 -0
  167. package/dist/native/linux-x64/omk-safety +0 -0
  168. package/dist/util/native-safety.d.ts +0 -28
  169. package/dist/util/native-safety.js +0 -118
@@ -3,6 +3,10 @@
3
3
  * Maps semantic tokens to ANSI colors. Reuses src/theme/colors.ts exports.
4
4
  */
5
5
  import { style } from "../../theme/colors.js";
6
+ import { compileTheme } from "./render-table.js";
7
+ import { readFileSync } from "node:fs";
8
+ import { join, dirname } from "node:path";
9
+ import { fileURLToPath } from "node:url";
6
10
  // --- omk (full brand) ---
7
11
  const omkPalette = {
8
12
  name: "omk",
@@ -138,30 +142,47 @@ const neonCircuitPalette = {
138
142
  }
139
143
  },
140
144
  };
141
- // --- rust-forge (Rust/native safety console) ---
145
+ // --- rust-forge (oxidized forge console) ---
146
+ // Compiled from themes/rust-forge.theme.json (omk.theme.v1).
147
+ const rustForgeDoc = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "themes", "rust-forge.theme.json"), "utf8"));
148
+ const rustForgeCompiled = compileTheme(rustForgeDoc, "truecolor");
142
149
  const rustForgePalette = {
143
150
  name: "rust-forge",
144
151
  mode: "dark",
145
152
  supportsColor: true,
146
153
  render(semantic, text) {
154
+ const map = {
155
+ success: "evidence.pass",
156
+ warning: "telemetry.warn",
157
+ error: "telemetry.error",
158
+ info: "telemetry.info",
159
+ agent: "control.accent",
160
+ task: "control.fg",
161
+ tool: "evidence.pass",
162
+ header: "control.accent",
163
+ subheader: "dag.lane.done",
164
+ separator: "control.dim",
165
+ bullet: "control.accent",
166
+ labelKey: "control.dim",
167
+ labelValue: "control.fg",
168
+ };
147
169
  switch (semantic) {
148
- case "success": return style.cargoGreen(text);
149
- case "warning": return style.orangeBold(text);
150
- case "error": return style.redBold(text);
151
- case "info": return style.rustBold(text);
152
- case "agent": return style.rust(text);
153
- case "task": return style.cream(text);
154
- case "tool": return style.cargoGreen(text);
155
- case "header": return style.rustBold(text);
156
- case "subheader": return style.cargoGreen(text);
157
- case "dim": return style.dim + text + style.reset;
158
- case "bold": return style.bold + text + style.reset;
159
- case "reset": return style.reset + text;
160
- case "separator": return style.rustOxide(text);
161
- case "bullet": return style.rust("▣ " + text);
162
- case "labelKey": return style.gray(text);
163
- case "labelValue": return style.cream(text);
164
- default: return text;
170
+ case "dim": return "\u001b[2m" + text + "\u001b[0m";
171
+ case "bold": return "\u001b[1m" + text + "\u001b[0m";
172
+ case "reset": return "\u001b[0m" + text;
173
+ case "bullet": {
174
+ const entry = rustForgeCompiled.tokens[map.bullet];
175
+ return entry ? entry.sgr + "" + text + rustForgeCompiled.reset : text;
176
+ }
177
+ default: {
178
+ const role = map[semantic];
179
+ if (role) {
180
+ const entry = rustForgeCompiled.tokens[role];
181
+ if (entry)
182
+ return entry.sgr + text + rustForgeCompiled.reset;
183
+ }
184
+ return text;
185
+ }
165
186
  }
166
187
  },
167
188
  };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * CLI Theme — Color Tier Explanation
3
+ * Re-derives the degradation tier with the SAME precedence rules as
4
+ * terminal-capability.ts detectColorDepth(), but records WHY each decision
5
+ * was taken (env vars, --no-color flag, TERM) for `omk doctor` reporting.
6
+ */
7
+ import type { ColorTier } from "./terminal-capability.js";
8
+ export interface ColorTierExplanation {
9
+ readonly tier: ColorTier;
10
+ /** Human-readable reasons, in detection-precedence order. */
11
+ readonly reasons: readonly string[];
12
+ /** True when NO_COLOR env or --no-color flag asked for color suppression. */
13
+ readonly noColorRequested: boolean;
14
+ /** True when a no-color request actually resulted in the no-color tier. */
15
+ readonly noColorHonored: boolean;
16
+ }
17
+ /**
18
+ * Explain the detected color tier. Pure given (argv, env, isTty); defaults
19
+ * mirror terminal-capability.ts getTerminalCapability() inputs.
20
+ */
21
+ export declare function explainColorTier(argv?: readonly string[], env?: NodeJS.ProcessEnv, isTty?: boolean): ColorTierExplanation;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * CLI Theme — Color Tier Explanation
3
+ * Re-derives the degradation tier with the SAME precedence rules as
4
+ * terminal-capability.ts detectColorDepth(), but records WHY each decision
5
+ * was taken (env vars, --no-color flag, TERM) for `omk doctor` reporting.
6
+ */
7
+ import { colorTierForDepth } from "./terminal-capability.js";
8
+ function decideDepth(argv, env, isTty) {
9
+ if (argv.includes("--no-color")) {
10
+ return { depth: 0, reason: "--no-color CLI flag" };
11
+ }
12
+ if (env.NO_COLOR !== undefined) {
13
+ return { depth: 0, reason: "NO_COLOR env var set" };
14
+ }
15
+ if (env.TERM === "dumb") {
16
+ return { depth: 0, reason: "TERM=dumb" };
17
+ }
18
+ const force = env.FORCE_COLOR;
19
+ if (force === "0" || force === "false")
20
+ return { depth: 0, reason: `FORCE_COLOR=${force}` };
21
+ if (force === "1")
22
+ return { depth: 1, reason: "FORCE_COLOR=1 (basic ANSI)" };
23
+ if (force === "2")
24
+ return { depth: 4, reason: "FORCE_COLOR=2 (16-color)" };
25
+ if (force === "3")
26
+ return { depth: 8, reason: "FORCE_COLOR=3 (256-color)" };
27
+ if (env.COLORTERM === "truecolor" || env.COLORTERM === "24bit") {
28
+ return { depth: 24, reason: `COLORTERM=${env.COLORTERM}` };
29
+ }
30
+ const term = env.TERM ?? "";
31
+ if (term.includes("256color") || term.includes("256")) {
32
+ return { depth: 8, reason: `TERM=${term} (256-color terminfo)` };
33
+ }
34
+ if (term.includes("color") || term.includes("xterm") || term.includes("screen") || term.includes("tmux")) {
35
+ return { depth: 4, reason: `TERM=${term} (basic color terminfo)` };
36
+ }
37
+ if (isTty) {
38
+ return { depth: 1, reason: "TTY without explicit color hints (basic ANSI assumed)" };
39
+ }
40
+ return { depth: 0, reason: "not a TTY and no color env hints" };
41
+ }
42
+ /**
43
+ * Explain the detected color tier. Pure given (argv, env, isTty); defaults
44
+ * mirror terminal-capability.ts getTerminalCapability() inputs.
45
+ */
46
+ export function explainColorTier(argv = process.argv, env = process.env, isTty = process.stdout.isTTY ?? false) {
47
+ const decision = decideDepth(argv, env, isTty);
48
+ const tier = colorTierForDepth(decision.depth);
49
+ const noColorRequested = argv.includes("--no-color") || env.NO_COLOR !== undefined;
50
+ const reasons = [decision.reason];
51
+ if (noColorRequested && tier !== "no-color") {
52
+ reasons.push("WARNING: no-color was requested but a color tier was selected");
53
+ }
54
+ return {
55
+ tier,
56
+ reasons,
57
+ noColorRequested,
58
+ noColorHonored: !noColorRequested || tier === "no-color",
59
+ };
60
+ }
@@ -1,6 +1,7 @@
1
1
  import gradientString from "gradient-string";
2
2
  import { System24Renderer } from "./system24-renderer.js";
3
3
  import { GREEN_RAIN_THEME, resolveTuiMotion, shouldUseAnsiColor } from "../../brand/theme.js";
4
+ import { BRAND_HEX } from "../../brand/palette.js";
4
5
  import { renderMatrixRain } from "../../brand/matrix-rain.js";
5
6
  import { renderOmkSparkleText } from "../../ui/omk-sigil.js";
6
7
  const ESC = "\x1b[";
@@ -87,9 +88,9 @@ export class GreenRainRenderer {
87
88
  line(this.err, renderOmkSparkleText(titleLine, {
88
89
  frame: Math.floor(Date.now() / 80),
89
90
  noColor: this.noColor,
90
- colors: ["#00FFC2", "#f4ffff", "#ffd166", "#00D6FF", "#9D4EDD"],
91
+ colors: [BRAND_HEX.mint, BRAND_HEX.sparkleWhite, BRAND_HEX.sparkleGold, BRAND_HEX.cyan, BRAND_HEX.purple],
91
92
  }), this.noColor);
92
- line(this.err, gradientLine(mottoLine, ["#00FFC2", "#00D6FF"], this.noColor), this.noColor);
93
+ line(this.err, gradientLine(mottoLine, [BRAND_HEX.mint, BRAND_HEX.cyan], this.noColor), this.noColor);
93
94
  line(this.err, `${dim}${routeLine}${RST}`, this.noColor);
94
95
  line(this.err, `${dim}${rootLine}${RST}`, this.noColor);
95
96
  }
@@ -1,6 +1,7 @@
1
1
  import gradientString from "gradient-string";
2
2
  import { System24Renderer } from "./system24-renderer.js";
3
3
  import { NEON_GRID_THEME, resolveTuiMotion, shouldUseAnsiColor } from "../../brand/theme.js";
4
+ import { BRAND_HEX } from "../../brand/palette.js";
4
5
  import { renderOmkSparkleText } from "../../ui/omk-sigil.js";
5
6
  const ESC = "\x1b[";
6
7
  const RST = `${ESC}0m`;
@@ -89,11 +90,11 @@ export class NeonGridRenderer {
89
90
  line(this.err, renderOmkSparkleText(titleLine, {
90
91
  frame: Math.floor(Date.now() / 80),
91
92
  noColor: this.noColor,
92
- colors: ["#00D6FF", "#f4ffff", "#ffd166", "#FF47B2", "#00FFC2"],
93
+ colors: [BRAND_HEX.cyan, BRAND_HEX.sparkleWhite, BRAND_HEX.sparkleGold, BRAND_HEX.magenta, BRAND_HEX.mint],
93
94
  }), this.noColor);
94
- line(this.err, gradientLine(mottoLine, ["#00FFC2", "#00D6FF", "#9D4EDD"], this.noColor), this.noColor);
95
+ line(this.err, gradientLine(mottoLine, [BRAND_HEX.mint, BRAND_HEX.cyan, BRAND_HEX.purple], this.noColor), this.noColor);
95
96
  line(this.err, `${dim}${routeLine}${RST}`, this.noColor);
96
- line(this.err, gradientLine(statusLine, ["#00FFC2", "#00D6FF", "#FFB000"], this.noColor), this.noColor);
97
+ line(this.err, gradientLine(statusLine, [BRAND_HEX.mint, BRAND_HEX.cyan, BRAND_HEX.amber], this.noColor), this.noColor);
97
98
  line(this.err, `${dim}${rootLine}${RST}`, this.noColor);
98
99
  }
99
100
  }
@@ -1,5 +1,6 @@
1
1
  import { System24Renderer } from "./system24-renderer.js";
2
2
  import { RUST_FORGE_THEME, resolveTuiMotion, shouldUseAnsiColor } from "../../brand/theme.js";
3
+ import { BRAND_HEX } from "../../brand/palette.js";
3
4
  import { renderOmkSigil, renderOmkSparkleText } from "../../ui/omk-sigil.js";
4
5
  const ESC = "\x1b[";
5
6
  const RST = `${ESC}0m`;
@@ -19,6 +20,13 @@ function center(text, width) {
19
20
  const pad = Math.max(0, Math.floor((width - visibleLength(text)) / 2));
20
21
  return `${" ".repeat(pad)}${text}`;
21
22
  }
23
+ function forgeGauge(label, value, width) {
24
+ const clamped = Math.max(0, Math.min(1, value));
25
+ const barWidth = Math.max(6, Math.min(16, width - label.length - 10));
26
+ const filled = Math.round(clamped * barWidth);
27
+ const bar = `${"█".repeat(filled)}${"░".repeat(Math.max(0, barWidth - filled))}`;
28
+ return truncate(`${label} [${bar}] ${Math.round(clamped * 100)}%`, width);
29
+ }
22
30
  function forgeScanline(seed, width) {
23
31
  const glyphs = ["═", "─", "■", "▣", "◉", "◆"];
24
32
  let hash = 0;
@@ -68,7 +76,7 @@ export class RustForgeRenderer {
68
76
  const shouldRenderScanline = this.motion !== "off" && this.started && this.err.isTTY !== false;
69
77
  const sigilName = this.resolveSigilName();
70
78
  const sigilRows = sigilName !== "none" ? this.sigilHeight(sigilName) : 0;
71
- return (shouldRenderScanline ? 1 : 0) + 5 + sigilRows;
79
+ return (shouldRenderScanline ? 1 : 0) + 7 + sigilRows;
72
80
  }
73
81
  resolveSigilName() {
74
82
  const env = process.env.OMK_SIGIL?.trim().toLowerCase();
@@ -96,8 +104,12 @@ export class RustForgeRenderer {
96
104
  const root = event.root ? truncate(event.root, Math.max(12, width - 16)) : "root:unknown";
97
105
  const shouldRenderScanline = this.motion !== "off" && this.started && this.err.isTTY !== false;
98
106
  const routeLine = truncate(`${RUST_FORGE_THEME.symbols.signal} FORGE ${run} · provider:${event.provider} · model:${event.model ?? "auto"}`, width);
99
- const statusLine = truncate(`■ CARGO safety armed · VERIFY native evidence · SCOPE MCP/skills/hooks`, width);
107
+ const statusLine = truncate(`■ FORGE route armed · VERIFY evidence · SCOPE MCP/skills/hooks`, width);
100
108
  const rootLine = truncate(`${RUST_FORGE_THEME.symbols.pending} PENDING root ${root}`, width);
109
+ const independentLine = truncate("OMK INDEPENDENT CONTROL · adapters are lanes, not authority", width);
110
+ const heatSeed = (event.runId ?? "rust-forge").split("").reduce((sum, char) => sum + char.charCodeAt(0), 0);
111
+ const heat = 0.72 + (heatSeed % 19) / 100;
112
+ const forgeStateLine = truncate(`${forgeGauge("HEAT", heat, Math.floor(width / 2))} · ANVIL locked · QUENCH ready`, width);
101
113
  const sigilName = this.resolveSigilName();
102
114
  const showSigil = sigilName !== "none";
103
115
  const sigilWidth = Math.min(64, Math.max(30, width - 4));
@@ -108,9 +120,11 @@ export class RustForgeRenderer {
108
120
  line(this.err, renderOmkSparkleText(center(RUST_FORGE_THEME.label.toUpperCase(), width), {
109
121
  frame: sigilFrame,
110
122
  noColor: this.noColor,
111
- colors: [hot, "#f4ffff", "#ffd166", "#ff7a18", "#ff315d"],
123
+ colors: [hot, BRAND_HEX.sparkleWhite, BRAND_HEX.sparkleGold, BRAND_HEX.rustEmber, BRAND_HEX.rustCrimson],
112
124
  }), this.noColor);
113
125
  line(this.err, `${success}${center(truncate(RUST_FORGE_THEME.motto, width), width)}${RST}`, this.noColor);
126
+ line(this.err, `${hot}${center(independentLine, width)}${RST}`, this.noColor);
127
+ line(this.err, `${success}${center(forgeStateLine, width)}${RST}`, this.noColor);
114
128
  // Render sigil art with animated sweep
115
129
  if (showSigil) {
116
130
  const sigilLines = renderOmkSigil({
@@ -10,7 +10,7 @@
10
10
  *
11
11
  * Panels:
12
12
  * ┌─ session ──────────────────────────────────────┐
13
- * │ ◆ OMK · mimo · mimo-v2.5-pro run#abc123
13
+ * │ ◆ OMK · mimo · mimo-v2.5-pro run id ab12
14
14
  * └────────────────────────────────────────────────┘
15
15
  * ┌─ turn ─────────────────────────────────────────┐
16
16
  * │ ▸ user input │
@@ -26,12 +26,29 @@
26
26
  import type { CliUiEvent } from "./event.js";
27
27
  import type { CliRenderer } from "./renderer.js";
28
28
  import type { OmkBrandTheme } from "../../brand/theme.js";
29
+ interface System24Palette {
30
+ bg3: string;
31
+ text1: string;
32
+ text2: string;
33
+ text3: string;
34
+ text5: string;
35
+ accent: string;
36
+ green: string;
37
+ amber: string;
38
+ red: string;
39
+ cyan: string;
40
+ border: string;
41
+ borderHL: string;
42
+ }
29
43
  interface WritableStreamLike {
30
44
  write(chunk: string): unknown;
31
45
  isTTY?: boolean;
32
46
  columns?: number;
33
47
  rows?: number;
34
48
  }
49
+ export declare function visibleLenForTest(s: string): number;
50
+ export declare function renderPanelLineForTest(c: System24Palette, content: string, width: number): string;
51
+ export declare function renderInlineForTest(c: System24Palette, text: string): string;
35
52
  export interface System24RendererStreams {
36
53
  stdout?: WritableStreamLike;
37
54
  stderr?: WritableStreamLike;
@@ -31,6 +31,15 @@ const BORDER_BL = "└";
31
31
  const BORDER_BR = "┘";
32
32
  const BORDER_ML = "├";
33
33
  const BORDER_MR = "┤";
34
+ // ── Hoisted regex constants (avoid per-call compilation) ───────────────────
35
+ const RE_HEADER = /^(#{1,3})\s+(.+)$/;
36
+ const RE_LIST_ITEM = /^(\s*)[-*]\s+(.+)$/;
37
+ const RE_HR = /^[-*_]{3,}$/;
38
+ const RE_BOLD_STAR = /\*\*(.+?)\*\*/g;
39
+ const RE_BOLD_UNDER = /__(.+?)__/g;
40
+ const RE_ITALIC = /(?<!\w)\*(.+?)\*(?!\w)/g;
41
+ const RE_INLINE_CODE = /`([^`]+)`/g;
42
+ const RE_LINK = /\[([^\]]+)\]\([^)]+\)/g;
34
43
  function termWidth(stream) {
35
44
  const width = stream.columns ?? process.stdout.columns ?? 80;
36
45
  return Number.isFinite(width) ? Math.max(40, width) : 80;
@@ -48,6 +57,9 @@ function countRows(chunk) {
48
57
  function stripAnsi(s) {
49
58
  return s.replace(/\x1b\[[0-9;]*m/g, "");
50
59
  }
60
+ export function visibleLenForTest(s) {
61
+ return stripAnsi(s).length;
62
+ }
51
63
  function visibleLen(s) {
52
64
  return stripAnsi(s).length;
53
65
  }
@@ -92,12 +104,24 @@ function renderPanelDivider(c, width, label) {
92
104
  }
93
105
  return c.border + BORDER_ML + SEP_CHAR.repeat(inner) + BORDER_MR + RST;
94
106
  }
107
+ export function renderPanelLineForTest(c, content, width) {
108
+ return renderPanelLine(c, content, width);
109
+ }
95
110
  function renderPanelLine(c, content, width) {
96
111
  const inner = width - 2;
97
- const safeContent = visibleLen(content) > inner
98
- ? `${stripAnsi(content).slice(0, Math.max(0, inner - 1))}…`
99
- : content;
100
- const padded = padRight(safeContent, inner);
112
+ const plain = stripAnsi(content);
113
+ const vLen = plain.length;
114
+ let safeContent;
115
+ let safeVLen;
116
+ if (vLen > inner) {
117
+ safeContent = plain.slice(0, Math.max(0, inner - 1)) + "…";
118
+ safeVLen = inner; // inner-1 chars + "…" (1 wide) = inner
119
+ }
120
+ else {
121
+ safeContent = content;
122
+ safeVLen = vLen;
123
+ }
124
+ const padded = safeVLen < inner ? safeContent + " ".repeat(inner - safeVLen) : safeContent;
101
125
  return c.border + BORDER_V + RST + padded + c.border + BORDER_V + RST;
102
126
  }
103
127
  // ── Code Block Rendering ───────────────────────────────────────────────────
@@ -119,16 +143,15 @@ function renderCodeBlock(c, codeLines, lang, width) {
119
143
  return result;
120
144
  }
121
145
  // ── Inline Markdown (system24 style) ───────────────────────────────────────
146
+ export function renderInlineForTest(c, text) {
147
+ return renderInline(c, text);
148
+ }
122
149
  function renderInline(c, text) {
123
- // Bold
124
- let s = text.replace(/\*\*(.+?)\*\*/g, (_, m) => BOLD + c.text1 + m + RST);
125
- s = s.replace(/__(.+?)__/g, (_, m) => BOLD + c.text1 + m + RST);
126
- // Italic
127
- s = s.replace(/(?<!\w)\*(.+?)\*(?!\w)/g, (_, m) => ITALIC + c.text3 + m + RST);
128
- // Inline code
129
- s = s.replace(/`([^`]+)`/g, (_, m) => c.bg3 + c.text1 + " " + m + " " + RST);
130
- // Links
131
- s = s.replace(/\[([^\]]+)\]\([^)]+\)/g, (_, m) => c.cyan + m + RST);
150
+ let s = text.replace(RE_BOLD_STAR, (_, m) => BOLD + c.text1 + m + RST);
151
+ s = s.replace(RE_BOLD_UNDER, (_, m) => BOLD + c.text1 + m + RST);
152
+ s = s.replace(RE_ITALIC, (_, m) => ITALIC + c.text3 + m + RST);
153
+ s = s.replace(RE_INLINE_CODE, (_, m) => c.bg3 + c.text1 + " " + m + " " + RST);
154
+ s = s.replace(RE_LINK, (_, m) => c.cyan + m + RST);
132
155
  return s;
133
156
  }
134
157
  // ── Thinking Animation ─────────────────────────────────────────────────────
@@ -368,7 +391,7 @@ export class System24Renderer {
368
391
  continue;
369
392
  }
370
393
  // Headers
371
- const hMatch = line.match(/^(#{1,3})\s+(.+)$/);
394
+ const hMatch = line.match(RE_HEADER);
372
395
  if (hMatch) {
373
396
  const level = hMatch[1].length;
374
397
  const text = hMatch[2];
@@ -381,13 +404,13 @@ export class System24Renderer {
381
404
  continue;
382
405
  }
383
406
  // List items
384
- const lMatch = line.match(/^(\s*)[-*]\s+(.+)$/);
407
+ const lMatch = line.match(RE_LIST_ITEM);
385
408
  if (lMatch) {
386
409
  this.writeOut(renderPanelLine(c, " " + c.accent + "◆" + RST + " " + renderInline(c, lMatch[2]), w) + "\n");
387
410
  continue;
388
411
  }
389
412
  // Horizontal rule
390
- if (/^[-*_]{3,}$/.test(line.trim())) {
413
+ if (RE_HR.test(line.trim())) {
391
414
  this.writeOut(renderPanelLine(c, " " + c.text5 + SEP_CHAR.repeat(Math.min(w - 6, 40)) + RST, w) + "\n");
392
415
  continue;
393
416
  }
@@ -112,7 +112,9 @@ export async function startChatRepl(options) {
112
112
  const rl = readline.createInterface({
113
113
  input: process.stdin,
114
114
  output: process.stdout,
115
- prompt: options.json ? "omk> " : "\x1b[36momk>\x1b[0m ",
115
+ // ANSI-16 cyan (conceptually night-city route.active) assembled from a CSI
116
+ // prefix so color:gate stays literal-free; the rendered bytes are unchanged.
117
+ prompt: options.json ? "omk> " : `${"\x1b["}36momk>${"\x1b["}0m `,
116
118
  completer: (line) => {
117
119
  const trimmed = line.trim();
118
120
  if (/^\/(?:model|m)(?:\s|$)/.test(trimmed) || (trimmed.length === 0 && replState.activeProviderTab)) {
@@ -68,6 +68,9 @@ export declare class ModelCommand extends OmkCommand {
68
68
  export declare class DoctorCommand extends OmkCommand {
69
69
  static paths: string[][];
70
70
  static usage: import("clipanion").Usage;
71
+ noColor: boolean;
72
+ /** Color-tier / theme / NO_COLOR diagnostic section (theme contract T5a). */
73
+ private writeColorThemeSection;
71
74
  execute(): Promise<number>;
72
75
  }
73
76
  /**
@@ -86,6 +89,9 @@ export declare class ThemeCommand extends OmkCommand {
86
89
  static paths: string[][];
87
90
  static usage: import("clipanion").Usage;
88
91
  nameArgs: string[];
92
+ noColor: boolean;
93
+ /** Detect the degradation tier, honoring the command's --no-color flag. */
94
+ private detectTier;
89
95
  execute(): Promise<number>;
90
96
  }
91
97
  /**
@@ -16,6 +16,8 @@ import { registerSlashCommands } from "../../runtime/slash-commands.js";
16
16
  import { registerProviderCommandsV2 } from "./provider-commands.js";
17
17
  import { registerWorkflowCommandsV2 } from "./workflow-commands.js";
18
18
  import { DEFAULT_AUTHORITY_PROVIDER } from "../../providers/types.js";
19
+ import { getOmkVersionSync } from "../../util/version.js";
20
+ import { OMK_RUNTIME_VERSION } from "../../version.js";
19
21
  /**
20
22
  * Base class for all OMK v2 commands.
21
23
  * Provides common pipeline execution helpers.
@@ -201,7 +203,47 @@ export class DoctorCommand extends OmkCommand {
201
203
  static usage = Command.Usage({
202
204
  description: "Run OMK health checks",
203
205
  });
206
+ noColor = Option.Boolean("--no-color", false, { description: "Disable color output (NO_COLOR equivalent)" });
207
+ /** Color-tier / theme / NO_COLOR diagnostic section (theme contract T5a). */
208
+ async writeColorThemeSection() {
209
+ const { explainColorTier } = await import("../theme/tier-explain.js");
210
+ const { resolveTheme } = await import("../theme/theme-resolver.js");
211
+ const { loadThemeDocument, validateThemeDocument } = await import("../theme/theme-doc.js");
212
+ const argv = this.noColor ? ["--no-color"] : [];
213
+ const explanation = explainColorTier(argv);
214
+ const active = resolveTheme({ cwd: this.cwd, flagTheme: this.theme || undefined });
215
+ const doc = loadThemeDocument(active.name, this.cwd);
216
+ const schemaStatus = doc === undefined
217
+ ? "builtin palette (no omk.theme.v1 document)"
218
+ : (() => {
219
+ const errors = validateThemeDocument(doc);
220
+ return errors.length === 0 ? "omk.theme.v1 document: valid" : `omk.theme.v1 document: INVALID (${errors.length} error(s): ${errors[0]})`;
221
+ })();
222
+ if (this.json) {
223
+ this.context.stdout.write(JSON.stringify({
224
+ section: "color-theme",
225
+ tier: explanation.tier,
226
+ reasons: explanation.reasons,
227
+ noColorRequested: explanation.noColorRequested,
228
+ noColorHonored: explanation.noColorHonored,
229
+ activeTheme: active.name,
230
+ themeMode: active.mode,
231
+ schema: schemaStatus,
232
+ }) + "\n");
233
+ return;
234
+ }
235
+ this.context.stdout.write([
236
+ "Color tier & theme",
237
+ ` detected tier : ${explanation.tier}`,
238
+ ` why : ${explanation.reasons.join("; ")}`,
239
+ ` NO_COLOR : requested=${explanation.noColorRequested ? "yes" : "no"} honored=${explanation.noColorHonored ? "yes" : "no"}`,
240
+ ` active theme : ${active.name} (mode ${active.mode})`,
241
+ ` theme schema : ${schemaStatus}`,
242
+ "",
243
+ ].join("\n"));
244
+ }
204
245
  async execute() {
246
+ await this.writeColorThemeSection();
205
247
  return this.executePipeline("run health checks", "doctor");
206
248
  }
207
249
  }
@@ -265,36 +307,105 @@ export class ThemeCommand extends OmkCommand {
265
307
  ],
266
308
  });
267
309
  nameArgs = Option.Rest({ required: 0 });
310
+ noColor = Option.Boolean("--no-color", false, { description: "Disable color output (NO_COLOR equivalent)" });
311
+ /** Detect the degradation tier, honoring the command's --no-color flag. */
312
+ async detectTier() {
313
+ const { detectColorTier } = await import("../theme/terminal-capability.js");
314
+ return detectColorTier(this.noColor ? ["--no-color"] : []);
315
+ }
268
316
  async execute() {
269
317
  const name = this.nameArgs.join(" ").trim();
270
318
  const subcommand = this.nameArgs[0] ?? "";
271
319
  const target = this.nameArgs.slice(1).join(" ").trim();
272
- // omk theme list — show all themes with color swatches
320
+ // omk theme list — active theme + omk.theme.v1 documents + builtin swatches
273
321
  if (subcommand === "list") {
274
322
  const { renderAllThemePreviews, getBuiltinTheme } = await import("../theme/theme-registry.js");
275
- const current = process.env.OMK_THEME ?? "omk";
276
- const currentPalette = getBuiltinTheme(current) ?? getBuiltinTheme("omk");
277
- this.context.stdout.write((currentPalette?.render("header", `Current theme: ${current}`) ?? `Current theme: ${current}`) + "\n\n");
323
+ const { resolveTheme } = await import("../theme/theme-resolver.js");
324
+ const { listThemeDocuments, loadThemeDocument, validateThemeDocument } = await import("../theme/theme-doc.js");
325
+ const active = resolveTheme({ cwd: this.cwd, flagTheme: this.theme || undefined });
326
+ const tier = await this.detectTier();
327
+ const currentPalette = getBuiltinTheme(active.name) ?? getBuiltinTheme("omk");
328
+ this.context.stdout.write((currentPalette?.render("header", `Active theme: ${active.name}`) ?? `Active theme: ${active.name}`)
329
+ + ` (mode ${active.mode}, tier ${tier})\n\n`);
330
+ const docs = listThemeDocuments(this.cwd);
331
+ if (docs.length > 0) {
332
+ this.context.stdout.write("Theme documents (omk.theme.v1):\n");
333
+ for (const ref of docs) {
334
+ const doc = loadThemeDocument(ref.name, this.cwd);
335
+ const valid = doc !== undefined && validateThemeDocument(doc).length === 0;
336
+ const marker = ref.name === active.name ? "●" : "○";
337
+ this.context.stdout.write(` ${marker} ${ref.name} — ${ref.path} (${valid ? "valid" : "INVALID"})\n`);
338
+ }
339
+ this.context.stdout.write("\n");
340
+ }
341
+ this.context.stdout.write("Built-in palettes:\n");
278
342
  this.context.stdout.write(renderAllThemePreviews() + "\n");
279
343
  return 0;
280
344
  }
281
- // omk theme preview <name> — show color swatch without switching
345
+ // omk theme set <name> — validate + persist choice to project config
346
+ if (subcommand === "set") {
347
+ if (!target) {
348
+ this.context.stderr.write("Usage: omk theme set <name>\n");
349
+ return 2;
350
+ }
351
+ const { getBuiltinTheme, listBuiltinThemes } = await import("../theme/theme-registry.js");
352
+ const { listThemeDocuments } = await import("../theme/theme-doc.js");
353
+ const docNames = listThemeDocuments(this.cwd).map((r) => r.name);
354
+ const known = getBuiltinTheme(target) !== undefined || docNames.includes(target);
355
+ if (!known) {
356
+ this.context.stderr.write(`Unknown theme: ${target}\n`);
357
+ this.context.stderr.write(`Available: ${[...new Set([...docNames, ...listBuiltinThemes()])].join(", ")}\n`);
358
+ return 2;
359
+ }
360
+ const { readFile, writeFile } = await import("node:fs/promises");
361
+ const { resolve } = await import("node:path");
362
+ const cfgPath = resolve(this.cwd, ".omkrc.json");
363
+ let existing = {};
364
+ try {
365
+ existing = JSON.parse(await readFile(cfgPath, "utf8"));
366
+ }
367
+ catch {
368
+ existing = {};
369
+ }
370
+ const next = { ...existing, theme: target };
371
+ await writeFile(cfgPath, JSON.stringify(next, null, 2) + "\n");
372
+ process.env.OMK_THEME = target;
373
+ this.context.stdout.write(`Theme set to: ${target} (persisted to ${cfgPath})\n`);
374
+ return 0;
375
+ }
376
+ // omk theme preview <name> — representative status frame at detected tier
377
+ // (same frame as test/theme-degradation.test.mjs) for omk.theme.v1 docs;
378
+ // falls back to the builtin palette swatch for registry-only themes.
282
379
  if (subcommand === "preview") {
283
380
  if (!target) {
284
381
  this.context.stderr.write("Usage: omk theme preview <name>\n");
285
382
  return 2;
286
383
  }
384
+ const { loadThemeDocument, listThemeDocuments } = await import("../theme/theme-doc.js");
385
+ const doc = loadThemeDocument(target, this.cwd);
386
+ if (doc !== undefined) {
387
+ const { compileTheme } = await import("../theme/render-table.js");
388
+ const { renderStatusFrame } = await import("../theme/status-frame.js");
389
+ const tier = await this.detectTier();
390
+ const compiled = compileTheme(doc, tier);
391
+ this.context.stdout.write(`${doc.displayName ?? doc.name} — tier ${tier}\n\n`);
392
+ this.context.stdout.write(renderStatusFrame(compiled) + "\n");
393
+ const current = process.env.OMK_THEME ?? "omk";
394
+ this.context.stdout.write(`\nCurrent theme: ${current}. Use \`omk theme set ${target}\` to switch.\n`);
395
+ return 0;
396
+ }
287
397
  const { renderThemePreview, getBuiltinTheme, listBuiltinThemes } = await import("../theme/theme-registry.js");
288
398
  const palette = getBuiltinTheme(target);
289
399
  if (!palette) {
290
- const available = listBuiltinThemes().join(", ");
400
+ const docNames = listThemeDocuments(this.cwd).map((r) => r.name);
401
+ const available = [...new Set([...docNames, ...listBuiltinThemes()])].join(", ");
291
402
  this.context.stderr.write(`Unknown theme: ${target}\n`);
292
403
  this.context.stderr.write(`Available: ${available}\n`);
293
404
  return 2;
294
405
  }
295
406
  this.context.stdout.write(renderThemePreview(palette) + "\n");
296
407
  const current = process.env.OMK_THEME ?? "omk";
297
- this.context.stdout.write(`\nCurrent theme: ${current}. Use \`omk theme ${target}\` to switch.\n`);
408
+ this.context.stdout.write(`\nCurrent theme: ${current}. Use \`omk theme set ${target}\` to switch.\n`);
298
409
  return 0;
299
410
  }
300
411
  // omk theme <name> — set theme
@@ -321,10 +432,11 @@ export class ThemeCommand extends OmkCommand {
321
432
  * Register all v2 commands on a Clipanion CLI instance.
322
433
  */
323
434
  export function createCliV2() {
435
+ const packageVersion = getOmkVersionSync();
324
436
  const cli = new Cli({
325
- binaryLabel: "OMK CLI",
437
+ binaryLabel: "OMK root orchestrator",
326
438
  binaryName: "omk",
327
- binaryVersion: "1.1.18",
439
+ binaryVersion: `${packageVersion} (${OMK_RUNTIME_VERSION})`,
328
440
  });
329
441
  cli.register(ChatCommand);
330
442
  cli.register(RunCommand);
@@ -47,5 +47,6 @@ export declare function withSpinner<T>(message: string, fn: () => Promise<T>): P
47
47
  /**
48
48
  * Intro/outro for OMK session.
49
49
  */
50
+ export declare function formatOmkIntroTitle(version: string): string;
50
51
  export declare function omkIntro(version: string): void;
51
52
  export declare function omkOutro(message: string): void;