@cryer/star-cli 0.2.1 → 0.2.2
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/{chunk-MJ2O7NWI.js → chunk-GN236FD4.js} +157 -11
- package/dist/chunk-GN236FD4.js.map +1 -0
- package/dist/{loop-R3RQCNFT.js → loop-ADVWZA62.js} +2 -2
- package/dist/main.js +571 -128
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-MJ2O7NWI.js.map +0 -1
- /package/dist/{loop-R3RQCNFT.js.map → loop-ADVWZA62.js.map} +0 -0
package/dist/main.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
SKIP_DIRS,
|
|
5
5
|
TodoStore,
|
|
6
6
|
WORKING_DIFF_MAX_LINES,
|
|
7
|
+
appendUserMemory,
|
|
7
8
|
bashTool,
|
|
8
9
|
buildAllowRule,
|
|
9
10
|
buildCommitPrompt,
|
|
@@ -12,6 +13,7 @@ import {
|
|
|
12
13
|
collectWorkingDiff,
|
|
13
14
|
compactMessages,
|
|
14
15
|
createDefaultRegistry,
|
|
16
|
+
createIgnorePredicate,
|
|
15
17
|
defaultTaskManager,
|
|
16
18
|
discoverSkills,
|
|
17
19
|
estimateTokens,
|
|
@@ -34,8 +36,9 @@ import {
|
|
|
34
36
|
sessionsDir,
|
|
35
37
|
starHome,
|
|
36
38
|
summarizeMessages,
|
|
37
|
-
undoTurnSnapshots
|
|
38
|
-
|
|
39
|
+
undoTurnSnapshots,
|
|
40
|
+
userMemoryPath
|
|
41
|
+
} from "./chunk-GN236FD4.js";
|
|
39
42
|
|
|
40
43
|
// src/main.tsx
|
|
41
44
|
import { Command } from "commander";
|
|
@@ -269,14 +272,17 @@ var UsageTracker = class {
|
|
|
269
272
|
};
|
|
270
273
|
|
|
271
274
|
// src/cli/repl.tsx
|
|
272
|
-
import { Box as
|
|
273
|
-
import { useCallback, useEffect as useEffect2, useMemo, useRef as useRef2, useState as
|
|
275
|
+
import { Box as Box10, Text as Text11, render, useApp, useInput as useInput6 } from "ink";
|
|
276
|
+
import { useCallback, useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState3 } from "react";
|
|
274
277
|
|
|
275
278
|
// src/config/save.ts
|
|
276
279
|
import fs from "fs";
|
|
277
280
|
import path2 from "path";
|
|
278
281
|
import { parse, stringify } from "smol-toml";
|
|
279
282
|
async function addAllowRule(rule) {
|
|
283
|
+
return addPermissionRule("allow", rule);
|
|
284
|
+
}
|
|
285
|
+
async function addPermissionRule(kind, rule) {
|
|
280
286
|
const filePath = globalConfigPath();
|
|
281
287
|
let raw = {};
|
|
282
288
|
try {
|
|
@@ -286,9 +292,9 @@ async function addAllowRule(rule) {
|
|
|
286
292
|
if (error.code !== "ENOENT") throw error;
|
|
287
293
|
}
|
|
288
294
|
const permissions = typeof raw.permissions === "object" && raw.permissions !== null ? raw.permissions : {};
|
|
289
|
-
const
|
|
290
|
-
if (
|
|
291
|
-
permissions
|
|
295
|
+
const rules = Array.isArray(permissions[kind]) ? permissions[kind] : [];
|
|
296
|
+
if (rules.includes(rule)) return false;
|
|
297
|
+
permissions[kind] = [...rules, rule];
|
|
292
298
|
raw.permissions = permissions;
|
|
293
299
|
await fs.promises.mkdir(path2.dirname(filePath), { recursive: true });
|
|
294
300
|
await fs.promises.writeFile(filePath, stringify(raw), "utf8");
|
|
@@ -332,7 +338,8 @@ var ModelConfigSchema = z.object({
|
|
|
332
338
|
completionPrice: z.number().nonnegative().optional()
|
|
333
339
|
});
|
|
334
340
|
var PermissionsConfigSchema = z.object({
|
|
335
|
-
allow: z.array(z.string()).default([])
|
|
341
|
+
allow: z.array(z.string()).default([]),
|
|
342
|
+
deny: z.array(z.string()).default([])
|
|
336
343
|
});
|
|
337
344
|
var HookConfigSchema = z.object({
|
|
338
345
|
event: z.enum(["PreToolUse", "PostToolUse", "Stop"]),
|
|
@@ -370,7 +377,7 @@ var ConfigSchema = z.object({
|
|
|
370
377
|
// STAR_NO_NOTIFY=1 disables without touching the config.
|
|
371
378
|
notifyBell: z.boolean().default(true),
|
|
372
379
|
notifyBellThresholdSec: z.number().positive().default(10),
|
|
373
|
-
permissions: PermissionsConfigSchema.default({ allow: [] }),
|
|
380
|
+
permissions: PermissionsConfigSchema.default({ allow: [], deny: [] }),
|
|
374
381
|
hooks: z.array(HookConfigSchema).default([])
|
|
375
382
|
});
|
|
376
383
|
function contextWindowTokens(config, modelName) {
|
|
@@ -1253,6 +1260,61 @@ function stringifyPart(value) {
|
|
|
1253
1260
|
return typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
1254
1261
|
}
|
|
1255
1262
|
|
|
1263
|
+
// src/cli/commands/builtin.ts
|
|
1264
|
+
import { readFileSync } from "fs";
|
|
1265
|
+
|
|
1266
|
+
// src/session/search.ts
|
|
1267
|
+
var SNIPPET_LENGTH = 100;
|
|
1268
|
+
function flattenMessage(message) {
|
|
1269
|
+
const content = message.content;
|
|
1270
|
+
if (typeof content === "string") return content;
|
|
1271
|
+
if (!Array.isArray(content)) return "";
|
|
1272
|
+
const parts = [];
|
|
1273
|
+
for (const part of content) {
|
|
1274
|
+
if (part.type === "text") {
|
|
1275
|
+
parts.push(part.text);
|
|
1276
|
+
} else if (part.type === "tool-call") {
|
|
1277
|
+
parts.push(JSON.stringify(part.args));
|
|
1278
|
+
} else if (part.type === "tool-result") {
|
|
1279
|
+
parts.push(typeof part.result === "string" ? part.result : JSON.stringify(part.result));
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
return parts.join("\n");
|
|
1283
|
+
}
|
|
1284
|
+
function makeSnippet(text, query) {
|
|
1285
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
1286
|
+
const index = flat.toLowerCase().indexOf(query.toLowerCase());
|
|
1287
|
+
if (index === -1 || flat.length <= SNIPPET_LENGTH) return flat.slice(0, SNIPPET_LENGTH);
|
|
1288
|
+
const side = Math.max(0, Math.floor((SNIPPET_LENGTH - query.length) / 2));
|
|
1289
|
+
let start = Math.max(0, index - side);
|
|
1290
|
+
const end = Math.min(flat.length, start + SNIPPET_LENGTH);
|
|
1291
|
+
start = Math.max(0, end - SNIPPET_LENGTH);
|
|
1292
|
+
return `${start > 0 ? "\u2026" : ""}${flat.slice(start, end)}${end < flat.length ? "\u2026" : ""}`;
|
|
1293
|
+
}
|
|
1294
|
+
async function searchSessions(query, limit = 10) {
|
|
1295
|
+
const hits = [];
|
|
1296
|
+
const needle = query.toLowerCase();
|
|
1297
|
+
for (const meta of await SessionStore.list()) {
|
|
1298
|
+
if (hits.length >= limit) break;
|
|
1299
|
+
const store = await SessionStore.open(meta.id);
|
|
1300
|
+
if (!store) continue;
|
|
1301
|
+
const text = (await store.messages()).map(flattenMessage).filter((part) => part.length > 0).join("\n");
|
|
1302
|
+
if (text.toLowerCase().includes(needle)) {
|
|
1303
|
+
hits.push({ meta, snippet: makeSnippet(text, query) });
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
return hits;
|
|
1307
|
+
}
|
|
1308
|
+
function formatSearchResults(hits, query) {
|
|
1309
|
+
if (hits.length === 0) return `No sessions matching "${query}".`;
|
|
1310
|
+
const lines = hits.map(({ meta, snippet }) => {
|
|
1311
|
+
const title = meta.title || "(untitled)";
|
|
1312
|
+
return `${shortSessionId(meta.id)} ${title} (${relativeTime(meta.updatedAt)})
|
|
1313
|
+
${snippet}`;
|
|
1314
|
+
});
|
|
1315
|
+
return [...lines, "Resume with /resume <id>"].join("\n");
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1256
1318
|
// src/cli/diff-preview.ts
|
|
1257
1319
|
import { readFile as readFile3 } from "fs/promises";
|
|
1258
1320
|
import path10 from "path";
|
|
@@ -1440,31 +1502,41 @@ async function generateDiffPreview(toolName, args, cwd) {
|
|
|
1440
1502
|
|
|
1441
1503
|
// src/cli/commands/builtin.ts
|
|
1442
1504
|
function registerBuiltinCommands(registry) {
|
|
1443
|
-
registry.register(
|
|
1505
|
+
const register = (command) => registry.register(command);
|
|
1506
|
+
register({
|
|
1444
1507
|
name: "help",
|
|
1445
1508
|
description: "List available commands",
|
|
1446
1509
|
usage: "/help",
|
|
1510
|
+
category: "General",
|
|
1447
1511
|
run(_args, ctx) {
|
|
1448
|
-
const
|
|
1512
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1513
|
+
for (const cmd of registry.list()) {
|
|
1514
|
+
const category = cmd.category ?? "Other";
|
|
1449
1515
|
const usage = cmd.usage ?? `/${cmd.name}`;
|
|
1450
|
-
|
|
1451
|
-
|
|
1516
|
+
const lines = groups.get(category) ?? [];
|
|
1517
|
+
lines.push(` ${usage} - ${cmd.description}`);
|
|
1518
|
+
groups.set(category, lines);
|
|
1519
|
+
}
|
|
1520
|
+
const sections = [...groups.entries()].map(([name, lines]) => [name, ...lines].join("\n"));
|
|
1452
1521
|
ctx.addSystemMessage(`Available commands:
|
|
1453
|
-
|
|
1522
|
+
|
|
1523
|
+
${sections.join("\n\n")}`);
|
|
1454
1524
|
}
|
|
1455
1525
|
});
|
|
1456
|
-
|
|
1526
|
+
register({
|
|
1457
1527
|
name: "clear",
|
|
1458
1528
|
description: "Clear message history",
|
|
1459
1529
|
usage: "/clear",
|
|
1530
|
+
category: "General",
|
|
1460
1531
|
run(_args, ctx) {
|
|
1461
1532
|
ctx.clearMessages();
|
|
1462
1533
|
}
|
|
1463
1534
|
});
|
|
1464
|
-
|
|
1535
|
+
register({
|
|
1465
1536
|
name: "new",
|
|
1466
1537
|
description: "Start a new session with a clean context",
|
|
1467
1538
|
usage: "/new",
|
|
1539
|
+
category: "Sessions",
|
|
1468
1540
|
async run(_args, ctx) {
|
|
1469
1541
|
if (!ctx.newSession) {
|
|
1470
1542
|
ctx.addSystemMessage("/new: this context cannot start a new session.");
|
|
@@ -1473,10 +1545,11 @@ ${lines.join("\n")}`);
|
|
|
1473
1545
|
ctx.addSystemMessage(await ctx.newSession());
|
|
1474
1546
|
}
|
|
1475
1547
|
});
|
|
1476
|
-
|
|
1548
|
+
register({
|
|
1477
1549
|
name: "clear-sessions",
|
|
1478
1550
|
description: "Delete stored sessions: this directory by default, every session with --all (the current session is kept)",
|
|
1479
1551
|
usage: "/clear-sessions [--all]",
|
|
1552
|
+
category: "Sessions",
|
|
1480
1553
|
async run(args, ctx) {
|
|
1481
1554
|
const arg = args.trim();
|
|
1482
1555
|
if (arg !== "" && arg !== "--all") {
|
|
@@ -1492,157 +1565,242 @@ ${lines.join("\n")}`);
|
|
|
1492
1565
|
ctx.addSystemMessage(await ctx.clearSessions(arg === "--all"));
|
|
1493
1566
|
}
|
|
1494
1567
|
});
|
|
1495
|
-
|
|
1568
|
+
register({
|
|
1496
1569
|
name: "exit",
|
|
1497
1570
|
description: "Exit the application",
|
|
1498
1571
|
usage: "/exit",
|
|
1572
|
+
category: "General",
|
|
1499
1573
|
run(_args, ctx) {
|
|
1500
1574
|
ctx.exit();
|
|
1501
1575
|
}
|
|
1502
1576
|
});
|
|
1503
|
-
|
|
1577
|
+
register({
|
|
1504
1578
|
name: "q",
|
|
1505
1579
|
description: "Exit the application (alias of /exit)",
|
|
1506
1580
|
usage: "/q",
|
|
1581
|
+
category: "General",
|
|
1507
1582
|
run(_args, ctx) {
|
|
1508
1583
|
ctx.exit();
|
|
1509
1584
|
}
|
|
1510
1585
|
});
|
|
1511
|
-
|
|
1586
|
+
register({
|
|
1512
1587
|
name: "model",
|
|
1513
|
-
description: "
|
|
1588
|
+
description: "Switch the current model (opens a picker when no name is given)",
|
|
1514
1589
|
usage: "/model [name]",
|
|
1590
|
+
category: "Settings",
|
|
1515
1591
|
async run(args, ctx) {
|
|
1516
1592
|
if (!args) {
|
|
1517
|
-
ctx.addSystemMessage(ctx.
|
|
1593
|
+
ctx.addSystemMessage(await ctx.pickModel());
|
|
1518
1594
|
} else {
|
|
1519
1595
|
ctx.addSystemMessage(await ctx.switchModel(args));
|
|
1520
1596
|
}
|
|
1521
1597
|
}
|
|
1522
1598
|
});
|
|
1523
|
-
|
|
1599
|
+
register({
|
|
1524
1600
|
name: "resume",
|
|
1525
|
-
description: "
|
|
1601
|
+
description: "Resume a session by id, or pick one from a list (--all for every directory)",
|
|
1526
1602
|
usage: "/resume [sessionId | --all]",
|
|
1603
|
+
category: "Sessions",
|
|
1527
1604
|
async run(args, ctx) {
|
|
1528
1605
|
const arg = args.trim();
|
|
1529
1606
|
if (arg === "--all") {
|
|
1530
|
-
ctx.addSystemMessage(await ctx.
|
|
1607
|
+
ctx.addSystemMessage(await ctx.pickSession(true));
|
|
1531
1608
|
} else if (!arg) {
|
|
1532
|
-
ctx.addSystemMessage(await ctx.
|
|
1609
|
+
ctx.addSystemMessage(await ctx.pickSession(false));
|
|
1533
1610
|
} else {
|
|
1534
1611
|
ctx.addSystemMessage(await ctx.resumeSession(arg));
|
|
1535
1612
|
}
|
|
1536
1613
|
}
|
|
1537
1614
|
});
|
|
1538
|
-
|
|
1615
|
+
register({
|
|
1616
|
+
name: "fork",
|
|
1617
|
+
description: "Fork the current session into a new one and switch to it",
|
|
1618
|
+
usage: "/fork",
|
|
1619
|
+
category: "Sessions",
|
|
1620
|
+
async run(_args, ctx) {
|
|
1621
|
+
ctx.addSystemMessage(await ctx.forkSession());
|
|
1622
|
+
}
|
|
1623
|
+
});
|
|
1624
|
+
register({
|
|
1625
|
+
name: "search",
|
|
1626
|
+
description: "Full-text search across all stored sessions",
|
|
1627
|
+
usage: "/search <query>",
|
|
1628
|
+
category: "Sessions",
|
|
1629
|
+
async run(args, ctx) {
|
|
1630
|
+
const query = args.trim();
|
|
1631
|
+
if (!query) {
|
|
1632
|
+
ctx.addSystemMessage("Usage: /search <query>");
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
ctx.addSystemMessage(formatSearchResults(await searchSessions(query), query));
|
|
1636
|
+
}
|
|
1637
|
+
});
|
|
1638
|
+
register({
|
|
1539
1639
|
name: "todo",
|
|
1540
1640
|
description: "Show the current todo list",
|
|
1541
1641
|
usage: "/todo",
|
|
1642
|
+
category: "Info",
|
|
1542
1643
|
async run(_args, ctx) {
|
|
1543
1644
|
ctx.addSystemMessage(await ctx.showTodos());
|
|
1544
1645
|
}
|
|
1545
1646
|
});
|
|
1546
|
-
|
|
1647
|
+
register({
|
|
1547
1648
|
name: "tasks",
|
|
1548
1649
|
description: "List background shell tasks",
|
|
1549
1650
|
usage: "/tasks",
|
|
1651
|
+
category: "Info",
|
|
1550
1652
|
run(_args, ctx) {
|
|
1551
1653
|
ctx.addSystemMessage(ctx.listTasks());
|
|
1552
1654
|
}
|
|
1553
1655
|
});
|
|
1554
|
-
|
|
1656
|
+
register({
|
|
1555
1657
|
name: "cost",
|
|
1556
1658
|
description: "Show API token usage for this session",
|
|
1557
1659
|
usage: "/cost",
|
|
1660
|
+
category: "Info",
|
|
1558
1661
|
run(_args, ctx) {
|
|
1559
1662
|
ctx.addSystemMessage(ctx.showUsage());
|
|
1560
1663
|
}
|
|
1561
1664
|
});
|
|
1562
|
-
|
|
1665
|
+
register({
|
|
1563
1666
|
name: "usage",
|
|
1564
1667
|
description: "Show token usage aggregated across all sessions",
|
|
1565
1668
|
usage: "/usage",
|
|
1669
|
+
category: "Info",
|
|
1566
1670
|
async run(_args, ctx) {
|
|
1567
1671
|
ctx.addSystemMessage(await ctx.showGlobalUsage());
|
|
1568
1672
|
}
|
|
1569
1673
|
});
|
|
1570
|
-
|
|
1674
|
+
register({
|
|
1571
1675
|
name: "config",
|
|
1572
1676
|
description: "Show the current configuration",
|
|
1573
1677
|
usage: "/config",
|
|
1678
|
+
category: "Info",
|
|
1574
1679
|
run(_args, ctx) {
|
|
1575
1680
|
ctx.addSystemMessage(ctx.describeConfig());
|
|
1576
1681
|
}
|
|
1577
1682
|
});
|
|
1578
|
-
|
|
1683
|
+
register({
|
|
1579
1684
|
name: "compact",
|
|
1580
1685
|
description: "Compact the conversation history to free up context",
|
|
1581
1686
|
usage: "/compact",
|
|
1687
|
+
category: "Sessions",
|
|
1582
1688
|
async run(_args, ctx) {
|
|
1583
1689
|
ctx.addSystemMessage(await ctx.compactContext());
|
|
1584
1690
|
}
|
|
1585
1691
|
});
|
|
1586
|
-
|
|
1692
|
+
register({
|
|
1587
1693
|
name: "export",
|
|
1588
1694
|
description: "Export the current session to a Markdown file",
|
|
1589
1695
|
usage: "/export [path]",
|
|
1696
|
+
category: "Sessions",
|
|
1590
1697
|
async run(args, ctx) {
|
|
1591
1698
|
ctx.addSystemMessage(await ctx.exportSession(args));
|
|
1592
1699
|
}
|
|
1593
1700
|
});
|
|
1594
|
-
|
|
1701
|
+
register({
|
|
1595
1702
|
name: "permission",
|
|
1596
|
-
description: "
|
|
1703
|
+
description: "Set the permission mode (opens a picker when no mode is given)",
|
|
1597
1704
|
usage: "/permission [ask|auto|readonly|yolo]",
|
|
1705
|
+
category: "Settings",
|
|
1598
1706
|
async run(args, ctx) {
|
|
1599
|
-
|
|
1707
|
+
const arg = args.trim();
|
|
1708
|
+
if (!arg) {
|
|
1709
|
+
ctx.addSystemMessage(await ctx.pickPermissionMode());
|
|
1710
|
+
} else {
|
|
1711
|
+
ctx.addSystemMessage(await ctx.permissionMode(arg));
|
|
1712
|
+
}
|
|
1600
1713
|
}
|
|
1601
1714
|
});
|
|
1602
|
-
|
|
1715
|
+
register({
|
|
1603
1716
|
name: "plan",
|
|
1604
1717
|
description: "Toggle plan mode: read-only research, then approve the plan before executing",
|
|
1605
1718
|
usage: "/plan",
|
|
1719
|
+
category: "Settings",
|
|
1606
1720
|
async run(_args, ctx) {
|
|
1607
1721
|
ctx.addSystemMessage(await ctx.planMode());
|
|
1608
1722
|
}
|
|
1609
1723
|
});
|
|
1610
|
-
|
|
1724
|
+
register({
|
|
1725
|
+
name: "memory",
|
|
1726
|
+
description: "Show user memory (MEMORY.md), or add an entry",
|
|
1727
|
+
usage: "/memory [add <text>]",
|
|
1728
|
+
category: "Settings",
|
|
1729
|
+
run(args, ctx) {
|
|
1730
|
+
const arg = args.trim();
|
|
1731
|
+
if (arg === "add" || arg.startsWith("add ")) {
|
|
1732
|
+
const text = arg.slice(3).trim();
|
|
1733
|
+
if (!text) {
|
|
1734
|
+
ctx.addSystemMessage("Usage: /memory add <text>");
|
|
1735
|
+
return;
|
|
1736
|
+
}
|
|
1737
|
+
appendUserMemory(text);
|
|
1738
|
+
ctx.addSystemMessage(`Saved to user memory: - ${text}`);
|
|
1739
|
+
return;
|
|
1740
|
+
}
|
|
1741
|
+
if (arg) {
|
|
1742
|
+
ctx.addSystemMessage(`Unknown argument "${arg}". Usage: /memory [add <text>]`);
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1745
|
+
const file = userMemoryPath();
|
|
1746
|
+
let content = "";
|
|
1747
|
+
try {
|
|
1748
|
+
content = readFileSync(file, "utf8").trim();
|
|
1749
|
+
} catch {
|
|
1750
|
+
content = "";
|
|
1751
|
+
}
|
|
1752
|
+
if (!content) {
|
|
1753
|
+
ctx.addSystemMessage(
|
|
1754
|
+
`No user memory yet (${file}).
|
|
1755
|
+
Add entries with /memory add <text> or ask the agent to remember something.`
|
|
1756
|
+
);
|
|
1757
|
+
return;
|
|
1758
|
+
}
|
|
1759
|
+
ctx.addSystemMessage(`User memory (${file}):
|
|
1760
|
+
${content}`);
|
|
1761
|
+
}
|
|
1762
|
+
});
|
|
1763
|
+
register({
|
|
1611
1764
|
name: "undo",
|
|
1612
1765
|
description: "Undo the last conversation turn: revert its file changes (write_file/edit_file) and retract its messages",
|
|
1613
1766
|
usage: "/undo",
|
|
1767
|
+
category: "Changes",
|
|
1614
1768
|
async run(_args, ctx) {
|
|
1615
1769
|
ctx.addSystemMessage(await ctx.undo());
|
|
1616
1770
|
}
|
|
1617
1771
|
});
|
|
1618
|
-
|
|
1772
|
+
register({
|
|
1619
1773
|
name: "rewind",
|
|
1620
1774
|
description: "List file-change checkpoints, or rewind to just before one: restore files and retract the conversation",
|
|
1621
1775
|
usage: "/rewind [n]",
|
|
1776
|
+
category: "Changes",
|
|
1622
1777
|
async run(args, ctx) {
|
|
1623
1778
|
ctx.addSystemMessage(await ctx.rewind(args.trim()));
|
|
1624
1779
|
}
|
|
1625
1780
|
});
|
|
1626
|
-
|
|
1781
|
+
register({
|
|
1627
1782
|
name: "init",
|
|
1628
1783
|
description: "Generate an AGENTS.md for the current project",
|
|
1629
1784
|
usage: "/init [force]",
|
|
1785
|
+
category: "Settings",
|
|
1630
1786
|
async run(args, ctx) {
|
|
1631
1787
|
ctx.addSystemMessage(await ctx.initProject(args));
|
|
1632
1788
|
}
|
|
1633
1789
|
});
|
|
1634
|
-
|
|
1790
|
+
register({
|
|
1635
1791
|
name: "doctor",
|
|
1636
1792
|
description: "Run environment and configuration checks",
|
|
1637
1793
|
usage: "/doctor",
|
|
1794
|
+
category: "Info",
|
|
1638
1795
|
async run(_args, ctx) {
|
|
1639
1796
|
ctx.addSystemMessage(await ctx.runDoctor());
|
|
1640
1797
|
}
|
|
1641
1798
|
});
|
|
1642
|
-
|
|
1799
|
+
register({
|
|
1643
1800
|
name: "skills",
|
|
1644
1801
|
description: "List available skills (project scope overrides user scope)",
|
|
1645
1802
|
usage: "/skills",
|
|
1803
|
+
category: "Info",
|
|
1646
1804
|
run(_args, ctx) {
|
|
1647
1805
|
const skills = discoverSkills(ctx.cwd ?? process.cwd());
|
|
1648
1806
|
if (skills.length === 0) {
|
|
@@ -1656,10 +1814,11 @@ ${lines.join("\n")}`);
|
|
|
1656
1814
|
${lines.join("\n")}`);
|
|
1657
1815
|
}
|
|
1658
1816
|
});
|
|
1659
|
-
|
|
1817
|
+
register({
|
|
1660
1818
|
name: "commit",
|
|
1661
1819
|
description: "Analyze uncommitted changes and create a git commit (Conventional Commits)",
|
|
1662
1820
|
usage: "/commit [instructions]",
|
|
1821
|
+
category: "Changes",
|
|
1663
1822
|
async run(args, ctx) {
|
|
1664
1823
|
const cwd = ctx.cwd ?? process.cwd();
|
|
1665
1824
|
if (!isGitRepo(cwd)) {
|
|
@@ -1678,10 +1837,11 @@ ${lines.join("\n")}`);
|
|
|
1678
1837
|
ctx.addSystemMessage("/commit: this context cannot submit prompts to the model.");
|
|
1679
1838
|
}
|
|
1680
1839
|
});
|
|
1681
|
-
|
|
1840
|
+
register({
|
|
1682
1841
|
name: "copy",
|
|
1683
1842
|
description: "Copy the last assistant reply to the clipboard (`all` for the whole conversation)",
|
|
1684
1843
|
usage: "/copy [all]",
|
|
1844
|
+
category: "Sessions",
|
|
1685
1845
|
async run(args, ctx) {
|
|
1686
1846
|
const arg = args.trim();
|
|
1687
1847
|
if (arg !== "" && arg !== "all") {
|
|
@@ -1706,10 +1866,11 @@ ${lines.join("\n")}`);
|
|
|
1706
1866
|
);
|
|
1707
1867
|
}
|
|
1708
1868
|
});
|
|
1709
|
-
|
|
1869
|
+
register({
|
|
1710
1870
|
name: "diff",
|
|
1711
1871
|
description: "Show uncommitted changes (git status + colored diff)",
|
|
1712
1872
|
usage: "/diff",
|
|
1873
|
+
category: "Changes",
|
|
1713
1874
|
run(_args, ctx) {
|
|
1714
1875
|
const cwd = ctx.cwd ?? process.cwd();
|
|
1715
1876
|
if (!isGitRepo(cwd)) {
|
|
@@ -1745,7 +1906,7 @@ ${result.diff}${note ? `
|
|
|
1745
1906
|
}
|
|
1746
1907
|
|
|
1747
1908
|
// src/cli/commands/custom.ts
|
|
1748
|
-
import { readFileSync, readdirSync } from "fs";
|
|
1909
|
+
import { readFileSync as readFileSync2, readdirSync } from "fs";
|
|
1749
1910
|
import path11 from "path";
|
|
1750
1911
|
var NAME_PATTERN = /^[a-z0-9-]+$/;
|
|
1751
1912
|
var DESCRIPTION_PATTERN = /^<!--\s*description:\s*(.+?)\s*-->\s*$/;
|
|
@@ -1753,7 +1914,7 @@ var DEFAULT_DESCRIPTION = "Custom prompt command";
|
|
|
1753
1914
|
function parseCommandFile(filePath, name) {
|
|
1754
1915
|
let raw;
|
|
1755
1916
|
try {
|
|
1756
|
-
raw =
|
|
1917
|
+
raw = readFileSync2(filePath, "utf8");
|
|
1757
1918
|
} catch {
|
|
1758
1919
|
return null;
|
|
1759
1920
|
}
|
|
@@ -1799,6 +1960,7 @@ function registerCustomCommands(registry, cwd, home) {
|
|
|
1799
1960
|
name: command.name,
|
|
1800
1961
|
description: command.description,
|
|
1801
1962
|
usage: `/${command.name} [args]`,
|
|
1963
|
+
category: "Custom",
|
|
1802
1964
|
run(args, ctx) {
|
|
1803
1965
|
const prompt = command.template.split("$ARGUMENTS").join(args);
|
|
1804
1966
|
if (ctx.submitPrompt) {
|
|
@@ -2215,6 +2377,33 @@ function formatCheckpointList(snapshots, cwd) {
|
|
|
2215
2377
|
].join("\n");
|
|
2216
2378
|
}
|
|
2217
2379
|
|
|
2380
|
+
// src/cli/commands/suggest.ts
|
|
2381
|
+
var MAX_SUGGESTIONS = 5;
|
|
2382
|
+
function isSubsequence(query, target) {
|
|
2383
|
+
let i = 0;
|
|
2384
|
+
for (const ch of target) {
|
|
2385
|
+
if (ch === query[i]) i += 1;
|
|
2386
|
+
if (i === query.length) return true;
|
|
2387
|
+
}
|
|
2388
|
+
return query.length === 0;
|
|
2389
|
+
}
|
|
2390
|
+
function filterCommands(input, commands) {
|
|
2391
|
+
if (!input.startsWith("/")) return [];
|
|
2392
|
+
if (/\s/.test(input)) return [];
|
|
2393
|
+
const prefix = input.slice(1).toLowerCase();
|
|
2394
|
+
const byName = (a, b) => a.name.localeCompare(b.name);
|
|
2395
|
+
const prefixed = commands.filter((cmd) => cmd.name.toLowerCase().startsWith(prefix)).sort(byName);
|
|
2396
|
+
const fuzzy = commands.filter(
|
|
2397
|
+
(cmd) => !cmd.name.toLowerCase().startsWith(prefix) && isSubsequence(prefix, cmd.name.toLowerCase())
|
|
2398
|
+
).sort(byName);
|
|
2399
|
+
return [...prefixed, ...fuzzy].slice(0, MAX_SUGGESTIONS);
|
|
2400
|
+
}
|
|
2401
|
+
function didYouMeanSuffix(names) {
|
|
2402
|
+
if (names.length === 0) return "";
|
|
2403
|
+
const listed = names.slice(0, 3).map((name) => `/${name}`);
|
|
2404
|
+
return ` Did you mean: ${listed.join(", ")}?`;
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2218
2407
|
// src/cli/components/ExitPlanPrompt.tsx
|
|
2219
2408
|
import { Box, Text, useInput } from "ink";
|
|
2220
2409
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -2235,15 +2424,6 @@ function ExitPlanPrompt({ onDecision }) {
|
|
|
2235
2424
|
import { Box as Box2, Text as Text2, useInput as useInput2 } from "ink";
|
|
2236
2425
|
import { useEffect, useRef, useState } from "react";
|
|
2237
2426
|
|
|
2238
|
-
// src/cli/commands/suggest.ts
|
|
2239
|
-
var MAX_SUGGESTIONS = 5;
|
|
2240
|
-
function filterCommands(input, commands) {
|
|
2241
|
-
if (!input.startsWith("/")) return [];
|
|
2242
|
-
if (/\s/.test(input)) return [];
|
|
2243
|
-
const prefix = input.slice(1).toLowerCase();
|
|
2244
|
-
return commands.filter((cmd) => cmd.name.toLowerCase().startsWith(prefix)).sort((a, b) => a.name.localeCompare(b.name)).slice(0, MAX_SUGGESTIONS);
|
|
2245
|
-
}
|
|
2246
|
-
|
|
2247
2427
|
// src/cli/path-suggest.ts
|
|
2248
2428
|
import { readdir } from "fs/promises";
|
|
2249
2429
|
import path15 from "path";
|
|
@@ -2264,6 +2444,7 @@ async function suggestPaths(token, cwd, maxScanEntries = MAX_SCAN_ENTRIES, maxSu
|
|
|
2264
2444
|
const dirAbs = path15.resolve(cwd, dirPart === "" ? "." : dirPart);
|
|
2265
2445
|
const entries = await readdir(dirAbs, { withFileTypes: true }).catch(() => null);
|
|
2266
2446
|
if (!entries) return [];
|
|
2447
|
+
const ignore = createIgnorePredicate(cwd);
|
|
2267
2448
|
const lower = basePrefix.toLowerCase();
|
|
2268
2449
|
const matches = [];
|
|
2269
2450
|
let scanned = 0;
|
|
@@ -2275,6 +2456,7 @@ async function suggestPaths(token, cwd, maxScanEntries = MAX_SCAN_ENTRIES, maxSu
|
|
|
2275
2456
|
if (isDir && SKIP_DIRS.has(entry.name)) continue;
|
|
2276
2457
|
if (entry.name.startsWith(".") && !basePrefix.startsWith(".")) continue;
|
|
2277
2458
|
if (!entry.name.toLowerCase().startsWith(lower)) continue;
|
|
2459
|
+
if (ignore?.(path15.join(dirAbs, entry.name), isDir)) continue;
|
|
2278
2460
|
matches.push({ path: `${dirPart}${entry.name}${isDir ? "/" : ""}`, isDir });
|
|
2279
2461
|
}
|
|
2280
2462
|
matches.sort((a, b) => a.path.localeCompare(b.path));
|
|
@@ -2289,6 +2471,7 @@ function InputBox({
|
|
|
2289
2471
|
commands,
|
|
2290
2472
|
cwd,
|
|
2291
2473
|
refill,
|
|
2474
|
+
initialHistory,
|
|
2292
2475
|
onSubmit,
|
|
2293
2476
|
onInterrupt,
|
|
2294
2477
|
onExit,
|
|
@@ -2296,7 +2479,7 @@ function InputBox({
|
|
|
2296
2479
|
}) {
|
|
2297
2480
|
const [value, setValue] = useState("");
|
|
2298
2481
|
const [cursor, setCursor] = useState(0);
|
|
2299
|
-
const [history, setHistory] = useState([]);
|
|
2482
|
+
const [history, setHistory] = useState(() => initialHistory ?? []);
|
|
2300
2483
|
const [highlight, setHighlight] = useState(0);
|
|
2301
2484
|
const [suggestionsDismissed, setSuggestionsDismissed] = useState(false);
|
|
2302
2485
|
const [pathSuggestions, setPathSuggestions] = useState([]);
|
|
@@ -2478,12 +2661,13 @@ function InputBox({
|
|
|
2478
2661
|
/* @__PURE__ */ jsx2(Text2, { inverse: true, children: at }),
|
|
2479
2662
|
/* @__PURE__ */ jsx2(Text2, { children: after })
|
|
2480
2663
|
] }),
|
|
2481
|
-
suggestions.length > 0 && /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", paddingLeft: 2, children: suggestions.map(
|
|
2482
|
-
|
|
2483
|
-
|
|
2664
|
+
suggestions.length > 0 && /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", paddingLeft: 2, children: suggestions.map((cmd, index) => {
|
|
2665
|
+
const label = cmd.usage ?? `/${cmd.name}`;
|
|
2666
|
+
return index === activeIndex ? /* @__PURE__ */ jsx2(Text2, { bold: true, inverse: true, children: `${label} - ${cmd.description}` }, cmd.name) : /* @__PURE__ */ jsxs2(Text2, { children: [
|
|
2667
|
+
/* @__PURE__ */ jsx2(Text2, { color: "cyan", children: label }),
|
|
2484
2668
|
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: ` - ${cmd.description}` })
|
|
2485
|
-
] }, cmd.name)
|
|
2486
|
-
) }),
|
|
2669
|
+
] }, cmd.name);
|
|
2670
|
+
}) }),
|
|
2487
2671
|
showPathSuggestions && /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", paddingLeft: 2, children: pathSuggestions.map(
|
|
2488
2672
|
(sugg, index) => index === activeIndex ? /* @__PURE__ */ jsx2(Text2, { bold: true, inverse: true, children: `@${sugg.path}` }, sugg.path) : /* @__PURE__ */ jsxs2(Text2, { children: [
|
|
2489
2673
|
/* @__PURE__ */ jsx2(Text2, { color: "cyan", children: `@${sugg.path}` }),
|
|
@@ -2591,10 +2775,72 @@ function RewindConfirmPrompt({ summary, onDecision }) {
|
|
|
2591
2775
|
] });
|
|
2592
2776
|
}
|
|
2593
2777
|
|
|
2778
|
+
// src/cli/components/SelectPrompt.tsx
|
|
2779
|
+
import { Box as Box6, Text as Text7, useInput as useInput5 } from "ink";
|
|
2780
|
+
import { useState as useState2 } from "react";
|
|
2781
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2782
|
+
var SELECT_PROMPT_MAX_VISIBLE = 9;
|
|
2783
|
+
function SelectPrompt({ title, options, onSelect, onCancel }) {
|
|
2784
|
+
const [index, setIndex] = useState2(0);
|
|
2785
|
+
useInput5((input, key) => {
|
|
2786
|
+
if (key.escape) {
|
|
2787
|
+
onCancel();
|
|
2788
|
+
return;
|
|
2789
|
+
}
|
|
2790
|
+
if (key.return) {
|
|
2791
|
+
const option = options[index];
|
|
2792
|
+
if (option) onSelect(option.value);
|
|
2793
|
+
return;
|
|
2794
|
+
}
|
|
2795
|
+
if (key.downArrow || input === "j") {
|
|
2796
|
+
setIndex((i) => Math.min(i + 1, options.length - 1));
|
|
2797
|
+
} else if (key.upArrow || input === "k") {
|
|
2798
|
+
setIndex((i) => Math.max(i - 1, 0));
|
|
2799
|
+
}
|
|
2800
|
+
});
|
|
2801
|
+
const maxStart = Math.max(0, options.length - SELECT_PROMPT_MAX_VISIBLE);
|
|
2802
|
+
const start = Math.min(Math.max(0, index - Math.floor(SELECT_PROMPT_MAX_VISIBLE / 2)), maxStart);
|
|
2803
|
+
const visible = options.slice(start, start + SELECT_PROMPT_MAX_VISIBLE);
|
|
2804
|
+
const above = start;
|
|
2805
|
+
const below = options.length - start - visible.length;
|
|
2806
|
+
return /* @__PURE__ */ jsxs7(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
|
|
2807
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, color: "cyan", children: title }),
|
|
2808
|
+
above > 0 && /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
2809
|
+
"\u2026 ",
|
|
2810
|
+
above,
|
|
2811
|
+
" more above"
|
|
2812
|
+
] }),
|
|
2813
|
+
visible.map((option, i) => {
|
|
2814
|
+
const active = start + i === index;
|
|
2815
|
+
return /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
2816
|
+
/* @__PURE__ */ jsxs7(Text7, { bold: active, inverse: active, children: [
|
|
2817
|
+
active ? "\u276F " : " ",
|
|
2818
|
+
option.label
|
|
2819
|
+
] }),
|
|
2820
|
+
option.hint && /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
2821
|
+
" (",
|
|
2822
|
+
option.hint,
|
|
2823
|
+
")"
|
|
2824
|
+
] }),
|
|
2825
|
+
option.description && /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
2826
|
+
" \u2014 ",
|
|
2827
|
+
option.description
|
|
2828
|
+
] })
|
|
2829
|
+
] }, option.value);
|
|
2830
|
+
}),
|
|
2831
|
+
below > 0 && /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
2832
|
+
"\u2026 ",
|
|
2833
|
+
below,
|
|
2834
|
+
" more below"
|
|
2835
|
+
] }),
|
|
2836
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "[\u2191/\u2193 or j/k] move [Enter] select [Esc] cancel" })
|
|
2837
|
+
] });
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2594
2840
|
// src/cli/components/StatusBar.tsx
|
|
2595
|
-
import { Box as
|
|
2841
|
+
import { Box as Box7, Text as Text8, useStdout } from "ink";
|
|
2596
2842
|
import { memo as memo2 } from "react";
|
|
2597
|
-
import { jsxs as
|
|
2843
|
+
import { jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2598
2844
|
var BG_LABEL_MAX = 40;
|
|
2599
2845
|
var WIDE_COLUMNS = 120;
|
|
2600
2846
|
function cwdLabel(cwd, wide) {
|
|
@@ -2616,36 +2862,36 @@ var StatusBar = memo2(function StatusBar2({
|
|
|
2616
2862
|
const wide = (stdout?.columns ?? 80) >= WIDE_COLUMNS;
|
|
2617
2863
|
const joined = backgroundTasks.join(", ");
|
|
2618
2864
|
const labels = joined.length > BG_LABEL_MAX ? `${joined.slice(0, BG_LABEL_MAX)}\u2026` : joined;
|
|
2619
|
-
return /* @__PURE__ */
|
|
2620
|
-
/* @__PURE__ */
|
|
2865
|
+
return /* @__PURE__ */ jsxs8(Box7, { justifyContent: "space-between", children: [
|
|
2866
|
+
/* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
2621
2867
|
cwdLabel(cwd, wide),
|
|
2622
2868
|
gitBranch ? ` [${gitBranch}]` : ""
|
|
2623
2869
|
] }),
|
|
2624
|
-
backgroundTasks.length > 0 && /* @__PURE__ */
|
|
2870
|
+
backgroundTasks.length > 0 && /* @__PURE__ */ jsxs8(Text8, { color: "yellow", children: [
|
|
2625
2871
|
"bg: ",
|
|
2626
2872
|
backgroundTasks.length,
|
|
2627
2873
|
" (",
|
|
2628
2874
|
labels,
|
|
2629
2875
|
")"
|
|
2630
2876
|
] }),
|
|
2631
|
-
/* @__PURE__ */
|
|
2877
|
+
/* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
2632
2878
|
"model: ",
|
|
2633
2879
|
model
|
|
2634
2880
|
] }),
|
|
2635
|
-
contextPercent != null && /* @__PURE__ */
|
|
2881
|
+
contextPercent != null && /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
2636
2882
|
"ctx: ",
|
|
2637
2883
|
contextPercent,
|
|
2638
2884
|
"%"
|
|
2639
2885
|
] }),
|
|
2640
|
-
sessionCostUsd != null && sessionCostUsd > 0 && /* @__PURE__ */
|
|
2886
|
+
sessionCostUsd != null && sessionCostUsd > 0 && /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
2641
2887
|
"$",
|
|
2642
2888
|
formatDollars(sessionCostUsd)
|
|
2643
2889
|
] }),
|
|
2644
|
-
/* @__PURE__ */
|
|
2890
|
+
/* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
2645
2891
|
tokens2,
|
|
2646
2892
|
" tokens"
|
|
2647
2893
|
] }),
|
|
2648
|
-
/* @__PURE__ */
|
|
2894
|
+
/* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
|
|
2649
2895
|
"mode: ",
|
|
2650
2896
|
permissionMode
|
|
2651
2897
|
] })
|
|
@@ -2653,22 +2899,22 @@ var StatusBar = memo2(function StatusBar2({
|
|
|
2653
2899
|
});
|
|
2654
2900
|
|
|
2655
2901
|
// src/cli/components/StreamingMessage.tsx
|
|
2656
|
-
import { Box as
|
|
2902
|
+
import { Box as Box8, Text as Text9 } from "ink";
|
|
2657
2903
|
import { memo as memo3 } from "react";
|
|
2658
|
-
import { jsx as
|
|
2904
|
+
import { jsx as jsx8, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2659
2905
|
var StreamingMessage = memo3(function StreamingMessage2({
|
|
2660
2906
|
text,
|
|
2661
2907
|
continuation
|
|
2662
2908
|
}) {
|
|
2663
|
-
return /* @__PURE__ */
|
|
2664
|
-
!continuation && /* @__PURE__ */
|
|
2665
|
-
/* @__PURE__ */
|
|
2909
|
+
return /* @__PURE__ */ jsxs9(Box8, { flexDirection: "column", marginBottom: 1, children: [
|
|
2910
|
+
!continuation && /* @__PURE__ */ jsx8(Text9, { bold: true, color: "green", children: "star" }),
|
|
2911
|
+
/* @__PURE__ */ jsx8(Text9, { color: "green", children: text })
|
|
2666
2912
|
] });
|
|
2667
2913
|
});
|
|
2668
2914
|
|
|
2669
2915
|
// src/cli/components/ThinkingIndicator.tsx
|
|
2670
|
-
import { Box as
|
|
2671
|
-
import { jsxs as
|
|
2916
|
+
import { Box as Box9, Text as Text10 } from "ink";
|
|
2917
|
+
import { jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2672
2918
|
var SPINNER_FRAMES = ["-", "\\", "|", "/"];
|
|
2673
2919
|
var REASONING_TAIL_LENGTH = 200;
|
|
2674
2920
|
var THOUGHT_SUMMARY_LENGTH = 100;
|
|
@@ -2684,13 +2930,13 @@ function ThinkingIndicator({
|
|
|
2684
2930
|
}) {
|
|
2685
2931
|
const tail = reasoning ? truncateTail(reasoning, REASONING_TAIL_LENGTH) : "";
|
|
2686
2932
|
const spinner = SPINNER_FRAMES[frame % SPINNER_FRAMES.length];
|
|
2687
|
-
return /* @__PURE__ */
|
|
2688
|
-
/* @__PURE__ */
|
|
2933
|
+
return /* @__PURE__ */ jsxs10(Box9, { flexDirection: "column", children: [
|
|
2934
|
+
/* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
2689
2935
|
spinner,
|
|
2690
2936
|
" ",
|
|
2691
2937
|
activity ?? "star is thinking\u2026"
|
|
2692
2938
|
] }),
|
|
2693
|
-
tail !== "" && /* @__PURE__ */
|
|
2939
|
+
tail !== "" && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
2694
2940
|
" ",
|
|
2695
2941
|
tail
|
|
2696
2942
|
] })
|
|
@@ -2712,6 +2958,51 @@ function isDoubleEscape(previous, now, windowMs = DOUBLE_ESC_WINDOW_MS) {
|
|
|
2712
2958
|
return previous !== null && now - previous <= windowMs;
|
|
2713
2959
|
}
|
|
2714
2960
|
|
|
2961
|
+
// src/cli/history.ts
|
|
2962
|
+
import fs10 from "fs";
|
|
2963
|
+
import path16 from "path";
|
|
2964
|
+
var DEFAULT_LIMIT = 500;
|
|
2965
|
+
var MAX_FILE_LINES = 1e3;
|
|
2966
|
+
var TRIM_TO_LINES = 500;
|
|
2967
|
+
function historyPath() {
|
|
2968
|
+
return path16.join(starHome(), "history");
|
|
2969
|
+
}
|
|
2970
|
+
function readLines(file) {
|
|
2971
|
+
const raw = fs10.readFileSync(file, "utf8");
|
|
2972
|
+
return raw.split("\n").map((line) => line.replace(/\r$/, "")).filter((line) => line.trim().length > 0);
|
|
2973
|
+
}
|
|
2974
|
+
function loadHistory(limit = DEFAULT_LIMIT) {
|
|
2975
|
+
try {
|
|
2976
|
+
return readLines(historyPath()).slice(-limit);
|
|
2977
|
+
} catch {
|
|
2978
|
+
return [];
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
function appendHistory(entry) {
|
|
2982
|
+
const line = entry.replace(/[\r\n]+/g, " ").trim();
|
|
2983
|
+
if (line.length === 0) return;
|
|
2984
|
+
try {
|
|
2985
|
+
const file = historyPath();
|
|
2986
|
+
let existing = [];
|
|
2987
|
+
try {
|
|
2988
|
+
existing = readLines(file);
|
|
2989
|
+
} catch {
|
|
2990
|
+
}
|
|
2991
|
+
if (existing[existing.length - 1] === line) return;
|
|
2992
|
+
if (existing.length >= MAX_FILE_LINES) {
|
|
2993
|
+
const kept = [...existing.slice(-(TRIM_TO_LINES - 1)), line];
|
|
2994
|
+
fs10.mkdirSync(path16.dirname(file), { recursive: true });
|
|
2995
|
+
fs10.writeFileSync(file, `${kept.join("\n")}
|
|
2996
|
+
`);
|
|
2997
|
+
return;
|
|
2998
|
+
}
|
|
2999
|
+
fs10.mkdirSync(path16.dirname(file), { recursive: true });
|
|
3000
|
+
fs10.appendFileSync(file, `${line}
|
|
3001
|
+
`);
|
|
3002
|
+
} catch {
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
|
|
2715
3006
|
// src/cli/notify.ts
|
|
2716
3007
|
var BELL = "\x07";
|
|
2717
3008
|
function shouldNotifyBell(input, isTTY) {
|
|
@@ -2827,7 +3118,7 @@ async function checkForUpdate(currentVersion, fetcher = defaultFetcher2) {
|
|
|
2827
3118
|
}
|
|
2828
3119
|
|
|
2829
3120
|
// src/cli/repl.tsx
|
|
2830
|
-
import { jsx as
|
|
3121
|
+
import { jsx as jsx9, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2831
3122
|
var CLEAR_TERMINAL = process.platform === "win32" ? "\x1B[2J\x1B[0f" : "\x1B[2J\x1B[3J\x1B[H";
|
|
2832
3123
|
function emptyUsage() {
|
|
2833
3124
|
return { requests: 0, promptTokens: 0, completionTokens: 0, totalTokens: 0 };
|
|
@@ -2839,6 +3130,16 @@ var SESSION_MODE_CYCLE = ["ask", "auto", "readonly", "plan"];
|
|
|
2839
3130
|
function formatUsage(usage) {
|
|
2840
3131
|
return `API usage this session: ${usage.requests} requests, ${usage.promptTokens} prompt + ${usage.completionTokens} completion = ${usage.totalTokens} tokens`;
|
|
2841
3132
|
}
|
|
3133
|
+
var SESSION_PICKER_CAP = 20;
|
|
3134
|
+
async function sessionPreview(id) {
|
|
3135
|
+
const store = await SessionStore.open(id);
|
|
3136
|
+
if (!store) return null;
|
|
3137
|
+
const first = (await store.messages()).find((m) => m.role === "user");
|
|
3138
|
+
if (!first) return null;
|
|
3139
|
+
const text = coreMessageText(first).replace(/\s+/g, " ").trim();
|
|
3140
|
+
if (!text) return null;
|
|
3141
|
+
return text.length > 60 ? `${text.slice(0, 60)}\u2026` : text;
|
|
3142
|
+
}
|
|
2842
3143
|
function Repl({
|
|
2843
3144
|
backend,
|
|
2844
3145
|
model,
|
|
@@ -2850,23 +3151,24 @@ function Repl({
|
|
|
2850
3151
|
initialUsage
|
|
2851
3152
|
}) {
|
|
2852
3153
|
const { exit } = useApp();
|
|
2853
|
-
const [initialDisplay] =
|
|
2854
|
-
const [messages, setMessages] =
|
|
2855
|
-
const [epoch, setEpoch] =
|
|
2856
|
-
const [streamingText, setStreamingText] =
|
|
2857
|
-
const [isStreaming, setIsStreaming] =
|
|
2858
|
-
const [thinking, setThinking] =
|
|
2859
|
-
const [thinkingText, setThinkingText] =
|
|
2860
|
-
const [thoughtSummary, setThoughtSummary] =
|
|
2861
|
-
const [, setUsageVersion] =
|
|
2862
|
-
const [modelName, setModelName] =
|
|
2863
|
-
const [pending, setPending] =
|
|
2864
|
-
const [spinnerTick, setSpinnerTick] =
|
|
2865
|
-
const [activity, setActivity] =
|
|
2866
|
-
const [bgLabels, setBgLabels] =
|
|
2867
|
-
const [permissionModeState, setPermissionModeState] =
|
|
2868
|
-
const [planApproval, setPlanApprovalState] =
|
|
2869
|
-
const [pendingRewind, setPendingRewindState] =
|
|
3154
|
+
const [initialDisplay] = useState3(() => buildDisplayMessages(initialMessages ?? []));
|
|
3155
|
+
const [messages, setMessages] = useState3(initialDisplay);
|
|
3156
|
+
const [epoch, setEpoch] = useState3(0);
|
|
3157
|
+
const [streamingText, setStreamingText] = useState3(null);
|
|
3158
|
+
const [isStreaming, setIsStreaming] = useState3(false);
|
|
3159
|
+
const [thinking, setThinking] = useState3(false);
|
|
3160
|
+
const [thinkingText, setThinkingText] = useState3("");
|
|
3161
|
+
const [thoughtSummary, setThoughtSummary] = useState3(null);
|
|
3162
|
+
const [, setUsageVersion] = useState3(0);
|
|
3163
|
+
const [modelName, setModelName] = useState3(model);
|
|
3164
|
+
const [pending, setPending] = useState3(null);
|
|
3165
|
+
const [spinnerTick, setSpinnerTick] = useState3(0);
|
|
3166
|
+
const [activity, setActivity] = useState3(null);
|
|
3167
|
+
const [bgLabels, setBgLabels] = useState3(() => runningTaskLabels());
|
|
3168
|
+
const [permissionModeState, setPermissionModeState] = useState3(permissionMode);
|
|
3169
|
+
const [planApproval, setPlanApprovalState] = useState3(false);
|
|
3170
|
+
const [pendingRewind, setPendingRewindState] = useState3(null);
|
|
3171
|
+
const [picker, setPickerState] = useState3(null);
|
|
2870
3172
|
const backendRef = useRef2(backend);
|
|
2871
3173
|
const nextIdRef = useRef2(initialDisplay.length);
|
|
2872
3174
|
const abortRef = useRef2(null);
|
|
@@ -2881,21 +3183,23 @@ function Repl({
|
|
|
2881
3183
|
const prevModeRef = useRef2(null);
|
|
2882
3184
|
const planApprovalRef = useRef2(false);
|
|
2883
3185
|
const pendingRewindRef = useRef2(null);
|
|
3186
|
+
const pickerRef = useRef2(null);
|
|
2884
3187
|
const modelNameRef = useRef2(model);
|
|
2885
3188
|
const sessionStoreRef = useRef2(sessionStore);
|
|
2886
3189
|
const usageRef = useRef2(initialUsage ? { ...initialUsage } : emptyUsage());
|
|
2887
3190
|
const turnChunksRef = useRef2(0);
|
|
2888
3191
|
const messagesRef = useRef2(initialDisplay);
|
|
2889
|
-
const [queueItems, setQueueItems] =
|
|
3192
|
+
const [queueItems, setQueueItems] = useState3([]);
|
|
2890
3193
|
const queueRef = useRef2(new PromptQueue());
|
|
2891
|
-
const [pendingImages, setPendingImages] =
|
|
3194
|
+
const [pendingImages, setPendingImages] = useState3([]);
|
|
2892
3195
|
const pendingImagesRef = useRef2([]);
|
|
2893
3196
|
const imageSeqRef = useRef2(1);
|
|
2894
|
-
const [inputRefill, setInputRefill] =
|
|
3197
|
+
const [inputRefill, setInputRefill] = useState3();
|
|
2895
3198
|
const refillSeqRef = useRef2(0);
|
|
2896
3199
|
const lastEscRef = useRef2(null);
|
|
2897
|
-
const [gitBranch, setGitBranch] =
|
|
2898
|
-
const [contextPercent, setContextPercent] =
|
|
3200
|
+
const [gitBranch, setGitBranch] = useState3(null);
|
|
3201
|
+
const [contextPercent, setContextPercent] = useState3(null);
|
|
3202
|
+
const [initialHistory] = useState3(() => loadHistory());
|
|
2899
3203
|
const budgetWarnedRef = useRef2(false);
|
|
2900
3204
|
const budgetExceededRef = useRef2(false);
|
|
2901
3205
|
const runStreamRef = useRef2(
|
|
@@ -2945,6 +3249,34 @@ function Repl({
|
|
|
2945
3249
|
pendingRewindRef.current = p;
|
|
2946
3250
|
setPendingRewindState(p);
|
|
2947
3251
|
}, []);
|
|
3252
|
+
const setPicker = useCallback((p) => {
|
|
3253
|
+
pickerRef.current = p;
|
|
3254
|
+
setPickerState(p);
|
|
3255
|
+
}, []);
|
|
3256
|
+
const showPicker = useCallback(
|
|
3257
|
+
(title, options) => {
|
|
3258
|
+
if (pickerRef.current) return Promise.resolve(null);
|
|
3259
|
+
return new Promise((resolve) => {
|
|
3260
|
+
setPicker({ title, options, resolve });
|
|
3261
|
+
});
|
|
3262
|
+
},
|
|
3263
|
+
[setPicker]
|
|
3264
|
+
);
|
|
3265
|
+
const handlePickerSelect = useCallback(
|
|
3266
|
+
(value) => {
|
|
3267
|
+
const p = pickerRef.current;
|
|
3268
|
+
if (!p) return;
|
|
3269
|
+
setPicker(null);
|
|
3270
|
+
p.resolve(value);
|
|
3271
|
+
},
|
|
3272
|
+
[setPicker]
|
|
3273
|
+
);
|
|
3274
|
+
const handlePickerCancel = useCallback(() => {
|
|
3275
|
+
const p = pickerRef.current;
|
|
3276
|
+
if (!p) return;
|
|
3277
|
+
setPicker(null);
|
|
3278
|
+
p.resolve(null);
|
|
3279
|
+
}, [setPicker]);
|
|
2948
3280
|
const stageClipboardImage = useCallback((img) => {
|
|
2949
3281
|
pendingImagesRef.current = [
|
|
2950
3282
|
...pendingImagesRef.current,
|
|
@@ -3085,9 +3417,9 @@ function Repl({
|
|
|
3085
3417
|
}
|
|
3086
3418
|
exit();
|
|
3087
3419
|
}, [exit, pushMessage]);
|
|
3088
|
-
|
|
3420
|
+
useInput6((_input, key) => {
|
|
3089
3421
|
if (key.escape) {
|
|
3090
|
-
const busy = abortRef.current !== null || pendingRef.current !== null || planApprovalRef.current || pendingRewindRef.current !== null;
|
|
3422
|
+
const busy = abortRef.current !== null || pendingRef.current !== null || planApprovalRef.current || pendingRewindRef.current !== null || pickerRef.current !== null;
|
|
3091
3423
|
if (busy) {
|
|
3092
3424
|
lastEscRef.current = null;
|
|
3093
3425
|
interrupt();
|
|
@@ -3104,7 +3436,7 @@ function Repl({
|
|
|
3104
3436
|
}
|
|
3105
3437
|
lastEscRef.current = null;
|
|
3106
3438
|
if (key.shift && key.tab) {
|
|
3107
|
-
if (abortRef.current || pendingRef.current || planApprovalRef.current || pendingRewindRef.current)
|
|
3439
|
+
if (abortRef.current || pendingRef.current || planApprovalRef.current || pendingRewindRef.current || pickerRef.current)
|
|
3108
3440
|
return;
|
|
3109
3441
|
const current = config.permissionMode;
|
|
3110
3442
|
const index = SESSION_MODE_CYCLE.indexOf(current);
|
|
@@ -3461,6 +3793,97 @@ ${lines.join("\n")}`;
|
|
|
3461
3793
|
return formatSessionEntries(entries, { showCwd: all });
|
|
3462
3794
|
},
|
|
3463
3795
|
resumeSession: resume,
|
|
3796
|
+
pickModel: async () => {
|
|
3797
|
+
const models = listModels(config);
|
|
3798
|
+
if (models.length === 0) return "No models configured.";
|
|
3799
|
+
const options = models.map((m) => {
|
|
3800
|
+
const parts = [`${m.provider}/${m.model}`, `${contextWindowTokens(config, m.name)} ctx`];
|
|
3801
|
+
if (m.promptPrice !== void 0 && m.completionPrice !== void 0) {
|
|
3802
|
+
parts.push(`$${m.promptPrice}/$${m.completionPrice} per 1M tokens`);
|
|
3803
|
+
}
|
|
3804
|
+
return {
|
|
3805
|
+
value: m.name,
|
|
3806
|
+
label: m.name,
|
|
3807
|
+
description: parts.join(" \xB7 "),
|
|
3808
|
+
hint: m.name === modelNameRef.current ? "current" : void 0
|
|
3809
|
+
};
|
|
3810
|
+
});
|
|
3811
|
+
const picked = await showPicker("Select a model", options);
|
|
3812
|
+
if (!picked || picked === modelNameRef.current) {
|
|
3813
|
+
return `Model unchanged \u2014 still "${modelNameRef.current}".`;
|
|
3814
|
+
}
|
|
3815
|
+
return switchModel(picked);
|
|
3816
|
+
},
|
|
3817
|
+
pickPermissionMode: async () => {
|
|
3818
|
+
const current = config.permissionMode;
|
|
3819
|
+
const descriptions = {
|
|
3820
|
+
ask: "reads allowed; writes/exec ask for confirmation",
|
|
3821
|
+
auto: "everything allowed except hard-denied dangerous commands/paths",
|
|
3822
|
+
readonly: "read-only; all writes/exec denied",
|
|
3823
|
+
yolo: "allow everything, never ask (disables ALL safety checks)",
|
|
3824
|
+
plan: "read-only research, then approve a plan before executing (this session only)"
|
|
3825
|
+
};
|
|
3826
|
+
const options = ["ask", "auto", "readonly", "yolo", "plan"].map((m) => ({
|
|
3827
|
+
value: m,
|
|
3828
|
+
label: m,
|
|
3829
|
+
description: descriptions[m],
|
|
3830
|
+
hint: m === current ? "current" : void 0
|
|
3831
|
+
}));
|
|
3832
|
+
const picked = await showPicker("Select a permission mode", options);
|
|
3833
|
+
if (!picked || picked === current) {
|
|
3834
|
+
return `Permission mode unchanged \u2014 still ${current}.`;
|
|
3835
|
+
}
|
|
3836
|
+
if (picked === "plan") return ctx.planMode();
|
|
3837
|
+
return ctx.permissionMode(picked);
|
|
3838
|
+
},
|
|
3839
|
+
pickSession: async (all) => {
|
|
3840
|
+
const entries = (await listSessionEntries(all ? void 0 : cwd)).slice(
|
|
3841
|
+
0,
|
|
3842
|
+
SESSION_PICKER_CAP
|
|
3843
|
+
);
|
|
3844
|
+
if (entries.length === 0) {
|
|
3845
|
+
return all ? "No sessions found." : "No sessions found for this directory.";
|
|
3846
|
+
}
|
|
3847
|
+
const options = await Promise.all(
|
|
3848
|
+
entries.map(async ({ meta, messageCount }) => {
|
|
3849
|
+
const parts = [`${messageCount} messages \xB7 ${relativeTime(meta.updatedAt)}`];
|
|
3850
|
+
const preview = await sessionPreview(meta.id);
|
|
3851
|
+
if (preview) parts.push(preview);
|
|
3852
|
+
if (all) parts.push(`[${meta.cwd}]`);
|
|
3853
|
+
return {
|
|
3854
|
+
value: meta.id,
|
|
3855
|
+
label: meta.title || shortSessionId(meta.id),
|
|
3856
|
+
description: parts.join(" \xB7 "),
|
|
3857
|
+
hint: meta.id === sessionStoreRef.current?.id ? "current" : void 0
|
|
3858
|
+
};
|
|
3859
|
+
})
|
|
3860
|
+
);
|
|
3861
|
+
const picked = await showPicker(
|
|
3862
|
+
all ? "Resume a session (all directories)" : "Resume a session",
|
|
3863
|
+
options
|
|
3864
|
+
);
|
|
3865
|
+
if (!picked) return "Session unchanged \u2014 picker cancelled.";
|
|
3866
|
+
return resume(picked);
|
|
3867
|
+
},
|
|
3868
|
+
forkSession: async () => {
|
|
3869
|
+
const current = backendRef.current;
|
|
3870
|
+
const store = sessionStoreRef.current;
|
|
3871
|
+
if (!(current instanceof AgentLoop) || !store) {
|
|
3872
|
+
return "Nothing to fork \u2014 no active session.";
|
|
3873
|
+
}
|
|
3874
|
+
const messages2 = await store.messages();
|
|
3875
|
+
if (messages2.length === 0) {
|
|
3876
|
+
return "Nothing to fork \u2014 the current session has no messages yet.";
|
|
3877
|
+
}
|
|
3878
|
+
const fork = await SessionStore.create(cwd, modelNameRef.current);
|
|
3879
|
+
await fork.replaceMessages(messages2);
|
|
3880
|
+
const oldMeta = await store.meta();
|
|
3881
|
+
const title = `Fork of ${oldMeta.title || shortSessionId(oldMeta.id)}`;
|
|
3882
|
+
await fork.setTitle(title);
|
|
3883
|
+
current.setSessionStore(fork);
|
|
3884
|
+
sessionStoreRef.current = fork;
|
|
3885
|
+
return `Forked into new session ${fork.id} ("${title}") and switched to it. Checkpoints and rewind history do not carry over.`;
|
|
3886
|
+
},
|
|
3464
3887
|
newSession: async () => {
|
|
3465
3888
|
const current = backendRef.current;
|
|
3466
3889
|
if (!(current instanceof AgentLoop)) {
|
|
@@ -3664,7 +4087,8 @@ ${plan.affected.length} file change(s) will be reverted, ${messageCount} message
|
|
|
3664
4087
|
applyMessages,
|
|
3665
4088
|
redrawMessages,
|
|
3666
4089
|
applySessionMode,
|
|
3667
|
-
setPendingRewind
|
|
4090
|
+
setPendingRewind,
|
|
4091
|
+
showPicker
|
|
3668
4092
|
]);
|
|
3669
4093
|
const runShellBang = useCallback(
|
|
3670
4094
|
async (raw) => {
|
|
@@ -3704,6 +4128,7 @@ ${plan.affected.length} file change(s) will be reverted, ${messageCount} message
|
|
|
3704
4128
|
);
|
|
3705
4129
|
const handleSubmit = useCallback(
|
|
3706
4130
|
(text) => {
|
|
4131
|
+
appendHistory(text);
|
|
3707
4132
|
if (text.startsWith("!")) {
|
|
3708
4133
|
if (abortRef.current) {
|
|
3709
4134
|
queueRef.current.enqueue({ text, images: [] });
|
|
@@ -3718,7 +4143,11 @@ ${plan.affected.length} file change(s) will be reverted, ${messageCount} message
|
|
|
3718
4143
|
if (!parsed) return;
|
|
3719
4144
|
const command = registry.get(parsed.name);
|
|
3720
4145
|
if (!command) {
|
|
3721
|
-
|
|
4146
|
+
const suggestions = registry.complete(parsed.name).map((cmd) => cmd.name);
|
|
4147
|
+
pushMessage(
|
|
4148
|
+
"system",
|
|
4149
|
+
`Unknown command: /${parsed.name} (try /help)${didYouMeanSuffix(suggestions)}`
|
|
4150
|
+
);
|
|
3722
4151
|
return;
|
|
3723
4152
|
}
|
|
3724
4153
|
void command.run(parsed.args, registry.ctx);
|
|
@@ -3744,7 +4173,11 @@ ${plan.affected.length} file change(s) will be reverted, ${messageCount} message
|
|
|
3744
4173
|
[registry, pushMessage, runStream, runShellBang, config, clearPendingImages, sessionCostUsd]
|
|
3745
4174
|
);
|
|
3746
4175
|
const commandHints = useMemo(
|
|
3747
|
-
() => registry.list().map((cmd) => ({
|
|
4176
|
+
() => registry.list().map((cmd) => ({
|
|
4177
|
+
name: cmd.name,
|
|
4178
|
+
description: cmd.description,
|
|
4179
|
+
usage: cmd.usage ?? `/${cmd.name}`
|
|
4180
|
+
})),
|
|
3748
4181
|
[registry]
|
|
3749
4182
|
);
|
|
3750
4183
|
useEffect2(() => {
|
|
@@ -3759,9 +4192,9 @@ ${plan.affected.length} file change(s) will be reverted, ${messageCount} message
|
|
|
3759
4192
|
}
|
|
3760
4193
|
}, [cwd, config, isStreaming, epoch]);
|
|
3761
4194
|
const sessionCost = sessionCostUsd();
|
|
3762
|
-
return /* @__PURE__ */
|
|
3763
|
-
/* @__PURE__ */
|
|
3764
|
-
(thinking || activity !== null) && /* @__PURE__ */
|
|
4195
|
+
return /* @__PURE__ */ jsxs11(Box10, { flexDirection: "column", children: [
|
|
4196
|
+
/* @__PURE__ */ jsx9(MessageList, { messages }, `messages-${epoch}`),
|
|
4197
|
+
(thinking || activity !== null) && /* @__PURE__ */ jsx9(
|
|
3765
4198
|
ThinkingIndicator,
|
|
3766
4199
|
{
|
|
3767
4200
|
reasoning: thinkingText,
|
|
@@ -3769,9 +4202,9 @@ ${plan.affected.length} file change(s) will be reverted, ${messageCount} message
|
|
|
3769
4202
|
activity: activity ?? void 0
|
|
3770
4203
|
}
|
|
3771
4204
|
),
|
|
3772
|
-
!thinking && activity === null && thoughtSummary !== null && /* @__PURE__ */
|
|
3773
|
-
streamingText !== null && (streamingText !== "" || turnChunksRef.current === 0) && /* @__PURE__ */
|
|
3774
|
-
pending && /* @__PURE__ */
|
|
4205
|
+
!thinking && activity === null && thoughtSummary !== null && /* @__PURE__ */ jsx9(Text11, { dimColor: true, children: thoughtSummary }),
|
|
4206
|
+
streamingText !== null && (streamingText !== "" || turnChunksRef.current === 0) && /* @__PURE__ */ jsx9(StreamingMessage, { text: streamingText, continuation: turnChunksRef.current > 0 }),
|
|
4207
|
+
pending && /* @__PURE__ */ jsx9(
|
|
3775
4208
|
PermissionPrompt,
|
|
3776
4209
|
{
|
|
3777
4210
|
request: pending.request,
|
|
@@ -3779,32 +4212,42 @@ ${plan.affected.length} file change(s) will be reverted, ${messageCount} message
|
|
|
3779
4212
|
onDecision: handleDecision
|
|
3780
4213
|
}
|
|
3781
4214
|
),
|
|
3782
|
-
planApproval && /* @__PURE__ */
|
|
3783
|
-
pendingRewind && /* @__PURE__ */
|
|
3784
|
-
|
|
4215
|
+
planApproval && /* @__PURE__ */ jsx9(ExitPlanPrompt, { onDecision: handlePlanDecision }),
|
|
4216
|
+
pendingRewind && /* @__PURE__ */ jsx9(RewindConfirmPrompt, { summary: pendingRewind.summary, onDecision: handleRewindDecision }),
|
|
4217
|
+
picker && /* @__PURE__ */ jsx9(
|
|
4218
|
+
SelectPrompt,
|
|
4219
|
+
{
|
|
4220
|
+
title: picker.title,
|
|
4221
|
+
options: picker.options,
|
|
4222
|
+
onSelect: handlePickerSelect,
|
|
4223
|
+
onCancel: handlePickerCancel
|
|
4224
|
+
}
|
|
4225
|
+
),
|
|
4226
|
+
queueItems.map((item) => /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
|
|
3785
4227
|
"queued: ",
|
|
3786
4228
|
item.text.length > 80 ? `${item.text.slice(0, 80)}\u2026` : item.text
|
|
3787
4229
|
] }, item.id)),
|
|
3788
|
-
pendingImages.map((staged) => /* @__PURE__ */
|
|
4230
|
+
pendingImages.map((staged) => /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
|
|
3789
4231
|
"[image attached: ",
|
|
3790
4232
|
staged.image.path,
|
|
3791
4233
|
"]"
|
|
3792
4234
|
] }, staged.id)),
|
|
3793
|
-
/* @__PURE__ */
|
|
4235
|
+
/* @__PURE__ */ jsx9(
|
|
3794
4236
|
InputBox,
|
|
3795
4237
|
{
|
|
3796
4238
|
isStreaming,
|
|
3797
|
-
disabled: pending !== null || planApproval || pendingRewind !== null,
|
|
4239
|
+
disabled: pending !== null || planApproval || pendingRewind !== null || picker !== null,
|
|
3798
4240
|
commands: commandHints,
|
|
3799
4241
|
cwd,
|
|
3800
4242
|
refill: inputRefill,
|
|
4243
|
+
initialHistory,
|
|
3801
4244
|
onSubmit: handleSubmit,
|
|
3802
4245
|
onInterrupt: interrupt,
|
|
3803
4246
|
onExit: handleExit,
|
|
3804
4247
|
onPasteImage: handlePasteImage
|
|
3805
4248
|
}
|
|
3806
4249
|
),
|
|
3807
|
-
/* @__PURE__ */
|
|
4250
|
+
/* @__PURE__ */ jsx9(
|
|
3808
4251
|
StatusBar,
|
|
3809
4252
|
{
|
|
3810
4253
|
cwd,
|
|
@@ -3824,7 +4267,7 @@ function renderRepl(backend, opts) {
|
|
|
3824
4267
|
defaultTaskManager.cleanup();
|
|
3825
4268
|
});
|
|
3826
4269
|
return render(
|
|
3827
|
-
/* @__PURE__ */
|
|
4270
|
+
/* @__PURE__ */ jsx9(
|
|
3828
4271
|
Repl,
|
|
3829
4272
|
{
|
|
3830
4273
|
backend,
|
|
@@ -3841,7 +4284,7 @@ function renderRepl(backend, opts) {
|
|
|
3841
4284
|
}
|
|
3842
4285
|
|
|
3843
4286
|
// src/config/loader.ts
|
|
3844
|
-
import
|
|
4287
|
+
import fs11 from "fs";
|
|
3845
4288
|
import { parse as parse2 } from "smol-toml";
|
|
3846
4289
|
var PartialConfigSchema = ConfigSchema.partial();
|
|
3847
4290
|
function parseTomlFile(content, filePath) {
|
|
@@ -3869,8 +4312,8 @@ function applyOverrides(config, overrides) {
|
|
|
3869
4312
|
return merged;
|
|
3870
4313
|
}
|
|
3871
4314
|
function readTomlFileSync(filePath) {
|
|
3872
|
-
if (!
|
|
3873
|
-
const content =
|
|
4315
|
+
if (!fs11.existsSync(filePath)) return {};
|
|
4316
|
+
const content = fs11.readFileSync(filePath, "utf8");
|
|
3874
4317
|
return parseTomlFile(content, filePath);
|
|
3875
4318
|
}
|
|
3876
4319
|
function loadConfigSync(cwd, overrides) {
|