@polderlabs/bizar 3.20.8 → 3.20.11

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 CHANGED
@@ -150,14 +150,22 @@ function showExportHelp() {
150
150
 
151
151
  function showInstallHelp() {
152
152
  console.log(`
153
- bizar install — Run the interactive installer
153
+ bizar install — Run the canonical BizarHarness installer
154
154
 
155
155
  Usage:
156
156
  bizar install
157
157
 
158
158
  Description:
159
- Installs agents, rules, commands, plugin support, RTK, Semble,
160
- and the Skills CLI.
159
+ v3.20.11+ thin wrapper around ./install.sh. The bash script
160
+ auto-installs every system dep (uv, python3.12, chrome-headless-shell
161
+ + runtime libs, jq), installs browser-harness via uv, syncs agents /
162
+ commands / hooks / skills into ~/.config/opencode/, configures
163
+ opencode.json + the Bizar plugin, and prints a single status banner.
164
+
165
+ No API key collection, no interactive prompts, no opencode restart —
166
+ the installer is safe to re-run anytime.
167
+
168
+ Run from a fresh git clone instead: \`cd BizarHarness && ./install.sh\`
161
169
  `);
162
170
  }
163
171
 
package/cli/doctor.mjs CHANGED
@@ -25,7 +25,28 @@ import { homedir } from 'node:os';
25
25
  import { join } from 'node:path';
26
26
  import { opencodeConfigDir, opencodeAgentsDir } from './utils.mjs';
27
27
 
28
- const REQUIRED_AGENTS = ['odin.md', 'quick.md', 'thor.md', 'tyr.md'];
28
+ // v3.20.11: list every agent the install script is expected to deploy.
29
+ // Adding a new agent to `config/agents/` without adding it here causes
30
+ // doctor to silently under-count ("all 4 core agents present" when there
31
+ // are actually 14). The list mirrors cli/install.mjs AGENT_FILES plus
32
+ // `browser-harness.md` (added in v3.20.7) and `_shared/AGENT_BASELINE.md`
33
+ // is intentionally excluded (it's a skill, not an agent).
34
+ const REQUIRED_AGENTS = [
35
+ 'odin.md',
36
+ 'vor.md',
37
+ 'frigg.md',
38
+ 'quick.md',
39
+ 'mimir.md',
40
+ 'heimdall.md',
41
+ 'hermod.md',
42
+ 'thor.md',
43
+ 'baldr.md',
44
+ 'tyr.md',
45
+ 'vidarr.md',
46
+ 'forseti.md',
47
+ 'semble-search.md',
48
+ 'browser-harness.md',
49
+ ];
29
50
 
30
51
  // ── helpers ─────────────────────────────────────────────────────────────────
31
52
 
@@ -281,16 +281,22 @@ describe('runDoctor() with fixture HOME', () => {
281
281
 
282
282
  test('agent-files-installed fails when core agents missing', async () => {
283
283
  writeOpencodeConfig({});
284
- writeAgents('odin.md'); // missing quick, thor, tyr
284
+ writeAgents('odin.md'); // missing the other 13
285
285
  const result = await runDoctor({ silent: true });
286
286
  const r = findCheck(result, 'agent-files-installed');
287
287
  assert.equal(r.ok, false);
288
288
  assert.match(r.message, /missing/);
289
289
  });
290
290
 
291
- test('agent-files-installed passes when all core agents present', async () => {
291
+ test('agent-files-installed passes when all 14 agents present', async () => {
292
292
  writeOpencodeConfig({});
293
- writeAgents('odin.md', 'quick.md', 'thor.md', 'tyr.md');
293
+ // v3.20.11: doctor now expects all 14 agents (was 4 in v3.20.10).
294
+ writeAgents(
295
+ 'odin.md', 'vor.md', 'frigg.md', 'quick.md',
296
+ 'mimir.md', 'heimdall.md', 'hermod.md', 'thor.md', 'baldr.md',
297
+ 'tyr.md', 'vidarr.md', 'forseti.md',
298
+ 'semble-search.md', 'browser-harness.md',
299
+ );
294
300
  const result = await runDoctor({ silent: true });
295
301
  const r = findCheck(result, 'agent-files-installed');
296
302
  assert.equal(r.ok, true, r.message);
package/cli/install.mjs CHANGED
@@ -206,231 +206,62 @@ export async function installPluginFromGlobal(opts = {}) {
206
206
  return true;
207
207
  }
208
208
 
209
+ /**
210
+ * runInstaller — v3.20.11 thin wrapper.
211
+ *
212
+ * As of v3.20.11, `bizar install` is a thin wrapper around the canonical
213
+ * `install.sh` script at the repo root. The bash script handles all the
214
+ * heavy lifting: system-dep installation (uv, python3.12, chrome-headless-shell
215
+ * + runtime libs, jq), browser-harness via uv, Chrome lifecycle, agent /
216
+ * command / hook / skill sync, opencode.json merging, install-state.
217
+ *
218
+ * Why a thin wrapper instead of the old TUI:
219
+ * - One source of truth. The bash script is also what `git clone` users
220
+ * run (`./install.sh`), what `bizar update` re-runs, and what the npm
221
+ * postinstall hook invokes. Keeping a parallel Node implementation
222
+ * guarantees the two will diverge.
223
+ * - No interactive prompts. The TUI asked for API keys, restart
224
+ * confirmation, and component selection — none of which belong in a
225
+ * `npm i -g @polderlabs/bizar` install (no TTY, secrets stay local).
226
+ * - Cross-platform. The bash script already detects Windows and falls
227
+ * back to the npm-based path; re-implementing that in Node is busywork.
228
+ *
229
+ * Returns the install.sh exit code (0 = success, 1 = partial failure).
230
+ * On platforms where bash isn't available (rare — Windows without WSL),
231
+ * falls back to running the npm-based path directly.
232
+ */
209
233
  export async function runInstaller() {
210
- showBanner();
211
-
212
- // ── Detect opencode ──
213
- sectionHeading('Pre-flight');
214
- const env = await detectOpenCode();
215
-
216
- if (!env.exists) {
217
- console.log(chalk.yellow(' ⚠ opencode config directory not found.'));
218
- console.log(chalk.dim(' The installer will create it at:'));
219
- console.log(chalk.dim(` ${env.configDir}`));
220
- } else {
221
- console.log(chalk.green(` ✓ opencode detected at ${env.configDir}`));
222
- if (env.version) console.log(chalk.dim(` version ${env.version}`));
223
- }
224
-
225
- const rtkInstalled = await detectRtk();
226
- if (rtkInstalled) {
227
- console.log(chalk.green(' ✓ RTK detected (token optimizer)'));
228
- } else {
229
- console.log(chalk.yellow(' ○ RTK not detected — will install'));
230
- }
231
-
232
- const sembleInstalled = await detectSemble();
233
- if (sembleInstalled) {
234
- console.log(chalk.green(' ✓ Semble detected (code search)'));
235
- } else {
236
- console.log(chalk.yellow(' ○ Semble not detected — will install'));
237
- }
238
-
239
- const skillsCliInstalled = await detectSkillsCli();
240
- if (skillsCliInstalled) {
241
- console.log(chalk.green(' ✓ Skills CLI detected (skill discovery)'));
242
- } else {
243
- console.log(chalk.yellow(' ○ Skills CLI not detected — will install'));
244
- }
245
- console.log();
246
-
247
- // ── Step 1: Component selection ──
248
- sectionHeading('Component Selection');
249
- const components = await promptComponents();
250
-
251
- // ── Step 2: Agent selection (if agents component chosen) ──
252
- let selectedAgents = [];
253
- if (components.includes('agents')) {
254
- sectionHeading('Agent Selection');
255
- selectedAgents = await promptAgents();
256
- }
257
-
258
- // ── Step 3: Install mode ──
259
- sectionHeading('Installation Mode');
260
- const mode = await promptInstallMode();
261
-
262
- // ── Step 4: Skill packs (via skills.sh) ──
263
- sectionHeading('Skills from skills.sh');
264
- const skillPacks = await promptSkillPacks();
265
-
266
- // ── Step 5: Build summary & confirm ──
267
- const summary = buildSummary(components, selectedAgents, env.configDir, skillPacks);
268
- showPantheon();
269
- console.log();
270
- console.log(chalk.dim(' Summary:'));
271
- console.log(chalk.dim(` Components : ${summary.components}`));
272
- console.log(chalk.dim(` Agents : ${summary.agents}`));
273
- console.log(chalk.dim(` Target : ${summary.target}`));
274
- console.log(chalk.dim(` Mode : ${mode}`));
275
- console.log();
276
-
277
- const confirmed = await promptConfirmInstall(summary);
278
- if (!confirmed) {
279
- console.log(chalk.yellow('\n Installation cancelled.\n'));
280
- process.exit(0);
281
- }
282
-
283
- // ── Step 5: Install ──
284
- console.log();
285
- sectionHeading('Installing');
286
-
287
- if (components.includes('agents') && selectedAgents.length > 0) {
288
- await installAgents(selectedAgents, mode);
289
- }
290
-
291
- if (components.includes('agents-md')) {
292
- await installAgentsMd(mode);
293
- }
294
-
295
- if (components.includes('skill-bizar')) {
296
- await installSkill('bizar');
297
- }
234
+ const { existsSync } = await import('node:fs');
235
+ const { join, dirname } = await import('node:path');
236
+ const { fileURLToPath } = await import('node:url');
237
+ const { spawnSync } = await import('node:child_process');
298
238
 
299
- if (components.includes('skill-improve')) {
300
- await installSkill('self-improvement');
301
- }
302
-
303
- if (components.includes('skill-cpp-std')) {
304
- await installSkill('cpp-coding-standards');
305
- }
306
-
307
- if (components.includes('skill-cpp-test')) {
308
- await installSkill('cpp-testing');
309
- }
310
-
311
- if (components.includes('skill-esp-idf')) {
312
- await installSkill('embedded-esp-idf');
313
- }
314
-
315
- if (components.includes('opencode-json')) {
316
- await installOpencodeJson(mode);
317
- }
318
-
319
- // Always attempt to merge the 7 BizarHarness tool keys idempotently.
320
- // Safe to call even if opencode-json wasn't selected — skips silently.
321
- {
322
- const result = await mergeToolsIntoUserConfig();
323
- if (result.merged) {
324
- console.log(chalk.green(` ✓ ${result.added.length} BizarHarness tool key(s) merged into opencode.json`));
325
- }
326
- }
239
+ const __dirname = dirname(fileURLToPath(import.meta.url));
240
+ // cli/install.mjs → ../install.sh
241
+ const installSh = join(__dirname, '..', 'install.sh');
327
242
 
328
- if (components.includes('bizar')) {
329
- await installBizarFolder();
330
- }
331
-
332
- if (components.includes('plugin-bizar')) {
333
- const result = await installPluginBizar();
334
- if (result.errors.length > 0) {
335
- console.log(chalk.yellow(` ⚠ Plugin install: ${result.errors.length} error(s)`));
336
- }
243
+ if (!existsSync(installSh)) {
244
+ console.error(chalk.red(' ✗ install.sh not found at ' + installSh));
245
+ console.error(chalk.dim(' Run `git clone https://github.com/DrB0rk/BizarHarness` first, then `cd BizarHarness && ./install.sh`.'));
246
+ process.exit(1);
337
247
  }
338
248
 
339
- // Also try to install the plugin from the separate global npm package
340
- // `@polderlabs/bizar-plugin` (preferred path going forward).
341
- if (components.includes('plugin-bizar')) {
342
- await installPluginFromGlobal();
343
- }
249
+ console.log(chalk.bold.hex('#6366f1')('\n BizarHarness installer (delegating to install.sh)\n'));
344
250
 
345
- // ── Rules, hooks, commands (optional components) ──
346
- if (components.includes('rules')) {
347
- const n = await installRules();
348
- console.log(chalk.green(` ✓ ${n} rules installed`));
349
- }
350
- if (components.includes('hooks')) {
351
- const n = await installHooks();
352
- console.log(chalk.green(` ✓ ${n} hooks installed`));
353
- }
354
- if (components.includes('commands')) {
355
- const n = await installCommands();
356
- console.log(chalk.green(` ✓ ${n} commands installed`));
251
+ const useBash = process.platform !== 'win32' || process.env.WSL_DISTRO_NAME;
252
+ if (useBash) {
253
+ const r = spawnSync('bash', [installSh], { stdio: 'inherit' });
254
+ process.exit(r.status ?? 1);
357
255
  }
358
256
 
359
- // ── RTK (always installed token optimization required) ──
360
- await installRtk();
361
-
362
- // ── Semble (always installed code search required) ──
363
- await installSemble();
364
-
365
- // ── Skills CLI (always installed — skill discovery required) ──
366
- await installSkillsCli();
367
-
368
- // ── Skill packs (via skills.sh ecosystem) ──
369
- for (const pack of skillPacks) {
370
- await installCuratedSkills([pack]);
371
- }
372
-
373
- // ── Step 6: API keys ──
374
- sectionHeading('API Keys');
375
- console.log(chalk.dim(' You can configure API keys now or later via /connect in opencode.'));
376
- const keys = await promptApiKeys();
377
-
378
- if (keys.opencodeZen || keys.minimax || keys.openai) {
379
- console.log(chalk.dim('\n Keys noted. Add them to your opencode.json or run /connect in opencode.\n'));
380
- }
381
-
382
- // ── Summary ──
383
- console.log();
384
- console.log(boxen(
385
- chalk.bold.hex('#6366f1')(' ⚡ BIZARHARNESS INSTALLED ⚡\n') +
386
- '\n' +
387
- chalk.dim(' Norse Pantheon active.\n') +
388
- '\n' +
389
- chalk.green(` ✓ ${selectedAgents.length} agents installed`) + '\n' +
390
- chalk.green(` ✓ ${summary.parts.length} components configured`) + '\n' +
391
- '\n' +
392
- chalk.green(' ✓ RTK ') + chalk.dim(`(${rtkInstalled ? 'already configured' : 'installed & configured'})`) + '\n' +
393
- chalk.green(' ✓ Semble ') + chalk.dim(`(${sembleInstalled ? 'ready' : 'installed'})`) + '\n' +
394
- chalk.green(' ✓ Skills CLI ') + chalk.dim(`(${skillsCliInstalled ? 'ready' : 'installed'})`) + '\n' +
395
- (skillPacks.length > 0 ? chalk.green(` ✓ Skill packs: ${skillPacks.join(', ')}`) + '\n' : '') + '\n' +
396
- chalk.hex('#a855f7')(' Next steps:') + '\n' +
397
- chalk.hex('#a855f7')(' 1. Restart opencode') + '\n' +
398
- chalk.hex('#a855f7')(' 2. Run /connect to add API keys') + '\n' +
399
- chalk.hex('#a855f7')(' 3. Run /models to verify agents'),
400
- {
401
- padding: 1,
402
- margin: 0,
403
- borderStyle: 'round',
404
- borderColor: '#6366f1',
405
- },
406
- ));
407
- console.log();
408
-
409
- // ── Provider auto-detect (v3.16.0) — best-effort, doesn't block install
410
- const { runProvidersDetect } = await import('./providers-detect.mjs');
411
- try {
412
- await runProvidersDetect(['--no-probe']);
413
- } catch {
414
- // best-effort
415
- }
416
-
417
- // ── Restart prompt ──
418
- const shouldRestart = await promptRestartOpenCode();
419
- if (shouldRestart) {
420
- console.log(chalk.dim(' Restarting opencode...'));
421
- try {
422
- const { execSync } = await import('node:child_process');
423
- execSync('opencode', { stdio: 'inherit' });
424
- } catch {
425
- console.log(chalk.yellow(' Could not restart automatically. Restart opencode manually.'));
426
- }
427
- }
428
-
429
- // ── Post-install: graphify ──
430
- await promptGraphifyInstall();
431
-
432
- // ── Post-install ──
433
- console.log(chalk.dim('\n Odin watches. The Pantheon awaits. ᛟ\n'));
257
+ // Windows without WSLbash isn't available. The bash script has a
258
+ // Windows fallback that uses npm-based install paths. Run the npm path
259
+ // directly: install the BizarHarness npm packages + copy plugin from
260
+ // global to ~/.config/opencode/plugins/bizar/. This is the same code
261
+ // path install.sh uses for `platform == win32`.
262
+ console.log(chalk.dim(' bash not available — running npm-based installer (Windows path)'));
263
+ console.log('');
264
+ await runPostInstall();
434
265
  }
435
266
 
436
267
  // ── Interactive prompts for optional packages ─────────────────────────────────
package/cli/update.mjs CHANGED
@@ -290,32 +290,38 @@ function rerunInstallScript() {
290
290
  return { ok: false, message: 'could not locate npm global root; skipping install-script rerun' };
291
291
  }
292
292
  const pkgRoot = join(globalRoot, ...PKG_MAIN.split('/'));
293
- const binPath = join(pkgRoot, 'cli', 'bin.mjs');
294
- if (existsSync(binPath)) {
295
- console.log(chalk.dim(`\n Re-running setup via ${binPath} --setup...`));
296
- const r = spawnSync(process.execPath, [binPath, '--setup'], { stdio: 'inherit' });
297
- if (r.status === 0) {
298
- return { ok: true, message: 'setup re-run' };
293
+ // v3.20.11: prefer the canonical `install.sh` script over `cli/bin.mjs
294
+ // --setup`. The bash script is the single source of truth (it handles
295
+ // system deps, chrome-headless-shell runtime libs, browser-harness,
296
+ // opencode.json merging, etc.); the --setup path is a legacy fallback
297
+ // for environments where bash isn't on PATH.
298
+ const installSh = join(pkgRoot, 'install.sh');
299
+ if (process.platform === 'win32') {
300
+ // On Windows without WSL, bash isn't available. The bash script has
301
+ // a Windows fallback that uses npm-based install paths. Fall through
302
+ // to the bin.mjs --setup path, which still does the agent copy +
303
+ // plugin-from-global install on Windows.
304
+ console.log(chalk.dim(' Windows: using bin.mjs --setup path (bash not available)'));
305
+ } else if (existsSync(installSh)) {
306
+ console.log(chalk.dim(`\n Re-running install script at ${installSh}...`));
307
+ const r = spawnSync('bash', [installSh], { stdio: 'inherit' });
308
+ if (r.status !== 0) {
309
+ return { ok: false, message: 'install script rerun failed' };
299
310
  }
300
- return { ok: false, message: 'setup rerun failed' };
311
+ return { ok: true, message: 'install script re-run' };
312
+ } else {
313
+ console.log(chalk.dim(' install.sh not present — falling back to bin.mjs --setup'));
301
314
  }
302
- const installSh = join(pkgRoot, 'install.sh');
303
- if (!existsSync(installSh)) {
315
+ const binPath = join(pkgRoot, 'cli', 'bin.mjs');
316
+ if (!existsSync(binPath)) {
304
317
  return { ok: false, message: 'could not locate a compatible setup script to re-run' };
305
318
  }
306
- if (process.platform === 'win32') {
307
- // On Windows, the bash install path doesn't apply. The plugin is
308
- // already installed globally via the npm install
309
- // (see cli/install.mjs:installPluginFromGlobal).
310
- console.log(chalk.dim(' Skipping install.sh (Windows uses npm-based plugin install)'));
311
- return { ok: true, message: 'install.sh skipped on Windows' };
312
- }
313
- console.log(chalk.dim(`\n Re-running install script at ${installSh}...`));
314
- const r = spawnSync('bash', [installSh], { stdio: 'inherit' });
315
- if (r.status !== 0) {
316
- return { ok: false, message: 'install script rerun failed' };
319
+ console.log(chalk.dim(`\n Re-running setup via ${binPath} --setup...`));
320
+ const r = spawnSync(process.execPath, [binPath, '--setup'], { stdio: 'inherit' });
321
+ if (r.status === 0) {
322
+ return { ok: true, message: 'setup re-run' };
317
323
  }
318
- return { ok: true, message: 'install script re-run' };
324
+ return { ok: false, message: 'setup rerun failed' };
319
325
  }
320
326
 
321
327
  // ---------------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polderlabs/bizar",
3
- "version": "3.20.8",
3
+ "version": "3.20.11",
4
4
  "description": "Norse-pantheon multi-agent system for opencode — 13 agents across 4 cost tiers with cost-aware routing, plans, and a configurable agent harness.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,7 +19,7 @@
19
19
  "build:sdk": "npm run build -w @polderlabs/bizar-sdk",
20
20
  "test:sdk": "npm run test -w @polderlabs/bizar-sdk",
21
21
  "test:sdk:watch": "npm run test:watch -w @polderlabs/bizar-sdk",
22
- "test": "npm run typecheck && npm run test:sdk && (cd plugins/bizar && bun test tests/loop.test.ts tests/block.test.ts tests/stall-think.test.ts tests/tools/bg-get-comments.test.ts tests/tools/bg-spawn-delegation.test.ts tests/tools/opencode-runner.test.ts tests/settings.test.ts tests/commands.test.ts tests/commands-impl.test.ts tests/tools/plan-action.test.ts tests/tools/wait-for-feedback.test.ts tests/reasoning-clean.test.ts tests/key-rotation.test.ts) && node bizar-dash/tests/smoke-v2.mjs && node --test bizar-dash/tests/path-safe.test.mjs bizar-dash/tests/tmux-wrap.test.mjs bizar-dash/tests/opencode-runner.test.mjs bizar-dash/tests/mod-instructions.node.test.mjs bizar-dash/tests/mod-upgrade.node.test.mjs bizar-dash/tests/graphify-mod-spawn.node.test.mjs bizar-dash/tests/no-agent-browser.node.test.mjs",
22
+ "test": "npm run typecheck && npm run test:sdk && (cd plugins/bizar && bun test tests/loop.test.ts tests/block.test.ts tests/stall-think.test.ts tests/tools/bg-get-comments.test.ts tests/tools/bg-spawn-delegation.test.ts tests/tools/opencode-runner.test.ts tests/settings.test.ts tests/commands.test.ts tests/commands-impl.test.ts tests/tools/plan-action.test.ts tests/tools/wait-for-feedback.test.ts tests/reasoning-clean.test.ts tests/key-rotation.test.ts) && node bizar-dash/tests/smoke-v2.mjs && node --test bizar-dash/tests/path-safe.test.mjs bizar-dash/tests/tmux-wrap.test.mjs bizar-dash/tests/opencode-runner.test.mjs bizar-dash/tests/mod-instructions.node.test.mjs bizar-dash/tests/mod-upgrade.node.test.mjs bizar-dash/tests/graphify-mod-spawn.node.test.mjs bizar-dash/tests/no-agent-browser.node.test.mjs bizar-dash/tests/providers-store-backup-keys.node.test.mjs",
23
23
  "build": "npm run build:sdk"
24
24
  },
25
25
  "keywords": [