march-cli 0.1.23 → 0.1.25

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.
Files changed (237) hide show
  1. package/README.md +1 -1
  2. package/README.zh.md +1 -1
  3. package/bin/march.mjs +13 -13
  4. package/package.json +49 -43
  5. package/src/agent/command-exec-tool.mjs +172 -172
  6. package/src/agent/context-stats-tool.mjs +57 -57
  7. package/src/agent/editing/cohesion-warning.mjs +57 -0
  8. package/src/agent/editing/diff-apply.mjs +28 -28
  9. package/src/agent/editing/diff-format.mjs +57 -57
  10. package/src/agent/editing/lsp-report.mjs +69 -69
  11. package/src/agent/file-edit-tool.mjs +277 -262
  12. package/src/agent/file-tools/read-file-tool.mjs +112 -112
  13. package/src/agent/file-tools/read-image-tool.mjs +76 -76
  14. package/src/agent/model-payload-dumper.mjs +208 -208
  15. package/src/agent/pi-session/pi-session-sidecar-failure.mjs +10 -10
  16. package/src/agent/provider/payload-messages.mjs +138 -138
  17. package/src/agent/runner/codex-large-context-guard.mjs +87 -87
  18. package/src/agent/runner/codex-transport-compression.mjs +180 -180
  19. package/src/agent/runner/codex-transport-debug.mjs +113 -113
  20. package/src/agent/runner/codex-websocket-event-debug.mjs +130 -130
  21. package/src/agent/runner/fast-model.mjs +36 -36
  22. package/src/agent/runner/runner-cleanup.mjs +12 -12
  23. package/src/agent/runner/runner-init.mjs +15 -15
  24. package/src/agent/runner/runner-session-state.mjs +40 -40
  25. package/src/agent/runner/runner-utils.mjs +24 -24
  26. package/src/agent/runner.mjs +299 -299
  27. package/src/agent/runtime/ipc/ipc-peer.mjs +99 -99
  28. package/src/agent/runtime/ipc/process-ipc-transport.mjs +16 -16
  29. package/src/agent/runtime/remote-runner-client.mjs +73 -73
  30. package/src/agent/runtime/remote-ui-client.mjs +20 -20
  31. package/src/agent/runtime/runner-ipc-target.mjs +125 -125
  32. package/src/agent/runtime/runner-process-client.mjs +47 -47
  33. package/src/agent/runtime/runner-process-entry.mjs +11 -11
  34. package/src/agent/runtime/runner-process-factory.mjs +111 -108
  35. package/src/agent/runtime/runner-runtime-host.mjs +79 -79
  36. package/src/agent/runtime/runtime-factory.mjs +42 -42
  37. package/src/agent/runtime/runtime-host.mjs +34 -34
  38. package/src/agent/runtime/ui-event-bridge.mjs +95 -95
  39. package/src/agent/screen-tools/list-windows-tool.mjs +39 -39
  40. package/src/agent/screen-tools/screen-tool.mjs +49 -49
  41. package/src/agent/screen-tools/windows-screen.mjs +133 -133
  42. package/src/agent/session/session-auto-name.mjs +41 -41
  43. package/src/agent/session/session-binding.mjs +12 -12
  44. package/src/agent/session/session-options.mjs +47 -47
  45. package/src/agent/tool-names.mjs +1 -1
  46. package/src/agent/tool-result.mjs +3 -3
  47. package/src/agent/tool-summary.mjs +112 -112
  48. package/src/agent/tools.mjs +58 -58
  49. package/src/agent/turn/turn-events.mjs +111 -111
  50. package/src/agent/turn/turn-logging.mjs +30 -30
  51. package/src/agent/turn/turn-runner.mjs +196 -196
  52. package/src/agent/vision-capability.mjs +14 -14
  53. package/src/auth/login-command.mjs +90 -90
  54. package/src/auth/storage.mjs +34 -34
  55. package/src/cli/args.mjs +96 -79
  56. package/src/cli/commands/copy-command.mjs +87 -87
  57. package/src/cli/commands/export-command.mjs +206 -206
  58. package/src/cli/commands/extensions-command.mjs +53 -53
  59. package/src/cli/commands/help-command.mjs +7 -7
  60. package/src/cli/commands/model-command.mjs +141 -141
  61. package/src/cli/commands/paste-image-command.mjs +43 -43
  62. package/src/cli/commands/provider-command.mjs +59 -59
  63. package/src/cli/commands/status-command.mjs +196 -194
  64. package/src/cli/commands/thinking-command.mjs +87 -87
  65. package/src/cli/fallback-ui.mjs +156 -156
  66. package/src/cli/input/attachment-tokens.mjs +20 -20
  67. package/src/cli/input/autocomplete.mjs +74 -74
  68. package/src/cli/input/external-editor.mjs +39 -39
  69. package/src/cli/input/file-search/index.mjs +160 -160
  70. package/src/cli/input/history-store.mjs +35 -35
  71. package/src/cli/input/image-clipboard.mjs +55 -55
  72. package/src/cli/input/keybinding-dispatch.mjs +76 -76
  73. package/src/cli/input/keybindings.mjs +96 -96
  74. package/src/cli/input/mode-state.mjs +43 -43
  75. package/src/cli/input/prompt-templates.mjs +84 -84
  76. package/src/cli/input/select-with-keyboard.mjs +86 -86
  77. package/src/cli/permissions.mjs +103 -103
  78. package/src/cli/repl-commands.mjs +86 -86
  79. package/src/cli/repl-loop.mjs +183 -183
  80. package/src/cli/selector-list.mjs +21 -21
  81. package/src/cli/session/pi-session-switch-command.mjs +41 -41
  82. package/src/cli/session/session-command.mjs +23 -23
  83. package/src/cli/session/session-list-command.mjs +68 -68
  84. package/src/cli/session/session-name-command.mjs +26 -26
  85. package/src/cli/session/session-source-command.mjs +89 -89
  86. package/src/cli/session/session-switch-command.mjs +1 -1
  87. package/src/cli/shell/shell-command.mjs +55 -55
  88. package/src/cli/shell/shell-drawer-controls.mjs +33 -33
  89. package/src/cli/shell/shell-drawer.mjs +192 -192
  90. package/src/cli/shell/shell-split-layout.mjs +70 -70
  91. package/src/cli/slash-commands.mjs +192 -192
  92. package/src/cli/startup/create-runtime-runner.mjs +61 -61
  93. package/src/cli/startup/runtime-close.mjs +23 -23
  94. package/src/cli/startup/startup-banner.mjs +71 -71
  95. package/src/cli/startup/startup-session.mjs +51 -51
  96. package/src/cli/status-line-updater.mjs +75 -75
  97. package/src/cli/tool-output.mjs +9 -9
  98. package/src/cli/tui/editor/external-editor-runner.mjs +24 -24
  99. package/src/cli/tui/input/mouse-selection-controller.mjs +91 -91
  100. package/src/cli/tui/input/mouse-tracking.mjs +20 -20
  101. package/src/cli/tui/layout/main-pane-layout.mjs +47 -47
  102. package/src/cli/tui/layout/safe-render-boundary.mjs +46 -46
  103. package/src/cli/tui/markdown-renderer.mjs +285 -285
  104. package/src/cli/tui/output/scroll-state.mjs +79 -79
  105. package/src/cli/tui/output/text-line-renderer.mjs +50 -50
  106. package/src/cli/tui/output/tool-card-renderer.mjs +59 -59
  107. package/src/cli/tui/output/visible-lines.mjs +8 -8
  108. package/src/cli/tui/output-buffer.mjs +293 -293
  109. package/src/cli/tui/permission-request-ui.mjs +18 -18
  110. package/src/cli/tui/recall-rendering.mjs +28 -25
  111. package/src/cli/tui/render/render-scheduler.mjs +26 -26
  112. package/src/cli/tui/render/stream-delta-buffer.mjs +46 -46
  113. package/src/cli/tui/select/editor-select-list.mjs +111 -111
  114. package/src/cli/tui/selection-screen.mjs +269 -269
  115. package/src/cli/tui/status/retry-status.mjs +72 -72
  116. package/src/cli/tui/status/spinner-status.mjs +42 -42
  117. package/src/cli/tui/status/status-bar.mjs +225 -225
  118. package/src/cli/tui/syntax/highlighting.mjs +260 -260
  119. package/src/cli/tui/syntax/languages.mjs +91 -91
  120. package/src/cli/tui/syntax/tree-sitter/bash.highlights.scm +261 -261
  121. package/src/cli/tui/syntax/tree-sitter/c.highlights.scm +341 -341
  122. package/src/cli/tui/syntax/tree-sitter/cpp.highlights.scm +268 -268
  123. package/src/cli/tui/syntax/tree-sitter/csharp.highlights.scm +577 -577
  124. package/src/cli/tui/syntax/tree-sitter/css.highlights.scm +109 -109
  125. package/src/cli/tui/syntax/tree-sitter/diff.highlights.scm +49 -49
  126. package/src/cli/tui/syntax/tree-sitter/go.highlights.scm +254 -254
  127. package/src/cli/tui/syntax/tree-sitter/html.highlights.scm +13 -13
  128. package/src/cli/tui/syntax/tree-sitter/java.highlights.scm +330 -330
  129. package/src/cli/tui/syntax/tree-sitter/json.highlights.scm +38 -38
  130. package/src/cli/tui/syntax/tree-sitter/php.highlights.scm +203 -203
  131. package/src/cli/tui/syntax/tree-sitter/python.highlights.scm +137 -137
  132. package/src/cli/tui/syntax/tree-sitter/ruby.highlights.scm +309 -309
  133. package/src/cli/tui/syntax/tree-sitter/rust.highlights.scm +531 -531
  134. package/src/cli/tui/syntax/tree-sitter/toml.highlights.scm +39 -39
  135. package/src/cli/tui/syntax/tree-sitter/tsx.highlights.scm +35 -35
  136. package/src/cli/tui/syntax/tree-sitter/typescript.highlights.scm +35 -35
  137. package/src/cli/tui/syntax/tree-sitter/yaml.highlights.scm +99 -99
  138. package/src/cli/tui/tool-rendering.mjs +87 -87
  139. package/src/cli/tui/tui-diff-rendering.mjs +157 -157
  140. package/src/cli/tui/tui-handlers.mjs +111 -111
  141. package/src/cli/tui/tui-input-controller.mjs +61 -61
  142. package/src/cli/tui/ui-theme.mjs +157 -157
  143. package/src/cli/ui.mjs +297 -297
  144. package/src/config/config-json.mjs +108 -84
  145. package/src/config/dotenv.mjs +20 -20
  146. package/src/config/features.mjs +75 -75
  147. package/src/config/loader.mjs +156 -143
  148. package/src/config/settings-command.mjs +97 -97
  149. package/src/context/engine.mjs +199 -198
  150. package/src/context/injections.mjs +26 -26
  151. package/src/context/profiles.mjs +39 -39
  152. package/src/context/project-context.mjs +20 -20
  153. package/src/context/session-status.mjs +25 -17
  154. package/src/context/shell-layers.mjs +23 -23
  155. package/src/context/system-core/base.md +50 -50
  156. package/src/context/system-core/prompts/deepseek-v4-pro.md +3 -3
  157. package/src/context/system-core/prompts/default.md +3 -3
  158. package/src/context/system-core.mjs +35 -35
  159. package/src/debug/logger.mjs +141 -141
  160. package/src/debug/model-context-dumper.mjs +52 -52
  161. package/src/extensions/discovery.mjs +40 -40
  162. package/src/extensions/lifecycle-adapter.mjs +210 -210
  163. package/src/extensions/lifecycle-manifest.mjs +69 -69
  164. package/src/image-gen/index.mjs +7 -7
  165. package/src/image-gen/provider.mjs +231 -231
  166. package/src/image-gen/tool.mjs +84 -84
  167. package/src/lsp/client.mjs +257 -257
  168. package/src/lsp/diagnostic-store.mjs +42 -42
  169. package/src/lsp/diagnostics-format.mjs +72 -72
  170. package/src/lsp/managed-node-server.mjs +99 -99
  171. package/src/lsp/path-match.mjs +10 -10
  172. package/src/lsp/server-definitions.mjs +188 -188
  173. package/src/lsp/servers.mjs +165 -165
  174. package/src/lsp/service.mjs +110 -110
  175. package/src/lsp/status-message.mjs +9 -9
  176. package/src/lsp/typescript-project-resolver.mjs +186 -186
  177. package/src/main.mjs +294 -299
  178. package/src/mcp/client.mjs +195 -195
  179. package/src/mcp/config.mjs +130 -130
  180. package/src/mcp/index.mjs +48 -48
  181. package/src/mcp/tools.mjs +98 -98
  182. package/src/memory/command.mjs +120 -0
  183. package/src/memory/markdown/markdown-delete.mjs +23 -23
  184. package/src/memory/markdown/markdown-format.mjs +128 -128
  185. package/src/memory/markdown/markdown-recall.mjs +28 -28
  186. package/src/memory/markdown/ripgrep.mjs +16 -16
  187. package/src/memory/markdown/sqlite-index.mjs +87 -87
  188. package/src/memory/markdown-store.mjs +272 -286
  189. package/src/memory/markdown-tools.mjs +174 -103
  190. package/src/memory/remote/client.mjs +68 -0
  191. package/src/memory/remote/config.mjs +52 -0
  192. package/src/memory/remote/server.mjs +99 -0
  193. package/src/memory/search.mjs +183 -0
  194. package/src/network/environment.mjs +131 -131
  195. package/src/notification/desktop-notifier.mjs +262 -262
  196. package/src/platform/open-file.mjs +28 -28
  197. package/src/platform/spawn-command.mjs +27 -27
  198. package/src/provider/accept-command.mjs +89 -89
  199. package/src/provider/command.mjs +21 -21
  200. package/src/provider/config-command.mjs +129 -129
  201. package/src/provider/custom-provider.mjs +113 -113
  202. package/src/provider/hosted-tools.mjs +111 -111
  203. package/src/provider/presets.mjs +72 -72
  204. package/src/provider/share-command.mjs +79 -79
  205. package/src/provider/share-payload.mjs +52 -52
  206. package/src/session/attachment-display.mjs +16 -16
  207. package/src/session/attachment-references.mjs +65 -65
  208. package/src/session/attachments.mjs +140 -140
  209. package/src/session/persist.mjs +1 -1
  210. package/src/session/pi-manager.mjs +34 -34
  211. package/src/session/session-utils.mjs +16 -16
  212. package/src/session/sidecar-sync.mjs +19 -19
  213. package/src/session/sidecar.mjs +69 -69
  214. package/src/session/transcript.mjs +83 -83
  215. package/src/session/tree.mjs +42 -42
  216. package/src/shell/cli-runtime.mjs +11 -11
  217. package/src/shell/hints.mjs +12 -12
  218. package/src/shell/node-pty-adapter.mjs +81 -81
  219. package/src/shell/runtime-state.mjs +126 -126
  220. package/src/shell/runtime.mjs +252 -252
  221. package/src/shell/screen-buffer.mjs +136 -136
  222. package/src/shell/tool-read.mjs +74 -74
  223. package/src/shell/tools.mjs +299 -299
  224. package/src/supergrok/actions/image-generate.mjs +60 -60
  225. package/src/supergrok/actions/search.mjs +78 -78
  226. package/src/supergrok/auth.mjs +36 -36
  227. package/src/supergrok/constants.mjs +18 -18
  228. package/src/supergrok/oauth-provider.mjs +278 -278
  229. package/src/supergrok/provider.mjs +35 -35
  230. package/src/supergrok/response.mjs +76 -76
  231. package/src/supergrok/tool.mjs +61 -61
  232. package/src/text/ansi.mjs +3 -3
  233. package/src/web/config-command.mjs +43 -43
  234. package/src/web/fetch.mjs +78 -78
  235. package/src/web/presets.mjs +16 -16
  236. package/src/web/search.mjs +83 -83
  237. package/src/web/tools.mjs +107 -107
@@ -1,68 +1,68 @@
1
- import { formatSessionTree } from "../../session/tree.mjs";
2
-
3
- export function formatSessionList(sessions, currentSessionId = null) {
4
- if (sessions.length === 0) return ["(no saved sessions)"];
5
- const lines = sessions.map((session) => {
6
- const marker = session.id === currentSessionId ? " *" : " ";
7
- const parent = session.parentSessionId ? ` fork:${session.parentSessionId}` : "";
8
- return `${marker} ${session.id} ${session.turnCount}t ${session.cwd} ${session.savedAt?.slice(0, 19) ?? "?"}${parent}`;
9
- });
10
- lines.push("(* = current session)");
11
- return lines;
12
- }
13
-
14
- export function listSessionCommand({ sessions, currentSessionId, tree = false }) {
15
- if (tree) return formatSessionTree(sessions, currentSessionId);
16
- return formatSessionList(sessions, currentSessionId);
17
- }
18
-
19
- export function formatPiSessionList(sessions) {
20
- if (sessions.length === 0) return ["(no pi sessions)"];
21
- const lines = sessions.map((session) => {
22
- const label = session.name || session.firstMessage || "(no messages)";
23
- const savedAt = session.savedAt?.slice(0, 19) ?? "?";
24
- return ` ${session.id} ${session.turnCount}m ${savedAt} ${label}`;
25
- });
26
- lines.push("(pi JSONL session files; use /session to restore a previous session)");
27
- return lines;
28
- }
29
-
30
- export function formatPiSessionTree(sessions, currentSessionId = null) {
31
- if (sessions.length === 0) return ["(no pi sessions)"];
32
-
33
- const nodes = new Map();
34
- const byPath = new Map();
35
- for (const session of sessions) {
36
- const node = { ...session, children: [] };
37
- nodes.set(session.id, node);
38
- if (session.path) byPath.set(session.path, node);
39
- }
40
-
41
- const roots = [];
42
- for (const node of nodes.values()) {
43
- const parent = node.parentSessionPath ? byPath.get(node.parentSessionPath) : null;
44
- if (parent) parent.children.push(node);
45
- else roots.push(node);
46
- }
47
-
48
- const bySavedAtDesc = (a, b) => (b.savedAt ?? "").localeCompare(a.savedAt ?? "");
49
- const sortDeep = (items) => {
50
- items.sort(bySavedAtDesc);
51
- for (const item of items) sortDeep(item.children);
52
- };
53
- sortDeep(roots);
54
-
55
- const lines = [];
56
- const visit = (node, depth) => {
57
- const marker = node.id === currentSessionId ? "*" : "-";
58
- const savedAt = node.savedAt?.slice(0, 19) ?? "?";
59
- const label = node.name || node.firstMessage || "(no messages)";
60
- const indent = " ".repeat(depth);
61
- lines.push(`${indent}${marker} ${node.id} ${node.turnCount ?? 0}m ${savedAt} ${label}`);
62
- for (const child of node.children) visit(child, depth + 1);
63
- };
64
-
65
- for (const root of roots) visit(root, 0);
66
- lines.push("(* = current pi session; file-level tree uses pi JSONL parentSessionPath)");
67
- return lines;
68
- }
1
+ import { formatSessionTree } from "../../session/tree.mjs";
2
+
3
+ export function formatSessionList(sessions, currentSessionId = null) {
4
+ if (sessions.length === 0) return ["(no saved sessions)"];
5
+ const lines = sessions.map((session) => {
6
+ const marker = session.id === currentSessionId ? " *" : " ";
7
+ const parent = session.parentSessionId ? ` fork:${session.parentSessionId}` : "";
8
+ return `${marker} ${session.id} ${session.turnCount}t ${session.cwd} ${session.savedAt?.slice(0, 19) ?? "?"}${parent}`;
9
+ });
10
+ lines.push("(* = current session)");
11
+ return lines;
12
+ }
13
+
14
+ export function listSessionCommand({ sessions, currentSessionId, tree = false }) {
15
+ if (tree) return formatSessionTree(sessions, currentSessionId);
16
+ return formatSessionList(sessions, currentSessionId);
17
+ }
18
+
19
+ export function formatPiSessionList(sessions) {
20
+ if (sessions.length === 0) return ["(no pi sessions)"];
21
+ const lines = sessions.map((session) => {
22
+ const label = session.name || session.firstMessage || "(no messages)";
23
+ const savedAt = session.savedAt?.slice(0, 19) ?? "?";
24
+ return ` ${session.id} ${session.turnCount}m ${savedAt} ${label}`;
25
+ });
26
+ lines.push("(pi JSONL session files; use /session to restore a previous session)");
27
+ return lines;
28
+ }
29
+
30
+ export function formatPiSessionTree(sessions, currentSessionId = null) {
31
+ if (sessions.length === 0) return ["(no pi sessions)"];
32
+
33
+ const nodes = new Map();
34
+ const byPath = new Map();
35
+ for (const session of sessions) {
36
+ const node = { ...session, children: [] };
37
+ nodes.set(session.id, node);
38
+ if (session.path) byPath.set(session.path, node);
39
+ }
40
+
41
+ const roots = [];
42
+ for (const node of nodes.values()) {
43
+ const parent = node.parentSessionPath ? byPath.get(node.parentSessionPath) : null;
44
+ if (parent) parent.children.push(node);
45
+ else roots.push(node);
46
+ }
47
+
48
+ const bySavedAtDesc = (a, b) => (b.savedAt ?? "").localeCompare(a.savedAt ?? "");
49
+ const sortDeep = (items) => {
50
+ items.sort(bySavedAtDesc);
51
+ for (const item of items) sortDeep(item.children);
52
+ };
53
+ sortDeep(roots);
54
+
55
+ const lines = [];
56
+ const visit = (node, depth) => {
57
+ const marker = node.id === currentSessionId ? "*" : "-";
58
+ const savedAt = node.savedAt?.slice(0, 19) ?? "?";
59
+ const label = node.name || node.firstMessage || "(no messages)";
60
+ const indent = " ".repeat(depth);
61
+ lines.push(`${indent}${marker} ${node.id} ${node.turnCount ?? 0}m ${savedAt} ${label}`);
62
+ for (const child of node.children) visit(child, depth + 1);
63
+ };
64
+
65
+ for (const root of roots) visit(root, 0);
66
+ lines.push("(* = current pi session; file-level tree uses pi JSONL parentSessionPath)");
67
+ return lines;
68
+ }
@@ -1,26 +1,26 @@
1
- export function parseSessionNameCommand(input) {
2
- if (input !== "/name" && !input.startsWith("/name ")) return { type: "none" };
3
- const name = input.slice("/name".length).trim();
4
- if (!name) return { type: "show" };
5
- if (name.length > 120) return { type: "error", message: "Session name must be 120 characters or less" };
6
- return { type: "set", name };
7
- }
8
-
9
- export function handleSessionNameCommand(command, { runner } = {}) {
10
- if (command.type === "error") return [`Error: ${command.message}`];
11
- if (command.type === "show") return [`Session name: ${runner.engine.sessionName || "(unnamed)"}`];
12
-
13
- const name = runner.setSessionName
14
- ? runner.setSessionName(command.name)
15
- : setEngineSessionName(runner.engine, command.name);
16
- return [`Session named: ${name}`];
17
- }
18
-
19
- function setEngineSessionName(engine, name) {
20
- if (typeof engine.setSessionName === "function") {
21
- engine.setSessionName(name);
22
- } else {
23
- engine.sessionName = String(name || "").trim();
24
- }
25
- return engine.sessionName;
26
- }
1
+ export function parseSessionNameCommand(input) {
2
+ if (input !== "/name" && !input.startsWith("/name ")) return { type: "none" };
3
+ const name = input.slice("/name".length).trim();
4
+ if (!name) return { type: "show" };
5
+ if (name.length > 120) return { type: "error", message: "Session name must be 120 characters or less" };
6
+ return { type: "set", name };
7
+ }
8
+
9
+ export function handleSessionNameCommand(command, { runner } = {}) {
10
+ if (command.type === "error") return [`Error: ${command.message}`];
11
+ if (command.type === "show") return [`Session name: ${runner.engine.sessionName || "(unnamed)"}`];
12
+
13
+ const name = runner.setSessionName
14
+ ? runner.setSessionName(command.name)
15
+ : setEngineSessionName(runner.engine, command.name);
16
+ return [`Session named: ${name}`];
17
+ }
18
+
19
+ function setEngineSessionName(engine, name) {
20
+ if (typeof engine.setSessionName === "function") {
21
+ engine.setSessionName(name);
22
+ } else {
23
+ engine.sessionName = String(name || "").trim();
24
+ }
25
+ return engine.sessionName;
26
+ }
@@ -1,89 +1,89 @@
1
- import { listPiSessionInfos } from "../../session/pi-manager.mjs";
2
- import { loadPiSessionTranscriptTurns } from "../../session/transcript.mjs";
3
- import { resumePiSessionById } from "./pi-session-switch-command.mjs";
4
-
5
- export async function handleSessionSourceCommand(trimmed, {
6
- ui,
7
- runner,
8
- sessionState,
9
- sessionsRoot,
10
- projectMarchDir,
11
- }) {
12
- if (trimmed === "/save") {
13
- const stats = runner.getSessionStats?.();
14
- ui.writeln(`Pi session auto-saved: ${stats?.sessionId ?? sessionState.sessionId}`);
15
- return { handled: true };
16
- }
17
-
18
- if (trimmed === "/session") {
19
- const sessions = await listPiSessionInfos({
20
- cwd: runner.engine.cwd,
21
- projectMarchDir,
22
- });
23
- if (sessions.length === 0) {
24
- ui.writeln("No previous sessions.");
25
- return { handled: true };
26
- }
27
- if (!ui.selectList) {
28
- ui.writeln("Session selector is only available in TUI.");
29
- return { handled: true };
30
- }
31
- const currentSessionId = runner.getSessionStats?.().sessionId ?? null;
32
- const item = await ui.selectList({
33
- items: buildSessionSelectItems(sessions, currentSessionId),
34
- selectedIndex: Math.max(0, sessions.findIndex((session) => session.id === currentSessionId)),
35
- width: 72,
36
- suppressInitialConfirm: true,
37
- searchable: true,
38
- getSearchText: sessionSelectSearchText,
39
- });
40
- if (!item) {
41
- ui.writeln("Session unchanged.");
42
- return { handled: true };
43
- }
44
- const lines = await resumePiSessionById(item.session.id, { runner, sessions, projectMarchDir });
45
- if (isResumeSuccess(lines)) restoreTranscriptFromSession(item.session, ui);
46
- for (const line of lines) {
47
- ui.writeln(line);
48
- }
49
- return { handled: true };
50
- }
51
-
52
- return { handled: false };
53
- }
54
-
55
- export function buildSessionSelectItems(sessions, currentSessionId = null) {
56
- return sessions.map((session) => {
57
- const label = session.name || session.firstMessage || "(no messages)";
58
- const savedAt = formatSessionSelectTime(session.savedAt);
59
- return {
60
- value: session.id,
61
- label,
62
- description: savedAt,
63
- session,
64
- };
65
- });
66
- }
67
-
68
- function sessionSelectSearchText(item) {
69
- const session = item?.session;
70
- return `${item?.label ?? ""} ${item?.description ?? ""} ${session?.id ?? ""} ${session?.name ?? ""} ${session?.firstMessage ?? ""} ${session?.turnCount ?? ""}`;
71
- }
72
-
73
- function restoreTranscriptFromSession(session, ui) {
74
- if (typeof ui.restoreTranscript !== "function") return;
75
- try {
76
- ui.restoreTranscript(loadPiSessionTranscriptTurns(session.path));
77
- } catch (err) {
78
- ui.writeln(`Warning: failed to restore session transcript: ${err.message}`);
79
- }
80
- }
81
-
82
- function isResumeSuccess(lines) {
83
- return Array.isArray(lines) && lines.some((line) => String(line).startsWith("Resumed pi session:"));
84
- }
85
-
86
- function formatSessionSelectTime(value) {
87
- if (!value) return "?";
88
- return String(value).slice(0, 16).replace("T", " ");
89
- }
1
+ import { listPiSessionInfos } from "../../session/pi-manager.mjs";
2
+ import { loadPiSessionTranscriptTurns } from "../../session/transcript.mjs";
3
+ import { resumePiSessionById } from "./pi-session-switch-command.mjs";
4
+
5
+ export async function handleSessionSourceCommand(trimmed, {
6
+ ui,
7
+ runner,
8
+ sessionState,
9
+ sessionsRoot,
10
+ projectMarchDir,
11
+ }) {
12
+ if (trimmed === "/save") {
13
+ const stats = runner.getSessionStats?.();
14
+ ui.writeln(`Pi session auto-saved: ${stats?.sessionId ?? sessionState.sessionId}`);
15
+ return { handled: true };
16
+ }
17
+
18
+ if (trimmed === "/session") {
19
+ const sessions = await listPiSessionInfos({
20
+ cwd: runner.engine.cwd,
21
+ projectMarchDir,
22
+ });
23
+ if (sessions.length === 0) {
24
+ ui.writeln("No previous sessions.");
25
+ return { handled: true };
26
+ }
27
+ if (!ui.selectList) {
28
+ ui.writeln("Session selector is only available in TUI.");
29
+ return { handled: true };
30
+ }
31
+ const currentSessionId = runner.getSessionStats?.().sessionId ?? null;
32
+ const item = await ui.selectList({
33
+ items: buildSessionSelectItems(sessions, currentSessionId),
34
+ selectedIndex: Math.max(0, sessions.findIndex((session) => session.id === currentSessionId)),
35
+ width: 72,
36
+ suppressInitialConfirm: true,
37
+ searchable: true,
38
+ getSearchText: sessionSelectSearchText,
39
+ });
40
+ if (!item) {
41
+ ui.writeln("Session unchanged.");
42
+ return { handled: true };
43
+ }
44
+ const lines = await resumePiSessionById(item.session.id, { runner, sessions, projectMarchDir });
45
+ if (isResumeSuccess(lines)) restoreTranscriptFromSession(item.session, ui);
46
+ for (const line of lines) {
47
+ ui.writeln(line);
48
+ }
49
+ return { handled: true };
50
+ }
51
+
52
+ return { handled: false };
53
+ }
54
+
55
+ export function buildSessionSelectItems(sessions, currentSessionId = null) {
56
+ return sessions.map((session) => {
57
+ const label = session.name || session.firstMessage || "(no messages)";
58
+ const savedAt = formatSessionSelectTime(session.savedAt);
59
+ return {
60
+ value: session.id,
61
+ label,
62
+ description: savedAt,
63
+ session,
64
+ };
65
+ });
66
+ }
67
+
68
+ function sessionSelectSearchText(item) {
69
+ const session = item?.session;
70
+ return `${item?.label ?? ""} ${item?.description ?? ""} ${session?.id ?? ""} ${session?.name ?? ""} ${session?.firstMessage ?? ""} ${session?.turnCount ?? ""}`;
71
+ }
72
+
73
+ function restoreTranscriptFromSession(session, ui) {
74
+ if (typeof ui.restoreTranscript !== "function") return;
75
+ try {
76
+ ui.restoreTranscript(loadPiSessionTranscriptTurns(session.path));
77
+ } catch (err) {
78
+ ui.writeln(`Warning: failed to restore session transcript: ${err.message}`);
79
+ }
80
+ }
81
+
82
+ function isResumeSuccess(lines) {
83
+ return Array.isArray(lines) && lines.some((line) => String(line).startsWith("Resumed pi session:"));
84
+ }
85
+
86
+ function formatSessionSelectTime(value) {
87
+ if (!value) return "?";
88
+ return String(value).slice(0, 16).replace("T", " ");
89
+ }
@@ -1 +1 @@
1
- // Legacy session switch command removed. Use /session to restore previous pi sessions.
1
+ // Legacy session switch command removed. Use /session to restore previous pi sessions.
@@ -1,55 +1,55 @@
1
- export function parseShellCommand(input) {
2
- if (input === "/shell") return { type: "list" };
3
- if (input === "/shell spawn") return { type: "spawn", name: "" };
4
- if (input.startsWith("/shell spawn ")) {
5
- const name = input.slice("/shell spawn ".length).trim();
6
- return name ? { type: "spawn", name } : { type: "spawn", name: "" };
7
- }
8
- if (input.startsWith("/shell ")) {
9
- const idOrName = input.slice("/shell ".length).trim();
10
- return idOrName ? { type: "show", idOrName } : { type: "list" };
11
- }
12
- return { type: "none" };
13
- }
14
-
15
- export function handleShellCommand(command, { shellRuntime = null } = {}) {
16
- if (!shellRuntime) {
17
- return ["Shell runtime is disabled. Restart without --no-shell-runtime to use /shell."];
18
- }
19
- if (command.type === "spawn") {
20
- const shell = shellRuntime.spawnShell({ name: command.name || undefined });
21
- return [
22
- `Spawned shell: ${shell.id} ${shell.name} ${shell.status}`,
23
- "Open the right-side shell pane with Alt+S, then type directly to send input.",
24
- ];
25
- }
26
- if (command.type === "list") {
27
- const shells = shellRuntime.listShells();
28
- if (!shells.length) return ["No shells."];
29
- return [
30
- "Shells:",
31
- ...shells.map(formatShellListItem),
32
- "Use /shell <id-or-name> to inspect recent output, or /shell spawn [name] to start one.",
33
- ];
34
- }
35
- if (command.type === "show") {
36
- const shell = findShell(shellRuntime.listShells(), command.idOrName);
37
- if (!shell) return [`Error: shell not found: ${command.idOrName}`];
38
- const snapshot = shellRuntime.snapshotShell(shell.id);
39
- return [
40
- formatShellListItem(shell),
41
- "Recent output:",
42
- snapshot.screen?.plain || snapshot.plain || "(no output)",
43
- ];
44
- }
45
- return [];
46
- }
47
-
48
- function findShell(shells, idOrName) {
49
- return shells.find((shell) => shell.id === idOrName || shell.name === idOrName) ?? null;
50
- }
51
-
52
- function formatShellListItem(shell) {
53
- const args = shell.args?.length ? ` ${shell.args.join(" ")}` : "";
54
- return `${shell.id} ${shell.name} ${shell.status} ${shell.command}${args} ${shell.lineCount ?? 0} lines`;
55
- }
1
+ export function parseShellCommand(input) {
2
+ if (input === "/shell") return { type: "list" };
3
+ if (input === "/shell spawn") return { type: "spawn", name: "" };
4
+ if (input.startsWith("/shell spawn ")) {
5
+ const name = input.slice("/shell spawn ".length).trim();
6
+ return name ? { type: "spawn", name } : { type: "spawn", name: "" };
7
+ }
8
+ if (input.startsWith("/shell ")) {
9
+ const idOrName = input.slice("/shell ".length).trim();
10
+ return idOrName ? { type: "show", idOrName } : { type: "list" };
11
+ }
12
+ return { type: "none" };
13
+ }
14
+
15
+ export function handleShellCommand(command, { shellRuntime = null } = {}) {
16
+ if (!shellRuntime) {
17
+ return ["Shell runtime is disabled. Restart without --no-shell-runtime to use /shell."];
18
+ }
19
+ if (command.type === "spawn") {
20
+ const shell = shellRuntime.spawnShell({ name: command.name || undefined });
21
+ return [
22
+ `Spawned shell: ${shell.id} ${shell.name} ${shell.status}`,
23
+ "Open the right-side shell pane with Alt+S, then type directly to send input.",
24
+ ];
25
+ }
26
+ if (command.type === "list") {
27
+ const shells = shellRuntime.listShells();
28
+ if (!shells.length) return ["No shells."];
29
+ return [
30
+ "Shells:",
31
+ ...shells.map(formatShellListItem),
32
+ "Use /shell <id-or-name> to inspect recent output, or /shell spawn [name] to start one.",
33
+ ];
34
+ }
35
+ if (command.type === "show") {
36
+ const shell = findShell(shellRuntime.listShells(), command.idOrName);
37
+ if (!shell) return [`Error: shell not found: ${command.idOrName}`];
38
+ const snapshot = shellRuntime.snapshotShell(shell.id);
39
+ return [
40
+ formatShellListItem(shell),
41
+ "Recent output:",
42
+ snapshot.screen?.plain || snapshot.plain || "(no output)",
43
+ ];
44
+ }
45
+ return [];
46
+ }
47
+
48
+ function findShell(shells, idOrName) {
49
+ return shells.find((shell) => shell.id === idOrName || shell.name === idOrName) ?? null;
50
+ }
51
+
52
+ function formatShellListItem(shell) {
53
+ const args = shell.args?.length ? ` ${shell.args.join(" ")}` : "";
54
+ return `${shell.id} ${shell.name} ${shell.status} ${shell.command}${args} ${shell.lineCount ?? 0} lines`;
55
+ }
@@ -1,33 +1,33 @@
1
- import { brightBlack } from "../tui/ui-theme.mjs";
2
-
3
- export function createShellDrawerControls({ shellDrawer, output, requestRender, now = () => Date.now(), toggleDebounceMs = 150 }) {
4
- let lastToggleAt = 0;
5
-
6
- return {
7
- toggle() {
8
- const current = now();
9
- if (current - lastToggleAt < toggleDebounceMs) {
10
- return shellDrawer.isVisible();
11
- }
12
- lastToggleAt = current;
13
- const visible = shellDrawer.toggle();
14
- requestRender();
15
- return visible;
16
- },
17
-
18
- selectNext() {
19
- if (!shellDrawer.isVisible()) return false;
20
- const shell = shellDrawer.selectNextShell();
21
- if (shell) output.writeln(brightBlack(`● shell: ${shell.name} (${shell.id})`));
22
- requestRender();
23
- return shell;
24
- },
25
-
26
- scroll(delta) {
27
- if (!shellDrawer.isVisible()) return false;
28
- const state = shellDrawer.scroll(delta);
29
- requestRender();
30
- return state;
31
- },
32
- };
33
- }
1
+ import { brightBlack } from "../tui/ui-theme.mjs";
2
+
3
+ export function createShellDrawerControls({ shellDrawer, output, requestRender, now = () => Date.now(), toggleDebounceMs = 150 }) {
4
+ let lastToggleAt = 0;
5
+
6
+ return {
7
+ toggle() {
8
+ const current = now();
9
+ if (current - lastToggleAt < toggleDebounceMs) {
10
+ return shellDrawer.isVisible();
11
+ }
12
+ lastToggleAt = current;
13
+ const visible = shellDrawer.toggle();
14
+ requestRender();
15
+ return visible;
16
+ },
17
+
18
+ selectNext() {
19
+ if (!shellDrawer.isVisible()) return false;
20
+ const shell = shellDrawer.selectNextShell();
21
+ if (shell) output.writeln(brightBlack(`● shell: ${shell.name} (${shell.id})`));
22
+ requestRender();
23
+ return shell;
24
+ },
25
+
26
+ scroll(delta) {
27
+ if (!shellDrawer.isVisible()) return false;
28
+ const state = shellDrawer.scroll(delta);
29
+ requestRender();
30
+ return state;
31
+ },
32
+ };
33
+ }