lsd-pi 1.1.4 → 1.1.6
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/README.md +2 -1
- package/dist/headless-ui.js +2 -0
- package/dist/onboarding.js +11 -8
- package/dist/resources/extensions/async-jobs/async-bash-tool.js +14 -0
- package/dist/resources/extensions/async-jobs/await-tool.js +14 -0
- package/dist/resources/extensions/async-jobs/cancel-job-tool.js +7 -0
- package/dist/resources/extensions/cache-timer/index.js +5 -0
- package/dist/resources/extensions/codex-rotate/IMPLEMENTATION.md +18 -13
- package/dist/resources/extensions/codex-rotate/README.md +9 -3
- package/dist/resources/extensions/codex-rotate/commands.js +15 -8
- package/dist/resources/extensions/codex-rotate/index.js +17 -8
- package/dist/resources/extensions/memory/auto-extract.js +196 -80
- package/dist/resources/extensions/memory/dream.js +86 -19
- package/dist/resources/extensions/shared/rtk.js +89 -87
- package/dist/resources/extensions/subagent/index.js +33 -7
- package/dist/startup-model-validation.js +12 -2
- package/dist/update-check.js +2 -2
- package/dist/update-cmd.js +3 -3
- package/dist/welcome-screen.js +43 -14
- package/package.json +3 -2
- package/packages/pi-coding-agent/dist/core/agent-session.clear-queue.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/agent-session.clear-queue.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session.clear-queue.test.js +46 -0
- package/packages/pi-coding-agent/dist/core/agent-session.clear-queue.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +43 -4
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.js +2 -0
- package/packages/pi-coding-agent/dist/core/keybindings.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/pty-executor.d.ts +48 -0
- package/packages/pi-coding-agent/dist/core/pty-executor.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/pty-executor.js +173 -0
- package/packages/pi-coding-agent/dist/core/pty-executor.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +16 -3
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +18 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tool-approval.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tool-approval.js +2 -2
- package/packages/pi-coding-agent/dist/core/tool-approval.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.js +23 -2
- package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/pty.d.ts +50 -0
- package/packages/pi-coding-agent/dist/core/tools/pty.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/pty.js +289 -0
- package/packages/pi-coding-agent/dist/core/tools/pty.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +36 -22
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts +3 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +23 -62
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.js +1 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js +1 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.d.ts +39 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.js +182 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +36 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +2 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +106 -77
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +2 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +4 -13
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts +11 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +49 -13
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +27 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +251 -39
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +2 -2
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
- package/packages/pi-coding-agent/dist/utils/terminal-screen.d.ts +10 -0
- package/packages/pi-coding-agent/dist/utils/terminal-screen.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/utils/terminal-screen.js +67 -0
- package/packages/pi-coding-agent/dist/utils/terminal-screen.js.map +1 -0
- package/packages/pi-coding-agent/dist/utils/terminal-serializer.d.ts +7 -0
- package/packages/pi-coding-agent/dist/utils/terminal-serializer.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/utils/terminal-serializer.js +67 -0
- package/packages/pi-coding-agent/dist/utils/terminal-serializer.js.map +1 -0
- package/packages/pi-coding-agent/package.json +9 -4
- package/packages/pi-coding-agent/src/core/agent-session.clear-queue.test.ts +50 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +50 -4
- package/packages/pi-coding-agent/src/core/extensions/types.ts +1 -1
- package/packages/pi-coding-agent/src/core/keybindings.ts +4 -1
- package/packages/pi-coding-agent/src/core/pty-executor.ts +229 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +16 -3
- package/packages/pi-coding-agent/src/core/settings-manager.ts +27 -0
- package/packages/pi-coding-agent/src/core/tool-approval.ts +2 -2
- package/packages/pi-coding-agent/src/core/tools/index.ts +35 -2
- package/packages/pi-coding-agent/src/core/tools/pty.ts +354 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +37 -24
- package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +22 -70
- package/packages/pi-coding-agent/src/modes/interactive/components/branch-summary-message.ts +1 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/compaction-summary-message.ts +1 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/embedded-terminal.ts +224 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +45 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +2 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +104 -81
- package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +5 -19
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +55 -13
- package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +2 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +3 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +296 -48
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +2 -2
- package/packages/pi-coding-agent/src/utils/terminal-screen.ts +77 -0
- package/packages/pi-coding-agent/src/utils/terminal-serializer.ts +72 -0
- package/packages/pi-tui/dist/components/__tests__/editor-dropped-image.test.d.ts +2 -0
- package/packages/pi-tui/dist/components/__tests__/editor-dropped-image.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/__tests__/editor-dropped-image.test.js +105 -0
- package/packages/pi-tui/dist/components/__tests__/editor-dropped-image.test.js.map +1 -0
- package/packages/pi-tui/dist/components/editor.d.ts +4 -0
- package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/editor.js +57 -3
- package/packages/pi-tui/dist/components/editor.js.map +1 -1
- package/packages/pi-tui/dist/components/loader.d.ts +26 -6
- package/packages/pi-tui/dist/components/loader.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/loader.js +178 -18
- package/packages/pi-tui/dist/components/loader.js.map +1 -1
- package/packages/pi-tui/src/components/editor.ts +65 -3
- package/packages/pi-tui/src/components/loader.ts +196 -19
- package/pkg/dist/modes/interactive/theme/themes.js +2 -2
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/async-jobs/async-bash-tool.ts +13 -0
- package/src/resources/extensions/async-jobs/await-tool.ts +13 -0
- package/src/resources/extensions/async-jobs/cancel-job-tool.ts +8 -0
- package/src/resources/extensions/cache-timer/index.ts +102 -96
- package/src/resources/extensions/codex-rotate/IMPLEMENTATION.md +18 -13
- package/src/resources/extensions/codex-rotate/README.md +9 -3
- package/src/resources/extensions/codex-rotate/commands.ts +335 -329
- package/src/resources/extensions/codex-rotate/index.ts +85 -75
- package/src/resources/extensions/memory/auto-extract.ts +330 -204
- package/src/resources/extensions/memory/dream.ts +88 -21
- package/src/resources/extensions/memory/tests/auto-extract.test.ts +200 -144
- package/src/resources/extensions/shared/rtk.js +112 -0
- package/src/resources/extensions/subagent/index.ts +35 -6
|
@@ -58,27 +58,29 @@ function readJsonFile(path: string): Record<string, unknown> {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
function parseAutoDreamSettings(source: Record<string, unknown>): Partial<AutoDreamSettings> {
|
|
61
|
+
// Check top-level autoDream field first (set via /settings UI)
|
|
62
|
+
const topLevel = source.autoDream;
|
|
61
63
|
const memory = source.memory;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const settings = memory as {
|
|
64
|
+
const nested = (memory && typeof memory === 'object') ? memory as {
|
|
65
65
|
autoDream?: unknown;
|
|
66
66
|
autoDreamMinHours?: unknown;
|
|
67
67
|
autoDreamMinSessions?: unknown;
|
|
68
|
-
};
|
|
68
|
+
} : undefined;
|
|
69
|
+
|
|
70
|
+
// Top-level takes precedence for enabled; nested.memory for thresholds
|
|
71
|
+
const enabledSource = typeof topLevel === 'boolean' ? topLevel
|
|
72
|
+
: nested && typeof nested.autoDream === 'boolean' ? nested.autoDream
|
|
73
|
+
: undefined;
|
|
69
74
|
|
|
70
75
|
return {
|
|
71
|
-
enabled:
|
|
72
|
-
typeof settings.autoDream === 'boolean'
|
|
73
|
-
? settings.autoDream
|
|
74
|
-
: DEFAULT_AUTO_DREAM_SETTINGS.enabled,
|
|
76
|
+
...(enabledSource !== undefined ? { enabled: enabledSource } : {}),
|
|
75
77
|
minHours:
|
|
76
|
-
typeof
|
|
77
|
-
? Math.max(1,
|
|
78
|
+
nested && typeof nested.autoDreamMinHours === 'number' && Number.isFinite(nested.autoDreamMinHours)
|
|
79
|
+
? Math.max(1, nested.autoDreamMinHours)
|
|
78
80
|
: DEFAULT_AUTO_DREAM_SETTINGS.minHours,
|
|
79
81
|
minSessions:
|
|
80
|
-
typeof
|
|
81
|
-
? Math.max(1, Math.floor(
|
|
82
|
+
nested && typeof nested.autoDreamMinSessions === 'number' && Number.isFinite(nested.autoDreamMinSessions)
|
|
83
|
+
? Math.max(1, Math.floor(nested.autoDreamMinSessions))
|
|
82
84
|
: DEFAULT_AUTO_DREAM_SETTINGS.minSessions,
|
|
83
85
|
};
|
|
84
86
|
}
|
|
@@ -383,6 +385,13 @@ const { join, delimiter } = require('node:path');
|
|
|
383
385
|
const [cliPath, cwd, tmpPromptPath, auditPath, logPath, memoryDir, sessionDir, instruction, model, trigger, priorMtime, sessionCount] = process.argv.slice(1);
|
|
384
386
|
let finalized = false;
|
|
385
387
|
let pendingLogText = '';
|
|
388
|
+
let completionState = null;
|
|
389
|
+
let completionTimer = null;
|
|
390
|
+
let hardTimeout = null;
|
|
391
|
+
const ANSI_PATTERN = /\u001B\[[0-?]*[ -/]*[@-~]/g;
|
|
392
|
+
const CACHE_TIMER_RE = /^\[phase\]\s+cache-timer(?:\s*:\s*.*)?\s*$/i;
|
|
393
|
+
const SESSION_ENDED_RE = /^\[agent\]\s+Session ended/;
|
|
394
|
+
const HEADLESS_STATUS_RE = /^\[headless\]\s+Status:\s+(\w+)\s*$/i;
|
|
386
395
|
|
|
387
396
|
function newestMemoryMtime(dir) {
|
|
388
397
|
try {
|
|
@@ -454,6 +463,43 @@ function pruneBrokenMemoryRefs(dir) {
|
|
|
454
463
|
}
|
|
455
464
|
}
|
|
456
465
|
|
|
466
|
+
function stripAnsi(text) {
|
|
467
|
+
return String(text).replace(ANSI_PATTERN, '');
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function classifyLogLine(rawLine) {
|
|
471
|
+
const stripped = stripAnsi(rawLine).trim();
|
|
472
|
+
if (!stripped) {
|
|
473
|
+
return { stripped, keep: false, completion: null, completionReason: null };
|
|
474
|
+
}
|
|
475
|
+
if (CACHE_TIMER_RE.test(stripped)) {
|
|
476
|
+
return { stripped, keep: false, completion: null, completionReason: null };
|
|
477
|
+
}
|
|
478
|
+
if (SESSION_ENDED_RE.test(stripped)) {
|
|
479
|
+
return { stripped, keep: true, completion: 'finished', completionReason: 'session_end_detected' };
|
|
480
|
+
}
|
|
481
|
+
const headlessStatusMatch = stripped.match(HEADLESS_STATUS_RE);
|
|
482
|
+
if (headlessStatusMatch) {
|
|
483
|
+
const status = String(headlessStatusMatch[1] || '').toLowerCase();
|
|
484
|
+
if (status === 'complete') {
|
|
485
|
+
return { stripped, keep: true, completion: 'finished', completionReason: 'headless_status_complete' };
|
|
486
|
+
}
|
|
487
|
+
return { stripped, keep: true, completion: 'failed', completionReason: 'headless_status_' + status };
|
|
488
|
+
}
|
|
489
|
+
return { stripped, keep: true, completion: null, completionReason: null };
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function scheduleCompletion(completion, completionReason) {
|
|
493
|
+
if (!completion || completionState || completionTimer) return;
|
|
494
|
+
completionState = { completion, completionReason };
|
|
495
|
+
completionTimer = setTimeout(() => {
|
|
496
|
+
finalize(completion, completion === 'finished' ? 0 : 1, null, completionReason);
|
|
497
|
+
try { child.kill('SIGTERM'); } catch {}
|
|
498
|
+
setTimeout(() => { try { child.kill('SIGKILL'); } catch {} }, 2000).unref();
|
|
499
|
+
}, 1500);
|
|
500
|
+
completionTimer.unref();
|
|
501
|
+
}
|
|
502
|
+
|
|
457
503
|
function writeAudit(status, extra = []) {
|
|
458
504
|
try {
|
|
459
505
|
writeFileSync(auditPath, [
|
|
@@ -488,9 +534,18 @@ function rollbackLock() {
|
|
|
488
534
|
|
|
489
535
|
function flushLogText(text, force = false) {
|
|
490
536
|
pendingLogText += text;
|
|
491
|
-
const parts = pendingLogText.split(
|
|
537
|
+
const parts = pendingLogText.split(/(?:\r?\n|\r)/);
|
|
492
538
|
pendingLogText = force ? '' : (parts.pop() ?? '');
|
|
493
|
-
|
|
539
|
+
|
|
540
|
+
const kept = [];
|
|
541
|
+
for (const rawLine of parts) {
|
|
542
|
+
const classified = classifyLogLine(rawLine);
|
|
543
|
+
if (classified.keep) kept.push(rawLine);
|
|
544
|
+
if (classified.completion) {
|
|
545
|
+
scheduleCompletion(classified.completion, classified.completionReason);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
494
549
|
if (kept.length > 0) appendFileSync(logPath, kept.join('\n') + '\n');
|
|
495
550
|
}
|
|
496
551
|
|
|
@@ -504,6 +559,8 @@ function appendLog(chunk) {
|
|
|
504
559
|
function finalize(status, code, signal, completionReason) {
|
|
505
560
|
if (finalized) return;
|
|
506
561
|
finalized = true;
|
|
562
|
+
if (completionTimer) clearTimeout(completionTimer);
|
|
563
|
+
if (hardTimeout) clearTimeout(hardTimeout);
|
|
507
564
|
flushLogText('', true);
|
|
508
565
|
const beforeBrokenRefs = listBrokenMemoryRefs(memoryDir);
|
|
509
566
|
const prunedRefs = beforeBrokenRefs.length > 0 ? pruneBrokenMemoryRefs(memoryDir) : [];
|
|
@@ -539,7 +596,9 @@ const bundledPaths = Array.from(
|
|
|
539
596
|
new Set(
|
|
540
597
|
[process.env.GSD_BUNDLED_EXTENSION_PATHS, process.env.LSD_BUNDLED_EXTENSION_PATHS]
|
|
541
598
|
.filter(Boolean)
|
|
542
|
-
.flatMap((value) => String(value).split(delimiter).map((entry) => entry.trim()).filter(Boolean))
|
|
599
|
+
.flatMap((value) => String(value).split(delimiter).map((entry) => entry.trim()).filter(Boolean))
|
|
600
|
+
// Explicitly disable cache-timer extension for dream workers.
|
|
601
|
+
.filter((entry) => !/[\\/]cache-timer[\\/]/i.test(entry)),
|
|
543
602
|
),
|
|
544
603
|
);
|
|
545
604
|
for (const extensionPath of bundledPaths) childArgs.push('--extension', extensionPath);
|
|
@@ -548,12 +607,20 @@ childArgs.push('--bare', '--context', tmpPromptPath, '--context-text', instructi
|
|
|
548
607
|
|
|
549
608
|
const child = spawn(process.execPath, childArgs, {
|
|
550
609
|
cwd,
|
|
551
|
-
env: {
|
|
610
|
+
env: {
|
|
611
|
+
...process.env,
|
|
612
|
+
LSD_MEMORY_DREAM: '1',
|
|
613
|
+
// Hard-disable cache timer in maintenance workers.
|
|
614
|
+
LSD_DISABLE_CACHE_TIMER: '1',
|
|
615
|
+
// Dream workers run headless and cannot answer auto-mode classifier prompts.
|
|
616
|
+
// Force non-auto permissions and rely on memory-extension path/tool guards.
|
|
617
|
+
LUCENT_CODE_PERMISSION_MODE: 'danger-full-access',
|
|
618
|
+
},
|
|
552
619
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
553
620
|
});
|
|
554
621
|
|
|
555
|
-
|
|
556
|
-
finalize('failed', null, 'timeout', 'timeout');
|
|
622
|
+
hardTimeout = setTimeout(() => {
|
|
623
|
+
finalize('failed', null, 'timeout', completionState?.completionReason ?? 'timeout');
|
|
557
624
|
try { child.kill('SIGTERM'); } catch {}
|
|
558
625
|
setTimeout(() => { try { child.kill('SIGKILL'); } catch {} }, 2000).unref();
|
|
559
626
|
}, 180000);
|
|
@@ -563,12 +630,12 @@ child.stdout.on('data', appendLog);
|
|
|
563
630
|
child.stderr.on('data', appendLog);
|
|
564
631
|
child.on('error', (err) => {
|
|
565
632
|
appendLog(String(err && err.stack ? err.stack : err) + '\n');
|
|
566
|
-
|
|
633
|
+
flushLogText('', true);
|
|
567
634
|
finalize('failed', null, 'spawn_error', String(err && err.message ? err.message : err));
|
|
568
635
|
});
|
|
569
636
|
child.on('exit', (code, signal) => {
|
|
570
|
-
|
|
571
|
-
finalize(code === 0 ? 'finished' : 'failed', code, signal, 'child_exit');
|
|
637
|
+
flushLogText('', true);
|
|
638
|
+
finalize(code === 0 ? 'finished' : 'failed', code, signal, completionState?.completionReason ?? 'child_exit');
|
|
572
639
|
});
|
|
573
640
|
`;
|
|
574
641
|
|
|
@@ -1,158 +1,214 @@
|
|
|
1
1
|
import test, { describe } from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
+
import vm from 'node:vm';
|
|
3
4
|
import { mkdirSync, rmSync } from 'node:fs';
|
|
4
5
|
import { join } from 'node:path';
|
|
5
6
|
import { tmpdir } from 'node:os';
|
|
6
|
-
import { buildTranscriptSummary, buildExtractionPrompt, stripAnsiForAutoExtractLog } from '../auto-extract.js';
|
|
7
|
+
import { buildTranscriptSummary, buildExtractionPrompt, stripAnsiForAutoExtractLog, classifyAutoExtractLogLine, buildAutoExtractHelperScript } from '../auto-extract.js';
|
|
7
8
|
|
|
8
9
|
function makeTempDir(): string {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const dir = join(tmpdir(), `mem-extract-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
11
|
+
mkdirSync(dir, { recursive: true });
|
|
12
|
+
return dir;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
describe('buildTranscriptSummary', () => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
16
|
+
test('returns transcript even for a short conversation when there is user-authored content', () => {
|
|
17
|
+
const entries = [
|
|
18
|
+
{ type: 'message', message: { role: 'user', content: 'hello' } },
|
|
19
|
+
{ type: 'message', message: { role: 'assistant', content: 'hi' } },
|
|
20
|
+
];
|
|
21
|
+
const summary = buildTranscriptSummary(entries);
|
|
22
|
+
assert.ok(summary.includes('User: hello'));
|
|
23
|
+
assert.ok(summary.includes('Assistant: hi'));
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('returns empty string for empty array', () => {
|
|
27
|
+
assert.equal(buildTranscriptSummary([]), '');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('returns empty string when there is no user-authored content', () => {
|
|
31
|
+
const entries = [
|
|
32
|
+
{ type: 'message', message: { role: 'assistant', content: 'hi' } },
|
|
33
|
+
];
|
|
34
|
+
assert.equal(buildTranscriptSummary(entries), '');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('extracts user and assistant text messages', () => {
|
|
38
|
+
const entries = [
|
|
39
|
+
{ type: 'message', message: { role: 'user', content: 'hello' } },
|
|
40
|
+
{ type: 'message', message: { role: 'assistant', content: 'hi there' } },
|
|
41
|
+
{ type: 'message', message: { role: 'user', content: 'how are you' } },
|
|
42
|
+
];
|
|
43
|
+
const summary = buildTranscriptSummary(entries);
|
|
44
|
+
assert.ok(summary.includes('User: hello'));
|
|
45
|
+
assert.ok(summary.includes('Assistant: hi there'));
|
|
46
|
+
assert.ok(summary.includes('User: how are you'));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('skips entries where type !== message', () => {
|
|
50
|
+
const entries = [
|
|
51
|
+
{ type: 'other', data: 'ignored' },
|
|
52
|
+
{ type: 'message', message: { role: 'user', content: 'hello' } },
|
|
53
|
+
{ type: 'tool_result', id: '1', content: 'ignored' },
|
|
54
|
+
{ type: 'message', message: { role: 'assistant', content: 'hi' } },
|
|
55
|
+
{ type: 'message', message: { role: 'user', content: 'test' } },
|
|
56
|
+
];
|
|
57
|
+
const summary = buildTranscriptSummary(entries);
|
|
58
|
+
assert.ok(summary.includes('User: hello'));
|
|
59
|
+
assert.ok(summary.includes('Assistant: hi'));
|
|
60
|
+
assert.ok(summary.includes('User: test'));
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('handles array content (multi-part), includes only text', () => {
|
|
64
|
+
const entries = [
|
|
65
|
+
{ type: 'message', message: { role: 'user', content: [{ type: 'text', text: 'hello' }, { type: 'tool_use', id: '1', name: 'bash', input: {} }] } },
|
|
66
|
+
{ type: 'message', message: { role: 'assistant', content: 'response' } },
|
|
67
|
+
{ type: 'message', message: { role: 'user', content: 'end' } },
|
|
68
|
+
];
|
|
69
|
+
const summary = buildTranscriptSummary(entries);
|
|
70
|
+
assert.ok(summary.includes('User: hello'));
|
|
71
|
+
assert.ok(!summary.includes('tool_use'));
|
|
72
|
+
assert.ok(summary.includes('Assistant: response'));
|
|
73
|
+
assert.ok(summary.includes('User: end'));
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('truncates messages over 2000 chars', () => {
|
|
77
|
+
const longText = 'a'.repeat(3000);
|
|
78
|
+
const entries = [
|
|
79
|
+
{ type: 'message', message: { role: 'user', content: 'start' } },
|
|
80
|
+
{ type: 'message', message: { role: 'assistant', content: longText } },
|
|
81
|
+
{ type: 'message', message: { role: 'user', content: 'end' } },
|
|
82
|
+
];
|
|
83
|
+
const summary = buildTranscriptSummary(entries);
|
|
84
|
+
assert.ok(summary.includes('…'));
|
|
85
|
+
assert.ok(summary.length < 4000); // Should be truncated
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('labels user messages with User: and assistant with Assistant:', () => {
|
|
89
|
+
const entries = [
|
|
90
|
+
{ type: 'message', message: { role: 'user', content: 'question' } },
|
|
91
|
+
{ type: 'message', message: { role: 'assistant', content: 'answer' } },
|
|
92
|
+
{ type: 'message', message: { role: 'user', content: 'thanks' } },
|
|
93
|
+
];
|
|
94
|
+
const summary = buildTranscriptSummary(entries);
|
|
95
|
+
assert.ok(summary.includes('User:'));
|
|
96
|
+
assert.ok(summary.includes('Assistant:'));
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('buildAutoExtractHelperScript', () => {
|
|
101
|
+
test('preserves regex escapes in the generated helper source', () => {
|
|
102
|
+
const script = buildAutoExtractHelperScript();
|
|
103
|
+
|
|
104
|
+
assert.ok(script.includes(String.raw`const CACHE_TIMER_RE = /^\[phase\]\s+cache-timer(?:\s*:\s*.*)?\s*$/i;`));
|
|
105
|
+
assert.ok(script.includes(String.raw`const SESSION_ENDED_RE = /^\[agent\]\s+Session ended/;`));
|
|
106
|
+
assert.ok(script.includes(String.raw`const HEADLESS_STATUS_RE = /^\[headless\]\s+Status:\s+(\w+)\s*$/i;`));
|
|
107
|
+
assert.ok(script.includes(String.raw`const parts = pendingLogText.split(/\r?\n/);`));
|
|
108
|
+
assert.ok(script.includes(`appendFileSync(logPath, kept.join('\\n') + '\\n')`));
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('generates syntactically valid helper code', () => {
|
|
112
|
+
const script = buildAutoExtractHelperScript();
|
|
113
|
+
assert.doesNotThrow(() => new vm.Script(script));
|
|
114
|
+
});
|
|
97
115
|
});
|
|
98
116
|
|
|
99
117
|
describe('buildExtractionPrompt', () => {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
118
|
+
test('strips ANSI codes so session-end and cache-timer lines can be classified', () => {
|
|
119
|
+
const coloredSessionEnd = '\u001b[36m[agent] Session ended\u001b[0m';
|
|
120
|
+
const coloredCacheTimer = '\u001b[36m[phase] cache-timer\u001b[0m';
|
|
121
|
+
|
|
122
|
+
assert.equal(stripAnsiForAutoExtractLog(coloredSessionEnd), '[agent] Session ended');
|
|
123
|
+
assert.equal(stripAnsiForAutoExtractLog(coloredCacheTimer), '[phase] cache-timer');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('classifies cache-timer lines as ignorable noise', () => {
|
|
127
|
+
assert.deepEqual(classifyAutoExtractLogLine('[phase] cache-timer'), {
|
|
128
|
+
stripped: '[phase] cache-timer',
|
|
129
|
+
keep: false,
|
|
130
|
+
completion: 'none',
|
|
131
|
+
completionReason: null,
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('classifies cache-timer lines with rendered values as ignorable noise', () => {
|
|
136
|
+
assert.deepEqual(classifyAutoExtractLogLine('[phase] cache-timer: ⏱ 0:05'), {
|
|
137
|
+
stripped: '[phase] cache-timer: ⏱ 0:05',
|
|
138
|
+
keep: false,
|
|
139
|
+
completion: 'none',
|
|
140
|
+
completionReason: null,
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('classifies headless completion as a successful terminal signal', () => {
|
|
145
|
+
assert.deepEqual(classifyAutoExtractLogLine('[headless] Status: complete'), {
|
|
146
|
+
stripped: '[headless] Status: complete',
|
|
147
|
+
keep: true,
|
|
148
|
+
completion: 'success',
|
|
149
|
+
completionReason: 'headless_status_complete',
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test('classifies non-complete headless statuses as failures', () => {
|
|
154
|
+
assert.deepEqual(classifyAutoExtractLogLine('[headless] Status: timeout'), {
|
|
155
|
+
stripped: '[headless] Status: timeout',
|
|
156
|
+
keep: true,
|
|
157
|
+
completion: 'failure',
|
|
158
|
+
completionReason: 'headless_status_timeout',
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('contains the memory directory path in output', () => {
|
|
163
|
+
const memoryDir = '/tmp/test-memory';
|
|
164
|
+
const entries = [
|
|
165
|
+
{ type: 'message', message: { role: 'user', content: 'test1' } },
|
|
166
|
+
{ type: 'message', message: { role: 'assistant', content: 'test2' } },
|
|
167
|
+
{ type: 'message', message: { role: 'user', content: 'test3' } },
|
|
168
|
+
];
|
|
169
|
+
const prompt = buildExtractionPrompt(memoryDir, buildTranscriptSummary(entries));
|
|
170
|
+
assert.ok(prompt.includes(memoryDir));
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('contains the transcript in output', () => {
|
|
174
|
+
const memoryDir = '/tmp/test-memory';
|
|
175
|
+
const entries = [
|
|
176
|
+
{ type: 'message', message: { role: 'user', content: 'hello world' } },
|
|
177
|
+
{ type: 'message', message: { role: 'assistant', content: 'response' } },
|
|
178
|
+
{ type: 'message', message: { role: 'user', content: 'end' } },
|
|
179
|
+
];
|
|
180
|
+
const prompt = buildExtractionPrompt(memoryDir, buildTranscriptSummary(entries));
|
|
181
|
+
assert.ok(prompt.includes('User: hello world'));
|
|
182
|
+
assert.ok(prompt.includes('Assistant: response'));
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test('contains "None yet" when memory dir is empty', () => {
|
|
186
|
+
const memoryDir = makeTempDir();
|
|
187
|
+
const cleanup = () => rmSync(memoryDir, { recursive: true, force: true });
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
const entries = [
|
|
191
|
+
{ type: 'message', message: { role: 'user', content: 'test1' } },
|
|
192
|
+
{ type: 'message', message: { role: 'assistant', content: 'test2' } },
|
|
193
|
+
{ type: 'message', message: { role: 'user', content: 'test3' } },
|
|
194
|
+
];
|
|
195
|
+
const prompt = buildExtractionPrompt(memoryDir, buildTranscriptSummary(entries));
|
|
196
|
+
assert.ok(prompt.includes('None yet'));
|
|
197
|
+
} finally {
|
|
198
|
+
cleanup();
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test('contains extraction rules (Save ONLY)', () => {
|
|
203
|
+
const memoryDir = '/tmp/test-memory';
|
|
204
|
+
const entries = [
|
|
205
|
+
{ type: 'message', message: { role: 'user', content: 'test1' } },
|
|
206
|
+
{ type: 'message', message: { role: 'assistant', content: 'test2' } },
|
|
207
|
+
{ type: 'message', message: { role: 'user', content: 'test3' } },
|
|
208
|
+
];
|
|
209
|
+
const prompt = buildExtractionPrompt(memoryDir, buildTranscriptSummary(entries));
|
|
210
|
+
assert.ok(prompt.includes('Save ONLY'));
|
|
211
|
+
assert.ok(prompt.includes('raw code snippets'));
|
|
212
|
+
assert.ok(!prompt.includes('Do NOT save: code patterns, architecture'));
|
|
213
|
+
});
|
|
158
214
|
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process'
|
|
2
|
+
import { existsSync } from 'node:fs'
|
|
3
|
+
import { homedir } from 'node:os'
|
|
4
|
+
import { delimiter, join } from 'node:path'
|
|
5
|
+
|
|
6
|
+
const GSD_RTK_PATH_ENV = 'GSD_RTK_PATH'
|
|
7
|
+
const GSD_RTK_DISABLED_ENV = 'GSD_RTK_DISABLED'
|
|
8
|
+
const GSD_RTK_REWRITE_TIMEOUT_MS_ENV = 'GSD_RTK_REWRITE_TIMEOUT_MS'
|
|
9
|
+
const RTK_TELEMETRY_DISABLED_ENV = 'RTK_TELEMETRY_DISABLED'
|
|
10
|
+
const RTK_REWRITE_TIMEOUT_MS = 5_000
|
|
11
|
+
|
|
12
|
+
function isTruthy(value) {
|
|
13
|
+
if (!value) return false
|
|
14
|
+
const normalized = value.trim().toLowerCase()
|
|
15
|
+
return normalized === '1' || normalized === 'true' || normalized === 'yes'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getRewriteTimeoutMs(env = process.env) {
|
|
19
|
+
const configured = Number.parseInt(env[GSD_RTK_REWRITE_TIMEOUT_MS_ENV] ?? '', 10)
|
|
20
|
+
if (Number.isFinite(configured) && configured > 0) return configured
|
|
21
|
+
return RTK_REWRITE_TIMEOUT_MS
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isRtkEnabled(env = process.env) {
|
|
25
|
+
return !isTruthy(env[GSD_RTK_DISABLED_ENV])
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function buildRtkEnv(env = process.env) {
|
|
29
|
+
return {
|
|
30
|
+
...env,
|
|
31
|
+
[RTK_TELEMETRY_DISABLED_ENV]: '1',
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getManagedRtkDir(env = process.env) {
|
|
36
|
+
return join(env.GSD_HOME || join(homedir(), '.lsd'), 'agent', 'bin')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getRtkBinaryName(platform = process.platform) {
|
|
40
|
+
return platform === 'win32' ? 'rtk.exe' : 'rtk'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getPathValue(env) {
|
|
44
|
+
const pathKey = Object.keys(env).find((key) => key.toLowerCase() === 'path')
|
|
45
|
+
return pathKey ? env[pathKey] : env.PATH
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function resolvePathCandidates(pathValue) {
|
|
49
|
+
if (!pathValue) return []
|
|
50
|
+
return pathValue
|
|
51
|
+
.split(delimiter)
|
|
52
|
+
.map((part) => part.trim())
|
|
53
|
+
.filter(Boolean)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function resolveSystemRtkPath(pathValue, platform = process.platform) {
|
|
57
|
+
const candidates = platform === 'win32'
|
|
58
|
+
? ['rtk.exe', 'rtk.cmd', 'rtk.bat', 'rtk']
|
|
59
|
+
: ['rtk']
|
|
60
|
+
|
|
61
|
+
for (const dir of resolvePathCandidates(pathValue)) {
|
|
62
|
+
for (const candidate of candidates) {
|
|
63
|
+
const fullPath = join(dir, candidate)
|
|
64
|
+
if (existsSync(fullPath)) return fullPath
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return null
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function resolveRtkBinaryPath(options = {}) {
|
|
72
|
+
const env = options.env ?? process.env
|
|
73
|
+
const platform = options.platform ?? process.platform
|
|
74
|
+
|
|
75
|
+
const explicitPath = options.binaryPath ?? env[GSD_RTK_PATH_ENV]
|
|
76
|
+
if (explicitPath && existsSync(explicitPath)) return explicitPath
|
|
77
|
+
|
|
78
|
+
const managedDir = getManagedRtkDir(env)
|
|
79
|
+
const managedPath = join(managedDir, getRtkBinaryName(platform))
|
|
80
|
+
if (existsSync(managedPath)) return managedPath
|
|
81
|
+
if (platform === 'win32') {
|
|
82
|
+
const managedCmd = join(managedDir, 'rtk.cmd')
|
|
83
|
+
if (existsSync(managedCmd)) return managedCmd
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return resolveSystemRtkPath(options.pathValue ?? getPathValue(env), platform)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function rewriteCommandWithRtk(command, options = {}) {
|
|
90
|
+
const env = options.env ?? process.env
|
|
91
|
+
|
|
92
|
+
if (!command.trim()) return command
|
|
93
|
+
if (!isRtkEnabled(env)) return command
|
|
94
|
+
|
|
95
|
+
const binaryPath = options.binaryPath ?? resolveRtkBinaryPath({ env })
|
|
96
|
+
if (!binaryPath) return command
|
|
97
|
+
|
|
98
|
+
const run = options.spawnSyncImpl ?? spawnSync
|
|
99
|
+
const result = run(binaryPath, ['rewrite', command], {
|
|
100
|
+
encoding: 'utf-8',
|
|
101
|
+
env: buildRtkEnv(env),
|
|
102
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
103
|
+
timeout: getRewriteTimeoutMs(env),
|
|
104
|
+
shell: /\.(cmd|bat)$/i.test(binaryPath),
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
if (result.error) return command
|
|
108
|
+
if (result.status !== 0 && result.status !== 3) return command
|
|
109
|
+
|
|
110
|
+
const rewritten = (result.stdout ?? '').trimEnd()
|
|
111
|
+
return rewritten || command
|
|
112
|
+
}
|