helixmind 0.5.26 → 0.6.0
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/dist/cli/agent/loop.js +3 -3
- package/dist/cli/agent/loop.js.map +1 -1
- package/dist/cli/agent/permissions.d.ts.map +1 -1
- package/dist/cli/agent/permissions.js +27 -9
- package/dist/cli/agent/permissions.js.map +1 -1
- package/dist/cli/agent/sandbox.d.ts +2 -12
- package/dist/cli/agent/sandbox.d.ts.map +1 -1
- package/dist/cli/agent/sandbox.js +3 -124
- package/dist/cli/agent/sandbox.js.map +1 -1
- package/dist/cli/agent/security.d.ts +4 -0
- package/dist/cli/agent/security.d.ts.map +1 -0
- package/dist/cli/agent/security.js +7 -0
- package/dist/cli/agent/security.js.map +1 -0
- package/dist/cli/agent/shell/classifier.d.ts +9 -0
- package/dist/cli/agent/shell/classifier.d.ts.map +1 -0
- package/dist/cli/agent/shell/classifier.js +283 -0
- package/dist/cli/agent/shell/classifier.js.map +1 -0
- package/dist/cli/agent/shell/summary.d.ts +3 -0
- package/dist/cli/agent/shell/summary.d.ts.map +1 -0
- package/dist/cli/agent/shell/summary.js +45 -0
- package/dist/cli/agent/shell/summary.js.map +1 -0
- package/dist/cli/agent/shell/types.d.ts +14 -0
- package/dist/cli/agent/shell/types.d.ts.map +1 -0
- package/dist/cli/agent/shell/types.js +2 -0
- package/dist/cli/agent/shell/types.js.map +1 -0
- package/dist/cli/agent/shell/windows.d.ts +5 -0
- package/dist/cli/agent/shell/windows.d.ts.map +1 -0
- package/dist/cli/agent/shell/windows.js +113 -0
- package/dist/cli/agent/shell/windows.js.map +1 -0
- package/dist/cli/agent/tools/edit-file.js +1 -1
- package/dist/cli/agent/tools/edit-file.js.map +1 -1
- package/dist/cli/agent/tools/find.js +1 -1
- package/dist/cli/agent/tools/find.js.map +1 -1
- package/dist/cli/agent/tools/git-commit.js +7 -7
- package/dist/cli/agent/tools/git-commit.js.map +1 -1
- package/dist/cli/agent/tools/git-diff.js +3 -3
- package/dist/cli/agent/tools/git-diff.js.map +1 -1
- package/dist/cli/agent/tools/git-log.js +3 -3
- package/dist/cli/agent/tools/git-log.js.map +1 -1
- package/dist/cli/agent/tools/git-status.js +6 -6
- package/dist/cli/agent/tools/git-status.js.map +1 -1
- package/dist/cli/agent/tools/list-dir.js +3 -3
- package/dist/cli/agent/tools/list-dir.js.map +1 -1
- package/dist/cli/agent/tools/read-file.js +1 -1
- package/dist/cli/agent/tools/read-file.js.map +1 -1
- package/dist/cli/agent/tools/registry.d.ts +3 -0
- package/dist/cli/agent/tools/registry.d.ts.map +1 -1
- package/dist/cli/agent/tools/registry.js.map +1 -1
- package/dist/cli/agent/tools/run-command.js +18 -17
- package/dist/cli/agent/tools/run-command.js.map +1 -1
- package/dist/cli/agent/tools/search.js +2 -2
- package/dist/cli/agent/tools/search.js.map +1 -1
- package/dist/cli/agent/tools/write-file.js +1 -1
- package/dist/cli/agent/tools/write-file.js.map +1 -1
- package/dist/cli/bench/runner.d.ts.map +1 -1
- package/dist/cli/bench/runner.js +1 -0
- package/dist/cli/bench/runner.js.map +1 -1
- package/dist/cli/brain/web-chat-handler.d.ts.map +1 -1
- package/dist/cli/brain/web-chat-handler.js +1 -0
- package/dist/cli/brain/web-chat-handler.js.map +1 -1
- package/dist/cli/commands/chat.d.ts.map +1 -1
- package/dist/cli/commands/chat.js +270 -92
- package/dist/cli/commands/chat.js.map +1 -1
- package/dist/cli/config/store.d.ts +11 -0
- package/dist/cli/config/store.d.ts.map +1 -1
- package/dist/cli/config/store.js +13 -0
- package/dist/cli/config/store.js.map +1 -1
- package/dist/cli/sessions/session.d.ts +5 -0
- package/dist/cli/sessions/session.d.ts.map +1 -1
- package/dist/cli/sessions/session.js +2 -0
- package/dist/cli/sessions/session.js.map +1 -1
- package/dist/cli/sessions/tab-view.d.ts.map +1 -1
- package/dist/cli/sessions/tab-view.js +6 -0
- package/dist/cli/sessions/tab-view.js.map +1 -1
- package/dist/cli/ui/statusbar.d.ts +4 -0
- package/dist/cli/ui/statusbar.d.ts.map +1 -1
- package/dist/cli/ui/statusbar.js +14 -1
- package/dist/cli/ui/statusbar.js.map +1 -1
- package/dist/cli/worktree/git.d.ts +9 -0
- package/dist/cli/worktree/git.d.ts.map +1 -0
- package/dist/cli/worktree/git.js +108 -0
- package/dist/cli/worktree/git.js.map +1 -0
- package/dist/cli/worktree/manager.d.ts +12 -0
- package/dist/cli/worktree/manager.d.ts.map +1 -0
- package/dist/cli/worktree/manager.js +48 -0
- package/dist/cli/worktree/manager.js.map +1 -0
- package/dist/cli/worktree/policy.d.ts +5 -0
- package/dist/cli/worktree/policy.d.ts.map +1 -0
- package/dist/cli/worktree/policy.js +46 -0
- package/dist/cli/worktree/policy.js.map +1 -0
- package/dist/cli/worktree/runtime.d.ts +11 -0
- package/dist/cli/worktree/runtime.d.ts.map +1 -0
- package/dist/cli/worktree/runtime.js +27 -0
- package/dist/cli/worktree/runtime.js.map +1 -0
- package/dist/cli/worktree/session.d.ts +11 -0
- package/dist/cli/worktree/session.d.ts.map +1 -0
- package/dist/cli/worktree/session.js +22 -0
- package/dist/cli/worktree/session.js.map +1 -0
- package/dist/cli/worktree/types.d.ts +28 -0
- package/dist/cli/worktree/types.d.ts.map +1 -0
- package/dist/cli/worktree/types.js +2 -0
- package/dist/cli/worktree/types.js.map +1 -0
- package/package.json +1 -1
|
@@ -70,6 +70,8 @@ import { AGENT_IDENTITIES } from '../agent/plan-types.js';
|
|
|
70
70
|
import { SwarmController } from '../agent/swarm.js';
|
|
71
71
|
import { TaskOrchestrator } from '../jarvis/orchestrator.js';
|
|
72
72
|
import { ParallelExecutor } from '../jarvis/parallel.js';
|
|
73
|
+
import { WorktreeManager } from '../worktree/manager.js';
|
|
74
|
+
import { prepareExecutionRuntime } from '../worktree/runtime.js';
|
|
73
75
|
import chalk from 'chalk';
|
|
74
76
|
const HELP_CATEGORIES = [
|
|
75
77
|
{
|
|
@@ -171,6 +173,8 @@ const HELP_CATEGORIES = [
|
|
|
171
173
|
{ cmd: '/undo', label: '/undo', description: 'Undo file changes' },
|
|
172
174
|
{ cmd: '/diff', label: '/diff', description: 'Show uncommitted git changes' },
|
|
173
175
|
{ cmd: '/git', label: '/git', description: 'Show git branch & status' },
|
|
176
|
+
{ cmd: '/worktree', label: '/worktree', description: 'Show isolated worktree status' },
|
|
177
|
+
{ cmd: '/worktree auto', label: '/worktree auto', description: 'Enable isolated worktrees for risky runs' },
|
|
174
178
|
{ cmd: '/project', label: '/project', description: 'Show project info' },
|
|
175
179
|
{ cmd: '/export', label: '/export', description: 'Export spiral as ZIP' },
|
|
176
180
|
],
|
|
@@ -257,12 +261,14 @@ ${chalk.hex('#00cc66').bold(' Validation Matrix')}
|
|
|
257
261
|
${theme.primary('/validation strict'.padEnd(22))} ${theme.dim('Treat warnings as errors')}
|
|
258
262
|
${theme.primary('/validation stats'.padEnd(22))} ${theme.dim('Show validation statistics')}
|
|
259
263
|
|
|
260
|
-
${chalk.hex('#8a2be2').bold(' Code & Git')}
|
|
261
|
-
${theme.primary('/undo [n|list]'.padEnd(22))} ${theme.dim('Undo last n file changes (or list history)')}
|
|
262
|
-
${theme.primary('/diff'.padEnd(22))} ${theme.dim('Show all uncommitted git changes')}
|
|
263
|
-
${theme.primary('/git'.padEnd(22))} ${theme.dim('Show git branch & status')}
|
|
264
|
-
${theme.primary('/
|
|
265
|
-
${theme.primary('/
|
|
264
|
+
${chalk.hex('#8a2be2').bold(' Code & Git')}
|
|
265
|
+
${theme.primary('/undo [n|list]'.padEnd(22))} ${theme.dim('Undo last n file changes (or list history)')}
|
|
266
|
+
${theme.primary('/diff'.padEnd(22))} ${theme.dim('Show all uncommitted git changes')}
|
|
267
|
+
${theme.primary('/git'.padEnd(22))} ${theme.dim('Show git branch & status')}
|
|
268
|
+
${theme.primary('/worktree'.padEnd(22))} ${theme.dim('Show isolated worktree status')}
|
|
269
|
+
${theme.primary('/worktree auto'.padEnd(22))} ${theme.dim('Enable worktrees for risky agent runs')}
|
|
270
|
+
${theme.primary('/project'.padEnd(22))} ${theme.dim('Show detected project info')}
|
|
271
|
+
${theme.primary('/export [dir]'.padEnd(22))} ${theme.dim('Export spiral as ZIP archive')}
|
|
266
272
|
|
|
267
273
|
${chalk.hex('#6c757d').bold(' Navigation')}
|
|
268
274
|
${theme.primary('/exit /quit'.padEnd(22))} ${theme.dim('Exit HelixMind')}
|
|
@@ -274,6 +280,26 @@ export async function chatCommand(options) {
|
|
|
274
280
|
const configDir = join(homedir(), '.helixmind');
|
|
275
281
|
const store = new ConfigStore(configDir);
|
|
276
282
|
let config = store.getAll();
|
|
283
|
+
const projectRoot = process.cwd();
|
|
284
|
+
const createWorktreeManager = () => new WorktreeManager(config.worktree);
|
|
285
|
+
const activeWorktreeRuntimes = new Map();
|
|
286
|
+
const prepareSessionRuntime = async (reason, sessionId, onStatus) => {
|
|
287
|
+
const runtime = await prepareExecutionRuntime(createWorktreeManager(), { reason, sessionId, projectRoot }, onStatus);
|
|
288
|
+
if (runtime.isolated) {
|
|
289
|
+
activeWorktreeRuntimes.set(sessionId, runtime);
|
|
290
|
+
}
|
|
291
|
+
let released = false;
|
|
292
|
+
return {
|
|
293
|
+
...runtime,
|
|
294
|
+
release: async () => {
|
|
295
|
+
if (released)
|
|
296
|
+
return;
|
|
297
|
+
released = true;
|
|
298
|
+
activeWorktreeRuntimes.delete(sessionId);
|
|
299
|
+
await runtime.release();
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
};
|
|
277
303
|
// Collect startup banner — written into scroll region AFTER screen activation
|
|
278
304
|
// so content sits directly above the input frame (no gap).
|
|
279
305
|
const startupBannerParts = [renderLogo(), renderVersion(VERSION) + '\n\n'];
|
|
@@ -1017,13 +1043,22 @@ export async function chatCommand(options) {
|
|
|
1017
1043
|
autonomousMode = true;
|
|
1018
1044
|
(async () => {
|
|
1019
1045
|
const completed = [];
|
|
1046
|
+
const runtime = await prepareSessionRuntime('autonomous', bgSession.id, (message) => {
|
|
1047
|
+
bgSession.capture(message);
|
|
1048
|
+
});
|
|
1049
|
+
if (runtime.worktree) {
|
|
1050
|
+
bgSession.worktree = {
|
|
1051
|
+
root: runtime.executionRoot,
|
|
1052
|
+
reason: runtime.worktree.reason,
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1020
1055
|
try {
|
|
1021
1056
|
await runAutonomousLoop({
|
|
1022
1057
|
sendMessage: async (prompt) => {
|
|
1023
1058
|
bgSession.controller.reset();
|
|
1024
1059
|
const resultTextHolder = { text: '' };
|
|
1025
1060
|
bgSession.buffer.onSummary = (t) => { resultTextHolder.text = t; };
|
|
1026
|
-
await sendAgentMessage(prompt, bgSession.history, provider, project, spiralEngine, config, permissions, bgSession.undoStack, checkpointStore, bgSession.controller, new ActivityIndicator(), bgSession.buffer, (inp, out) => { sessionTokensInput += inp; sessionTokensOutput += out; }, () => { sessionToolCalls++; }, undefined, { enabled: false, verbose: false, strict: false });
|
|
1061
|
+
await sendAgentMessage(prompt, bgSession.history, provider, project, spiralEngine, config, permissions, bgSession.undoStack, checkpointStore, bgSession.controller, new ActivityIndicator(), bgSession.buffer, (inp, out) => { sessionTokensInput += inp; sessionTokensOutput += out; }, () => { sessionToolCalls++; }, undefined, { enabled: false, verbose: false, strict: false }, undefined, undefined, undefined, undefined, undefined, undefined, undefined, runtime);
|
|
1027
1062
|
bgSession.buffer.onSummary = undefined;
|
|
1028
1063
|
return resultTextHolder.text;
|
|
1029
1064
|
},
|
|
@@ -1045,6 +1080,9 @@ export async function chatCommand(options) {
|
|
|
1045
1080
|
bgSession.capture(`Error: ${err}`);
|
|
1046
1081
|
}
|
|
1047
1082
|
}
|
|
1083
|
+
finally {
|
|
1084
|
+
await runtime.release();
|
|
1085
|
+
}
|
|
1048
1086
|
autonomousMode = false;
|
|
1049
1087
|
sessionMgr.complete(bgSession.id, {
|
|
1050
1088
|
text: completed.join('\n'),
|
|
@@ -1572,9 +1610,23 @@ export async function chatCommand(options) {
|
|
|
1572
1610
|
onStatusChange: () => { updateStatusBar(); },
|
|
1573
1611
|
runWorkerSession: async (session, prompt, agentIdentity) => {
|
|
1574
1612
|
const bgActivity = new ActivityIndicator();
|
|
1613
|
+
const runtime = await prepareSessionRuntime('swarm_worker', session.id, (message) => {
|
|
1614
|
+
session.capture(message);
|
|
1615
|
+
});
|
|
1616
|
+
if (runtime.worktree) {
|
|
1617
|
+
session.worktree = {
|
|
1618
|
+
root: runtime.executionRoot,
|
|
1619
|
+
reason: runtime.worktree.reason,
|
|
1620
|
+
};
|
|
1621
|
+
}
|
|
1575
1622
|
session.onCapture = (line, index) => { pushOutputLine(session.id, line, index); };
|
|
1576
1623
|
pushSessionCreated(serializeSession(session));
|
|
1577
|
-
|
|
1624
|
+
try {
|
|
1625
|
+
await sendAgentMessage(prompt, session.history, provider, project, spiralEngine, config, permissions, session.undoStack, checkpointStore, session.controller, bgActivity, session.buffer, (inp, out) => { sessionTokensInput += inp; sessionTokensOutput += out; }, () => { sessionToolCalls++; roundToolCalls++; }, undefined, { enabled: validationEnabled, verbose: validationVerbose, strict: validationStrict }, undefined, undefined, undefined, undefined, undefined, undefined, undefined, runtime);
|
|
1626
|
+
}
|
|
1627
|
+
finally {
|
|
1628
|
+
await runtime.release();
|
|
1629
|
+
}
|
|
1578
1630
|
const text = session.buffer.buildContext();
|
|
1579
1631
|
const errors = session.buffer.getRecentErrors().map(e => e.summary);
|
|
1580
1632
|
return { text, errors };
|
|
@@ -1910,6 +1962,10 @@ export async function chatCommand(options) {
|
|
|
1910
1962
|
activeMode: jarvisDaemonSession && jarvisDaemonSession.status === 'running'
|
|
1911
1963
|
? 'jarvis'
|
|
1912
1964
|
: autonomousMode ? 'monitor' : 'cli',
|
|
1965
|
+
worktree: {
|
|
1966
|
+
mode: config.worktree.mode,
|
|
1967
|
+
active: activeWorktreeRuntimes.size,
|
|
1968
|
+
},
|
|
1913
1969
|
jarvisName: jarvisDaemonSession && jarvisDaemonSession.status === 'running'
|
|
1914
1970
|
? jarvisIdentity.getIdentity().name
|
|
1915
1971
|
: undefined,
|
|
@@ -2246,54 +2302,22 @@ export async function chatCommand(options) {
|
|
|
2246
2302
|
}
|
|
2247
2303
|
// Command suggestions are now handled by InputManager's built-in _interceptTtyWrite.
|
|
2248
2304
|
// The panel opens/updates/closes automatically as the user types slash commands.
|
|
2249
|
-
// === ESC detection ===
|
|
2305
|
+
// === ESC detection (single ESC = stop for normal agents) ===
|
|
2250
2306
|
// Double-ESC (rewind browser) is handled by the raw data listener below.
|
|
2251
2307
|
// Skip if a full-screen browser (Rewind/Plan) is open — it handles ESC itself.
|
|
2252
2308
|
// Skip if ESC was used to close the suggestion panel (panelJustClosed flag)
|
|
2253
2309
|
//
|
|
2254
|
-
// Special modes (Jarvis
|
|
2255
|
-
//
|
|
2256
|
-
//
|
|
2310
|
+
// Special modes (Jarvis, autonomous): ESC is handled ENTIRELY by the raw
|
|
2311
|
+
// data listener below — the keypress handler does NOTHING for them.
|
|
2312
|
+
// This prevents conflicts between the two handlers and avoids cursor jumps
|
|
2313
|
+
// from hint messages. See raw data listener for: quick double-ESC → Rewind,
|
|
2314
|
+
// deliberate double-ESC (>1s gap) → stop special mode.
|
|
2257
2315
|
if (key.name === 'escape' && !fullScreenBrowserOpen && !panelJustClosed) {
|
|
2258
2316
|
const jarvisRunning = jarvisDaemonSession && jarvisDaemonSession.status === 'running';
|
|
2259
2317
|
const specialMode = jarvisRunning || autonomousMode;
|
|
2260
2318
|
const normalRunning = agentRunning || sessionMgr.hasBackgroundTasks;
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
// Stopping requires deliberate double-ESC (>1s gap) — handled below.
|
|
2264
|
-
const now = Date.now();
|
|
2265
|
-
if (lastSpecialEscTime > 0 && (now - lastSpecialEscTime) >= 1000) {
|
|
2266
|
-
// Deliberate double-ESC (>1s gap) → STOP special mode
|
|
2267
|
-
lastSpecialEscTime = 0;
|
|
2268
|
-
closeSuggestionPanel();
|
|
2269
|
-
activity.stop('Stopped');
|
|
2270
|
-
agentController.abort();
|
|
2271
|
-
sessionMgr.abortAll();
|
|
2272
|
-
if (activeSwarm) {
|
|
2273
|
-
activeSwarm.abort();
|
|
2274
|
-
activeSwarm = null;
|
|
2275
|
-
}
|
|
2276
|
-
autonomousMode = false;
|
|
2277
|
-
if (jarvisRunning) {
|
|
2278
|
-
jarvisDaemonSession.abort();
|
|
2279
|
-
jarvisDaemonSession = null;
|
|
2280
|
-
jarvisQueue.setDaemonState('stopped');
|
|
2281
|
-
jarvisPaused = false;
|
|
2282
|
-
releaseJarvisSlot();
|
|
2283
|
-
}
|
|
2284
|
-
typeAheadBuffer.length = 0;
|
|
2285
|
-
agentRunning = false;
|
|
2286
|
-
process.stdout.write('\n');
|
|
2287
|
-
renderInfo(chalk.red('\u23F9 STOPPED') + chalk.dim(' \u2014 Special mode exited (deliberate double-ESC).'));
|
|
2288
|
-
showPrompt();
|
|
2289
|
-
}
|
|
2290
|
-
else {
|
|
2291
|
-
// First ESC or too fast — record time, show hint
|
|
2292
|
-
lastSpecialEscTime = now;
|
|
2293
|
-
screen.writeOutput(chalk.dim(' \u23F8 ESC registered \u2014 press ESC again after 1s to stop, or quick double-ESC for Rewind\n'));
|
|
2294
|
-
}
|
|
2295
|
-
}
|
|
2296
|
-
else if (normalRunning) {
|
|
2319
|
+
// Special modes: skip entirely — raw data handler manages ESC
|
|
2320
|
+
if (!specialMode && normalRunning) {
|
|
2297
2321
|
// Normal agent work: single ESC = immediate stop (unchanged)
|
|
2298
2322
|
closeSuggestionPanel();
|
|
2299
2323
|
activity.stop('Stopped');
|
|
@@ -2381,21 +2405,58 @@ export async function chatCommand(options) {
|
|
|
2381
2405
|
// Bare ESC = exactly 1 byte: \x1b
|
|
2382
2406
|
// Double ESC = exactly 2 bytes: \x1b\x1b
|
|
2383
2407
|
// Anything else starting with \x1b is an ANSI escape sequence → ignore
|
|
2408
|
+
// Detect special mode (Jarvis/autonomous) — ESC handling is done entirely here
|
|
2409
|
+
const isSpecialMode = !!(jarvisDaemonSession && jarvisDaemonSession.status === 'running') || autonomousMode;
|
|
2384
2410
|
if (bytes.length === 2 && bytes[0] === 0x1b && bytes[1] === 0x1b) {
|
|
2385
|
-
// Double-ESC in one chunk → open rewind immediately
|
|
2411
|
+
// Double-ESC in one chunk → open rewind immediately (all modes)
|
|
2386
2412
|
lastRawEscTime = 0;
|
|
2413
|
+
lastSpecialEscTime = 0;
|
|
2387
2414
|
await openRewindBrowser();
|
|
2388
2415
|
return;
|
|
2389
2416
|
}
|
|
2390
2417
|
if (bytes.length === 1 && bytes[0] === 0x1b) {
|
|
2391
|
-
// Single bare ESC — check timing for double-ESC
|
|
2392
2418
|
const now = Date.now();
|
|
2419
|
+
// Quick double-ESC detection (<800ms) → Rewind (all modes)
|
|
2393
2420
|
if (now - lastRawEscTime < RAW_ESC_THRESHOLD && lastRawEscTime > 0) {
|
|
2394
2421
|
lastRawEscTime = 0;
|
|
2422
|
+
lastSpecialEscTime = 0;
|
|
2395
2423
|
await openRewindBrowser();
|
|
2424
|
+
return;
|
|
2396
2425
|
}
|
|
2397
|
-
|
|
2398
|
-
|
|
2426
|
+
// Special mode: deliberate double-ESC (>1s gap) → stop
|
|
2427
|
+
if (isSpecialMode && lastSpecialEscTime > 0 && (now - lastSpecialEscTime) >= 1000) {
|
|
2428
|
+
lastSpecialEscTime = 0;
|
|
2429
|
+
lastRawEscTime = 0;
|
|
2430
|
+
activity.stop('Stopped');
|
|
2431
|
+
agentController.abort();
|
|
2432
|
+
sessionMgr.abortAll();
|
|
2433
|
+
if (activeSwarm) {
|
|
2434
|
+
activeSwarm.abort();
|
|
2435
|
+
activeSwarm = null;
|
|
2436
|
+
}
|
|
2437
|
+
autonomousMode = false;
|
|
2438
|
+
const jarvisRunning = jarvisDaemonSession && jarvisDaemonSession.status === 'running';
|
|
2439
|
+
if (jarvisRunning) {
|
|
2440
|
+
jarvisDaemonSession.abort();
|
|
2441
|
+
jarvisDaemonSession = null;
|
|
2442
|
+
jarvisQueue.setDaemonState('stopped');
|
|
2443
|
+
jarvisPaused = false;
|
|
2444
|
+
releaseJarvisSlot();
|
|
2445
|
+
}
|
|
2446
|
+
typeAheadBuffer.length = 0;
|
|
2447
|
+
agentRunning = false;
|
|
2448
|
+
renderInfo(chalk.red('\u23F9 STOPPED') + chalk.dim(' \u2014 Special mode exited.'));
|
|
2449
|
+
showPrompt();
|
|
2450
|
+
return;
|
|
2451
|
+
}
|
|
2452
|
+
// Record ESC time for next detection
|
|
2453
|
+
lastRawEscTime = now;
|
|
2454
|
+
if (isSpecialMode) {
|
|
2455
|
+
lastSpecialEscTime = now;
|
|
2456
|
+
// Brief hint via chrome row (no scroll region write = no cursor jump)
|
|
2457
|
+
chrome.setRow(0, chalk.dim('\u23F8 ESC \u2014 double-ESC: Rewind | wait 1s + ESC: stop'));
|
|
2458
|
+
// Auto-restore chrome row 0 after 2 seconds
|
|
2459
|
+
setTimeout(() => { chrome.drawFrameBottom(); }, 2000);
|
|
2399
2460
|
}
|
|
2400
2461
|
return;
|
|
2401
2462
|
}
|
|
@@ -2925,6 +2986,15 @@ export async function chatCommand(options) {
|
|
|
2925
2986
|
// Run in background — user keeps their prompt
|
|
2926
2987
|
(async () => {
|
|
2927
2988
|
const completed = [];
|
|
2989
|
+
const runtime = await prepareSessionRuntime('autonomous', bgSession.id, (message) => {
|
|
2990
|
+
bgSession.capture(message);
|
|
2991
|
+
});
|
|
2992
|
+
if (runtime.worktree) {
|
|
2993
|
+
bgSession.worktree = {
|
|
2994
|
+
root: runtime.executionRoot,
|
|
2995
|
+
reason: runtime.worktree.reason,
|
|
2996
|
+
};
|
|
2997
|
+
}
|
|
2928
2998
|
try {
|
|
2929
2999
|
await runAutonomousLoop({
|
|
2930
3000
|
sendMessage: async (prompt) => {
|
|
@@ -2953,6 +3023,9 @@ export async function chatCommand(options) {
|
|
|
2953
3023
|
bgSession.capture(`Error: ${err}`);
|
|
2954
3024
|
}
|
|
2955
3025
|
}
|
|
3026
|
+
finally {
|
|
3027
|
+
await runtime.release();
|
|
3028
|
+
}
|
|
2956
3029
|
autonomousMode = false;
|
|
2957
3030
|
sessionMgr.complete(bgSession.id, {
|
|
2958
3031
|
text: completed.join('\n'),
|
|
@@ -3140,6 +3213,12 @@ export async function chatCommand(options) {
|
|
|
3140
3213
|
engine: planEngine,
|
|
3141
3214
|
getState: () => planModeState,
|
|
3142
3215
|
setState: (s) => { planModeState = s; },
|
|
3216
|
+
}, {
|
|
3217
|
+
listActive: () => Array.from(activeWorktreeRuntimes.entries()).map(([sessionId, runtime]) => ({
|
|
3218
|
+
sessionId,
|
|
3219
|
+
reason: runtime.worktree?.reason ?? 'unknown',
|
|
3220
|
+
root: runtime.executionRoot,
|
|
3221
|
+
})),
|
|
3143
3222
|
});
|
|
3144
3223
|
if (handled === 'exit') {
|
|
3145
3224
|
spiralEngine?.close();
|
|
@@ -3245,43 +3324,61 @@ export async function chatCommand(options) {
|
|
|
3245
3324
|
planEngine.approve(plan.id);
|
|
3246
3325
|
renderInfo(chalk.green('\u2713 Plan approved — executing...'));
|
|
3247
3326
|
updateStatusBar();
|
|
3248
|
-
await
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3327
|
+
const runtime = await prepareSessionRuntime('plan_execution', plan.id, (message) => {
|
|
3328
|
+
renderInfo(chalk.dim(message));
|
|
3329
|
+
});
|
|
3330
|
+
const mainSession = sessionMgr.main;
|
|
3331
|
+
if (runtime.worktree) {
|
|
3332
|
+
mainSession.worktree = {
|
|
3333
|
+
root: runtime.executionRoot,
|
|
3334
|
+
reason: runtime.worktree.reason,
|
|
3335
|
+
};
|
|
3336
|
+
}
|
|
3337
|
+
try {
|
|
3338
|
+
await executePlan({
|
|
3339
|
+
plan,
|
|
3340
|
+
planEngine,
|
|
3341
|
+
agentLoopOptions: {
|
|
3342
|
+
provider,
|
|
3343
|
+
systemPrompt: assembleSystemPrompt(project.name !== 'unknown' ? project : null, { level_1: [], level_2: [], level_3: [], level_4: [], level_5: [], total_tokens: 0, node_count: 0 }, sessionBuffer.buildContext() || undefined, { provider: provider.name, model: provider.model }),
|
|
3344
|
+
permissions,
|
|
3345
|
+
toolContext: {
|
|
3346
|
+
projectRoot,
|
|
3347
|
+
executionRoot: runtime.executionRoot,
|
|
3348
|
+
undoStack,
|
|
3349
|
+
spiralEngine,
|
|
3350
|
+
bugJournal,
|
|
3351
|
+
browserController,
|
|
3352
|
+
visionProcessor,
|
|
3353
|
+
learningJournal: jarvisLearning,
|
|
3354
|
+
worktree: runtime.worktree,
|
|
3355
|
+
},
|
|
3356
|
+
checkpointStore,
|
|
3357
|
+
sessionBuffer,
|
|
3358
|
+
agentIdentity: AGENT_IDENTITIES.main,
|
|
3279
3359
|
},
|
|
3280
|
-
|
|
3281
|
-
|
|
3360
|
+
controller: agentController,
|
|
3361
|
+
conversationHistory: agentHistory,
|
|
3362
|
+
callbacks: {
|
|
3363
|
+
onPlanStepStart: (step, idx, total) => {
|
|
3364
|
+
renderPlanStepStart(step, idx, total, 'main');
|
|
3365
|
+
activity.setPlanProgress(idx, total, step.title);
|
|
3366
|
+
},
|
|
3367
|
+
onPlanStepEnd: (step, status) => {
|
|
3368
|
+
const idx = plan.steps.indexOf(step) + 1;
|
|
3369
|
+
renderPlanStepEnd(step, idx, plan.steps.length, status);
|
|
3370
|
+
activity.clearPlanProgress();
|
|
3371
|
+
},
|
|
3372
|
+
onPlanComplete: (completedPlan) => {
|
|
3373
|
+
renderPlanComplete(completedPlan);
|
|
3374
|
+
},
|
|
3282
3375
|
},
|
|
3283
|
-
}
|
|
3284
|
-
}
|
|
3376
|
+
});
|
|
3377
|
+
}
|
|
3378
|
+
finally {
|
|
3379
|
+
delete mainSession.worktree;
|
|
3380
|
+
await runtime.release();
|
|
3381
|
+
}
|
|
3285
3382
|
if (choice === 'approve_auto')
|
|
3286
3383
|
permissions.setSkipPermissions(false);
|
|
3287
3384
|
}
|
|
@@ -3322,12 +3419,26 @@ export async function chatCommand(options) {
|
|
|
3322
3419
|
onStatusChange: (_active, _total) => { updateStatusBar(); },
|
|
3323
3420
|
runWorkerSession: async (session, prompt, agentIdentity) => {
|
|
3324
3421
|
const bgActivity = new ActivityIndicator();
|
|
3422
|
+
const runtime = await prepareSessionRuntime('swarm_worker', session.id, (message) => {
|
|
3423
|
+
session.capture(message);
|
|
3424
|
+
});
|
|
3425
|
+
if (runtime.worktree) {
|
|
3426
|
+
session.worktree = {
|
|
3427
|
+
root: runtime.executionRoot,
|
|
3428
|
+
reason: runtime.worktree.reason,
|
|
3429
|
+
};
|
|
3430
|
+
}
|
|
3325
3431
|
// Wire output streaming to brain
|
|
3326
3432
|
session.onCapture = (line, index) => {
|
|
3327
3433
|
_pushOutputLine(session.id, line, index);
|
|
3328
3434
|
};
|
|
3329
3435
|
_pushSessionCreated(_serializeSession(session));
|
|
3330
|
-
|
|
3436
|
+
try {
|
|
3437
|
+
await sendAgentMessage(prompt, session.history, provider, project, spiralEngine, config, permissions, session.undoStack, checkpointStore, session.controller, bgActivity, session.buffer, (inp, out) => { sessionTokensInput += inp; sessionTokensOutput += out; }, () => { sessionToolCalls++; roundToolCalls++; }, undefined, { enabled: validationEnabled, verbose: validationVerbose, strict: validationStrict }, bugJournal, undefined, undefined, undefined, null, undefined, undefined, runtime);
|
|
3438
|
+
}
|
|
3439
|
+
finally {
|
|
3440
|
+
await runtime.release();
|
|
3441
|
+
}
|
|
3331
3442
|
const text = session.buffer.buildContext();
|
|
3332
3443
|
const errors = session.buffer.getRecentErrors().map(e => e.summary);
|
|
3333
3444
|
return { text, errors };
|
|
@@ -3601,13 +3712,14 @@ async function runBackgroundSession(session, prompt, provider, project, spiralEn
|
|
|
3601
3712
|
durationMs: session.elapsed,
|
|
3602
3713
|
};
|
|
3603
3714
|
}
|
|
3604
|
-
async function sendAgentMessage(input, agentHistory, provider, project, spiralEngine, config, permissions, undoStack, checkpointStore, controller, activity, sessionBuffer, onTokens, onToolCall, onAgentStart, validationOpts, bugJournal, browserController, visionProcessor, onBrowserScreenshot, jarvisContext, onStepInfo, learningJournal) {
|
|
3715
|
+
async function sendAgentMessage(input, agentHistory, provider, project, spiralEngine, config, permissions, undoStack, checkpointStore, controller, activity, sessionBuffer, onTokens, onToolCall, onAgentStart, validationOpts, bugJournal, browserController, visionProcessor, onBrowserScreenshot, jarvisContext, onStepInfo, learningJournal, runtime) {
|
|
3605
3716
|
// User message was rendered by renderUserMessage() in the caller before entering here.
|
|
3717
|
+
const executionRoot = runtime?.executionRoot ?? process.cwd();
|
|
3606
3718
|
// Intent Detection: Check if user wants to feed the codebase
|
|
3607
3719
|
const feedIntent = detectFeedIntent(input);
|
|
3608
3720
|
if (feedIntent.detected && feedIntent.confidence > 0.7 && spiralEngine) {
|
|
3609
3721
|
renderInfo('\u{1F300} Analyzing project in the background...\n');
|
|
3610
|
-
const rootDir =
|
|
3722
|
+
const rootDir = executionRoot;
|
|
3611
3723
|
// Background feed runs silently (no progress output) to avoid colliding
|
|
3612
3724
|
// with the activity indicator which also writes \r\x1b[K on the same line.
|
|
3613
3725
|
runFeedPipeline(rootDir, spiralEngine, {
|
|
@@ -3691,6 +3803,7 @@ async function sendAgentMessage(input, agentHistory, provider, project, spiralEn
|
|
|
3691
3803
|
permissions,
|
|
3692
3804
|
toolContext: {
|
|
3693
3805
|
projectRoot: process.cwd(),
|
|
3806
|
+
executionRoot,
|
|
3694
3807
|
undoStack,
|
|
3695
3808
|
spiralEngine,
|
|
3696
3809
|
bugJournal,
|
|
@@ -3698,6 +3811,7 @@ async function sendAgentMessage(input, agentHistory, provider, project, spiralEn
|
|
|
3698
3811
|
visionProcessor,
|
|
3699
3812
|
onBrowserScreenshot: onBrowserScreenshot ?? undefined,
|
|
3700
3813
|
learningJournal,
|
|
3814
|
+
worktree: runtime?.worktree,
|
|
3701
3815
|
},
|
|
3702
3816
|
checkpointStore,
|
|
3703
3817
|
sessionBuffer,
|
|
@@ -3894,7 +4008,7 @@ function showFullAutonomousWarning() {
|
|
|
3894
4008
|
process.stdout.write(d('\u2502 ') + d('ESC = stop agent if needed.') + d(' \u2502') + '\n');
|
|
3895
4009
|
process.stdout.write(d('\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F') + '\n\n');
|
|
3896
4010
|
}
|
|
3897
|
-
async function handleSlashCommand(input, messages, agentHistory, config, spiralEngine, store, rl, permissions, undoStack, checkpointStore, sessionBuffer, sessionTokens, sessionToolCalls, onProviderSwitch, onBrainSwitch, currentBrainScope, onAutonomous, onValidation, sessionManager, onRegisterBrainHandlers, onSubPrompt, bugJournal, jarvisCtx, onModeChange, chrome, planCtx) {
|
|
4011
|
+
async function handleSlashCommand(input, messages, agentHistory, config, spiralEngine, store, rl, permissions, undoStack, checkpointStore, sessionBuffer, sessionTokens, sessionToolCalls, onProviderSwitch, onBrainSwitch, currentBrainScope, onAutonomous, onValidation, sessionManager, onRegisterBrainHandlers, onSubPrompt, bugJournal, jarvisCtx, onModeChange, chrome, planCtx, worktreeCtx) {
|
|
3898
4012
|
const parts = input.split(/\s+/);
|
|
3899
4013
|
let cmd = parts[0].toLowerCase();
|
|
3900
4014
|
// ─── Chrome-aware selectMenu ───────────────────────────
|
|
@@ -3998,7 +4112,7 @@ async function handleSlashCommand(input, messages, agentHistory, config, spiralE
|
|
|
3998
4112
|
rl.resume();
|
|
3999
4113
|
if (helpIdx >= 0 && helpCmds[helpIdx]) {
|
|
4000
4114
|
// Execute the selected command
|
|
4001
|
-
return handleSlashCommand(helpCmds[helpIdx], messages, agentHistory, config, spiralEngine, store, rl, permissions, undoStack, checkpointStore, sessionBuffer, sessionTokens, sessionToolCalls, onProviderSwitch, onBrainSwitch, currentBrainScope, onAutonomous, onValidation, sessionManager, onRegisterBrainHandlers, onSubPrompt, bugJournal, jarvisCtx, onModeChange, chrome, planCtx);
|
|
4115
|
+
return handleSlashCommand(helpCmds[helpIdx], messages, agentHistory, config, spiralEngine, store, rl, permissions, undoStack, checkpointStore, sessionBuffer, sessionTokens, sessionToolCalls, onProviderSwitch, onBrainSwitch, currentBrainScope, onAutonomous, onValidation, sessionManager, onRegisterBrainHandlers, onSubPrompt, bugJournal, jarvisCtx, onModeChange, chrome, planCtx, worktreeCtx);
|
|
4002
4116
|
}
|
|
4003
4117
|
break;
|
|
4004
4118
|
}
|
|
@@ -4559,6 +4673,70 @@ async function handleSlashCommand(input, messages, agentHistory, config, spiralE
|
|
|
4559
4673
|
renderInfo('Not a git repository.');
|
|
4560
4674
|
}
|
|
4561
4675
|
break;
|
|
4676
|
+
case '/worktree': {
|
|
4677
|
+
const arg = parts[1]?.toLowerCase();
|
|
4678
|
+
const cleanupArg = parts[2]?.toLowerCase();
|
|
4679
|
+
const current = store.getAll().worktree;
|
|
4680
|
+
const active = worktreeCtx?.listActive() ?? [];
|
|
4681
|
+
const renderWorktreeStatus = () => {
|
|
4682
|
+
const modeColor = current.mode === 'force'
|
|
4683
|
+
? chalk.hex('#FF6600')
|
|
4684
|
+
: current.mode === 'auto'
|
|
4685
|
+
? chalk.cyan
|
|
4686
|
+
: chalk.dim;
|
|
4687
|
+
renderInfo(`Worktree mode: ${modeColor(current.mode)} | cleanup: ${current.cleanup} | branch prefix: ${current.branchPrefix} | max age: ${current.maxAgeHours}h`);
|
|
4688
|
+
if (active.length === 0) {
|
|
4689
|
+
renderInfo(chalk.dim('No active isolated runs.'));
|
|
4690
|
+
}
|
|
4691
|
+
else {
|
|
4692
|
+
renderInfo(`Active isolated runs: ${active.length}`);
|
|
4693
|
+
for (const runtime of active.slice(0, 8)) {
|
|
4694
|
+
renderInfo(` ${runtime.reason} [${runtime.sessionId}] -> ${runtime.root}`);
|
|
4695
|
+
}
|
|
4696
|
+
}
|
|
4697
|
+
renderInfo(chalk.dim('Usage: /worktree [status|auto|force|off|cleanup keep|cleanup remove|list]'));
|
|
4698
|
+
};
|
|
4699
|
+
if (!arg || arg === 'status' || arg === 'show') {
|
|
4700
|
+
renderWorktreeStatus();
|
|
4701
|
+
break;
|
|
4702
|
+
}
|
|
4703
|
+
if (arg === 'list') {
|
|
4704
|
+
if (active.length === 0) {
|
|
4705
|
+
renderInfo(chalk.dim('No active isolated runs.'));
|
|
4706
|
+
}
|
|
4707
|
+
else {
|
|
4708
|
+
renderInfo(`Active isolated runs: ${active.length}`);
|
|
4709
|
+
for (const runtime of active) {
|
|
4710
|
+
renderInfo(` ${runtime.reason} [${runtime.sessionId}] -> ${runtime.root}`);
|
|
4711
|
+
}
|
|
4712
|
+
}
|
|
4713
|
+
break;
|
|
4714
|
+
}
|
|
4715
|
+
if (arg === 'on') {
|
|
4716
|
+
store.set('worktree.mode', 'auto');
|
|
4717
|
+
renderInfo(chalk.cyan('Worktree mode set to auto') + chalk.dim(' — future /auto, /plan and /swarm runs use isolated worktrees.'));
|
|
4718
|
+
onModeChange?.();
|
|
4719
|
+
break;
|
|
4720
|
+
}
|
|
4721
|
+
if (arg === 'off' || arg === 'auto' || arg === 'force') {
|
|
4722
|
+
store.set('worktree.mode', arg);
|
|
4723
|
+
renderInfo(`Worktree mode set to ${arg}.`);
|
|
4724
|
+
onModeChange?.();
|
|
4725
|
+
break;
|
|
4726
|
+
}
|
|
4727
|
+
if (arg === 'cleanup') {
|
|
4728
|
+
if (cleanupArg === 'keep' || cleanupArg === 'remove') {
|
|
4729
|
+
store.set('worktree.cleanup', cleanupArg);
|
|
4730
|
+
renderInfo(`Worktree cleanup set to ${cleanupArg}.`);
|
|
4731
|
+
}
|
|
4732
|
+
else {
|
|
4733
|
+
renderInfo('Usage: /worktree cleanup [keep|remove]');
|
|
4734
|
+
}
|
|
4735
|
+
break;
|
|
4736
|
+
}
|
|
4737
|
+
renderWorktreeStatus();
|
|
4738
|
+
break;
|
|
4739
|
+
}
|
|
4562
4740
|
case '/export': {
|
|
4563
4741
|
const outputDir = parts[1] || process.cwd();
|
|
4564
4742
|
if (spiralEngine) {
|