march-cli 0.1.24 → 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.
- package/bin/march.mjs +13 -13
- package/package.json +49 -49
- package/src/agent/command-exec-tool.mjs +172 -172
- package/src/agent/context-stats-tool.mjs +57 -57
- package/src/agent/editing/diff-apply.mjs +28 -28
- package/src/agent/editing/diff-format.mjs +57 -57
- package/src/agent/editing/lsp-report.mjs +69 -69
- package/src/agent/file-edit-tool.mjs +250 -250
- package/src/agent/file-tools/read-file-tool.mjs +112 -112
- package/src/agent/file-tools/read-image-tool.mjs +76 -76
- package/src/agent/model-payload-dumper.mjs +208 -208
- package/src/agent/pi-session/pi-session-sidecar-failure.mjs +10 -10
- package/src/agent/provider/payload-messages.mjs +138 -138
- package/src/agent/runner/codex-large-context-guard.mjs +87 -87
- package/src/agent/runner/codex-transport-compression.mjs +180 -180
- package/src/agent/runner/codex-transport-debug.mjs +113 -113
- package/src/agent/runner/codex-websocket-event-debug.mjs +130 -130
- package/src/agent/runner/fast-model.mjs +36 -36
- package/src/agent/runner/runner-cleanup.mjs +12 -12
- package/src/agent/runner/runner-init.mjs +15 -15
- package/src/agent/runner/runner-session-state.mjs +40 -40
- package/src/agent/runner/runner-utils.mjs +24 -24
- package/src/agent/runner.mjs +299 -299
- package/src/agent/runtime/ipc/ipc-peer.mjs +99 -99
- package/src/agent/runtime/ipc/process-ipc-transport.mjs +16 -16
- package/src/agent/runtime/remote-runner-client.mjs +73 -73
- package/src/agent/runtime/remote-ui-client.mjs +20 -20
- package/src/agent/runtime/runner-ipc-target.mjs +125 -125
- package/src/agent/runtime/runner-process-client.mjs +47 -47
- package/src/agent/runtime/runner-process-entry.mjs +11 -11
- package/src/agent/runtime/runner-process-factory.mjs +111 -108
- package/src/agent/runtime/runner-runtime-host.mjs +79 -79
- package/src/agent/runtime/runtime-factory.mjs +42 -42
- package/src/agent/runtime/runtime-host.mjs +34 -34
- package/src/agent/runtime/ui-event-bridge.mjs +95 -95
- package/src/agent/screen-tools/list-windows-tool.mjs +39 -39
- package/src/agent/screen-tools/screen-tool.mjs +49 -49
- package/src/agent/screen-tools/windows-screen.mjs +133 -133
- package/src/agent/session/session-auto-name.mjs +41 -41
- package/src/agent/session/session-binding.mjs +12 -12
- package/src/agent/session/session-options.mjs +47 -47
- package/src/agent/tool-names.mjs +1 -1
- package/src/agent/tool-result.mjs +3 -3
- package/src/agent/tool-summary.mjs +112 -112
- package/src/agent/tools.mjs +58 -58
- package/src/agent/turn/turn-events.mjs +111 -111
- package/src/agent/turn/turn-logging.mjs +30 -30
- package/src/agent/turn/turn-runner.mjs +196 -196
- package/src/agent/vision-capability.mjs +14 -14
- package/src/auth/login-command.mjs +90 -90
- package/src/auth/storage.mjs +34 -34
- package/src/cli/args.mjs +96 -79
- package/src/cli/commands/copy-command.mjs +87 -87
- package/src/cli/commands/export-command.mjs +206 -206
- package/src/cli/commands/extensions-command.mjs +53 -53
- package/src/cli/commands/help-command.mjs +7 -7
- package/src/cli/commands/model-command.mjs +141 -141
- package/src/cli/commands/paste-image-command.mjs +43 -43
- package/src/cli/commands/provider-command.mjs +59 -59
- package/src/cli/commands/status-command.mjs +196 -194
- package/src/cli/commands/thinking-command.mjs +87 -87
- package/src/cli/fallback-ui.mjs +156 -156
- package/src/cli/input/attachment-tokens.mjs +20 -20
- package/src/cli/input/autocomplete.mjs +74 -74
- package/src/cli/input/external-editor.mjs +39 -39
- package/src/cli/input/file-search/index.mjs +160 -160
- package/src/cli/input/history-store.mjs +35 -35
- package/src/cli/input/image-clipboard.mjs +55 -55
- package/src/cli/input/keybinding-dispatch.mjs +76 -76
- package/src/cli/input/keybindings.mjs +96 -96
- package/src/cli/input/mode-state.mjs +43 -43
- package/src/cli/input/prompt-templates.mjs +84 -84
- package/src/cli/input/select-with-keyboard.mjs +86 -86
- package/src/cli/permissions.mjs +103 -103
- package/src/cli/repl-commands.mjs +86 -86
- package/src/cli/repl-loop.mjs +183 -183
- package/src/cli/selector-list.mjs +21 -21
- package/src/cli/session/pi-session-switch-command.mjs +41 -41
- package/src/cli/session/session-command.mjs +23 -23
- package/src/cli/session/session-list-command.mjs +68 -68
- package/src/cli/session/session-name-command.mjs +26 -26
- package/src/cli/session/session-source-command.mjs +89 -89
- package/src/cli/session/session-switch-command.mjs +1 -1
- package/src/cli/shell/shell-command.mjs +55 -55
- package/src/cli/shell/shell-drawer-controls.mjs +33 -33
- package/src/cli/shell/shell-drawer.mjs +192 -192
- package/src/cli/shell/shell-split-layout.mjs +70 -70
- package/src/cli/slash-commands.mjs +192 -192
- package/src/cli/startup/create-runtime-runner.mjs +61 -61
- package/src/cli/startup/runtime-close.mjs +23 -23
- package/src/cli/startup/startup-banner.mjs +71 -71
- package/src/cli/startup/startup-session.mjs +51 -51
- package/src/cli/status-line-updater.mjs +75 -75
- package/src/cli/tool-output.mjs +9 -9
- package/src/cli/tui/editor/external-editor-runner.mjs +24 -24
- package/src/cli/tui/input/mouse-selection-controller.mjs +91 -91
- package/src/cli/tui/input/mouse-tracking.mjs +20 -20
- package/src/cli/tui/layout/main-pane-layout.mjs +47 -47
- package/src/cli/tui/layout/safe-render-boundary.mjs +46 -46
- package/src/cli/tui/markdown-renderer.mjs +285 -285
- package/src/cli/tui/output/scroll-state.mjs +79 -79
- package/src/cli/tui/output/text-line-renderer.mjs +50 -50
- package/src/cli/tui/output/tool-card-renderer.mjs +59 -59
- package/src/cli/tui/output/visible-lines.mjs +8 -8
- package/src/cli/tui/output-buffer.mjs +293 -293
- package/src/cli/tui/permission-request-ui.mjs +18 -18
- package/src/cli/tui/recall-rendering.mjs +28 -25
- package/src/cli/tui/render/render-scheduler.mjs +26 -26
- package/src/cli/tui/render/stream-delta-buffer.mjs +46 -46
- package/src/cli/tui/select/editor-select-list.mjs +111 -111
- package/src/cli/tui/selection-screen.mjs +269 -269
- package/src/cli/tui/status/retry-status.mjs +72 -72
- package/src/cli/tui/status/spinner-status.mjs +42 -42
- package/src/cli/tui/status/status-bar.mjs +225 -225
- package/src/cli/tui/syntax/highlighting.mjs +260 -260
- package/src/cli/tui/syntax/languages.mjs +91 -91
- package/src/cli/tui/syntax/tree-sitter/bash.highlights.scm +261 -261
- package/src/cli/tui/syntax/tree-sitter/c.highlights.scm +341 -341
- package/src/cli/tui/syntax/tree-sitter/cpp.highlights.scm +268 -268
- package/src/cli/tui/syntax/tree-sitter/csharp.highlights.scm +577 -577
- package/src/cli/tui/syntax/tree-sitter/css.highlights.scm +109 -109
- package/src/cli/tui/syntax/tree-sitter/diff.highlights.scm +49 -49
- package/src/cli/tui/syntax/tree-sitter/go.highlights.scm +254 -254
- package/src/cli/tui/syntax/tree-sitter/html.highlights.scm +13 -13
- package/src/cli/tui/syntax/tree-sitter/java.highlights.scm +330 -330
- package/src/cli/tui/syntax/tree-sitter/json.highlights.scm +38 -38
- package/src/cli/tui/syntax/tree-sitter/php.highlights.scm +203 -203
- package/src/cli/tui/syntax/tree-sitter/python.highlights.scm +137 -137
- package/src/cli/tui/syntax/tree-sitter/ruby.highlights.scm +309 -309
- package/src/cli/tui/syntax/tree-sitter/rust.highlights.scm +531 -531
- package/src/cli/tui/syntax/tree-sitter/toml.highlights.scm +39 -39
- package/src/cli/tui/syntax/tree-sitter/tsx.highlights.scm +35 -35
- package/src/cli/tui/syntax/tree-sitter/typescript.highlights.scm +35 -35
- package/src/cli/tui/syntax/tree-sitter/yaml.highlights.scm +99 -99
- package/src/cli/tui/tool-rendering.mjs +87 -87
- package/src/cli/tui/tui-diff-rendering.mjs +157 -157
- package/src/cli/tui/tui-handlers.mjs +111 -111
- package/src/cli/tui/tui-input-controller.mjs +61 -61
- package/src/cli/tui/ui-theme.mjs +157 -157
- package/src/cli/ui.mjs +297 -297
- package/src/config/config-json.mjs +108 -84
- package/src/config/dotenv.mjs +20 -20
- package/src/config/features.mjs +75 -75
- package/src/config/loader.mjs +156 -143
- package/src/config/settings-command.mjs +97 -97
- package/src/context/engine.mjs +199 -198
- package/src/context/injections.mjs +26 -26
- package/src/context/profiles.mjs +39 -39
- package/src/context/project-context.mjs +20 -20
- package/src/context/session-status.mjs +25 -17
- package/src/context/shell-layers.mjs +23 -23
- package/src/context/system-core/base.md +50 -50
- package/src/context/system-core/prompts/deepseek-v4-pro.md +3 -3
- package/src/context/system-core/prompts/default.md +3 -3
- package/src/context/system-core.mjs +35 -35
- package/src/debug/logger.mjs +141 -141
- package/src/debug/model-context-dumper.mjs +52 -52
- package/src/extensions/discovery.mjs +40 -40
- package/src/extensions/lifecycle-adapter.mjs +210 -210
- package/src/extensions/lifecycle-manifest.mjs +69 -69
- package/src/image-gen/index.mjs +7 -7
- package/src/image-gen/provider.mjs +231 -231
- package/src/image-gen/tool.mjs +84 -84
- package/src/lsp/client.mjs +257 -257
- package/src/lsp/diagnostic-store.mjs +42 -42
- package/src/lsp/diagnostics-format.mjs +72 -72
- package/src/lsp/managed-node-server.mjs +99 -99
- package/src/lsp/path-match.mjs +10 -10
- package/src/lsp/server-definitions.mjs +188 -188
- package/src/lsp/servers.mjs +165 -165
- package/src/lsp/service.mjs +110 -110
- package/src/lsp/status-message.mjs +9 -9
- package/src/lsp/typescript-project-resolver.mjs +186 -186
- package/src/main.mjs +294 -299
- package/src/mcp/client.mjs +195 -195
- package/src/mcp/config.mjs +130 -130
- package/src/mcp/index.mjs +48 -48
- package/src/mcp/tools.mjs +98 -98
- package/src/memory/command.mjs +120 -0
- package/src/memory/markdown/markdown-delete.mjs +23 -23
- package/src/memory/markdown/markdown-format.mjs +128 -128
- package/src/memory/markdown/markdown-recall.mjs +28 -28
- package/src/memory/markdown/ripgrep.mjs +16 -16
- package/src/memory/markdown/sqlite-index.mjs +87 -87
- package/src/memory/markdown-store.mjs +272 -286
- package/src/memory/markdown-tools.mjs +174 -103
- package/src/memory/remote/client.mjs +68 -0
- package/src/memory/remote/config.mjs +52 -0
- package/src/memory/remote/server.mjs +99 -0
- package/src/memory/search.mjs +183 -0
- package/src/network/environment.mjs +131 -131
- package/src/notification/desktop-notifier.mjs +262 -262
- package/src/platform/open-file.mjs +28 -28
- package/src/platform/spawn-command.mjs +27 -27
- package/src/provider/accept-command.mjs +89 -89
- package/src/provider/command.mjs +21 -21
- package/src/provider/config-command.mjs +129 -129
- package/src/provider/custom-provider.mjs +113 -113
- package/src/provider/hosted-tools.mjs +111 -111
- package/src/provider/presets.mjs +72 -72
- package/src/provider/share-command.mjs +79 -79
- package/src/provider/share-payload.mjs +52 -52
- package/src/session/attachment-display.mjs +16 -16
- package/src/session/attachment-references.mjs +65 -65
- package/src/session/attachments.mjs +140 -140
- package/src/session/persist.mjs +1 -1
- package/src/session/pi-manager.mjs +34 -34
- package/src/session/session-utils.mjs +16 -16
- package/src/session/sidecar-sync.mjs +19 -19
- package/src/session/sidecar.mjs +69 -69
- package/src/session/transcript.mjs +83 -83
- package/src/session/tree.mjs +42 -42
- package/src/shell/cli-runtime.mjs +11 -11
- package/src/shell/hints.mjs +12 -12
- package/src/shell/node-pty-adapter.mjs +81 -81
- package/src/shell/runtime-state.mjs +126 -126
- package/src/shell/runtime.mjs +252 -252
- package/src/shell/screen-buffer.mjs +136 -136
- package/src/shell/tool-read.mjs +74 -74
- package/src/shell/tools.mjs +299 -299
- package/src/supergrok/actions/image-generate.mjs +60 -60
- package/src/supergrok/actions/search.mjs +78 -78
- package/src/supergrok/auth.mjs +36 -36
- package/src/supergrok/constants.mjs +18 -18
- package/src/supergrok/oauth-provider.mjs +278 -278
- package/src/supergrok/provider.mjs +35 -35
- package/src/supergrok/response.mjs +76 -76
- package/src/supergrok/tool.mjs +61 -61
- package/src/text/ansi.mjs +3 -3
- package/src/web/config-command.mjs +43 -43
- package/src/web/fetch.mjs +78 -78
- package/src/web/presets.mjs +16 -16
- package/src/web/search.mjs +83 -83
- package/src/web/tools.mjs +107 -107
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
|
|
3
|
-
export const DEFAULT_TRANSCRIPT_TURN_LIMIT = 20;
|
|
4
|
-
|
|
5
|
-
export function loadPiSessionTranscriptTurns(sessionPath, { limit = DEFAULT_TRANSCRIPT_TURN_LIMIT } = {}) {
|
|
6
|
-
const entries = readPiSessionEntries(sessionPath);
|
|
7
|
-
const turns = [];
|
|
8
|
-
let current = null;
|
|
9
|
-
|
|
10
|
-
for (const entry of entries) {
|
|
11
|
-
if (entry?.type !== "message") continue;
|
|
12
|
-
const message = entry.message;
|
|
13
|
-
if (message?.role === "user") {
|
|
14
|
-
current = { userMessage: extractMessageText(message), assistantMessage: "" };
|
|
15
|
-
turns.push(current);
|
|
16
|
-
continue;
|
|
17
|
-
}
|
|
18
|
-
if (message?.role === "assistant") {
|
|
19
|
-
const text = extractMessageText(message);
|
|
20
|
-
if (!current) {
|
|
21
|
-
current = { userMessage: "", assistantMessage: text };
|
|
22
|
-
turns.push(current);
|
|
23
|
-
} else if (current.assistantMessage) {
|
|
24
|
-
current.assistantMessage += `\n\n${text}`;
|
|
25
|
-
} else {
|
|
26
|
-
current.assistantMessage = text;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const normalized = turns
|
|
32
|
-
.filter((turn) => turn.userMessage || turn.assistantMessage)
|
|
33
|
-
.map((turn, index) => ({ index: index + 1, ...turn }));
|
|
34
|
-
return normalized.slice(-Math.max(0, limit));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function writeTranscriptToOutput(output, turns) {
|
|
38
|
-
if (!Array.isArray(turns) || turns.length === 0) return;
|
|
39
|
-
for (const turn of turns) {
|
|
40
|
-
if (turn.userMessage) {
|
|
41
|
-
output.writeln("You");
|
|
42
|
-
for (const line of String(turn.userMessage).split(/\r?\n/)) output.writeln(line);
|
|
43
|
-
}
|
|
44
|
-
if (turn.assistantMessage) {
|
|
45
|
-
output.writeln("");
|
|
46
|
-
output.writeln("March");
|
|
47
|
-
output.writeMarkdown(String(turn.assistantMessage));
|
|
48
|
-
output.ensureNewline();
|
|
49
|
-
output.sealCurrentText();
|
|
50
|
-
}
|
|
51
|
-
output.writeln("");
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function readPiSessionEntries(sessionPath) {
|
|
56
|
-
const text = readFileSync(sessionPath, "utf8");
|
|
57
|
-
const entries = [];
|
|
58
|
-
for (const line of text.split(/\r?\n/)) {
|
|
59
|
-
if (!line.trim()) continue;
|
|
60
|
-
try {
|
|
61
|
-
entries.push(JSON.parse(line));
|
|
62
|
-
} catch {
|
|
63
|
-
// Ignore partial or malformed JSONL entries; transcript restore is best effort.
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return entries;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function extractMessageText(message) {
|
|
70
|
-
const content = message?.content;
|
|
71
|
-
if (typeof content === "string") return content;
|
|
72
|
-
if (Array.isArray(content)) {
|
|
73
|
-
return content
|
|
74
|
-
.map((part) => {
|
|
75
|
-
if (typeof part === "string") return part;
|
|
76
|
-
if (part?.type === "text" || part?.type === "input_text") return part.text ?? "";
|
|
77
|
-
return "";
|
|
78
|
-
})
|
|
79
|
-
.filter(Boolean)
|
|
80
|
-
.join("\n");
|
|
81
|
-
}
|
|
82
|
-
return "";
|
|
83
|
-
}
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_TRANSCRIPT_TURN_LIMIT = 20;
|
|
4
|
+
|
|
5
|
+
export function loadPiSessionTranscriptTurns(sessionPath, { limit = DEFAULT_TRANSCRIPT_TURN_LIMIT } = {}) {
|
|
6
|
+
const entries = readPiSessionEntries(sessionPath);
|
|
7
|
+
const turns = [];
|
|
8
|
+
let current = null;
|
|
9
|
+
|
|
10
|
+
for (const entry of entries) {
|
|
11
|
+
if (entry?.type !== "message") continue;
|
|
12
|
+
const message = entry.message;
|
|
13
|
+
if (message?.role === "user") {
|
|
14
|
+
current = { userMessage: extractMessageText(message), assistantMessage: "" };
|
|
15
|
+
turns.push(current);
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (message?.role === "assistant") {
|
|
19
|
+
const text = extractMessageText(message);
|
|
20
|
+
if (!current) {
|
|
21
|
+
current = { userMessage: "", assistantMessage: text };
|
|
22
|
+
turns.push(current);
|
|
23
|
+
} else if (current.assistantMessage) {
|
|
24
|
+
current.assistantMessage += `\n\n${text}`;
|
|
25
|
+
} else {
|
|
26
|
+
current.assistantMessage = text;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const normalized = turns
|
|
32
|
+
.filter((turn) => turn.userMessage || turn.assistantMessage)
|
|
33
|
+
.map((turn, index) => ({ index: index + 1, ...turn }));
|
|
34
|
+
return normalized.slice(-Math.max(0, limit));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function writeTranscriptToOutput(output, turns) {
|
|
38
|
+
if (!Array.isArray(turns) || turns.length === 0) return;
|
|
39
|
+
for (const turn of turns) {
|
|
40
|
+
if (turn.userMessage) {
|
|
41
|
+
output.writeln("You");
|
|
42
|
+
for (const line of String(turn.userMessage).split(/\r?\n/)) output.writeln(line);
|
|
43
|
+
}
|
|
44
|
+
if (turn.assistantMessage) {
|
|
45
|
+
output.writeln("");
|
|
46
|
+
output.writeln("March");
|
|
47
|
+
output.writeMarkdown(String(turn.assistantMessage));
|
|
48
|
+
output.ensureNewline();
|
|
49
|
+
output.sealCurrentText();
|
|
50
|
+
}
|
|
51
|
+
output.writeln("");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function readPiSessionEntries(sessionPath) {
|
|
56
|
+
const text = readFileSync(sessionPath, "utf8");
|
|
57
|
+
const entries = [];
|
|
58
|
+
for (const line of text.split(/\r?\n/)) {
|
|
59
|
+
if (!line.trim()) continue;
|
|
60
|
+
try {
|
|
61
|
+
entries.push(JSON.parse(line));
|
|
62
|
+
} catch {
|
|
63
|
+
// Ignore partial or malformed JSONL entries; transcript restore is best effort.
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return entries;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function extractMessageText(message) {
|
|
70
|
+
const content = message?.content;
|
|
71
|
+
if (typeof content === "string") return content;
|
|
72
|
+
if (Array.isArray(content)) {
|
|
73
|
+
return content
|
|
74
|
+
.map((part) => {
|
|
75
|
+
if (typeof part === "string") return part;
|
|
76
|
+
if (part?.type === "text" || part?.type === "input_text") return part.text ?? "";
|
|
77
|
+
return "";
|
|
78
|
+
})
|
|
79
|
+
.filter(Boolean)
|
|
80
|
+
.join("\n");
|
|
81
|
+
}
|
|
82
|
+
return "";
|
|
83
|
+
}
|
package/src/session/tree.mjs
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
export function buildSessionTree(sessions) {
|
|
2
|
-
const nodes = new Map();
|
|
3
|
-
for (const session of sessions) {
|
|
4
|
-
nodes.set(session.id, { ...session, children: [] });
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
const roots = [];
|
|
8
|
-
for (const node of nodes.values()) {
|
|
9
|
-
const parent = node.parentSessionId ? nodes.get(node.parentSessionId) : null;
|
|
10
|
-
if (parent) {
|
|
11
|
-
parent.children.push(node);
|
|
12
|
-
} else {
|
|
13
|
-
roots.push(node);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const bySavedAtDesc = (a, b) => (b.savedAt ?? "").localeCompare(a.savedAt ?? "");
|
|
18
|
-
const sortDeep = (items) => {
|
|
19
|
-
items.sort(bySavedAtDesc);
|
|
20
|
-
for (const item of items) sortDeep(item.children);
|
|
21
|
-
};
|
|
22
|
-
sortDeep(roots);
|
|
23
|
-
return roots;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function formatSessionTree(sessions, currentSessionId = null) {
|
|
27
|
-
const roots = buildSessionTree(sessions);
|
|
28
|
-
if (roots.length === 0) return ["(no saved sessions)"];
|
|
29
|
-
|
|
30
|
-
const lines = [];
|
|
31
|
-
const visit = (node, depth) => {
|
|
32
|
-
const marker = node.id === currentSessionId ? "*" : "-";
|
|
33
|
-
const savedAt = node.savedAt?.slice(0, 19) ?? "?";
|
|
34
|
-
const indent = " ".repeat(depth);
|
|
35
|
-
lines.push(`${indent}${marker} ${node.id} ${node.turnCount ?? 0}t ${savedAt}`);
|
|
36
|
-
for (const child of node.children) visit(child, depth + 1);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
for (const root of roots) visit(root, 0);
|
|
40
|
-
lines.push("(* = current session)");
|
|
41
|
-
return lines;
|
|
42
|
-
}
|
|
1
|
+
export function buildSessionTree(sessions) {
|
|
2
|
+
const nodes = new Map();
|
|
3
|
+
for (const session of sessions) {
|
|
4
|
+
nodes.set(session.id, { ...session, children: [] });
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const roots = [];
|
|
8
|
+
for (const node of nodes.values()) {
|
|
9
|
+
const parent = node.parentSessionId ? nodes.get(node.parentSessionId) : null;
|
|
10
|
+
if (parent) {
|
|
11
|
+
parent.children.push(node);
|
|
12
|
+
} else {
|
|
13
|
+
roots.push(node);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const bySavedAtDesc = (a, b) => (b.savedAt ?? "").localeCompare(a.savedAt ?? "");
|
|
18
|
+
const sortDeep = (items) => {
|
|
19
|
+
items.sort(bySavedAtDesc);
|
|
20
|
+
for (const item of items) sortDeep(item.children);
|
|
21
|
+
};
|
|
22
|
+
sortDeep(roots);
|
|
23
|
+
return roots;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function formatSessionTree(sessions, currentSessionId = null) {
|
|
27
|
+
const roots = buildSessionTree(sessions);
|
|
28
|
+
if (roots.length === 0) return ["(no saved sessions)"];
|
|
29
|
+
|
|
30
|
+
const lines = [];
|
|
31
|
+
const visit = (node, depth) => {
|
|
32
|
+
const marker = node.id === currentSessionId ? "*" : "-";
|
|
33
|
+
const savedAt = node.savedAt?.slice(0, 19) ?? "?";
|
|
34
|
+
const indent = " ".repeat(depth);
|
|
35
|
+
lines.push(`${indent}${marker} ${node.id} ${node.turnCount ?? 0}t ${savedAt}`);
|
|
36
|
+
for (const child of node.children) visit(child, depth + 1);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
for (const root of roots) visit(root, 0);
|
|
40
|
+
lines.push("(* = current session)");
|
|
41
|
+
return lines;
|
|
42
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { createShellRuntime } from "./runtime.mjs";
|
|
2
|
-
import { createNodePtyAdapterFactory } from "./node-pty-adapter.mjs";
|
|
3
|
-
|
|
4
|
-
export function createCliShellRuntime({ cwd = process.cwd(), env = process.env } = {}) {
|
|
5
|
-
return createShellRuntime({
|
|
6
|
-
createPty: createNodePtyAdapterFactory({
|
|
7
|
-
defaultCwd: cwd,
|
|
8
|
-
defaultEnv: env,
|
|
9
|
-
}),
|
|
10
|
-
});
|
|
11
|
-
}
|
|
1
|
+
import { createShellRuntime } from "./runtime.mjs";
|
|
2
|
+
import { createNodePtyAdapterFactory } from "./node-pty-adapter.mjs";
|
|
3
|
+
|
|
4
|
+
export function createCliShellRuntime({ cwd = process.cwd(), env = process.env } = {}) {
|
|
5
|
+
return createShellRuntime({
|
|
6
|
+
createPty: createNodePtyAdapterFactory({
|
|
7
|
+
defaultCwd: cwd,
|
|
8
|
+
defaultEnv: env,
|
|
9
|
+
}),
|
|
10
|
+
});
|
|
11
|
+
}
|
package/src/shell/hints.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
export function formatShellHints(shellRuntime, { limit = 5 } = {}) {
|
|
2
|
-
const shells = (shellRuntime?.listShells?.() ?? []).filter((shell) => shell.status !== "killed").slice(0, limit);
|
|
3
|
-
if (shells.length === 0) return "";
|
|
4
|
-
const lines = ["[shell_hints]"];
|
|
5
|
-
for (const shell of shells) {
|
|
6
|
-
const args = shell.args?.length ? ` ${shell.args.join(" ")}` : "";
|
|
7
|
-
const count = shell.scrollbackLineCount ?? shell.lineCount ?? 0;
|
|
8
|
-
lines.push(`- ${shell.id} ${shell.name} ${shell.status} command: ${shell.command}${args} cwd: ${shell.cwd} lines: ${count}`);
|
|
9
|
-
}
|
|
10
|
-
lines.push("Use terminal_read or terminal_snapshot to inspect shell output.");
|
|
11
|
-
return lines.join("\n");
|
|
12
|
-
}
|
|
1
|
+
export function formatShellHints(shellRuntime, { limit = 5 } = {}) {
|
|
2
|
+
const shells = (shellRuntime?.listShells?.() ?? []).filter((shell) => shell.status !== "killed").slice(0, limit);
|
|
3
|
+
if (shells.length === 0) return "";
|
|
4
|
+
const lines = ["[shell_hints]"];
|
|
5
|
+
for (const shell of shells) {
|
|
6
|
+
const args = shell.args?.length ? ` ${shell.args.join(" ")}` : "";
|
|
7
|
+
const count = shell.scrollbackLineCount ?? shell.lineCount ?? 0;
|
|
8
|
+
lines.push(`- ${shell.id} ${shell.name} ${shell.status} command: ${shell.command}${args} cwd: ${shell.cwd} lines: ${count}`);
|
|
9
|
+
}
|
|
10
|
+
lines.push("Use terminal_read or terminal_snapshot to inspect shell output.");
|
|
11
|
+
return lines.join("\n");
|
|
12
|
+
}
|
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
import pty from "node-pty";
|
|
2
|
-
|
|
3
|
-
export function createNodePtyAdapterFactory({
|
|
4
|
-
ptyModule = pty,
|
|
5
|
-
defaultCwd = process.cwd(),
|
|
6
|
-
defaultEnv = process.env,
|
|
7
|
-
platform = process.platform,
|
|
8
|
-
} = {}) {
|
|
9
|
-
return function createNodePtyAdapter({
|
|
10
|
-
command,
|
|
11
|
-
args = [],
|
|
12
|
-
cwd = defaultCwd,
|
|
13
|
-
env = defaultEnv,
|
|
14
|
-
cols = 80,
|
|
15
|
-
rows = 24,
|
|
16
|
-
onData,
|
|
17
|
-
onExit,
|
|
18
|
-
onError,
|
|
19
|
-
}) {
|
|
20
|
-
const resolved = resolveShellCommand({ command, args, platform });
|
|
21
|
-
let term;
|
|
22
|
-
try {
|
|
23
|
-
term = ptyModule.spawn(resolved.command, resolved.args, {
|
|
24
|
-
name: "xterm-color",
|
|
25
|
-
cols,
|
|
26
|
-
rows,
|
|
27
|
-
cwd,
|
|
28
|
-
env,
|
|
29
|
-
});
|
|
30
|
-
} catch (error) {
|
|
31
|
-
onError?.(error);
|
|
32
|
-
throw error;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
let disposed = false;
|
|
36
|
-
const disposeTerminal = () => {
|
|
37
|
-
if (disposed) return;
|
|
38
|
-
disposed = true;
|
|
39
|
-
// node-pty's Windows kill path can emit noisy AttachConsole failures after
|
|
40
|
-
// a natural exit. Closing the backing socket releases Node handles without
|
|
41
|
-
// forcing the PTY helper down the kill path.
|
|
42
|
-
if (typeof term._socket?.destroy === "function") {
|
|
43
|
-
term._socket.destroy();
|
|
44
|
-
} else if (typeof term.destroy === "function") {
|
|
45
|
-
term.destroy();
|
|
46
|
-
} else if (typeof term.kill === "function") {
|
|
47
|
-
term.kill();
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
term.onData?.((chunk) => onData?.(chunk));
|
|
52
|
-
term.onExit?.((event) => {
|
|
53
|
-
onExit?.(event);
|
|
54
|
-
disposeTerminal();
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
return {
|
|
58
|
-
write: (text) => term.write(String(text ?? "")),
|
|
59
|
-
resize: (nextCols, nextRows) => term.resize?.(nextCols, nextRows),
|
|
60
|
-
kill: () => disposeTerminal(),
|
|
61
|
-
dispose: () => disposeTerminal(),
|
|
62
|
-
};
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function resolveShellCommand({ command, args = [], platform = process.platform }) {
|
|
67
|
-
if (command) return { command: normalizeWindowsShellCommand(command, platform), args: [...args] };
|
|
68
|
-
if (platform === "win32") {
|
|
69
|
-
return { command: "powershell.exe", args: ["-NoLogo", "-NoProfile"] };
|
|
70
|
-
}
|
|
71
|
-
return { command: process.env.SHELL || "sh", args: [] };
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function normalizeWindowsShellCommand(command, platform) {
|
|
75
|
-
if (platform !== "win32") return command;
|
|
76
|
-
const normalized = String(command).trim().toLowerCase();
|
|
77
|
-
if (normalized === "powershell") return "powershell.exe";
|
|
78
|
-
if (normalized === "pwsh") return "pwsh.exe";
|
|
79
|
-
if (normalized === "cmd") return "cmd.exe";
|
|
80
|
-
return command;
|
|
81
|
-
}
|
|
1
|
+
import pty from "node-pty";
|
|
2
|
+
|
|
3
|
+
export function createNodePtyAdapterFactory({
|
|
4
|
+
ptyModule = pty,
|
|
5
|
+
defaultCwd = process.cwd(),
|
|
6
|
+
defaultEnv = process.env,
|
|
7
|
+
platform = process.platform,
|
|
8
|
+
} = {}) {
|
|
9
|
+
return function createNodePtyAdapter({
|
|
10
|
+
command,
|
|
11
|
+
args = [],
|
|
12
|
+
cwd = defaultCwd,
|
|
13
|
+
env = defaultEnv,
|
|
14
|
+
cols = 80,
|
|
15
|
+
rows = 24,
|
|
16
|
+
onData,
|
|
17
|
+
onExit,
|
|
18
|
+
onError,
|
|
19
|
+
}) {
|
|
20
|
+
const resolved = resolveShellCommand({ command, args, platform });
|
|
21
|
+
let term;
|
|
22
|
+
try {
|
|
23
|
+
term = ptyModule.spawn(resolved.command, resolved.args, {
|
|
24
|
+
name: "xterm-color",
|
|
25
|
+
cols,
|
|
26
|
+
rows,
|
|
27
|
+
cwd,
|
|
28
|
+
env,
|
|
29
|
+
});
|
|
30
|
+
} catch (error) {
|
|
31
|
+
onError?.(error);
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let disposed = false;
|
|
36
|
+
const disposeTerminal = () => {
|
|
37
|
+
if (disposed) return;
|
|
38
|
+
disposed = true;
|
|
39
|
+
// node-pty's Windows kill path can emit noisy AttachConsole failures after
|
|
40
|
+
// a natural exit. Closing the backing socket releases Node handles without
|
|
41
|
+
// forcing the PTY helper down the kill path.
|
|
42
|
+
if (typeof term._socket?.destroy === "function") {
|
|
43
|
+
term._socket.destroy();
|
|
44
|
+
} else if (typeof term.destroy === "function") {
|
|
45
|
+
term.destroy();
|
|
46
|
+
} else if (typeof term.kill === "function") {
|
|
47
|
+
term.kill();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
term.onData?.((chunk) => onData?.(chunk));
|
|
52
|
+
term.onExit?.((event) => {
|
|
53
|
+
onExit?.(event);
|
|
54
|
+
disposeTerminal();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
write: (text) => term.write(String(text ?? "")),
|
|
59
|
+
resize: (nextCols, nextRows) => term.resize?.(nextCols, nextRows),
|
|
60
|
+
kill: () => disposeTerminal(),
|
|
61
|
+
dispose: () => disposeTerminal(),
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function resolveShellCommand({ command, args = [], platform = process.platform }) {
|
|
67
|
+
if (command) return { command: normalizeWindowsShellCommand(command, platform), args: [...args] };
|
|
68
|
+
if (platform === "win32") {
|
|
69
|
+
return { command: "powershell.exe", args: ["-NoLogo", "-NoProfile"] };
|
|
70
|
+
}
|
|
71
|
+
return { command: process.env.SHELL || "sh", args: [] };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function normalizeWindowsShellCommand(command, platform) {
|
|
75
|
+
if (platform !== "win32") return command;
|
|
76
|
+
const normalized = String(command).trim().toLowerCase();
|
|
77
|
+
if (normalized === "powershell") return "powershell.exe";
|
|
78
|
+
if (normalized === "pwsh") return "pwsh.exe";
|
|
79
|
+
if (normalized === "cmd") return "cmd.exe";
|
|
80
|
+
return command;
|
|
81
|
+
}
|