march-cli 0.1.34 → 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/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
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { basename, join, resolve } from "node:path";
|
|
2
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { createUI } from "../ui.mjs";
|
|
4
|
+
import { createPermissionController, MODE } from "../permissions.mjs";
|
|
5
|
+
import { loadKeybindings } from "../input/keybindings.mjs";
|
|
6
|
+
import { createInputHistoryStore } from "../input/history-store.mjs";
|
|
7
|
+
import { createModeState } from "../input/mode-state.mjs";
|
|
8
|
+
import { loadPromptTemplates } from "../input/prompt-templates.mjs";
|
|
9
|
+
import { createStatusLineUpdater } from "../status-line-updater.mjs";
|
|
10
|
+
import { wireTuiHandlers } from "../tui/tui-handlers.mjs";
|
|
11
|
+
import { createMarchAuthStorage } from "../../auth/storage.mjs";
|
|
12
|
+
import { createRuntimeRunner } from "./create-runtime-runner.mjs";
|
|
13
|
+
import { createCliShellRuntime } from "../../shell/cli-runtime.mjs";
|
|
14
|
+
import { MarkdownMemoryStore } from "../../memory/markdown-store.mjs";
|
|
15
|
+
import { createMarkdownMemoryTools } from "../../memory/markdown-tools.mjs";
|
|
16
|
+
import { discoverProjectExtensionPaths } from "../../extensions/discovery.mjs";
|
|
17
|
+
import { loadProjectLifecycleHookManifests } from "../../extensions/lifecycle-manifest.mjs";
|
|
18
|
+
import { loadOrCreateProjectId, resumeStartupSession } from "./startup-session.mjs";
|
|
19
|
+
import { initializeMcp } from "../../mcp/index.mjs";
|
|
20
|
+
import { createWebToolsFromConfig } from "../../web/tools.mjs";
|
|
21
|
+
import { createModelContextDumper } from "../../debug/model-context-dumper.mjs";
|
|
22
|
+
import { createLogger, installProcessLogHandlers } from "../../debug/logger.mjs";
|
|
23
|
+
import { defaultProfilePaths, ensureProfileFiles } from "../../context/profiles.mjs";
|
|
24
|
+
import { createDesktopTurnNotifier } from "../../notification/desktop-notifier.mjs";
|
|
25
|
+
import { normalizeRemoteMemorySources } from "../../memory/remote/config.mjs";
|
|
26
|
+
import { resolveMemoryRoot } from "../../memory/root.mjs";
|
|
27
|
+
import { ensureBrowserDaemon } from "../../browser/client/lifecycle.mjs";
|
|
28
|
+
|
|
29
|
+
export async function createCliAppRuntime({ args, config, cwd, argv, stateRoot, useRuntimeProcess } = {}) {
|
|
30
|
+
if (!existsSync(stateRoot)) mkdirSync(stateRoot, { recursive: true });
|
|
31
|
+
await ensureBrowserDaemon({ stateRoot }).catch(() => {});
|
|
32
|
+
|
|
33
|
+
const logger = createLogger({ logDir: join(stateRoot, "logs") });
|
|
34
|
+
installProcessLogHandlers(logger);
|
|
35
|
+
logger.event("process.start", {
|
|
36
|
+
cwd,
|
|
37
|
+
argv,
|
|
38
|
+
version: process.version,
|
|
39
|
+
platform: process.platform,
|
|
40
|
+
logPath: logger.path,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const provider = args.provider ?? config.provider ?? null;
|
|
44
|
+
const serviceTier = config.serviceTier ?? null;
|
|
45
|
+
const model = args.model ?? config.model ?? null;
|
|
46
|
+
const extensionPaths = [
|
|
47
|
+
...discoverProjectExtensionPaths(cwd),
|
|
48
|
+
...args.extensions.map((extensionPath) => resolve(cwd, extensionPath)),
|
|
49
|
+
];
|
|
50
|
+
const lifecycleManifests = loadProjectLifecycleHookManifests(cwd);
|
|
51
|
+
const keybindingConfig = loadKeybindings(cwd);
|
|
52
|
+
const promptTemplateConfig = loadPromptTemplates(cwd);
|
|
53
|
+
const authConfig = createMarchAuthStorage({ provider: provider ?? "deepseek", providers: config.providers, cwd });
|
|
54
|
+
|
|
55
|
+
if (!authConfig.hasAuth) {
|
|
56
|
+
process.stderr.write("Error: no providers configured. Run: march provider --config\n");
|
|
57
|
+
return { ok: false, code: 1, logger };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const projectMarchDir = resolve(cwd, ".march");
|
|
61
|
+
if (!existsSync(projectMarchDir)) mkdirSync(projectMarchDir, { recursive: true });
|
|
62
|
+
const inputHistoryStore = createInputHistoryStore({ path: join(projectMarchDir, "input-history.json") });
|
|
63
|
+
const modeState = createModeState();
|
|
64
|
+
const namespace = loadOrCreateProjectId(projectMarchDir);
|
|
65
|
+
const memoryRoot = resolveMemoryRoot(config.memoryRoot, stateRoot);
|
|
66
|
+
const profilePaths = defaultProfilePaths();
|
|
67
|
+
ensureProfileFiles(profilePaths);
|
|
68
|
+
const memoryStore = new MarkdownMemoryStore({ root: memoryRoot });
|
|
69
|
+
const remoteMemorySources = normalizeRemoteMemorySources(config);
|
|
70
|
+
const memoryTools = createMarkdownMemoryTools(memoryStore, { remoteSources: remoteMemorySources });
|
|
71
|
+
const currentProject = basename(cwd);
|
|
72
|
+
const shellRuntime = args.shellRuntime ? createCliShellRuntime({ cwd }) : null;
|
|
73
|
+
|
|
74
|
+
const mcpInit = useRuntimeProcess
|
|
75
|
+
? { clientManager: null, mcpTools: [], mcpInjections: [], errors: [] }
|
|
76
|
+
: await initializeMcp({ projectDir: cwd });
|
|
77
|
+
for (const { server, error } of mcpInit.errors) {
|
|
78
|
+
if (!args.json) process.stderr.write(`[mcp] ${server}: ${error}\n`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const webTools = createWebToolsFromConfig(config);
|
|
82
|
+
const turnNotifier = createDesktopTurnNotifier({ enabled: Boolean(config.notifications?.turnEnd), config: config.notifications });
|
|
83
|
+
const permissionMode = args.permissionMode ?? MODE.BYPASS;
|
|
84
|
+
const permissionController = createPermissionController({ mode: permissionMode });
|
|
85
|
+
const usePiSessions = true;
|
|
86
|
+
const usePiRuntimeHost = true;
|
|
87
|
+
const sessionSource = "pi";
|
|
88
|
+
const sessionsRoot = join(projectMarchDir, "sessions");
|
|
89
|
+
const sessionState = {
|
|
90
|
+
sessionId: args.resume ?? Date.now().toString(36),
|
|
91
|
+
sessionDir: null,
|
|
92
|
+
};
|
|
93
|
+
sessionState.sessionDir = join(sessionsRoot, sessionState.sessionId);
|
|
94
|
+
const contextDumpRoot = resolve(projectMarchDir, "context-dumps", sessionState.sessionId);
|
|
95
|
+
const modelContextDumper = createModelContextDumper({
|
|
96
|
+
enabled: args.dumpContext,
|
|
97
|
+
rootDir: contextDumpRoot,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const ui = createUI({
|
|
101
|
+
json: args.json,
|
|
102
|
+
cwd,
|
|
103
|
+
keybindings: keybindingConfig.keybindings,
|
|
104
|
+
promptTemplates: promptTemplateConfig.templates,
|
|
105
|
+
shellRuntime,
|
|
106
|
+
historyStore: inputHistoryStore,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
let turnRunning = false;
|
|
110
|
+
let refreshStatusBar = null;
|
|
111
|
+
const runnerOptions = {
|
|
112
|
+
cwd,
|
|
113
|
+
modelId: model,
|
|
114
|
+
provider,
|
|
115
|
+
serviceTier,
|
|
116
|
+
providers: config.providers,
|
|
117
|
+
config,
|
|
118
|
+
stateRoot,
|
|
119
|
+
memoryRoot,
|
|
120
|
+
profilePaths,
|
|
121
|
+
namespace,
|
|
122
|
+
projectMarchDir,
|
|
123
|
+
extensionPaths,
|
|
124
|
+
permissionMode,
|
|
125
|
+
shellRuntime: Boolean(shellRuntime),
|
|
126
|
+
lifecycleHooks: lifecycleManifests.hooks,
|
|
127
|
+
lifecycleDiagnostics: lifecycleManifests.diagnostics,
|
|
128
|
+
modelContextDumper: { enabled: args.dumpContext, rootDir: contextDumpRoot },
|
|
129
|
+
remoteMemorySources,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
let runner;
|
|
133
|
+
try {
|
|
134
|
+
runner = await createRuntimeRunner({
|
|
135
|
+
useRuntimeProcess, runnerOptions, ui, memoryStore, memoryTools, shellRuntime,
|
|
136
|
+
mcpTools: mcpInit.mcpTools, mcpInjections: mcpInit.mcpInjections, mcpClientManager: mcpInit.clientManager, webTools,
|
|
137
|
+
usePiSessions, usePiRuntimeHost, authStorage: authConfig.authStorage,
|
|
138
|
+
permissionController, modelContextDumper, turnNotifier, logger,
|
|
139
|
+
refreshStatusBar: (...args) => refreshStatusBar?.(...args),
|
|
140
|
+
});
|
|
141
|
+
} catch (err) {
|
|
142
|
+
process.stderr.write(`Error: ${err.message}\n`);
|
|
143
|
+
logger.error("runtime.start_failed", { error: err });
|
|
144
|
+
memoryStore.close?.();
|
|
145
|
+
return { ok: false, code: 1, logger };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
refreshStatusBar = createStatusLineUpdater({
|
|
149
|
+
ui,
|
|
150
|
+
runner,
|
|
151
|
+
sessionState,
|
|
152
|
+
sessionSource,
|
|
153
|
+
getMode: () => modeState.get(),
|
|
154
|
+
});
|
|
155
|
+
const initialContextTokens = typeof runner.estimateContextTokens === "function"
|
|
156
|
+
? await runner.estimateContextTokens("")
|
|
157
|
+
: null;
|
|
158
|
+
refreshStatusBar(initialContextTokens ? { contextTokens: initialContextTokens } : undefined);
|
|
159
|
+
|
|
160
|
+
wireTuiHandlers({
|
|
161
|
+
ui,
|
|
162
|
+
runner,
|
|
163
|
+
sessionState,
|
|
164
|
+
projectMarchDir,
|
|
165
|
+
refreshStatusBar,
|
|
166
|
+
isTurnRunning: () => turnRunning,
|
|
167
|
+
modeState,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const startupResume = await resumeStartupSession({
|
|
171
|
+
resumeId: args.resume,
|
|
172
|
+
runner,
|
|
173
|
+
sessionState,
|
|
174
|
+
projectMarchDir,
|
|
175
|
+
ui,
|
|
176
|
+
});
|
|
177
|
+
refreshStatusBar();
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
ok: true,
|
|
181
|
+
args,
|
|
182
|
+
cwd,
|
|
183
|
+
ui,
|
|
184
|
+
runner,
|
|
185
|
+
memoryStore,
|
|
186
|
+
currentProject,
|
|
187
|
+
sessionState,
|
|
188
|
+
sessionsRoot,
|
|
189
|
+
projectMarchDir,
|
|
190
|
+
sessionSource,
|
|
191
|
+
extensionPaths,
|
|
192
|
+
keybindingConfig,
|
|
193
|
+
promptTemplateConfig,
|
|
194
|
+
startupResume,
|
|
195
|
+
contextDumpRoot,
|
|
196
|
+
logger,
|
|
197
|
+
modeState,
|
|
198
|
+
refreshStatusBar,
|
|
199
|
+
setTurnRunning(value) { turnRunning = value; },
|
|
200
|
+
};
|
|
201
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { runBrowserCommand } from "../../browser/cli/command.mjs";
|
|
2
2
|
import { runGatewayCommand } from "../../gateway/command.mjs";
|
|
3
|
+
import { runWebUiCommand } from "../../web-ui/command.mjs";
|
|
3
4
|
|
|
4
5
|
export async function runConfiguredCliCommand(args, { config, cwd, stateRoot }) {
|
|
5
6
|
if (args.command?.name === "browser") {
|
|
@@ -13,5 +14,13 @@ export async function runConfiguredCliCommand(args, { config, cwd, stateRoot })
|
|
|
13
14
|
if (args.command?.name === "gateway" && args.command.args?.[0] !== "run") {
|
|
14
15
|
return { handled: true, code: await runGatewayCommand(args, { config, cwd }) };
|
|
15
16
|
}
|
|
17
|
+
if (args.command?.name === "web") {
|
|
18
|
+
try {
|
|
19
|
+
return { handled: true, code: await runWebUiCommand(args, { config, cwd, stateRoot }) };
|
|
20
|
+
} catch (err) {
|
|
21
|
+
process.stderr.write(`Error: ${err.message}\n`);
|
|
22
|
+
return { handled: true, code: 1 };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
16
25
|
return { handled: false, code: null };
|
|
17
26
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { runLoginCommand } from "../../auth/login-command.mjs";
|
|
3
|
+
import { runProviderCommand } from "../../provider/command.mjs";
|
|
4
|
+
import { runWebSearchConfigCommand } from "../../web/config-command.mjs";
|
|
5
|
+
import { runMemoryCommand } from "../../memory/command.mjs";
|
|
6
|
+
import { resolveMemoryRoot } from "../../memory/root.mjs";
|
|
7
|
+
import { runConfiguredCliCommand } from "./configured-command.mjs";
|
|
8
|
+
|
|
9
|
+
export async function runEarlyCliCommand(args, { config, cwd, stateRoot }) {
|
|
10
|
+
if (args.command?.name === "login") {
|
|
11
|
+
try {
|
|
12
|
+
return { handled: true, code: await runLoginCommand({ providerId: args.command.args[0] ?? args.provider }) };
|
|
13
|
+
} catch (err) {
|
|
14
|
+
process.stderr.write(`Error: ${err.message}\n`);
|
|
15
|
+
return { handled: true, code: 1 };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (args.command?.name === "provider") return { handled: true, code: await runProviderCommand(args) };
|
|
19
|
+
if (args.command?.name === "websearch") {
|
|
20
|
+
if (args.providerConfig) return { handled: true, code: await runWebSearchConfigCommand({ homeDir: homedir() }) };
|
|
21
|
+
process.stderr.write("Usage: march websearch --config\n");
|
|
22
|
+
return { handled: true, code: 1 };
|
|
23
|
+
}
|
|
24
|
+
if (args.command?.name === "memory") {
|
|
25
|
+
args.memoryRoot = resolveMemoryRoot(config.memoryRoot, stateRoot);
|
|
26
|
+
return { handled: true, code: await runMemoryCommand(args, { homeDir: homedir() }) };
|
|
27
|
+
}
|
|
28
|
+
return await runConfiguredCliCommand(args, { config, cwd, stateRoot });
|
|
29
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { appendModeReminder } from "../input/mode-state.mjs";
|
|
2
|
+
import { inverse } from "../tui/ui-theme.mjs";
|
|
3
|
+
import { formatRecallHints } from "../../memory/markdown-store.mjs";
|
|
4
|
+
import { formatMessageAttachmentsForDisplay } from "../../session/attachment-display.mjs";
|
|
5
|
+
import { formatShellHints } from "../../shell/hints.mjs";
|
|
6
|
+
|
|
7
|
+
export function prepareTurnInput({ prompt, runner, memoryStore, currentProject, modeState = null }) {
|
|
8
|
+
memoryStore.beginTurn();
|
|
9
|
+
const engine = runner.engine ?? {};
|
|
10
|
+
const carryoverAlreadyRendered = engine.hasRenderedPendingAssistantRecallHints?.() ?? false;
|
|
11
|
+
const carryoverRecallHints = engine.takePendingAssistantRecallHints?.() ?? [];
|
|
12
|
+
const userRecallHints = memoryStore.recallForUser(prompt, {
|
|
13
|
+
currentProject,
|
|
14
|
+
excludedIds: engine.getRecentRecallMemoryIds?.() ?? [],
|
|
15
|
+
});
|
|
16
|
+
const modePrompt = appendModeReminder(prompt, modeState?.get?.());
|
|
17
|
+
const fullPrompt = appendPromptBlocks(
|
|
18
|
+
modePrompt,
|
|
19
|
+
formatRecallHints("user", userRecallHints),
|
|
20
|
+
formatRecallHints("assistant", carryoverRecallHints),
|
|
21
|
+
formatShellHints(runner.shellRuntime),
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
fullPrompt,
|
|
26
|
+
userMessage: prompt,
|
|
27
|
+
runOptions: { userRecallHints, currentProject },
|
|
28
|
+
displayMessage: formatUserDisplayMessage(prompt),
|
|
29
|
+
userRecallHints,
|
|
30
|
+
carryoverRecallHints,
|
|
31
|
+
shouldRenderCarryoverRecall: carryoverRecallHints.length > 0 && !carryoverAlreadyRendered,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function formatUserDisplayMessage(prompt) {
|
|
36
|
+
return `${inverse(" USER ")} ${formatMessageAttachmentsForDisplay(prompt)}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function appendPromptBlocks(prompt, ...blocks) {
|
|
40
|
+
return [prompt, ...blocks.filter(Boolean)].join("\n\n");
|
|
41
|
+
}
|
package/src/main.mjs
CHANGED
|
@@ -1,46 +1,18 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
|
-
import { join,
|
|
3
|
-
import { existsSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { join, relative } from "node:path";
|
|
4
3
|
import { fileURLToPath } from "node:url";
|
|
5
4
|
import { parseCliArgs, showHelp } from "./cli/args.mjs";
|
|
6
|
-
import { createUI } from "./cli/ui.mjs";
|
|
7
|
-
import { createPermissionController, MODE } from "./cli/permissions.mjs";
|
|
8
|
-
import { loadKeybindings } from "./cli/input/keybindings.mjs";
|
|
9
|
-
import { createInputHistoryStore } from "./cli/input/history-store.mjs";
|
|
10
|
-
import { createModeState } from "./cli/input/mode-state.mjs";
|
|
11
|
-
import { loadPromptTemplates } from "./cli/input/prompt-templates.mjs";
|
|
12
5
|
import { runInteractiveRepl, runSingleShotPrompt } from "./cli/repl-loop.mjs";
|
|
13
6
|
import { closeMarchRuntime } from "./cli/startup/runtime-close.mjs";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { createMarchAuthStorage } from "./auth/storage.mjs";
|
|
17
|
-
import { runLoginCommand } from "./auth/login-command.mjs";
|
|
18
|
-
import { createRuntimeRunner } from "./cli/startup/create-runtime-runner.mjs";
|
|
19
|
-
import { createCliShellRuntime } from "./shell/cli-runtime.mjs";
|
|
20
|
-
import { MarkdownMemoryStore } from "./memory/markdown-store.mjs";
|
|
21
|
-
import { createMarkdownMemoryTools } from "./memory/markdown-tools.mjs";
|
|
7
|
+
import { createCliAppRuntime } from "./cli/startup/app-runtime.mjs";
|
|
8
|
+
import { formatStartupBanner } from "./cli/startup/startup-banner.mjs";
|
|
22
9
|
import { loadDotEnv } from "./config/dotenv.mjs";
|
|
23
10
|
import { loadConfig } from "./config/loader.mjs";
|
|
24
|
-
import { discoverProjectExtensionPaths } from "./extensions/discovery.mjs";
|
|
25
|
-
import { loadProjectLifecycleHookManifests } from "./extensions/lifecycle-manifest.mjs";
|
|
26
|
-
import { loadOrCreateProjectId, resumeStartupSession } from "./cli/startup/startup-session.mjs";
|
|
27
|
-
import { formatStartupBanner } from "./cli/startup/startup-banner.mjs";
|
|
28
|
-
import { initializeMcp } from "./mcp/index.mjs";
|
|
29
|
-
import { createWebToolsFromConfig } from "./web/tools.mjs";
|
|
30
|
-
import { createModelContextDumper } from "./debug/model-context-dumper.mjs";
|
|
31
|
-
import { createLogger, installProcessLogHandlers } from "./debug/logger.mjs";
|
|
32
|
-
import { defaultProfilePaths, ensureProfileFiles } from "./context/profiles.mjs";
|
|
33
|
-
import { runProviderCommand } from "./provider/command.mjs";
|
|
34
|
-
import { runWebSearchConfigCommand } from "./web/config-command.mjs";
|
|
35
|
-
import { createDesktopTurnNotifier } from "./notification/desktop-notifier.mjs";
|
|
36
11
|
import { registerSuperGrokOAuthProvider } from "./supergrok/oauth-provider.mjs";
|
|
37
12
|
import { installNetworkEnvironment } from "./network/environment.mjs";
|
|
38
|
-
import {
|
|
39
|
-
import { normalizeRemoteMemorySources } from "./memory/remote/config.mjs";
|
|
40
|
-
import { resolveMemoryRoot } from "./memory/root.mjs";
|
|
41
|
-
import { runConfiguredCliCommand } from "./cli/startup/configured-command.mjs";
|
|
13
|
+
import { runEarlyCliCommand } from "./cli/startup/early-command.mjs";
|
|
42
14
|
import { maybeRunGatewayDaemonCommand } from "./cli/startup/gateway-daemon-command.mjs";
|
|
43
|
-
|
|
15
|
+
|
|
44
16
|
export async function run(argv) {
|
|
45
17
|
const cwd = process.cwd();
|
|
46
18
|
loadDotEnv(cwd);
|
|
@@ -56,239 +28,72 @@ export async function run(argv) {
|
|
|
56
28
|
const stateRoot = join(homedir(), ".march");
|
|
57
29
|
const useRuntimeProcess = process.env.MARCH_RUNTIME_PROCESS !== "0";
|
|
58
30
|
installNetworkEnvironment(config.network);
|
|
59
|
-
if (args.command?.name === "login") {
|
|
60
|
-
try {
|
|
61
|
-
return await runLoginCommand({
|
|
62
|
-
providerId: args.command.args[0] ?? args.provider,
|
|
63
|
-
});
|
|
64
|
-
} catch (err) {
|
|
65
|
-
process.stderr.write(`Error: ${err.message}\n`);
|
|
66
|
-
return 1;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
if (args.command?.name === "provider") {
|
|
70
|
-
return await runProviderCommand(args);
|
|
71
|
-
}
|
|
72
|
-
if (args.command?.name === "websearch") {
|
|
73
|
-
if (args.providerConfig) return await runWebSearchConfigCommand({ homeDir: homedir() });
|
|
74
|
-
process.stderr.write("Usage: march websearch --config\n");
|
|
75
|
-
return 1;
|
|
76
|
-
}
|
|
77
|
-
if (args.command?.name === "memory") {
|
|
78
|
-
args.memoryRoot = resolveMemoryRoot(config.memoryRoot, stateRoot);
|
|
79
|
-
return await runMemoryCommand(args, { homeDir: homedir() });
|
|
80
|
-
}
|
|
81
|
-
const configuredCommand = await runConfiguredCliCommand(args, { config, cwd, stateRoot });
|
|
82
|
-
if (configuredCommand.handled) return configuredCommand.code;
|
|
83
|
-
if (!existsSync(stateRoot)) mkdirSync(stateRoot, { recursive: true });
|
|
84
|
-
await ensureBrowserDaemon({ stateRoot }).catch(() => {});
|
|
85
|
-
const logger = createLogger({ logDir: join(stateRoot, "logs") });
|
|
86
|
-
installProcessLogHandlers(logger);
|
|
87
|
-
logger.event("process.start", {
|
|
88
|
-
cwd,
|
|
89
|
-
argv,
|
|
90
|
-
version: process.version,
|
|
91
|
-
platform: process.platform,
|
|
92
|
-
logPath: logger.path,
|
|
93
|
-
});
|
|
94
31
|
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
const model = args.model ?? config.model ?? null;
|
|
98
|
-
const extensionPaths = [
|
|
99
|
-
...discoverProjectExtensionPaths(cwd),
|
|
100
|
-
...args.extensions.map((extensionPath) => resolve(cwd, extensionPath)),
|
|
101
|
-
];
|
|
102
|
-
const lifecycleManifests = loadProjectLifecycleHookManifests(cwd);
|
|
103
|
-
const keybindingConfig = loadKeybindings(cwd);
|
|
104
|
-
const promptTemplateConfig = loadPromptTemplates(cwd);
|
|
105
|
-
const authConfig = createMarchAuthStorage({ provider: provider ?? "deepseek", providers: config.providers, cwd });
|
|
32
|
+
const earlyCommand = await runEarlyCliCommand(args, { config, cwd, stateRoot });
|
|
33
|
+
if (earlyCommand.handled) return earlyCommand.code;
|
|
106
34
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return 1;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const projectMarchDir = resolve(cwd, ".march");
|
|
113
|
-
if (!existsSync(projectMarchDir)) mkdirSync(projectMarchDir, { recursive: true });
|
|
114
|
-
const inputHistoryStore = createInputHistoryStore({ path: join(projectMarchDir, "input-history.json") });
|
|
115
|
-
const modeState = createModeState();
|
|
116
|
-
const namespace = loadOrCreateProjectId(projectMarchDir);
|
|
117
|
-
const memoryRoot = resolveMemoryRoot(config.memoryRoot, stateRoot);
|
|
118
|
-
const profilePaths = defaultProfilePaths();
|
|
119
|
-
ensureProfileFiles(profilePaths);
|
|
120
|
-
const memoryStore = new MarkdownMemoryStore({ root: memoryRoot });
|
|
121
|
-
const remoteMemorySources = normalizeRemoteMemorySources(config);
|
|
122
|
-
const memoryTools = createMarkdownMemoryTools(memoryStore, { remoteSources: remoteMemorySources });
|
|
123
|
-
const currentProject = basename(cwd);
|
|
124
|
-
const shellRuntime = args.shellRuntime ? createCliShellRuntime({ cwd }) : null;
|
|
35
|
+
const app = await createCliAppRuntime({ args, config, cwd, argv, stateRoot, useRuntimeProcess });
|
|
36
|
+
if (!app.ok) return app.code;
|
|
125
37
|
|
|
126
|
-
const
|
|
127
|
-
? { clientManager: null, mcpTools: [], mcpInjections: [], errors: [] }
|
|
128
|
-
: await initializeMcp({ projectDir: cwd });
|
|
129
|
-
const { clientManager: mcpClientManager, mcpTools, mcpInjections } = mcpInit;
|
|
130
|
-
for (const { server, error } of mcpInit.errors) {
|
|
131
|
-
if (args.json) {
|
|
132
|
-
// errors will be surfaced in diagnostics via runner status
|
|
133
|
-
} else {
|
|
134
|
-
process.stderr.write(`[mcp] ${server}: ${error}\n`);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const webTools = createWebToolsFromConfig(config);
|
|
139
|
-
const turnNotifier = createDesktopTurnNotifier({ enabled: Boolean(config.notifications?.turnEnd), config: config.notifications });
|
|
140
|
-
const permissionMode = args.permissionMode ?? MODE.BYPASS;
|
|
141
|
-
const permissionController = createPermissionController({ mode: permissionMode });
|
|
142
|
-
const usePiSessions = true;
|
|
143
|
-
const usePiRuntimeHost = true;
|
|
144
|
-
const sessionSource = "pi";
|
|
145
|
-
const sessionsRoot = join(projectMarchDir, "sessions");
|
|
146
|
-
const sessionState = {
|
|
147
|
-
sessionId: args.resume ?? Date.now().toString(36),
|
|
148
|
-
sessionDir: null,
|
|
149
|
-
};
|
|
150
|
-
sessionState.sessionDir = join(sessionsRoot, sessionState.sessionId);
|
|
151
|
-
const contextDumpRoot = resolve(projectMarchDir, "context-dumps", sessionState.sessionId);
|
|
152
|
-
const modelContextDumper = createModelContextDumper({
|
|
153
|
-
enabled: args.dumpContext,
|
|
154
|
-
rootDir: contextDumpRoot,
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
const ui = createUI({
|
|
158
|
-
json: args.json,
|
|
159
|
-
cwd,
|
|
160
|
-
keybindings: keybindingConfig.keybindings,
|
|
161
|
-
promptTemplates: promptTemplateConfig.templates,
|
|
162
|
-
shellRuntime,
|
|
163
|
-
historyStore: inputHistoryStore,
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
// Esc to abort current turn
|
|
167
|
-
let turnRunning = false;
|
|
168
|
-
let refreshStatusBar = null;
|
|
169
|
-
|
|
170
|
-
const runnerOptions = {
|
|
171
|
-
cwd,
|
|
172
|
-
modelId: model,
|
|
173
|
-
provider,
|
|
174
|
-
serviceTier,
|
|
175
|
-
providers: config.providers,
|
|
38
|
+
const gatewayDaemonCommand = await maybeRunGatewayDaemonCommand(args, {
|
|
176
39
|
config,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
permissionMode,
|
|
184
|
-
shellRuntime: Boolean(shellRuntime),
|
|
185
|
-
lifecycleHooks: lifecycleManifests.hooks,
|
|
186
|
-
lifecycleDiagnostics: lifecycleManifests.diagnostics,
|
|
187
|
-
modelContextDumper: { enabled: args.dumpContext, rootDir: contextDumpRoot },
|
|
188
|
-
remoteMemorySources,
|
|
189
|
-
};
|
|
190
|
-
const runner = await createRuntimeRunner({
|
|
191
|
-
useRuntimeProcess,
|
|
192
|
-
runnerOptions,
|
|
193
|
-
ui,
|
|
194
|
-
memoryStore,
|
|
195
|
-
memoryTools,
|
|
196
|
-
shellRuntime,
|
|
197
|
-
mcpTools,
|
|
198
|
-
mcpInjections,
|
|
199
|
-
mcpClientManager,
|
|
200
|
-
webTools,
|
|
201
|
-
usePiSessions,
|
|
202
|
-
usePiRuntimeHost,
|
|
203
|
-
authStorage: authConfig.authStorage,
|
|
204
|
-
permissionController,
|
|
205
|
-
modelContextDumper,
|
|
206
|
-
turnNotifier,
|
|
207
|
-
logger,
|
|
208
|
-
refreshStatusBar: (...args) => refreshStatusBar?.(...args),
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
refreshStatusBar = createStatusLineUpdater({
|
|
212
|
-
ui,
|
|
213
|
-
runner,
|
|
214
|
-
sessionState,
|
|
215
|
-
sessionSource,
|
|
216
|
-
getMode: () => modeState.get(),
|
|
217
|
-
});
|
|
218
|
-
const initialContextTokens = typeof runner.estimateContextTokens === "function"
|
|
219
|
-
? await runner.estimateContextTokens("")
|
|
220
|
-
: null;
|
|
221
|
-
refreshStatusBar(initialContextTokens ? { contextTokens: initialContextTokens } : undefined);
|
|
222
|
-
|
|
223
|
-
wireTuiHandlers({
|
|
224
|
-
ui,
|
|
225
|
-
runner,
|
|
226
|
-
sessionState,
|
|
227
|
-
projectMarchDir,
|
|
228
|
-
refreshStatusBar,
|
|
229
|
-
isTurnRunning: () => turnRunning,
|
|
230
|
-
modeState,
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
const startupResume = await resumeStartupSession({
|
|
234
|
-
resumeId: args.resume,
|
|
235
|
-
runner,
|
|
236
|
-
sessionState,
|
|
237
|
-
projectMarchDir,
|
|
238
|
-
ui,
|
|
40
|
+
cwd,
|
|
41
|
+
runner: app.runner,
|
|
42
|
+
currentProject: app.currentProject,
|
|
43
|
+
memoryStore: app.memoryStore,
|
|
44
|
+
ui: app.ui,
|
|
45
|
+
logger: app.logger,
|
|
239
46
|
});
|
|
240
|
-
refreshStatusBar();
|
|
241
|
-
const gatewayDaemonCommand = await maybeRunGatewayDaemonCommand(args, { config, cwd, runner, currentProject, memoryStore, ui, logger });
|
|
242
47
|
if (gatewayDaemonCommand.handled) return gatewayDaemonCommand.code;
|
|
243
48
|
|
|
244
49
|
if (args.prompt) {
|
|
245
|
-
|
|
50
|
+
app.setTurnRunning(true);
|
|
246
51
|
try {
|
|
247
52
|
await runSingleShotPrompt({
|
|
248
53
|
prompt: args.prompt,
|
|
249
|
-
runner,
|
|
250
|
-
memoryStore,
|
|
251
|
-
currentProject,
|
|
252
|
-
ui,
|
|
253
|
-
sessionState,
|
|
254
|
-
refreshStatusBar,
|
|
255
|
-
modeState,
|
|
54
|
+
runner: app.runner,
|
|
55
|
+
memoryStore: app.memoryStore,
|
|
56
|
+
currentProject: app.currentProject,
|
|
57
|
+
ui: app.ui,
|
|
58
|
+
sessionState: app.sessionState,
|
|
59
|
+
refreshStatusBar: app.refreshStatusBar,
|
|
60
|
+
modeState: app.modeState,
|
|
256
61
|
});
|
|
257
62
|
} finally {
|
|
258
|
-
|
|
259
|
-
await closeMarchRuntime({ runner, memoryStore, ui, logger, blankLine: true });
|
|
63
|
+
app.setTurnRunning(false);
|
|
64
|
+
await closeMarchRuntime({ runner: app.runner, memoryStore: app.memoryStore, ui: app.ui, logger: app.logger, blankLine: true });
|
|
260
65
|
}
|
|
261
|
-
logger.event("process.exit", { code: 0 });
|
|
66
|
+
app.logger.event("process.exit", { code: 0 });
|
|
262
67
|
return 0;
|
|
263
68
|
}
|
|
264
69
|
|
|
265
|
-
const dumpContextPath = args.dumpContext ? relative(cwd, contextDumpRoot) : null;
|
|
266
|
-
if (startupResume.transcriptTurns?.length > 0) ui.restoreTranscript?.(startupResume.transcriptTurns);
|
|
267
|
-
for (const line of formatStartupBanner({ cwd, modelId: runner.engine.modelId, thinkingLevel: runner.engine.thinkingLevel, mode: modeState.get(), dumpContextPath })) ui.writeln(line);
|
|
70
|
+
const dumpContextPath = args.dumpContext ? relative(cwd, app.contextDumpRoot) : null;
|
|
71
|
+
if (app.startupResume.transcriptTurns?.length > 0) app.ui.restoreTranscript?.(app.startupResume.transcriptTurns);
|
|
72
|
+
for (const line of formatStartupBanner({ cwd, modelId: app.runner.engine.modelId, thinkingLevel: app.runner.engine.thinkingLevel, mode: app.modeState.get(), dumpContextPath })) app.ui.writeln(line);
|
|
268
73
|
try {
|
|
269
74
|
await runInteractiveRepl({
|
|
270
75
|
cwd,
|
|
271
76
|
args,
|
|
272
|
-
ui,
|
|
273
|
-
runner,
|
|
274
|
-
memoryStore,
|
|
275
|
-
currentProject,
|
|
276
|
-
sessionState,
|
|
277
|
-
sessionsRoot,
|
|
278
|
-
projectMarchDir,
|
|
279
|
-
sessionSource,
|
|
280
|
-
extensionPaths,
|
|
281
|
-
keybindingConfig,
|
|
282
|
-
promptTemplateConfig,
|
|
283
|
-
renderStartupBanner: () => formatStartupBanner({ cwd, modelId: runner.engine.modelId, thinkingLevel: runner.engine.thinkingLevel, mode: modeState.get(), dumpContextPath }),
|
|
284
|
-
refreshStatusBar,
|
|
285
|
-
setTurnRunning:
|
|
286
|
-
modeState,
|
|
77
|
+
ui: app.ui,
|
|
78
|
+
runner: app.runner,
|
|
79
|
+
memoryStore: app.memoryStore,
|
|
80
|
+
currentProject: app.currentProject,
|
|
81
|
+
sessionState: app.sessionState,
|
|
82
|
+
sessionsRoot: app.sessionsRoot,
|
|
83
|
+
projectMarchDir: app.projectMarchDir,
|
|
84
|
+
sessionSource: app.sessionSource,
|
|
85
|
+
extensionPaths: app.extensionPaths,
|
|
86
|
+
keybindingConfig: app.keybindingConfig,
|
|
87
|
+
promptTemplateConfig: app.promptTemplateConfig,
|
|
88
|
+
renderStartupBanner: () => formatStartupBanner({ cwd, modelId: app.runner.engine.modelId, thinkingLevel: app.runner.engine.thinkingLevel, mode: app.modeState.get(), dumpContextPath }),
|
|
89
|
+
refreshStatusBar: app.refreshStatusBar,
|
|
90
|
+
setTurnRunning: app.setTurnRunning,
|
|
91
|
+
modeState: app.modeState,
|
|
287
92
|
});
|
|
288
93
|
} finally {
|
|
289
|
-
await closeMarchRuntime({ runner, memoryStore, ui, logger });
|
|
94
|
+
await closeMarchRuntime({ runner: app.runner, memoryStore: app.memoryStore, ui: app.ui, logger: app.logger });
|
|
290
95
|
}
|
|
291
|
-
logger.event("process.exit", { code: 0 });
|
|
96
|
+
app.logger.event("process.exit", { code: 0 });
|
|
292
97
|
return 0;
|
|
293
98
|
}
|
|
294
99
|
|