tickmarkr 1.78.0 → 1.80.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 (39) hide show
  1. package/dist/adapters/claude-code.d.ts +11 -0
  2. package/dist/adapters/claude-code.js +105 -16
  3. package/dist/adapters/kimi.d.ts +6 -0
  4. package/dist/adapters/kimi.js +88 -26
  5. package/dist/adapters/registry.js +6 -1
  6. package/dist/brand.d.ts +4 -0
  7. package/dist/brand.js +4 -0
  8. package/dist/cli/commands/doctor.d.ts +2 -0
  9. package/dist/cli/commands/doctor.js +23 -0
  10. package/dist/cli/commands/status.d.ts +4 -0
  11. package/dist/cli/commands/status.js +237 -18
  12. package/dist/cli/commands/ui.d.ts +1 -1
  13. package/dist/cli/commands/ui.js +11 -1
  14. package/dist/drivers/herdr.js +36 -7
  15. package/dist/drivers/types.d.ts +22 -1
  16. package/dist/gates/acceptance.d.ts +5 -0
  17. package/dist/gates/acceptance.js +5 -3
  18. package/dist/gates/llm.d.ts +9 -0
  19. package/dist/gates/llm.js +74 -0
  20. package/dist/gates/review.d.ts +5 -0
  21. package/dist/gates/review.js +5 -3
  22. package/dist/run/journal.d.ts +1 -1
  23. package/dist/tui/cockpit/capture.d.ts +117 -0
  24. package/dist/tui/cockpit/capture.js +329 -0
  25. package/dist/tui/cockpit/components.d.ts +179 -0
  26. package/dist/tui/cockpit/components.js +280 -0
  27. package/dist/tui/cockpit/demo.d.ts +41 -0
  28. package/dist/tui/cockpit/demo.js +144 -0
  29. package/dist/tui/cockpit/derive.d.ts +39 -0
  30. package/dist/tui/cockpit/derive.js +390 -0
  31. package/dist/tui/cockpit/layout.d.ts +76 -0
  32. package/dist/tui/cockpit/layout.js +84 -0
  33. package/dist/tui/cockpit/run-cockpit.d.ts +19 -0
  34. package/dist/tui/cockpit/run-cockpit.js +194 -0
  35. package/dist/tui/cockpit/setup-cockpit.d.ts +41 -0
  36. package/dist/tui/cockpit/setup-cockpit.js +488 -0
  37. package/dist/tui/cockpit/theme.d.ts +161 -0
  38. package/dist/tui/cockpit/theme.js +170 -0
  39. package/package.json +2 -1
@@ -0,0 +1,170 @@
1
+ import { BRAND_RAMP, GLYPHS } from "../../brand.js";
2
+ /** The logo remains governed by src/brand.ts; cockpit data never modifies this ramp. */
3
+ export const LOGO_RAMP = BRAND_RAMP;
4
+ /** Near-black panel fill against which every data shade must remain legible. */
5
+ export const COCKPIT_SURFACE = "#1a1a19";
6
+ /** Bright-to-deep, single-hue data ink. Depth communicates magnitude, never tile identity. */
7
+ export const COCKPIT_DATA_RAMP = [
8
+ { xterm: 84, hex: "#5fff5f" },
9
+ { xterm: 41, hex: "#00d75f" },
10
+ { xterm: 35, hex: "#00af5f" },
11
+ { xterm: 29, hex: "#00875f" },
12
+ ];
13
+ /** Documented near-misses retained as guard fixtures, not special cases in the guard. */
14
+ export const EXCLUDED_DATA_SHADES = {
15
+ neighbourCollision: { xterm: 78, hex: "#5fd787" },
16
+ insufficientContrast: { xterm: 22, hex: "#005f00" },
17
+ };
18
+ /**
19
+ * The only coloured semantic inks outside the green ramp. Shape and text make
20
+ * both meanings survive NO_COLOR and reduced-colour terminals.
21
+ */
22
+ export const COCKPIT_INKS = {
23
+ dataRamp: COCKPIT_DATA_RAMP,
24
+ warning: {
25
+ xterm: 214,
26
+ hex: "#ffaf00",
27
+ glyph: GLYPHS.attention,
28
+ label: "warning",
29
+ },
30
+ failure: {
31
+ xterm: 203,
32
+ hex: "#ff5f5f",
33
+ glyph: GLYPHS.fail,
34
+ label: "failure",
35
+ },
36
+ };
37
+ /** Every drawable cockpit ink; the panel surface and text emphasis are not inks. */
38
+ export const COCKPIT_INK_VOCABULARY = [
39
+ ...COCKPIT_DATA_RAMP,
40
+ COCKPIT_INKS.warning,
41
+ COCKPIT_INKS.failure,
42
+ ];
43
+ /** Text hierarchy is independent of hue: weight and dimming are its only axes. */
44
+ export const TEXT_EMPHASIS_TOKENS = {
45
+ strong: { weight: "bold", dimmed: false },
46
+ normal: { weight: "normal", dimmed: false },
47
+ dim: { weight: "normal", dimmed: true },
48
+ };
49
+ /** Deliberate 16-colour choices. Renderers select this map; they do not infer one. */
50
+ export const REDUCED_COLOUR_MAPPING = {
51
+ dataRamp: [
52
+ { from: COCKPIT_DATA_RAMP[0].hex, to: "bright-green" },
53
+ { from: COCKPIT_DATA_RAMP[1].hex, to: "green" },
54
+ { from: COCKPIT_DATA_RAMP[2].hex, to: "green" },
55
+ { from: COCKPIT_DATA_RAMP[3].hex, to: "dim-green" },
56
+ ],
57
+ warning: { from: COCKPIT_INKS.warning.hex, to: "yellow" },
58
+ failure: { from: COCKPIT_INKS.failure.hex, to: "red" },
59
+ };
60
+ /**
61
+ * Resolve reusable tile ink from magnitude alone. Tile names and positions are
62
+ * deliberately absent from the contract, so separate tiles cannot acquire identity hues.
63
+ */
64
+ export function resolveTileColour({ magnitude }) {
65
+ const ink = COCKPIT_DATA_RAMP[magnitude];
66
+ if (!ink)
67
+ throw new RangeError(`invalid tile magnitude: ${magnitude}`);
68
+ return ink;
69
+ }
70
+ export const DATA_RAMP_GUARD = {
71
+ requiredSteps: 4,
72
+ minimumContrast: 3,
73
+ minimumNeighbourDistance: 0.08,
74
+ maximumLightnessStepSpread: 0.02,
75
+ };
76
+ const parseHex = (hex) => {
77
+ const match = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex);
78
+ if (!match)
79
+ return undefined;
80
+ return [
81
+ Number.parseInt(match[1], 16) / 255,
82
+ Number.parseInt(match[2], 16) / 255,
83
+ Number.parseInt(match[3], 16) / 255,
84
+ ];
85
+ };
86
+ const linearChannel = (channel) => channel <= 0.04045
87
+ ? channel / 12.92
88
+ : ((channel + 0.055) / 1.055) ** 2.4;
89
+ const relativeLuminance = ([red, green, blue]) => (0.2126 * linearChannel(red))
90
+ + (0.7152 * linearChannel(green))
91
+ + (0.0722 * linearChannel(blue));
92
+ const toOklab = ([red, green, blue]) => {
93
+ const r = linearChannel(red);
94
+ const g = linearChannel(green);
95
+ const b = linearChannel(blue);
96
+ const l = Math.cbrt((0.4122214708 * r) + (0.5363325363 * g) + (0.0514459929 * b));
97
+ const m = Math.cbrt((0.2119034982 * r) + (0.6806995451 * g) + (0.1073969566 * b));
98
+ const s = Math.cbrt((0.0883024619 * r) + (0.2817188376 * g) + (0.6299787005 * b));
99
+ return [
100
+ (0.2104542553 * l) + (0.793617785 * m) - (0.0040720468 * s),
101
+ (1.9779984951 * l) - (2.428592205 * m) + (0.4505937099 * s),
102
+ (0.0259040371 * l) + (0.7827717662 * m) - (0.808675766 * s),
103
+ ];
104
+ };
105
+ const oklabDistance = (left, right) => Math.hypot(left[0] - right[0], left[1] - right[1], left[2] - right[2]);
106
+ const contrastRatio = (foreground, background) => {
107
+ const foregroundLuminance = relativeLuminance(foreground);
108
+ const backgroundLuminance = relativeLuminance(background);
109
+ const lighter = Math.max(foregroundLuminance, backgroundLuminance);
110
+ const darker = Math.min(foregroundLuminance, backgroundLuminance);
111
+ return (lighter + 0.05) / (darker + 0.05);
112
+ };
113
+ /**
114
+ * Evaluate any four candidate values. Xterm indexes and declared palette names
115
+ * are intentionally ignored: only parsed colour values can earn a verdict.
116
+ */
117
+ export function guardDataRamp(candidate, surface = COCKPIT_SURFACE) {
118
+ const issues = [];
119
+ if (candidate.length !== DATA_RAMP_GUARD.requiredSteps) {
120
+ issues.push({ kind: "step-count", indexes: candidate.map((_, index) => index) });
121
+ }
122
+ const parsed = candidate.map((step) => parseHex(step.hex));
123
+ const invalidIndexes = parsed.flatMap((value, index) => value ? [] : [index]);
124
+ const surfaceRgb = parseHex(surface);
125
+ if (invalidIndexes.length > 0 || !surfaceRgb) {
126
+ issues.push({
127
+ kind: "invalid-colour",
128
+ indexes: !surfaceRgb ? [-1, ...invalidIndexes] : invalidIndexes,
129
+ });
130
+ }
131
+ const labs = parsed.map((value) => value ? toOklab(value) : undefined);
132
+ const lightness = labs.map((value) => value?.[0] ?? Number.NaN);
133
+ const lightnessSteps = lightness.slice(1).map((value, index) => lightness[index] - value);
134
+ const neighbourDistances = labs.slice(1).map((value, index) => {
135
+ const previous = labs[index];
136
+ return previous && value ? oklabDistance(previous, value) : Number.NaN;
137
+ });
138
+ const contrastRatios = parsed.map((value) => value && surfaceRgb ? contrastRatio(value, surfaceRgb) : Number.NaN);
139
+ const unorderedIndexes = lightnessSteps.flatMap((step, index) => Number.isFinite(step) && step <= 0 ? [index, index + 1] : []);
140
+ if (unorderedIndexes.length > 0) {
141
+ issues.push({ kind: "lightness-order", indexes: [...new Set(unorderedIndexes)] });
142
+ }
143
+ const finiteSteps = lightnessSteps.filter(Number.isFinite);
144
+ if (finiteSteps.length > 1
145
+ && Math.max(...finiteSteps) - Math.min(...finiteSteps)
146
+ > DATA_RAMP_GUARD.maximumLightnessStepSpread) {
147
+ issues.push({
148
+ kind: "lightness-spacing",
149
+ indexes: lightnessSteps.flatMap((_, index) => [index, index + 1]),
150
+ });
151
+ }
152
+ const collisionIndexes = neighbourDistances.flatMap((distance, index) => Number.isFinite(distance) && distance < DATA_RAMP_GUARD.minimumNeighbourDistance
153
+ ? [index, index + 1]
154
+ : []);
155
+ if (collisionIndexes.length > 0) {
156
+ issues.push({ kind: "neighbour-collision", indexes: [...new Set(collisionIndexes)] });
157
+ }
158
+ const contrastIndexes = contrastRatios.flatMap((ratio, index) => Number.isFinite(ratio) && ratio < DATA_RAMP_GUARD.minimumContrast ? [index] : []);
159
+ if (contrastIndexes.length > 0) {
160
+ issues.push({ kind: "contrast", indexes: contrastIndexes });
161
+ }
162
+ return {
163
+ ok: issues.length === 0,
164
+ lightness,
165
+ lightnessSteps,
166
+ neighbourDistances,
167
+ contrastRatios,
168
+ issues,
169
+ };
170
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tickmarkr",
3
- "version": "1.78.0",
3
+ "version": "1.80.0",
4
4
  "description": "Spec in, verified work out.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -31,6 +31,7 @@
31
31
  ],
32
32
  "scripts": {
33
33
  "build": "tsc -p tsconfig.json",
34
+ "build:clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && npm run build",
34
35
  "lint": "oxlint src tests scripts",
35
36
  "pretest": "npm run build",
36
37
  "test": "vitest run",