tickmarkr 1.85.0 → 1.87.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 (78) 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 +108 -0
  5. package/dist/adapters/catalog.js +189 -0
  6. package/dist/adapters/claude-code.js +5 -3
  7. package/dist/adapters/fake.js +33 -4
  8. package/dist/adapters/model-lints.d.ts +25 -5
  9. package/dist/adapters/model-lints.js +184 -50
  10. package/dist/adapters/model-windows.d.ts +31 -0
  11. package/dist/adapters/model-windows.js +69 -0
  12. package/dist/adapters/prompt.d.ts +5 -1
  13. package/dist/adapters/prompt.js +13 -4
  14. package/dist/adapters/registry.d.ts +34 -27
  15. package/dist/adapters/registry.js +215 -112
  16. package/dist/adapters/types.js +15 -3
  17. package/dist/brand.d.ts +5 -1
  18. package/dist/brand.js +18 -2
  19. package/dist/cli/commands/doctor.d.ts +3 -0
  20. package/dist/cli/commands/doctor.js +43 -21
  21. package/dist/cli/commands/fleet.d.ts +7 -0
  22. package/dist/cli/commands/fleet.js +94 -74
  23. package/dist/cli/commands/init.js +118 -5
  24. package/dist/cli/commands/plan.js +11 -1
  25. package/dist/cli/commands/resume.js +7 -1
  26. package/dist/cli/commands/status.js +44 -18
  27. package/dist/compile/gsd.d.ts +2 -1
  28. package/dist/compile/gsd.js +68 -2
  29. package/dist/compile/native.d.ts +14 -0
  30. package/dist/compile/native.js +168 -12
  31. package/dist/config/config.d.ts +20 -5
  32. package/dist/config/config.js +96 -64
  33. package/dist/config/fleet-overlay.d.ts +25 -20
  34. package/dist/config/fleet-overlay.js +195 -77
  35. package/dist/config/fleet-why.d.ts +23 -0
  36. package/dist/config/fleet-why.js +42 -0
  37. package/dist/drivers/herdr.d.ts +1 -0
  38. package/dist/drivers/herdr.js +70 -19
  39. package/dist/gates/acceptance.js +7 -2
  40. package/dist/gates/llm.d.ts +0 -1
  41. package/dist/gates/llm.js +5 -30
  42. package/dist/gates/review.d.ts +2 -1
  43. package/dist/gates/review.js +9 -7
  44. package/dist/gates/run-gates.d.ts +1 -0
  45. package/dist/gates/run-gates.js +21 -2
  46. package/dist/gates/verdict-cause.d.ts +4 -0
  47. package/dist/gates/verdict-cause.js +63 -0
  48. package/dist/graph/schema.d.ts +6 -0
  49. package/dist/graph/schema.js +8 -5
  50. package/dist/route/preference.d.ts +1 -1
  51. package/dist/route/preference.js +8 -1
  52. package/dist/route/router.d.ts +0 -5
  53. package/dist/route/router.js +16 -20
  54. package/dist/run/consult.d.ts +6 -0
  55. package/dist/run/consult.js +49 -26
  56. package/dist/run/daemon.js +81 -20
  57. package/dist/run/journal.js +87 -7
  58. package/dist/tui/cockpit/capture.d.ts +12 -0
  59. package/dist/tui/cockpit/capture.js +37 -1
  60. package/dist/tui/cockpit/components.js +8 -8
  61. package/dist/tui/cockpit/theme.d.ts +32 -26
  62. package/dist/tui/cockpit/theme.js +11 -5
  63. package/dist/tui/ink/components.d.ts +0 -15
  64. package/dist/tui/ink/components.js +0 -17
  65. package/dist/tui/ink/fleet-app.d.ts +4 -1
  66. package/dist/tui/ink/fleet-app.js +134 -13
  67. package/fixtures/gateway-models.json +1 -0
  68. package/fixtures/sample.native.md +1 -1
  69. package/package.json +1 -1
  70. package/skills/tickmarkr-overseer/SKILL.md +464 -34
  71. package/skills/tickmarkr-overseer/scripts/watch-artifacts.sh +86 -0
  72. package/skills/tickmarkr-overseer/scripts/watch-panes.sh +1 -1
  73. package/dist/tui/ink/studio-app.d.ts +0 -59
  74. package/dist/tui/ink/studio-app.js +0 -320
  75. package/dist/tui/save.d.ts +0 -38
  76. package/dist/tui/save.js +0 -96
  77. package/dist/tui/staging.d.ts +0 -29
  78. package/dist/tui/staging.js +0 -78
@@ -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.