ctx-switch 2.0.6 → 2.0.7
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/index.mjs +142 -22
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -32,6 +32,8 @@ function parseArgs(argv) {
|
|
|
32
32
|
refine: false,
|
|
33
33
|
help: false,
|
|
34
34
|
version: false,
|
|
35
|
+
pickUser: false,
|
|
36
|
+
fromUser: null,
|
|
35
37
|
session: null,
|
|
36
38
|
model: null,
|
|
37
39
|
provider: "openrouter",
|
|
@@ -59,6 +61,12 @@ function parseArgs(argv) {
|
|
|
59
61
|
case "--refine":
|
|
60
62
|
options.refine = true;
|
|
61
63
|
break;
|
|
64
|
+
case "--pick-user":
|
|
65
|
+
options.pickUser = true;
|
|
66
|
+
break;
|
|
67
|
+
case "--from-user":
|
|
68
|
+
options.fromUser = Number(requireValue(arg, args));
|
|
69
|
+
break;
|
|
62
70
|
case "--session":
|
|
63
71
|
options.session = requireValue(arg, args);
|
|
64
72
|
break;
|
|
@@ -122,6 +130,9 @@ function parseArgs(argv) {
|
|
|
122
130
|
if (!Number.isInteger(options.limit) || options.limit <= 0) {
|
|
123
131
|
throw new Error(`Invalid limit "${options.limit}". Expected a positive integer.`);
|
|
124
132
|
}
|
|
133
|
+
if (options.fromUser !== null && (!Number.isInteger(options.fromUser) || options.fromUser <= 0)) {
|
|
134
|
+
throw new Error(`Invalid --from-user value "${options.fromUser}". Expected a positive integer.`);
|
|
135
|
+
}
|
|
125
136
|
return options;
|
|
126
137
|
}
|
|
127
138
|
function getHelpText({ name, version }) {
|
|
@@ -142,6 +153,8 @@ function getHelpText({ name, version }) {
|
|
|
142
153
|
" -o, --output <file> Write the final prompt to a file",
|
|
143
154
|
" --source <name> Session source: claude, codex, opencode (interactive if omitted)",
|
|
144
155
|
" --session <id|path> Use a specific session file or session id",
|
|
156
|
+
" --pick-user Interactively choose the starting user prompt for preserved context",
|
|
157
|
+
" --from-user <n> Start preserved context from the nth substantive user prompt",
|
|
145
158
|
" --target <name> Prompt target: generic, claude, codex, cursor, chatgpt",
|
|
146
159
|
" -n, --limit <count> Limit rows for the sessions command (default: 10)",
|
|
147
160
|
"",
|
|
@@ -163,6 +176,8 @@ function getHelpText({ name, version }) {
|
|
|
163
176
|
` ${name} --source codex --target claude`,
|
|
164
177
|
` ${name} --source codex --target codex`,
|
|
165
178
|
` ${name} --source opencode`,
|
|
179
|
+
` ${name} --pick-user`,
|
|
180
|
+
` ${name} --from-user 2`,
|
|
166
181
|
` ${name} --refine --model openrouter/free`,
|
|
167
182
|
` ${name} --output ./handoff.md`,
|
|
168
183
|
` ${name} doctor`,
|
|
@@ -1322,6 +1337,10 @@ function compactText(text, maxChars = 800) {
|
|
|
1322
1337
|
function unique(list) {
|
|
1323
1338
|
return [...new Set(list.filter(Boolean))];
|
|
1324
1339
|
}
|
|
1340
|
+
function isLocalCommandMarkup(text) {
|
|
1341
|
+
const trimmed = text.trim().toLowerCase();
|
|
1342
|
+
return trimmed.includes("<local-command-caveat>") || trimmed.includes("<command-name>") || trimmed.includes("<command-message>") || trimmed.includes("<command-args>") || trimmed.includes("<local-command-stdout>");
|
|
1343
|
+
}
|
|
1325
1344
|
function extractFilePath2(input) {
|
|
1326
1345
|
const value = input.file_path || input.path || input.target_file || input.filePath;
|
|
1327
1346
|
return typeof value === "string" ? value : null;
|
|
@@ -1330,6 +1349,9 @@ function extractCommand2(input) {
|
|
|
1330
1349
|
const value = input.command || input.cmd;
|
|
1331
1350
|
return typeof value === "string" ? value : null;
|
|
1332
1351
|
}
|
|
1352
|
+
function getSubstantiveUserIndexes(messages) {
|
|
1353
|
+
return messages.map((message, index) => ({ message, index })).filter(({ message }) => message.role === "user" && message.content && !isNoiseMessage(message.content)).map(({ index }) => index);
|
|
1354
|
+
}
|
|
1333
1355
|
function buildTargetGuidance(target) {
|
|
1334
1356
|
switch (target) {
|
|
1335
1357
|
case "claude":
|
|
@@ -1346,6 +1368,7 @@ function buildTargetGuidance(target) {
|
|
|
1346
1368
|
}
|
|
1347
1369
|
function isNoiseMessage(text) {
|
|
1348
1370
|
const trimmed = text.trim().toLowerCase();
|
|
1371
|
+
if (isLocalCommandMarkup(trimmed)) return true;
|
|
1349
1372
|
const normalized = trimmed.replace(/[^\p{L}\p{N}\s]/gu, " ").replace(/\s+/g, " ").trim();
|
|
1350
1373
|
if (trimmed.length < 5) return true;
|
|
1351
1374
|
const noise = [
|
|
@@ -1376,6 +1399,12 @@ function isNoiseMessage(text) {
|
|
|
1376
1399
|
if (trimmed.startsWith("[request interrupted")) return true;
|
|
1377
1400
|
return false;
|
|
1378
1401
|
}
|
|
1402
|
+
function isAssistantNoiseMessage(text) {
|
|
1403
|
+
const trimmed = text.trim().toLowerCase();
|
|
1404
|
+
if (!trimmed) return true;
|
|
1405
|
+
if (isLocalCommandMarkup(trimmed)) return true;
|
|
1406
|
+
return trimmed.includes("you're out of extra usage") || trimmed.includes("resets 3:30pm") || trimmed.includes("rate limit") || trimmed.includes("login interrupted");
|
|
1407
|
+
}
|
|
1379
1408
|
function isReferentialMessage(text) {
|
|
1380
1409
|
const normalized = text.trim().toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, " ").replace(/\s+/g, " ").trim();
|
|
1381
1410
|
if (!normalized || normalized.length > 220) return false;
|
|
@@ -1386,11 +1415,11 @@ function isMetaQualityAssistantMessage(text) {
|
|
|
1386
1415
|
return /\b(handoff|prompt)\b/.test(lower) && /\b(good|bad|better|worse|quality)\b/.test(lower);
|
|
1387
1416
|
}
|
|
1388
1417
|
function filterUserMessages(messages) {
|
|
1389
|
-
const all = messages.filter((m) => m.role === "user" && m.content).map((m) => m.content.trim());
|
|
1418
|
+
const all = messages.filter((m) => m.role === "user" && m.content).map((m) => m.content.trim()).filter((msg) => !isNoiseMessage(msg));
|
|
1390
1419
|
if (all.length <= 2) return all;
|
|
1391
1420
|
const first = all[0];
|
|
1392
1421
|
const last = all[all.length - 1];
|
|
1393
|
-
const middle = all.slice(1, -1)
|
|
1422
|
+
const middle = all.slice(1, -1);
|
|
1394
1423
|
return [first, ...middle, last];
|
|
1395
1424
|
}
|
|
1396
1425
|
function extractUnresolvedErrors(messages) {
|
|
@@ -1423,11 +1452,11 @@ function extractKeyDecisions(messages) {
|
|
|
1423
1452
|
}
|
|
1424
1453
|
return decisions.slice(-5);
|
|
1425
1454
|
}
|
|
1426
|
-
function
|
|
1455
|
+
function findFocusedWindowFrom(messages, fromUserMessage) {
|
|
1427
1456
|
if (messages.length === 0) {
|
|
1428
1457
|
return { messages, sessionAppearsComplete: false };
|
|
1429
1458
|
}
|
|
1430
|
-
const substantiveUserIndexes = messages
|
|
1459
|
+
const substantiveUserIndexes = getSubstantiveUserIndexes(messages);
|
|
1431
1460
|
if (substantiveUserIndexes.length === 0) {
|
|
1432
1461
|
return { messages, sessionAppearsComplete: false };
|
|
1433
1462
|
}
|
|
@@ -1437,7 +1466,9 @@ function findFocusedWindow(messages) {
|
|
|
1437
1466
|
);
|
|
1438
1467
|
const postToolUsers = substantiveUserIndexes.filter((index) => index > lastToolIndex);
|
|
1439
1468
|
let startIndex = 0;
|
|
1440
|
-
if (
|
|
1469
|
+
if (typeof fromUserMessage === "number" && fromUserMessage > 0) {
|
|
1470
|
+
startIndex = substantiveUserIndexes[Math.min(fromUserMessage - 1, substantiveUserIndexes.length - 1)] ?? 0;
|
|
1471
|
+
} else if (postToolUsers.length > 0) {
|
|
1441
1472
|
startIndex = postToolUsers[0];
|
|
1442
1473
|
} else if (lastToolIndex >= 0) {
|
|
1443
1474
|
startIndex = substantiveUserIndexes.filter((index) => index <= lastToolIndex).at(-1) ?? 0;
|
|
@@ -1445,7 +1476,7 @@ function findFocusedWindow(messages) {
|
|
|
1445
1476
|
startIndex = substantiveUserIndexes.at(-1) ?? 0;
|
|
1446
1477
|
}
|
|
1447
1478
|
const startMessage = messages[startIndex];
|
|
1448
|
-
if (startMessage?.role === "user" && isReferentialMessage(startMessage.content)) {
|
|
1479
|
+
if ((fromUserMessage === null || typeof fromUserMessage === "undefined") && startMessage?.role === "user" && isReferentialMessage(startMessage.content)) {
|
|
1449
1480
|
const previousSubstantive = substantiveUserIndexes.filter((index) => index < startIndex).at(-1);
|
|
1450
1481
|
if (typeof previousSubstantive === "number") {
|
|
1451
1482
|
startIndex = previousSubstantive;
|
|
@@ -1453,10 +1484,25 @@ function findFocusedWindow(messages) {
|
|
|
1453
1484
|
}
|
|
1454
1485
|
const focused = messages.slice(startIndex);
|
|
1455
1486
|
const hasToolActivity = focused.some((message) => message.role === "assistant" && message.toolCalls.length > 0);
|
|
1456
|
-
const lastMessage = focused.
|
|
1487
|
+
const lastMessage = [...focused].reverse().find((message) => {
|
|
1488
|
+
if (!message.content.trim() && message.toolCalls.length === 0) return false;
|
|
1489
|
+
if (message.role === "assistant" && message.content.trim() && isAssistantNoiseMessage(message.content)) {
|
|
1490
|
+
return false;
|
|
1491
|
+
}
|
|
1492
|
+
if (message.role === "user" && message.content.trim() && isNoiseMessage(message.content)) {
|
|
1493
|
+
return false;
|
|
1494
|
+
}
|
|
1495
|
+
return true;
|
|
1496
|
+
});
|
|
1457
1497
|
const sessionAppearsComplete = Boolean(lastMessage) && lastMessage.role === "assistant" && lastMessage.toolCalls.length === 0 && !hasToolActivity;
|
|
1458
1498
|
return { messages: focused, sessionAppearsComplete };
|
|
1459
1499
|
}
|
|
1500
|
+
function listSubstantiveUserMessages(messages) {
|
|
1501
|
+
return getSubstantiveUserIndexes(messages).map((messageIndex, index) => ({
|
|
1502
|
+
index: index + 1,
|
|
1503
|
+
text: messages[messageIndex]?.content.trim() || ""
|
|
1504
|
+
}));
|
|
1505
|
+
}
|
|
1460
1506
|
function extractWorkSummary(messages) {
|
|
1461
1507
|
const filesModified = /* @__PURE__ */ new Set();
|
|
1462
1508
|
const commands = [];
|
|
@@ -1482,7 +1528,7 @@ function extractWorkSummary(messages) {
|
|
|
1482
1528
|
function extractLastAssistantAnswer(messages) {
|
|
1483
1529
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1484
1530
|
const message = messages[i];
|
|
1485
|
-
if (message.role === "assistant" && message.content.trim()) {
|
|
1531
|
+
if (message.role === "assistant" && message.content.trim() && !isAssistantNoiseMessage(message.content)) {
|
|
1486
1532
|
return compactText(message.content, 500);
|
|
1487
1533
|
}
|
|
1488
1534
|
}
|
|
@@ -1491,14 +1537,21 @@ function extractLastAssistantAnswer(messages) {
|
|
|
1491
1537
|
function summarizeToolCall(toolCall) {
|
|
1492
1538
|
const filePath = extractFilePath2(toolCall.input);
|
|
1493
1539
|
const command = extractCommand2(toolCall.input);
|
|
1540
|
+
const url = typeof toolCall.input.url === "string" ? toolCall.input.url : null;
|
|
1541
|
+
const query = typeof toolCall.input.query === "string" ? toolCall.input.query : null;
|
|
1542
|
+
const description = typeof toolCall.input.description === "string" ? toolCall.input.description : typeof toolCall.input.prompt === "string" ? toolCall.input.prompt : null;
|
|
1494
1543
|
if (filePath) return `${toolCall.tool} ${filePath}`;
|
|
1495
1544
|
if (command) return `${toolCall.tool}: ${summarizeCommand(command)}`;
|
|
1545
|
+
if (url) return `${toolCall.tool}: ${compactText(url, 120)}`;
|
|
1546
|
+
if (description) return `${toolCall.tool}: ${compactText(description, 120)}`;
|
|
1547
|
+
if (query) return `${toolCall.tool}: ${compactText(query, 120)}`;
|
|
1496
1548
|
return toolCall.tool;
|
|
1497
1549
|
}
|
|
1498
1550
|
function findLastActiveAssistant(messages) {
|
|
1499
1551
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1500
1552
|
const message = messages[i];
|
|
1501
1553
|
if (message.role !== "assistant") continue;
|
|
1554
|
+
if (message.content.trim() && isAssistantNoiseMessage(message.content)) continue;
|
|
1502
1555
|
if (message.content.trim() || message.toolCalls.length > 0) {
|
|
1503
1556
|
return message;
|
|
1504
1557
|
}
|
|
@@ -1526,7 +1579,8 @@ function buildRemainingWorkHints({
|
|
|
1526
1579
|
errors,
|
|
1527
1580
|
work,
|
|
1528
1581
|
focusFiles,
|
|
1529
|
-
recentCommands
|
|
1582
|
+
recentCommands,
|
|
1583
|
+
lastAssistantAnswer
|
|
1530
1584
|
}) {
|
|
1531
1585
|
if (sessionAppearsComplete) return [];
|
|
1532
1586
|
const hints = [];
|
|
@@ -1544,26 +1598,33 @@ function buildRemainingWorkHints({
|
|
|
1544
1598
|
if (focusFiles.length > 0) {
|
|
1545
1599
|
hints.push("Run `git diff --` on the active files to see the exact in-progress changes before editing further.");
|
|
1546
1600
|
}
|
|
1601
|
+
if (hints.length === 0 && lastAssistantAnswer) {
|
|
1602
|
+
hints.push("Continue from the last meaningful assistant answer above. This session appears to have stalled after planning or approval, not after code changes.");
|
|
1603
|
+
}
|
|
1547
1604
|
if (hints.length === 0) {
|
|
1548
1605
|
hints.push("Inspect the active files and run `git diff` to determine the next concrete implementation step.");
|
|
1549
1606
|
}
|
|
1550
1607
|
return hints;
|
|
1551
1608
|
}
|
|
1552
1609
|
function selectSessionHistoryMessages(focusedMessages, allMessages, sessionAppearsComplete) {
|
|
1553
|
-
|
|
1554
|
-
const hasAssistantActivity = focusedMessages.some(
|
|
1555
|
-
(message) => message.role === "assistant" && (message.content.trim() || message.toolCalls.length > 0)
|
|
1556
|
-
);
|
|
1557
|
-
if (focusedMessages.length >= 3 && hasAssistantActivity) return focusedMessages;
|
|
1558
|
-
const filtered = allMessages.filter((message) => {
|
|
1610
|
+
const sanitizeHistoryMessages = (messages) => messages.filter((message) => {
|
|
1559
1611
|
if (message.role === "assistant" && message.content.trim() && isMetaQualityAssistantMessage(message.content)) {
|
|
1560
1612
|
return false;
|
|
1561
1613
|
}
|
|
1614
|
+
if (message.role === "assistant" && message.content.trim() && isAssistantNoiseMessage(message.content)) {
|
|
1615
|
+
return false;
|
|
1616
|
+
}
|
|
1562
1617
|
if (message.role === "user" && message.content && isNoiseMessage(message.content)) {
|
|
1563
1618
|
return false;
|
|
1564
1619
|
}
|
|
1565
1620
|
return Boolean(message.content.trim()) || message.toolCalls.length > 0;
|
|
1566
1621
|
});
|
|
1622
|
+
if (sessionAppearsComplete) return sanitizeHistoryMessages(focusedMessages);
|
|
1623
|
+
const hasAssistantActivity = focusedMessages.some(
|
|
1624
|
+
(message) => message.role === "assistant" && (message.content.trim() || message.toolCalls.length > 0)
|
|
1625
|
+
);
|
|
1626
|
+
if (focusedMessages.length >= 3 && hasAssistantActivity) return sanitizeHistoryMessages(focusedMessages);
|
|
1627
|
+
const filtered = sanitizeHistoryMessages(allMessages);
|
|
1567
1628
|
return filtered.slice(-8);
|
|
1568
1629
|
}
|
|
1569
1630
|
function buildSessionHistory(focusedMessages, allMessages, sessionAppearsComplete) {
|
|
@@ -1571,7 +1632,7 @@ function buildSessionHistory(focusedMessages, allMessages, sessionAppearsComplet
|
|
|
1571
1632
|
const entries = historyMessages.map((message) => {
|
|
1572
1633
|
const parts = [];
|
|
1573
1634
|
if (message.content.trim()) {
|
|
1574
|
-
if (message.role === "assistant" && isMetaQualityAssistantMessage(message.content)) {
|
|
1635
|
+
if (message.role === "assistant" && (isMetaQualityAssistantMessage(message.content) || isAssistantNoiseMessage(message.content))) {
|
|
1575
1636
|
return null;
|
|
1576
1637
|
}
|
|
1577
1638
|
parts.push(compactText(message.content, 220));
|
|
@@ -1610,7 +1671,7 @@ function extractFocusFiles(ctx, work) {
|
|
|
1610
1671
|
]).slice(0, 6);
|
|
1611
1672
|
}
|
|
1612
1673
|
function buildRawPrompt(ctx, options = {}) {
|
|
1613
|
-
const focused =
|
|
1674
|
+
const focused = findFocusedWindowFrom(ctx.messages, options.fromUserMessage);
|
|
1614
1675
|
const userMessages = filterUserMessages(focused.messages);
|
|
1615
1676
|
const errors = extractUnresolvedErrors(focused.messages);
|
|
1616
1677
|
const decisions = extractKeyDecisions(focused.messages);
|
|
@@ -1624,7 +1685,8 @@ function buildRawPrompt(ctx, options = {}) {
|
|
|
1624
1685
|
errors,
|
|
1625
1686
|
work,
|
|
1626
1687
|
focusFiles,
|
|
1627
|
-
recentCommands
|
|
1688
|
+
recentCommands,
|
|
1689
|
+
lastAssistantAnswer
|
|
1628
1690
|
});
|
|
1629
1691
|
const sessionHistory = buildSessionHistory(focused.messages, ctx.messages, focused.sessionAppearsComplete);
|
|
1630
1692
|
let prompt = "";
|
|
@@ -2128,6 +2190,9 @@ function formatRunSummary({
|
|
|
2128
2190
|
lines.push(` Messages: ${ctx.messages.length}`);
|
|
2129
2191
|
lines.push(` Files: ${ctx.filesModified.length} modified, ${ctx.filesRead.length} read`);
|
|
2130
2192
|
lines.push(` Git: ${summarizeGitContext(ctx.gitContext)}`);
|
|
2193
|
+
if (options.fromUser) {
|
|
2194
|
+
lines.push(` Focus: user prompt #${options.fromUser}`);
|
|
2195
|
+
}
|
|
2131
2196
|
lines.push(` Target: ${options.target}`);
|
|
2132
2197
|
lines.push(` Mode: ${mode === "raw" ? "raw" : `refined via ${options.provider}${model ? ` (${model})` : ""}`}`);
|
|
2133
2198
|
return lines.join("\n");
|
|
@@ -2279,9 +2344,50 @@ async function promptForSource() {
|
|
|
2279
2344
|
});
|
|
2280
2345
|
});
|
|
2281
2346
|
}
|
|
2347
|
+
function summarizePromptChoice(text) {
|
|
2348
|
+
return text.replace(/\s+/g, " ").trim().slice(0, 100);
|
|
2349
|
+
}
|
|
2350
|
+
async function promptForUserStart(messages) {
|
|
2351
|
+
const userPrompts = listSubstantiveUserMessages(messages);
|
|
2352
|
+
if (userPrompts.length === 0) {
|
|
2353
|
+
return null;
|
|
2354
|
+
}
|
|
2355
|
+
if (userPrompts.length === 1) {
|
|
2356
|
+
process.stderr.write("\nOnly one substantive user prompt was found. Using it automatically.\n");
|
|
2357
|
+
return 1;
|
|
2358
|
+
}
|
|
2359
|
+
const rl = readline.createInterface({
|
|
2360
|
+
input: process.stdin,
|
|
2361
|
+
output: process.stderr
|
|
2362
|
+
});
|
|
2363
|
+
process.stderr.write("\nSelect the user prompt to preserve context from:\n\n");
|
|
2364
|
+
for (const prompt of userPrompts) {
|
|
2365
|
+
process.stderr.write(` ${prompt.index}) ${summarizePromptChoice(prompt.text)}
|
|
2366
|
+
`);
|
|
2367
|
+
}
|
|
2368
|
+
process.stderr.write("\n");
|
|
2369
|
+
return new Promise((resolve) => {
|
|
2370
|
+
rl.question(`Enter choice (1-${userPrompts.length}): `, (answer) => {
|
|
2371
|
+
rl.close();
|
|
2372
|
+
const trimmed = answer.trim();
|
|
2373
|
+
if (!trimmed) {
|
|
2374
|
+
process.stderr.write("Invalid choice, using automatic focus.\n");
|
|
2375
|
+
resolve(null);
|
|
2376
|
+
return;
|
|
2377
|
+
}
|
|
2378
|
+
const idx = Number.parseInt(trimmed, 10);
|
|
2379
|
+
if (idx >= 1 && idx <= userPrompts.length) {
|
|
2380
|
+
resolve(idx);
|
|
2381
|
+
} else {
|
|
2382
|
+
process.stderr.write("Invalid choice, using automatic focus.\n");
|
|
2383
|
+
resolve(null);
|
|
2384
|
+
}
|
|
2385
|
+
});
|
|
2386
|
+
});
|
|
2387
|
+
}
|
|
2282
2388
|
async function main(argv = process.argv.slice(2)) {
|
|
2283
2389
|
const options = parseArgs(argv);
|
|
2284
|
-
const pkgInfo = { name: "ctx-switch", version: "2.0.
|
|
2390
|
+
const pkgInfo = { name: "ctx-switch", version: "2.0.7" };
|
|
2285
2391
|
const ui = createTheme(process.stderr);
|
|
2286
2392
|
if (options.help) {
|
|
2287
2393
|
process.stdout.write(`${getHelpText(pkgInfo)}
|
|
@@ -2344,6 +2450,20 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2344
2450
|
if (messages.length === 0) {
|
|
2345
2451
|
fail(`Parsed zero usable messages from ${sessionPath}`);
|
|
2346
2452
|
}
|
|
2453
|
+
let fromUserMessage = options.fromUser;
|
|
2454
|
+
const userPrompts = listSubstantiveUserMessages(messages);
|
|
2455
|
+
if (fromUserMessage !== null && fromUserMessage > userPrompts.length) {
|
|
2456
|
+
fail(
|
|
2457
|
+
`Requested --from-user ${fromUserMessage}, but only ${userPrompts.length} substantive user prompt(s) were found.`,
|
|
2458
|
+
{
|
|
2459
|
+
suggestions: ["Run `ctx-switch --pick-user` to choose interactively.", "Omit `--from-user` to use automatic focus."]
|
|
2460
|
+
}
|
|
2461
|
+
);
|
|
2462
|
+
}
|
|
2463
|
+
if (options.pickUser && process.stdin.isTTY) {
|
|
2464
|
+
ui.step("Choosing preserved context start");
|
|
2465
|
+
fromUserMessage = await promptForUserStart(messages);
|
|
2466
|
+
}
|
|
2347
2467
|
ui.step("Capturing git context");
|
|
2348
2468
|
const gitContext = getGitContext(cwd);
|
|
2349
2469
|
const ctx = buildSessionContext({
|
|
@@ -2358,7 +2478,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2358
2478
|
let activeModel = null;
|
|
2359
2479
|
if (!options.refine) {
|
|
2360
2480
|
ui.step("Building continuation prompt");
|
|
2361
|
-
finalPrompt = buildRawPrompt(ctx, { target: options.target });
|
|
2481
|
+
finalPrompt = buildRawPrompt(ctx, { target: options.target, fromUserMessage });
|
|
2362
2482
|
} else {
|
|
2363
2483
|
const provider = options.provider;
|
|
2364
2484
|
let apiKey = getApiKey({
|
|
@@ -2396,7 +2516,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2396
2516
|
apiKey,
|
|
2397
2517
|
model,
|
|
2398
2518
|
systemPrompt: buildRefinementSystemPrompt(options.target),
|
|
2399
|
-
userPrompt: buildRefinementDump(ctx, { target: options.target }),
|
|
2519
|
+
userPrompt: buildRefinementDump(ctx, { target: options.target, fromUserMessage }),
|
|
2400
2520
|
timeoutMs: 0,
|
|
2401
2521
|
onStatus: (status) => {
|
|
2402
2522
|
if (!streamStarted) reporter.update(status);
|
|
@@ -2429,7 +2549,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2429
2549
|
ui.note(`Provider detail: ${refined.rawError}`);
|
|
2430
2550
|
}
|
|
2431
2551
|
ui.note("Falling back to the raw structured prompt.");
|
|
2432
|
-
finalPrompt = buildRawPrompt(ctx, { target: options.target });
|
|
2552
|
+
finalPrompt = buildRawPrompt(ctx, { target: options.target, fromUserMessage });
|
|
2433
2553
|
}
|
|
2434
2554
|
}
|
|
2435
2555
|
}
|
package/package.json
CHANGED