@polderlabs/bizar 10.16.1 → 10.17.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.
- package/cli/bin.mjs +16 -0
- package/cli/commands/evidence.mjs +356 -0
- package/cli/commands/install.mjs +34 -12
- package/cli/commands/models.mjs +606 -17
- package/cli/install/index.mjs +69 -4
- package/cli/provision.mjs +212 -6
- package/config/claude/agents/office-manager.md +4 -0
- package/config/claude/hooks/agent-model-guard.mjs +68 -2
- package/config/workflows/bizar-debug.js +8 -6
- package/config/workflows/bizar-implement.js +11 -7
- package/config/workflows/bizar-research.js +15 -9
- package/config/workflows/lib/dispatch.js +859 -0
- package/config/workflows/ultracode-research.js +8 -6
- package/config/workflows/ultracode-review.js +15 -3
- package/config/workflows/ultracode.js +10 -8
- package/package.json +1 -1
- package/packages/sdk/dist/router/agent-model-registry.d.ts +210 -1
- package/packages/sdk/dist/router/agent-model-registry.js +292 -5
- package/packages/sdk/dist/router/dispatch-evidence.d.ts +176 -0
- package/packages/sdk/dist/router/dispatch-evidence.js +505 -0
- package/packages/sdk/dist/router/failover.d.ts +188 -0
- package/packages/sdk/dist/router/failover.js +255 -0
- package/packages/sdk/dist/router/index.d.ts +84 -9
- package/packages/sdk/dist/router/index.js +100 -12
- package/packages/sdk/dist/router/model-profile.d.ts +229 -0
- package/packages/sdk/dist/router/model-profile.js +199 -0
- package/packages/sdk/dist/router/model-router.d.ts +51 -21
- package/packages/sdk/dist/router/model-router.js +90 -29
- package/packages/sdk/dist/router/outcome-learner.d.ts +180 -0
- package/packages/sdk/dist/router/outcome-learner.js +502 -0
- package/packages/sdk/dist/router/select-dispatch-model.d.ts +227 -0
- package/packages/sdk/dist/router/select-dispatch-model.js +532 -0
- package/packages/sdk/dist/version.d.ts +1 -1
- package/packages/sdk/dist/version.js +1 -1
- package/packages/sdk/package.json +1 -1
- package/scripts/git-hooks/pre-push +6 -0
package/cli/bin.mjs
CHANGED
|
@@ -432,6 +432,22 @@ async function main() {
|
|
|
432
432
|
break;
|
|
433
433
|
}
|
|
434
434
|
|
|
435
|
+
case 'evidence': {
|
|
436
|
+
const mod = await importCommand('evidence');
|
|
437
|
+
if (!mod) {
|
|
438
|
+
console.error(chalk.red(` ✗ Could not load evidence command module`));
|
|
439
|
+
process.exit(EXIT_ERROR);
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
dbg('loaded command module:', 'evidence');
|
|
443
|
+
const found = await mod.run(cmd, cmdArgs, isHelpRequest);
|
|
444
|
+
if (found === false) {
|
|
445
|
+
console.error(chalk.red(` ✗ Usage: bizar evidence <subcommand> — run 'bizar evidence --help'`));
|
|
446
|
+
process.exit(EXIT_USAGE);
|
|
447
|
+
}
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
|
|
435
451
|
case 'model': {
|
|
436
452
|
// Deprecated alias. Routes to the original `model.mjs` so the
|
|
437
453
|
// legacy JSON shape (`{ providers: { ... }, total: N }`) and table
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/evidence.mjs
|
|
3
|
+
*
|
|
4
|
+
* `bizar evidence` — F-191 / IMP-018 per-dispatch model evidence CLI.
|
|
5
|
+
*
|
|
6
|
+
* bizar evidence tail --limit N # print the last N rows
|
|
7
|
+
* bizar evidence show <id> # print one record (decision + outcome)
|
|
8
|
+
* bizar evidence verify <id> # exit 0 on integrity OK, 1 on mismatch
|
|
9
|
+
* bizar evidence run <runId> # print every record for a run
|
|
10
|
+
* bizar evidence audit # rows missing outcome or with provider mismatch
|
|
11
|
+
*
|
|
12
|
+
* The store lives at `~/.config/bizar/evidence/dispatch.jsonl` by default;
|
|
13
|
+
* `BIZAR_EVIDENCE_DIR` overrides the directory and `BIZAR_HOME` overrides
|
|
14
|
+
* the home root. The wire format is the JSONL produced by the SDK's
|
|
15
|
+
* `createFileEvidenceStore` and the workflow-side `appendEvidence`
|
|
16
|
+
* mirror in `config/workflows/lib/dispatch.js`.
|
|
17
|
+
*/
|
|
18
|
+
import chalk from 'chalk';
|
|
19
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
20
|
+
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
21
|
+
import { homedir } from 'node:os';
|
|
22
|
+
|
|
23
|
+
// ── Evidence dir resolution ──────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
export function resolveEvidenceDir({ cwd = process.cwd(), env = process.env } = {}) {
|
|
26
|
+
if (env.BIZAR_EVIDENCE_DIR && typeof env.BIZAR_EVIDENCE_DIR === 'string') {
|
|
27
|
+
return isAbsolute(env.BIZAR_EVIDENCE_DIR)
|
|
28
|
+
? env.BIZAR_EVIDENCE_DIR
|
|
29
|
+
: resolve(cwd, env.BIZAR_EVIDENCE_DIR);
|
|
30
|
+
}
|
|
31
|
+
const home = env.BIZAR_HOME || (env.HOME ? `${env.HOME}/.config/bizar` : null)
|
|
32
|
+
|| join(homedir(), '.config', 'bizar');
|
|
33
|
+
return join(home, 'evidence');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function evidenceFilePath(opts = {}) {
|
|
37
|
+
return join(resolveEvidenceDir(opts), 'dispatch.jsonl');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ── Pure helpers ─────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Read every line from the JSONL evidence file. Returns an empty array
|
|
44
|
+
* when the file does not exist yet.
|
|
45
|
+
*/
|
|
46
|
+
export function readEvidenceRows(opts = {}) {
|
|
47
|
+
const path = evidenceFilePath(opts);
|
|
48
|
+
if (!existsSync(path)) return [];
|
|
49
|
+
let raw;
|
|
50
|
+
try { raw = readFileSync(path, 'utf8'); } catch { return []; }
|
|
51
|
+
const rows = [];
|
|
52
|
+
for (const line of raw.split('\n')) {
|
|
53
|
+
const trimmed = line.trim();
|
|
54
|
+
if (!trimmed) continue;
|
|
55
|
+
try { rows.push(JSON.parse(trimmed)); } catch { /* skip malformed lines */ }
|
|
56
|
+
}
|
|
57
|
+
return rows;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Pure structural integrity check. Mirrors the SDK's
|
|
62
|
+
* `EvidenceStore.verifyIntegrity` so the CLI matches the SDK without
|
|
63
|
+
* importing the TS source.
|
|
64
|
+
*
|
|
65
|
+
* Returns `{ ok: true }` when the row + chain is intact, otherwise
|
|
66
|
+
* `{ ok: false, reason: '<machine-readable string>' }`.
|
|
67
|
+
*/
|
|
68
|
+
export function verifyIntegrityForRow(row) {
|
|
69
|
+
if (!row || typeof row !== 'object') return { ok: false, reason: 'not-found' };
|
|
70
|
+
if (row.decision && typeof row.decision === 'object'
|
|
71
|
+
&& row.decision.routingDecisionId !== row.routingDecisionId) {
|
|
72
|
+
return { ok: false, reason: 'decision-id-mismatch' };
|
|
73
|
+
}
|
|
74
|
+
const inputs = row.inputs && typeof row.inputs === 'object' ? row.inputs : null;
|
|
75
|
+
if (!inputs) return { ok: false, reason: 'inputs-missing' };
|
|
76
|
+
for (const field of ['selectedProfilesHash', 'staticProfilesHash', 'budgetHash', 'healthHash']) {
|
|
77
|
+
if (typeof inputs[field] !== 'string' || !/^[0-9a-f]{64}$/.test(inputs[field])) {
|
|
78
|
+
return { ok: false, reason: `inputs-hash-mismatch:${field}` };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (!row.schemaVersion || row.schemaVersion !== 1) {
|
|
82
|
+
return { ok: false, reason: 'schema-version-mismatch' };
|
|
83
|
+
}
|
|
84
|
+
if (!row.createdAt || Number.isNaN(Date.parse(row.createdAt))) {
|
|
85
|
+
return { ok: false, reason: 'createdAt-missing' };
|
|
86
|
+
}
|
|
87
|
+
return { ok: true };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function verifyIntegrityForId(routingDecisionId, opts = {}) {
|
|
91
|
+
const rows = readEvidenceRows(opts).filter((r) => r.routingDecisionId === routingDecisionId);
|
|
92
|
+
if (rows.length === 0) return { ok: false, reason: 'not-found' };
|
|
93
|
+
for (const row of rows) {
|
|
94
|
+
const check = verifyIntegrityForRow(row);
|
|
95
|
+
if (!check.ok) return check;
|
|
96
|
+
}
|
|
97
|
+
return { ok: true };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function tailRows({ limit = 10, dir } = {}) {
|
|
101
|
+
const rows = readEvidenceRows(dir ? { cwd: process.cwd(), env: { ...process.env, BIZAR_EVIDENCE_DIR: dir } } : {});
|
|
102
|
+
const safeLimit = typeof limit === 'number' && limit >= 0 ? limit : rows.length;
|
|
103
|
+
return rows.slice(Math.max(0, rows.length - safeLimit));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function findByRunId(runId, opts = {}) {
|
|
107
|
+
if (typeof runId !== 'string' || !runId) return [];
|
|
108
|
+
return readEvidenceRows(opts).filter((r) => r.runId === runId);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function getById(routingDecisionId, opts = {}) {
|
|
112
|
+
const rows = readEvidenceRows(opts).filter((r) => r.routingDecisionId === routingDecisionId);
|
|
113
|
+
if (rows.length === 0) return null;
|
|
114
|
+
return rows.reduce((latest, row) => (row.sequence ?? 0) > (latest.sequence ?? 0) ? row : latest);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Audit rows: missing outcome OR `actualProviderModel` disagrees with
|
|
119
|
+
* `decision.modelId`. The 100% agreement metric from IMPROVEMENTS.md
|
|
120
|
+
* line 881 lives here.
|
|
121
|
+
*/
|
|
122
|
+
export function auditRows(opts = {}) {
|
|
123
|
+
const rows = readEvidenceRows(opts);
|
|
124
|
+
const primaries = rows.filter((r) => (r.sequence ?? 0) === 0);
|
|
125
|
+
const missingOutcome = primaries.filter((r) => !r.outcome);
|
|
126
|
+
const mismatched = primaries.filter((r) => r.outcome
|
|
127
|
+
&& typeof r.outcome.actualProviderModel === 'string'
|
|
128
|
+
&& r.decision
|
|
129
|
+
&& typeof r.decision.modelId === 'string'
|
|
130
|
+
&& r.outcome.actualProviderModel !== r.decision.modelId);
|
|
131
|
+
return { missingOutcome, mismatched, totalRows: rows.length, primaryRows: primaries.length };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ── Help / argument parsing ─────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
function showHelp() {
|
|
137
|
+
console.log(`
|
|
138
|
+
bizar evidence — F-191 per-dispatch model evidence audit trail
|
|
139
|
+
|
|
140
|
+
Usage:
|
|
141
|
+
bizar evidence tail --limit N Print the last N rows (default 10)
|
|
142
|
+
bizar evidence show <routingDecisionId>
|
|
143
|
+
Print one record (decision + outcome)
|
|
144
|
+
bizar evidence verify <routingDecisionId>
|
|
145
|
+
Run integrity check; exit 0 / 1
|
|
146
|
+
bizar evidence run <runId> Print every record for a run
|
|
147
|
+
bizar evidence audit Rows missing outcome or with model mismatch
|
|
148
|
+
|
|
149
|
+
Common flags:
|
|
150
|
+
--dir <path> Override the evidence directory
|
|
151
|
+
--json Emit machine-readable JSON
|
|
152
|
+
`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function parseLimit(args) {
|
|
156
|
+
const flag = args.find((a) => a.startsWith('--limit='));
|
|
157
|
+
if (flag) {
|
|
158
|
+
const value = Number(flag.slice('--limit='.length));
|
|
159
|
+
if (Number.isFinite(value) && value >= 0) return value;
|
|
160
|
+
}
|
|
161
|
+
const idx = args.indexOf('--limit');
|
|
162
|
+
if (idx !== -1 && idx + 1 < args.length) {
|
|
163
|
+
const value = Number(args[idx + 1]);
|
|
164
|
+
if (Number.isFinite(value) && value >= 0) return value;
|
|
165
|
+
}
|
|
166
|
+
return 10;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function parseDir(args) {
|
|
170
|
+
const flag = args.find((a) => a.startsWith('--dir='));
|
|
171
|
+
if (flag) return flag.slice('--dir='.length);
|
|
172
|
+
const idx = args.indexOf('--dir');
|
|
173
|
+
if (idx !== -1 && idx + 1 < args.length) return args[idx + 1];
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ── Subcommand handlers ──────────────────────────────────────────────────────
|
|
178
|
+
|
|
179
|
+
function printRow(row, { json = false } = {}) {
|
|
180
|
+
if (json) {
|
|
181
|
+
process.stdout.write(JSON.stringify(row, null, 2) + '\n');
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const head = `${row.routingDecisionId} seq=${row.sequence ?? 0} run=${row.runId} agent=${row.agentName ?? '-'} phase=${row.workflowPhase ?? '-'} created=${row.createdAt}`;
|
|
185
|
+
console.log(head);
|
|
186
|
+
console.log(` decision.modelId = ${row.decision?.modelId ?? '(null)'}`);
|
|
187
|
+
console.log(` decision.tier = ${row.decision?.tier ?? '(unknown)'}`);
|
|
188
|
+
console.log(` decision.reason = ${row.decision?.reason ?? '-'}`);
|
|
189
|
+
if (row.outcome) {
|
|
190
|
+
console.log(` outcome.status = ${row.outcome.status}`);
|
|
191
|
+
if (typeof row.outcome.durationMs === 'number') {
|
|
192
|
+
console.log(` outcome.duration = ${row.outcome.durationMs}ms`);
|
|
193
|
+
}
|
|
194
|
+
if (row.outcome.actualProviderModel) {
|
|
195
|
+
console.log(` outcome.actual = ${row.outcome.actualProviderModel}`);
|
|
196
|
+
}
|
|
197
|
+
if (row.outcome.errorMessage) {
|
|
198
|
+
console.log(` outcome.error = ${row.outcome.errorMessage}`);
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
console.log(chalk.yellow(' outcome = (missing)'));
|
|
202
|
+
}
|
|
203
|
+
console.log('');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function handleTail(args) {
|
|
207
|
+
const wantJson = args.includes('--json');
|
|
208
|
+
const limit = parseLimit(args);
|
|
209
|
+
const dir = parseDir(args);
|
|
210
|
+
const rows = tailRows({ limit, dir });
|
|
211
|
+
if (wantJson) {
|
|
212
|
+
process.stdout.write(JSON.stringify(rows, null, 2) + '\n');
|
|
213
|
+
return 0;
|
|
214
|
+
}
|
|
215
|
+
if (rows.length === 0) {
|
|
216
|
+
console.log(chalk.yellow(' ! evidence store is empty (or file does not exist)'));
|
|
217
|
+
return 0;
|
|
218
|
+
}
|
|
219
|
+
for (const row of rows) printRow(row, {});
|
|
220
|
+
return 0;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function handleShow(args) {
|
|
224
|
+
const wantJson = args.includes('--json');
|
|
225
|
+
const positional = args.filter((a) => !a.startsWith('-'));
|
|
226
|
+
const id = positional[0];
|
|
227
|
+
if (!id) {
|
|
228
|
+
console.error(chalk.red(' ✗ bizar evidence show requires a routingDecisionId'));
|
|
229
|
+
return 2;
|
|
230
|
+
}
|
|
231
|
+
const dir = parseDir(args);
|
|
232
|
+
const row = getById(id, dir ? { cwd: process.cwd(), env: { ...process.env, BIZAR_EVIDENCE_DIR: dir } } : {});
|
|
233
|
+
if (!row) {
|
|
234
|
+
if (wantJson) {
|
|
235
|
+
process.stdout.write(JSON.stringify({ error: `routingDecisionId=${id} not found` }, null, 2) + '\n');
|
|
236
|
+
} else {
|
|
237
|
+
console.error(chalk.red(` ✗ routingDecisionId=${id} not found`));
|
|
238
|
+
}
|
|
239
|
+
return 2;
|
|
240
|
+
}
|
|
241
|
+
if (wantJson) {
|
|
242
|
+
process.stdout.write(JSON.stringify(row, null, 2) + '\n');
|
|
243
|
+
return 0;
|
|
244
|
+
}
|
|
245
|
+
printRow(row, {});
|
|
246
|
+
return 0;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function handleVerify(args) {
|
|
250
|
+
const wantJson = args.includes('--json');
|
|
251
|
+
const positional = args.filter((a) => !a.startsWith('-'));
|
|
252
|
+
const id = positional[0];
|
|
253
|
+
if (!id) {
|
|
254
|
+
console.error(chalk.red(' ✗ bizar evidence verify requires a routingDecisionId'));
|
|
255
|
+
return 2;
|
|
256
|
+
}
|
|
257
|
+
const dir = parseDir(args);
|
|
258
|
+
const check = verifyIntegrityForId(id, dir ? { cwd: process.cwd(), env: { ...process.env, BIZAR_EVIDENCE_DIR: dir } } : {});
|
|
259
|
+
if (wantJson) {
|
|
260
|
+
process.stdout.write(JSON.stringify(check, null, 2) + '\n');
|
|
261
|
+
} else if (!check.ok) {
|
|
262
|
+
console.error(chalk.red(` ✗ integrity check failed: ${check.reason}`));
|
|
263
|
+
} else {
|
|
264
|
+
console.log(chalk.green(` ✓ integrity OK for ${id}`));
|
|
265
|
+
}
|
|
266
|
+
return check.ok ? 0 : 1;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function handleRun(args) {
|
|
270
|
+
const wantJson = args.includes('--json');
|
|
271
|
+
const positional = args.filter((a) => !a.startsWith('-'));
|
|
272
|
+
const runId = positional[0];
|
|
273
|
+
if (!runId) {
|
|
274
|
+
console.error(chalk.red(' ✗ bizar evidence run requires a runId'));
|
|
275
|
+
return 2;
|
|
276
|
+
}
|
|
277
|
+
const dir = parseDir(args);
|
|
278
|
+
const rows = findByRunId(runId, dir ? { cwd: process.cwd(), env: { ...process.env, BIZAR_EVIDENCE_DIR: dir } } : {});
|
|
279
|
+
if (wantJson) {
|
|
280
|
+
process.stdout.write(JSON.stringify(rows, null, 2) + '\n');
|
|
281
|
+
return 0;
|
|
282
|
+
}
|
|
283
|
+
if (rows.length === 0) {
|
|
284
|
+
console.log(chalk.yellow(` ! no rows for runId=${runId}`));
|
|
285
|
+
return 0;
|
|
286
|
+
}
|
|
287
|
+
for (const row of rows) printRow(row, {});
|
|
288
|
+
return 0;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function handleAudit(args) {
|
|
292
|
+
const wantJson = args.includes('--json');
|
|
293
|
+
const dir = parseDir(args);
|
|
294
|
+
const report = auditRows(dir ? { cwd: process.cwd(), env: { ...process.env, BIZAR_EVIDENCE_DIR: dir } } : {});
|
|
295
|
+
if (wantJson) {
|
|
296
|
+
process.stdout.write(JSON.stringify(report, null, 2) + '\n');
|
|
297
|
+
return report.missingOutcome.length === 0 && report.mismatched.length === 0 ? 0 : 1;
|
|
298
|
+
}
|
|
299
|
+
console.log(chalk.green(` primary rows: ${report.primaryRows} total rows: ${report.totalRows}`));
|
|
300
|
+
console.log(chalk.yellow(` missing outcome: ${report.missingOutcome.length}`));
|
|
301
|
+
console.log(chalk.yellow(` provider-model mismatch: ${report.mismatched.length}`));
|
|
302
|
+
if (report.missingOutcome.length > 0) {
|
|
303
|
+
console.log('');
|
|
304
|
+
console.log(chalk.yellow(' Missing outcome:'));
|
|
305
|
+
for (const row of report.missingOutcome.slice(0, 20)) {
|
|
306
|
+
console.log(` ${row.routingDecisionId} decision.modelId=${row.decision?.modelId ?? '(null)'} run=${row.runId}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (report.mismatched.length > 0) {
|
|
310
|
+
console.log('');
|
|
311
|
+
console.log(chalk.yellow(' Provider-model mismatch:'));
|
|
312
|
+
for (const row of report.mismatched.slice(0, 20)) {
|
|
313
|
+
console.log(` ${row.routingDecisionId} decision.modelId=${row.decision?.modelId} actualProviderModel=${row.outcome?.actualProviderModel}`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return report.missingOutcome.length === 0 && report.mismatched.length === 0 ? 0 : 1;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ── run() entrypoint ─────────────────────────────────────────────────────────
|
|
320
|
+
|
|
321
|
+
export async function run(name, args, isHelpRequest) {
|
|
322
|
+
if (name !== 'evidence') return false;
|
|
323
|
+
if (isHelpRequest || args.includes('--help') || args.includes('-h')) {
|
|
324
|
+
showHelp();
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
const sub = args[0];
|
|
328
|
+
if (!sub) {
|
|
329
|
+
showHelp();
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
// Strip the subcommand from args before delegating.
|
|
333
|
+
const rest = args.slice(1);
|
|
334
|
+
switch (sub) {
|
|
335
|
+
case 'tail':
|
|
336
|
+
process.exit(handleTail(rest));
|
|
337
|
+
return true;
|
|
338
|
+
case 'show':
|
|
339
|
+
process.exit(handleShow(rest));
|
|
340
|
+
return true;
|
|
341
|
+
case 'verify':
|
|
342
|
+
process.exit(handleVerify(rest));
|
|
343
|
+
return true;
|
|
344
|
+
case 'run':
|
|
345
|
+
process.exit(handleRun(rest));
|
|
346
|
+
return true;
|
|
347
|
+
case 'audit':
|
|
348
|
+
process.exit(handleAudit(rest));
|
|
349
|
+
return true;
|
|
350
|
+
default:
|
|
351
|
+
console.error(chalk.red(` ✗ unknown evidence subcommand: ${sub}`));
|
|
352
|
+
showHelp();
|
|
353
|
+
process.exit(2);
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
}
|
package/cli/commands/install.mjs
CHANGED
|
@@ -19,9 +19,11 @@ export function showInstallHelp() {
|
|
|
19
19
|
Usage:
|
|
20
20
|
bizar install Install (or refresh) every component
|
|
21
21
|
bizar install --dry-run Print what would happen, change nothing
|
|
22
|
-
bizar install --force
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
bizar install --force Full clean install: wipe Bizar-managed dirs,
|
|
23
|
+
back up settings env vars, re-sync everything
|
|
24
|
+
from the repo. Preserves ~/.config/bizar/
|
|
25
|
+
login state. Combine with --yes to skip prompts.
|
|
26
|
+
bizar install --deep Alias for --force (clean-install semantics)
|
|
25
27
|
bizar install --yes Assume yes for any non-destructive prompt
|
|
26
28
|
bizar install --help Show this help
|
|
27
29
|
|
|
@@ -30,6 +32,21 @@ export function showInstallHelp() {
|
|
|
30
32
|
difference is just mode=install vs mode=update. Every step is
|
|
31
33
|
idempotent — running this twice is safe.
|
|
32
34
|
|
|
35
|
+
F-183 (v10.16.2+) — --force promotes the install from
|
|
36
|
+
overwrite+prune-stale to a fully clean install. It wipes:
|
|
37
|
+
- ~/.claude/{agents,skills,commands,hooks,rules,workflows,plugins}/
|
|
38
|
+
- ~/.agents/ (shared skill-registry + lock)
|
|
39
|
+
- ~/.claude/settings.json (env vars backed up to BIZAR_SAVED_ENV)
|
|
40
|
+
and preserves:
|
|
41
|
+
- ~/.config/bizar/ (login state, telemetry, model picks, worktree-queue)
|
|
42
|
+
- ~/.claude/.credentials.json, statsig/, .playwright-mcp/
|
|
43
|
+
- any user-created subdirs under ~/.claude/ outside the managed set
|
|
44
|
+
|
|
45
|
+
The freshly-emitted settings.json inherits the F-181 wildcard
|
|
46
|
+
permissions.allow expansion and the F-176 empty deny/ask lists
|
|
47
|
+
while the prior ANTHROPIC_* and BIZAR_* env vars are merged back
|
|
48
|
+
in from the stash.
|
|
49
|
+
|
|
33
50
|
1. Installs @polderlabs/bizar via npm (skipped if already current).
|
|
34
51
|
2. Shells to ./install.sh for platform-specific system dependencies.
|
|
35
52
|
3. Syncs agent files, slash commands, and bundled skills into
|
|
@@ -38,7 +55,6 @@ export function showInstallHelp() {
|
|
|
38
55
|
5. Wires Claude Code lifecycle hooks (SessionStart / PreToolUse /
|
|
39
56
|
PostToolUse / UserPromptSubmit) under ~/.claude/hooks/.
|
|
40
57
|
6. Runs 'bizar doctor' as a post-install health check.
|
|
41
|
-
|
|
42
58
|
No API key collection, no interactive prompts.
|
|
43
59
|
`);
|
|
44
60
|
}
|
|
@@ -48,7 +64,6 @@ export function showUpdateHelp() {
|
|
|
48
64
|
bizar update — Update @anthropic-ai/claude-code + @polderlabs/bizar
|
|
49
65
|
(which bundles the CLI, SDK, agents, skills, hooks, and commands). Detects what's
|
|
50
66
|
installed and only touches what's missing or out of date.
|
|
51
|
-
|
|
52
67
|
Usage:
|
|
53
68
|
bizar update Update all installed Bizar components
|
|
54
69
|
bizar update --check Only print current vs. latest; do not update
|
|
@@ -57,11 +72,9 @@ export function showUpdateHelp() {
|
|
|
57
72
|
bizar update --force Override .bizar/PRE_PUSH_NOTES.md blockers
|
|
58
73
|
bizar update --yes Same as --force, but named for one-line scripts
|
|
59
74
|
bizar update --help Show this help
|
|
60
|
-
|
|
61
75
|
Components updated:
|
|
62
76
|
@anthropic-ai/claude-code the Claude Code CLI itself
|
|
63
77
|
@polderlabs/bizar CLI + SDK + agents + skills + hooks
|
|
64
|
-
|
|
65
78
|
Behavior (v4.4.7+):
|
|
66
79
|
• Single unified provisioner. 'bizar install' and 'bizar update' are
|
|
67
80
|
the same code path with different mode flags. Every step is
|
|
@@ -72,13 +85,11 @@ export function showUpdateHelp() {
|
|
|
72
85
|
regressions before claude tries to start.
|
|
73
86
|
• With --check: prints the version matrix and release-notes excerpt
|
|
74
87
|
between current and latest, exits non-zero if an update is available.
|
|
75
|
-
|
|
76
88
|
Examples:
|
|
77
89
|
bizar update Full auto-update (recommended)
|
|
78
90
|
bizar update --check Show version matrix + notes, do nothing
|
|
79
91
|
bizar update --channel=beta Upgrade to latest beta build
|
|
80
92
|
bizar update --dry-run Preview what would change
|
|
81
|
-
|
|
82
93
|
Errors:
|
|
83
94
|
Network failures (registry offline / DNS) and npm permission issues
|
|
84
95
|
are surfaced with the raw npm output. The provisioner never silently
|
|
@@ -87,7 +98,6 @@ export function showUpdateHelp() {
|
|
|
87
98
|
}
|
|
88
99
|
|
|
89
100
|
// ── Command runners ────────────────────────────────────────────────────────────
|
|
90
|
-
|
|
91
101
|
export async function install(args, isHelpRequest) {
|
|
92
102
|
if (isHelpRequest) {
|
|
93
103
|
showInstallHelp();
|
|
@@ -95,9 +105,21 @@ export async function install(args, isHelpRequest) {
|
|
|
95
105
|
}
|
|
96
106
|
// parseFlags lives in cli/provision.mjs and is the canonical argv
|
|
97
107
|
// parser for the installer family. Reusing it keeps install and
|
|
98
|
-
// update in lockstep on flag semantics.
|
|
108
|
+
// update in lockstep on flag semantics. --deep is parsed as an
|
|
109
|
+
// alias for --force (clean-install semantics, F-183).
|
|
99
110
|
const { mode, dryRun, force, yes } = parseFlags(args);
|
|
100
|
-
await runInstaller({ mode, dryRun, force, yes });
|
|
111
|
+
const result = await runInstaller({ mode, dryRun, force, yes });
|
|
112
|
+
// F-183 — print a one-line summary of the wipe scope so operators
|
|
113
|
+
// can see at a glance what changed without re-reading the verbose
|
|
114
|
+
// step list.
|
|
115
|
+
if (force && result?.clean) {
|
|
116
|
+
console.log('');
|
|
117
|
+
console.log(chalk.cyan(` Summary (F-183): wiped ${result.clean.wiped.length} path(s); preserved ${result.clean.preserved.length} path(s).`));
|
|
118
|
+
if (result.doctor) {
|
|
119
|
+
const ok = result.doctor.failed === 0;
|
|
120
|
+
console.log(chalk[ok ? 'green' : 'yellow'](` Doctor: ${result.doctor.passed} passed, ${result.doctor.failed} failed.`));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
101
123
|
// v4.4.3 — After install, repair any stale bin symlinks so the
|
|
102
124
|
// user picks up the new code.
|
|
103
125
|
try {
|