ctx-switch 2.0.5 → 2.0.6
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 +218 -19
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -829,7 +829,10 @@ import { execFileSync as execFileSync2 } from "node:child_process";
|
|
|
829
829
|
import os4 from "node:os";
|
|
830
830
|
import path6 from "node:path";
|
|
831
831
|
var OPENCODE_DB_PATH = path6.join(os4.homedir(), ".local", "share", "opencode", "opencode.db");
|
|
832
|
-
function
|
|
832
|
+
function getOpenCodeDbPath() {
|
|
833
|
+
return process.env.CTX_SWITCH_OPENCODE_DB_PATH || OPENCODE_DB_PATH;
|
|
834
|
+
}
|
|
835
|
+
function runSqlite(query, dbPath = getOpenCodeDbPath()) {
|
|
833
836
|
try {
|
|
834
837
|
const stdout = execFileSync2("sqlite3", ["-json", dbPath, query], {
|
|
835
838
|
encoding: "utf8",
|
|
@@ -882,7 +885,31 @@ function resolveSessionPath3(selection, cwd) {
|
|
|
882
885
|
const match = sessions.find((s) => s.id === selection || s.id.includes(selection));
|
|
883
886
|
return match ? match.id : null;
|
|
884
887
|
}
|
|
888
|
+
function detectToolError(tool, status, output) {
|
|
889
|
+
if (status === "error") return true;
|
|
890
|
+
if (!output) return void 0;
|
|
891
|
+
if (/Process exited with code [1-9]\d*/.test(output)) return true;
|
|
892
|
+
if (/^npm ERR!/m.test(output)) return true;
|
|
893
|
+
if (/^(Error|TypeError|ReferenceError|SyntaxError):/m.test(output)) return true;
|
|
894
|
+
if (/^(bash|sh|zsh):/m.test(output)) return true;
|
|
895
|
+
if (tool === "read" || tool === "glob" || tool === "grep") return void 0;
|
|
896
|
+
return void 0;
|
|
897
|
+
}
|
|
898
|
+
function extractDelegatedSessionId(part) {
|
|
899
|
+
const metadataSessionId = part.state?.metadata?.sessionId;
|
|
900
|
+
if (typeof metadataSessionId === "string" && metadataSessionId.trim()) {
|
|
901
|
+
return metadataSessionId.trim();
|
|
902
|
+
}
|
|
903
|
+
const output = part.state?.output;
|
|
904
|
+
if (typeof output !== "string") return null;
|
|
905
|
+
const match = output.match(/\btask_id:\s*(ses_[^\s)]+)/);
|
|
906
|
+
return match?.[1] || null;
|
|
907
|
+
}
|
|
885
908
|
function parseSession3(sessionId) {
|
|
909
|
+
return parseSessionInternal(sessionId, /* @__PURE__ */ new Set());
|
|
910
|
+
}
|
|
911
|
+
function parseSessionInternal(sessionId, seenSessions) {
|
|
912
|
+
seenSessions.add(sessionId);
|
|
886
913
|
const messages = [];
|
|
887
914
|
const meta = {
|
|
888
915
|
cwd: null,
|
|
@@ -918,6 +945,7 @@ function parseSession3(sessionId) {
|
|
|
918
945
|
if (!role || role !== "user" && role !== "assistant") continue;
|
|
919
946
|
const textParts = [];
|
|
920
947
|
const toolCalls = [];
|
|
948
|
+
const delegatedAssistantMessages = [];
|
|
921
949
|
for (const partRow of partRows) {
|
|
922
950
|
let part;
|
|
923
951
|
try {
|
|
@@ -930,9 +958,17 @@ function parseSession3(sessionId) {
|
|
|
930
958
|
} else if (part.type === "tool" && part.tool) {
|
|
931
959
|
const input = part.state?.input || {};
|
|
932
960
|
const output = part.state?.output || "";
|
|
933
|
-
const isError = part.state?.status
|
|
961
|
+
const isError = detectToolError(part.tool, part.state?.status, output);
|
|
934
962
|
const toolName = normalizeToolName(part.tool);
|
|
935
963
|
const normalizedInput = normalizeInput(part.tool, input);
|
|
964
|
+
if (toolName === "task") {
|
|
965
|
+
const delegatedSessionId = extractDelegatedSessionId(part);
|
|
966
|
+
if (delegatedSessionId && !seenSessions.has(delegatedSessionId)) {
|
|
967
|
+
normalizedInput.delegated_session_id = delegatedSessionId;
|
|
968
|
+
const delegated = parseSessionInternal(delegatedSessionId, seenSessions);
|
|
969
|
+
delegatedAssistantMessages.push(...delegated.messages.filter((message) => message.role === "assistant"));
|
|
970
|
+
}
|
|
971
|
+
}
|
|
936
972
|
toolCalls.push({
|
|
937
973
|
id: part.callID || null,
|
|
938
974
|
tool: toolName,
|
|
@@ -950,11 +986,15 @@ function parseSession3(sessionId) {
|
|
|
950
986
|
toolCalls,
|
|
951
987
|
timestamp: null
|
|
952
988
|
});
|
|
989
|
+
if (role === "assistant" && delegatedAssistantMessages.length > 0) {
|
|
990
|
+
messages.push(...delegatedAssistantMessages);
|
|
991
|
+
}
|
|
953
992
|
}
|
|
954
993
|
return { messages, meta };
|
|
955
994
|
}
|
|
956
995
|
function normalizeToolName(tool) {
|
|
957
996
|
const mapping = {
|
|
997
|
+
apply_patch: "apply_patch",
|
|
958
998
|
read: "read",
|
|
959
999
|
edit: "edit",
|
|
960
1000
|
write: "write",
|
|
@@ -970,10 +1010,14 @@ function normalizeToolName(tool) {
|
|
|
970
1010
|
return mapping[tool.toLowerCase()] || tool;
|
|
971
1011
|
}
|
|
972
1012
|
function normalizeInput(tool, input) {
|
|
973
|
-
|
|
974
|
-
|
|
1013
|
+
const normalized = { ...input };
|
|
1014
|
+
if (normalized.filePath && !normalized.file_path) {
|
|
1015
|
+
normalized.file_path = normalized.filePath;
|
|
975
1016
|
}
|
|
976
|
-
|
|
1017
|
+
if (tool === "apply_patch" && typeof normalized.patchText === "string" && !normalized.patch) {
|
|
1018
|
+
normalized.patch = normalized.patchText;
|
|
1019
|
+
}
|
|
1020
|
+
return normalized;
|
|
977
1021
|
}
|
|
978
1022
|
|
|
979
1023
|
// src/session.ts
|
|
@@ -1040,6 +1084,23 @@ function extractParsedCommands(input) {
|
|
|
1040
1084
|
if (!Array.isArray(parsed)) return [];
|
|
1041
1085
|
return parsed.filter((entry) => Boolean(entry) && typeof entry === "object");
|
|
1042
1086
|
}
|
|
1087
|
+
function tokenizeShellCommand(command) {
|
|
1088
|
+
const matches = command.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
|
|
1089
|
+
return matches.map((token) => token.replace(/^['"]|['"]$/g, ""));
|
|
1090
|
+
}
|
|
1091
|
+
function looksLikeFilePath(token) {
|
|
1092
|
+
if (!token || token.startsWith("-")) return false;
|
|
1093
|
+
if (/[|<>;$()]/.test(token)) return false;
|
|
1094
|
+
return token.includes("/") || token.includes(".");
|
|
1095
|
+
}
|
|
1096
|
+
function extractReadPathsFromCommand(command, cwd) {
|
|
1097
|
+
const tokens = tokenizeShellCommand(command.trim());
|
|
1098
|
+
if (tokens.length === 0) return [];
|
|
1099
|
+
const base = path7.basename(tokens[0]);
|
|
1100
|
+
const readCommands = /* @__PURE__ */ new Set(["cat", "head", "tail", "sed", "nl", "wc"]);
|
|
1101
|
+
if (!readCommands.has(base)) return [];
|
|
1102
|
+
return tokens.slice(1).filter((token) => looksLikeFilePath(token)).filter((token) => !/^\d+(,\d+)?p?$/.test(token)).map((token) => resolveSessionFilePath(token, cwd));
|
|
1103
|
+
}
|
|
1043
1104
|
function buildSessionContext({
|
|
1044
1105
|
messages,
|
|
1045
1106
|
meta,
|
|
@@ -1059,6 +1120,7 @@ function buildSessionContext({
|
|
|
1059
1120
|
const command = extractCommand(toolCall.input);
|
|
1060
1121
|
const patchPaths = extractPatchFilePaths(toolCall.input, cwd);
|
|
1061
1122
|
const parsedCommands = extractParsedCommands(toolCall.input);
|
|
1123
|
+
const commandReadPaths = command ? extractReadPathsFromCommand(command, cwd) : [];
|
|
1062
1124
|
if (filePath && /(edit|write|create|multi_edit)/.test(toolName)) {
|
|
1063
1125
|
filesModified.add(resolveSessionFilePath(filePath, cwd));
|
|
1064
1126
|
} else if (filePath && /(read|grep|glob|search)/.test(toolName)) {
|
|
@@ -1071,6 +1133,9 @@ function buildSessionContext({
|
|
|
1071
1133
|
if (parsedCommand.type !== "read" || typeof parsedCommand.path !== "string") continue;
|
|
1072
1134
|
filesRead.add(resolveSessionFilePath(parsedCommand.path, cwd));
|
|
1073
1135
|
}
|
|
1136
|
+
for (const readPath of commandReadPaths) {
|
|
1137
|
+
filesRead.add(readPath);
|
|
1138
|
+
}
|
|
1074
1139
|
if (command && /(bash|command|run|exec_command)/.test(toolName)) {
|
|
1075
1140
|
commands.push(command);
|
|
1076
1141
|
}
|
|
@@ -1268,7 +1333,7 @@ function extractCommand2(input) {
|
|
|
1268
1333
|
function buildTargetGuidance(target) {
|
|
1269
1334
|
switch (target) {
|
|
1270
1335
|
case "claude":
|
|
1271
|
-
return "The next agent is Claude Code. It should read the active files first,
|
|
1336
|
+
return "The next agent is Claude Code. It should read the active files first, inspect the current workspace with git commands, and continue the implementation or debugging directly.";
|
|
1272
1337
|
case "codex":
|
|
1273
1338
|
return "The next agent is Codex. It should inspect the current files first, avoid redoing completed work, and finish any remaining implementation or verification.";
|
|
1274
1339
|
case "cursor":
|
|
@@ -1276,7 +1341,7 @@ function buildTargetGuidance(target) {
|
|
|
1276
1341
|
case "chatgpt":
|
|
1277
1342
|
return "The next agent is ChatGPT. It should reason from the current workspace state, explain what remains, and provide explicit next actions.";
|
|
1278
1343
|
default:
|
|
1279
|
-
return "The next agent should read the active files first,
|
|
1344
|
+
return "The next agent should read the active files first, inspect the current workspace with git commands, continue the interrupted work directly, and avoid redoing completed steps.";
|
|
1280
1345
|
}
|
|
1281
1346
|
}
|
|
1282
1347
|
function isNoiseMessage(text) {
|
|
@@ -1316,6 +1381,10 @@ function isReferentialMessage(text) {
|
|
|
1316
1381
|
if (!normalized || normalized.length > 220) return false;
|
|
1317
1382
|
return /^(ok|okay|alright|now|so)\b/.test(normalized) || /\b(it|that|again|better|same|continue|still|also|another|more)\b/.test(normalized);
|
|
1318
1383
|
}
|
|
1384
|
+
function isMetaQualityAssistantMessage(text) {
|
|
1385
|
+
const lower = text.toLowerCase();
|
|
1386
|
+
return /\b(handoff|prompt)\b/.test(lower) && /\b(good|bad|better|worse|quality)\b/.test(lower);
|
|
1387
|
+
}
|
|
1319
1388
|
function filterUserMessages(messages) {
|
|
1320
1389
|
const all = messages.filter((m) => m.role === "user" && m.content).map((m) => m.content.trim());
|
|
1321
1390
|
if (all.length <= 2) return all;
|
|
@@ -1344,10 +1413,10 @@ function extractKeyDecisions(messages) {
|
|
|
1344
1413
|
const decisions = [];
|
|
1345
1414
|
for (const msg of messages) {
|
|
1346
1415
|
if (msg.role !== "assistant" || !msg.content) continue;
|
|
1347
|
-
|
|
1348
|
-
if (/\b(handoff|prompt)\b/.test(lower) && /\b(good|bad|better|worse|quality)\b/.test(lower)) {
|
|
1416
|
+
if (isMetaQualityAssistantMessage(msg.content)) {
|
|
1349
1417
|
continue;
|
|
1350
1418
|
}
|
|
1419
|
+
const lower = msg.content.toLowerCase();
|
|
1351
1420
|
if (/\b(root cause|the issue is|the problem is|caused by|failed because|failing because|need to)\b/.test(lower) || /\b(exposed|revealed|showed)\b.*\b(gap|issue|problem|bug)\b/.test(lower) || /\bmissing\b/.test(lower)) {
|
|
1352
1421
|
decisions.push(compactText(msg.content, 300));
|
|
1353
1422
|
}
|
|
@@ -1419,6 +1488,103 @@ function extractLastAssistantAnswer(messages) {
|
|
|
1419
1488
|
}
|
|
1420
1489
|
return null;
|
|
1421
1490
|
}
|
|
1491
|
+
function summarizeToolCall(toolCall) {
|
|
1492
|
+
const filePath = extractFilePath2(toolCall.input);
|
|
1493
|
+
const command = extractCommand2(toolCall.input);
|
|
1494
|
+
if (filePath) return `${toolCall.tool} ${filePath}`;
|
|
1495
|
+
if (command) return `${toolCall.tool}: ${summarizeCommand(command)}`;
|
|
1496
|
+
return toolCall.tool;
|
|
1497
|
+
}
|
|
1498
|
+
function findLastActiveAssistant(messages) {
|
|
1499
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1500
|
+
const message = messages[i];
|
|
1501
|
+
if (message.role !== "assistant") continue;
|
|
1502
|
+
if (message.content.trim() || message.toolCalls.length > 0) {
|
|
1503
|
+
return message;
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
return null;
|
|
1507
|
+
}
|
|
1508
|
+
function buildCurrentStatus(messages, errors, sessionAppearsComplete) {
|
|
1509
|
+
const lastAssistant = findLastActiveAssistant(messages);
|
|
1510
|
+
const lastStep = lastAssistant?.content?.trim() ? compactText(lastAssistant.content, 400) : null;
|
|
1511
|
+
const lastToolActions = lastAssistant ? lastAssistant.toolCalls.slice(-4).map(summarizeToolCall) : [];
|
|
1512
|
+
let status = "In progress";
|
|
1513
|
+
if (sessionAppearsComplete) {
|
|
1514
|
+
status = "Latest exchange complete";
|
|
1515
|
+
} else if (errors.length > 0) {
|
|
1516
|
+
status = "Blocked by unresolved errors";
|
|
1517
|
+
} else if (lastAssistant?.toolCalls.length) {
|
|
1518
|
+
status = "Mid-task after recent tool activity";
|
|
1519
|
+
} else if (lastAssistant?.content.trim()) {
|
|
1520
|
+
status = "Awaiting the next concrete action";
|
|
1521
|
+
}
|
|
1522
|
+
return { status, lastStep, lastToolActions };
|
|
1523
|
+
}
|
|
1524
|
+
function buildRemainingWorkHints({
|
|
1525
|
+
sessionAppearsComplete,
|
|
1526
|
+
errors,
|
|
1527
|
+
work,
|
|
1528
|
+
focusFiles,
|
|
1529
|
+
recentCommands
|
|
1530
|
+
}) {
|
|
1531
|
+
if (sessionAppearsComplete) return [];
|
|
1532
|
+
const hints = [];
|
|
1533
|
+
if (errors.length > 0) {
|
|
1534
|
+
hints.push("Resolve the unresolved errors above before extending the implementation.");
|
|
1535
|
+
}
|
|
1536
|
+
if (work.filesModified.length > 0) {
|
|
1537
|
+
hints.push(`Inspect the in-progress changes in ${work.filesModified.map((filePath) => `\`${filePath}\``).join(", ")} and decide what still needs to be finished or verified.`);
|
|
1538
|
+
} else if (focusFiles.length > 0) {
|
|
1539
|
+
hints.push(`Start by reading ${focusFiles.map((filePath) => `\`${filePath}\``).join(", ")} to reconstruct the current working set.`);
|
|
1540
|
+
}
|
|
1541
|
+
if (recentCommands.length > 0) {
|
|
1542
|
+
hints.push("Rerun or extend the recent checks to confirm the current state before making further changes.");
|
|
1543
|
+
}
|
|
1544
|
+
if (focusFiles.length > 0) {
|
|
1545
|
+
hints.push("Run `git diff --` on the active files to see the exact in-progress changes before editing further.");
|
|
1546
|
+
}
|
|
1547
|
+
if (hints.length === 0) {
|
|
1548
|
+
hints.push("Inspect the active files and run `git diff` to determine the next concrete implementation step.");
|
|
1549
|
+
}
|
|
1550
|
+
return hints;
|
|
1551
|
+
}
|
|
1552
|
+
function selectSessionHistoryMessages(focusedMessages, allMessages, sessionAppearsComplete) {
|
|
1553
|
+
if (sessionAppearsComplete) return focusedMessages;
|
|
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) => {
|
|
1559
|
+
if (message.role === "assistant" && message.content.trim() && isMetaQualityAssistantMessage(message.content)) {
|
|
1560
|
+
return false;
|
|
1561
|
+
}
|
|
1562
|
+
if (message.role === "user" && message.content && isNoiseMessage(message.content)) {
|
|
1563
|
+
return false;
|
|
1564
|
+
}
|
|
1565
|
+
return Boolean(message.content.trim()) || message.toolCalls.length > 0;
|
|
1566
|
+
});
|
|
1567
|
+
return filtered.slice(-8);
|
|
1568
|
+
}
|
|
1569
|
+
function buildSessionHistory(focusedMessages, allMessages, sessionAppearsComplete) {
|
|
1570
|
+
const historyMessages = selectSessionHistoryMessages(focusedMessages, allMessages, sessionAppearsComplete);
|
|
1571
|
+
const entries = historyMessages.map((message) => {
|
|
1572
|
+
const parts = [];
|
|
1573
|
+
if (message.content.trim()) {
|
|
1574
|
+
if (message.role === "assistant" && isMetaQualityAssistantMessage(message.content)) {
|
|
1575
|
+
return null;
|
|
1576
|
+
}
|
|
1577
|
+
parts.push(compactText(message.content, 220));
|
|
1578
|
+
}
|
|
1579
|
+
if (message.role === "assistant" && message.toolCalls.length > 0) {
|
|
1580
|
+
parts.push(`[tools] ${message.toolCalls.slice(-4).map(summarizeToolCall).join(", ")}`);
|
|
1581
|
+
}
|
|
1582
|
+
if (parts.length === 0) return null;
|
|
1583
|
+
return `${message.role.toUpperCase()}: ${parts.join(" | ")}`;
|
|
1584
|
+
}).filter((entry) => Boolean(entry));
|
|
1585
|
+
if (entries.length <= 8) return entries;
|
|
1586
|
+
return [entries[0], "...", ...entries.slice(-6)];
|
|
1587
|
+
}
|
|
1422
1588
|
function summarizeCommand(command) {
|
|
1423
1589
|
return compactText(command.replace(/\s+/g, " ").trim(), 140);
|
|
1424
1590
|
}
|
|
@@ -1452,6 +1618,15 @@ function buildRawPrompt(ctx, options = {}) {
|
|
|
1452
1618
|
const focusFiles = extractFocusFiles(ctx, work);
|
|
1453
1619
|
const recentCommands = extractRecentCommands(work.commands);
|
|
1454
1620
|
const lastAssistantAnswer = extractLastAssistantAnswer(focused.messages);
|
|
1621
|
+
const currentStatus = buildCurrentStatus(focused.messages, errors, focused.sessionAppearsComplete);
|
|
1622
|
+
const remainingWorkHints = buildRemainingWorkHints({
|
|
1623
|
+
sessionAppearsComplete: focused.sessionAppearsComplete,
|
|
1624
|
+
errors,
|
|
1625
|
+
work,
|
|
1626
|
+
focusFiles,
|
|
1627
|
+
recentCommands
|
|
1628
|
+
});
|
|
1629
|
+
const sessionHistory = buildSessionHistory(focused.messages, ctx.messages, focused.sessionAppearsComplete);
|
|
1455
1630
|
let prompt = "";
|
|
1456
1631
|
prompt += "# Task\n\n";
|
|
1457
1632
|
prompt += `Project: \`${ctx.sessionCwd}\`
|
|
@@ -1479,6 +1654,26 @@ function buildRawPrompt(ctx, options = {}) {
|
|
|
1479
1654
|
|
|
1480
1655
|
`;
|
|
1481
1656
|
}
|
|
1657
|
+
prompt += "## Current Status\n\n";
|
|
1658
|
+
prompt += `- Status: ${currentStatus.status}
|
|
1659
|
+
`;
|
|
1660
|
+
if (currentStatus.lastStep) {
|
|
1661
|
+
prompt += `- Last active step: ${currentStatus.lastStep}
|
|
1662
|
+
`;
|
|
1663
|
+
}
|
|
1664
|
+
if (currentStatus.lastToolActions.length > 0) {
|
|
1665
|
+
prompt += `- Last tool actions: ${currentStatus.lastToolActions.join(", ")}
|
|
1666
|
+
`;
|
|
1667
|
+
}
|
|
1668
|
+
prompt += "\n";
|
|
1669
|
+
if (sessionHistory.length > 0) {
|
|
1670
|
+
prompt += "## Session History\n\n";
|
|
1671
|
+
for (const entry of sessionHistory) {
|
|
1672
|
+
prompt += `- ${entry}
|
|
1673
|
+
`;
|
|
1674
|
+
}
|
|
1675
|
+
prompt += "\n";
|
|
1676
|
+
}
|
|
1482
1677
|
if (errors.length > 0) {
|
|
1483
1678
|
prompt += "## DO NOT REPEAT \u2014 Unresolved Errors\n\n";
|
|
1484
1679
|
prompt += "These errors occurred and were NOT fixed. Avoid the same approaches.\n\n";
|
|
@@ -1531,6 +1726,14 @@ function buildRawPrompt(ctx, options = {}) {
|
|
|
1531
1726
|
prompt += "## Read These Files First\n\n";
|
|
1532
1727
|
for (const filePath of focusFiles) {
|
|
1533
1728
|
prompt += `- \`${filePath}\`
|
|
1729
|
+
`;
|
|
1730
|
+
}
|
|
1731
|
+
prompt += "\n";
|
|
1732
|
+
}
|
|
1733
|
+
if (remainingWorkHints.length > 0) {
|
|
1734
|
+
prompt += "## Likely Remaining Work\n\n";
|
|
1735
|
+
for (const hint of remainingWorkHints) {
|
|
1736
|
+
prompt += `- ${hint}
|
|
1534
1737
|
`;
|
|
1535
1738
|
}
|
|
1536
1739
|
prompt += "\n";
|
|
@@ -1541,12 +1744,6 @@ function buildRawPrompt(ctx, options = {}) {
|
|
|
1541
1744
|
if (git.status) {
|
|
1542
1745
|
prompt += "```\n" + git.status + "\n```\n\n";
|
|
1543
1746
|
}
|
|
1544
|
-
if (git.staged.diff) {
|
|
1545
|
-
prompt += "**Staged diff:**\n```diff\n" + git.staged.diff + "\n```\n\n";
|
|
1546
|
-
}
|
|
1547
|
-
if (git.unstaged.diff) {
|
|
1548
|
-
prompt += "**Unstaged diff:**\n```diff\n" + git.unstaged.diff + "\n```\n\n";
|
|
1549
|
-
}
|
|
1550
1747
|
if (git.untracked.length > 0) {
|
|
1551
1748
|
const shown = git.untracked.slice(0, 6);
|
|
1552
1749
|
prompt += "**Untracked files:**\n";
|
|
@@ -1577,11 +1774,13 @@ function buildRawPrompt(ctx, options = {}) {
|
|
|
1577
1774
|
if (errors.length > 0) {
|
|
1578
1775
|
prompt += "2. **Check the errors above** \u2014 do NOT repeat failed approaches. Try a different strategy.\n";
|
|
1579
1776
|
}
|
|
1580
|
-
prompt += `${errors.length > 0 ? "3" : "2"}. **
|
|
1777
|
+
prompt += `${errors.length > 0 ? "3" : "2"}. **Inspect the workspace state explicitly** \u2014 run \`git status --short\`, \`git diff --stat\`, and \`git diff -- ${focusFiles.length > 0 ? focusFiles.slice(0, 4).join(" ") : "."}\` before changing code.
|
|
1778
|
+
`;
|
|
1779
|
+
prompt += `${errors.length > 0 ? "4" : "3"}. **Identify what's done vs what remains** \u2014 use the Current Status, Session History, Likely Remaining Work, recent commands, active files, and git state above as the source of truth for the current thread.
|
|
1581
1780
|
`;
|
|
1582
|
-
prompt += `${errors.length > 0 ? "
|
|
1781
|
+
prompt += `${errors.length > 0 ? "5" : "4"}. **Continue from the last active step** \u2014 if the stop point is still ambiguous, inspect the read-first files and rerun the recent commands before changing code.
|
|
1583
1782
|
`;
|
|
1584
|
-
prompt += `${errors.length > 0 ? "
|
|
1783
|
+
prompt += `${errors.length > 0 ? "6" : "5"}. **Verify** \u2014 rerun or extend the relevant commands/checks above to confirm everything works.
|
|
1585
1784
|
`;
|
|
1586
1785
|
}
|
|
1587
1786
|
return prompt;
|
|
@@ -2082,7 +2281,7 @@ async function promptForSource() {
|
|
|
2082
2281
|
}
|
|
2083
2282
|
async function main(argv = process.argv.slice(2)) {
|
|
2084
2283
|
const options = parseArgs(argv);
|
|
2085
|
-
const pkgInfo = { name: "ctx-switch", version: "2.0.
|
|
2284
|
+
const pkgInfo = { name: "ctx-switch", version: "2.0.6" };
|
|
2086
2285
|
const ui = createTheme(process.stderr);
|
|
2087
2286
|
if (options.help) {
|
|
2088
2287
|
process.stdout.write(`${getHelpText(pkgInfo)}
|
package/package.json
CHANGED