@polderlabs/bizar 3.9.0 → 3.10.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/README.md +47 -1
- package/cli/bin.mjs +175 -68
- package/cli/copy.mjs +13 -6
- package/cli/graph.mjs +54 -12
- package/cli/init.mjs +16 -6
- package/cli/install.mjs +58 -15
- package/cli/update.mjs +41 -21
- package/config/AGENTS.md +6 -1
- package/config/agents/baldr.md +3 -0
- package/config/agents/forseti.md +3 -0
- package/config/agents/frigg.md +4 -0
- package/config/agents/heimdall.md +3 -0
- package/config/agents/hermod.md +3 -0
- package/config/agents/mimir.md +3 -0
- package/config/agents/quick.md +4 -0
- package/config/agents/semble-search.md +3 -0
- package/config/agents/thor.md +3 -0
- package/config/agents/tyr.md +3 -0
- package/config/agents/vidarr.md +3 -0
- package/config/agents/vor.md +3 -0
- package/config/commands/bizar.md +2 -2
- package/config/opencode.json +368 -0
- package/config/opencode.json.template +30 -0
- package/config/rules/thinking.md +56 -0
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -143,7 +143,9 @@ npx bizar
|
|
|
143
143
|
|
|
144
144
|
The interactive installer walks you through component selection, agent choice, install mode, API key setup, and auto-restarts opencode.
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
> **Windows users:** the `npm install -g @polderlabs/bizar` command above is the recommended path on Windows. The installer uses `irm | iex` for uv, `py -m pip` for the pip fallback, `taskkill` for forced kills, and JS `setTimeout` instead of `sleep` — so it works on both Windows PowerShell and POSIX shells. See the [Windows](#-windows) section below for prerequisites, known limitations, and the optional graph feature install.
|
|
147
|
+
|
|
148
|
+
### Source — git clone (Linux/macOS contributors)
|
|
147
149
|
|
|
148
150
|
```bash
|
|
149
151
|
git clone git@github.com:DrB0rk/BizarHarness.git
|
|
@@ -154,6 +156,50 @@ chmod +x install.sh
|
|
|
154
156
|
|
|
155
157
|
Copies agent definitions and config to `~/.config/opencode/`, merges `opencode.json`, and prints next steps. Use this if you want to hack on BizarHarness itself.
|
|
156
158
|
|
|
159
|
+
> This `install.sh` script is bash-only and does **not** run on Windows natively. Windows contributors should use the npm path above instead.
|
|
160
|
+
|
|
161
|
+
### Windows
|
|
162
|
+
|
|
163
|
+
The `bizar` CLI is cross-platform — the npm install runs a Node.js installer that handles `curl | sh` redirects, signal handling, and temp paths on Windows PowerShell automatically.
|
|
164
|
+
|
|
165
|
+
**Prerequisites**
|
|
166
|
+
|
|
167
|
+
- [Node.js 18+](https://nodejs.org/) on `PATH`
|
|
168
|
+
- [Python 3.10+](https://www.python.org/downloads/windows/) (only required for the optional `bizar graph` knowledge-graph feature)
|
|
169
|
+
- [Git for Windows](https://git-scm.com/download/win) — only needed if you want to run the bash `install.sh` from a Git Bash shell
|
|
170
|
+
|
|
171
|
+
**Install**
|
|
172
|
+
|
|
173
|
+
```powershell
|
|
174
|
+
npm install -g @polderlabs/bizar
|
|
175
|
+
bizar
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Optional: graph feature (knowledge graph + per-project .bizar/)**
|
|
179
|
+
|
|
180
|
+
The `bizar graph build` subcommand uses [graphify](https://github.com/bretbhomas/graphify). Install uv and graphify from PowerShell:
|
|
181
|
+
|
|
182
|
+
```powershell
|
|
183
|
+
# Install uv (Python package manager) — Windows-native PowerShell installer
|
|
184
|
+
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
185
|
+
|
|
186
|
+
# Install graphify
|
|
187
|
+
uv tool install graphifyy
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Then build the project graph from any repo:
|
|
191
|
+
|
|
192
|
+
```powershell
|
|
193
|
+
bizar graph build
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Known Windows limitations**
|
|
197
|
+
|
|
198
|
+
- The `blessed` TUI library may render with quirks inside Windows Terminal; if you see garbled output in the TUI, use the browser UI via `bizar dash start --bg` and open http://localhost:4321 instead.
|
|
199
|
+
- Some shell scripts under `config/skills/embedded-esp-idf/` are bash-only and require Git Bash (or WSL).
|
|
200
|
+
- The bash `install.sh` does not run on Windows cmd/PowerShell; use the `npm install -g` path above.
|
|
201
|
+
- RTK (Rust Token Killer) ships a bash-only installer. On Windows, install it manually via [Cargo](https://github.com/rtk-ai/rtk#installation) or a [prebuilt binary](https://github.com/rtk-ai/rtk/releases), then run `rtk init -g --opencode`.
|
|
202
|
+
|
|
157
203
|
### Prerequisites
|
|
158
204
|
|
|
159
205
|
- [opencode CLI](https://opencode.ai) installed and on `$PATH`
|
package/cli/bin.mjs
CHANGED
|
@@ -2,20 +2,17 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* cli/bin.mjs
|
|
4
4
|
*
|
|
5
|
-
* v3.
|
|
5
|
+
* v3.10.0 — `bizar` runtime CLI.
|
|
6
6
|
*
|
|
7
7
|
* Architecture:
|
|
8
8
|
* - `bizar` is the core runtime + installer + audit/init/export/update/plan
|
|
9
|
-
* + service commands.
|
|
10
|
-
* - The dashboard lives in
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* + service + dash commands.
|
|
10
|
+
* - The dashboard lives in `@polderlabs/bizar-dash` as a library.
|
|
11
|
+
* Commands live under `bizar dash <subcommand>` (new canonical form).
|
|
12
|
+
* `bizar dashboard` is a deprecated alias (still works, prints warning).
|
|
13
13
|
*
|
|
14
|
-
* Subcommands
|
|
15
|
-
* install, audit, init, export, plan, update, test-gate, service
|
|
16
|
-
*
|
|
17
|
-
* Flags (unchanged):
|
|
18
|
-
* --web / --no-web / --web-only / --bg / --detach
|
|
14
|
+
* Subcommands:
|
|
15
|
+
* install, audit, init, export, plan, update, test-gate, service, dash
|
|
19
16
|
*/
|
|
20
17
|
import { existsSync } from 'node:fs';
|
|
21
18
|
import { readFileSync } from 'node:fs';
|
|
@@ -77,36 +74,34 @@ function showHelp() {
|
|
|
77
74
|
Bizar — Norse Pantheon Agent System for opencode
|
|
78
75
|
|
|
79
76
|
Usage:
|
|
80
|
-
bizar
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
bizar
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
bizar
|
|
96
|
-
bizar
|
|
97
|
-
bizar
|
|
98
|
-
bizar
|
|
77
|
+
bizar <command> [options]
|
|
78
|
+
|
|
79
|
+
Commands:
|
|
80
|
+
install Run the interactive installer
|
|
81
|
+
audit Run security audit on agent configuration
|
|
82
|
+
init Initialize .bizar/ in current project
|
|
83
|
+
export [target] Export agents/rules to another harness
|
|
84
|
+
plan <subcommand> Manage visual plans
|
|
85
|
+
graph Per-project knowledge graph (powered by graphify)
|
|
86
|
+
test-gate Detect & run the project's test suite
|
|
87
|
+
update Update opencode, bizar, and/or bizar-plugin
|
|
88
|
+
service Manage the background service daemon
|
|
89
|
+
dash <subcommand> Manage the dashboard (start/stop/status/tui)
|
|
90
|
+
|
|
91
|
+
Examples:
|
|
92
|
+
bizar install
|
|
93
|
+
bizar audit
|
|
94
|
+
bizar dash start
|
|
95
|
+
bizar dash start --bg
|
|
96
|
+
bizar dash stop
|
|
97
|
+
bizar dash status
|
|
98
|
+
|
|
99
|
+
Run \`bizar <command> --help\` for per-command help.
|
|
99
100
|
|
|
100
101
|
Install:
|
|
101
102
|
npm install -g @polderlabs/bizar Install globally
|
|
102
|
-
npm install -g @polderlabs/bizar-dash Optional
|
|
103
|
+
npm install -g @polderlabs/bizar-dash Optional dashboard package
|
|
103
104
|
npm install -g @polderlabs/bizar-plugin Bizar opencode plugin
|
|
104
|
-
|
|
105
|
-
Notes:
|
|
106
|
-
The TUI is the default command — press 1-8 for tabs, q to quit.
|
|
107
|
-
\`bizar --bg\` launches the dashboard detached; \`bizar dashboard stop\`
|
|
108
|
-
terminates it. The web dashboard lives in the \`@polderlabs/bizar-dash\`
|
|
109
|
-
package — if it's not installed, you'll be prompted to install it.
|
|
110
105
|
`);
|
|
111
106
|
}
|
|
112
107
|
|
|
@@ -181,7 +176,7 @@ function showInstallHelp() {
|
|
|
181
176
|
|
|
182
177
|
function showUpdateHelp() {
|
|
183
178
|
console.log(`
|
|
184
|
-
bizar update — Update opencode, bizar,
|
|
179
|
+
bizar update — Update opencode, bizar, and/or bizar-plugin
|
|
185
180
|
|
|
186
181
|
Usage:
|
|
187
182
|
bizar update Interactive prompt for components
|
|
@@ -207,7 +202,7 @@ function showUpdateHelp() {
|
|
|
207
202
|
• Sends SIGTERM, waits up to 5s, escalates to SIGKILL if needed.
|
|
208
203
|
• Re-runs the install script so the deployed plugin source matches
|
|
209
204
|
the just-upgraded npm version (avoids the version-skew trap).
|
|
210
|
-
• If the dashboard was running and bizar
|
|
205
|
+
• If the dashboard was running and bizar or dash was updated,
|
|
211
206
|
spawns a fresh detached dashboard process with the new code
|
|
212
207
|
(skipped with --no-restart).
|
|
213
208
|
|
|
@@ -244,19 +239,35 @@ function showServiceHelp() {
|
|
|
244
239
|
`);
|
|
245
240
|
}
|
|
246
241
|
|
|
247
|
-
function
|
|
242
|
+
function showDashHelp() {
|
|
248
243
|
console.log(`
|
|
249
|
-
bizar
|
|
244
|
+
bizar dash — Manage the Bizar dashboard
|
|
250
245
|
|
|
251
246
|
Usage:
|
|
252
|
-
bizar
|
|
253
|
-
bizar dashboard start Same
|
|
254
|
-
bizar dashboard stop Kill the running dashboard
|
|
255
|
-
bizar dashboard status Show port + URL
|
|
247
|
+
bizar dash <subcommand> [options]
|
|
256
248
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
249
|
+
Subcommands:
|
|
250
|
+
start [--bg] [--port N] Start the dashboard (default port 4321)
|
|
251
|
+
stop Stop the running dashboard
|
|
252
|
+
status Show dashboard port and URL
|
|
253
|
+
tui [--no-web] Launch the TUI
|
|
254
|
+
|
|
255
|
+
Options:
|
|
256
|
+
--bg Detach and run in background (for start)
|
|
257
|
+
--port N Override the default port
|
|
258
|
+
--no-web Skip launching the web UI (for tui)
|
|
259
|
+
|
|
260
|
+
Examples:
|
|
261
|
+
bizar dash start
|
|
262
|
+
bizar dash start --bg
|
|
263
|
+
bizar dash stop
|
|
264
|
+
bizar dash status
|
|
265
|
+
bizar dash tui
|
|
266
|
+
bizar dash tui --no-web
|
|
267
|
+
|
|
268
|
+
Note:
|
|
269
|
+
\`bizar dashboard\` is a deprecated alias for \`bizar dash\` and still
|
|
270
|
+
works, but new code should use \`bizar dash\`.
|
|
260
271
|
`);
|
|
261
272
|
}
|
|
262
273
|
|
|
@@ -420,35 +431,131 @@ async function main() {
|
|
|
420
431
|
} else if (args[0] === 'service') {
|
|
421
432
|
if (isHelpRequest) showServiceHelp();
|
|
422
433
|
else await runServiceCommand(args[1]);
|
|
423
|
-
} else if (args[0] === '
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
434
|
+
} else if (args[0] === 'dash' || args[0] === 'dashboard') {
|
|
435
|
+
// `bizar dashboard` is a deprecated alias for `bizar dash`
|
|
436
|
+
if (args[0] === 'dashboard') {
|
|
437
|
+
console.warn(chalk.yellow(' ⚠ `bizar dashboard` is deprecated, use `bizar dash` instead.'));
|
|
438
|
+
}
|
|
439
|
+
const dashArgs = args.slice(1); // everything after 'dash' or 'dashboard'
|
|
440
|
+
if (dashArgs.length === 0 || isHelpRequest) {
|
|
441
|
+
showDashHelp();
|
|
442
|
+
} else {
|
|
443
|
+
await runDash(dashArgs);
|
|
444
|
+
}
|
|
431
445
|
} else if (isHelpRequest) {
|
|
432
446
|
showHelp();
|
|
433
447
|
} else {
|
|
434
|
-
//
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
448
|
+
// No args — show help (breaking: previously launched TUI)
|
|
449
|
+
showHelp();
|
|
450
|
+
process.exit(1);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// ── Dashboard subcommand ───────────────────────────────────────────────────────
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Parse dash-specific options from an array of args.
|
|
458
|
+
* Returns { opts, remaining } where opts have --bg / --port stripped.
|
|
459
|
+
*/
|
|
460
|
+
function parseDashOpts(dashArgs) {
|
|
461
|
+
const opts = { bg: false, port: null, noWeb: false };
|
|
462
|
+
const remaining = [];
|
|
463
|
+
for (let i = 0; i < dashArgs.length; i++) {
|
|
464
|
+
const a = dashArgs[i];
|
|
465
|
+
if (a === '--bg') {
|
|
466
|
+
opts.bg = true;
|
|
467
|
+
} else if (a === '--no-web') {
|
|
468
|
+
opts.noWeb = true;
|
|
469
|
+
} else if (a === '--port' && i + 1 < dashArgs.length) {
|
|
470
|
+
opts.port = Number(dashArgs[i + 1]);
|
|
471
|
+
i++;
|
|
442
472
|
} else {
|
|
443
|
-
|
|
444
|
-
console.log(chalk.cyan(' npm install -g @polderlabs/bizar-dash'));
|
|
445
|
-
console.log('');
|
|
446
|
-
console.log('Or run the installer to set up everything:');
|
|
447
|
-
console.log(chalk.cyan(' npx -y @polderlabs/bizar install'));
|
|
473
|
+
remaining.push(a);
|
|
448
474
|
}
|
|
449
475
|
}
|
|
476
|
+
return { opts, remaining };
|
|
450
477
|
}
|
|
451
478
|
|
|
479
|
+
/**
|
|
480
|
+
* Try to load the dashboard CLI module.
|
|
481
|
+
* Try 1: import via the package exports map (@polderlabs/bizar-dash/dash-cli)
|
|
482
|
+
* Try 2: file path probing for older installs or dev trees
|
|
483
|
+
*/
|
|
484
|
+
async function loadDashCli() {
|
|
485
|
+
// Try 1: import via exports map (requires Tyr's package.json changes)
|
|
486
|
+
try {
|
|
487
|
+
const mod = await import('@polderlabs/bizar-dash/dash-cli');
|
|
488
|
+
return mod;
|
|
489
|
+
} catch (_e) {
|
|
490
|
+
// fall through to file probing
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Try 2: file path probing
|
|
494
|
+
const { join } = await import('node:path');
|
|
495
|
+
const { homedir } = await import('node:os');
|
|
496
|
+
const candidates = [
|
|
497
|
+
// npm global install
|
|
498
|
+
join(homedir(), '.npm-global', 'lib', 'node_modules', '@polderlabs', 'bizar-dash', 'src', 'cli.mjs'),
|
|
499
|
+
// nvm
|
|
500
|
+
join(homedir(), '.nvm', 'versions', 'node'),
|
|
501
|
+
// relative to current process
|
|
502
|
+
join(process.execPath, '..', '..', 'lib', 'node_modules', '@polderlabs', 'bizar-dash', 'src', 'cli.mjs'),
|
|
503
|
+
// local node_modules
|
|
504
|
+
join(process.cwd(), 'node_modules', '@polderlabs', 'bizar-dash', 'src', 'cli.mjs'),
|
|
505
|
+
];
|
|
506
|
+
|
|
507
|
+
const { pathToFileURL } = await import('node:url');
|
|
508
|
+
for (const p of candidates) {
|
|
509
|
+
try {
|
|
510
|
+
const url = pathToFileURL(p).href;
|
|
511
|
+
const mod = await import(url);
|
|
512
|
+
return mod;
|
|
513
|
+
} catch (_e) {
|
|
514
|
+
// try next
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Dispatch a dashboard subcommand by loading the dashboard module and
|
|
522
|
+
* calling the appropriate exported function.
|
|
523
|
+
*/
|
|
524
|
+
async function runDash(dashArgs) {
|
|
525
|
+
const { opts, remaining } = parseDashOpts(dashArgs);
|
|
526
|
+
const sub = remaining[0];
|
|
527
|
+
const subOpts = { ...opts, subArgs: remaining.slice(1) };
|
|
528
|
+
|
|
529
|
+
const dashModule = await loadDashCli();
|
|
530
|
+
if (!dashModule) {
|
|
531
|
+
console.error(chalk.red(' ✗ Dashboard not installed.'));
|
|
532
|
+
console.error(chalk.dim(' Run: npm install -g @polderlabs/bizar-dash'));
|
|
533
|
+
console.error(chalk.dim(' Or: npx -y @polderlabs/bizar install'));
|
|
534
|
+
process.exit(1);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
switch (sub) {
|
|
538
|
+
case 'start':
|
|
539
|
+
await dashModule.start(subOpts);
|
|
540
|
+
break;
|
|
541
|
+
case 'stop':
|
|
542
|
+
await dashModule.stop(subOpts);
|
|
543
|
+
break;
|
|
544
|
+
case 'status':
|
|
545
|
+
await dashModule.status(subOpts);
|
|
546
|
+
break;
|
|
547
|
+
case 'tui':
|
|
548
|
+
await dashModule.tui(subOpts);
|
|
549
|
+
break;
|
|
550
|
+
default:
|
|
551
|
+
console.error(chalk.red(` ✗ Unknown subcommand: ${sub}`));
|
|
552
|
+
showDashHelp();
|
|
553
|
+
process.exit(1);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
558
|
+
|
|
452
559
|
await main().catch((err) => {
|
|
453
560
|
console.error(chalk.red(`bizar: ${err && err.message ? err.message : String(err)}`));
|
|
454
561
|
process.exit(1);
|
package/cli/copy.mjs
CHANGED
|
@@ -356,6 +356,9 @@ export async function installRtk() {
|
|
|
356
356
|
const spinner = ora({ text: 'Installing RTK (Rust Token Killer)...', color: 'magenta' }).start();
|
|
357
357
|
|
|
358
358
|
if (process.platform === 'win32') {
|
|
359
|
+
// RTK ships a bash-only installer; no PowerShell equivalent. On
|
|
360
|
+
// Windows the user must install RTK manually (e.g. via Cargo or
|
|
361
|
+
// prebuilt binary) and re-run `rtk init -g --opencode`.
|
|
359
362
|
spinner.fail(chalk.red('Automatic RTK install not supported on Windows. Install manually from https://github.com/rtk-ai/rtk'));
|
|
360
363
|
return false;
|
|
361
364
|
}
|
|
@@ -392,13 +395,17 @@ export async function installSemble() {
|
|
|
392
395
|
spinner.text = 'Installing uv (Python package manager)...';
|
|
393
396
|
try {
|
|
394
397
|
if (process.platform === 'win32') {
|
|
395
|
-
|
|
396
|
-
|
|
398
|
+
// Windows: use the official PowerShell installer
|
|
399
|
+
execSync(
|
|
400
|
+
'powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"',
|
|
401
|
+
{ stdio: 'pipe', timeout: 60000 },
|
|
402
|
+
);
|
|
403
|
+
} else {
|
|
404
|
+
execSync(
|
|
405
|
+
'curl -LsSf https://astral.sh/uv/install.sh | sh',
|
|
406
|
+
{ stdio: 'pipe', timeout: 60000 },
|
|
407
|
+
);
|
|
397
408
|
}
|
|
398
|
-
execSync(
|
|
399
|
-
'curl -LsSf https://astral.sh/uv/install.sh | sh',
|
|
400
|
-
{ stdio: 'pipe', timeout: 60000 },
|
|
401
|
-
);
|
|
402
409
|
spinner.text = 'Setting up Semble...';
|
|
403
410
|
} catch {
|
|
404
411
|
spinner.fail(chalk.red('uv install failed. Install manually from https://docs.astral.sh/uv'));
|
package/cli/graph.mjs
CHANGED
|
@@ -20,13 +20,18 @@ export const GRAPHIFY_OUT_ENV = 'GRAPHIFY_OUT';
|
|
|
20
20
|
/**
|
|
21
21
|
* Find a Python 3 interpreter on PATH.
|
|
22
22
|
* Returns the absolute path string, or null if none found.
|
|
23
|
+
*
|
|
24
|
+
* On Windows the `py` launcher is the most reliable option; on Unix
|
|
25
|
+
* we prefer python3 then python.
|
|
23
26
|
*/
|
|
24
27
|
export function findPython() {
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
const candidates = process.platform === 'win32'
|
|
29
|
+
? ['py', 'python', 'python3']
|
|
30
|
+
: ['python3', 'python'];
|
|
31
|
+
for (const name of candidates) {
|
|
27
32
|
try {
|
|
28
33
|
const result = spawnSync(name, ['--version'], { encoding: 'utf8', timeout: 5000 });
|
|
29
|
-
if (result.status === 0 && result.stdout.includes('Python
|
|
34
|
+
if (result.status === 0 && result.stdout.includes('Python')) {
|
|
30
35
|
return name;
|
|
31
36
|
}
|
|
32
37
|
} catch {
|
|
@@ -37,10 +42,33 @@ export function findPython() {
|
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
/**
|
|
40
|
-
* Check whether graphify is available
|
|
45
|
+
* Check whether graphify is available.
|
|
46
|
+
*
|
|
47
|
+
* Strategy:
|
|
48
|
+
* 1. Probe PATH for the `graphify` binary (uv tool install puts a shim at
|
|
49
|
+
* ~/.local/bin/graphify — invisible to the isolated python -m lookup).
|
|
50
|
+
* 2. Fall back to `python -m graphify --version` for pip/pipx installs.
|
|
51
|
+
*
|
|
41
52
|
* Returns true if available, false otherwise.
|
|
42
53
|
*/
|
|
43
54
|
function checkGraphify(python) {
|
|
55
|
+
// Try 1: check for the graphify binary on PATH (uv tool install shim).
|
|
56
|
+
// On Windows the Python launcher `py` is not on PATH by default, but the
|
|
57
|
+
// graphify shim is placed next to pip/pipx so a plain `where` works.
|
|
58
|
+
const whichCmd = process.platform === 'win32' ? 'where' : 'which';
|
|
59
|
+
try {
|
|
60
|
+
const whichResult = spawnSync(whichCmd, ['graphify'], {
|
|
61
|
+
encoding: 'utf8',
|
|
62
|
+
timeout: 5000,
|
|
63
|
+
});
|
|
64
|
+
if (whichResult.status === 0 && (whichResult.stdout || '').trim().length > 0) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
} catch {
|
|
68
|
+
// fall through to python -m check
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Try 2: check via python -m (catches pip/pipx installs into system python)
|
|
44
72
|
try {
|
|
45
73
|
const result = spawnSync(python, ['-m', 'graphify', '--version'], {
|
|
46
74
|
encoding: 'utf8',
|
|
@@ -219,19 +247,33 @@ async function cmdInstall() {
|
|
|
219
247
|
return 1;
|
|
220
248
|
}
|
|
221
249
|
|
|
222
|
-
|
|
250
|
+
// Prefer uv (handles PEP 668 isolated installs cleanly); fall back to pip.
|
|
251
|
+
const { detectUv } = await import('./utils.mjs');
|
|
252
|
+
const hasUv = await detectUv();
|
|
223
253
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
254
|
+
if (hasUv) {
|
|
255
|
+
console.log(chalk.dim(' Installing graphify via uv...\n'));
|
|
256
|
+
const result = spawnSync('uv', ['tool', 'install', 'graphifyy'], { stdio: 'inherit' });
|
|
257
|
+
if (result.status !== 0) {
|
|
258
|
+
console.log(chalk.red(' ✗ uv tool install graphifyy failed.\n'));
|
|
259
|
+
return 1;
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
console.log(chalk.dim(' Installing graphify via pip...\n'));
|
|
263
|
+
// On Windows `pip` may not be on PATH; use `py -m pip` as the fallback.
|
|
264
|
+
const pipCmd = process.platform === 'win32'
|
|
265
|
+
? [python, '-m', 'pip']
|
|
266
|
+
: ['pip'];
|
|
267
|
+
const result = spawnSync(pipCmd[0], pipCmd.slice(1).concat(['install', 'graphifyy']), { stdio: 'inherit' });
|
|
268
|
+
if (result.status !== 0) {
|
|
269
|
+
console.log(chalk.red(' ✗ pip install graphifyy failed.\n'));
|
|
270
|
+
return 1;
|
|
271
|
+
}
|
|
230
272
|
}
|
|
231
273
|
|
|
232
274
|
// graphify install --platform opencode --project
|
|
233
275
|
console.log(chalk.dim('\n $ graphify install --platform opencode --project'));
|
|
234
|
-
result = spawnSync('graphify', ['install', '--platform', 'opencode', '--project'], {
|
|
276
|
+
const result = spawnSync('graphify', ['install', '--platform', 'opencode', '--project'], {
|
|
235
277
|
stdio: 'inherit',
|
|
236
278
|
});
|
|
237
279
|
if (result.status !== 0) {
|
package/cli/init.mjs
CHANGED
|
@@ -154,12 +154,22 @@ ${stack.runner ? `- Dev: \`${stack.runner}\`` : ''}
|
|
|
154
154
|
// Soft step: never fails init. If graphify is missing or build errors,
|
|
155
155
|
// the user can retry manually with `bizar graph build`.
|
|
156
156
|
console.log(chalk.bold('\n--- Graph ---\n'));
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
});
|
|
162
|
-
|
|
157
|
+
|
|
158
|
+
// Check for the graphify binary on PATH first (uv tool install shim).
|
|
159
|
+
// Falls back to python -m for pip/pipx installs.
|
|
160
|
+
const whichCmd = process.platform === 'win32' ? 'where' : 'which';
|
|
161
|
+
const whichCheck = spawnSync(whichCmd, ['graphify'], { encoding: 'utf8', timeout: 5000 });
|
|
162
|
+
let graphifyAvailable = whichCheck.status === 0 && (whichCheck.stdout || '').trim().length > 0;
|
|
163
|
+
|
|
164
|
+
if (!graphifyAvailable) {
|
|
165
|
+
const python = process.platform === 'win32' ? 'py' : 'python3';
|
|
166
|
+
const detectGraphify = spawnSync(python, ['-c', 'import graphify; print(graphify.__version__)'], {
|
|
167
|
+
cwd,
|
|
168
|
+
encoding: 'utf8',
|
|
169
|
+
timeout: 5000,
|
|
170
|
+
});
|
|
171
|
+
graphifyAvailable = detectGraphify.status === 0 && (detectGraphify.stdout || '').trim().length > 0;
|
|
172
|
+
}
|
|
163
173
|
|
|
164
174
|
if (!graphifyAvailable) {
|
|
165
175
|
console.log(chalk.yellow(' graphify not detected — skipping project graph build.'));
|