@noir-ai/cli 1.8.0 → 1.9.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/dist/bin.js +420 -121
- package/dist/bin.js.map +1 -1
- package/dist/tui/index.js +666 -230
- package/dist/tui/index.js.map +1 -1
- package/package.json +11 -11
package/dist/bin.js
CHANGED
|
@@ -1181,6 +1181,264 @@ function shouldShowBanner(opts) {
|
|
|
1181
1181
|
return true;
|
|
1182
1182
|
}
|
|
1183
1183
|
|
|
1184
|
+
// src/tui/commands/registry.ts
|
|
1185
|
+
var DESTRUCTIVE_PREFIXES = [
|
|
1186
|
+
["context", "index"],
|
|
1187
|
+
["context", "forget"],
|
|
1188
|
+
["memory", "save"],
|
|
1189
|
+
["memory", "forget"],
|
|
1190
|
+
["memory", "consolidate"],
|
|
1191
|
+
["task", "new"],
|
|
1192
|
+
["task", "advance"],
|
|
1193
|
+
["skills", "sync"],
|
|
1194
|
+
["daemon", "start"],
|
|
1195
|
+
["daemon", "stop"],
|
|
1196
|
+
["daemon", "restart"],
|
|
1197
|
+
["init"],
|
|
1198
|
+
["create"],
|
|
1199
|
+
["sync"],
|
|
1200
|
+
["install"],
|
|
1201
|
+
["update"]
|
|
1202
|
+
];
|
|
1203
|
+
function isDestructive(argv) {
|
|
1204
|
+
return DESTRUCTIVE_PREFIXES.some((prefix) => {
|
|
1205
|
+
if (argv.length < prefix.length) return false;
|
|
1206
|
+
return prefix.every((tok, i) => argv[i] === tok);
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
function capitalizeFirst(s) {
|
|
1210
|
+
if (s.length === 0) return s;
|
|
1211
|
+
return `${s[0]?.toUpperCase() ?? ""}${s.slice(1)}`;
|
|
1212
|
+
}
|
|
1213
|
+
function hasAction(cmd) {
|
|
1214
|
+
return cmd._actionHandler != null;
|
|
1215
|
+
}
|
|
1216
|
+
function collectLeaves(cmd, path, out) {
|
|
1217
|
+
const children = cmd.commands;
|
|
1218
|
+
const isLeaf = hasAction(cmd) && children.length === 0;
|
|
1219
|
+
if (isLeaf) {
|
|
1220
|
+
const argv = path;
|
|
1221
|
+
const id = argv.join(" ");
|
|
1222
|
+
const description = cmd.description() || argv.join(" ");
|
|
1223
|
+
out.push({
|
|
1224
|
+
id,
|
|
1225
|
+
label: argv.map(capitalizeFirst).join(": "),
|
|
1226
|
+
argv,
|
|
1227
|
+
description,
|
|
1228
|
+
category: argv[0] ?? "",
|
|
1229
|
+
keywords: argv,
|
|
1230
|
+
destructive: isDestructive(argv)
|
|
1231
|
+
});
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
for (const child of children) {
|
|
1235
|
+
collectLeaves(child, [...path, child.name()], out);
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
function buildPaletteCommands(program2) {
|
|
1239
|
+
const out = [];
|
|
1240
|
+
collectLeaves(program2, [], out);
|
|
1241
|
+
return out;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
// src/tui/commands/sections.ts
|
|
1245
|
+
var HOME_SECTIONS = [
|
|
1246
|
+
{
|
|
1247
|
+
id: "status",
|
|
1248
|
+
label: "Status & context",
|
|
1249
|
+
hint: "snapshot \xB7 index \xB7 search \xB7 doctor",
|
|
1250
|
+
key: "1",
|
|
1251
|
+
items: [
|
|
1252
|
+
{
|
|
1253
|
+
id: "status",
|
|
1254
|
+
label: "Status",
|
|
1255
|
+
hint: "project + daemon + workflow + store snapshot (noir status)"
|
|
1256
|
+
},
|
|
1257
|
+
{
|
|
1258
|
+
id: "context index",
|
|
1259
|
+
label: "Index project",
|
|
1260
|
+
hint: "(re)index files into context \xB7 \u26A0 destructive",
|
|
1261
|
+
destructive: true
|
|
1262
|
+
},
|
|
1263
|
+
{
|
|
1264
|
+
id: "context search",
|
|
1265
|
+
label: "Search context",
|
|
1266
|
+
hint: "hybrid search over indexed files",
|
|
1267
|
+
needsArg: { prompt: "Context search query:", placeholder: "e.g. auth flow, deploy steps" }
|
|
1268
|
+
},
|
|
1269
|
+
{
|
|
1270
|
+
id: "doctor",
|
|
1271
|
+
label: "Doctor",
|
|
1272
|
+
hint: "environment + project health checks"
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
id: "context status",
|
|
1276
|
+
label: "Context status",
|
|
1277
|
+
hint: "index freshness + counts"
|
|
1278
|
+
}
|
|
1279
|
+
]
|
|
1280
|
+
},
|
|
1281
|
+
{
|
|
1282
|
+
id: "memory",
|
|
1283
|
+
label: "Memory",
|
|
1284
|
+
hint: "recall \xB7 save \xB7 sessions \xB7 forget \xB7 consolidate",
|
|
1285
|
+
key: "2",
|
|
1286
|
+
items: [
|
|
1287
|
+
{
|
|
1288
|
+
id: "memory recall",
|
|
1289
|
+
label: "Recall memory",
|
|
1290
|
+
hint: "search cross-session memory",
|
|
1291
|
+
needsArg: { prompt: "Recall query:", placeholder: "e.g. auth flow, deploy steps" }
|
|
1292
|
+
},
|
|
1293
|
+
{
|
|
1294
|
+
id: "memory save",
|
|
1295
|
+
label: "Save memory",
|
|
1296
|
+
hint: "save an observation \xB7 \u26A0 destructive",
|
|
1297
|
+
destructive: true,
|
|
1298
|
+
needsArg: { prompt: "Memory content:", placeholder: "what you want to remember" }
|
|
1299
|
+
},
|
|
1300
|
+
{
|
|
1301
|
+
id: "memory sessions",
|
|
1302
|
+
label: "Sessions",
|
|
1303
|
+
hint: "list recent memory sessions"
|
|
1304
|
+
},
|
|
1305
|
+
{
|
|
1306
|
+
id: "memory forget",
|
|
1307
|
+
label: "Forget memory",
|
|
1308
|
+
hint: "forget observations by id \xB7 \u26A0 destructive",
|
|
1309
|
+
destructive: true
|
|
1310
|
+
},
|
|
1311
|
+
{
|
|
1312
|
+
id: "memory consolidate",
|
|
1313
|
+
label: "Consolidate",
|
|
1314
|
+
hint: "fold observations into lessons \xB7 \u26A0 destructive",
|
|
1315
|
+
destructive: true
|
|
1316
|
+
}
|
|
1317
|
+
]
|
|
1318
|
+
},
|
|
1319
|
+
{
|
|
1320
|
+
id: "workflow",
|
|
1321
|
+
label: "Workflow",
|
|
1322
|
+
hint: "next \xB7 status \xB7 advance \xB7 handoff \xB7 wrap",
|
|
1323
|
+
key: "3",
|
|
1324
|
+
items: [
|
|
1325
|
+
{
|
|
1326
|
+
id: "task next",
|
|
1327
|
+
label: "Next task",
|
|
1328
|
+
hint: "suggest next phase + applicable skill"
|
|
1329
|
+
},
|
|
1330
|
+
{
|
|
1331
|
+
id: "task status",
|
|
1332
|
+
label: "Task status",
|
|
1333
|
+
hint: "active task status"
|
|
1334
|
+
},
|
|
1335
|
+
{
|
|
1336
|
+
id: "task advance",
|
|
1337
|
+
label: "Advance task",
|
|
1338
|
+
hint: "advance to next phase \xB7 \u26A0 destructive",
|
|
1339
|
+
destructive: true
|
|
1340
|
+
},
|
|
1341
|
+
{
|
|
1342
|
+
id: "handoff",
|
|
1343
|
+
label: "Handoff",
|
|
1344
|
+
hint: "ready-to-paste host prompt"
|
|
1345
|
+
},
|
|
1346
|
+
{
|
|
1347
|
+
id: "wrap",
|
|
1348
|
+
label: "Wrap (end session)",
|
|
1349
|
+
hint: "session-end handoff",
|
|
1350
|
+
dispatch: ["wrap", "--write"]
|
|
1351
|
+
}
|
|
1352
|
+
]
|
|
1353
|
+
},
|
|
1354
|
+
{
|
|
1355
|
+
id: "setup",
|
|
1356
|
+
label: "Setup & maintenance",
|
|
1357
|
+
hint: "init \xB7 create \xB7 sync \xB7 skills \xB7 install \xB7 update",
|
|
1358
|
+
key: "4",
|
|
1359
|
+
items: [
|
|
1360
|
+
{
|
|
1361
|
+
id: "init",
|
|
1362
|
+
label: "Init",
|
|
1363
|
+
hint: "scaffold Noir in this project \xB7 \u26A0 destructive",
|
|
1364
|
+
destructive: true
|
|
1365
|
+
},
|
|
1366
|
+
{
|
|
1367
|
+
id: "create",
|
|
1368
|
+
label: "Create",
|
|
1369
|
+
hint: "bootstrap in a new/empty dir \xB7 \u26A0 destructive",
|
|
1370
|
+
destructive: true
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
id: "sync",
|
|
1374
|
+
label: "Sync",
|
|
1375
|
+
hint: "re-emit managed files + skills \xB7 \u26A0 destructive",
|
|
1376
|
+
destructive: true
|
|
1377
|
+
},
|
|
1378
|
+
{
|
|
1379
|
+
id: "skills list",
|
|
1380
|
+
label: "Skills list",
|
|
1381
|
+
hint: "list installed skills"
|
|
1382
|
+
},
|
|
1383
|
+
{
|
|
1384
|
+
id: "skills sync",
|
|
1385
|
+
label: "Sync skills",
|
|
1386
|
+
hint: "re-emit builtin skills \xB7 \u26A0 destructive",
|
|
1387
|
+
destructive: true
|
|
1388
|
+
},
|
|
1389
|
+
{
|
|
1390
|
+
id: "update",
|
|
1391
|
+
label: "Update",
|
|
1392
|
+
hint: "check for a new version \xB7 \u26A0 destructive",
|
|
1393
|
+
destructive: true
|
|
1394
|
+
},
|
|
1395
|
+
{
|
|
1396
|
+
id: "install",
|
|
1397
|
+
label: "Install / migrate",
|
|
1398
|
+
hint: "native install / migrate \xB7 \u26A0 destructive",
|
|
1399
|
+
destructive: true
|
|
1400
|
+
}
|
|
1401
|
+
]
|
|
1402
|
+
},
|
|
1403
|
+
{
|
|
1404
|
+
id: "dashboard",
|
|
1405
|
+
label: "Dashboard (full-screen)",
|
|
1406
|
+
hint: "noir tui \xB7 noir palette \xB7 home",
|
|
1407
|
+
key: "5",
|
|
1408
|
+
items: [
|
|
1409
|
+
{
|
|
1410
|
+
id: "tui",
|
|
1411
|
+
label: "Dashboard (full-screen)",
|
|
1412
|
+
hint: "live status \xB7 /command \xB7 Ctrl+K palette (noir tui)"
|
|
1413
|
+
},
|
|
1414
|
+
{
|
|
1415
|
+
id: "palette",
|
|
1416
|
+
label: "All commands (fuzzy palette)",
|
|
1417
|
+
hint: "fuzzy search over every command (noir palette)"
|
|
1418
|
+
}
|
|
1419
|
+
]
|
|
1420
|
+
}
|
|
1421
|
+
];
|
|
1422
|
+
async function resolveSections(commands) {
|
|
1423
|
+
const byId = new Map(commands.map((c2) => [c2.id, c2]));
|
|
1424
|
+
const out = [];
|
|
1425
|
+
for (const section of HOME_SECTIONS) {
|
|
1426
|
+
const items = [];
|
|
1427
|
+
for (const action of section.items) {
|
|
1428
|
+
const entry = byId.get(action.id);
|
|
1429
|
+
if (!entry) continue;
|
|
1430
|
+
items.push({
|
|
1431
|
+
...action,
|
|
1432
|
+
// Resolve the dispatch argv from the registry (never hand-written).
|
|
1433
|
+
dispatch: action.dispatch ?? [...entry.argv]
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1436
|
+
if (items.length === 0) continue;
|
|
1437
|
+
out.push({ ...section, items });
|
|
1438
|
+
}
|
|
1439
|
+
return out;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1184
1442
|
// src/commands/home.ts
|
|
1185
1443
|
function tryProject() {
|
|
1186
1444
|
try {
|
|
@@ -1195,7 +1453,7 @@ function shouldShowMigrationBanner(rec, currentVersion) {
|
|
|
1195
1453
|
if (rec.dismissedVersions?.includes(currentVersion)) return false;
|
|
1196
1454
|
return true;
|
|
1197
1455
|
}
|
|
1198
|
-
var COMMANDS_HINT = "Commands:
|
|
1456
|
+
var COMMANDS_HINT = "Commands: status \xB7 context \xB7 memory \xB7 task \xB7 skills \xB7 daemon \xB7 doctor \xB7 install \xB7 update \xB7 tui \xB7 palette";
|
|
1199
1457
|
async function home(opts, deps) {
|
|
1200
1458
|
if (isInteractive(opts) && opts.tui !== false) {
|
|
1201
1459
|
await runMenu(opts, deps);
|
|
@@ -1207,6 +1465,138 @@ async function home(opts, deps) {
|
|
|
1207
1465
|
}
|
|
1208
1466
|
await deps.dispatch(["status"]);
|
|
1209
1467
|
}
|
|
1468
|
+
async function pickSection(clack, sections, opts) {
|
|
1469
|
+
const result = await clack.selectKey({
|
|
1470
|
+
message: "What would you like to do?",
|
|
1471
|
+
options: [
|
|
1472
|
+
...sections.map((s) => ({
|
|
1473
|
+
value: s.id,
|
|
1474
|
+
label: s.label,
|
|
1475
|
+
hint: s.hint
|
|
1476
|
+
})),
|
|
1477
|
+
{ value: "exit", label: "Exit", hint: "leave the home menu" }
|
|
1478
|
+
]
|
|
1479
|
+
});
|
|
1480
|
+
if (clack.isCancel(result)) {
|
|
1481
|
+
clack.cancel("Cancelled.");
|
|
1482
|
+
fail(EXIT.CANCELLED, "cancelled", opts);
|
|
1483
|
+
}
|
|
1484
|
+
return result;
|
|
1485
|
+
}
|
|
1486
|
+
async function pickAction(clack, section, sections, opts) {
|
|
1487
|
+
const idx = sections.findIndex((s) => s.id === section.id);
|
|
1488
|
+
const hasPrev = idx > 0;
|
|
1489
|
+
const hasNext = idx !== -1 && idx < sections.length - 1;
|
|
1490
|
+
const result = await clack.select({
|
|
1491
|
+
message: section.label,
|
|
1492
|
+
initialValue: section.items[0]?.id,
|
|
1493
|
+
options: [
|
|
1494
|
+
...section.items.map((a) => ({
|
|
1495
|
+
value: a.id,
|
|
1496
|
+
label: a.label,
|
|
1497
|
+
hint: a.hint
|
|
1498
|
+
})),
|
|
1499
|
+
// Navigation options so the user can move across sections without
|
|
1500
|
+
// re-entering the section picker (the "lancar" navigation requirement).
|
|
1501
|
+
...hasPrev ? [{ value: "prev", label: "\u2190 Prev section", hint: sections[idx - 1]?.label }] : [],
|
|
1502
|
+
...hasNext ? [{ value: "next", label: "\u2192 Next section", hint: sections[idx + 1]?.label }] : [],
|
|
1503
|
+
{ value: "back", label: "\u2191 Back to sections", hint: "return to the section picker" }
|
|
1504
|
+
]
|
|
1505
|
+
});
|
|
1506
|
+
if (clack.isCancel(result)) {
|
|
1507
|
+
clack.cancel("Back to the home menu.");
|
|
1508
|
+
return "back";
|
|
1509
|
+
}
|
|
1510
|
+
if (opts.verbose) {
|
|
1511
|
+
process.stderr.write(`noir: home: section '${section.id}' selected
|
|
1512
|
+
`);
|
|
1513
|
+
}
|
|
1514
|
+
return result;
|
|
1515
|
+
}
|
|
1516
|
+
async function collectArg(clack, action, opts) {
|
|
1517
|
+
if (!action.needsArg) return null;
|
|
1518
|
+
const value = await clack.text({
|
|
1519
|
+
message: action.needsArg.prompt,
|
|
1520
|
+
placeholder: action.needsArg.placeholder
|
|
1521
|
+
});
|
|
1522
|
+
if (clack.isCancel(value)) {
|
|
1523
|
+
clack.cancel("Cancelled.");
|
|
1524
|
+
fail(EXIT.CANCELLED, "cancelled", opts);
|
|
1525
|
+
}
|
|
1526
|
+
return String(value);
|
|
1527
|
+
}
|
|
1528
|
+
async function argvForAction(clack, action, opts) {
|
|
1529
|
+
const base = action.dispatch ?? [action.id];
|
|
1530
|
+
let argv = [...base];
|
|
1531
|
+
if (action.needsArg) {
|
|
1532
|
+
const arg = await collectArg(clack, action, opts);
|
|
1533
|
+
if (arg === null) return null;
|
|
1534
|
+
argv = [...base, arg];
|
|
1535
|
+
}
|
|
1536
|
+
if (action.destructive || isDestructive(argv)) {
|
|
1537
|
+
const confirmed = await clack.confirm({
|
|
1538
|
+
message: `Run \`noir ${argv.join(" ")}\`? This may modify project files / the store.`
|
|
1539
|
+
});
|
|
1540
|
+
if (clack.isCancel(confirmed) || !confirmed) return null;
|
|
1541
|
+
}
|
|
1542
|
+
return argv;
|
|
1543
|
+
}
|
|
1544
|
+
async function runGroupedMenu(clack, opts, deps, sections) {
|
|
1545
|
+
let currentSection = null;
|
|
1546
|
+
while (true) {
|
|
1547
|
+
if (currentSection === null) {
|
|
1548
|
+
const picked = await pickSection(clack, sections, opts);
|
|
1549
|
+
if (clack.isCancel(picked) || picked === "exit") {
|
|
1550
|
+
clack.outro("bye");
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1553
|
+
const idx = sections.findIndex((s) => s.id === picked);
|
|
1554
|
+
if (idx === -1) {
|
|
1555
|
+
continue;
|
|
1556
|
+
}
|
|
1557
|
+
currentSection = idx;
|
|
1558
|
+
continue;
|
|
1559
|
+
}
|
|
1560
|
+
const section = sections[currentSection];
|
|
1561
|
+
if (!section) {
|
|
1562
|
+
currentSection = null;
|
|
1563
|
+
continue;
|
|
1564
|
+
}
|
|
1565
|
+
const chosen = await pickAction(clack, section, sections, opts);
|
|
1566
|
+
if (clack.isCancel(chosen)) {
|
|
1567
|
+
currentSection = null;
|
|
1568
|
+
continue;
|
|
1569
|
+
}
|
|
1570
|
+
switch (chosen) {
|
|
1571
|
+
case "back": {
|
|
1572
|
+
currentSection = null;
|
|
1573
|
+
continue;
|
|
1574
|
+
}
|
|
1575
|
+
case "next": {
|
|
1576
|
+
currentSection = (currentSection + 1) % sections.length;
|
|
1577
|
+
continue;
|
|
1578
|
+
}
|
|
1579
|
+
case "prev": {
|
|
1580
|
+
currentSection = (currentSection - 1 + sections.length) % sections.length;
|
|
1581
|
+
continue;
|
|
1582
|
+
}
|
|
1583
|
+
default: {
|
|
1584
|
+
const action = section.items.find((a) => a.id === chosen);
|
|
1585
|
+
if (!action) {
|
|
1586
|
+
currentSection = null;
|
|
1587
|
+
continue;
|
|
1588
|
+
}
|
|
1589
|
+
const argv = await argvForAction(clack, action, opts);
|
|
1590
|
+
if (argv === null) {
|
|
1591
|
+
continue;
|
|
1592
|
+
}
|
|
1593
|
+
await deps.dispatch(argv);
|
|
1594
|
+
clack.outro("done");
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1210
1600
|
async function runMenu(opts, deps) {
|
|
1211
1601
|
const clack = await import("@clack/prompts");
|
|
1212
1602
|
const project = tryProject();
|
|
@@ -1245,62 +1635,8 @@ ${COMMANDS_HINT}
|
|
|
1245
1635
|
);
|
|
1246
1636
|
}
|
|
1247
1637
|
clack.intro(project ? `noir \u2014 ${project.name}` : "noir");
|
|
1248
|
-
const
|
|
1249
|
-
|
|
1250
|
-
initialValue: "status",
|
|
1251
|
-
options: [
|
|
1252
|
-
{ value: "status", label: "Status", hint: "project + daemon + store snapshot" },
|
|
1253
|
-
{ value: "index", label: "Index project", hint: "(re)index files into context" },
|
|
1254
|
-
{ value: "recall", label: "Recall memory", hint: "search cross-session memory" },
|
|
1255
|
-
{ value: "next", label: "Next task", hint: "suggest next phase + skill" },
|
|
1256
|
-
{ value: "handoff", label: "Handoff", hint: "ready-to-paste host prompt" },
|
|
1257
|
-
{ value: "daemon", label: "Start daemon", hint: "foreground daemon" },
|
|
1258
|
-
{ value: "sync", label: "Sync skills", hint: "re-emit builtin skills" },
|
|
1259
|
-
{ value: "exit", label: "Exit" }
|
|
1260
|
-
]
|
|
1261
|
-
});
|
|
1262
|
-
if (clack.isCancel(choice)) {
|
|
1263
|
-
clack.cancel("Cancelled.");
|
|
1264
|
-
fail(EXIT.CANCELLED, "cancelled", opts);
|
|
1265
|
-
}
|
|
1266
|
-
const argv = await argvForChoice(choice, clack, opts);
|
|
1267
|
-
if (argv === null) {
|
|
1268
|
-
clack.outro("bye");
|
|
1269
|
-
return;
|
|
1270
|
-
}
|
|
1271
|
-
await deps.dispatch(argv);
|
|
1272
|
-
clack.outro("done");
|
|
1273
|
-
}
|
|
1274
|
-
async function argvForChoice(choice, clack, opts) {
|
|
1275
|
-
switch (choice) {
|
|
1276
|
-
case "status":
|
|
1277
|
-
return ["status"];
|
|
1278
|
-
case "index":
|
|
1279
|
-
return ["context", "index"];
|
|
1280
|
-
case "next":
|
|
1281
|
-
return ["task", "next"];
|
|
1282
|
-
case "handoff":
|
|
1283
|
-
return ["handoff"];
|
|
1284
|
-
case "daemon":
|
|
1285
|
-
return ["daemon", "start"];
|
|
1286
|
-
case "sync":
|
|
1287
|
-
return ["sync"];
|
|
1288
|
-
case "exit":
|
|
1289
|
-
return null;
|
|
1290
|
-
case "recall": {
|
|
1291
|
-
const query = await clack.text({
|
|
1292
|
-
message: "Recall query:",
|
|
1293
|
-
placeholder: "e.g. auth flow, ContextEngine, deploy steps"
|
|
1294
|
-
});
|
|
1295
|
-
if (clack.isCancel(query)) {
|
|
1296
|
-
clack.cancel("Cancelled.");
|
|
1297
|
-
fail(EXIT.CANCELLED, "cancelled", opts);
|
|
1298
|
-
}
|
|
1299
|
-
return ["memory", "recall", String(query)];
|
|
1300
|
-
}
|
|
1301
|
-
default:
|
|
1302
|
-
return null;
|
|
1303
|
-
}
|
|
1638
|
+
const sections = await resolveSections(deps.commands ?? []);
|
|
1639
|
+
await runGroupedMenu(clack, opts, deps, sections);
|
|
1304
1640
|
}
|
|
1305
1641
|
|
|
1306
1642
|
// src/commands/memory.ts
|
|
@@ -1702,66 +2038,6 @@ async function skillsSync(opts) {
|
|
|
1702
2038
|
}
|
|
1703
2039
|
}
|
|
1704
2040
|
|
|
1705
|
-
// src/tui/commands/registry.ts
|
|
1706
|
-
var DESTRUCTIVE_PREFIXES = [
|
|
1707
|
-
["context", "index"],
|
|
1708
|
-
["context", "forget"],
|
|
1709
|
-
["memory", "save"],
|
|
1710
|
-
["memory", "forget"],
|
|
1711
|
-
["memory", "consolidate"],
|
|
1712
|
-
["task", "new"],
|
|
1713
|
-
["task", "advance"],
|
|
1714
|
-
["skills", "sync"],
|
|
1715
|
-
["daemon", "start"],
|
|
1716
|
-
["daemon", "stop"],
|
|
1717
|
-
["daemon", "restart"],
|
|
1718
|
-
["init"],
|
|
1719
|
-
["create"],
|
|
1720
|
-
["sync"],
|
|
1721
|
-
["install"],
|
|
1722
|
-
["update"]
|
|
1723
|
-
];
|
|
1724
|
-
function isDestructive(argv) {
|
|
1725
|
-
return DESTRUCTIVE_PREFIXES.some((prefix) => {
|
|
1726
|
-
if (argv.length < prefix.length) return false;
|
|
1727
|
-
return prefix.every((tok, i) => argv[i] === tok);
|
|
1728
|
-
});
|
|
1729
|
-
}
|
|
1730
|
-
function capitalizeFirst(s) {
|
|
1731
|
-
if (s.length === 0) return s;
|
|
1732
|
-
return `${s[0]?.toUpperCase() ?? ""}${s.slice(1)}`;
|
|
1733
|
-
}
|
|
1734
|
-
function hasAction(cmd) {
|
|
1735
|
-
return cmd._actionHandler != null;
|
|
1736
|
-
}
|
|
1737
|
-
function collectLeaves(cmd, path, out) {
|
|
1738
|
-
const children = cmd.commands;
|
|
1739
|
-
const isLeaf = hasAction(cmd) && children.length === 0;
|
|
1740
|
-
if (isLeaf) {
|
|
1741
|
-
const argv = path;
|
|
1742
|
-
const id = argv.join(" ");
|
|
1743
|
-
const description = cmd.description() || argv.join(" ");
|
|
1744
|
-
out.push({
|
|
1745
|
-
id,
|
|
1746
|
-
label: argv.map(capitalizeFirst).join(": "),
|
|
1747
|
-
argv,
|
|
1748
|
-
description,
|
|
1749
|
-
category: argv[0] ?? "",
|
|
1750
|
-
keywords: argv,
|
|
1751
|
-
destructive: isDestructive(argv)
|
|
1752
|
-
});
|
|
1753
|
-
return;
|
|
1754
|
-
}
|
|
1755
|
-
for (const child of children) {
|
|
1756
|
-
collectLeaves(child, [...path, child.name()], out);
|
|
1757
|
-
}
|
|
1758
|
-
}
|
|
1759
|
-
function buildPaletteCommands(program2) {
|
|
1760
|
-
const out = [];
|
|
1761
|
-
collectLeaves(program2, [], out);
|
|
1762
|
-
return out;
|
|
1763
|
-
}
|
|
1764
|
-
|
|
1765
2041
|
// src/bin.ts
|
|
1766
2042
|
function buildPaletteCommandsForTui() {
|
|
1767
2043
|
return buildPaletteCommands(createProgram());
|
|
@@ -2201,16 +2477,25 @@ function createProgram() {
|
|
|
2201
2477
|
};
|
|
2202
2478
|
await runTui(opts, dispatch);
|
|
2203
2479
|
});
|
|
2204
|
-
|
|
2205
|
-
|
|
2480
|
+
program2.command("palette").description("fuzzy command palette \u2014 run any noir command (Ink)").action(async (...args) => {
|
|
2481
|
+
const g = actionGlobals(args);
|
|
2482
|
+
const opts = toCliOptions(g);
|
|
2483
|
+
requireInteractive(opts, "`noir palette`");
|
|
2484
|
+
const tuiUrl = new URL("./tui/index.js", import.meta.url).href;
|
|
2485
|
+
const { runPalette } = await import(
|
|
2486
|
+
/* @vite-ignore */
|
|
2487
|
+
tuiUrl
|
|
2488
|
+
);
|
|
2489
|
+
const dispatch = async (argv) => {
|
|
2206
2490
|
const sub = createProgram();
|
|
2207
2491
|
try {
|
|
2208
2492
|
await sub.parseAsync([...argv], { from: "user" });
|
|
2209
2493
|
} catch (err) {
|
|
2210
2494
|
handleError(err);
|
|
2211
2495
|
}
|
|
2212
|
-
}
|
|
2213
|
-
|
|
2496
|
+
};
|
|
2497
|
+
await runPalette(opts, dispatch);
|
|
2498
|
+
});
|
|
2214
2499
|
program2.action(async (...args) => {
|
|
2215
2500
|
const cmd = trailingCmd(args);
|
|
2216
2501
|
const leftovers = cmd.args;
|
|
@@ -2226,6 +2511,20 @@ function createProgram() {
|
|
|
2226
2511
|
return program2;
|
|
2227
2512
|
}
|
|
2228
2513
|
var program = createProgram();
|
|
2514
|
+
var homeDeps = {
|
|
2515
|
+
dispatch: async (argv) => {
|
|
2516
|
+
const sub = createProgram();
|
|
2517
|
+
try {
|
|
2518
|
+
await sub.parseAsync([...argv], { from: "user" });
|
|
2519
|
+
} catch (err) {
|
|
2520
|
+
handleError(err);
|
|
2521
|
+
}
|
|
2522
|
+
},
|
|
2523
|
+
// The grouped home menu resolves its sections against the LIVE palette
|
|
2524
|
+
// registry (home-consolidation S1/S2) — the same source the TUI palette
|
|
2525
|
+
// uses — so the menu cannot drift from the commander tree.
|
|
2526
|
+
commands: buildPaletteCommands(createProgram())
|
|
2527
|
+
};
|
|
2229
2528
|
async function run(argv = []) {
|
|
2230
2529
|
try {
|
|
2231
2530
|
await program.parseAsync([...argv], { from: "user" });
|