@lumpcode/core 0.0.12 → 0.0.14

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.
Files changed (48) hide show
  1. package/README.md +2 -2
  2. package/dist/helpers/executeStepsForContextList/main.d.ts +3 -3
  3. package/dist/helpers/executeStepsForContextList/main.d.ts.map +1 -1
  4. package/dist/helpers/getContextStatus/main.d.ts +3 -3
  5. package/dist/helpers/getContextStatus/main.d.ts.map +1 -1
  6. package/dist/helpers/getToDoContextList/main.d.ts +4 -4
  7. package/dist/helpers/getToDoContextList/main.d.ts.map +1 -1
  8. package/dist/index.cjs +123 -7
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.js +123 -7
  11. package/dist/index.js.map +1 -1
  12. package/dist/types/BranchFn.d.ts +2 -2
  13. package/dist/types/BranchFn.d.ts.map +1 -1
  14. package/dist/types/CommandFn.d.ts +3 -3
  15. package/dist/types/CommandFn.d.ts.map +1 -1
  16. package/dist/types/ContextStatus.d.ts +1 -7
  17. package/dist/types/ContextStatus.d.ts.map +1 -1
  18. package/dist/types/GetContextListFn.d.ts +3 -3
  19. package/dist/types/GetContextListFn.d.ts.map +1 -1
  20. package/dist/types/GitCommitMessageFn.d.ts +3 -3
  21. package/dist/types/GitCommitMessageFn.d.ts.map +1 -1
  22. package/dist/types/HistoryEntry.d.ts +3 -3
  23. package/dist/types/HistoryEntry.d.ts.map +1 -1
  24. package/dist/types/PostCommandExecFn.d.ts +5 -4
  25. package/dist/types/PostCommandExecFn.d.ts.map +1 -1
  26. package/dist/types/PromptFn.d.ts +4 -4
  27. package/dist/types/PromptFn.d.ts.map +1 -1
  28. package/dist/types/SetupFn.d.ts +2 -2
  29. package/dist/types/SetupFn.d.ts.map +1 -1
  30. package/dist/types/Step.d.ts +6 -5
  31. package/dist/types/Step.d.ts.map +1 -1
  32. package/dist/types/Steps.d.ts +3 -1
  33. package/dist/types/Steps.d.ts.map +1 -1
  34. package/dist/types/TeardownFn.d.ts +2 -2
  35. package/dist/types/TeardownFn.d.ts.map +1 -1
  36. package/dist/usages/runLump/main.d.ts +9 -9
  37. package/dist/usages/runLump/main.d.ts.map +1 -1
  38. package/dist/utils/resolveSpawnExecutable/index.d.ts +1 -0
  39. package/dist/utils/resolveSpawnExecutable/index.d.ts.map +1 -1
  40. package/dist/utils/resolveSpawnExecutable/main.d.ts +4 -0
  41. package/dist/utils/resolveSpawnExecutable/main.d.ts.map +1 -1
  42. package/dist/utils/resolveSpawnExecutable/npmCmdShimFixture.d.ts +6 -0
  43. package/dist/utils/resolveSpawnExecutable/npmCmdShimFixture.d.ts.map +1 -0
  44. package/dist/utils/resolveSpawnExecutable/windowsNpmCmdShimBody.d.ts +6 -0
  45. package/dist/utils/resolveSpawnExecutable/windowsNpmCmdShimBody.d.ts.map +1 -0
  46. package/dist/utils/resolveSpawnExecutable/windowsNpmCmdShimFixture.d.ts +6 -0
  47. package/dist/utils/resolveSpawnExecutable/windowsNpmCmdShimFixture.d.ts.map +1 -0
  48. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6,7 +6,6 @@ import { extname, dirname } from 'node:path';
6
6
  import * as fs$1 from 'node:fs/promises';
7
7
  import { readFile, mkdir, writeFile } from 'node:fs/promises';
8
8
  import ignore from 'ignore';
9
- import z from 'zod';
10
9
 
11
10
  function set(object, path, value) {
12
11
  const keys = Array.isArray(path) ? path : [path];
@@ -65,6 +64,10 @@ const DEFAULT_WINDOWS_PATHEXT = [
65
64
  '.WSH',
66
65
  '.MSC',
67
66
  ];
67
+ /** Matches a quoted script path using %dp0% / %~dp0 relative to a Windows .cmd shim. */
68
+ const WINDOWS_DP0_SCRIPT_RE = /"(?:%dp0%|%~dp0)\\?((?:[^"\r\n])+?\.(?:js|mjs|cjs))"/i;
69
+ /** Absolute Windows path to a Node script in quotes. */
70
+ const WINDOWS_ABS_SCRIPT_RE = /"([A-Za-z]:\\[^"\r\n]+\.(?:js|mjs|cjs))"/i;
68
71
  function windowsPathext() {
69
72
  const fromEnv = process.env.PATHEXT?.split(';').map((entry) => entry.trim()).filter(Boolean);
70
73
  return fromEnv?.length ? fromEnv : DEFAULT_WINDOWS_PATHEXT;
@@ -130,9 +133,83 @@ function wrapWindowsCmdShim(resolvedPath, args) {
130
133
  args: ['/d', '/s', '/c', resolvedPath, ...args],
131
134
  };
132
135
  }
136
+ function resolveWindowsJsPathFromShim(shimDir, relativeOrAbsolute) {
137
+ if (/^[A-Za-z]:[\\/]/.test(relativeOrAbsolute)) {
138
+ return path.resolve(relativeOrAbsolute);
139
+ }
140
+ // Normalize Windows separators so stubbed-win32 tests on POSIX still resolve.
141
+ const parts = relativeOrAbsolute.replace(/^[\\/]+/, '').split(/[/\\]+/).filter(Boolean);
142
+ return path.resolve(shimDir, ...parts);
143
+ }
144
+ /**
145
+ * Prefer a real Node binary — never a .cmd/.bat shim (those reintroduce cmd.exe),
146
+ * and never the host SEA/`lumpcode` binary via process.execPath.
147
+ * Returns bare `"node"` as last resort so spawn fails with ENOENT instead of
148
+ * silently falling back to cmd.exe after a Node entry was identified.
149
+ */
150
+ function resolveNodeExecutable(preferredDir) {
151
+ if (preferredDir != null) {
152
+ const bundledNode = path.join(preferredDir, 'node.exe');
153
+ if (fileExists(bundledNode)) {
154
+ return bundledNode;
155
+ }
156
+ }
157
+ for (const name of ['node.exe', 'node']) {
158
+ const found = resolveOnPath(name);
159
+ if (!found)
160
+ continue;
161
+ const ext = path.extname(found).toLowerCase();
162
+ if (ext === '.cmd' || ext === '.bat')
163
+ continue;
164
+ return found;
165
+ }
166
+ const base = path.basename(process.execPath).toLowerCase();
167
+ if (base === 'node' || base === 'node.exe') {
168
+ return process.execPath;
169
+ }
170
+ return 'node';
171
+ }
172
+ /**
173
+ * Parse an npm/yarn-style Windows `.cmd` shim to `node <script.js>`.
174
+ * Returns null only when the file is not a recognizable Node cmd-shim
175
+ * (caller may wrap with cmd.exe). Once a script path is found, never falls
176
+ * back to cmd.exe — missing Node uses bare `"node"` for a clear spawn failure.
177
+ */
178
+ function tryUnwrapWindowsNpmCmdShim(cmdPath) {
179
+ let body;
180
+ try {
181
+ body = fs.readFileSync(cmdPath, 'utf8');
182
+ }
183
+ catch {
184
+ return null;
185
+ }
186
+ const shimDir = path.dirname(cmdPath);
187
+ const lines = body.split(/\r?\n/);
188
+ // Windows npm-cmd-shim ends with `%*` pass-through; prefer that line for the script path.
189
+ const passThroughLine = [...lines].reverse().find((line) => /%\*\s*$/.test(line));
190
+ const searchOrder = passThroughLine != null ? [passThroughLine, body] : [body];
191
+ for (const searchIn of searchOrder) {
192
+ const dp0Match = searchIn.match(WINDOWS_DP0_SCRIPT_RE);
193
+ if (dp0Match?.[1]) {
194
+ const candidate = resolveWindowsJsPathFromShim(shimDir, dp0Match[1]);
195
+ if (fileExists(candidate)) {
196
+ return { scriptPath: candidate };
197
+ }
198
+ }
199
+ const absMatch = searchIn.match(WINDOWS_ABS_SCRIPT_RE);
200
+ if (absMatch?.[1] && fileExists(absMatch[1])) {
201
+ return { scriptPath: absMatch[1] };
202
+ }
203
+ }
204
+ return null;
205
+ }
133
206
  /**
134
207
  * Resolves bare executable names on Windows so npm-style `.cmd` shims and
135
208
  * extensionless Node entrypoints work with `child_process.spawn` (no shell).
209
+ *
210
+ * For Node agent CLIs installed via npm (copilot.cmd, cursor-agent.cmd, …),
211
+ * unwraps the shim to `node <entry.js> …args` so prompt argv is not mangled by
212
+ * cmd.exe / `%*`. Unrecognized `.cmd`/`.bat` files still wrap through cmd.exe.
136
213
  */
137
214
  function resolveSpawnExecutable(executable, args) {
138
215
  if (process.platform !== 'win32') {
@@ -153,17 +230,52 @@ function resolveSpawnExecutable(executable, args) {
153
230
  }
154
231
  const ext = path.extname(resolved).toLowerCase();
155
232
  if (ext === '.cmd' || ext === '.bat') {
233
+ const unwrapped = tryUnwrapWindowsNpmCmdShim(resolved);
234
+ if (unwrapped != null) {
235
+ return {
236
+ executable: resolveNodeExecutable(path.dirname(resolved)),
237
+ args: [unwrapped.scriptPath, ...args],
238
+ };
239
+ }
156
240
  return wrapWindowsCmdShim(resolved, args);
157
241
  }
158
242
  if (isNodeScript(resolved)) {
159
243
  return {
160
- executable: process.execPath,
244
+ executable: resolveNodeExecutable(path.dirname(resolved)),
161
245
  args: [resolved, ...args],
162
246
  };
163
247
  }
164
248
  return { executable: resolved, args };
165
249
  }
166
250
 
251
+ /**
252
+ * Minimal Windows npm-cmd-shim `.cmd` body (modern `"%_prog%"` + `"%dp0%\\…\\.js" %*` form).
253
+ * Shared by core spawn tests and CLI e2e path-agent install — do not duplicate.
254
+ */
255
+ function windowsNpmCmdShimBody(relativeJsFromShimDir) {
256
+ const target = relativeJsFromShimDir.replace(/\//g, '\\');
257
+ return [
258
+ '@ECHO off',
259
+ 'GOTO start',
260
+ ':find_dp0',
261
+ 'SET dp0=%~dp0',
262
+ 'EXIT /b',
263
+ ':start',
264
+ 'SETLOCAL',
265
+ 'CALL :find_dp0',
266
+ '',
267
+ 'IF EXIST "%dp0%\\node.exe" (',
268
+ ' SET "_prog=%dp0%\\node.exe"',
269
+ ') ELSE (',
270
+ ' SET "_prog=node"',
271
+ ' SET PATHEXT=%PATHEXT:;.JS;=;%',
272
+ ')',
273
+ '',
274
+ `endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & set PATHEXT=%PATHEXT:;.JS;=;% & "%_prog%" "%dp0%\\${target}" %*`,
275
+ '',
276
+ ].join('\r\n');
277
+ }
278
+
167
279
  function formatMessage(prefix, message) {
168
280
  if (!prefix)
169
281
  return message;
@@ -3591,7 +3703,10 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
3591
3703
  }
3592
3704
  }
3593
3705
  if (postCommandExecFn) {
3594
- await postCommandExecFn(postCommandExecFnInput);
3706
+ const returnedSteps = await postCommandExecFn(postCommandExecFnInput);
3707
+ if (returnedSteps != null && returnedSteps.length > 0) {
3708
+ await walkAndExecuteSteps(returnedSteps, nextCallHeadIndex);
3709
+ }
3595
3710
  }
3596
3711
  }
3597
3712
  }
@@ -3730,7 +3845,8 @@ async function scanDirectory({ dirPath, allPaths, gitignoresMap, projectRoot, lo
3730
3845
  }
3731
3846
 
3732
3847
  async function getContextStatus(params) {
3733
- const { contextName, gitCommitMessageFn, projectRoot, baseBranch, lumpVariables = {}, contextVariables = {}, remoteName = "origin", logger, } = params;
3848
+ const { contextName, gitCommitMessageFn, projectRoot, baseBranch, lumpVariables: lumpVariablesInput, contextVariables = {}, remoteName = "origin", logger, } = params;
3849
+ const lumpVariables = (lumpVariablesInput ?? {});
3734
3850
  const commitMessage = gitCommitMessageFn({
3735
3851
  context: { name: contextName, variables: contextVariables },
3736
3852
  lumpVariables,
@@ -3850,7 +3966,6 @@ async function getToDoContextList(params) {
3850
3966
  }
3851
3967
 
3852
3968
  const contextStatus = ['toDo', 'branchPushed', 'finished'];
3853
- const contextStatusSchema = z.enum(contextStatus);
3854
3969
 
3855
3970
  const defaultGitCommitMessageFn = ({ context }) => {
3856
3971
  return `LUMP:${context.name}`;
@@ -3900,7 +4015,8 @@ const defaultSetupWorkspaceFnWithWorktree = async (input) => {
3900
4015
  };
3901
4016
 
3902
4017
  async function runLump(input) {
3903
- const { baseBranch, branchFn, lumpVariables = {}, getContextListFn, gitAddCommandFn = defaultGitAddCommandFn, gitCommitCommandFn = defaultGitCommitCommandFn, gitCommitMessageFn = defaultGitCommitMessageFn, gitPushCommandFn = defaultGitPushCommandFn, numberOfContextsPerBranch = 1, projectRoot, steps, setupFn = () => ({ contextRunState: {} }), setupWorkspaceFn = defaultSetupWorkspaceFn, teardownFn = () => undefined, teardownWorkspaceFn = defaultTeardownWorkspaceFn, getKeepHistoryFilePathFn = () => undefined, logger: loggerInput, } = input;
4018
+ const { baseBranch, branchFn, lumpVariables: lumpVariablesInput, getContextListFn, gitAddCommandFn = defaultGitAddCommandFn, gitCommitCommandFn = defaultGitCommitCommandFn, gitCommitMessageFn = defaultGitCommitMessageFn, gitPushCommandFn = defaultGitPushCommandFn, numberOfContextsPerBranch = 1, projectRoot, steps, setupFn = () => ({ contextRunState: {} }), setupWorkspaceFn = defaultSetupWorkspaceFn, teardownFn = () => undefined, teardownWorkspaceFn = defaultTeardownWorkspaceFn, getKeepHistoryFilePathFn = () => undefined, logger: loggerInput, } = input;
4019
+ const lumpVariables = (lumpVariablesInput ?? {});
3904
4020
  const logger = loggerInput ?? createConsoleLogger({});
3905
4021
  const contextListToDoResult = await getToDoContextList({
3906
4022
  getContextListFn,
@@ -3954,5 +4070,5 @@ async function runLump(input) {
3954
4070
  });
3955
4071
  }
3956
4072
 
3957
- 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 };
4073
+ export { appendHistoryEntry, collectStepsForContext, contextStatus, 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 };
3958
4074
  //# sourceMappingURL=index.js.map