@lumpcode/core 0.0.11 → 0.0.13
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/helpers/executeStepsForContextList/main.d.ts +1 -1
- package/dist/helpers/getCodeBasePaths/main.d.ts +2 -2
- package/dist/helpers/getCodeBasePaths/main.d.ts.map +1 -1
- package/dist/helpers/getToDoContextList/main.d.ts +2 -2
- package/dist/helpers/getToDoContextList/main.d.ts.map +1 -1
- package/dist/index.cjs +151 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +151 -9
- package/dist/index.js.map +1 -1
- package/dist/utils/historyFile/main.d.ts.map +1 -1
- package/dist/utils/resolveSpawnExecutable/index.d.ts +1 -0
- package/dist/utils/resolveSpawnExecutable/index.d.ts.map +1 -1
- package/dist/utils/resolveSpawnExecutable/main.d.ts +4 -0
- package/dist/utils/resolveSpawnExecutable/main.d.ts.map +1 -1
- package/dist/utils/resolveSpawnExecutable/npmCmdShimFixture.d.ts +6 -0
- package/dist/utils/resolveSpawnExecutable/npmCmdShimFixture.d.ts.map +1 -0
- package/dist/utils/resolveSpawnExecutable/windowsNpmCmdShimBody.d.ts +6 -0
- package/dist/utils/resolveSpawnExecutable/windowsNpmCmdShimBody.d.ts.map +1 -0
- package/dist/utils/resolveSpawnExecutable/windowsNpmCmdShimFixture.d.ts +6 -0
- package/dist/utils/resolveSpawnExecutable/windowsNpmCmdShimFixture.d.ts.map +1 -0
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -65,6 +65,10 @@ const DEFAULT_WINDOWS_PATHEXT = [
|
|
|
65
65
|
'.WSH',
|
|
66
66
|
'.MSC',
|
|
67
67
|
];
|
|
68
|
+
/** Matches a quoted script path using %dp0% / %~dp0 relative to a Windows .cmd shim. */
|
|
69
|
+
const WINDOWS_DP0_SCRIPT_RE = /"(?:%dp0%|%~dp0)\\?((?:[^"\r\n])+?\.(?:js|mjs|cjs))"/i;
|
|
70
|
+
/** Absolute Windows path to a Node script in quotes. */
|
|
71
|
+
const WINDOWS_ABS_SCRIPT_RE = /"([A-Za-z]:\\[^"\r\n]+\.(?:js|mjs|cjs))"/i;
|
|
68
72
|
function windowsPathext() {
|
|
69
73
|
const fromEnv = process.env.PATHEXT?.split(';').map((entry) => entry.trim()).filter(Boolean);
|
|
70
74
|
return fromEnv?.length ? fromEnv : DEFAULT_WINDOWS_PATHEXT;
|
|
@@ -130,9 +134,83 @@ function wrapWindowsCmdShim(resolvedPath, args) {
|
|
|
130
134
|
args: ['/d', '/s', '/c', resolvedPath, ...args],
|
|
131
135
|
};
|
|
132
136
|
}
|
|
137
|
+
function resolveWindowsJsPathFromShim(shimDir, relativeOrAbsolute) {
|
|
138
|
+
if (/^[A-Za-z]:[\\/]/.test(relativeOrAbsolute)) {
|
|
139
|
+
return path.resolve(relativeOrAbsolute);
|
|
140
|
+
}
|
|
141
|
+
// Normalize Windows separators so stubbed-win32 tests on POSIX still resolve.
|
|
142
|
+
const parts = relativeOrAbsolute.replace(/^[\\/]+/, '').split(/[/\\]+/).filter(Boolean);
|
|
143
|
+
return path.resolve(shimDir, ...parts);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Prefer a real Node binary — never a .cmd/.bat shim (those reintroduce cmd.exe),
|
|
147
|
+
* and never the host SEA/`lumpcode` binary via process.execPath.
|
|
148
|
+
* Returns bare `"node"` as last resort so spawn fails with ENOENT instead of
|
|
149
|
+
* silently falling back to cmd.exe after a Node entry was identified.
|
|
150
|
+
*/
|
|
151
|
+
function resolveNodeExecutable(preferredDir) {
|
|
152
|
+
if (preferredDir != null) {
|
|
153
|
+
const bundledNode = path.join(preferredDir, 'node.exe');
|
|
154
|
+
if (fileExists(bundledNode)) {
|
|
155
|
+
return bundledNode;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
for (const name of ['node.exe', 'node']) {
|
|
159
|
+
const found = resolveOnPath(name);
|
|
160
|
+
if (!found)
|
|
161
|
+
continue;
|
|
162
|
+
const ext = path.extname(found).toLowerCase();
|
|
163
|
+
if (ext === '.cmd' || ext === '.bat')
|
|
164
|
+
continue;
|
|
165
|
+
return found;
|
|
166
|
+
}
|
|
167
|
+
const base = path.basename(process.execPath).toLowerCase();
|
|
168
|
+
if (base === 'node' || base === 'node.exe') {
|
|
169
|
+
return process.execPath;
|
|
170
|
+
}
|
|
171
|
+
return 'node';
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Parse an npm/yarn-style Windows `.cmd` shim to `node <script.js>`.
|
|
175
|
+
* Returns null only when the file is not a recognizable Node cmd-shim
|
|
176
|
+
* (caller may wrap with cmd.exe). Once a script path is found, never falls
|
|
177
|
+
* back to cmd.exe — missing Node uses bare `"node"` for a clear spawn failure.
|
|
178
|
+
*/
|
|
179
|
+
function tryUnwrapWindowsNpmCmdShim(cmdPath) {
|
|
180
|
+
let body;
|
|
181
|
+
try {
|
|
182
|
+
body = fs.readFileSync(cmdPath, 'utf8');
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
const shimDir = path.dirname(cmdPath);
|
|
188
|
+
const lines = body.split(/\r?\n/);
|
|
189
|
+
// Windows npm-cmd-shim ends with `%*` pass-through; prefer that line for the script path.
|
|
190
|
+
const passThroughLine = [...lines].reverse().find((line) => /%\*\s*$/.test(line));
|
|
191
|
+
const searchOrder = passThroughLine != null ? [passThroughLine, body] : [body];
|
|
192
|
+
for (const searchIn of searchOrder) {
|
|
193
|
+
const dp0Match = searchIn.match(WINDOWS_DP0_SCRIPT_RE);
|
|
194
|
+
if (dp0Match?.[1]) {
|
|
195
|
+
const candidate = resolveWindowsJsPathFromShim(shimDir, dp0Match[1]);
|
|
196
|
+
if (fileExists(candidate)) {
|
|
197
|
+
return { scriptPath: candidate };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const absMatch = searchIn.match(WINDOWS_ABS_SCRIPT_RE);
|
|
201
|
+
if (absMatch?.[1] && fileExists(absMatch[1])) {
|
|
202
|
+
return { scriptPath: absMatch[1] };
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
133
207
|
/**
|
|
134
208
|
* Resolves bare executable names on Windows so npm-style `.cmd` shims and
|
|
135
209
|
* extensionless Node entrypoints work with `child_process.spawn` (no shell).
|
|
210
|
+
*
|
|
211
|
+
* For Node agent CLIs installed via npm (copilot.cmd, cursor-agent.cmd, …),
|
|
212
|
+
* unwraps the shim to `node <entry.js> …args` so prompt argv is not mangled by
|
|
213
|
+
* cmd.exe / `%*`. Unrecognized `.cmd`/`.bat` files still wrap through cmd.exe.
|
|
136
214
|
*/
|
|
137
215
|
function resolveSpawnExecutable(executable, args) {
|
|
138
216
|
if (process.platform !== 'win32') {
|
|
@@ -153,17 +231,52 @@ function resolveSpawnExecutable(executable, args) {
|
|
|
153
231
|
}
|
|
154
232
|
const ext = path.extname(resolved).toLowerCase();
|
|
155
233
|
if (ext === '.cmd' || ext === '.bat') {
|
|
234
|
+
const unwrapped = tryUnwrapWindowsNpmCmdShim(resolved);
|
|
235
|
+
if (unwrapped != null) {
|
|
236
|
+
return {
|
|
237
|
+
executable: resolveNodeExecutable(path.dirname(resolved)),
|
|
238
|
+
args: [unwrapped.scriptPath, ...args],
|
|
239
|
+
};
|
|
240
|
+
}
|
|
156
241
|
return wrapWindowsCmdShim(resolved, args);
|
|
157
242
|
}
|
|
158
243
|
if (isNodeScript(resolved)) {
|
|
159
244
|
return {
|
|
160
|
-
executable:
|
|
245
|
+
executable: resolveNodeExecutable(path.dirname(resolved)),
|
|
161
246
|
args: [resolved, ...args],
|
|
162
247
|
};
|
|
163
248
|
}
|
|
164
249
|
return { executable: resolved, args };
|
|
165
250
|
}
|
|
166
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Minimal Windows npm-cmd-shim `.cmd` body (modern `"%_prog%"` + `"%dp0%\\…\\.js" %*` form).
|
|
254
|
+
* Shared by core spawn tests and CLI e2e path-agent install — do not duplicate.
|
|
255
|
+
*/
|
|
256
|
+
function windowsNpmCmdShimBody(relativeJsFromShimDir) {
|
|
257
|
+
const target = relativeJsFromShimDir.replace(/\//g, '\\');
|
|
258
|
+
return [
|
|
259
|
+
'@ECHO off',
|
|
260
|
+
'GOTO start',
|
|
261
|
+
':find_dp0',
|
|
262
|
+
'SET dp0=%~dp0',
|
|
263
|
+
'EXIT /b',
|
|
264
|
+
':start',
|
|
265
|
+
'SETLOCAL',
|
|
266
|
+
'CALL :find_dp0',
|
|
267
|
+
'',
|
|
268
|
+
'IF EXIST "%dp0%\\node.exe" (',
|
|
269
|
+
' SET "_prog=%dp0%\\node.exe"',
|
|
270
|
+
') ELSE (',
|
|
271
|
+
' SET "_prog=node"',
|
|
272
|
+
' SET PATHEXT=%PATHEXT:;.JS;=;%',
|
|
273
|
+
')',
|
|
274
|
+
'',
|
|
275
|
+
`endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & set PATHEXT=%PATHEXT:;.JS;=;% & "%_prog%" "%dp0%\\${target}" %*`,
|
|
276
|
+
'',
|
|
277
|
+
].join('\r\n');
|
|
278
|
+
}
|
|
279
|
+
|
|
167
280
|
function formatMessage(prefix, message) {
|
|
168
281
|
if (!prefix)
|
|
169
282
|
return message;
|
|
@@ -3144,6 +3257,38 @@ async function pathExists(filePath) {
|
|
|
3144
3257
|
}
|
|
3145
3258
|
}
|
|
3146
3259
|
|
|
3260
|
+
/** Strip terminal ANSI escapes and other C0 controls js-yaml rejects in literal blocks. */
|
|
3261
|
+
function sanitizeHistoryText(value) {
|
|
3262
|
+
return value
|
|
3263
|
+
.replace(/\x1b\[[0-9;]*[A-Za-z]/g, '')
|
|
3264
|
+
.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, '');
|
|
3265
|
+
}
|
|
3266
|
+
function parseHistoryYamlContent(content) {
|
|
3267
|
+
const parsed = load(content, { schema: YAML11_SCHEMA });
|
|
3268
|
+
if (!Array.isArray(parsed)) {
|
|
3269
|
+
throw new Error('History file must contain a YAML sequence');
|
|
3270
|
+
}
|
|
3271
|
+
return parsed;
|
|
3272
|
+
}
|
|
3273
|
+
function tryParseHistoryContent(content) {
|
|
3274
|
+
try {
|
|
3275
|
+
return parseHistoryYamlContent(content);
|
|
3276
|
+
}
|
|
3277
|
+
catch (firstError) {
|
|
3278
|
+
const sanitized = sanitizeHistoryText(content);
|
|
3279
|
+
if (sanitized === content) {
|
|
3280
|
+
throw firstError;
|
|
3281
|
+
}
|
|
3282
|
+
return parseHistoryYamlContent(sanitized);
|
|
3283
|
+
}
|
|
3284
|
+
}
|
|
3285
|
+
function sanitizeHistoryEntry(entry) {
|
|
3286
|
+
return {
|
|
3287
|
+
...entry,
|
|
3288
|
+
prompt: sanitizeHistoryText(entry.prompt),
|
|
3289
|
+
commandResult: sanitizeHistoryText(entry.commandResult),
|
|
3290
|
+
};
|
|
3291
|
+
}
|
|
3147
3292
|
function historyFormatFromPath(filePath) {
|
|
3148
3293
|
const ext = extname(filePath).toLowerCase();
|
|
3149
3294
|
if (ext === '.yaml' || ext === '.yml') {
|
|
@@ -3181,7 +3326,8 @@ function dumpHistoryEntries(entries) {
|
|
|
3181
3326
|
if (entries.length === 0) {
|
|
3182
3327
|
return '[]\n';
|
|
3183
3328
|
}
|
|
3184
|
-
|
|
3329
|
+
const sanitizedEntries = entries.map(sanitizeHistoryEntry);
|
|
3330
|
+
return dump(sanitizedEntries, {
|
|
3185
3331
|
schema: YAML11_SCHEMA,
|
|
3186
3332
|
lineWidth: 0,
|
|
3187
3333
|
noRefs: true,
|
|
@@ -3199,11 +3345,7 @@ async function readHistoryFile({ filePath, }) {
|
|
|
3199
3345
|
}
|
|
3200
3346
|
try {
|
|
3201
3347
|
const content = await readFile(filePath, 'utf-8');
|
|
3202
|
-
|
|
3203
|
-
if (!Array.isArray(parsed)) {
|
|
3204
|
-
return failure(`History file ${filePath} must contain a YAML sequence`);
|
|
3205
|
-
}
|
|
3206
|
-
return success(parsed);
|
|
3348
|
+
return success(tryParseHistoryContent(content));
|
|
3207
3349
|
}
|
|
3208
3350
|
catch (error) {
|
|
3209
3351
|
const detail = error instanceof Error ? error.message : String(error);
|
|
@@ -3243,7 +3385,7 @@ async function appendHistoryEntry({ filePath, entry, }) {
|
|
|
3243
3385
|
await mkdir(dirname(filePath), { recursive: true });
|
|
3244
3386
|
entries = [];
|
|
3245
3387
|
}
|
|
3246
|
-
entries.push(entry);
|
|
3388
|
+
entries.push(sanitizeHistoryEntry(entry));
|
|
3247
3389
|
return writeHistoryFile({ filePath, entries });
|
|
3248
3390
|
}
|
|
3249
3391
|
|
|
@@ -3925,5 +4067,5 @@ async function runLump(input) {
|
|
|
3925
4067
|
});
|
|
3926
4068
|
}
|
|
3927
4069
|
|
|
3928
|
-
export { appendHistoryEntry, collectStepsForContext, contextStatus, contextStatusSchema, createConsoleLogger, defaultGitAddCommandFn, defaultGitCommitCommandFn, defaultGitCommitMessageFn, defaultGitPushCommandFn, defaultSetupWorkspaceFn, defaultSetupWorkspaceFnWithWorktree, defaultTeardownWorkspaceFn, defaultTeardownWorkspaceFnWithWorktree, execAsync, execBinary, executeStepsForContextList, failure, formatExecFailureMessage, getCodeBasePaths, getContextStatus, getToDoContextList, historyFormatFromPath, parseGitLogHashSubjectLines, pathExists, readHistoryFile, resolveSpawnExecutable, runLump, set, shellSingleQuote, success, validateContextListNames, writeHistoryFile };
|
|
4070
|
+
export { appendHistoryEntry, collectStepsForContext, contextStatus, contextStatusSchema, createConsoleLogger, defaultGitAddCommandFn, defaultGitCommitCommandFn, defaultGitCommitMessageFn, defaultGitPushCommandFn, defaultSetupWorkspaceFn, defaultSetupWorkspaceFnWithWorktree, defaultTeardownWorkspaceFn, defaultTeardownWorkspaceFnWithWorktree, execAsync, execBinary, executeStepsForContextList, failure, formatExecFailureMessage, getCodeBasePaths, getContextStatus, getToDoContextList, historyFormatFromPath, parseGitLogHashSubjectLines, pathExists, readHistoryFile, resolveSpawnExecutable, runLump, set, shellSingleQuote, success, validateContextListNames, windowsNpmCmdShimBody, writeHistoryFile };
|
|
3929
4071
|
//# sourceMappingURL=index.js.map
|