@polderlabs/bizar 3.2.0 → 3.2.2
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 +28 -3
- package/cli/bootstrap.mjs +77 -0
- package/cli/install.mjs +95 -0
- package/package.json +2 -3
package/cli/bin.mjs
CHANGED
|
@@ -26,15 +26,29 @@ import { runInit } from './init.mjs';
|
|
|
26
26
|
import { runExport } from './export.mjs';
|
|
27
27
|
import runPlan from './plan.mjs';
|
|
28
28
|
import { runUpdate } from './update.mjs';
|
|
29
|
+
import { ensureSetup, checkSetupStatus } from './bootstrap.mjs';
|
|
29
30
|
|
|
30
31
|
const args = process.argv.slice(2);
|
|
31
32
|
|
|
33
|
+
// ── Bootstrap ─────────────────────────────────────────────────────────────────
|
|
34
|
+
// Every bin command checks setup status on first invocation.
|
|
35
|
+
// Skip only when: --postinstall (manual trigger), --check (status only),
|
|
36
|
+
// BIZAR_SKIP_INSTALL=1 (disabled), or already handled via npm script.
|
|
37
|
+
if (
|
|
38
|
+
!args.includes('--postinstall') &&
|
|
39
|
+
!args.includes('--check') &&
|
|
40
|
+
!process.env.BIZAR_SKIP_INSTALL
|
|
41
|
+
) {
|
|
42
|
+
await ensureSetup({ silent: true });
|
|
43
|
+
}
|
|
44
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
45
|
+
|
|
32
46
|
function showHelp() {
|
|
33
47
|
console.log(`
|
|
34
48
|
Bizar — Norse Pantheon Agent System for opencode
|
|
35
49
|
|
|
36
50
|
Usage:
|
|
37
|
-
bizar Launch the TUI dashboard (
|
|
51
|
+
bizar Launch the TUI dashboard (auto-runs first-time setup if needed)
|
|
38
52
|
bizar --web Launch TUI + auto-open web dashboard
|
|
39
53
|
bizar --no-web Launch TUI only (no browser)
|
|
40
54
|
bizar --web-only Web dashboard only (no TUI, in browser)
|
|
@@ -49,6 +63,8 @@ function showHelp() {
|
|
|
49
63
|
bizar update Update opencode, bizar, and/or bizar-plugin
|
|
50
64
|
bizar service Manage the background service daemon
|
|
51
65
|
bizar dashboard Launch the web dashboard (uses bizar-dash)
|
|
66
|
+
bizar --setup Re-run setup manually (agents, plugin, RTK, Semble, Skills CLI)
|
|
67
|
+
bizar --check Print setup status as JSON, exit 1 if setup needed
|
|
52
68
|
bizar --help Show this help
|
|
53
69
|
|
|
54
70
|
Install:
|
|
@@ -229,8 +245,17 @@ async function runServiceCommand(sub) {
|
|
|
229
245
|
await runService(sub || 'status', args.slice(2));
|
|
230
246
|
}
|
|
231
247
|
|
|
232
|
-
if (args
|
|
233
|
-
|
|
248
|
+
if (args.includes('--check')) {
|
|
249
|
+
const status = checkSetupStatus();
|
|
250
|
+
console.log(JSON.stringify(status, null, 2));
|
|
251
|
+
process.exit(status.needed ? 1 : 0);
|
|
252
|
+
} else if (args.includes('--setup')) {
|
|
253
|
+
await ensureSetup({ silent: false });
|
|
254
|
+
process.exit(0);
|
|
255
|
+
} else if (args.includes('--postinstall')) {
|
|
256
|
+
// Legacy manual trigger — now an alias for --setup
|
|
257
|
+
await ensureSetup({ silent: false });
|
|
258
|
+
process.exit(0);
|
|
234
259
|
} else if (args[0] === 'audit') {
|
|
235
260
|
if (args.includes('--help') || args.includes('-h')) showAuditHelp();
|
|
236
261
|
else await runAudit();
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/bootstrap.mjs
|
|
3
|
+
*
|
|
4
|
+
* v3.2.2 — Self-bootstrap on first bin invocation.
|
|
5
|
+
*
|
|
6
|
+
* Replaces the postinstall hook (which npm v10+ blocks by default).
|
|
7
|
+
* Every `bizar` bin command now checks setup status on entry and runs
|
|
8
|
+
* the postinstall logic automatically if anything is missing.
|
|
9
|
+
*
|
|
10
|
+
* Key design:
|
|
11
|
+
* - Checks for SETUP_MARKERS (odyssey files that prove setup was done)
|
|
12
|
+
* - If ALL markers exist → silent no-op
|
|
13
|
+
* - If ANY marker is missing → runs postinstall logic
|
|
14
|
+
* - Idempotent: already-installed components are skipped by the postinstall
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync } from 'node:fs';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import chalk from 'chalk';
|
|
19
|
+
|
|
20
|
+
import { opencodeAgentsDir, opencodeConfigDir } from './utils.mjs';
|
|
21
|
+
import { runPostInstall } from './install.mjs';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Key files that prove setup has been run at least once.
|
|
25
|
+
* If ALL exist → setup is considered complete.
|
|
26
|
+
* If ANY are missing → setup is needed.
|
|
27
|
+
*/
|
|
28
|
+
const SETUP_MARKERS = [
|
|
29
|
+
join(opencodeAgentsDir(), 'odin.md'), // core agent installed
|
|
30
|
+
join(opencodeConfigDir(), 'plugins', 'bizar'), // plugin installed
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Check which markers are currently present.
|
|
35
|
+
* Returns { needed: true, missing: [...] } if any marker is absent.
|
|
36
|
+
* Returns { needed: false } if all markers are present.
|
|
37
|
+
*/
|
|
38
|
+
export function checkSetupStatus() {
|
|
39
|
+
const missing = SETUP_MARKERS.filter((p) => !existsSync(p));
|
|
40
|
+
if (missing.length > 0) {
|
|
41
|
+
return { needed: true, missing };
|
|
42
|
+
}
|
|
43
|
+
return { needed: false };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Print a first-run setup message.
|
|
48
|
+
* Uses chalk — kept minimal to avoid polluting stdout before the TUI loads.
|
|
49
|
+
*/
|
|
50
|
+
function printSetupBanner() {
|
|
51
|
+
// Use console.log directly to avoid chalk formatting issues in non-TTY
|
|
52
|
+
console.log('\n ⚡ First-time setup — Bizar needs to install agents, plugin, RTK, Semble, Skills CLI...\n');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Run setup if anything is missing.
|
|
57
|
+
*
|
|
58
|
+
* Options:
|
|
59
|
+
* autoApprove — if true, skip interactive prompts (BIZAR_SKIP_OPTIONAL_INSTALLS already set by caller)
|
|
60
|
+
* silent — if true, suppress the "first-time setup" banner (used when invoked silently on bin entry)
|
|
61
|
+
*
|
|
62
|
+
* Idempotent: runPostInstall() skips already-installed components.
|
|
63
|
+
*/
|
|
64
|
+
export async function ensureSetup({ silent = false } = {}) {
|
|
65
|
+
const status = checkSetupStatus();
|
|
66
|
+
|
|
67
|
+
if (!status.needed) {
|
|
68
|
+
return; // already done, nothing to do
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Something is missing — run setup
|
|
72
|
+
if (!silent) {
|
|
73
|
+
printSetupBanner();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
await runPostInstall();
|
|
77
|
+
}
|
package/cli/install.mjs
CHANGED
|
@@ -294,7 +294,102 @@ export async function runInstaller() {
|
|
|
294
294
|
console.log(chalk.dim('\n Odin watches. The Pantheon awaits. ᛟ\n'));
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
+
// ── Interactive prompts for optional packages ─────────────────────────────────
|
|
298
|
+
|
|
299
|
+
import { execSync } from 'node:child_process';
|
|
300
|
+
import { createInterface } from 'node:readline/promises';
|
|
301
|
+
import { stdin, stdout } from 'node:process';
|
|
302
|
+
|
|
303
|
+
async function promptYesNo(question, defaultYes = true) {
|
|
304
|
+
// If not a TTY (CI, automated install), skip the prompt
|
|
305
|
+
if (!stdin.isTTY || !stdout.isTTY) {
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
310
|
+
try {
|
|
311
|
+
const hint = defaultYes ? '[Y/n]' : '[y/N]';
|
|
312
|
+
const answer = (await rl.question(` ${question} ${hint}: `)).trim().toLowerCase();
|
|
313
|
+
rl.close();
|
|
314
|
+
|
|
315
|
+
if (answer === '') return defaultYes;
|
|
316
|
+
if (['y', 'yes'].includes(answer)) return true;
|
|
317
|
+
if (['n', 'no'].includes(answer)) return false;
|
|
318
|
+
return defaultYes;
|
|
319
|
+
} catch {
|
|
320
|
+
rl.close();
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
async function isPackageInstalled(name) {
|
|
326
|
+
try {
|
|
327
|
+
const globalRoot = execSync('npm root -g', { encoding: 'utf8' }).trim();
|
|
328
|
+
require.resolve(name, { paths: [globalRoot] });
|
|
329
|
+
return true;
|
|
330
|
+
} catch {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async function promptAndInstallOptional() {
|
|
336
|
+
// Plugin
|
|
337
|
+
const pluginInstalled = await isPackageInstalled('@polderlabs/bizar-plugin');
|
|
338
|
+
if (!pluginInstalled) {
|
|
339
|
+
console.log('');
|
|
340
|
+
console.log(' The Bizar opencode plugin is required for the /bizar command and agent integration.');
|
|
341
|
+
const install = await promptYesNo(
|
|
342
|
+
'Install @polderlabs/bizar-plugin?',
|
|
343
|
+
true,
|
|
344
|
+
);
|
|
345
|
+
if (install) {
|
|
346
|
+
try {
|
|
347
|
+
console.log(' Installing @polderlabs/bizar-plugin...');
|
|
348
|
+
execSync('npm install -g @polderlabs/bizar-plugin', { stdio: 'inherit' });
|
|
349
|
+
console.log(' ✓ @polderlabs/bizar-plugin installed');
|
|
350
|
+
} catch (err) {
|
|
351
|
+
console.log(` ✗ Failed to install @polderlabs/bizar-plugin: ${err.message}`);
|
|
352
|
+
console.log(' You can install it later with: npm install -g @polderlabs/bizar-plugin');
|
|
353
|
+
}
|
|
354
|
+
} else {
|
|
355
|
+
console.log(' Skipped. Install later with: npm install -g @polderlabs/bizar-plugin');
|
|
356
|
+
}
|
|
357
|
+
} else {
|
|
358
|
+
console.log(' ✓ @polderlabs/bizar-plugin already installed');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Dashboard
|
|
362
|
+
const dashInstalled = await isPackageInstalled('@polderlabs/bizar-dash');
|
|
363
|
+
if (!dashInstalled) {
|
|
364
|
+
console.log('');
|
|
365
|
+
console.log(' The Bizar dashboard provides the web UI (React + Vite) and TUI (blessed).');
|
|
366
|
+
console.log(' It\'s optional — install it for the full experience, or use the CLI alone.');
|
|
367
|
+
const install = await promptYesNo(
|
|
368
|
+
'Install @polderlabs/bizar-dash?',
|
|
369
|
+
true,
|
|
370
|
+
);
|
|
371
|
+
if (install) {
|
|
372
|
+
try {
|
|
373
|
+
console.log(' Installing @polderlabs/bizar-dash...');
|
|
374
|
+
execSync('npm install -g @polderlabs/bizar-dash', { stdio: 'inherit' });
|
|
375
|
+
console.log(' ✓ @polderlabs/bizar-dash installed');
|
|
376
|
+
} catch (err) {
|
|
377
|
+
console.log(` ✗ Failed to install @polderlabs/bizar-dash: ${err.message}`);
|
|
378
|
+
console.log(' You can install it later with: npm install -g @polderlabs/bizar-dash');
|
|
379
|
+
}
|
|
380
|
+
} else {
|
|
381
|
+
console.log(' Skipped. Install later with: npm install -g @polderlabs/bizar-dash');
|
|
382
|
+
}
|
|
383
|
+
} else {
|
|
384
|
+
console.log(' ✓ @polderlabs/bizar-dash already installed');
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
297
388
|
export async function runPostInstall() {
|
|
389
|
+
// Skip interactive prompts in CI / non-TTY environments
|
|
390
|
+
if (!process.env.BIZAR_SKIP_OPTIONAL_INSTALLS) {
|
|
391
|
+
await promptAndInstallOptional();
|
|
392
|
+
}
|
|
298
393
|
const { mkdirSync, copyFileSync, existsSync } = await import('node:fs');
|
|
299
394
|
const { execSync } = await import('node:child_process');
|
|
300
395
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polderlabs/bizar",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.2",
|
|
4
4
|
"description": "Norse-pantheon multi-agent system for opencode — 13 agents across 4 cost tiers with cost-aware routing, per-project Hindsight memory, mods, schedules, and a background service daemon",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
"templates/"
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
|
-
"typecheck": "tsc --noEmit"
|
|
17
|
-
"postinstall": "node cli/bin.mjs --postinstall"
|
|
16
|
+
"typecheck": "tsc --noEmit"
|
|
18
17
|
},
|
|
19
18
|
"keywords": [
|
|
20
19
|
"opencode",
|