@bli-cockpit/cli 0.2.291 → 0.2.293

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 (61) hide show
  1. package/dist/adapters/claude-attribution-discovery.js +22 -0
  2. package/dist/adapters/claude-attribution-memoized-reads.js +3 -3
  3. package/dist/adapters/claude-attribution.js +5 -2
  4. package/dist/adapters/codex-attribution-memoized-reads.js +3 -3
  5. package/dist/adapters/codex-attribution.js +30 -12
  6. package/dist/adapters/codex-segment-discovery.js +66 -0
  7. package/dist/adapters/codex-session-revision.js +139 -0
  8. package/dist/adapters/codex-session-signals.js +2 -2
  9. package/dist/adapters/pi-session-facts.js +28 -0
  10. package/dist/adapters/pi-session-reader.js +132 -0
  11. package/dist/adapters/raw-evidence-codex-reader.js +17 -7
  12. package/dist/adapters/raw-evidence-transcript-body.js +3 -0
  13. package/dist/adapters/raw-evidence-transcript-file.js +27 -4
  14. package/dist/adapters/raw-evidence-transcript-stream.js +33 -1
  15. package/dist/agent-plugins-converge.js +24 -5
  16. package/dist/collection-scope.js +2 -0
  17. package/dist/commands/backfill-checkpoint.js +3 -1
  18. package/dist/commands/backfill-history-scan.js +38 -0
  19. package/dist/commands/backfill-issues.js +8 -20
  20. package/dist/commands/backfill-scan.js +18 -40
  21. package/dist/commands/backfill-upload.js +1 -0
  22. package/dist/commands/books-usage.js +2 -1
  23. package/dist/commands/books.js +12 -4
  24. package/dist/commands/codex-hooks-trust-choice.js +51 -0
  25. package/dist/commands/codex-hooks-trust-prompt.js +172 -0
  26. package/dist/commands/codex-managed-hooks.js +226 -0
  27. package/dist/commands/codex-revision-facts.js +64 -0
  28. package/dist/commands/doctor-report.js +2 -2
  29. package/dist/commands/doctor-update.js +2 -0
  30. package/dist/commands/doctor.js +2 -0
  31. package/dist/commands/local-args-collector-setup.js +2 -0
  32. package/dist/commands/local-args-collector-status.js +5 -3
  33. package/dist/commands/local-args-notes-targets.js +35 -0
  34. package/dist/commands/local-args-tower-notes.js +7 -18
  35. package/dist/commands/local-help-commands-tower.js +3 -2
  36. package/dist/commands/local-help-commands.js +3 -2
  37. package/dist/commands/local-help-notes.js +2 -1
  38. package/dist/commands/local-help.js +1 -1
  39. package/dist/commands/memory-install-codex-hooks.js +95 -2
  40. package/dist/commands/memory-install-receipt.js +17 -1
  41. package/dist/commands/memory-install-report.js +5 -4
  42. package/dist/commands/memory-install.js +15 -6
  43. package/dist/commands/memory-log.js +13 -2
  44. package/dist/commands/memory-map.js +6 -2
  45. package/dist/commands/memory-verb-list.js +1 -1
  46. package/dist/commands/notes-writes.js +24 -0
  47. package/dist/commands/notes.js +3 -2
  48. package/dist/commands/public-root.js +1 -1
  49. package/dist/commands/session-sync-record.js +3 -1
  50. package/dist/commands/session-sync-scan.js +14 -6
  51. package/dist/commands/session-sync-shelf.js +2 -0
  52. package/dist/commands/session-sync-upload.js +1 -0
  53. package/dist/commands/setup-receipt-lines.js +2 -8
  54. package/dist/commands/sources-discovery.js +63 -14
  55. package/dist/commands/sources-reconciliation.js +7 -5
  56. package/dist/commands/sources-verdict.js +3 -0
  57. package/dist/cursors/raw-evidence-cursor.js +1 -0
  58. package/dist/image-description-session.js +4 -2
  59. package/dist/native-session-roots.js +31 -0
  60. package/dist/t3-native-homes.js +64 -0
  61. package/package.json +4 -4
@@ -9,7 +9,7 @@
9
9
  * refusal that does not say what to do next is half a message. Every gap here
10
10
  * gets ONE command or ONE click, never a paragraph and never "see the docs".
11
11
  */
12
- import { CODEX_HOOKS_TRUST_GUIDE_URL, setupPieceMark, setupReceiptLine, setupReceiptPieces, } from "@bli-cockpit/telemetry-core";
12
+ import { codexHooksTrustAction, setupPieceMark, setupReceiptLine, setupReceiptPieces, } from "@bli-cockpit/telemetry-core";
13
13
  /** Cached host facts are old after the same seven-day window as their readers. */
14
14
  const OLD_READING_MS = 7 * 24 * 60 * 60 * 1000;
15
15
  /** The one fix for each piece, by key. Present for every key the block prints. */
@@ -40,12 +40,6 @@ const REASON_FIXES = {
40
40
  };
41
41
  /** Codex hooks a person switched OFF is a decision, not a fault. */
42
42
  const UNSUPPORTED_FIX = "Switched off in your own config; nothing to do.";
43
- /**
44
- * The one exception to "a gap gets a command": Codex hooks that are installed
45
- * and waiting to be trusted are finished by a PERSON, in the Codex UI, and no
46
- * command we could print would do it.
47
- */
48
- const NEEDS_TRUST_FIX = `Open Codex and press \`/hooks\` to trust them (once, ~15s). Step by step: ${CODEX_HOOKS_TRUST_GUIDE_URL}`;
49
43
  /**
50
44
  * The block a terminal prints: the one line, then one fix per gap, then
51
45
  * nothing. A fully connected machine gets one line and no advice — a receipt
@@ -81,7 +75,7 @@ export function setupReceiptText(reading, options = {}) {
81
75
  }
82
76
  function fixFor(key, piece) {
83
77
  if (piece.status === "needs_trust")
84
- return NEEDS_TRUST_FIX;
78
+ return codexHooksTrustAction(piece.reason);
85
79
  if (piece.status === "unsupported")
86
80
  return UNSUPPORTED_FIX;
87
81
  if (piece.status === "unknown") {
@@ -1,3 +1,5 @@
1
+ import { openCodexSessionRevision } from "../adapters/codex-session-revision.js";
2
+ import { nativeSessionRoots } from "../native-session-roots.js";
1
3
  /**
2
4
  * Every agent transcript on this machine, named the way the collector names
3
5
  * its upload (BLI-11122).
@@ -29,13 +31,7 @@ import { SESSION_FILE_UUID_PATTERN, sanitizeSessionId, sessionIdFromFileName, sh
29
31
  import { isSecretLikePath, safeKeySegment } from "../adapters/raw-evidence-keys.js";
30
32
  import { describeError } from "../health-detail.js";
31
33
  export function sourcesRoots(homeDir, pathApi = path) {
32
- return {
33
- codex: [
34
- pathApi.join(homeDir, ".codex", "sessions"),
35
- pathApi.join(homeDir, ".codex", "archived_sessions"),
36
- ],
37
- claudeProjects: pathApi.join(homeDir, ".claude", "projects"),
38
- };
34
+ return nativeSessionRoots(homeDir, pathApi);
39
35
  }
40
36
  /** Which kind of transcript a path is, or null for anything the collector does not upload as one. */
41
37
  export function classifyTranscriptPath(filePath, roots, pathApi = path) {
@@ -46,7 +42,13 @@ export function classifyTranscriptPath(filePath, roots, pathApi = path) {
46
42
  if (rel && !rel.startsWith("..") && !pathApi.isAbsolute(rel))
47
43
  return { kind: "codex_rollout" };
48
44
  }
49
- const rel = pathApi.relative(roots.claudeProjects, filePath);
45
+ const claudeRoot = (roots.claudeProjectDirs ?? [roots.claudeProjects]).find((root) => {
46
+ const relative = pathApi.relative(root, filePath);
47
+ return relative && !relative.startsWith("..") && !pathApi.isAbsolute(relative);
48
+ });
49
+ if (!claudeRoot)
50
+ return null;
51
+ const rel = pathApi.relative(claudeRoot, filePath);
50
52
  if (!rel || rel.startsWith("..") || pathApi.isAbsolute(rel))
51
53
  return null;
52
54
  const parts = rel.split(pathApi.sep);
@@ -128,15 +130,18 @@ function trimmed(value) {
128
130
  }
129
131
  /**
130
132
  * Walk every root, name every transcript. `olderThanMs` keeps only files whose
131
- * mtime is before it; the rest are counted, never read.
133
+ * revision mtime is before it. Native identities are read before that filter
134
+ * so an older segment cannot be misreported as the whole retained session.
132
135
  */
133
136
  export async function discoverLocalTranscripts(options) {
134
137
  const roots = sourcesRoots(options.homeDir);
135
- const result = { files: [], newer_count: 0, directory_read_failed: 0, file_read_failed: 0 };
138
+ const result = { files: [], newer_count: 0, directory_read_failed: roots.discoveryFailed ? 1 : 0, file_read_failed: 0 };
136
139
  let firstFailure = null;
137
140
  const recordFailure = (error) => { firstFailure ??= describeError(error); };
138
141
  const found = await findTranscriptFiles(options, roots, result, recordFailure);
139
142
  await nameTranscriptFiles(found, result, recordFailure);
143
+ await groupCodexRevisions(result);
144
+ result.files = result.files.filter(f => options.olderThanMs === null || f.mtime_ms < options.olderThanMs);
140
145
  if (result.directory_read_failed > 0 || result.file_read_failed > 0) {
141
146
  console.error("[collector sources] some transcripts could not be read; they are left out of the answer", JSON.stringify({
142
147
  reason: "sources_discovery_incomplete",
@@ -153,12 +158,17 @@ function shapeOrder(shape) {
153
158
  }
154
159
  async function findTranscriptFiles(options, roots, result, recordFailure) {
155
160
  const found = [];
156
- const stack = options.skipAll ? [] : [...(options.skipCodex ? [] : roots.codex), ...(options.skipClaude ? [] : [roots.claudeProjects])];
161
+ const stack = options.skipAll ? [] : [...(options.skipCodex ? [] : roots.codex), ...(options.skipClaude ? [] : (roots.claudeProjectDirs ?? [roots.claudeProjects]))];
157
162
  const seen = new Set();
163
+ const visitedDirectories = new Set();
158
164
  while (stack.length > 0) {
159
165
  const dir = stack.pop();
160
166
  let entries;
161
167
  try {
168
+ const directoryIdentity = await fs.realpath(dir);
169
+ if (visitedDirectories.has(directoryIdentity))
170
+ continue;
171
+ visitedDirectories.add(directoryIdentity);
162
172
  entries = await fs.readdir(dir, { withFileTypes: true });
163
173
  }
164
174
  catch (error) {
@@ -187,10 +197,8 @@ async function findTranscriptFiles(options, roots, result, recordFailure) {
187
197
  });
188
198
  if (!stat)
189
199
  continue;
190
- if (options.olderThanMs !== null && stat.mtimeMs >= options.olderThanMs) {
200
+ if (options.olderThanMs !== null && stat.mtimeMs >= options.olderThanMs)
191
201
  result.newer_count += 1;
192
- continue;
193
- }
194
202
  found.push({ file: full, size: stat.size, mtimeMs: stat.mtimeMs, shape });
195
203
  }
196
204
  }
@@ -237,4 +245,45 @@ async function nameTranscriptFiles(found, result, recordFailure) {
237
245
  key: transcriptKeyFor(source, named.session_id, item.shape.kind === "claude_subagent" ? item.shape.sidecar_stem : null),
238
246
  });
239
247
  }
248
+ }
249
+ /** One source row/pointer names the complete native session revision, never an arbitrary segment. */
250
+ async function groupCodexRevisions(result) {
251
+ const groups = new Map();
252
+ for (const file of result.files)
253
+ if (file.source === "codex") {
254
+ const group = groups.get(file.session_id) ?? [];
255
+ group.push(file);
256
+ groups.set(file.session_id, group);
257
+ }
258
+ const replacements = new Map();
259
+ for (const [id, files] of groups) {
260
+ if (files.length < 2)
261
+ continue;
262
+ const representative = [...files].sort((a, b) => a.file_path.localeCompare(b.file_path))[0];
263
+ const combined = { ...representative, segment_paths: files.map(f => f.file_path).sort(),
264
+ segment_stamps: files.map(f => ({ path: f.file_path, size: f.byte_size, mtime: f.mtime_ms })),
265
+ byte_size: files.reduce((n, f) => n + f.byte_size, 0), mtime_ms: Math.max(...files.map(f => f.mtime_ms)) };
266
+ try {
267
+ const source = await openCodexSessionRevision(combined.segment_paths);
268
+ try {
269
+ combined.byte_size = source.length;
270
+ }
271
+ finally {
272
+ await source.close();
273
+ }
274
+ }
275
+ catch {
276
+ result.file_read_failed++;
277
+ }
278
+ replacements.set(id, combined);
279
+ }
280
+ const emitted = new Set();
281
+ result.files = result.files.flatMap(file => {
282
+ if (file.source !== "codex" || !replacements.has(file.session_id))
283
+ return [file];
284
+ if (emitted.has(file.session_id))
285
+ return [];
286
+ emitted.add(file.session_id);
287
+ return [replacements.get(file.session_id)];
288
+ });
240
289
  }
@@ -22,7 +22,7 @@ export async function fetchSourceInventory(options) {
22
22
  throw new Error("invalid_response");
23
23
  const valid = body.rows.every((row) => row && typeof row.id === "string" && uuid.test(row.id) &&
24
24
  typeof row.source === "string" && (row.codex_session_id === null || typeof row.codex_session_id === "string") &&
25
- (row.raw_evidence_pointer_id === null || (typeof row.raw_evidence_pointer_id === "string" && uuid.test(row.raw_evidence_pointer_id))));
25
+ (row.raw_evidence_pointer_id === null || (typeof row.raw_evidence_pointer_id === "string" && row.raw_evidence_pointer_id.trim().length > 0)));
26
26
  if (!valid)
27
27
  throw new Error("invalid_response");
28
28
  const next = body.next_after;
@@ -67,14 +67,16 @@ export async function reconcileSource(input) {
67
67
  return inventory.status === "exhausted"
68
68
  ? answer("missing_pointer", "source_rows_without_pointer") : answer("unavailable_evidence", `row_inventory_${inventory.status}`);
69
69
  const objects = (remote.files.get(file.key) ?? []).filter(object => !object.stub &&
70
- (file.kind === "claude_subagent" || (object.ref_id && pointers.has(object.ref_id))));
70
+ (file.kind === "claude_subagent" || ((object.ref_id && pointers.has(object.ref_id)) || pointers.has(object.object_key))));
71
71
  if (!objects.length)
72
72
  return answer("unavailable_evidence", remote.status === "exhausted" ? "pointer_not_in_current_transcripts_or_stub" : `transcripts_${remote.status}`);
73
73
  const split = facts.slimSplit ? await facts.slimSplit() : null;
74
74
  const form = split?.slim ?? (facts.slimAware && facts.wholeForm ? await facts.wholeForm() : await facts.sanitized());
75
- const stat = await fs.stat(file.file_path);
76
- if (stat.size !== file.byte_size || stat.mtimeMs !== file.mtime_ms)
77
- return answer("unavailable_evidence", "local_file_changed_during_check");
75
+ for (const observed of file.segment_stamps ?? [{ path: file.file_path, size: file.byte_size, mtime: file.mtime_ms }]) {
76
+ const stat = await fs.stat(observed.path);
77
+ if (stat.size !== observed.size || stat.mtimeMs !== observed.mtime)
78
+ return answer("unavailable_evidence", "local_file_changed_during_check");
79
+ }
78
80
  if (form && objects.some(object => object.content_hash_sha256 === form.sha256 || object.content_hash_sha256 === split?.full.sha256))
79
81
  return answer("revision_verified", "current_redacted_hash_matches_pointer");
80
82
  // Sync refreshes the observed file hash even when growth damping retains an older
@@ -32,6 +32,7 @@
32
32
  *
33
33
  * Read-only: this module reads the file and never writes, moves or deletes it.
34
34
  */
35
+ import { codexRevisionFacts } from "./codex-revision-facts.js";
35
36
  import crypto from "node:crypto";
36
37
  import fs from "node:fs/promises";
37
38
  import path from "node:path";
@@ -162,6 +163,8 @@ upload = {}) {
162
163
  const mode = upload.mode ?? IMAGE_HANDLING_MODE;
163
164
  const describedKeys = upload.describedKeys ?? new Set();
164
165
  const slimAware = upload.slimAware ?? modeDropsImages(mode);
166
+ if (file.segment_paths)
167
+ return codexRevisionFacts(file.segment_paths, mode, describedKeys, slimAware);
165
168
  const redactForUpload = (text, original) => redactTranscriptForm(file, text, original);
166
169
  let split = null;
167
170
  let whole = null;
@@ -204,6 +204,7 @@ function parseSessionEntry(value) {
204
204
  return null;
205
205
  return {
206
206
  file_hash_sha256: optionalString(record["file_hash_sha256"]),
207
+ ...(typeof record["uploaded_source_revision"] === "string" ? { uploaded_source_revision: record["uploaded_source_revision"] } : {}),
207
208
  file_mtime_ms: optionalNumber(record["file_mtime_ms"]),
208
209
  byte_size: optionalNumber(record["byte_size"]),
209
210
  byte_offset: optionalNumber(record["byte_offset"]),
@@ -16,7 +16,7 @@ import { sha256OfBase64Run } from "./image-byte-scan.js";
16
16
  import { addPdfsToFinds, mergeOccurrences, pdfsOfText } from "./pdf-transcript-step.js";
17
17
  import { openPinnedTranscript } from "./adapters/pinned-transcript.js";
18
18
  import { openStagedOutput, readStagedText } from "./adapters/staged-output.js";
19
- import { TranscriptNotStreamable } from "./adapters/transcript-text-stream.js";
19
+ import { TranscriptNotStreamable, TranscriptTooLargeError } from "./adapters/transcript-text-stream.js";
20
20
  import { copyLinesStream } from "./slim/keep-line-copy.js";
21
21
  import { LineBytesSink, slimLineStream } from "./slim/slim-stream.js";
22
22
  import { logSlimmed, slimOrKeep, slimPassOrNull, slimTranscript } from "./slim/slim-transcript.js";
@@ -133,9 +133,11 @@ async function prepareFileInTwoPasses(run, input) {
133
133
  const startedAt = Date.now();
134
134
  // The file is opened once and its length pinned: the census offsets, the describe payload reads and the swap
135
135
  // pass all read these bytes, and a source that stopped being them is set aside (`verifyUnchanged`).
136
- const source = await openPinnedTranscript(input.filePath, { maxFileBytes: input.maxFileBytes });
136
+ const source = input.openSource ? await input.openSource() : await openPinnedTranscript(input.filePath, { maxFileBytes: input.maxFileBytes });
137
137
  let writer;
138
138
  try {
139
+ if (input.maxFileBytes && source.length > input.maxFileBytes)
140
+ throw new TranscriptTooLargeError(input.maxFileBytes);
139
141
  // Pass one: discover. Nothing of the text is kept; only where each image is and what it is.
140
142
  const census = await censusOfLines(source.lines());
141
143
  // Describe attempts finish and the swap decisions are settled before a single line is swapped, as with the whole text.
@@ -0,0 +1,31 @@
1
+ /** Shared native homes keep sync, backfill and reconciliation on the same inventory. */
2
+ import path from "node:path";
3
+ import { t3ImportedNativeHomes } from "./t3-native-homes.js";
4
+ export function nativeSessionRoots(homeDir, pathApi = path, environment = process.env) {
5
+ let importedHomes = [];
6
+ let discoveryFailed = false;
7
+ try {
8
+ importedHomes = t3ImportedNativeHomes(homeDir);
9
+ }
10
+ catch {
11
+ discoveryFailed = true;
12
+ } // The reader logged its reason; existing native homes remain available.
13
+ const expandHome = (value) => value === "~"
14
+ ? homeDir
15
+ : /^~[/\\]/u.test(value) ? pathApi.join(homeDir, value.slice(2)) : value;
16
+ const homes = (source, defaultHome, configured) => {
17
+ const extra = configured?.trim();
18
+ // Relative environment values depend on the launching app's cwd. Never guess it.
19
+ return [...new Set([defaultHome, ...importedHomes.filter((entry) => entry.source === source).map((entry) => entry.home), ...(extra && pathApi.isAbsolute(expandHome(extra)) ? [expandHome(extra)] : [])]
20
+ .map((home) => pathApi.normalize(home)))];
21
+ };
22
+ const claudeProjectDirs = homes("claude_code", pathApi.join(homeDir, ".claude"), environment.CLAUDE_CONFIG_DIR)
23
+ .map((home) => pathApi.join(home, "projects"));
24
+ return {
25
+ discoveryFailed,
26
+ codex: homes("codex", pathApi.join(homeDir, ".codex"), environment.CODEX_HOME)
27
+ .flatMap((home) => [pathApi.join(home, "sessions"), pathApi.join(home, "archived_sessions")]),
28
+ claudeProjects: claudeProjectDirs[0],
29
+ claudeProjectDirs,
30
+ };
31
+ }
@@ -0,0 +1,64 @@
1
+ /** Imported native references reveal homes without opening T3 settings or credentials. */
2
+ import { existsSync } from "node:fs";
3
+ import { createRequire } from "node:module";
4
+ import path from "node:path";
5
+ const MAX_IMPORTED_REFERENCES = 10_000;
6
+ /** Read only the installed 0.1.7 import-path projection, including live WAL state. */
7
+ export function t3ImportedNativeHomes(homeDir) {
8
+ const databasePath = path.join(homeDir, ".t3", "userdata", "statev2.sqlite");
9
+ if (!existsSync(databasePath))
10
+ return [];
11
+ let database;
12
+ try {
13
+ // Optional runtime door: the public CLI still supports Node 20. A T3 store on
14
+ // that runtime must name the missing reader rather than report complete coverage.
15
+ const { DatabaseSync } = createRequire(import.meta.url)("node:sqlite");
16
+ database = new DatabaseSync(databasePath, { readOnly: true });
17
+ // Read explicit session columns only. No messages, settings, auth tables,
18
+ // attached databases or application write pragmas. One statement is a WAL snapshot.
19
+ const references = database.prepare(`
20
+ SELECT DISTINCT json_extract(reference.value, '$.provider') AS provider,
21
+ json_extract(reference.value, '$.filePath') AS file_path
22
+ FROM provider_session_runtime AS session,
23
+ json_each(session.runtime_payload_json, '$.importedTranscripts') AS reference
24
+ LIMIT ${MAX_IMPORTED_REFERENCES + 1}
25
+ `).all();
26
+ if (references.length > MAX_IMPORTED_REFERENCES)
27
+ throw new Error("t3_import_inventory_capped");
28
+ const homes = new Map();
29
+ for (const reference of references) {
30
+ const home = nativeHomeFromImport(reference.provider, reference.file_path);
31
+ if (home)
32
+ homes.set(`${home.source}:${home.home}`, home);
33
+ }
34
+ console.error("[native-session-roots] T3 import homes discovered", JSON.stringify({
35
+ reason: "t3_import_homes_read", references: references.length, unique_homes: homes.size,
36
+ }));
37
+ return [...homes.values()];
38
+ }
39
+ catch (error) {
40
+ // Never turn an unreadable/capped inventory into a green empty scan.
41
+ const reason = error instanceof Error && error.message === "t3_import_inventory_capped"
42
+ ? "t3_import_inventory_capped" : "t3_import_inventory_unreadable";
43
+ console.error("[native-session-roots] T3 discovery failed", JSON.stringify({ reason,
44
+ error_code: error?.code ?? "unknown" }));
45
+ throw new Error(reason);
46
+ }
47
+ finally {
48
+ database?.close();
49
+ }
50
+ }
51
+ function nativeHomeFromImport(provider, filePath) {
52
+ if (typeof filePath !== "string" || !path.isAbsolute(filePath) || !filePath.endsWith(".jsonl"))
53
+ return null;
54
+ const parts = path.normalize(filePath).split(path.sep);
55
+ const source = provider === "codex" ? "codex" : provider === "claudeAgent" ? "claude_code" : null;
56
+ if (!source)
57
+ return null;
58
+ const marker = source === "codex"
59
+ ? Math.max(parts.lastIndexOf("sessions"), parts.lastIndexOf("archived_sessions"))
60
+ : parts.lastIndexOf("projects");
61
+ if (marker < 1)
62
+ return null;
63
+ return { source, home: parts.slice(0, marker).join(path.sep) || path.parse(filePath).root };
64
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bli-cockpit/cli",
3
- "version": "0.2.291",
3
+ "version": "0.2.293",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -27,9 +27,9 @@
27
27
  "test": "node dist/cli.js --help && node ../../scripts/assert-public-cli-routing.mjs && node ../../scripts/assert-public-cli-verb-help.mjs && node ../../scripts/assert-public-cli-surface-doors.mjs && node ../../scripts/assert-public-cli-runtime-files.mjs && node ../../scripts/assert-public-cli-exit-contract.mjs && node ../../scripts/assert-public-cli-no-fleet-posts.mjs && node ../../scripts/assert-public-cli-doctor-json.mjs && node ../../scripts/assert-public-package-pack.mjs --workspace=@bli-cockpit/cli"
28
28
  },
29
29
  "dependencies": {
30
- "@bli-cockpit/memory-mcp": "0.1.89",
31
- "@bli-cockpit/mcp": "0.1.176",
32
- "@bli-cockpit/telemetry-core": "0.1.95",
30
+ "@bli-cockpit/memory-mcp": "0.1.91",
31
+ "@bli-cockpit/mcp": "0.1.178",
32
+ "@bli-cockpit/telemetry-core": "0.1.97",
33
33
  "unpdf": "1.8.1"
34
34
  },
35
35
  "repository": {