@noir-ai/cli 1.8.0 → 1.9.0-beta.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/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/tui/index.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
|
|
@@ -1729,66 +2065,6 @@ async function serve(opts) {
|
|
|
1729
2065
|
}
|
|
1730
2066
|
}
|
|
1731
2067
|
|
|
1732
|
-
// src/tui/commands/registry.ts
|
|
1733
|
-
var DESTRUCTIVE_PREFIXES = [
|
|
1734
|
-
["context", "index"],
|
|
1735
|
-
["context", "forget"],
|
|
1736
|
-
["memory", "save"],
|
|
1737
|
-
["memory", "forget"],
|
|
1738
|
-
["memory", "consolidate"],
|
|
1739
|
-
["task", "new"],
|
|
1740
|
-
["task", "advance"],
|
|
1741
|
-
["skills", "sync"],
|
|
1742
|
-
["daemon", "start"],
|
|
1743
|
-
["daemon", "stop"],
|
|
1744
|
-
["daemon", "restart"],
|
|
1745
|
-
["init"],
|
|
1746
|
-
["create"],
|
|
1747
|
-
["sync"],
|
|
1748
|
-
["install"],
|
|
1749
|
-
["update"]
|
|
1750
|
-
];
|
|
1751
|
-
function isDestructive(argv) {
|
|
1752
|
-
return DESTRUCTIVE_PREFIXES.some((prefix) => {
|
|
1753
|
-
if (argv.length < prefix.length) return false;
|
|
1754
|
-
return prefix.every((tok, i) => argv[i] === tok);
|
|
1755
|
-
});
|
|
1756
|
-
}
|
|
1757
|
-
function capitalizeFirst(s) {
|
|
1758
|
-
if (s.length === 0) return s;
|
|
1759
|
-
return `${s[0]?.toUpperCase() ?? ""}${s.slice(1)}`;
|
|
1760
|
-
}
|
|
1761
|
-
function hasAction(cmd) {
|
|
1762
|
-
return cmd._actionHandler != null;
|
|
1763
|
-
}
|
|
1764
|
-
function collectLeaves(cmd, path, out) {
|
|
1765
|
-
const children = cmd.commands;
|
|
1766
|
-
const isLeaf = hasAction(cmd) && children.length === 0;
|
|
1767
|
-
if (isLeaf) {
|
|
1768
|
-
const argv = path;
|
|
1769
|
-
const id = argv.join(" ");
|
|
1770
|
-
const description = cmd.description() || argv.join(" ");
|
|
1771
|
-
out.push({
|
|
1772
|
-
id,
|
|
1773
|
-
label: argv.map(capitalizeFirst).join(": "),
|
|
1774
|
-
argv,
|
|
1775
|
-
description,
|
|
1776
|
-
category: argv[0] ?? "",
|
|
1777
|
-
keywords: argv,
|
|
1778
|
-
destructive: isDestructive(argv)
|
|
1779
|
-
});
|
|
1780
|
-
return;
|
|
1781
|
-
}
|
|
1782
|
-
for (const child of children) {
|
|
1783
|
-
collectLeaves(child, [...path, child.name()], out);
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
function buildPaletteCommands(program2) {
|
|
1787
|
-
const out = [];
|
|
1788
|
-
collectLeaves(program2, [], out);
|
|
1789
|
-
return out;
|
|
1790
|
-
}
|
|
1791
|
-
|
|
1792
2068
|
// src/bin.ts
|
|
1793
2069
|
function buildPaletteCommandsForTui() {
|
|
1794
2070
|
return buildPaletteCommands(createProgram());
|
|
@@ -2228,16 +2504,25 @@ function createProgram() {
|
|
|
2228
2504
|
};
|
|
2229
2505
|
await runTui2(opts, dispatch);
|
|
2230
2506
|
});
|
|
2231
|
-
|
|
2232
|
-
|
|
2507
|
+
program2.command("palette").description("fuzzy command palette \u2014 run any noir command (Ink)").action(async (...args) => {
|
|
2508
|
+
const g = actionGlobals(args);
|
|
2509
|
+
const opts = toCliOptions(g);
|
|
2510
|
+
requireInteractive(opts, "`noir palette`");
|
|
2511
|
+
const tuiUrl = new URL("./tui/index.js", import.meta.url).href;
|
|
2512
|
+
const { runPalette: runPalette2 } = await import(
|
|
2513
|
+
/* @vite-ignore */
|
|
2514
|
+
tuiUrl
|
|
2515
|
+
);
|
|
2516
|
+
const dispatch = async (argv) => {
|
|
2233
2517
|
const sub = createProgram();
|
|
2234
2518
|
try {
|
|
2235
2519
|
await sub.parseAsync([...argv], { from: "user" });
|
|
2236
2520
|
} catch (err) {
|
|
2237
2521
|
handleError(err);
|
|
2238
2522
|
}
|
|
2239
|
-
}
|
|
2240
|
-
|
|
2523
|
+
};
|
|
2524
|
+
await runPalette2(opts, dispatch);
|
|
2525
|
+
});
|
|
2241
2526
|
program2.action(async (...args) => {
|
|
2242
2527
|
const cmd = trailingCmd(args);
|
|
2243
2528
|
const leftovers = cmd.args;
|
|
@@ -2253,6 +2538,20 @@ function createProgram() {
|
|
|
2253
2538
|
return program2;
|
|
2254
2539
|
}
|
|
2255
2540
|
var program = createProgram();
|
|
2541
|
+
var homeDeps = {
|
|
2542
|
+
dispatch: async (argv) => {
|
|
2543
|
+
const sub = createProgram();
|
|
2544
|
+
try {
|
|
2545
|
+
await sub.parseAsync([...argv], { from: "user" });
|
|
2546
|
+
} catch (err) {
|
|
2547
|
+
handleError(err);
|
|
2548
|
+
}
|
|
2549
|
+
},
|
|
2550
|
+
// The grouped home menu resolves its sections against the LIVE palette
|
|
2551
|
+
// registry (home-consolidation S1/S2) — the same source the TUI palette
|
|
2552
|
+
// uses — so the menu cannot drift from the commander tree.
|
|
2553
|
+
commands: buildPaletteCommands(createProgram())
|
|
2554
|
+
};
|
|
2256
2555
|
async function run(argv = []) {
|
|
2257
2556
|
try {
|
|
2258
2557
|
await program.parseAsync([...argv], { from: "user" });
|
|
@@ -2275,8 +2574,8 @@ if (isMainModule) {
|
|
|
2275
2574
|
}
|
|
2276
2575
|
|
|
2277
2576
|
// src/tui/App.tsx
|
|
2278
|
-
import { Box as
|
|
2279
|
-
import { useEffect, useState as
|
|
2577
|
+
import { Box as Box3, Text as Text9, useApp, useInput as useInput3 } from "ink";
|
|
2578
|
+
import { useEffect, useState as useState4 } from "react";
|
|
2280
2579
|
|
|
2281
2580
|
// src/tui/CommandInput.tsx
|
|
2282
2581
|
import { Text } from "ink";
|
|
@@ -2363,10 +2662,86 @@ function Header({ tagline }) {
|
|
|
2363
2662
|
] });
|
|
2364
2663
|
}
|
|
2365
2664
|
|
|
2665
|
+
// src/tui/HomeMenu.tsx
|
|
2666
|
+
import { Box, Text as Text4, useInput } from "ink";
|
|
2667
|
+
import { useState } from "react";
|
|
2668
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2669
|
+
function flatten(sections) {
|
|
2670
|
+
const rows = [];
|
|
2671
|
+
for (const section of sections) {
|
|
2672
|
+
for (const action of section.items) {
|
|
2673
|
+
rows.push({ section, action });
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
return rows;
|
|
2677
|
+
}
|
|
2678
|
+
function HomeMenu({ sections, onSelect, onClose }) {
|
|
2679
|
+
const [active, setActive] = useState(0);
|
|
2680
|
+
const rows = flatten(sections);
|
|
2681
|
+
const activeRow = rows[Math.min(Math.max(active, 0), Math.max(0, rows.length - 1))];
|
|
2682
|
+
useInput((input, key) => {
|
|
2683
|
+
if (key.escape || key.tab) {
|
|
2684
|
+
onClose();
|
|
2685
|
+
return;
|
|
2686
|
+
}
|
|
2687
|
+
if (key.return) {
|
|
2688
|
+
if (activeRow) onSelect(activeRow.action);
|
|
2689
|
+
return;
|
|
2690
|
+
}
|
|
2691
|
+
if (key.upArrow) {
|
|
2692
|
+
setActive((i) => Math.max(0, i - 1));
|
|
2693
|
+
return;
|
|
2694
|
+
}
|
|
2695
|
+
if (key.downArrow) {
|
|
2696
|
+
setActive((i) => Math.min(rows.length - 1, i + 1));
|
|
2697
|
+
return;
|
|
2698
|
+
}
|
|
2699
|
+
if (input === "k") {
|
|
2700
|
+
setActive((i) => Math.max(0, i - 1));
|
|
2701
|
+
return;
|
|
2702
|
+
}
|
|
2703
|
+
if (input === "j") {
|
|
2704
|
+
setActive((i) => Math.min(rows.length - 1, i + 1));
|
|
2705
|
+
return;
|
|
2706
|
+
}
|
|
2707
|
+
});
|
|
2708
|
+
const rowsEl = [];
|
|
2709
|
+
let lastSectionId = null;
|
|
2710
|
+
for (let i = 0; i < rows.length; i++) {
|
|
2711
|
+
const row = rows[i];
|
|
2712
|
+
if (!row) continue;
|
|
2713
|
+
const { section, action } = row;
|
|
2714
|
+
if (section.id !== lastSectionId) {
|
|
2715
|
+
rowsEl.push(/* @__PURE__ */ jsx4(Text4, { children: c.dim(`\u2500\u2500 ${section.label} \u2500\u2500`) }, `sec:${section.id}`));
|
|
2716
|
+
lastSectionId = section.id;
|
|
2717
|
+
}
|
|
2718
|
+
const isActive = i === activeRowIndex(active, rows);
|
|
2719
|
+
const prefix = isActive ? "\u25B8 " : " ";
|
|
2720
|
+
rowsEl.push(
|
|
2721
|
+
/* @__PURE__ */ jsxs3(Text4, { children: [
|
|
2722
|
+
prefix,
|
|
2723
|
+
isActive ? c.accent(action.label) : action.label,
|
|
2724
|
+
/* @__PURE__ */ jsx4(Text4, { children: c.dim(` ${action.hint}`) })
|
|
2725
|
+
] }, action.id)
|
|
2726
|
+
);
|
|
2727
|
+
}
|
|
2728
|
+
return /* @__PURE__ */ jsxs3(Box, { flexDirection: "column", children: [
|
|
2729
|
+
/* @__PURE__ */ jsxs3(Text4, { children: [
|
|
2730
|
+
c.bold("\u25B8 home "),
|
|
2731
|
+
/* @__PURE__ */ jsx4(Text4, { children: c.dim(`quick actions (${rows.length})`) })
|
|
2732
|
+
] }),
|
|
2733
|
+
rowsEl,
|
|
2734
|
+
/* @__PURE__ */ jsx4(Text4, { children: c.dim("\u2191/\u2193 navigate \xB7 Enter run \xB7 Esc close") })
|
|
2735
|
+
] });
|
|
2736
|
+
}
|
|
2737
|
+
function activeRowIndex(active, rows) {
|
|
2738
|
+
return Math.min(Math.max(active, 0), Math.max(0, rows.length - 1));
|
|
2739
|
+
}
|
|
2740
|
+
|
|
2366
2741
|
// src/tui/hooks/useInputBuffer.ts
|
|
2367
|
-
import { useRef, useState } from "react";
|
|
2742
|
+
import { useRef, useState as useState2 } from "react";
|
|
2368
2743
|
function useInputBuffer() {
|
|
2369
|
-
const [buffer, setBuffer] =
|
|
2744
|
+
const [buffer, setBuffer] = useState2("");
|
|
2370
2745
|
const historyRef = useRef([]);
|
|
2371
2746
|
const cursorRef = useRef(-1);
|
|
2372
2747
|
function pushHistory(text) {
|
|
@@ -2395,8 +2770,8 @@ function useInputBuffer() {
|
|
|
2395
2770
|
}
|
|
2396
2771
|
|
|
2397
2772
|
// src/tui/OutputPane.tsx
|
|
2398
|
-
import { Text as
|
|
2399
|
-
import { Fragment, jsx as
|
|
2773
|
+
import { Text as Text5 } from "ink";
|
|
2774
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2400
2775
|
function toRows(visible, baseIndex) {
|
|
2401
2776
|
const rows = [];
|
|
2402
2777
|
const seen = /* @__PURE__ */ new Map();
|
|
@@ -2430,13 +2805,13 @@ function OutputPane({
|
|
|
2430
2805
|
}) {
|
|
2431
2806
|
const width = terminalWidth();
|
|
2432
2807
|
if (lines.length === 0) {
|
|
2433
|
-
return /* @__PURE__ */
|
|
2808
|
+
return /* @__PURE__ */ jsx5(Text5, { children: c.dim("(no output \u2014 type a /command, or ? for help)") });
|
|
2434
2809
|
}
|
|
2435
2810
|
const maxOffset = Math.max(0, lines.length - height);
|
|
2436
2811
|
const offset = Math.min(Math.max(0, scrollOffset), maxOffset);
|
|
2437
2812
|
const rows = toRows(lines.slice(offset, offset + height), offset);
|
|
2438
|
-
return /* @__PURE__ */
|
|
2439
|
-
title !== void 0 ? /* @__PURE__ */
|
|
2813
|
+
return /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
2814
|
+
title !== void 0 ? /* @__PURE__ */ jsx5(Text5, { children: c.dim(`\u2500\u2500 ${title} \u2500\u2500`) }) : null,
|
|
2440
2815
|
rows.map((row) => {
|
|
2441
2816
|
const truncated = row.text.length > width ? `${row.text.slice(0, Math.max(1, width - 1))}\u2026` : row.text;
|
|
2442
2817
|
let body = truncated;
|
|
@@ -2445,18 +2820,18 @@ function OutputPane({
|
|
|
2445
2820
|
} else if (highlightQuery !== void 0 && highlightQuery.length > 0) {
|
|
2446
2821
|
body = highlightFirst(truncated, highlightQuery);
|
|
2447
2822
|
}
|
|
2448
|
-
return /* @__PURE__ */
|
|
2823
|
+
return /* @__PURE__ */ jsx5(Text5, { children: body }, row.key);
|
|
2449
2824
|
})
|
|
2450
2825
|
] });
|
|
2451
2826
|
}
|
|
2452
2827
|
|
|
2453
2828
|
// src/tui/overlays/ConfirmOverlay.tsx
|
|
2454
|
-
import { Text as
|
|
2455
|
-
import { jsx as
|
|
2829
|
+
import { Text as Text6 } from "ink";
|
|
2830
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2456
2831
|
function ConfirmOverlay({ argv }) {
|
|
2457
|
-
return /* @__PURE__ */
|
|
2832
|
+
return /* @__PURE__ */ jsxs5(Text6, { children: [
|
|
2458
2833
|
c.warn(`! run /${argv.join(" ")}? (y/N)`),
|
|
2459
|
-
/* @__PURE__ */
|
|
2834
|
+
/* @__PURE__ */ jsx6(Text6, { children: c.dim(" [y approve \xB7 n/Esc back to palette]") })
|
|
2460
2835
|
] });
|
|
2461
2836
|
}
|
|
2462
2837
|
|
|
@@ -2564,9 +2939,9 @@ var handRolledMatcher = {
|
|
|
2564
2939
|
};
|
|
2565
2940
|
|
|
2566
2941
|
// src/tui/palette/Palette.tsx
|
|
2567
|
-
import { Box, Text as
|
|
2568
|
-
import { useState as
|
|
2569
|
-
import { Fragment as Fragment2, jsx as
|
|
2942
|
+
import { Box as Box2, Text as Text7, useInput as useInput2 } from "ink";
|
|
2943
|
+
import { useState as useState3 } from "react";
|
|
2944
|
+
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2570
2945
|
var VISIBLE_ROWS = 10;
|
|
2571
2946
|
var MATCH_LIMIT = 20;
|
|
2572
2947
|
function rankPalette(query, matcher, commands, recent) {
|
|
@@ -2589,9 +2964,9 @@ function highlight(label, matchedIndices) {
|
|
|
2589
2964
|
const chars = [];
|
|
2590
2965
|
for (let i = 0; i < label.length; i++) {
|
|
2591
2966
|
const ch = label[i] ?? "";
|
|
2592
|
-
chars.push(set.has(i) ? /* @__PURE__ */
|
|
2967
|
+
chars.push(set.has(i) ? /* @__PURE__ */ jsx7(Text7, { children: c.accent(ch) }, i) : /* @__PURE__ */ jsx7(Text7, { children: ch }, i));
|
|
2593
2968
|
}
|
|
2594
|
-
return /* @__PURE__ */
|
|
2969
|
+
return /* @__PURE__ */ jsx7(Fragment2, { children: chars });
|
|
2595
2970
|
}
|
|
2596
2971
|
function Palette({
|
|
2597
2972
|
commands,
|
|
@@ -2600,13 +2975,13 @@ function Palette({
|
|
|
2600
2975
|
onSelect,
|
|
2601
2976
|
onClose
|
|
2602
2977
|
}) {
|
|
2603
|
-
const [query, setQuery] =
|
|
2604
|
-
const [activeIndex, setActiveIndex] =
|
|
2978
|
+
const [query, setQuery] = useState3("");
|
|
2979
|
+
const [activeIndex, setActiveIndex] = useState3(0);
|
|
2605
2980
|
const ranked = rankPalette(query, matcher, commands, recent);
|
|
2606
2981
|
const visible = ranked.slice(0, VISIBLE_ROWS);
|
|
2607
2982
|
const active = Math.min(Math.max(activeIndex, 0), Math.max(0, visible.length - 1));
|
|
2608
2983
|
const recents = recentDeduped(recent, commands);
|
|
2609
|
-
|
|
2984
|
+
useInput2((input, key) => {
|
|
2610
2985
|
if (key.escape) {
|
|
2611
2986
|
onClose();
|
|
2612
2987
|
return;
|
|
@@ -2636,14 +3011,14 @@ function Palette({
|
|
|
2636
3011
|
}
|
|
2637
3012
|
});
|
|
2638
3013
|
const rows = [
|
|
2639
|
-
/* @__PURE__ */
|
|
3014
|
+
/* @__PURE__ */ jsxs6(Text7, { children: [
|
|
2640
3015
|
c.bold("\u25B8 palette "),
|
|
2641
|
-
/* @__PURE__ */
|
|
3016
|
+
/* @__PURE__ */ jsx7(Text7, { children: c.dim(`search (${visible.length} shown)`) })
|
|
2642
3017
|
] }, "header"),
|
|
2643
|
-
/* @__PURE__ */
|
|
3018
|
+
/* @__PURE__ */ jsxs6(Text7, { children: [
|
|
2644
3019
|
c.dim("> "),
|
|
2645
3020
|
query,
|
|
2646
|
-
/* @__PURE__ */
|
|
3021
|
+
/* @__PURE__ */ jsx7(Text7, { children: c.dim("\u258C") })
|
|
2647
3022
|
] }, "query")
|
|
2648
3023
|
];
|
|
2649
3024
|
let lastCategory = null;
|
|
@@ -2651,25 +3026,25 @@ function Palette({
|
|
|
2651
3026
|
const isRecent = recents.some((r) => r.id === cmd.id);
|
|
2652
3027
|
if (query.length === 0) {
|
|
2653
3028
|
if (isRecent && lastCategory !== "recent") {
|
|
2654
|
-
rows.push(/* @__PURE__ */
|
|
3029
|
+
rows.push(/* @__PURE__ */ jsx7(Text7, { children: c.dim("\u2500\u2500 recent \u2500\u2500") }, "cat:recent"));
|
|
2655
3030
|
lastCategory = "recent";
|
|
2656
3031
|
} else if (!isRecent && cmd.category !== lastCategory) {
|
|
2657
|
-
rows.push(/* @__PURE__ */
|
|
3032
|
+
rows.push(/* @__PURE__ */ jsx7(Text7, { children: c.dim(`\u2500\u2500 ${cmd.category} \u2500\u2500`) }, `cat:${cmd.category}`));
|
|
2658
3033
|
lastCategory = cmd.category;
|
|
2659
3034
|
}
|
|
2660
3035
|
}
|
|
2661
3036
|
const prefix = cmd.id === visible[active]?.cmd.id ? "\u25B8 " : " ";
|
|
2662
3037
|
rows.push(
|
|
2663
|
-
/* @__PURE__ */
|
|
2664
|
-
isRecent && query.length === 0 ? /* @__PURE__ */
|
|
3038
|
+
/* @__PURE__ */ jsxs6(Text7, { children: [
|
|
3039
|
+
isRecent && query.length === 0 ? /* @__PURE__ */ jsx7(Text7, { children: c.dim("\u21BA ") }) : null,
|
|
2665
3040
|
prefix,
|
|
2666
3041
|
highlight(cmd.label, indices),
|
|
2667
|
-
/* @__PURE__ */
|
|
3042
|
+
/* @__PURE__ */ jsx7(Text7, { children: c.dim(` ${cmd.description}`) })
|
|
2668
3043
|
] }, cmd.id)
|
|
2669
3044
|
);
|
|
2670
3045
|
}
|
|
2671
|
-
rows.push(/* @__PURE__ */
|
|
2672
|
-
return /* @__PURE__ */
|
|
3046
|
+
rows.push(/* @__PURE__ */ jsx7(Text7, { children: c.dim("\u2191/\u2193 navigate \xB7 Enter run \xB7 Esc close") }, "footer"));
|
|
3047
|
+
return /* @__PURE__ */ jsx7(Box2, { flexDirection: "column", children: rows.map((row) => row) });
|
|
2673
3048
|
}
|
|
2674
3049
|
function recentDeduped(recent, commands) {
|
|
2675
3050
|
const known = new Set(commands.map((cmd) => cmd.id));
|
|
@@ -2677,17 +3052,17 @@ function recentDeduped(recent, commands) {
|
|
|
2677
3052
|
}
|
|
2678
3053
|
|
|
2679
3054
|
// src/tui/StatusBar.tsx
|
|
2680
|
-
import { Text as
|
|
2681
|
-
import { jsx as
|
|
3055
|
+
import { Text as Text8 } from "ink";
|
|
3056
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2682
3057
|
function Cell({
|
|
2683
3058
|
label,
|
|
2684
3059
|
value,
|
|
2685
3060
|
accent
|
|
2686
3061
|
}) {
|
|
2687
|
-
return /* @__PURE__ */
|
|
2688
|
-
/* @__PURE__ */
|
|
3062
|
+
return /* @__PURE__ */ jsxs7(Text8, { children: [
|
|
3063
|
+
/* @__PURE__ */ jsx8(Text8, { children: c.dim(`${label}:`) }),
|
|
2689
3064
|
" ",
|
|
2690
|
-
/* @__PURE__ */
|
|
3065
|
+
/* @__PURE__ */ jsx8(Text8, { children: accent ? c.accent(value) : value })
|
|
2691
3066
|
] });
|
|
2692
3067
|
}
|
|
2693
3068
|
function StatusBar({ payload, loading }) {
|
|
@@ -2696,14 +3071,14 @@ function StatusBar({ payload, loading }) {
|
|
|
2696
3071
|
const phase = payload?.workflow?.phase ?? (loading ? "\u2026" : "idle");
|
|
2697
3072
|
const daemonRunning = payload?.daemon.running === true;
|
|
2698
3073
|
const daemon = payload === null && loading ? "\u2026" : daemonRunning ? "up" : "down";
|
|
2699
|
-
return /* @__PURE__ */
|
|
2700
|
-
/* @__PURE__ */
|
|
2701
|
-
/* @__PURE__ */
|
|
2702
|
-
/* @__PURE__ */
|
|
2703
|
-
/* @__PURE__ */
|
|
2704
|
-
/* @__PURE__ */
|
|
2705
|
-
/* @__PURE__ */
|
|
2706
|
-
/* @__PURE__ */
|
|
3074
|
+
return /* @__PURE__ */ jsxs7(Text8, { children: [
|
|
3075
|
+
/* @__PURE__ */ jsx8(Cell, { label: "host", value: host, accent: true }),
|
|
3076
|
+
/* @__PURE__ */ jsx8(Text8, { children: c.dim(" \xB7 ") }),
|
|
3077
|
+
/* @__PURE__ */ jsx8(Cell, { label: "mode", value: mode }),
|
|
3078
|
+
/* @__PURE__ */ jsx8(Text8, { children: c.dim(" \xB7 ") }),
|
|
3079
|
+
/* @__PURE__ */ jsx8(Cell, { label: "phase", value: phase }),
|
|
3080
|
+
/* @__PURE__ */ jsx8(Text8, { children: c.dim(" \xB7 ") }),
|
|
3081
|
+
/* @__PURE__ */ jsxs7(Text8, { children: [
|
|
2707
3082
|
c.dim("daemon:"),
|
|
2708
3083
|
" ",
|
|
2709
3084
|
daemonRunning ? c.ok(daemon) : c.warn(daemon)
|
|
@@ -2712,21 +3087,27 @@ function StatusBar({ payload, loading }) {
|
|
|
2712
3087
|
}
|
|
2713
3088
|
|
|
2714
3089
|
// src/tui/App.tsx
|
|
2715
|
-
import { jsx as
|
|
2716
|
-
function App({
|
|
3090
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3091
|
+
function App({
|
|
3092
|
+
deps,
|
|
3093
|
+
initialPayload = null,
|
|
3094
|
+
refreshMs = 5e3,
|
|
3095
|
+
initialMode = { kind: "dashboard" }
|
|
3096
|
+
}) {
|
|
2717
3097
|
const { exit } = useApp();
|
|
2718
3098
|
const { buffer, setBuffer, pushHistory, recall, clear } = useInputBuffer();
|
|
2719
|
-
const [mode, setMode] =
|
|
2720
|
-
const [payload, setPayload] =
|
|
2721
|
-
const [loading, setLoading] =
|
|
2722
|
-
const [output, setOutput] =
|
|
2723
|
-
const [searchLines, setSearchLines] =
|
|
2724
|
-
const [scrollOffset, setScrollOffset] =
|
|
2725
|
-
const [help, setHelp] =
|
|
2726
|
-
const [running, setRunning] =
|
|
2727
|
-
const [notice, setNotice] =
|
|
2728
|
-
const [pending, setPending] =
|
|
2729
|
-
const [recent, setRecent] =
|
|
3099
|
+
const [mode, setMode] = useState4(initialMode);
|
|
3100
|
+
const [payload, setPayload] = useState4(initialPayload);
|
|
3101
|
+
const [loading, setLoading] = useState4(initialPayload === null);
|
|
3102
|
+
const [output, setOutput] = useState4(null);
|
|
3103
|
+
const [searchLines, setSearchLines] = useState4([]);
|
|
3104
|
+
const [scrollOffset, setScrollOffset] = useState4(0);
|
|
3105
|
+
const [help, setHelp] = useState4(false);
|
|
3106
|
+
const [running, setRunning] = useState4(false);
|
|
3107
|
+
const [notice, setNotice] = useState4(null);
|
|
3108
|
+
const [pending, setPending] = useState4(null);
|
|
3109
|
+
const [recent, setRecent] = useState4([]);
|
|
3110
|
+
const [homeSections, setHomeSections] = useState4([]);
|
|
2730
3111
|
useEffect(() => {
|
|
2731
3112
|
if (!deps.loadRecent) return;
|
|
2732
3113
|
const loader = deps.loadRecent;
|
|
@@ -2739,6 +3120,15 @@ function App({ deps, initialPayload = null, refreshMs = 5e3 }) {
|
|
|
2739
3120
|
cancelled = true;
|
|
2740
3121
|
};
|
|
2741
3122
|
}, [deps.loadRecent]);
|
|
3123
|
+
useEffect(() => {
|
|
3124
|
+
let cancelled = false;
|
|
3125
|
+
void resolveSections(deps.commands ?? []).then((sections) => {
|
|
3126
|
+
if (!cancelled) setHomeSections(sections);
|
|
3127
|
+
});
|
|
3128
|
+
return () => {
|
|
3129
|
+
cancelled = true;
|
|
3130
|
+
};
|
|
3131
|
+
}, [deps.commands]);
|
|
2742
3132
|
useEffect(() => {
|
|
2743
3133
|
if (running) return;
|
|
2744
3134
|
let cancelled = false;
|
|
@@ -2856,6 +3246,10 @@ function App({ deps, initialPayload = null, refreshMs = 5e3 }) {
|
|
|
2856
3246
|
setHelp(true);
|
|
2857
3247
|
return;
|
|
2858
3248
|
}
|
|
3249
|
+
if (input === "h") {
|
|
3250
|
+
setMode({ kind: "home" });
|
|
3251
|
+
return;
|
|
3252
|
+
}
|
|
2859
3253
|
}
|
|
2860
3254
|
setBuffer((b) => b + input);
|
|
2861
3255
|
return;
|
|
@@ -2883,6 +3277,16 @@ function App({ deps, initialPayload = null, refreshMs = 5e3 }) {
|
|
|
2883
3277
|
void recordRuns([...cmd.argv]);
|
|
2884
3278
|
setMode({ kind: "dashboard" });
|
|
2885
3279
|
}
|
|
3280
|
+
function handleHomeSelect(action) {
|
|
3281
|
+
const argv = action.dispatch ?? [action.id];
|
|
3282
|
+
if (action.destructive) {
|
|
3283
|
+
setMode({ kind: "confirm", argv: [...argv] });
|
|
3284
|
+
return;
|
|
3285
|
+
}
|
|
3286
|
+
dispatchCmd([...argv]);
|
|
3287
|
+
void recordRuns([...argv]);
|
|
3288
|
+
setMode({ kind: "dashboard" });
|
|
3289
|
+
}
|
|
2886
3290
|
function handleConfirmApprove() {
|
|
2887
3291
|
if (mode.kind !== "confirm") return;
|
|
2888
3292
|
dispatchCmd([...mode.argv]);
|
|
@@ -2945,7 +3349,7 @@ function App({ deps, initialPayload = null, refreshMs = 5e3 }) {
|
|
|
2945
3349
|
const next = (mode.active + delta + n) % n;
|
|
2946
3350
|
setMode({ ...mode, active: next });
|
|
2947
3351
|
}
|
|
2948
|
-
|
|
3352
|
+
useInput3((input, key) => {
|
|
2949
3353
|
switch (mode.kind) {
|
|
2950
3354
|
case "dashboard":
|
|
2951
3355
|
handleDashboardInput(input, key);
|
|
@@ -2953,6 +3357,9 @@ function App({ deps, initialPayload = null, refreshMs = 5e3 }) {
|
|
|
2953
3357
|
case "palette":
|
|
2954
3358
|
return;
|
|
2955
3359
|
// Palette renders its own useInput; no App-level keys here
|
|
3360
|
+
case "home":
|
|
3361
|
+
return;
|
|
3362
|
+
// HomeMenu renders its own useInput (S4)
|
|
2956
3363
|
case "confirm":
|
|
2957
3364
|
handleConfirmInput(input, key);
|
|
2958
3365
|
return;
|
|
@@ -2979,12 +3386,12 @@ function App({ deps, initialPayload = null, refreshMs = 5e3 }) {
|
|
|
2979
3386
|
void recordRuns(argv);
|
|
2980
3387
|
}
|
|
2981
3388
|
if (help) {
|
|
2982
|
-
return /* @__PURE__ */
|
|
3389
|
+
return /* @__PURE__ */ jsx9(Help, {});
|
|
2983
3390
|
}
|
|
2984
3391
|
if (mode.kind === "palette") {
|
|
2985
|
-
return /* @__PURE__ */
|
|
2986
|
-
/* @__PURE__ */
|
|
2987
|
-
/* @__PURE__ */
|
|
3392
|
+
return /* @__PURE__ */ jsxs8(Box3, { flexDirection: "column", children: [
|
|
3393
|
+
/* @__PURE__ */ jsx9(Header, { tagline: "palette" }),
|
|
3394
|
+
/* @__PURE__ */ jsx9(
|
|
2988
3395
|
Palette,
|
|
2989
3396
|
{
|
|
2990
3397
|
commands: deps.commands ?? [],
|
|
@@ -2994,22 +3401,36 @@ function App({ deps, initialPayload = null, refreshMs = 5e3 }) {
|
|
|
2994
3401
|
onClose: () => setMode({ kind: "dashboard" })
|
|
2995
3402
|
}
|
|
2996
3403
|
),
|
|
2997
|
-
/* @__PURE__ */
|
|
3404
|
+
/* @__PURE__ */ jsx9(Footer, { running: false })
|
|
3405
|
+
] });
|
|
3406
|
+
}
|
|
3407
|
+
if (mode.kind === "home") {
|
|
3408
|
+
return /* @__PURE__ */ jsxs8(Box3, { flexDirection: "column", children: [
|
|
3409
|
+
/* @__PURE__ */ jsx9(Header, { tagline: "home" }),
|
|
3410
|
+
/* @__PURE__ */ jsx9(
|
|
3411
|
+
HomeMenu,
|
|
3412
|
+
{
|
|
3413
|
+
sections: homeSections,
|
|
3414
|
+
onSelect: handleHomeSelect,
|
|
3415
|
+
onClose: () => setMode({ kind: "dashboard" })
|
|
3416
|
+
}
|
|
3417
|
+
),
|
|
3418
|
+
/* @__PURE__ */ jsx9(Footer, { running: false })
|
|
2998
3419
|
] });
|
|
2999
3420
|
}
|
|
3000
3421
|
if (mode.kind === "confirm") {
|
|
3001
|
-
return /* @__PURE__ */
|
|
3002
|
-
/* @__PURE__ */
|
|
3003
|
-
/* @__PURE__ */
|
|
3004
|
-
/* @__PURE__ */
|
|
3422
|
+
return /* @__PURE__ */ jsxs8(Box3, { flexDirection: "column", children: [
|
|
3423
|
+
/* @__PURE__ */ jsx9(Header, { tagline: "confirm" }),
|
|
3424
|
+
/* @__PURE__ */ jsx9(ConfirmOverlay, { argv: mode.argv }),
|
|
3425
|
+
/* @__PURE__ */ jsx9(Footer, { running: false })
|
|
3005
3426
|
] });
|
|
3006
3427
|
}
|
|
3007
3428
|
if (mode.kind === "search") {
|
|
3008
3429
|
const activeLine = mode.active >= 0 ? mode.matches[mode.active] : void 0;
|
|
3009
3430
|
const matchCount = mode.matches.length;
|
|
3010
|
-
return /* @__PURE__ */
|
|
3011
|
-
/* @__PURE__ */
|
|
3012
|
-
/* @__PURE__ */
|
|
3431
|
+
return /* @__PURE__ */ jsxs8(Box3, { flexDirection: "column", children: [
|
|
3432
|
+
/* @__PURE__ */ jsx9(Header, { tagline: "search" }),
|
|
3433
|
+
/* @__PURE__ */ jsx9(
|
|
3013
3434
|
OutputPane,
|
|
3014
3435
|
{
|
|
3015
3436
|
lines: searchLines,
|
|
@@ -3019,55 +3440,60 @@ function App({ deps, initialPayload = null, refreshMs = 5e3 }) {
|
|
|
3019
3440
|
activeLine
|
|
3020
3441
|
}
|
|
3021
3442
|
),
|
|
3022
|
-
/* @__PURE__ */
|
|
3443
|
+
/* @__PURE__ */ jsxs8(Text9, { children: [
|
|
3023
3444
|
c.dim("search: "),
|
|
3024
3445
|
mode.query.length > 0 ? mode.query : c.dim("(type to filter output)"),
|
|
3025
|
-
/* @__PURE__ */
|
|
3446
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(" \u258C") })
|
|
3026
3447
|
] }),
|
|
3027
|
-
/* @__PURE__ */
|
|
3448
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(
|
|
3028
3449
|
matchCount === 0 ? "no matches \xB7 Esc exit" : `${mode.active + 1}/${matchCount} matches \xB7 n/Enter next \xB7 N prev \xB7 Esc exit`
|
|
3029
3450
|
) }),
|
|
3030
|
-
/* @__PURE__ */
|
|
3451
|
+
/* @__PURE__ */ jsx9(Footer, { running: false })
|
|
3031
3452
|
] });
|
|
3032
3453
|
}
|
|
3033
3454
|
const snapshotLines = formatStatusPayload(payload);
|
|
3034
3455
|
const paneLines = output?.lines ?? snapshotLines ?? [];
|
|
3035
3456
|
const paneTitle = output?.title ?? "snapshot";
|
|
3036
|
-
return /* @__PURE__ */
|
|
3037
|
-
/* @__PURE__ */
|
|
3038
|
-
/* @__PURE__ */
|
|
3039
|
-
/* @__PURE__ */
|
|
3040
|
-
/* @__PURE__ */
|
|
3041
|
-
notice !== null ? /* @__PURE__ */
|
|
3042
|
-
/* @__PURE__ */
|
|
3457
|
+
return /* @__PURE__ */ jsxs8(Box3, { flexDirection: "column", children: [
|
|
3458
|
+
/* @__PURE__ */ jsx9(Header, { tagline: "dashboard" }),
|
|
3459
|
+
/* @__PURE__ */ jsx9(StatusBar, { payload, loading }),
|
|
3460
|
+
/* @__PURE__ */ jsx9(OutputPane, { lines: paneLines, scrollOffset, title: paneTitle }),
|
|
3461
|
+
/* @__PURE__ */ jsx9(CommandInput, { buffer, running }),
|
|
3462
|
+
notice !== null ? /* @__PURE__ */ jsx9(Text9, { children: c.dim(notice) }) : null,
|
|
3463
|
+
/* @__PURE__ */ jsx9(Footer, { running })
|
|
3043
3464
|
] });
|
|
3044
3465
|
}
|
|
3045
3466
|
function Help() {
|
|
3046
|
-
return /* @__PURE__ */
|
|
3047
|
-
/* @__PURE__ */
|
|
3048
|
-
/* @__PURE__ */
|
|
3049
|
-
/* @__PURE__ */
|
|
3050
|
-
/* @__PURE__ */
|
|
3051
|
-
/* @__PURE__ */
|
|
3052
|
-
/* @__PURE__ */
|
|
3053
|
-
/* @__PURE__ */
|
|
3054
|
-
/* @__PURE__ */
|
|
3055
|
-
/* @__PURE__ */
|
|
3056
|
-
/* @__PURE__ */
|
|
3057
|
-
/* @__PURE__ */
|
|
3058
|
-
/* @__PURE__ */
|
|
3059
|
-
/* @__PURE__ */
|
|
3060
|
-
/* @__PURE__ */
|
|
3061
|
-
/* @__PURE__ */
|
|
3062
|
-
/* @__PURE__ */
|
|
3063
|
-
/* @__PURE__ */
|
|
3064
|
-
/* @__PURE__ */
|
|
3467
|
+
return /* @__PURE__ */ jsxs8(Box3, { flexDirection: "column", children: [
|
|
3468
|
+
/* @__PURE__ */ jsx9(Header, { tagline: "help" }),
|
|
3469
|
+
/* @__PURE__ */ jsx9(Text9, { children: " " }),
|
|
3470
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.bold("Keybindings") }),
|
|
3471
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(" /<command> run a Noir sub-command (e.g. /status, /sync, /task next)") }),
|
|
3472
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(" Enter run the typed /command") }),
|
|
3473
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(" Esc back: clear input \u2192 dismiss output \u2192 quit") }),
|
|
3474
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(" q quit (when the input is empty)") }),
|
|
3475
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(" h open the curated home quick-actions (home menu)") }),
|
|
3476
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(" \u2191 / \u2193 scroll the output pane") }),
|
|
3477
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(" ? toggle this help") }),
|
|
3478
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(" Ctrl+K open the command palette") }),
|
|
3479
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(" Ctrl+F find in the dispatched output pane") }),
|
|
3480
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(" n / N next / previous match in search (Enter = next)") }),
|
|
3481
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(" y / n approve / decline a destructive command prompt") }),
|
|
3482
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(" Ctrl+C force exit") }),
|
|
3483
|
+
/* @__PURE__ */ jsx9(Text9, { children: " " }),
|
|
3484
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.bold("Home menu (h)") }),
|
|
3485
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(" The curated quick actions mirror the bare-`noir` home menu:") }),
|
|
3486
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(" Status & context \xB7 Memory \xB7 Workflow \xB7 Setup & maintenance \xB7 Dashboard") }),
|
|
3487
|
+
/* @__PURE__ */ jsx9(Text9, { children: " " }),
|
|
3488
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.bold("Commands") }),
|
|
3489
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(" Dispatched through the same routing as `noir` at the prompt.") }),
|
|
3490
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(
|
|
3065
3491
|
" /status /sync /doctor /context search <q> /task next /memory recall <q>"
|
|
3066
3492
|
) }),
|
|
3067
|
-
/* @__PURE__ */
|
|
3068
|
-
/* @__PURE__ */
|
|
3069
|
-
/* @__PURE__ */
|
|
3070
|
-
/* @__PURE__ */
|
|
3493
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(" Commands that need their own interactive prompts (e.g. a /sync with a") }),
|
|
3494
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim(" conflict) are best run directly \u2014 exit first with q.") }),
|
|
3495
|
+
/* @__PURE__ */ jsx9(Text9, { children: " " }),
|
|
3496
|
+
/* @__PURE__ */ jsx9(Text9, { children: c.dim("press ? / Esc / q to close this help") })
|
|
3071
3497
|
] });
|
|
3072
3498
|
}
|
|
3073
3499
|
|
|
@@ -3127,7 +3553,7 @@ function recordRecent(projectId, argv) {
|
|
|
3127
3553
|
}
|
|
3128
3554
|
|
|
3129
3555
|
// src/tui/index.tsx
|
|
3130
|
-
import { jsx as
|
|
3556
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
3131
3557
|
function defaultFetchStatus(opts) {
|
|
3132
3558
|
return async () => {
|
|
3133
3559
|
try {
|
|
@@ -3138,6 +3564,16 @@ function defaultFetchStatus(opts) {
|
|
|
3138
3564
|
};
|
|
3139
3565
|
}
|
|
3140
3566
|
async function runTui(opts, dispatch) {
|
|
3567
|
+
const deps = await buildTuiDeps(opts, dispatch);
|
|
3568
|
+
const instance = render(/* @__PURE__ */ jsx10(App, { deps }));
|
|
3569
|
+
await instance.waitUntilExit();
|
|
3570
|
+
}
|
|
3571
|
+
async function runPalette(opts, dispatch) {
|
|
3572
|
+
const deps = await buildTuiDeps(opts, dispatch);
|
|
3573
|
+
const instance = render(/* @__PURE__ */ jsx10(App, { deps, initialMode: { kind: "palette" } }));
|
|
3574
|
+
await instance.waitUntilExit();
|
|
3575
|
+
}
|
|
3576
|
+
async function buildTuiDeps(opts, dispatch) {
|
|
3141
3577
|
let projectId = null;
|
|
3142
3578
|
try {
|
|
3143
3579
|
projectId = loadProjectInfo8(process.cwd()).id;
|
|
@@ -3161,10 +3597,10 @@ async function runTui(opts, dispatch) {
|
|
|
3161
3597
|
return loadRecent(projectId).map((e) => byId.get(e.id)).filter((c2) => c2 !== void 0);
|
|
3162
3598
|
}
|
|
3163
3599
|
};
|
|
3164
|
-
|
|
3165
|
-
await instance.waitUntilExit();
|
|
3600
|
+
return deps;
|
|
3166
3601
|
}
|
|
3167
3602
|
export {
|
|
3603
|
+
runPalette,
|
|
3168
3604
|
runTui
|
|
3169
3605
|
};
|
|
3170
3606
|
//# sourceMappingURL=index.js.map
|