@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/bizar-dash/src/server/memory-git.mjs +62 -0
- package/bizar-dash/src/server/memory-store.mjs +82 -10
- package/bizar-dash/tests/memory-cli-readlistdelete.test.mjs +405 -0
- package/bizar-dash/tests/memory-cli-setup.test.mjs +382 -0
- package/bizar-dash/tests/memory-conflicts.test.mjs +229 -0
- package/bizar-dash/tests/memory-namespace.test.mjs +404 -0
- package/bizar-dash/tests/memory-path-safety.test.mjs +427 -0
- package/bizar-dash/tests/memory-roundtrip.test.mjs +219 -0
- package/cli/memory.mjs +242 -6
- package/package.json +2 -2
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
|
-
|
|
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
|
|
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.
|
|
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": [
|