march-cli 0.1.33 → 0.1.35
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/package.json +12 -1
- package/src/agent/lifecycle/runner-lifecycle.mjs +16 -0
- package/src/agent/lifecycle/runtime-restart-tool.mjs +22 -0
- package/src/agent/runner.mjs +9 -14
- package/src/agent/runtime/remote-runner-client.mjs +7 -15
- package/src/agent/runtime/runner-ipc-target.mjs +3 -22
- package/src/agent/runtime/runner-process-client.mjs +101 -24
- package/src/agent/runtime/runner-runtime-host.mjs +2 -0
- package/src/agent/runtime/state/runner-state.mjs +80 -0
- package/src/agent/session/session-options.mjs +2 -1
- package/src/agent/tools.mjs +3 -1
- package/src/cli/args.mjs +14 -3
- package/src/cli/commands/catalog/visible-commands.mjs +5 -0
- package/src/cli/commands/help-command.mjs +1 -7
- package/src/cli/commands/registry/slash-command-registry.mjs +293 -0
- package/src/cli/input/autocomplete.mjs +2 -25
- package/src/cli/repl-loop.mjs +24 -41
- package/src/cli/slash-commands.mjs +19 -185
- package/src/cli/startup/app-runtime.mjs +201 -0
- package/src/cli/startup/configured-command.mjs +9 -0
- package/src/cli/startup/early-command.mjs +29 -0
- package/src/cli/turn/turn-input-preparer.mjs +41 -0
- package/src/main.mjs +47 -242
- package/src/memory/markdown/memory-id.mjs +36 -0
- package/src/memory/markdown-store.mjs +17 -6
- package/src/memory/markdown-tools.mjs +3 -2
- package/src/web-ui/command.mjs +112 -0
- package/src/web-ui/dist/assets/index-BUmhnID4.css +1 -0
- package/src/web-ui/dist/assets/index-CtuqTjcB.js +1845 -0
- package/src/web-ui/dist/index.html +13 -0
- package/src/web-ui/index.html +12 -0
- package/src/web-ui/runtime-host.mjs +185 -0
- package/src/web-ui/server.mjs +139 -0
- package/src/web-ui/session-manager.mjs +109 -0
- package/src/web-ui/src/App.tsx +7 -0
- package/src/web-ui/src/components/AppShell.tsx +47 -0
- package/src/web-ui/src/components/Composer.tsx +47 -0
- package/src/web-ui/src/components/FileExplorer.tsx +46 -0
- package/src/web-ui/src/components/RightSidebar.tsx +70 -0
- package/src/web-ui/src/components/SessionTimeline.tsx +31 -0
- package/src/web-ui/src/components/timeline/TimelineBlocks.tsx +109 -0
- package/src/web-ui/src/components/timeline/TimelineList.tsx +14 -0
- package/src/web-ui/src/fileTreeAdapter.ts +51 -0
- package/src/web-ui/src/main.tsx +11 -0
- package/src/web-ui/src/mockData.ts +87 -0
- package/src/web-ui/src/model.ts +62 -0
- package/src/web-ui/src/runtime/client.ts +74 -0
- package/src/web-ui/src/runtime/runtimeTimeline.ts +88 -0
- package/src/web-ui/src/runtime/useWebRuntime.ts +132 -0
- package/src/web-ui/src/styles/shell.css +156 -0
- package/src/web-ui/src/styles/tokens.css +116 -0
- package/src/web-ui/src/timelineAdapter.ts +43 -0
- package/src/web-ui/src/vite-env.d.ts +1 -0
- package/src/web-ui/tsconfig.json +20 -0
- package/src/web-ui/vite.config.mjs +11 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "march-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.35",
|
|
4
4
|
"description": "March CLI — terminal-native coding agent with context reconstruction",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/main.mjs",
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
"docs:dev": "vitepress dev docs --host 127.0.0.1",
|
|
23
23
|
"docs:build": "vitepress build docs",
|
|
24
24
|
"docs:preview": "vitepress preview docs --host 127.0.0.1",
|
|
25
|
+
"web:dev": "node bin/march.mjs web --dev",
|
|
26
|
+
"web:build": "tsc -p src/web-ui/tsconfig.json && vite build --config src/web-ui/vite.config.mjs",
|
|
27
|
+
"web:preview": "npm run web:build && node src/web-ui/server.mjs",
|
|
25
28
|
"context": "cd .. && node march-cli/bin/march.mjs --dump-context",
|
|
26
29
|
"notify:experiment": "node scripts/notify-experiment.mjs",
|
|
27
30
|
"publish:env": "node scripts/npm-publish-from-env.mjs"
|
|
@@ -31,10 +34,13 @@
|
|
|
31
34
|
"@earendil-works/pi-coding-agent": "^0.74.0",
|
|
32
35
|
"@earendil-works/pi-tui": "^0.74.0",
|
|
33
36
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
37
|
+
"@pierre/trees": "^1.0.0-beta.4",
|
|
34
38
|
"@xterm/headless": "^5.5.0",
|
|
35
39
|
"marked": "^18.0.3",
|
|
36
40
|
"node-notifier": "^10.0.1",
|
|
37
41
|
"node-pty": "^1.1.0",
|
|
42
|
+
"react": "^18.3.1",
|
|
43
|
+
"react-dom": "^18.3.1",
|
|
38
44
|
"typebox": "^1.0.58",
|
|
39
45
|
"undici": "^7.25.0",
|
|
40
46
|
"web-tree-sitter": "^0.26.8",
|
|
@@ -44,6 +50,11 @@
|
|
|
44
50
|
"@vscode/ripgrep": "^1.18.0"
|
|
45
51
|
},
|
|
46
52
|
"devDependencies": {
|
|
53
|
+
"@types/react": "^18.3.29",
|
|
54
|
+
"@types/react-dom": "^18.3.7",
|
|
55
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
56
|
+
"typescript": "^6.0.3",
|
|
57
|
+
"vite": "^8.0.14",
|
|
47
58
|
"vitepress": "^1.6.4"
|
|
48
59
|
}
|
|
49
60
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function createRunnerLifecycle() {
|
|
2
|
+
let pendingAction = null;
|
|
3
|
+
return {
|
|
4
|
+
requestRuntimeRestart({ reason = "" } = {}) {
|
|
5
|
+
pendingAction = { type: "restart_runtime", reason };
|
|
6
|
+
},
|
|
7
|
+
takePendingAction() {
|
|
8
|
+
const action = pendingAction;
|
|
9
|
+
pendingAction = null;
|
|
10
|
+
return action;
|
|
11
|
+
},
|
|
12
|
+
clearPendingAction() {
|
|
13
|
+
pendingAction = null;
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
import { toolText } from "../tool-result.mjs";
|
|
4
|
+
|
|
5
|
+
export function createRuntimeRestartTool({ lifecycle }) {
|
|
6
|
+
return defineTool({
|
|
7
|
+
name: "request_runtime_restart",
|
|
8
|
+
label: "Request Runtime Restart",
|
|
9
|
+
description: "Request March to restart the runtime after the current turn so the next turn loads updated runner/tool code from disk.",
|
|
10
|
+
parameters: Type.Object({
|
|
11
|
+
reason: Type.Optional(Type.String({ description: "Why the runtime needs to restart" })),
|
|
12
|
+
}),
|
|
13
|
+
execute: async (_toolCallId, params = {}) => {
|
|
14
|
+
const reason = String(params.reason ?? "").trim();
|
|
15
|
+
lifecycle?.requestRuntimeRestart?.({ reason });
|
|
16
|
+
return toolText(
|
|
17
|
+
"March runtime restart requested. The current turn will finish first; the next turn will use the latest code from disk.",
|
|
18
|
+
{ lifecycleAction: { type: "restart_runtime", reason } },
|
|
19
|
+
);
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
}
|
package/src/agent/runner.mjs
CHANGED
|
@@ -26,6 +26,7 @@ import { appendFastVariants, createFastModelEntry, fromFastEntryModel, isFastPro
|
|
|
26
26
|
import { registerSuperGrokProvider } from "../supergrok/provider.mjs";
|
|
27
27
|
import { registerCustomProviders } from "../provider/custom-provider.mjs";
|
|
28
28
|
import { injectHostedTools } from "../provider/hosted-tools.mjs";
|
|
29
|
+
import { createRunnerLifecycle } from "./lifecycle/runner-lifecycle.mjs";
|
|
29
30
|
export { MARCH_BASE_TOOL_NAMES, installModelPayloadDumper };
|
|
30
31
|
export { createDefaultSessionManager, resolveRunnerSessionManager } from "./runner/runner-init.mjs";
|
|
31
32
|
export { getRunnerSessionStats, syncEngineSessionState } from "./runner/runner-session-state.mjs";
|
|
@@ -58,6 +59,7 @@ export async function createRunner({ cwd, modelId = null, provider = null, provi
|
|
|
58
59
|
const resolvedSessionManager = resolveRunnerSessionManager(cwd, sessionManager);
|
|
59
60
|
const sessionBinding = createSessionBinding(null);
|
|
60
61
|
let currentModelCallKind = "model", currentTurnId = null, currentPromptForContext = "";
|
|
62
|
+
const lifecycle = createRunnerLifecycle();
|
|
61
63
|
let currentTurnContextMode = "rebuild";
|
|
62
64
|
let nextTurnContextMode = "rebuild";
|
|
63
65
|
let lastNotificationResult = null, runtimeHost = null, lifecycleAdapter = null;
|
|
@@ -70,7 +72,7 @@ export async function createRunner({ cwd, modelId = null, provider = null, provi
|
|
|
70
72
|
sessionManager: resolvedSessionManager, sessionBinding, engine, ui: runtimeUi,
|
|
71
73
|
projectMarchDir,
|
|
72
74
|
memoryTools, memoryStore, shellRuntime, lspService, mcpTools, webTools,
|
|
73
|
-
permissionController, extensionPaths, hostedTools,
|
|
75
|
+
lifecycle, permissionController, extensionPaths, hostedTools,
|
|
74
76
|
onRebind: (session) => {
|
|
75
77
|
installModelPayloadDumper(session, modelContextDumper, () => currentModelCallKind, onLoggedModelPayload, injectMarchSystemContext);
|
|
76
78
|
syncEngineSessionState(engine, session);
|
|
@@ -82,7 +84,7 @@ export async function createRunner({ cwd, modelId = null, provider = null, provi
|
|
|
82
84
|
} else {
|
|
83
85
|
const sessionOptions = resolveRunnerSessionOptions({
|
|
84
86
|
cwd, stateRoot, provider, modelId, modelRegistry, engine, ui: runtimeUi,
|
|
85
|
-
memoryTools, shellRuntime, lspService, mcpTools, webTools, permissionController,
|
|
87
|
+
memoryTools, shellRuntime, lspService, mcpTools, webTools, lifecycle, permissionController,
|
|
86
88
|
authStorage: resolvedAuth, projectMarchDir,
|
|
87
89
|
getCurrentModel: () => sessionBinding.get()?.model ?? selectedModel,
|
|
88
90
|
});
|
|
@@ -115,6 +117,7 @@ export async function createRunner({ cwd, modelId = null, provider = null, provi
|
|
|
115
117
|
const contextMode = nextTurnContextMode;
|
|
116
118
|
currentTurnContextMode = contextMode;
|
|
117
119
|
nextTurnContextMode = "rebuild";
|
|
120
|
+
lifecycle.clearPendingAction();
|
|
118
121
|
const turnStartedAt = Date.now();
|
|
119
122
|
const codexTransportStatsBefore = getCodexTransportDebugSnapshot(sessionBinding.get());
|
|
120
123
|
const turnLog = beginLoggedTurn({ logger, engine, modelId, provider, contextMode, userMessage, userRecallHints, startedAt: turnStartedAt }); currentTurnId = turnLog.turnId;
|
|
@@ -135,6 +138,8 @@ export async function createRunner({ cwd, modelId = null, provider = null, provi
|
|
|
135
138
|
draft: result?.draft ?? "",
|
|
136
139
|
durationMs: Date.now() - turnStartedAt,
|
|
137
140
|
}, (notificationResult) => { lastNotificationResult = notificationResult; });
|
|
141
|
+
const lifecycleAction = lifecycle.takePendingAction();
|
|
142
|
+
if (lifecycleAction) result.lifecycleAction = lifecycleAction;
|
|
138
143
|
turnLog.endSuccess(result);
|
|
139
144
|
return result;
|
|
140
145
|
} catch (err) {
|
|
@@ -186,21 +191,11 @@ export async function createRunner({ cwd, modelId = null, provider = null, provi
|
|
|
186
191
|
return model;
|
|
187
192
|
},
|
|
188
193
|
getScopedModels() { return appendFastVariants(sessionBinding.get().scopedModels); },
|
|
189
|
-
getConfiguredProviders() {
|
|
190
|
-
const configured = Object.values(providers ?? {}).map((profile) => profile?.type).filter(Boolean);
|
|
191
|
-
const available = (modelRegistry.getAvailable?.() ?? []).map((model) => model.provider);
|
|
192
|
-
return [...new Set([...configured, ...available])];
|
|
193
|
-
},
|
|
194
|
+
getConfiguredProviders() { return [...new Set([...Object.values(providers ?? {}).map((profile) => profile?.type).filter(Boolean), ...(modelRegistry.getAvailable?.() ?? []).map((model) => model.provider)])]; },
|
|
194
195
|
getSessionStats() { return getRunnerSessionStats(sessionBinding.get(), runtimeHost); },
|
|
195
196
|
getLastNotificationResult() { return lastNotificationResult; },
|
|
196
197
|
async notifyTest({ title = "March", message = "If you see this, March runtime notifications work." } = {}) {
|
|
197
|
-
lastNotificationResult = await notifyTurnEndBestEffort(turnNotifier, {
|
|
198
|
-
status: "success",
|
|
199
|
-
sessionName: engine.sessionName,
|
|
200
|
-
title,
|
|
201
|
-
message,
|
|
202
|
-
durationMs: 0,
|
|
203
|
-
});
|
|
198
|
+
lastNotificationResult = await notifyTurnEndBestEffort(turnNotifier, { status: "success", sessionName: engine.sessionName, title, message, durationMs: 0 });
|
|
204
199
|
return lastNotificationResult;
|
|
205
200
|
},
|
|
206
201
|
estimateContextTokens(userMessage = "") {
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import { createRunnerEngineStateFacade } from "./state/runner-state.mjs";
|
|
2
|
+
|
|
1
3
|
export function createRemoteRunnerClient(peer, { initialState = null } = {}) {
|
|
2
4
|
let state = initialState;
|
|
5
|
+
const engineFacade = createRunnerEngineStateFacade({
|
|
6
|
+
getState: () => state,
|
|
7
|
+
setState: (nextState) => { state = nextState; },
|
|
8
|
+
});
|
|
3
9
|
|
|
4
10
|
const client = {
|
|
5
|
-
get engine() { return
|
|
11
|
+
get engine() { return engineFacade; },
|
|
6
12
|
get runtimeState() { return state; },
|
|
7
13
|
async init(options = {}) {
|
|
8
14
|
state = await peer.call("init", options);
|
|
@@ -56,18 +62,4 @@ export function createRemoteRunnerClient(peer, { initialState = null } = {}) {
|
|
|
56
62
|
return response;
|
|
57
63
|
}
|
|
58
64
|
|
|
59
|
-
function createEngineFacade() {
|
|
60
|
-
const engine = state?.engine ?? {};
|
|
61
|
-
return {
|
|
62
|
-
...engine,
|
|
63
|
-
hasRenderedPendingAssistantRecallHints: () => true,
|
|
64
|
-
takePendingAssistantRecallHints: () => [],
|
|
65
|
-
peekPendingAssistantRecallHints: () => [],
|
|
66
|
-
markPendingAssistantRecallHintsRendered: () => {},
|
|
67
|
-
getRecentRecallMemoryIds: () => [],
|
|
68
|
-
restoreSession: () => {
|
|
69
|
-
throw new Error("remote runner session restore is not available");
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
65
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createRunnerStateSnapshot } from "./state/runner-state.mjs";
|
|
2
|
+
|
|
1
3
|
export function createRunnerIpcTarget({ createRunnerImpl, runnerOptions = {} } = {}) {
|
|
2
4
|
if (typeof createRunnerImpl !== "function") throw new Error("createRunnerImpl is required");
|
|
3
5
|
|
|
@@ -100,26 +102,5 @@ export function createRunnerIpcTarget({ createRunnerImpl, runnerOptions = {} } =
|
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
export function getRunnerState(runner) {
|
|
103
|
-
|
|
104
|
-
const scopedModels = runner.getScopedModels?.() ?? [];
|
|
105
|
-
const thinkingLevel = runner.getThinkingLevel?.() ?? runner.engine?.thinkingLevel ?? null;
|
|
106
|
-
return {
|
|
107
|
-
engine: {
|
|
108
|
-
cwd: runner.engine?.cwd ?? null,
|
|
109
|
-
modelId: runner.engine?.modelId ?? currentModel?.id ?? null,
|
|
110
|
-
provider: runner.engine?.provider ?? currentModel?.provider ?? null,
|
|
111
|
-
thinkingLevel,
|
|
112
|
-
sessionName: runner.engine?.sessionName ?? "",
|
|
113
|
-
turns: runner.engine?.turns ?? [],
|
|
114
|
-
},
|
|
115
|
-
currentModel,
|
|
116
|
-
scopedModels,
|
|
117
|
-
configuredProviders: runner.getConfiguredProviders?.() ?? [],
|
|
118
|
-
availableThinkingLevels: runner.getAvailableThinkingLevels?.() ?? [],
|
|
119
|
-
canSwitchPiSession: runner.canSwitchPiSession?.() ?? false,
|
|
120
|
-
sessionStats: runner.getSessionStats?.() ?? null,
|
|
121
|
-
lspStatus: runner.getLspStatus?.() ?? null,
|
|
122
|
-
extensionDiagnostics: runner.getExtensionDiagnostics?.() ?? [],
|
|
123
|
-
extensionLifecycleState: runner.getExtensionLifecycleState?.() ?? null,
|
|
124
|
-
};
|
|
105
|
+
return createRunnerStateSnapshot(runner);
|
|
125
106
|
}
|
|
@@ -14,34 +14,111 @@ export async function createRunnerProcessClient({
|
|
|
14
14
|
forkImpl = fork,
|
|
15
15
|
timeoutMs = 0,
|
|
16
16
|
} = {}) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
let active = await startRuntime();
|
|
18
|
+
let disposed = false;
|
|
19
|
+
const localProps = new Map();
|
|
20
|
+
|
|
21
|
+
const runner = new Proxy({}, {
|
|
22
|
+
get(_target, prop) {
|
|
23
|
+
if (prop === "restartRuntime") return restartRuntime;
|
|
24
|
+
if (prop === "dispose") return dispose;
|
|
25
|
+
if (localProps.has(prop)) return localProps.get(prop);
|
|
26
|
+
const value = active.runner[prop];
|
|
27
|
+
return typeof value === "function" ? value.bind(active.runner) : value;
|
|
28
|
+
},
|
|
29
|
+
set(_target, prop, value) {
|
|
30
|
+
localProps.set(prop, value);
|
|
31
|
+
return true;
|
|
32
|
+
},
|
|
33
|
+
has(_target, prop) {
|
|
34
|
+
return prop === "restartRuntime" || prop === "dispose" || localProps.has(prop) || prop in active.runner;
|
|
25
35
|
},
|
|
26
|
-
timeoutMs,
|
|
27
36
|
});
|
|
28
|
-
const runner = createRemoteRunnerClient(peer);
|
|
29
|
-
try {
|
|
30
|
-
await runner.init(runnerOptions);
|
|
31
|
-
} catch (error) {
|
|
32
|
-
peer.dispose();
|
|
33
|
-
child.kill?.();
|
|
34
|
-
throw error;
|
|
35
|
-
}
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
return { runner, get child() { return active.child; }, dispose };
|
|
39
|
+
|
|
40
|
+
async function startRuntime() {
|
|
41
|
+
const child = forkImpl(entry, [], {
|
|
42
|
+
stdio: ["ignore", "inherit", "inherit", "ipc"],
|
|
43
|
+
});
|
|
44
|
+
const peer = createProcessRuntimeIpcPeer({
|
|
45
|
+
processLike: child,
|
|
46
|
+
target: {
|
|
47
|
+
...createRuntimeUiEventTarget(ui),
|
|
48
|
+
modelPayload: (event) => onModelPayload?.(event),
|
|
49
|
+
},
|
|
50
|
+
timeoutMs,
|
|
51
|
+
});
|
|
52
|
+
const remoteRunner = createRemoteRunnerClient(peer);
|
|
39
53
|
try {
|
|
40
|
-
await
|
|
41
|
-
}
|
|
54
|
+
await waitForRuntimeInit({ child, initPromise: remoteRunner.init(runnerOptions) });
|
|
55
|
+
} catch (error) {
|
|
56
|
+
peer.dispose();
|
|
42
57
|
child.kill?.();
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
runner: remoteRunner,
|
|
62
|
+
child,
|
|
63
|
+
async dispose() {
|
|
64
|
+
try {
|
|
65
|
+
await remoteRunner.dispose();
|
|
66
|
+
} finally {
|
|
67
|
+
child.kill?.();
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function restartRuntime({ restoreSession = true } = {}) {
|
|
74
|
+
if (disposed) throw new Error("runtime runner is already disposed");
|
|
75
|
+
const previousState = await refreshRunnerState(active.runner);
|
|
76
|
+
const previousSessionFile = previousState?.sessionStats?.sessionFile ?? previousState?.sessionStats?.sessionPath ?? null;
|
|
77
|
+
const previousActive = active;
|
|
78
|
+
await previousActive.dispose();
|
|
79
|
+
active = await startRuntime();
|
|
80
|
+
|
|
81
|
+
// Rebind the same persisted pi session after the fresh child imports updated source.
|
|
82
|
+
if (restoreSession && previousSessionFile && active.runner.canSwitchPiSession?.()) {
|
|
83
|
+
await active.runner.switchPiSession(previousSessionFile, previousState?.engine ?? null);
|
|
43
84
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
85
|
+
return await refreshRunnerState(active.runner);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function dispose() {
|
|
89
|
+
if (disposed) return;
|
|
90
|
+
disposed = true;
|
|
91
|
+
await active.dispose();
|
|
92
|
+
}
|
|
47
93
|
}
|
|
94
|
+
|
|
95
|
+
async function refreshRunnerState(runner) {
|
|
96
|
+
if (typeof runner.refreshState === "function") return await runner.refreshState();
|
|
97
|
+
return runner.runtimeState ?? null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function waitForRuntimeInit({ child, initPromise }) {
|
|
101
|
+
return new Promise((resolve, reject) => {
|
|
102
|
+
let settled = false;
|
|
103
|
+
const cleanup = () => {
|
|
104
|
+
child.off?.("exit", onExit);
|
|
105
|
+
child.off?.("error", onError);
|
|
106
|
+
};
|
|
107
|
+
const finish = (fn, value) => {
|
|
108
|
+
if (settled) return;
|
|
109
|
+
settled = true;
|
|
110
|
+
cleanup();
|
|
111
|
+
fn(value);
|
|
112
|
+
};
|
|
113
|
+
const onExit = (code, signal) => {
|
|
114
|
+
const codeText = code == null ? "" : ` with code ${code}`;
|
|
115
|
+
const signalText = signal ? ` (${signal})` : "";
|
|
116
|
+
finish(reject, new Error(`Runtime process exited during startup${codeText}${signalText}`));
|
|
117
|
+
};
|
|
118
|
+
const onError = (error) => finish(reject, error);
|
|
119
|
+
|
|
120
|
+
child.once?.("exit", onExit);
|
|
121
|
+
child.once?.("error", onError);
|
|
122
|
+
initPromise.then((value) => finish(resolve, value), (error) => finish(reject, error));
|
|
123
|
+
});
|
|
124
|
+
}
|
|
@@ -24,6 +24,7 @@ export async function createRunnerRuntimeHost({
|
|
|
24
24
|
lspService = null,
|
|
25
25
|
mcpTools = [],
|
|
26
26
|
webTools = [],
|
|
27
|
+
lifecycle = null,
|
|
27
28
|
permissionController = null,
|
|
28
29
|
extensionPaths = [],
|
|
29
30
|
hostedTools = {},
|
|
@@ -59,6 +60,7 @@ export async function createRunnerRuntimeHost({
|
|
|
59
60
|
lspService,
|
|
60
61
|
mcpTools,
|
|
61
62
|
webTools,
|
|
63
|
+
lifecycle,
|
|
62
64
|
permissionController,
|
|
63
65
|
authStorage,
|
|
64
66
|
projectMarchDir,
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export function createRunnerStateSnapshot(runner) {
|
|
2
|
+
const currentModel = runner.getCurrentModel?.() ?? null;
|
|
3
|
+
const scopedModels = runner.getScopedModels?.() ?? [];
|
|
4
|
+
const thinkingLevel = runner.getThinkingLevel?.() ?? runner.engine?.thinkingLevel ?? null;
|
|
5
|
+
const engine = runner.engine ?? {};
|
|
6
|
+
return {
|
|
7
|
+
engine: {
|
|
8
|
+
cwd: engine.cwd ?? null,
|
|
9
|
+
modelId: engine.modelId ?? currentModel?.id ?? null,
|
|
10
|
+
provider: engine.provider ?? currentModel?.provider ?? null,
|
|
11
|
+
thinkingLevel,
|
|
12
|
+
sessionName: engine.sessionName ?? "",
|
|
13
|
+
remoteMemorySources: engine.remoteMemorySources ?? [],
|
|
14
|
+
turns: engine.turns ?? [],
|
|
15
|
+
pendingAssistantRecallHints: engine.peekPendingAssistantRecallHints?.() ?? engine.pendingAssistantRecallHints ?? [],
|
|
16
|
+
pendingAssistantRecallHintsRendered: engine.hasRenderedPendingAssistantRecallHints?.() ?? engine.pendingAssistantRecallHintsRendered ?? false,
|
|
17
|
+
recentRecallMemoryIds: [...(engine.getRecentRecallMemoryIds?.() ?? [])],
|
|
18
|
+
},
|
|
19
|
+
currentModel,
|
|
20
|
+
scopedModels,
|
|
21
|
+
configuredProviders: runner.getConfiguredProviders?.() ?? [],
|
|
22
|
+
availableThinkingLevels: runner.getAvailableThinkingLevels?.() ?? [],
|
|
23
|
+
canSwitchPiSession: runner.canSwitchPiSession?.() ?? false,
|
|
24
|
+
sessionStats: runner.getSessionStats?.() ?? null,
|
|
25
|
+
lspStatus: runner.getLspStatus?.() ?? null,
|
|
26
|
+
extensionDiagnostics: runner.getExtensionDiagnostics?.() ?? [],
|
|
27
|
+
extensionLifecycleState: runner.getExtensionLifecycleState?.() ?? null,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function createRunnerEngineStateFacade({ getState, setState }) {
|
|
32
|
+
return {
|
|
33
|
+
get cwd() { return engineState(getState()).cwd ?? null; },
|
|
34
|
+
get modelId() { return engineState(getState()).modelId ?? null; },
|
|
35
|
+
get provider() { return engineState(getState()).provider ?? null; },
|
|
36
|
+
get thinkingLevel() { return engineState(getState()).thinkingLevel ?? null; },
|
|
37
|
+
get sessionName() { return engineState(getState()).sessionName ?? ""; },
|
|
38
|
+
get remoteMemorySources() { return engineState(getState()).remoteMemorySources ?? []; },
|
|
39
|
+
get turns() { return engineState(getState()).turns ?? []; },
|
|
40
|
+
peekPendingAssistantRecallHints() {
|
|
41
|
+
return engineState(getState()).pendingAssistantRecallHints ?? [];
|
|
42
|
+
},
|
|
43
|
+
hasRenderedPendingAssistantRecallHints() {
|
|
44
|
+
return Boolean(engineState(getState()).pendingAssistantRecallHintsRendered);
|
|
45
|
+
},
|
|
46
|
+
markPendingAssistantRecallHintsRendered() {
|
|
47
|
+
updateEngineState(getState, setState, (engine) => {
|
|
48
|
+
if ((engine.pendingAssistantRecallHints ?? []).length > 0) {
|
|
49
|
+
engine.pendingAssistantRecallHintsRendered = true;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
takePendingAssistantRecallHints() {
|
|
54
|
+
const hints = engineState(getState()).pendingAssistantRecallHints ?? [];
|
|
55
|
+
updateEngineState(getState, setState, (engine) => {
|
|
56
|
+
engine.pendingAssistantRecallHints = [];
|
|
57
|
+
engine.pendingAssistantRecallHintsRendered = false;
|
|
58
|
+
});
|
|
59
|
+
return hints;
|
|
60
|
+
},
|
|
61
|
+
getRecentRecallMemoryIds() {
|
|
62
|
+
return engineState(getState()).recentRecallMemoryIds ?? [];
|
|
63
|
+
},
|
|
64
|
+
restoreSession() {
|
|
65
|
+
throw new Error("remote runner session restore is not available");
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function engineState(state) {
|
|
71
|
+
return state?.engine ?? {};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function updateEngineState(getState, setState, update) {
|
|
75
|
+
const state = getState();
|
|
76
|
+
if (!state) return;
|
|
77
|
+
const engine = { ...(state.engine ?? {}) };
|
|
78
|
+
update(engine);
|
|
79
|
+
setState({ ...state, engine });
|
|
80
|
+
}
|
|
@@ -14,6 +14,7 @@ export function resolveRunnerSessionOptions({
|
|
|
14
14
|
lspService = null,
|
|
15
15
|
mcpTools = [],
|
|
16
16
|
webTools = [],
|
|
17
|
+
lifecycle = null,
|
|
17
18
|
permissionController = null,
|
|
18
19
|
authStorage = null,
|
|
19
20
|
projectMarchDir = null,
|
|
@@ -30,7 +31,7 @@ export function resolveRunnerSessionOptions({
|
|
|
30
31
|
?? (provider && modelId ? getModel(provider, modelId) : null);
|
|
31
32
|
if (!model) throw new Error(`Model not found: ${provider}/${modelId}`);
|
|
32
33
|
|
|
33
|
-
const customTools = createMarchCustomTools({ cwd, engine, ui, memoryTools, shellRuntime, lspService, mcpTools, webTools, permissionController, authStorage, projectMarchDir, stateRoot, getCurrentModel: () => getCurrentModel?.() ?? model });
|
|
34
|
+
const customTools = createMarchCustomTools({ cwd, engine, ui, memoryTools, shellRuntime, lspService, mcpTools, webTools, lifecycle, permissionController, authStorage, projectMarchDir, stateRoot, getCurrentModel: () => getCurrentModel?.() ?? model });
|
|
34
35
|
const customToolNames = customTools.map((tool) => tool.name);
|
|
35
36
|
const tools = [
|
|
36
37
|
...customToolNames.filter((name) => name === "read"),
|
package/src/agent/tools.mjs
CHANGED
|
@@ -11,8 +11,9 @@ import { createShellTools } from "../shell/tools.mjs";
|
|
|
11
11
|
import { initImageGen } from "../image-gen/index.mjs";
|
|
12
12
|
import { createSuperGrokTool } from "../supergrok/tool.mjs";
|
|
13
13
|
import { createBrowserTools } from "../browser/tools/index.mjs";
|
|
14
|
+
import { createRuntimeRestartTool } from "./lifecycle/runtime-restart-tool.mjs";
|
|
14
15
|
|
|
15
|
-
export function createMarchCustomTools({ cwd, engine, ui, memoryTools = [], shellRuntime = null, lspService = null, mcpTools = [], webTools = [], permissionController = null, authStorage = null, projectMarchDir = null, stateRoot = null, getCurrentModel = null }) {
|
|
16
|
+
export function createMarchCustomTools({ cwd, engine, ui, memoryTools = [], shellRuntime = null, lspService = null, mcpTools = [], webTools = [], lifecycle = null, permissionController = null, authStorage = null, projectMarchDir = null, stateRoot = null, getCurrentModel = null }) {
|
|
16
17
|
const commandExecTool = createCommandExecTool({ cwd });
|
|
17
18
|
const contextStatsTool = createContextStatsTool({ engine });
|
|
18
19
|
const editFileTool = createEditFileTool({ engine, ui, lspService });
|
|
@@ -35,6 +36,7 @@ export function createMarchCustomTools({ cwd, engine, ui, memoryTools = [], shel
|
|
|
35
36
|
...memoryTools,
|
|
36
37
|
...mcpTools,
|
|
37
38
|
...webTools,
|
|
39
|
+
...(lifecycle ? [createRuntimeRestartTool({ lifecycle })] : []),
|
|
38
40
|
...createBrowserTools({ stateRoot }),
|
|
39
41
|
...(authStorage ? [createSuperGrokTool({ authStorage, projectMarchDir })] : []),
|
|
40
42
|
...(authStorage ? initImageGen({ authStorage, projectMarchDir }) : []),
|
package/src/cli/args.mjs
CHANGED
|
@@ -20,15 +20,18 @@ export function parseCliArgs(argv) {
|
|
|
20
20
|
"permission-mode": { type: "string" },
|
|
21
21
|
host: { type: "string" },
|
|
22
22
|
port: { type: "string" },
|
|
23
|
+
"api-port": { type: "string" },
|
|
23
24
|
name: { type: "string" },
|
|
24
25
|
token: { type: "string" },
|
|
25
26
|
foreground: { type: "boolean" },
|
|
27
|
+
workspace: { type: "string" },
|
|
28
|
+
dev: { type: "boolean" },
|
|
26
29
|
help: { type: "boolean", short: "h" },
|
|
27
30
|
},
|
|
28
31
|
allowPositionals: true,
|
|
29
32
|
});
|
|
30
33
|
|
|
31
|
-
const commandName = ["login", "provider", "websearch", "memory", "browser", "gateway"].includes(positionals[0]) ? positionals[0] : null;
|
|
34
|
+
const commandName = ["login", "provider", "web", "websearch", "memory", "browser", "gateway"].includes(positionals[0]) ? positionals[0] : null;
|
|
32
35
|
|
|
33
36
|
return {
|
|
34
37
|
command: commandName ? { name: commandName, args: positionals.slice(1) } : null,
|
|
@@ -47,9 +50,12 @@ export function parseCliArgs(argv) {
|
|
|
47
50
|
permissionMode: values["permission-mode"] ?? "bypassPermissions",
|
|
48
51
|
host: values.host ?? null,
|
|
49
52
|
port: values.port ?? null,
|
|
53
|
+
apiPort: values["api-port"] ?? null,
|
|
50
54
|
name: values.name ?? null,
|
|
51
55
|
token: values.token ?? null,
|
|
52
56
|
foreground: values.foreground ?? false,
|
|
57
|
+
workspace: values.workspace ?? null,
|
|
58
|
+
dev: values.dev ?? false,
|
|
53
59
|
help: values.help ?? false,
|
|
54
60
|
prompt: commandName ? "" : positionals.join(" "),
|
|
55
61
|
};
|
|
@@ -65,6 +71,8 @@ Usage:
|
|
|
65
71
|
march provider --config Configure provider credentials
|
|
66
72
|
march provider share [id] Share a provider profile
|
|
67
73
|
march provider accept <token>
|
|
74
|
+
march web [path] Start the local Web UI session manager
|
|
75
|
+
march web --dev Start Web UI with Vite hot reload
|
|
68
76
|
march websearch --config Configure web search credentials
|
|
69
77
|
march memory serve [folder]
|
|
70
78
|
march memory add <url>
|
|
@@ -92,8 +100,11 @@ Options:
|
|
|
92
100
|
--permission-mode <mode> Permission mode: default, bypassPermissions, dontAsk (default: bypassPermissions)
|
|
93
101
|
-e, --extension <path>
|
|
94
102
|
Load a pi extension path in the default runtime host (repeatable)
|
|
95
|
-
--host <host> With memory serve, bind host (default: 127.0.0.1)
|
|
96
|
-
--port <port> With memory serve, bind port
|
|
103
|
+
--host <host> With memory serve/web, bind host (default: 127.0.0.1)
|
|
104
|
+
--port <port> With memory serve/web, bind port
|
|
105
|
+
--api-port <port> With web --dev, bind API backend port
|
|
106
|
+
--workspace <path> With web, open an initial workspace session
|
|
107
|
+
--dev With web, use Vite dev server and proxy /api
|
|
97
108
|
--name <name> With memory serve/add, remote memory source name
|
|
98
109
|
--foreground With memory serve, run server in current process
|
|
99
110
|
-h, --help Show this help
|
|
@@ -1,7 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
return [
|
|
3
|
-
"Commands: /new, /exit, /help, /hotkeys, /templates, /do, /discuss, /mode, /export jsonl, /export html, /export gist <jsonl|html>, /settings, /extensions, /providers, /providers <name>, /model, /models, /session, /status, /shell, /shell spawn [name], /save, /name, /copy",
|
|
4
|
-
"Sessions: /session opens previous sessions and restores the selected one.",
|
|
5
|
-
"Shortcuts: Tab = toggle Do/Discuss, Esc = abort turn, Ctrl+C = abort turn / press twice to exit when idle, Ctrl+O = toggle tool output, Alt+S = shell pane, Alt+N = next shell, Alt+K/J = shell scroll, PageUp/PageDown = output scroll, Ctrl+G = external editor, Shift+Tab = thinking selector, Ctrl+T = thinking selector, Ctrl+L = model selector",
|
|
6
|
-
];
|
|
7
|
-
}
|
|
1
|
+
export { formatHelpLines } from "./registry/slash-command-registry.mjs";
|