@polderlabs/bizar 4.2.0 → 4.2.1

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/memory.mjs CHANGED
@@ -254,7 +254,25 @@ async function cmdInit(args) {
254
254
  const projectRoot = getProjectRoot();
255
255
  const { loadConfig, saveConfig } = memoryStore;
256
256
 
257
- const yesFlag = args.includes('--yes');
257
+ // ── Help (must come before any work — fixes gap in cmdInit --help coverage)
258
+ if (args.includes('--help') || args.includes('-h')) {
259
+ console.log(`
260
+ Usage: bizar memory init [options]
261
+
262
+ Initialize memory config for this project.
263
+
264
+ Options:
265
+ --memory-mode <managed|local-only> Vault mode (default: managed)
266
+ --memory-repo-name <name> Vault directory name (managed mode)
267
+ --yes Skip prompts; non-interactive
268
+ --non-interactive Alias for --yes
269
+ --help, -h Show this help
270
+ `.trim());
271
+ return;
272
+ }
273
+
274
+ // --non-interactive is the canonical alias; --yes kept for backward compat.
275
+ const yesFlag = args.includes('--yes') || args.includes('--non-interactive');
258
276
 
259
277
  const existing = loadConfig(projectRoot);
260
278
  if (existing.exists) {
@@ -879,7 +897,7 @@ async function cmdUnlink(_args) {
879
897
  async function cmdPull(_args) {
880
898
  const projectRoot = getProjectRoot();
881
899
  const { loadConfig, resolveVault } = memoryStore;
882
- const { pull, isGitInstalled } = memoryGit;
900
+ const { pull, isGitInstalled, ensureUpstream } = memoryGit;
883
901
 
884
902
  const { config } = loadConfig(projectRoot);
885
903
  if (config.mode === 'local-only') {
@@ -892,7 +910,18 @@ async function cmdPull(_args) {
892
910
  process.exit(1);
893
911
  }
894
912
 
895
- const { vaultRoot } = resolveVault(projectRoot);
913
+ const { vaultRoot, branch } = resolveVault(projectRoot);
914
+ const remoteName = config.gitRemote ? 'origin' : 'origin';
915
+
916
+ // Pre-flight: ensure upstream is set. Without this, `git pull` fails with
917
+ // "no tracking information" on freshly-cloned or locally-initialized vaults.
918
+ const upstreamResult = ensureUpstream(vaultRoot, config.branch || branch || 'main', remoteName);
919
+ if (!upstreamResult.ok) {
920
+ info(`upstream not set (${upstreamResult.error}); pull may fail`);
921
+ } else if (upstreamResult.action === 'set') {
922
+ success(`set upstream to ${remoteName}/${config.branch || branch || 'main'}`);
923
+ }
924
+
896
925
  info(`pulling into ${vaultRoot}`);
897
926
  const result = pull(vaultRoot);
898
927
  if (!result.ok) {
@@ -985,7 +1014,7 @@ async function cmdPush(_args) {
985
1014
  async function cmdSync(args) {
986
1015
  const projectRoot = getProjectRoot();
987
1016
  const { loadConfig, resolveVault, validateAll, scanForSecrets, listNotes } = memoryStore;
988
- const { pull, addAll, commit: gitCommit, push: gitPush, status: gitStatus, acquireLock, isGitInstalled } = memoryGit;
1017
+ const { pull, addAll, commit: gitCommit, push: gitPush, status: gitStatus, acquireLock, isGitInstalled, ensureUpstream } = memoryGit;
989
1018
 
990
1019
  const { config } = loadConfig(projectRoot);
991
1020
  if (config.mode === 'local-only') {
@@ -998,7 +1027,19 @@ async function cmdSync(args) {
998
1027
  process.exit(1);
999
1028
  }
1000
1029
 
1001
- const { vaultRoot } = resolveVault(projectRoot);
1030
+ const { vaultRoot, branch } = resolveVault(projectRoot);
1031
+ const branchName = config.branch || branch || 'main';
1032
+
1033
+ // Pre-flight: ensure upstream is set. Some workflows create a vault before
1034
+ // origin exists; without upstream, `git pull` fails with "no tracking".
1035
+ if (config.gitRemote) {
1036
+ const upstreamResult = ensureUpstream(vaultRoot, branchName, 'origin');
1037
+ if (!upstreamResult.ok) {
1038
+ info(`upstream not set (${upstreamResult.error}); pull may fail`);
1039
+ } else if (upstreamResult.action === 'set') {
1040
+ success(`set upstream to origin/${branchName}`);
1041
+ }
1042
+ }
1002
1043
 
1003
1044
  // Acquire lock
1004
1045
  const lock = acquireLock(vaultRoot);
@@ -1237,7 +1278,7 @@ async function cmdConflicts(_args) {
1237
1278
  const filePath = join(vaultRoot, note.relPath);
1238
1279
  try {
1239
1280
  const content = rf(filePath, 'utf8');
1240
- if (/^<{7}\s|^={7}\s|>{7}\s/.test(content)) {
1281
+ if (/^<{7}\s|^={7}\s|^>{7}\s/m.test(content)) {
1241
1282
  conflicts.push({ relPath: note.relPath, reason: 'git conflict markers detected' });
1242
1283
  }
1243
1284
  } catch { /* skip */ }
@@ -1346,6 +1387,189 @@ async function cmdDoctor(_args) {
1346
1387
  console.log();
1347
1388
  }
1348
1389
 
1390
+ // ─── read / list / delete (Fix 4 + Fix 5 plumbing) ────────────────────────────
1391
+
1392
+ /**
1393
+ * Parse `--namespace <ns>` and return the chosen namespace plus the remaining
1394
+ * positional args. Defaults to 'project' when the flag is absent.
1395
+ */
1396
+ function parseNamespaceFlag(args) {
1397
+ const idx = args.indexOf('--namespace');
1398
+ if (idx === -1) return { namespace: 'project', rest: args };
1399
+ const v = args[idx + 1];
1400
+ if (!v || v.startsWith('-')) return { namespace: 'project', rest: args };
1401
+ // Remove the flag and its value from the rest
1402
+ const rest = args.filter((a, i) => i !== idx && i !== idx + 1);
1403
+ return { namespace: v, rest };
1404
+ }
1405
+
1406
+ /**
1407
+ * `bizar memory read [--namespace <ns>] <relpath>`
1408
+ *
1409
+ * Read a single note and print its raw content (frontmatter + body) to stdout.
1410
+ */
1411
+ async function cmdRead(args) {
1412
+ const projectRoot = getProjectRoot();
1413
+ const { resolveVault, resolveNamespaceRoot } = memoryStore;
1414
+
1415
+ if (args.includes('--help') || args.includes('-h')) {
1416
+ console.log(`
1417
+ Usage: bizar memory read [--namespace <ns>] <relpath>
1418
+
1419
+ Read a single note and print it to stdout (YAML frontmatter + body).
1420
+
1421
+ Options:
1422
+ --namespace <project|global|user> Namespace to read from (default: project)
1423
+ --help, -h Show this help
1424
+ `.trim());
1425
+ return;
1426
+ }
1427
+
1428
+ const { namespace, rest } = parseNamespaceFlag(args);
1429
+ const relpath = rest.filter((a) => !a.startsWith('-'))[0];
1430
+ if (!relpath) {
1431
+ error('relpath is required: `bizar memory read <relpath>`');
1432
+ info('run `bizar memory read --help` for usage');
1433
+ process.exit(1);
1434
+ }
1435
+
1436
+ let note;
1437
+ if (namespace === 'project') {
1438
+ note = memoryStore.readNote(projectRoot, relpath);
1439
+ } else {
1440
+ const vi = resolveVault(projectRoot);
1441
+ const root = resolveNamespaceRoot(vi, namespace);
1442
+ if (!root) {
1443
+ error(`unknown namespace: ${namespace}`);
1444
+ process.exit(1);
1445
+ }
1446
+ note = memoryStore.readNote(projectRoot, relpath, { root });
1447
+ }
1448
+
1449
+ if (!note) {
1450
+ error(`note not found: ${relpath}`);
1451
+ process.exit(1);
1452
+ }
1453
+ process.stdout.write(note.raw);
1454
+ if (!note.raw.endsWith('\n')) process.stdout.write('\n');
1455
+ }
1456
+
1457
+ /**
1458
+ * `bizar memory list [--namespace <ns>] [--json] [dir]`
1459
+ *
1460
+ * List notes under a directory. Default: walk the project namespace root.
1461
+ * Pass `dir` (e.g. `decisions/`) to scope to a subdirectory. The dir is
1462
+ * resolved RELATIVE TO the namespace root.
1463
+ */
1464
+ async function cmdList(args) {
1465
+ const projectRoot = getProjectRoot();
1466
+ const { resolveVault, resolveNamespaceRoot } = memoryStore;
1467
+
1468
+ if (args.includes('--help') || args.includes('-h')) {
1469
+ console.log(`
1470
+ Usage: bizar memory list [--namespace <ns>] [dir]
1471
+
1472
+ List notes under a directory. Default: the project namespace root.
1473
+
1474
+ Options:
1475
+ --namespace <project|global|user> Namespace to list (default: project)
1476
+ --json Emit JSON array
1477
+ [dir] Subdirectory under the namespace (e.g. decisions/)
1478
+ --help, -h Show this help
1479
+ `.trim());
1480
+ return;
1481
+ }
1482
+
1483
+ const { namespace, rest } = parseNamespaceFlag(args);
1484
+ const asJson = rest.includes('--json');
1485
+ const dir = rest.filter((a) => !a.startsWith('-'))[0] || '';
1486
+
1487
+ let notes;
1488
+ if (namespace === 'project') {
1489
+ notes = memoryStore.listNotes(projectRoot, dir ? { namespace: dir } : {});
1490
+ } else {
1491
+ const vi = resolveVault(projectRoot);
1492
+ const root = resolveNamespaceRoot(vi, namespace);
1493
+ if (!root) {
1494
+ error(`unknown namespace: ${namespace}`);
1495
+ process.exit(1);
1496
+ }
1497
+ notes = memoryStore.listNotes(projectRoot, dir ? { root, namespace: dir } : { root });
1498
+ }
1499
+
1500
+ // listNotes returns relPaths RELATIVE to the search root, so when we
1501
+ // filter by `dir` we need to re-attach the dir prefix for display —
1502
+ // otherwise the user sees `0001-foo.md` instead of the more useful
1503
+ // `decisions/0001-foo.md`. Strip a trailing slash for cleanliness.
1504
+ const displayPrefix = dir ? dir.replace(/\/+$/, '') + '/' : '';
1505
+
1506
+ if (asJson) {
1507
+ console.log(JSON.stringify(notes.map((n) => ({
1508
+ relPath: displayPrefix + n.relPath,
1509
+ size: n.size,
1510
+ mtime: n.mtime,
1511
+ })), null, 2));
1512
+ return;
1513
+ }
1514
+ if (notes.length === 0) {
1515
+ console.log(chalk.dim(' (no notes)'));
1516
+ return;
1517
+ }
1518
+ for (const n of notes) {
1519
+ console.log(` ${displayPrefix}${n.relPath}`);
1520
+ }
1521
+ }
1522
+
1523
+ /**
1524
+ * `bizar memory delete [--namespace <ns>] <relpath>`
1525
+ *
1526
+ * Delete a single note from the vault.
1527
+ */
1528
+ async function cmdDelete(args) {
1529
+ const projectRoot = getProjectRoot();
1530
+ const { resolveVault, resolveNamespaceRoot } = memoryStore;
1531
+
1532
+ if (args.includes('--help') || args.includes('-h')) {
1533
+ console.log(`
1534
+ Usage: bizar memory delete [--namespace <ns>] <relpath>
1535
+
1536
+ Delete a single note from the vault.
1537
+
1538
+ Options:
1539
+ --namespace <project|global|user> Namespace to delete from (default: project)
1540
+ --help, -h Show this help
1541
+ `.trim());
1542
+ return;
1543
+ }
1544
+
1545
+ const { namespace, rest } = parseNamespaceFlag(args);
1546
+ const relpath = rest.filter((a) => !a.startsWith('-'))[0];
1547
+ if (!relpath) {
1548
+ error('relpath is required: `bizar memory delete <relpath>`');
1549
+ info('run `bizar memory delete --help` for usage');
1550
+ process.exit(1);
1551
+ }
1552
+
1553
+ let ok;
1554
+ if (namespace === 'project') {
1555
+ ok = memoryStore.deleteNote(projectRoot, relpath);
1556
+ } else {
1557
+ const vi = resolveVault(projectRoot);
1558
+ const root = resolveNamespaceRoot(vi, namespace);
1559
+ if (!root) {
1560
+ error(`unknown namespace: ${namespace}`);
1561
+ process.exit(1);
1562
+ }
1563
+ ok = memoryStore.deleteNote(projectRoot, relpath, { root });
1564
+ }
1565
+
1566
+ if (!ok) {
1567
+ error(`note not found: ${relpath}`);
1568
+ process.exit(1);
1569
+ }
1570
+ success(`deleted ${relpath}`);
1571
+ }
1572
+
1349
1573
  // ─── Dispatcher ──────────────────────────────────────────────────────────────
1350
1574
 
1351
1575
  /**
@@ -1372,6 +1596,15 @@ export async function runMemory(subcommand, args) {
1372
1596
  case 'write':
1373
1597
  await cmdWrite(args);
1374
1598
  break;
1599
+ case 'read':
1600
+ await cmdRead(args);
1601
+ break;
1602
+ case 'list':
1603
+ await cmdList(args);
1604
+ break;
1605
+ case 'delete':
1606
+ await cmdDelete(args);
1607
+ break;
1375
1608
  case 'pull':
1376
1609
  await cmdPull(args);
1377
1610
  break;
@@ -1422,6 +1655,9 @@ function showHelp() {
1422
1655
  link <path> Link to a shared memory repo (clone if URL)
1423
1656
  unlink Revert to local-only mode
1424
1657
  write Write a single note to the vault
1658
+ read Read a single note and print it to stdout
1659
+ list List notes (optionally filtered by namespace + dir)
1660
+ delete Delete a single note from the vault
1425
1661
  pull Git pull from the shared repo
1426
1662
  commit [-m] Git commit staged changes
1427
1663
  push Git push to the shared repo
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polderlabs/bizar",
3
- "version": "4.2.0",
3
+ "version": "4.2.1",
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. v4 ships as a single npm package bundling the dashboard server, opencode plugin, and typed SDK.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,7 +21,7 @@
21
21
  "build:sdk": "tsc -p packages/sdk/tsconfig.json",
22
22
  "test:sdk": "npm test --prefix packages/sdk",
23
23
  "test:sdk:watch": "npm run test:watch --prefix packages/sdk",
24
- "test": "npm run typecheck && npm run test:sdk && bun test plugins/bizar/tests/loop.test.ts plugins/bizar/tests/block.test.ts plugins/bizar/tests/stall-think.test.ts plugins/bizar/tests/tools/bg-get-comments.test.ts plugins/bizar/tests/tools/bg-spawn-delegation.test.ts plugins/bizar/tests/tools/opencode-runner.test.ts plugins/bizar/tests/settings.test.ts plugins/bizar/tests/commands.test.ts plugins/bizar/tests/commands-impl.test.ts plugins/bizar/tests/tools/plan-action.test.ts plugins/bizar/tests/tools/wait-for-feedback.test.ts plugins/bizar/tests/tools/read-glyph-feedback.test.ts plugins/bizar/tests/reasoning-clean.test.ts plugins/bizar/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 bizar-dash/tests/dashboard-ports.test.mjs bizar-dash/tests/submit-feedback.test.mjs bizar-dash/tests/yaml.test.mjs bizar-dash/tests/memory-store.test.mjs bizar-dash/tests/memory-schema.test.mjs bizar-dash/tests/memory-secrets.test.mjs bizar-dash/tests/memory-git.test.mjs bizar-dash/tests/memory-sync.test.mjs bizar-dash/tests/obsidian-back-compat.test.mjs bizar-dash/tests/memory-lightrag.test.mjs bizar-dash/tests/memory-config.test.mjs bizar-dash/tests/memory-cli.test.mjs",
24
+ "test": "npm run typecheck && npm run test:sdk && bun test plugins/bizar/tests/loop.test.ts plugins/bizar/tests/block.test.ts plugins/bizar/tests/stall-think.test.ts plugins/bizar/tests/tools/bg-get-comments.test.ts plugins/bizar/tests/tools/bg-spawn-delegation.test.ts plugins/bizar/tests/tools/opencode-runner.test.ts plugins/bizar/tests/settings.test.ts plugins/bizar/tests/commands.test.ts plugins/bizar/tests/commands-impl.test.ts plugins/bizar/tests/tools/plan-action.test.ts plugins/bizar/tests/tools/wait-for-feedback.test.ts plugins/bizar/tests/tools/read-glyph-feedback.test.ts plugins/bizar/tests/reasoning-clean.test.ts plugins/bizar/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 bizar-dash/tests/dashboard-ports.test.mjs bizar-dash/tests/submit-feedback.test.mjs bizar-dash/tests/yaml.test.mjs bizar-dash/tests/memory-store.test.mjs bizar-dash/tests/memory-schema.test.mjs bizar-dash/tests/memory-secrets.test.mjs bizar-dash/tests/memory-git.test.mjs bizar-dash/tests/memory-sync.test.mjs bizar-dash/tests/obsidian-back-compat.test.mjs bizar-dash/tests/memory-lightrag.test.mjs bizar-dash/tests/memory-config.test.mjs bizar-dash/tests/memory-cli.test.mjs bizar-dash/tests/memory-cli-readlistdelete.test.mjs bizar-dash/tests/memory-cli-setup.test.mjs bizar-dash/tests/memory-conflicts.test.mjs bizar-dash/tests/memory-namespace.test.mjs bizar-dash/tests/memory-path-safety.test.mjs bizar-dash/tests/memory-protocol-drift.test.mjs bizar-dash/tests/memory-roundtrip.test.mjs",
25
25
  "build": "npm run build:sdk"
26
26
  },
27
27
  "keywords": [