@bli-cockpit/cli 0.2.49 → 0.2.51
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/dist/adapters/raw-evidence-claude-reader.js +108 -0
- package/dist/adapters/raw-evidence-codex-reader.js +147 -0
- package/dist/adapters/raw-evidence-collection-state.js +199 -0
- package/dist/adapters/raw-evidence-facts.js +338 -0
- package/dist/adapters/raw-evidence-git-diff-reader.js +187 -0
- package/dist/adapters/raw-evidence-image-reader.js +107 -0
- package/dist/adapters/raw-evidence-sanitize.js +56 -0
- package/dist/adapters/raw-evidence-transcript-file.js +182 -0
- package/dist/adapters/raw-evidence.js +63 -1183
- package/dist/commands/backfill-batches.js +34 -0
- package/dist/commands/backfill-candidates.js +54 -0
- package/dist/commands/backfill-checkpoint.js +101 -0
- package/dist/commands/backfill-command-line.js +70 -0
- package/dist/commands/backfill-evidence-outcomes.js +104 -0
- package/dist/commands/backfill-issues.js +265 -0
- package/dist/commands/backfill-output.js +75 -0
- package/dist/commands/backfill-plan.js +71 -0
- package/dist/commands/backfill-reasons.js +107 -0
- package/dist/commands/backfill-report.js +298 -0
- package/dist/commands/backfill-result.js +150 -0
- package/dist/commands/backfill-scan.js +274 -0
- package/dist/commands/backfill-scope.js +114 -0
- package/dist/commands/backfill-session-report.js +145 -0
- package/dist/commands/backfill-types.js +1 -0
- package/dist/commands/backfill-upload.js +212 -0
- package/dist/commands/backfill.js +41 -1961
- package/dist/commands/doctor.js +57 -0
- package/dist/commands/jarvis-trace.js +184 -0
- package/dist/commands/jarvis.js +144 -4
- package/dist/commands/local-args-collector.js +26 -0
- package/dist/commands/local-args-tower.js +21 -0
- package/dist/commands/local-args.js +3 -1
- package/dist/commands/local-help.js +19 -2
- package/dist/commands/local.js +3 -0
- package/dist/commands/memory-install-claude.js +294 -0
- package/dist/commands/memory-install-codex.js +205 -0
- package/dist/commands/memory-install-contract.js +286 -0
- package/dist/commands/memory-install-files.js +63 -0
- package/dist/commands/memory-install-skills.js +121 -0
- package/dist/commands/memory-install-toml.js +265 -0
- package/dist/commands/memory-install.js +465 -0
- package/dist/commands/public-root.js +1 -1
- package/dist/commands/sync-followups.js +105 -0
- package/dist/commands/sync.js +7 -1
- package/dist/local-state-attributed-target.js +75 -0
- package/dist/local-state-config.js +147 -0
- package/dist/local-state-files.js +59 -0
- package/dist/local-state-identity.js +73 -0
- package/dist/local-state-pairing.js +263 -0
- package/dist/local-state-paths.js +61 -0
- package/dist/local-state-session.js +68 -0
- package/dist/local-state-status.js +163 -0
- package/dist/local-state-work-context.js +190 -0
- package/dist/local-state.js +34 -848
- package/dist/tower-client.js +3 -2
- package/dist/tower-stream.js +57 -3
- package/package.json +2 -1
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The smallest TOML surface that can put ONE table into `~/.codex/config.toml`
|
|
3
|
+
* and read it back — and nothing else.
|
|
4
|
+
*
|
|
5
|
+
* This is deliberately not a TOML parser. That file is a person's own Codex
|
|
6
|
+
* configuration: model, approval policy, sandbox, their other MCP servers. The
|
|
7
|
+
* rule is that everything outside `[mcp_servers.bli-memory]` comes out of an
|
|
8
|
+
* install byte-identical, which a parse-and-reserialise round trip cannot
|
|
9
|
+
* promise (it would reorder keys, drop comments and normalise strings). So the
|
|
10
|
+
* writer works on LINE SPANS: find our table's header, find where it ends, swap
|
|
11
|
+
* those lines, leave every other byte alone.
|
|
12
|
+
*
|
|
13
|
+
* The reader is the other half of BLI-2541's rule — verify against what the
|
|
14
|
+
* platform stored, not against what you wrote. It understands exactly the three
|
|
15
|
+
* value forms we emit (a quoted string, a single-line array of strings, a
|
|
16
|
+
* single-line inline table of strings). Anything else in our table reads back as
|
|
17
|
+
* unparsed, which surfaces as `read_back_mismatch` rather than as a shrug.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Finds `[a.b]` (or `["a"."b"]`, or any mix) and the span it owns: everything up
|
|
21
|
+
* to the next table header of any kind, or end of file.
|
|
22
|
+
*/
|
|
23
|
+
export function findTomlTableSpan(text, tablePath) {
|
|
24
|
+
const lines = text.split("\n");
|
|
25
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
26
|
+
const header = parseTableHeader(lines[index] ?? "");
|
|
27
|
+
if (!header || !samePath(header, tablePath))
|
|
28
|
+
continue;
|
|
29
|
+
let end = lines.length;
|
|
30
|
+
for (let cursor = index + 1; cursor < lines.length; cursor += 1) {
|
|
31
|
+
if (parseTableHeader(lines[cursor] ?? "") || isArrayTableHeader(lines[cursor] ?? "")) {
|
|
32
|
+
end = cursor;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return { start_line: index, end_line: end };
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
/** Reads our table's simple key/values back off stored text. Null = no such table. */
|
|
41
|
+
export function readTomlTable(text, tablePath) {
|
|
42
|
+
const span = findTomlTableSpan(text, tablePath);
|
|
43
|
+
if (!span)
|
|
44
|
+
return null;
|
|
45
|
+
const lines = text.split("\n").slice(span.start_line + 1, span.end_line);
|
|
46
|
+
const out = {};
|
|
47
|
+
for (const line of lines) {
|
|
48
|
+
const trimmed = line.trim();
|
|
49
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
50
|
+
continue;
|
|
51
|
+
const equals = trimmed.indexOf("=");
|
|
52
|
+
if (equals <= 0)
|
|
53
|
+
continue;
|
|
54
|
+
const key = unquoteKey(trimmed.slice(0, equals).trim());
|
|
55
|
+
if (!key)
|
|
56
|
+
continue;
|
|
57
|
+
// `null` means "present but in a form this reader does not model" — the
|
|
58
|
+
// caller reports a mismatch instead of pretending the field matched.
|
|
59
|
+
out[key] = parseTomlValue(trimmed.slice(equals + 1).trim());
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Replaces our table's lines, or appends the table at the end when it is
|
|
65
|
+
* absent. Everything outside the span is untouched, including trailing
|
|
66
|
+
* whitespace and comments.
|
|
67
|
+
*/
|
|
68
|
+
export function upsertTomlTable(text, tablePath, renderedTable) {
|
|
69
|
+
const block = renderedTable.replace(/\n+$/u, "");
|
|
70
|
+
const span = findTomlTableSpan(text, tablePath);
|
|
71
|
+
if (!span) {
|
|
72
|
+
if (!text.trim())
|
|
73
|
+
return `${block}\n`;
|
|
74
|
+
const separator = text.endsWith("\n") ? "\n" : "\n\n";
|
|
75
|
+
return `${text}${separator}${block}\n`;
|
|
76
|
+
}
|
|
77
|
+
const lines = text.split("\n");
|
|
78
|
+
const before = lines.slice(0, span.start_line);
|
|
79
|
+
const after = lines.slice(span.end_line);
|
|
80
|
+
return [...before, ...block.split("\n"), ...after].join("\n");
|
|
81
|
+
}
|
|
82
|
+
/** Removes our table's lines. Used by `--uninstall`-shaped callers and tests. */
|
|
83
|
+
export function removeTomlTable(text, tablePath) {
|
|
84
|
+
const span = findTomlTableSpan(text, tablePath);
|
|
85
|
+
if (!span)
|
|
86
|
+
return text;
|
|
87
|
+
const lines = text.split("\n");
|
|
88
|
+
return [...lines.slice(0, span.start_line), ...lines.slice(span.end_line)].join("\n");
|
|
89
|
+
}
|
|
90
|
+
export function renderTomlTable(tablePath, entries) {
|
|
91
|
+
const header = `[${tablePath.map(renderKey).join(".")}]`;
|
|
92
|
+
const body = entries.map(([key, value]) => `${renderKey(key)} = ${renderValue(value)}`);
|
|
93
|
+
return [header, ...body].join("\n");
|
|
94
|
+
}
|
|
95
|
+
function renderKey(key) {
|
|
96
|
+
return /^[A-Za-z0-9_-]+$/u.test(key) ? key : `"${key.replace(/"/gu, '\\"')}"`;
|
|
97
|
+
}
|
|
98
|
+
function renderValue(value) {
|
|
99
|
+
if (typeof value === "string")
|
|
100
|
+
return renderString(value);
|
|
101
|
+
if (Array.isArray(value))
|
|
102
|
+
return `[${value.map(renderString).join(", ")}]`;
|
|
103
|
+
const pairs = Object.entries(value).map(([key, entry]) => `${renderKey(key)} = ${renderString(entry)}`);
|
|
104
|
+
return pairs.length === 0 ? "{}" : `{ ${pairs.join(", ")} }`;
|
|
105
|
+
}
|
|
106
|
+
function renderString(value) {
|
|
107
|
+
return `"${value.replace(/\\/gu, "\\\\").replace(/"/gu, '\\"')}"`;
|
|
108
|
+
}
|
|
109
|
+
function parseTableHeader(line) {
|
|
110
|
+
const trimmed = line.trim();
|
|
111
|
+
if (!trimmed.startsWith("[") || trimmed.startsWith("[["))
|
|
112
|
+
return null;
|
|
113
|
+
const close = trimmed.indexOf("]");
|
|
114
|
+
if (close < 0)
|
|
115
|
+
return null;
|
|
116
|
+
// Anything after the closing bracket other than a comment is not a header.
|
|
117
|
+
const rest = trimmed.slice(close + 1).trim();
|
|
118
|
+
if (rest && !rest.startsWith("#"))
|
|
119
|
+
return null;
|
|
120
|
+
return splitKeyPath(trimmed.slice(1, close));
|
|
121
|
+
}
|
|
122
|
+
function isArrayTableHeader(line) {
|
|
123
|
+
return line.trim().startsWith("[[");
|
|
124
|
+
}
|
|
125
|
+
function splitKeyPath(raw) {
|
|
126
|
+
const parts = [];
|
|
127
|
+
let current = "";
|
|
128
|
+
let quote = null;
|
|
129
|
+
for (const character of raw) {
|
|
130
|
+
if (quote) {
|
|
131
|
+
if (character === quote)
|
|
132
|
+
quote = null;
|
|
133
|
+
else
|
|
134
|
+
current += character;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (character === '"' || character === "'") {
|
|
138
|
+
quote = character;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (character === ".") {
|
|
142
|
+
parts.push(current.trim());
|
|
143
|
+
current = "";
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
current += character;
|
|
147
|
+
}
|
|
148
|
+
parts.push(current.trim());
|
|
149
|
+
return parts.filter((part) => part.length > 0);
|
|
150
|
+
}
|
|
151
|
+
function samePath(left, right) {
|
|
152
|
+
return left.length === right.length && left.every((part, index) => part === right[index]);
|
|
153
|
+
}
|
|
154
|
+
function unquoteKey(raw) {
|
|
155
|
+
const trimmed = raw.trim();
|
|
156
|
+
if ((trimmed.startsWith('"') && trimmed.endsWith('"') && trimmed.length >= 2) ||
|
|
157
|
+
(trimmed.startsWith("'") && trimmed.endsWith("'") && trimmed.length >= 2)) {
|
|
158
|
+
return trimmed.slice(1, -1);
|
|
159
|
+
}
|
|
160
|
+
return trimmed;
|
|
161
|
+
}
|
|
162
|
+
function parseTomlValue(raw) {
|
|
163
|
+
const value = stripTrailingComment(raw).trim();
|
|
164
|
+
if (!value)
|
|
165
|
+
return null;
|
|
166
|
+
if (value.startsWith('"') || value.startsWith("'")) {
|
|
167
|
+
return parseTomlString(value);
|
|
168
|
+
}
|
|
169
|
+
if (value.startsWith("[")) {
|
|
170
|
+
if (!value.endsWith("]"))
|
|
171
|
+
return null;
|
|
172
|
+
const inner = value.slice(1, -1).trim();
|
|
173
|
+
if (!inner)
|
|
174
|
+
return [];
|
|
175
|
+
const items = splitTopLevel(inner);
|
|
176
|
+
const parsed = items.map(parseTomlString);
|
|
177
|
+
return parsed.every((item) => item !== null) ? parsed : null;
|
|
178
|
+
}
|
|
179
|
+
if (value.startsWith("{")) {
|
|
180
|
+
if (!value.endsWith("}"))
|
|
181
|
+
return null;
|
|
182
|
+
const inner = value.slice(1, -1).trim();
|
|
183
|
+
const out = {};
|
|
184
|
+
if (!inner)
|
|
185
|
+
return out;
|
|
186
|
+
for (const pair of splitTopLevel(inner)) {
|
|
187
|
+
const equals = pair.indexOf("=");
|
|
188
|
+
if (equals <= 0)
|
|
189
|
+
return null;
|
|
190
|
+
const key = unquoteKey(pair.slice(0, equals).trim());
|
|
191
|
+
const parsed = parseTomlString(pair.slice(equals + 1).trim());
|
|
192
|
+
if (!key || parsed === null)
|
|
193
|
+
return null;
|
|
194
|
+
out[key] = parsed;
|
|
195
|
+
}
|
|
196
|
+
return out;
|
|
197
|
+
}
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
function parseTomlString(raw) {
|
|
201
|
+
const value = raw.trim();
|
|
202
|
+
if (value.length < 2)
|
|
203
|
+
return null;
|
|
204
|
+
const quote = value[0];
|
|
205
|
+
if ((quote !== '"' && quote !== "'") || value[value.length - 1] !== quote)
|
|
206
|
+
return null;
|
|
207
|
+
const inner = value.slice(1, -1);
|
|
208
|
+
if (quote === "'")
|
|
209
|
+
return inner;
|
|
210
|
+
return inner.replace(/\\(["\\nrt])/gu, (_match, escaped) => {
|
|
211
|
+
if (escaped === "n")
|
|
212
|
+
return "\n";
|
|
213
|
+
if (escaped === "r")
|
|
214
|
+
return "\r";
|
|
215
|
+
if (escaped === "t")
|
|
216
|
+
return "\t";
|
|
217
|
+
return escaped;
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
/** Splits on commas that are not inside a quoted string. */
|
|
221
|
+
function splitTopLevel(raw) {
|
|
222
|
+
const parts = [];
|
|
223
|
+
let current = "";
|
|
224
|
+
let quote = null;
|
|
225
|
+
for (const character of raw) {
|
|
226
|
+
if (quote) {
|
|
227
|
+
current += character;
|
|
228
|
+
if (character === quote)
|
|
229
|
+
quote = null;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
if (character === '"' || character === "'") {
|
|
233
|
+
quote = character;
|
|
234
|
+
current += character;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (character === ",") {
|
|
238
|
+
parts.push(current.trim());
|
|
239
|
+
current = "";
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
current += character;
|
|
243
|
+
}
|
|
244
|
+
if (current.trim())
|
|
245
|
+
parts.push(current.trim());
|
|
246
|
+
return parts;
|
|
247
|
+
}
|
|
248
|
+
function stripTrailingComment(raw) {
|
|
249
|
+
let quote = null;
|
|
250
|
+
for (let index = 0; index < raw.length; index += 1) {
|
|
251
|
+
const character = raw[index];
|
|
252
|
+
if (quote) {
|
|
253
|
+
if (character === quote)
|
|
254
|
+
quote = null;
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (character === '"' || character === "'") {
|
|
258
|
+
quote = character;
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
if (character === "#")
|
|
262
|
+
return raw.slice(0, index);
|
|
263
|
+
}
|
|
264
|
+
return raw;
|
|
265
|
+
}
|