march-cli 0.1.34 → 0.1.36
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/code-search/cache.mjs +133 -0
- package/src/agent/code-search/chunk-rules.mjs +107 -0
- package/src/agent/code-search/chunker.mjs +125 -0
- package/src/agent/code-search/engine.mjs +109 -0
- package/src/agent/code-search/languages.mjs +25 -0
- package/src/agent/code-search/parser-pool.mjs +29 -0
- package/src/agent/code-search/rerank.mjs +43 -0
- package/src/agent/code-search/retrieval/bm25.mjs +47 -0
- package/src/agent/code-search/retrieval/fusion.mjs +18 -0
- package/src/agent/code-search/retrieval/model2vec.mjs +96 -0
- package/src/agent/code-search/retrieval/safetensors.mjs +49 -0
- package/src/agent/code-search/retrieval/vector.mjs +107 -0
- package/src/agent/code-search/retrieval/wordpiece.mjs +82 -0
- package/src/agent/code-search/scanner.mjs +84 -0
- package/src/agent/code-search/tokenize.mjs +16 -0
- package/src/agent/code-search/tool.mjs +75 -0
- package/src/agent/lifecycle/runner-lifecycle.mjs +16 -0
- package/src/agent/lifecycle/runtime-restart-tool.mjs +22 -0
- package/src/agent/runner/provider-quota-runtime.mjs +38 -0
- package/src/agent/runner.mjs +14 -14
- package/src/agent/runtime/remote-runner-client.mjs +9 -15
- package/src/agent/runtime/runner-ipc-target.mjs +10 -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 +81 -0
- package/src/agent/runtime/ui-event-bridge.mjs +2 -0
- package/src/agent/session/session-options.mjs +2 -1
- package/src/agent/tools.mjs +6 -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 +296 -0
- package/src/cli/commands/status-command.mjs +61 -35
- 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/context/system-core/base.md +5 -0
- package/src/main.mjs +47 -242
- package/src/provider/quota/codex.mjs +278 -0
- package/src/provider/quota/index.mjs +46 -0
- package/src/provider/quota/transport-observer.mjs +99 -0
- package/src/web-ui/command.mjs +112 -0
- package/src/web-ui/index.html +12 -0
- package/src/web-ui/runtime-host.mjs +188 -0
- package/src/web-ui/server.mjs +140 -0
- package/src/web-ui/session-manager.mjs +111 -0
- package/src/web-ui/src/App.tsx +7 -0
- package/src/web-ui/src/components/AppShell.tsx +48 -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 +115 -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 +82 -0
- package/src/web-ui/src/runtime/client.ts +81 -0
- package/src/web-ui/src/runtime/runtimeTimeline.ts +88 -0
- package/src/web-ui/src/runtime/useWebRuntime.ts +144 -0
- package/src/web-ui/src/styles/shell.css +166 -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
|
@@ -1,25 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { handleExportCommand, parseExportCommand } from "./commands/export-command.mjs";
|
|
3
|
-
import { handleModelCommand, listModels, parseModelCommand } from "./commands/model-command.mjs";
|
|
4
|
-
import { formatHotkeysPanel } from "./repl-commands.mjs";
|
|
5
|
-
import { copyLastAssistantMessage } from "./commands/copy-command.mjs";
|
|
6
|
-
import { handleSessionSourceCommand } from "./session/session-source-command.mjs";
|
|
7
|
-
import { statusCommand } from "./commands/status-command.mjs";
|
|
8
|
-
import { handleThinkingCommand, parseThinkingCommand } from "./commands/thinking-command.mjs";
|
|
9
|
-
import { formatPromptTemplateLines } from "./input/prompt-templates.mjs";
|
|
10
|
-
import { handleSettingsCommand, parseSettingsCommand } from "../config/settings-command.mjs";
|
|
11
|
-
import { handleSessionNameCommand, parseSessionNameCommand } from "./session/session-name-command.mjs";
|
|
12
|
-
import { handleShellCommand, parseShellCommand } from "./shell/shell-command.mjs";
|
|
13
|
-
import { handleProviderCommand, parseProviderCommand } from "./commands/provider-command.mjs";
|
|
14
|
-
import { handleModeCommand, parseModeCommand } from "./commands/mode-command.mjs";
|
|
15
|
-
import { formatHelpLines } from "./commands/help-command.mjs";
|
|
1
|
+
import { runSlashCommand } from "./commands/registry/slash-command-registry.mjs";
|
|
16
2
|
|
|
17
|
-
export async function handleSlashCommand(trimmed, {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
projectMarchDir,
|
|
3
|
+
export async function handleSlashCommand(trimmed, options) {
|
|
4
|
+
return runSlashCommand(trimmed, normalizeSlashCommandOptions(options));
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function normalizeSlashCommandOptions({
|
|
23
8
|
sessionSource = "pi",
|
|
24
9
|
extensionPaths = [],
|
|
25
10
|
keybindings,
|
|
@@ -30,170 +15,19 @@ export async function handleSlashCommand(trimmed, {
|
|
|
30
15
|
renderStartupBanner = null,
|
|
31
16
|
settingsHomeDir,
|
|
32
17
|
configHomeDir = settingsHomeDir,
|
|
33
|
-
|
|
18
|
+
...rest
|
|
34
19
|
}) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return { handled: true, exit: true };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (trimmed === "/new") {
|
|
41
|
-
if (!runner.canSwitchPiSession?.()) {
|
|
42
|
-
ui.writeln("Error: pi runtime host is not enabled");
|
|
43
|
-
return { handled: true };
|
|
44
|
-
}
|
|
45
|
-
let refreshContextTokens = false;
|
|
46
|
-
try {
|
|
47
|
-
const result = await runner.startNewSession();
|
|
48
|
-
if (result?.cancelled) {
|
|
49
|
-
ui.writeln("New session cancelled");
|
|
50
|
-
} else {
|
|
51
|
-
refreshContextTokens = true;
|
|
52
|
-
ui.clearOutput?.();
|
|
53
|
-
const bannerLines = typeof renderStartupBanner === "function" ? renderStartupBanner() : [];
|
|
54
|
-
if (bannerLines.length > 0) {
|
|
55
|
-
for (const line of bannerLines) ui.writeln(line);
|
|
56
|
-
} else {
|
|
57
|
-
ui.writeln(`Started new session: ${result.sessionId}`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
} catch (err) {
|
|
61
|
-
ui.writeln(`Error: ${err.message}`);
|
|
62
|
-
}
|
|
63
|
-
return { handled: true, refreshContextTokens };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (trimmed === "/help") {
|
|
67
|
-
for (const line of formatHelpLines()) ui.writeln(line);
|
|
68
|
-
return { handled: true };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const modeCommand = parseModeCommand(trimmed);
|
|
72
|
-
if (modeCommand.type !== "none") {
|
|
73
|
-
for (const line of handleModeCommand(modeCommand, { modeState })) ui.writeln(line);
|
|
74
|
-
return { handled: true };
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (trimmed === "/hotkeys") {
|
|
78
|
-
for (const line of formatHotkeysPanel(keybindings, keybindingDiagnostics)) ui.writeln(line);
|
|
79
|
-
return { handled: true };
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (trimmed === "/templates") {
|
|
83
|
-
for (const line of formatPromptTemplateLines(promptTemplates, promptTemplateDiagnostics)) ui.writeln(line);
|
|
84
|
-
return { handled: true };
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const exportCommand = parseExportCommand(trimmed);
|
|
88
|
-
if (exportCommand.type !== "none") {
|
|
89
|
-
for (const line of await handleExportCommand(exportCommand, { runner, sessionState, sessionSource, projectMarchDir })) ui.writeln(line);
|
|
90
|
-
return { handled: true };
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const settingsCommand = parseSettingsCommand(trimmed);
|
|
94
|
-
if (settingsCommand.type !== "none") {
|
|
95
|
-
for (const line of handleSettingsCommand(settingsCommand, { cwd: runner.engine.cwd, homeDir: settingsHomeDir })) {
|
|
96
|
-
ui.writeln(line);
|
|
97
|
-
}
|
|
98
|
-
return { handled: true };
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (trimmed === "/extensions") {
|
|
102
|
-
for (const line of listExtensionPathsCommand(
|
|
103
|
-
extensionPaths,
|
|
104
|
-
runner.getExtensionDiagnostics?.(),
|
|
105
|
-
runner.getExtensionLifecycleState?.(),
|
|
106
|
-
)) ui.writeln(line);
|
|
107
|
-
return { handled: true };
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const thinkingCommand = parseThinkingCommand(trimmed);
|
|
111
|
-
if (thinkingCommand.type !== "none") {
|
|
112
|
-
for (const line of await handleThinkingCommand(thinkingCommand, { runner, ui })) ui.writeln(line);
|
|
113
|
-
return { handled: true };
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (trimmed === "/status") {
|
|
117
|
-
for (const line of statusCommand({
|
|
118
|
-
runner,
|
|
119
|
-
sessionState,
|
|
120
|
-
sessionSource,
|
|
121
|
-
extensionDiagnostics: runner.getExtensionDiagnostics?.() ?? [],
|
|
122
|
-
lifecycleState: runner.getExtensionLifecycleState?.() ?? null,
|
|
123
|
-
})) ui.writeln(line);
|
|
124
|
-
return { handled: true };
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (trimmed === "/notify") {
|
|
128
|
-
const result = await runner.notifyTest?.();
|
|
129
|
-
ui.writeln(formatNotificationResult(result));
|
|
130
|
-
return { handled: true };
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const shellCommand = parseShellCommand(trimmed);
|
|
134
|
-
if (shellCommand.type !== "none") {
|
|
135
|
-
for (const line of handleShellCommand(shellCommand, { shellRuntime: runner.shellRuntime })) ui.writeln(line);
|
|
136
|
-
return { handled: true };
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const nameCommand = parseSessionNameCommand(trimmed);
|
|
140
|
-
if (nameCommand.type !== "none") {
|
|
141
|
-
for (const line of handleSessionNameCommand(nameCommand, { runner, sessionState, sessionSource })) ui.writeln(line);
|
|
142
|
-
return { handled: true };
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (trimmed === "/copy") {
|
|
146
|
-
for (const line of copyLastAssistantMessage({ engine: runner.engine, writeClipboard })) ui.writeln(line);
|
|
147
|
-
return { handled: true };
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (trimmed === "/mouse") {
|
|
151
|
-
ui.writeln("Mouse selection is always enabled.");
|
|
152
|
-
return { handled: true };
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const sessionSourceCommand = await handleSessionSourceCommand(trimmed, {
|
|
156
|
-
ui,
|
|
157
|
-
runner,
|
|
158
|
-
sessionState,
|
|
159
|
-
sessionsRoot,
|
|
160
|
-
projectMarchDir,
|
|
20
|
+
return {
|
|
21
|
+
...rest,
|
|
161
22
|
sessionSource,
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return { handled: true };
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const modelCommand = parseModelCommand(trimmed);
|
|
176
|
-
if (modelCommand.type !== "none") {
|
|
177
|
-
try {
|
|
178
|
-
ui.writeln(await handleModelCommand(modelCommand, { runner, ui, configHomeDir }));
|
|
179
|
-
} catch (err) {
|
|
180
|
-
ui.writeln(`Error: ${err.message}`);
|
|
181
|
-
}
|
|
182
|
-
return { handled: true };
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (trimmed === "/models") {
|
|
186
|
-
for (const line of listModels({ runner })) ui.writeln(line);
|
|
187
|
-
return { handled: true };
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return { handled: false };
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function formatNotificationResult(result) {
|
|
194
|
-
if (!result) return "notification: unavailable";
|
|
195
|
-
const channels = (result.results ?? [])
|
|
196
|
-
.map((entry) => `${entry.channel}:${entry.ok ? "ok" : entry.reason ?? "failed"}`)
|
|
197
|
-
.join(", ");
|
|
198
|
-
return `notification: ${result.ok ? "ok" : result.reason ?? "failed"}${channels ? ` (${channels})` : ""}`;
|
|
23
|
+
extensionPaths,
|
|
24
|
+
keybindings,
|
|
25
|
+
keybindingDiagnostics,
|
|
26
|
+
promptTemplates,
|
|
27
|
+
promptTemplateDiagnostics,
|
|
28
|
+
modeState,
|
|
29
|
+
renderStartupBanner,
|
|
30
|
+
settingsHomeDir,
|
|
31
|
+
configHomeDir,
|
|
32
|
+
};
|
|
199
33
|
}
|
|
@@ -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
|
+
}
|
|
@@ -103,6 +103,11 @@ The user primarily asks for software engineering work: fixing bugs, adding behav
|
|
|
103
103
|
- Use memory_save() to create memories or update whole metadata fields on an existing memory. Before creating a new memory, first search/open related memories and merge updates into an existing memory when they share the same topic, project, or decision thread; prefer modifying the existing memory file over creating a scattered new one. Tags are the primary retrieval key for future recall. Prefer lowercase-kebab-case tags like 'march-cli', 'tooling', 'permissions'.
|
|
104
104
|
- When learning multiple related external workflows or skills, maintain memory as an evolving domain library: start with the specific source name when only one item exists, then rename and rewrite the memory title/description as the scope grows; merge new related learnings into the same memory, preserving each source's unique traits while distilling reusable principles.
|
|
105
105
|
- Distinguish "migrating a Skill to memory" from "learning a Skill": migration preserves the complete Skill folder under memory_root/skills/ and creates a memory entry as its index; that memory should describe what the Skill is for and reference the copied Skill folder path so future recall knows how to use it. Learning only reads and internalizes the Skill's methods, scenarios, and principles into ordinary memory without copying source files. Infer the action from the user's wording, and ask when ambiguous.
|
|
106
|
+
- Do not proactively modify the agent profile. Update `agent.md` only when the user explicitly asks to change persistent agent behavior.
|
|
107
|
+
- Proactively maintain the user profile when stable, reusable user preferences, working style, goals, or identity signals appear in conversation.
|
|
108
|
+
- For user profile updates, distinguish explicit facts from inferred preferences. Write explicit facts directly; write inferred items as preferences or tendencies, and avoid overstating confidence.
|
|
109
|
+
- Do not store transient task details, sensitive information, or one-off opinions in the user profile. Use memory for project-specific reusable knowledge and current conversation for short-lived context.
|
|
110
|
+
- When a user profile update is non-obvious or potentially sensitive, ask before writing; otherwise update it as part of normal task completion and mention it briefly in the final summary.
|
|
106
111
|
- Unlike recall blocks, this system-core center is always visible in every model call. Only update the center for instructions that must always be followed; use memory for contextual, project-specific, or recall-dependent knowledge.
|
|
107
112
|
- If execution takes a meaningful detour, create or update a memory after the task. A detour means the initial plan or assumption failed, multiple approaches were tried, and the final successful path contains reusable project knowledge. Record the failed assumption, what was tried, and the successful approach. Prefer updating an existing related memory over creating a new one.
|
|
108
113
|
</memory_system>
|