tickmarkr 1.84.0 → 1.86.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 (87) hide show
  1. package/README.md +4 -2
  2. package/dist/adapters/catalog-remote.d.ts +64 -0
  3. package/dist/adapters/catalog-remote.js +287 -0
  4. package/dist/adapters/catalog.d.ts +96 -0
  5. package/dist/adapters/catalog.js +176 -0
  6. package/dist/adapters/claude-code.d.ts +1 -0
  7. package/dist/adapters/claude-code.js +59 -1
  8. package/dist/adapters/fake.js +42 -4
  9. package/dist/adapters/model-lints.d.ts +25 -5
  10. package/dist/adapters/model-lints.js +184 -50
  11. package/dist/adapters/model-windows.d.ts +31 -0
  12. package/dist/adapters/model-windows.js +69 -0
  13. package/dist/adapters/prompt.d.ts +5 -1
  14. package/dist/adapters/prompt.js +13 -4
  15. package/dist/adapters/registry.d.ts +25 -26
  16. package/dist/adapters/registry.js +173 -110
  17. package/dist/adapters/types.d.ts +3 -0
  18. package/dist/adapters/types.js +36 -3
  19. package/dist/brand.d.ts +5 -1
  20. package/dist/brand.js +18 -2
  21. package/dist/cli/commands/doctor.d.ts +3 -0
  22. package/dist/cli/commands/doctor.js +43 -21
  23. package/dist/cli/commands/fleet.d.ts +7 -0
  24. package/dist/cli/commands/fleet.js +94 -74
  25. package/dist/cli/commands/init.js +118 -5
  26. package/dist/cli/commands/status.js +202 -46
  27. package/dist/compile/collateral.d.ts +86 -2
  28. package/dist/compile/collateral.js +294 -3
  29. package/dist/compile/gsd.d.ts +2 -1
  30. package/dist/compile/gsd.js +68 -2
  31. package/dist/compile/native.d.ts +14 -0
  32. package/dist/compile/native.js +161 -12
  33. package/dist/config/config.d.ts +82 -5
  34. package/dist/config/config.js +253 -66
  35. package/dist/config/fleet-overlay.d.ts +25 -20
  36. package/dist/config/fleet-overlay.js +195 -77
  37. package/dist/config/fleet-why.d.ts +23 -0
  38. package/dist/config/fleet-why.js +42 -0
  39. package/dist/drivers/herdr.d.ts +21 -3
  40. package/dist/drivers/herdr.js +344 -110
  41. package/dist/gates/acceptance.js +7 -2
  42. package/dist/gates/baseline.d.ts +1 -0
  43. package/dist/gates/baseline.js +91 -13
  44. package/dist/gates/llm.d.ts +0 -1
  45. package/dist/gates/llm.js +5 -30
  46. package/dist/gates/review.d.ts +9 -1
  47. package/dist/gates/review.js +105 -10
  48. package/dist/gates/run-gates.d.ts +9 -0
  49. package/dist/gates/run-gates.js +285 -41
  50. package/dist/gates/verdict-cause.d.ts +4 -0
  51. package/dist/gates/verdict-cause.js +63 -0
  52. package/dist/graph/schema.d.ts +6 -0
  53. package/dist/graph/schema.js +8 -5
  54. package/dist/route/router.d.ts +0 -5
  55. package/dist/route/router.js +16 -20
  56. package/dist/run/consult.d.ts +6 -0
  57. package/dist/run/consult.js +35 -25
  58. package/dist/run/daemon.d.ts +48 -2
  59. package/dist/run/daemon.js +1488 -330
  60. package/dist/run/journal.d.ts +56 -3
  61. package/dist/run/journal.js +358 -4
  62. package/dist/run/stall.d.ts +35 -1
  63. package/dist/run/stall.js +118 -8
  64. package/dist/tui/cockpit/capture.d.ts +12 -0
  65. package/dist/tui/cockpit/capture.js +37 -1
  66. package/dist/tui/cockpit/components.d.ts +2 -0
  67. package/dist/tui/cockpit/components.js +8 -8
  68. package/dist/tui/cockpit/derive.d.ts +29 -2
  69. package/dist/tui/cockpit/derive.js +219 -23
  70. package/dist/tui/cockpit/run-cockpit.js +128 -27
  71. package/dist/tui/cockpit/theme.d.ts +32 -26
  72. package/dist/tui/cockpit/theme.js +11 -5
  73. package/dist/tui/ink/components.d.ts +0 -15
  74. package/dist/tui/ink/components.js +0 -17
  75. package/dist/tui/ink/fleet-app.d.ts +4 -1
  76. package/dist/tui/ink/fleet-app.js +134 -13
  77. package/fixtures/sample.native.md +1 -1
  78. package/package.json +1 -1
  79. package/skills/tickmarkr-overseer/SKILL.md +354 -34
  80. package/skills/tickmarkr-overseer/scripts/watch-artifacts.sh +70 -0
  81. package/skills/tickmarkr-overseer/scripts/watch-panes.sh +1 -1
  82. package/dist/tui/ink/studio-app.d.ts +0 -59
  83. package/dist/tui/ink/studio-app.js +0 -320
  84. package/dist/tui/save.d.ts +0 -38
  85. package/dist/tui/save.js +0 -96
  86. package/dist/tui/staging.d.ts +0 -29
  87. package/dist/tui/staging.js +0 -78
@@ -7,19 +7,25 @@ export type ColourValue = {
7
7
  export declare const LOGO_RAMP: readonly [84, 78, 41, 35];
8
8
  /** Near-black panel fill against which every data shade must remain legible. */
9
9
  export declare const COCKPIT_SURFACE: "#1a1a19";
10
- /** Bright-to-deep, single-hue data ink. Depth communicates magnitude, never tile identity. */
10
+ /**
11
+ * Bright-to-deep, single-hue data ink. Depth communicates magnitude, never tile identity.
12
+ * Anchored on xterm 114 (#87d787) — operator-chosen 2026-08-03 — with the hue (OKLab 144.2°)
13
+ * held constant down the ramp; steps descend in even OKLab lightness (0.094) and were searched
14
+ * and verified through guardDataRamp below. hex is the rendered ink (truecolor terminals);
15
+ * xterm is the nearest-distinct 256-colour fallback, documentation not render source.
16
+ */
11
17
  export declare const COCKPIT_DATA_RAMP: readonly [{
12
- readonly xterm: 84;
13
- readonly hex: "#5fff5f";
18
+ readonly xterm: 114;
19
+ readonly hex: "#87d787";
14
20
  }, {
15
- readonly xterm: 41;
16
- readonly hex: "#00d75f";
21
+ readonly xterm: 71;
22
+ readonly hex: "#5bbc5e";
17
23
  }, {
18
- readonly xterm: 35;
19
- readonly hex: "#00af5f";
24
+ readonly xterm: 65;
25
+ readonly hex: "#4f9a51";
20
26
  }, {
21
27
  readonly xterm: 29;
22
- readonly hex: "#00875f";
28
+ readonly hex: "#437944";
23
29
  }];
24
30
  /** Documented near-misses retained as guard fixtures, not special cases in the guard. */
25
31
  export declare const EXCLUDED_DATA_SHADES: {
@@ -38,17 +44,17 @@ export declare const EXCLUDED_DATA_SHADES: {
38
44
  */
39
45
  export declare const COCKPIT_INKS: {
40
46
  readonly dataRamp: readonly [{
41
- readonly xterm: 84;
42
- readonly hex: "#5fff5f";
47
+ readonly xterm: 114;
48
+ readonly hex: "#87d787";
43
49
  }, {
44
- readonly xterm: 41;
45
- readonly hex: "#00d75f";
50
+ readonly xterm: 71;
51
+ readonly hex: "#5bbc5e";
46
52
  }, {
47
- readonly xterm: 35;
48
- readonly hex: "#00af5f";
53
+ readonly xterm: 65;
54
+ readonly hex: "#4f9a51";
49
55
  }, {
50
56
  readonly xterm: 29;
51
- readonly hex: "#00875f";
57
+ readonly hex: "#437944";
52
58
  }];
53
59
  readonly warning: {
54
60
  readonly xterm: 214;
@@ -65,17 +71,17 @@ export declare const COCKPIT_INKS: {
65
71
  };
66
72
  /** Every drawable cockpit ink; the panel surface and text emphasis are not inks. */
67
73
  export declare const COCKPIT_INK_VOCABULARY: readonly [{
68
- readonly xterm: 84;
69
- readonly hex: "#5fff5f";
74
+ readonly xterm: 114;
75
+ readonly hex: "#87d787";
70
76
  }, {
71
- readonly xterm: 41;
72
- readonly hex: "#00d75f";
77
+ readonly xterm: 71;
78
+ readonly hex: "#5bbc5e";
73
79
  }, {
74
- readonly xterm: 35;
75
- readonly hex: "#00af5f";
80
+ readonly xterm: 65;
81
+ readonly hex: "#4f9a51";
76
82
  }, {
77
83
  readonly xterm: 29;
78
- readonly hex: "#00875f";
84
+ readonly hex: "#437944";
79
85
  }, {
80
86
  readonly xterm: 214;
81
87
  readonly hex: "#ffaf00";
@@ -105,16 +111,16 @@ export declare const TEXT_EMPHASIS_TOKENS: {
105
111
  /** Deliberate 16-colour choices. Renderers select this map; they do not infer one. */
106
112
  export declare const REDUCED_COLOUR_MAPPING: {
107
113
  readonly dataRamp: readonly [{
108
- readonly from: "#5fff5f";
114
+ readonly from: "#87d787";
109
115
  readonly to: "bright-green";
110
116
  }, {
111
- readonly from: "#00d75f";
117
+ readonly from: "#5bbc5e";
112
118
  readonly to: "green";
113
119
  }, {
114
- readonly from: "#00af5f";
120
+ readonly from: "#4f9a51";
115
121
  readonly to: "green";
116
122
  }, {
117
- readonly from: "#00875f";
123
+ readonly from: "#437944";
118
124
  readonly to: "dim-green";
119
125
  }];
120
126
  readonly warning: {
@@ -3,12 +3,18 @@ import { BRAND_RAMP, GLYPHS } from "../../brand.js";
3
3
  export const LOGO_RAMP = BRAND_RAMP;
4
4
  /** Near-black panel fill against which every data shade must remain legible. */
5
5
  export const COCKPIT_SURFACE = "#1a1a19";
6
- /** Bright-to-deep, single-hue data ink. Depth communicates magnitude, never tile identity. */
6
+ /**
7
+ * Bright-to-deep, single-hue data ink. Depth communicates magnitude, never tile identity.
8
+ * Anchored on xterm 114 (#87d787) — operator-chosen 2026-08-03 — with the hue (OKLab 144.2°)
9
+ * held constant down the ramp; steps descend in even OKLab lightness (0.094) and were searched
10
+ * and verified through guardDataRamp below. hex is the rendered ink (truecolor terminals);
11
+ * xterm is the nearest-distinct 256-colour fallback, documentation not render source.
12
+ */
7
13
  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" },
14
+ { xterm: 114, hex: "#87d787" },
15
+ { xterm: 71, hex: "#5bbc5e" },
16
+ { xterm: 65, hex: "#4f9a51" },
17
+ { xterm: 29, hex: "#437944" },
12
18
  ];
13
19
  /** Documented near-misses retained as guard fixtures, not special cases in the guard. */
14
20
  export const EXCLUDED_DATA_SHADES = {
@@ -1,5 +1,4 @@
1
1
  import type { ReactNode } from "react";
2
- import { type SaveTarget } from "../save.js";
3
2
  export type FleetListRow = {
4
3
  id: string;
5
4
  content: ReactNode;
@@ -22,17 +21,3 @@ export declare function FleetReviewScreen({ title, legend, diff, }: {
22
21
  legend: string;
23
22
  diff: string;
24
23
  }): import("react").JSX.Element;
25
- export type DiffConfirmationProps = {
26
- target: SaveTarget;
27
- targetPath: string;
28
- diff: string;
29
- liveRun: boolean;
30
- };
31
- export declare function DiffConfirmationScreen(props: DiffConfirmationProps): import("react").JSX.Element;
32
- export declare function StudioScreen({ tabs, active, lines, status, children, }: {
33
- tabs: string[];
34
- active: number;
35
- lines: string[];
36
- status?: string;
37
- children?: ReactNode;
38
- }): import("react").JSX.Element;
@@ -1,7 +1,6 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
3
  import { GLYPHS } from "../../brand.js";
4
- import { RELOAD_GUARD_NOTICE } from "../save.js";
5
4
  function keyedLines(lines) {
6
5
  const occurrences = new Map();
7
6
  return lines.map((text) => {
@@ -24,19 +23,3 @@ export function FleetListScreen({ title, legend, rows, cursor, details = [], })
24
23
  export function FleetReviewScreen({ title, legend, diff, }) {
25
24
  return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: title }), _jsx(Text, { dimColor: true, children: legend }), _jsx(Text, { children: diff })] }));
26
25
  }
27
- export function DiffConfirmationScreen(props) {
28
- return _jsx(TextLines, { lines: [
29
- `Save overlay — ${props.targetPath} (${props.target === "repo" ? "repository" : "global"} target)`,
30
- "t toggle target · y confirm · n/esc cancel",
31
- ...(props.liveRun ? [RELOAD_GUARD_NOTICE] : []),
32
- "─ diff ─",
33
- props.diff || "(no overlay changes)",
34
- ] });
35
- }
36
- export function StudioScreen({ tabs, active, lines, status = "no staged changes", children, }) {
37
- const tabBar = [
38
- "tickmarkr ui ──",
39
- ...tabs.map((label, index) => `[${index + 1}]${index === active ? "*" : ""}${label}`),
40
- ].join(" ");
41
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: tabBar }), children ?? _jsx(TextLines, { lines: lines }), _jsx(Text, { dimColor: true, children: status })] }));
42
- }
@@ -1,4 +1,4 @@
1
- import type { AuthHealth, WorkerAdapter } from "../../adapters/types.js";
1
+ import { type AuthHealth, type WorkerAdapter } from "../../adapters/types.js";
2
2
  import type { MapEntry, RoutingMode, Tier } from "../../config/config.js";
3
3
  import { type Shape } from "../../graph/schema.js";
4
4
  export type FleetSteeringKey = "review" | "consult";
@@ -40,6 +40,7 @@ type AgentCli = {
40
40
  };
41
41
  export type FleetModelGroup = {
42
42
  adapter: string;
43
+ vendor?: string;
43
44
  rows: Array<{
44
45
  model: string;
45
46
  tier?: Tier;
@@ -51,6 +52,8 @@ export type FleetClassification = {
51
52
  model: string;
52
53
  tier: Tier;
53
54
  note: string;
55
+ vendor?: string;
56
+ channel?: "sub" | "api";
54
57
  };
55
58
  export type FleetModeOption = {
56
59
  id: RoutingMode;
@@ -3,10 +3,13 @@ import { emitKeypressEvents } from "node:readline";
3
3
  import { PassThrough } from "node:stream";
4
4
  import { render, Text, useApp, useInput } from "ink";
5
5
  import { useRef, useState } from "react";
6
+ import { MODEL_ID_RE } from "../../adapters/types.js";
7
+ import { fleetFirstTouchProvenance } from "../../config/fleet-overlay.js";
6
8
  import { TIERS } from "../../graph/schema.js";
7
9
  import { FleetListScreen, FleetReviewScreen, ToggleMark } from "./components.js";
8
10
  const STEERING_KEYS = ["review", "consult"];
9
11
  const escape = String.fromCharCode(27);
12
+ const CHANNELS = ["sub", "api"];
10
13
  const inkBookkeepingWrites = new Set(["", `${escape}[?25l`, `${escape}[?25h`, `${escape}[?2026h`, `${escape}[?2026l`]);
11
14
  function inkOutput(output) {
12
15
  if (typeof output.on === "function" && typeof output.off === "function")
@@ -117,6 +120,11 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
117
120
  const [modelGroup, setModelGroup] = useState(0);
118
121
  const modelCursorRef = useRef(0);
119
122
  const [modelCursor, setModelCursor] = useState(0);
123
+ const modelIdRef = useRef("");
124
+ const [modelId, setModelId] = useState("");
125
+ const channelCursorRef = useRef(0);
126
+ const [channelCursor, setChannelCursor] = useState(0);
127
+ const channelByAdapterRef = useRef({});
120
128
  const tierCursorRef = useRef(0);
121
129
  const [tierCursor, setTierCursor] = useState(0);
122
130
  const pendingClassificationRef = useRef(null);
@@ -160,15 +168,30 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
160
168
  const group = enabledModelGroups()[modelGroupRef.current];
161
169
  if (!group)
162
170
  return [];
163
- return group.rows.map((row) => {
171
+ const rows = group.rows.map((row) => {
164
172
  const staged = classificationsRef.current.find((classification) => classification.adapter === group.adapter && classification.model === row.model);
165
173
  return staged ? { ...row, tier: staged.tier } : row;
166
174
  });
175
+ const known = new Set(rows.map((row) => row.model));
176
+ for (const staged of classificationsRef.current) {
177
+ if (staged.adapter === group.adapter && !known.has(staged.model)) {
178
+ rows.push({ model: staged.model, tier: staged.tier });
179
+ }
180
+ }
181
+ return rows;
167
182
  };
168
183
  const editorState = () => ({
169
184
  denyAdapters: [...(denyRef.current ?? [])].sort(),
170
185
  denyModels: [...(denyModelsRef.current ?? [])].sort(),
171
- classifications: classificationsRef.current,
186
+ classifications: classificationsRef.current.map((classification) => classification.vendor && classification.channel
187
+ ? {
188
+ ...classification,
189
+ note: fleetFirstTouchProvenance(classification.note, {
190
+ vendor: classification.vendor,
191
+ channel: classification.channel,
192
+ }),
193
+ }
194
+ : classification),
172
195
  selectedMode: selectedModeRef.current,
173
196
  map: stagedMap(),
174
197
  steering: structuredClone(steeringRef.current),
@@ -186,6 +209,38 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
186
209
  screenRef.current = next;
187
210
  setScreen(next);
188
211
  };
212
+ const beginClassification = (group, model) => {
213
+ tierCursorRef.current = 0;
214
+ setTierCursor(0);
215
+ const firstTouch = !group.rows.some((row) => row.tier !== undefined);
216
+ const pending = {
217
+ adapter: group.adapter,
218
+ model,
219
+ tier: TIERS[0],
220
+ };
221
+ if (!firstTouch) {
222
+ pendingClassificationRef.current = pending;
223
+ showScreen("tiers");
224
+ return;
225
+ }
226
+ if (!group.vendor) {
227
+ setNotice(`fleet: ${group.adapter} has no vendor declaration — classification cannot be saved`);
228
+ return;
229
+ }
230
+ pending.vendor = group.vendor;
231
+ const answered = channelByAdapterRef.current[group.adapter];
232
+ if (answered) {
233
+ pending.channel = answered;
234
+ pendingClassificationRef.current = pending;
235
+ showScreen("tiers");
236
+ return;
237
+ }
238
+ pendingClassificationRef.current = pending;
239
+ channelCursorRef.current = 0;
240
+ setChannelCursor(0);
241
+ setNotice("");
242
+ showScreen("channel");
243
+ };
189
244
  useInput((input, key) => {
190
245
  if (doneRef.current)
191
246
  return;
@@ -197,7 +252,13 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
197
252
  showScreen("steering");
198
253
  return;
199
254
  }
200
- if (key.escape || (screenRef.current !== "provenance" && input === "q") || (key.ctrl && input === "c")) {
255
+ if (key.escape && screenRef.current === "add-model") {
256
+ setNotice("");
257
+ showScreen("models");
258
+ return;
259
+ }
260
+ const typing = screenRef.current === "provenance" || screenRef.current === "add-model";
261
+ if (key.escape || (!typing && input === "q") || (key.ctrl && input === "c")) {
201
262
  finish({ kind: "quit" });
202
263
  return;
203
264
  }
@@ -276,14 +337,14 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
276
337
  setNotice("fleet: tier reassignment on classified models is not supported in v1 — edit config directly");
277
338
  return;
278
339
  }
279
- tierCursorRef.current = 0;
280
- setTierCursor(0);
281
- pendingClassificationRef.current = {
282
- adapter: group.adapter,
283
- model: row.model,
284
- tier: TIERS[0],
285
- };
286
- showScreen("tiers");
340
+ beginClassification(group, row.model);
341
+ return;
342
+ }
343
+ if (input === "n" && group) {
344
+ modelIdRef.current = "";
345
+ setModelId("");
346
+ setNotice("");
347
+ showScreen("add-model");
287
348
  return;
288
349
  }
289
350
  if (key.return) {
@@ -300,6 +361,57 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
300
361
  }
301
362
  return;
302
363
  }
364
+ if (screenRef.current === "add-model") {
365
+ if (key.return) {
366
+ const group = enabledModelGroups()[modelGroupRef.current];
367
+ const candidate = modelIdRef.current;
368
+ if (!MODEL_ID_RE.test(candidate)) {
369
+ setNotice(`fleet: model id must match ${MODEL_ID_RE.source}`);
370
+ return;
371
+ }
372
+ if (currentModelRows().some((row) => row.model === candidate)) {
373
+ setNotice(`fleet: ${candidate} is already listed for ${group?.adapter ?? "this adapter"}`);
374
+ return;
375
+ }
376
+ if (group)
377
+ beginClassification(group, candidate);
378
+ return;
379
+ }
380
+ if (key.backspace || key.delete) {
381
+ const next = modelIdRef.current.slice(0, -1);
382
+ modelIdRef.current = next;
383
+ setModelId(next);
384
+ return;
385
+ }
386
+ if (input && !key.ctrl && !key.meta) {
387
+ const next = modelIdRef.current + input;
388
+ modelIdRef.current = next;
389
+ setModelId(next);
390
+ setNotice("");
391
+ }
392
+ return;
393
+ }
394
+ if (screenRef.current === "channel") {
395
+ if (key.downArrow || input === "j") {
396
+ const next = Math.min(channelCursorRef.current + 1, CHANNELS.length - 1);
397
+ channelCursorRef.current = next;
398
+ setChannelCursor(next);
399
+ return;
400
+ }
401
+ if (key.upArrow || input === "k") {
402
+ const next = Math.max(channelCursorRef.current - 1, 0);
403
+ channelCursorRef.current = next;
404
+ setChannelCursor(next);
405
+ return;
406
+ }
407
+ if (key.return && pendingClassificationRef.current) {
408
+ const channel = CHANNELS[channelCursorRef.current];
409
+ channelByAdapterRef.current[pendingClassificationRef.current.adapter] = channel;
410
+ pendingClassificationRef.current.channel = channel;
411
+ showScreen("tiers");
412
+ }
413
+ return;
414
+ }
303
415
  if (screenRef.current === "tiers") {
304
416
  if (key.downArrow || input === "j") {
305
417
  const next = Math.min(tierCursorRef.current + 1, TIERS.length - 1);
@@ -596,8 +708,12 @@ export function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels
596
708
  content: row.tier ? (_jsxs(_Fragment, { children: [_jsx(ToggleMark, { active: !denied }), _jsx(Text, { children: ` ${row.model} ${row.tier} ${denied ? "denied" : "allowed"}` })] })) : _jsx(Text, { children: `( ) ${row.model} ??? unclassified${row.detectedAt ? ` (${row.detectedAt})` : ""}` }),
597
709
  };
598
710
  });
599
- return (_jsx(FleetListScreen, { title: `step 3/6 · models · ${group?.adapter ?? ""}`, legend: "\u2191\u2193/jk move \u00B7 space toggle \u00B7 t tier \u00B7 enter next \u00B7 esc/q quit", rows: rows, cursor: modelCursor, details: notice ? [notice] : [] }));
711
+ return (_jsx(FleetListScreen, { title: `step 3/6 · models · ${group?.adapter ?? ""}`, legend: "\u2191\u2193/jk move \u00B7 space toggle \u00B7 t tier \u00B7 n add model \u00B7 enter next \u00B7 esc/q quit", rows: rows, cursor: modelCursor, details: notice ? [notice] : [] }));
600
712
  }
713
+ if (screen === "add-model")
714
+ return (_jsx(FleetListScreen, { title: `add model · ${group?.adapter ?? ""}`, legend: "type model id \u00B7 enter classify \u00B7 esc cancel", rows: [{ id: "model", content: _jsx(Text, { children: modelId || "model id (required):" }) }], cursor: 0, details: notice ? [notice] : [] }));
715
+ if (screen === "channel")
716
+ return (_jsx(FleetListScreen, { title: `channel · ${pendingClassificationRef.current?.adapter ?? ""}`, legend: "\u2191\u2193/jk move \u00B7 enter select \u00B7 esc/q quit", rows: CHANNELS.map((channel) => ({ id: channel, content: _jsx(Text, { children: channel }) })), cursor: channelCursor }));
601
717
  if (screen === "tiers") {
602
718
  return (_jsx(FleetListScreen, { title: `pick · tier · ${pendingClassificationRef.current?.adapter}:${pendingClassificationRef.current?.model}`, legend: "\u2191\u2193/jk move \u00B7 enter select \u00B7 esc/q quit", rows: TIERS.map((tier) => ({ id: tier, content: _jsx(Text, { children: tier }) })), cursor: tierCursor }));
603
719
  }
@@ -651,9 +767,14 @@ export async function runFleetInkEditor({ ageMs, adapters, health, initialDenyAd
651
767
  authed: state.authed,
652
768
  }];
653
769
  });
770
+ const vendors = new Map(adapters.map((adapter) => [adapter.id, adapter.vendor]));
771
+ const declaredModelGroups = modelGroups.map((group) => ({
772
+ ...group,
773
+ vendor: group.vendor ?? vendors.get(group.adapter),
774
+ }));
654
775
  const bridgedInput = inkInput(input, initialInput);
655
776
  const legacyOutput = typeof output.on !== "function" || typeof output.off !== "function";
656
- const app = render(_jsx(FleetApp, { ageMs: ageMs, agents: agents, initialDenyAdapters: initialDenyAdapters, initialDenyModels: initialDenyModels, modelGroups: modelGroups, initialMode: initialMode, modeOptions: modeOptions, initialMap: initialMap, modePreview: modePreview, shapeRows: shapeRows, candidatesForShape: candidatesForShape, preferOptionsForShape: preferOptionsForShape, initialSteering: initialSteering, steeringOptionsFor: steeringOptionsFor, reviewOverlay: reviewOverlay, reloadGuard: reloadGuard }), {
777
+ const app = render(_jsx(FleetApp, { ageMs: ageMs, agents: agents, initialDenyAdapters: initialDenyAdapters, initialDenyModels: initialDenyModels, modelGroups: declaredModelGroups, initialMode: initialMode, modeOptions: modeOptions, initialMap: initialMap, modePreview: modePreview, shapeRows: shapeRows, candidatesForShape: candidatesForShape, preferOptionsForShape: preferOptionsForShape, initialSteering: initialSteering, steeringOptionsFor: steeringOptionsFor, reviewOverlay: reviewOverlay, reloadGuard: reloadGuard }), {
657
778
  // FleetIO's injected stream predates Ink and did not require ref/unref.
658
779
  // Real terminal streams pass through unchanged; the compatibility facade
659
780
  // adds only those lifecycle methods and leaves input decoding to Ink.
@@ -6,7 +6,7 @@
6
6
  - shape: implement
7
7
  - deps: none
8
8
  - files: src/compile/native.ts, src/compile/index.ts
9
- - context: docs/native.md, src/graph/schema.ts
9
+ - context: docs/codebase/ARCHITECTURE.md, src/graph/schema.ts
10
10
  - complexity: 8
11
11
  - humanGate: true
12
12
  - pin: claude-code opus
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tickmarkr",
3
- "version": "1.84.0",
3
+ "version": "1.86.0",
4
4
  "description": "Spec in, verified work out.",
5
5
  "type": "module",
6
6
  "license": "MIT",