open-multi-agent-kit 0.78.6 → 0.78.8
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/AGENTS.md +1 -1
- package/CHANGELOG.md +38 -0
- package/MATURITY.md +2 -2
- package/README.md +19 -4
- package/dist/brand/night-city.theme.json +80 -0
- package/dist/brand/palette.d.ts +73 -177
- package/dist/brand/palette.js +115 -39
- package/dist/brand/rust-forge.theme.json +216 -0
- package/dist/brand/theme-compiled.d.ts +61 -0
- package/dist/brand/theme-compiled.js +241 -0
- package/dist/brand/theme.js +50 -37
- package/dist/cli/release-promotion-gate.d.ts +8 -1
- package/dist/cli/release-promotion-gate.js +20 -9
- package/dist/cli/root.d.ts +1 -0
- package/dist/cli/root.js +2 -1
- package/dist/cli/theme/index.d.ts +11 -2
- package/dist/cli/theme/index.js +6 -1
- package/dist/cli/theme/oklab-quantize.d.ts +29 -0
- package/dist/cli/theme/oklab-quantize.js +105 -0
- package/dist/cli/theme/render-table.d.ts +59 -0
- package/dist/cli/theme/render-table.js +112 -0
- package/dist/cli/theme/status-frame.d.ts +10 -0
- package/dist/cli/theme/status-frame.js +22 -0
- package/dist/cli/theme/terminal-capability.d.ts +6 -1
- package/dist/cli/theme/terminal-capability.js +20 -4
- package/dist/cli/theme/theme-doc.d.ts +22 -0
- package/dist/cli/theme/theme-doc.js +97 -0
- package/dist/cli/theme/theme-registry.js +39 -18
- package/dist/cli/theme/tier-explain.d.ts +21 -0
- package/dist/cli/theme/tier-explain.js +60 -0
- package/dist/cli/ui/green-rain-renderer.js +3 -2
- package/dist/cli/ui/neon-grid-renderer.js +4 -3
- package/dist/cli/ui/rust-forge-renderer.js +17 -3
- package/dist/cli/ui/system24-renderer.d.ts +18 -1
- package/dist/cli/ui/system24-renderer.js +39 -16
- package/dist/cli/v2/chat-repl.js +3 -1
- package/dist/cli/v2/cli-v2-skeleton.d.ts +6 -0
- package/dist/cli/v2/cli-v2-skeleton.js +121 -9
- package/dist/cli/v2/interactive-prompt.d.ts +1 -0
- package/dist/cli/v2/interactive-prompt.js +6 -2
- package/dist/commands/chat/core.js +2 -2
- package/dist/commands/chat/runtime.js +2 -0
- package/dist/commands/chat/utils.js +12 -4
- package/dist/commands/cockpit/render.js +102 -64
- package/dist/commands/cockpit/utils.d.ts +2 -0
- package/dist/commands/design.js +18 -16
- package/dist/commands/doctor/checks.d.ts +0 -1
- package/dist/commands/doctor/checks.js +1 -36
- package/dist/commands/doctor/report.js +0 -11
- package/dist/commands/doctor/utils.js +2 -2
- package/dist/commands/init/content.d.ts +1 -1
- package/dist/commands/init/content.js +3 -11
- package/dist/commands/init.js +2 -2
- package/dist/commands/mcp/config.d.ts +32 -0
- package/dist/commands/mcp/config.js +264 -0
- package/dist/commands/mcp/doctor-fix.d.ts +12 -0
- package/dist/commands/mcp/doctor-fix.js +261 -0
- package/dist/commands/mcp/doctor.d.ts +55 -0
- package/dist/commands/mcp/doctor.js +401 -0
- package/dist/commands/mcp/list.d.ts +1 -0
- package/dist/commands/mcp/list.js +56 -0
- package/dist/commands/mcp/shared.d.ts +47 -0
- package/dist/commands/mcp/shared.js +192 -0
- package/dist/commands/mcp/test.d.ts +5 -0
- package/dist/commands/mcp/test.js +457 -0
- package/dist/commands/mcp.d.ts +8 -104
- package/dist/commands/mcp.js +5 -1603
- package/dist/contracts/proof.d.ts +1 -1
- package/dist/goal/control-loop.d.ts +15 -0
- package/dist/goal/control-loop.js +39 -5
- package/dist/hud/render.d.ts +2 -0
- package/dist/hud/render.js +7 -1
- package/dist/mcp/secret-scanner.d.ts +6 -0
- package/dist/mcp/secret-scanner.js +44 -11
- package/dist/memory/graph-delta-log.d.ts +137 -0
- package/dist/memory/graph-delta-log.js +650 -0
- package/dist/memory/graph-viewer.js +13 -32
- package/dist/memory/local-graph-memory-store.d.ts +36 -3
- package/dist/memory/local-graph-memory-store.js +255 -10
- package/dist/orchestration/log-streamer.js +13 -8
- package/dist/orchestration/routing.d.ts +4 -0
- package/dist/orchestration/routing.js +28 -3
- package/dist/orchestration/state-persister.js +8 -1
- package/dist/providers/anthropic-messages-runner.d.ts +17 -0
- package/dist/providers/anthropic-messages-runner.js +197 -0
- package/dist/providers/model-registry.d.ts +1 -1
- package/dist/providers/model-registry.js +37 -3
- package/dist/providers/provider-runtime.js +27 -11
- package/dist/providers/thinking-levels.js +4 -1
- package/dist/providers/types.d.ts +3 -3
- package/dist/runtime/contracts/weakness-remediation.d.ts +9 -1
- package/dist/runtime/contracts/weakness-remediation.js +9 -1
- package/dist/runtime/router-v2-scoring.d.ts +8 -3
- package/dist/runtime/router-v2-scoring.js +27 -22
- package/dist/runtime/runtime-router.d.ts +5 -0
- package/dist/runtime/runtime-router.js +40 -8
- package/dist/runtime/sandbox-profile.d.ts +29 -0
- package/dist/runtime/sandbox-profile.js +101 -0
- package/dist/runtime/slash-commands.js +300 -2
- package/dist/runtime/tool-dispatch-contracts.d.ts +15 -0
- package/dist/runtime/tool-dispatch-contracts.js +12 -0
- package/dist/runtime/weights-config.d.ts +90 -0
- package/dist/runtime/weights-config.js +249 -0
- package/dist/schema/envelope.schema.d.ts +18 -18
- package/dist/schema/evidence.schema.d.ts +2 -2
- package/dist/schema/proof-bundle.schema.d.ts +6 -6
- package/dist/schema/proof-bundle.schema.js +0 -1
- package/dist/schema/provider.schema.d.ts +2 -2
- package/dist/schema/run-manifest.schema.d.ts +16 -16
- package/dist/theme/ansi.js +9 -2
- package/dist/theme/extended-palette.d.ts +85 -0
- package/dist/theme/extended-palette.js +108 -0
- package/dist/theme/layout.js +29 -8
- package/dist/theme/metrics.js +5 -5
- package/dist/theme/parallel.js +25 -15
- package/dist/ui/omk-sigil.js +5 -14
- package/dist/ui/omk-working-sweep.js +5 -15
- package/dist/util/chat-agent-mode.d.ts +11 -0
- package/dist/util/chat-agent-mode.js +41 -8
- package/dist/util/chat-cockpit.d.ts +9 -0
- package/dist/util/chat-cockpit.js +77 -20
- package/dist/util/fs/core.d.ts +5 -0
- package/dist/util/fs/core.js +35 -0
- package/dist/util/fs/internal.d.ts +14 -0
- package/dist/util/fs/internal.js +169 -0
- package/dist/util/fs/kimi-sync.d.ts +41 -0
- package/dist/util/fs/kimi-sync.js +495 -0
- package/dist/util/fs/logo.d.ts +2 -0
- package/dist/util/fs/logo.js +78 -0
- package/dist/util/fs/manifest.d.ts +6 -0
- package/dist/util/fs/manifest.js +40 -0
- package/dist/util/fs/mcp-diagnose.d.ts +15 -0
- package/dist/util/fs/mcp-diagnose.js +204 -0
- package/dist/util/fs/mcp-runtime-config.d.ts +23 -0
- package/dist/util/fs/mcp-runtime-config.js +412 -0
- package/dist/util/fs/paths.d.ts +13 -0
- package/dist/util/fs/paths.js +82 -0
- package/dist/util/fs/preflight.d.ts +27 -0
- package/dist/util/fs/preflight.js +271 -0
- package/dist/util/fs.d.ts +8 -128
- package/dist/util/fs.js +8 -1757
- package/dist/util/terminal-layout.js +5 -1
- package/dist/util/version.js +2 -1
- package/docs/2026-06-11/critical-issues.md +20 -0
- package/docs/2026-06-11/improvements.md +14 -0
- package/docs/2026-06-11/init-checklist.md +25 -0
- package/docs/2026-06-11/plan.md +20 -0
- package/docs/ARCHITECTURE_ANALYSIS_CODEGRAPH.md +223 -0
- package/docs/adr/0001-no-native-rust-lane.md +52 -0
- package/docs/codex-oauth-setup.md +14 -0
- package/docs/decisions/2026-06-10-git-history-bloat.md +61 -0
- package/docs/decisions/ADR-theme-dark-only-assets.md +43 -0
- package/docs/getting-started.md +1 -1
- package/docs/headroom-omk-final-summary.md +188 -0
- package/docs/headroom-omk-integration.md +394 -0
- package/docs/headroom-omk-setup-guide.md +422 -0
- package/docs/headroom-omk-usage-examples.md +371 -0
- package/docs/provider-maturity.md +1 -1
- package/docs/versioning.md +3 -3
- package/package.json +10 -10
- package/readmeasset/.npmignore +2 -0
- package/readmeasset/ASSET_INDEX.md +7 -5
- package/readmeasset/ASSET_PROVENANCE.md +21 -5
- package/readmeasset/omk-control-surfaces.svg +55 -0
- package/readmeasset/omk-core-loop.svg +1 -0
- package/readmeasset/omk-logo-mark.svg +1 -0
- package/readmeasset/omk-release-assertions.svg +51 -0
- package/dist/native/linux-x64/omk-safety +0 -0
- package/dist/util/native-safety.d.ts +0 -28
- package/dist/util/native-safety.js +0 -118
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Theme — Render Table Compiler
|
|
3
|
+
* Compiles an omk.theme.v1 document (themes/*.theme.json) into per-surface
|
|
4
|
+
* render tables at startup. For each semantic token it precomputes the SGR
|
|
5
|
+
* sequence for the requested degradation tier:
|
|
6
|
+
* truecolor → SGR 38;2;R;G;B
|
|
7
|
+
* 256 → SGR 38;5;N via the OKLab nearest-neighbor lookup (16-255 only)
|
|
8
|
+
* 16 → the theme's hand-authored ansi16 (fallback16) mapping
|
|
9
|
+
* no-color → empty string
|
|
10
|
+
* Single entrypoint: compileTheme(theme, tier).
|
|
11
|
+
*/
|
|
12
|
+
import type { ColorTier } from "./terminal-capability.js";
|
|
13
|
+
export type ThemeUsage = "text" | "indicator" | "background";
|
|
14
|
+
export type ThemeTokenKind = "state" | "chrome" | "background";
|
|
15
|
+
export type Ansi16Name = "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white" | "brightBlack" | "brightRed" | "brightGreen" | "brightYellow" | "brightBlue" | "brightMagenta" | "brightCyan" | "brightWhite";
|
|
16
|
+
export interface ThemeSemanticSpec {
|
|
17
|
+
readonly color: string;
|
|
18
|
+
readonly glyph?: string;
|
|
19
|
+
readonly kind?: ThemeTokenKind;
|
|
20
|
+
readonly usage: ThemeUsage;
|
|
21
|
+
}
|
|
22
|
+
export interface OmkThemeV1 {
|
|
23
|
+
readonly schemaVersion: "omk.theme.v1";
|
|
24
|
+
readonly name: string;
|
|
25
|
+
readonly displayName?: string;
|
|
26
|
+
readonly mode: "dark" | "light";
|
|
27
|
+
readonly primitives: Readonly<Record<string, string>>;
|
|
28
|
+
readonly backgrounds: readonly string[];
|
|
29
|
+
readonly semantics: Readonly<Record<string, ThemeSemanticSpec>>;
|
|
30
|
+
readonly components: Readonly<Record<string, Readonly<Record<string, string>>>>;
|
|
31
|
+
readonly fallback16: Readonly<Record<string, string>>;
|
|
32
|
+
}
|
|
33
|
+
export interface RenderEntry {
|
|
34
|
+
/** Semantic role this entry resolves (e.g. "dag.lane.running"). */
|
|
35
|
+
readonly role: string;
|
|
36
|
+
/** Canonical 24-bit hex of the underlying primitive. */
|
|
37
|
+
readonly hex: string;
|
|
38
|
+
/** Mandatory state glyph, or "" for chrome/background tokens. */
|
|
39
|
+
readonly glyph: string;
|
|
40
|
+
/** Precomputed SGR open sequence for the compiled tier ("" on no-color). */
|
|
41
|
+
readonly sgr: string;
|
|
42
|
+
}
|
|
43
|
+
export interface CompiledTheme {
|
|
44
|
+
readonly name: string;
|
|
45
|
+
readonly tier: ColorTier;
|
|
46
|
+
/** SGR reset for this tier ("" on no-color). */
|
|
47
|
+
readonly reset: string;
|
|
48
|
+
/** Semantic role → precomputed render entry. */
|
|
49
|
+
readonly tokens: Readonly<Record<string, RenderEntry>>;
|
|
50
|
+
/** Component surface → slot → precomputed render entry. */
|
|
51
|
+
readonly surfaces: Readonly<Record<string, Readonly<Record<string, RenderEntry>>>>;
|
|
52
|
+
/** Wrap text in the precomputed sequence for a semantic role. */
|
|
53
|
+
readonly paint: (role: string, text: string) => string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Compile an omk.theme.v1 document into a per-surface render table for one
|
|
57
|
+
* degradation tier. Pure: computes everything up front, performs no I/O.
|
|
58
|
+
*/
|
|
59
|
+
export declare function compileTheme(theme: OmkThemeV1, tier: ColorTier): CompiledTheme;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Theme — Render Table Compiler
|
|
3
|
+
* Compiles an omk.theme.v1 document (themes/*.theme.json) into per-surface
|
|
4
|
+
* render tables at startup. For each semantic token it precomputes the SGR
|
|
5
|
+
* sequence for the requested degradation tier:
|
|
6
|
+
* truecolor → SGR 38;2;R;G;B
|
|
7
|
+
* 256 → SGR 38;5;N via the OKLab nearest-neighbor lookup (16-255 only)
|
|
8
|
+
* 16 → the theme's hand-authored ansi16 (fallback16) mapping
|
|
9
|
+
* no-color → empty string
|
|
10
|
+
* Single entrypoint: compileTheme(theme, tier).
|
|
11
|
+
*/
|
|
12
|
+
import { buildXterm256Lookup, normalizeHex, hexToRgb255 } from "./oklab-quantize.js";
|
|
13
|
+
const ESC = "\u001b[";
|
|
14
|
+
const SGR_RESET = `${ESC}0m`;
|
|
15
|
+
// Foreground SGR codes for the 16-color tier (standard 30-37, bright 90-97).
|
|
16
|
+
const ANSI16_FG = {
|
|
17
|
+
black: 30,
|
|
18
|
+
red: 31,
|
|
19
|
+
green: 32,
|
|
20
|
+
yellow: 33,
|
|
21
|
+
blue: 34,
|
|
22
|
+
magenta: 35,
|
|
23
|
+
cyan: 36,
|
|
24
|
+
white: 37,
|
|
25
|
+
brightBlack: 90,
|
|
26
|
+
brightRed: 91,
|
|
27
|
+
brightGreen: 92,
|
|
28
|
+
brightYellow: 93,
|
|
29
|
+
brightBlue: 94,
|
|
30
|
+
brightMagenta: 95,
|
|
31
|
+
brightCyan: 96,
|
|
32
|
+
brightWhite: 97,
|
|
33
|
+
};
|
|
34
|
+
function isAnsi16Name(name) {
|
|
35
|
+
return Object.prototype.hasOwnProperty.call(ANSI16_FG, name);
|
|
36
|
+
}
|
|
37
|
+
function resolvePrimitiveHex(theme, role, color) {
|
|
38
|
+
const raw = theme.primitives[color];
|
|
39
|
+
if (raw === undefined) {
|
|
40
|
+
throw new Error(`compileTheme(${theme.name}): semantic "${role}" references unknown primitive "${color}"`);
|
|
41
|
+
}
|
|
42
|
+
return normalizeHex(raw);
|
|
43
|
+
}
|
|
44
|
+
function sgrForTier(theme, role, hex, tier, lookup256) {
|
|
45
|
+
switch (tier) {
|
|
46
|
+
case "truecolor": {
|
|
47
|
+
const [r, g, b] = hexToRgb255(hex);
|
|
48
|
+
return `${ESC}38;2;${r};${g};${b}m`;
|
|
49
|
+
}
|
|
50
|
+
case "256": {
|
|
51
|
+
const index = lookup256.get(hex);
|
|
52
|
+
if (index === undefined) {
|
|
53
|
+
throw new Error(`compileTheme(${theme.name}): missing 256-color lookup for "${role}" (${hex})`);
|
|
54
|
+
}
|
|
55
|
+
return `${ESC}38;5;${index}m`;
|
|
56
|
+
}
|
|
57
|
+
case "16": {
|
|
58
|
+
const name = theme.fallback16[role];
|
|
59
|
+
if (name === undefined || !isAnsi16Name(name)) {
|
|
60
|
+
throw new Error(`compileTheme(${theme.name}): semantic "${role}" has no valid ansi16 fallback (got "${name ?? "<missing>"}")`);
|
|
61
|
+
}
|
|
62
|
+
return `${ESC}${ANSI16_FG[name]}m`;
|
|
63
|
+
}
|
|
64
|
+
case "no-color":
|
|
65
|
+
return "";
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Compile an omk.theme.v1 document into a per-surface render table for one
|
|
70
|
+
* degradation tier. Pure: computes everything up front, performs no I/O.
|
|
71
|
+
*/
|
|
72
|
+
export function compileTheme(theme, tier) {
|
|
73
|
+
// Precompute the OKLab → xterm-256 lookup once per compile (build/load time).
|
|
74
|
+
const distinctHexes = Object.entries(theme.semantics).map(([role, spec]) => resolvePrimitiveHex(theme, role, spec.color));
|
|
75
|
+
const lookup256 = tier === "256" ? buildXterm256Lookup(distinctHexes) : new Map();
|
|
76
|
+
const tokens = {};
|
|
77
|
+
for (const [role, spec] of Object.entries(theme.semantics)) {
|
|
78
|
+
const hex = resolvePrimitiveHex(theme, role, spec.color);
|
|
79
|
+
tokens[role] = {
|
|
80
|
+
role,
|
|
81
|
+
hex,
|
|
82
|
+
glyph: spec.glyph ?? "",
|
|
83
|
+
sgr: sgrForTier(theme, role, hex, tier, lookup256),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const surfaces = {};
|
|
87
|
+
for (const [surface, slots] of Object.entries(theme.components)) {
|
|
88
|
+
const compiled = {};
|
|
89
|
+
for (const [slot, role] of Object.entries(slots)) {
|
|
90
|
+
const entry = tokens[role];
|
|
91
|
+
if (entry === undefined) {
|
|
92
|
+
throw new Error(`compileTheme(${theme.name}): component ${surface}.${slot} references unknown semantic role "${role}"`);
|
|
93
|
+
}
|
|
94
|
+
compiled[slot] = entry;
|
|
95
|
+
}
|
|
96
|
+
surfaces[surface] = compiled;
|
|
97
|
+
}
|
|
98
|
+
const reset = tier === "no-color" ? "" : SGR_RESET;
|
|
99
|
+
return {
|
|
100
|
+
name: theme.name,
|
|
101
|
+
tier,
|
|
102
|
+
reset,
|
|
103
|
+
tokens,
|
|
104
|
+
surfaces,
|
|
105
|
+
paint(role, text) {
|
|
106
|
+
const entry = tokens[role];
|
|
107
|
+
if (entry === undefined || entry.sgr === "")
|
|
108
|
+
return text;
|
|
109
|
+
return entry.sgr + text + reset;
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Theme — Representative Status Frame
|
|
3
|
+
* Renders the ONE representative TUI status frame shared by the four-tier
|
|
4
|
+
* degradation snapshots (test/theme-degradation.test.mjs) and the
|
|
5
|
+
* `omk theme preview <name>` CLI surface. Keeping a single renderer
|
|
6
|
+
* guarantees the CLI preview shows exactly the snapshot-gated frame.
|
|
7
|
+
*/
|
|
8
|
+
import type { CompiledTheme } from "./render-table.js";
|
|
9
|
+
/** One representative TUI status frame built from theme tokens + glyphs. */
|
|
10
|
+
export declare function renderStatusFrame(ct: CompiledTheme): string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Theme — Representative Status Frame
|
|
3
|
+
* Renders the ONE representative TUI status frame shared by the four-tier
|
|
4
|
+
* degradation snapshots (test/theme-degradation.test.mjs) and the
|
|
5
|
+
* `omk theme preview <name>` CLI surface. Keeping a single renderer
|
|
6
|
+
* guarantees the CLI preview shows exactly the snapshot-gated frame.
|
|
7
|
+
*/
|
|
8
|
+
function paintWithGlyph(ct, role, text) {
|
|
9
|
+
const entry = ct.tokens[role];
|
|
10
|
+
const glyph = entry?.glyph ?? "";
|
|
11
|
+
return ct.paint(role, glyph === "" ? text : `${glyph} ${text}`);
|
|
12
|
+
}
|
|
13
|
+
/** One representative TUI status frame built from theme tokens + glyphs. */
|
|
14
|
+
export function renderStatusFrame(ct) {
|
|
15
|
+
const p = (role, text) => paintWithGlyph(ct, role, text);
|
|
16
|
+
return [
|
|
17
|
+
`${p("control.accent", "OMK//CONTROL")} ${p("control.dim", "night-city ops console")}`,
|
|
18
|
+
`${p("dag.lane.running", "lane compile")} ${p("dag.lane.done", "lane schema")} ${p("dag.lane.queued", "lane docs")}`,
|
|
19
|
+
`${p("evidence.pass", "contrast 48/48")} ${p("evidence.pending", "snapshots")} ${p("route.fallback", "provider kimi")}`,
|
|
20
|
+
`${p("telemetry.warn", "headroom 81%")} ${p("control.fg", "tier ready")}`,
|
|
21
|
+
].join("\n");
|
|
22
|
+
}
|
|
@@ -3,15 +3,20 @@
|
|
|
3
3
|
* Detects color support, unicode, TTY status, and terminal dimensions.
|
|
4
4
|
*/
|
|
5
5
|
export type ColorDepth = 0 | 1 | 4 | 8 | 24;
|
|
6
|
+
/** Degradation tier used by the render-table compiler. */
|
|
7
|
+
export type ColorTier = "truecolor" | "256" | "16" | "no-color";
|
|
6
8
|
export interface TerminalCapability {
|
|
7
9
|
readonly isTty: boolean;
|
|
8
10
|
readonly isCi: boolean;
|
|
9
11
|
readonly colorDisabled: boolean;
|
|
10
12
|
readonly colorDepth: ColorDepth;
|
|
13
|
+
readonly colorTier: ColorTier;
|
|
11
14
|
readonly unicode: boolean;
|
|
12
15
|
readonly width: number;
|
|
13
16
|
}
|
|
14
|
-
export declare function
|
|
17
|
+
export declare function colorTierForDepth(depth: ColorDepth): ColorTier;
|
|
18
|
+
export declare function detectColorTier(argv?: readonly string[]): ColorTier;
|
|
19
|
+
export declare function getTerminalCapability(argv?: readonly string[]): TerminalCapability;
|
|
15
20
|
export declare function defaultThemeForCapability(cap: TerminalCapability): {
|
|
16
21
|
name: string;
|
|
17
22
|
mode: "dark" | "light" | "auto" | "mono";
|
|
@@ -14,8 +14,11 @@ function detectCi() {
|
|
|
14
14
|
|| process.env.APPVEYOR
|
|
15
15
|
|| process.env.JENKINS_URL);
|
|
16
16
|
}
|
|
17
|
-
function detectColorDepth() {
|
|
18
|
-
// Explicit opt-out
|
|
17
|
+
function detectColorDepth(argv = process.argv) {
|
|
18
|
+
// Explicit opt-out: --no-color CLI flag or NO_COLOR env
|
|
19
|
+
if (argv.includes("--no-color")) {
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
19
22
|
if (process.env.NO_COLOR !== undefined || process.env.TERM === "dumb") {
|
|
20
23
|
return 0;
|
|
21
24
|
}
|
|
@@ -65,14 +68,27 @@ function getTerminalWidth() {
|
|
|
65
68
|
}
|
|
66
69
|
return 80;
|
|
67
70
|
}
|
|
68
|
-
export function
|
|
71
|
+
export function colorTierForDepth(depth) {
|
|
72
|
+
if (depth === 24)
|
|
73
|
+
return "truecolor";
|
|
74
|
+
if (depth === 8)
|
|
75
|
+
return "256";
|
|
76
|
+
if (depth === 4 || depth === 1)
|
|
77
|
+
return "16";
|
|
78
|
+
return "no-color";
|
|
79
|
+
}
|
|
80
|
+
export function detectColorTier(argv = process.argv) {
|
|
81
|
+
return colorTierForDepth(detectColorDepth(argv));
|
|
82
|
+
}
|
|
83
|
+
export function getTerminalCapability(argv = process.argv) {
|
|
69
84
|
const isCi = detectCi();
|
|
70
|
-
const colorDepth = detectColorDepth();
|
|
85
|
+
const colorDepth = detectColorDepth(argv);
|
|
71
86
|
return {
|
|
72
87
|
isTty: process.stdout.isTTY ?? false,
|
|
73
88
|
isCi,
|
|
74
89
|
colorDisabled: colorDepth === 0,
|
|
75
90
|
colorDepth,
|
|
91
|
+
colorTier: colorTierForDepth(colorDepth),
|
|
76
92
|
unicode: detectUnicode(),
|
|
77
93
|
width: getTerminalWidth(),
|
|
78
94
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Theme — omk.theme.v1 Document Loader
|
|
3
|
+
* Discovers, loads, and structurally validates themes/*.theme.json documents
|
|
4
|
+
* (the executable twin of schemas/omk.theme.v1.schema.json, mirroring
|
|
5
|
+
* scripts/theme-check.mjs validateTheme()). Read-only filesystem access.
|
|
6
|
+
*/
|
|
7
|
+
import type { OmkThemeV1 } from "./render-table.js";
|
|
8
|
+
export interface ThemeDocumentRef {
|
|
9
|
+
/** Theme name derived from the file name (e.g. "night-city"). */
|
|
10
|
+
readonly name: string;
|
|
11
|
+
/** Absolute path of the .theme.json document. */
|
|
12
|
+
readonly path: string;
|
|
13
|
+
}
|
|
14
|
+
/** List omk.theme.v1 documents discoverable from cwd (or the package root). */
|
|
15
|
+
export declare function listThemeDocuments(cwd?: string): readonly ThemeDocumentRef[];
|
|
16
|
+
/** Load one theme document by name. Returns undefined when absent/unreadable. */
|
|
17
|
+
export declare function loadThemeDocument(name: string, cwd?: string): OmkThemeV1 | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Structural validation of an omk.theme.v1 document — the same executable
|
|
20
|
+
* rules scripts/theme-check.mjs enforces in CI. Returns [] when valid.
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateThemeDocument(theme: OmkThemeV1): readonly string[];
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Theme — omk.theme.v1 Document Loader
|
|
3
|
+
* Discovers, loads, and structurally validates themes/*.theme.json documents
|
|
4
|
+
* (the executable twin of schemas/omk.theme.v1.schema.json, mirroring
|
|
5
|
+
* scripts/theme-check.mjs validateTheme()). Read-only filesystem access.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
8
|
+
import { join, dirname } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
const ANSI16_NAMES = new Set([
|
|
11
|
+
"black", "red", "green", "yellow", "blue", "magenta", "cyan", "white",
|
|
12
|
+
"brightBlack", "brightRed", "brightGreen", "brightYellow",
|
|
13
|
+
"brightBlue", "brightMagenta", "brightCyan", "brightWhite",
|
|
14
|
+
]);
|
|
15
|
+
/** Candidate themes/ directories: project cwd first, then the package root. */
|
|
16
|
+
function themesDirCandidates(cwd) {
|
|
17
|
+
const moduleRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
|
|
18
|
+
const candidates = [join(cwd, "themes"), join(moduleRoot, "themes")];
|
|
19
|
+
return [...new Set(candidates)];
|
|
20
|
+
}
|
|
21
|
+
/** List omk.theme.v1 documents discoverable from cwd (or the package root). */
|
|
22
|
+
export function listThemeDocuments(cwd = process.cwd()) {
|
|
23
|
+
for (const dir of themesDirCandidates(cwd)) {
|
|
24
|
+
if (!existsSync(dir))
|
|
25
|
+
continue;
|
|
26
|
+
const refs = readdirSync(dir)
|
|
27
|
+
.filter((f) => f.endsWith(".theme.json"))
|
|
28
|
+
.map((f) => ({ name: f.replace(/\.theme\.json$/, ""), path: join(dir, f) }));
|
|
29
|
+
if (refs.length > 0)
|
|
30
|
+
return refs;
|
|
31
|
+
}
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
/** Load one theme document by name. Returns undefined when absent/unreadable. */
|
|
35
|
+
export function loadThemeDocument(name, cwd = process.cwd()) {
|
|
36
|
+
const ref = listThemeDocuments(cwd).find((r) => r.name === name);
|
|
37
|
+
if (ref === undefined)
|
|
38
|
+
return undefined;
|
|
39
|
+
try {
|
|
40
|
+
return JSON.parse(readFileSync(ref.path, "utf8"));
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Structural validation of an omk.theme.v1 document — the same executable
|
|
48
|
+
* rules scripts/theme-check.mjs enforces in CI. Returns [] when valid.
|
|
49
|
+
*/
|
|
50
|
+
export function validateThemeDocument(theme) {
|
|
51
|
+
const errors = [];
|
|
52
|
+
const err = (m) => { errors.push(m); };
|
|
53
|
+
if (theme.schemaVersion !== "omk.theme.v1")
|
|
54
|
+
err(`schemaVersion must be "omk.theme.v1"`);
|
|
55
|
+
if (!/^[a-z][a-z0-9-]*$/.test(theme.name ?? ""))
|
|
56
|
+
err("invalid theme name");
|
|
57
|
+
if (theme.mode !== "dark" && theme.mode !== "light")
|
|
58
|
+
err("mode must be dark|light");
|
|
59
|
+
const primitives = theme.primitives ?? {};
|
|
60
|
+
for (const [k, v] of Object.entries(primitives)) {
|
|
61
|
+
if (!/^#[0-9A-Fa-f]{6}$/.test(v))
|
|
62
|
+
err(`primitive ${k}: bad hex ${v}`);
|
|
63
|
+
}
|
|
64
|
+
for (const bg of theme.backgrounds ?? []) {
|
|
65
|
+
if (primitives[bg] === undefined)
|
|
66
|
+
err(`background "${bg}" is not a primitive`);
|
|
67
|
+
}
|
|
68
|
+
const semantics = theme.semantics ?? {};
|
|
69
|
+
for (const [role, spec] of Object.entries(semantics)) {
|
|
70
|
+
if (primitives[spec.color] === undefined)
|
|
71
|
+
err(`semantic ${role}: color "${spec.color}" is not a primitive`);
|
|
72
|
+
const kind = spec.kind ?? "state";
|
|
73
|
+
if (kind === "state" && (spec.glyph === undefined || spec.glyph === "")) {
|
|
74
|
+
err(`semantic state ${role}: missing mandatory glyph`);
|
|
75
|
+
}
|
|
76
|
+
if (!["text", "indicator", "background"].includes(spec.usage))
|
|
77
|
+
err(`semantic ${role}: bad usage`);
|
|
78
|
+
}
|
|
79
|
+
for (const [comp, slots] of Object.entries(theme.components ?? {})) {
|
|
80
|
+
for (const [slot, role] of Object.entries(slots)) {
|
|
81
|
+
if (semantics[role] === undefined)
|
|
82
|
+
err(`component ${comp}.${slot}: unknown semantic role "${role}"`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const fb = theme.fallback16 ?? {};
|
|
86
|
+
for (const role of Object.keys(semantics)) {
|
|
87
|
+
if (fb[role] === undefined)
|
|
88
|
+
err(`fallback16: missing entry for semantic role "${role}"`);
|
|
89
|
+
}
|
|
90
|
+
for (const [role, name] of Object.entries(fb)) {
|
|
91
|
+
if (!ANSI16_NAMES.has(name))
|
|
92
|
+
err(`fallback16 ${role}: unknown ANSI-16 name "${name}"`);
|
|
93
|
+
if (semantics[role] === undefined)
|
|
94
|
+
err(`fallback16 ${role}: no such semantic role`);
|
|
95
|
+
}
|
|
96
|
+
return errors;
|
|
97
|
+
}
|
|
@@ -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 (
|
|
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 "
|
|
149
|
-
case "
|
|
150
|
-
case "
|
|
151
|
-
case "
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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: [
|
|
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, [
|
|
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: [
|
|
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, [
|
|
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, [
|
|
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) +
|
|
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(`■
|
|
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,
|
|
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({
|