@lumpcode/core 0.0.3 → 0.0.5

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 (42) hide show
  1. package/README.md +26 -4
  2. package/dist/helpers/collectStepsForContext/main.d.ts +2 -5
  3. package/dist/helpers/collectStepsForContext/main.d.ts.map +1 -1
  4. package/dist/helpers/executeStepsForContextList/main.d.ts +3 -3
  5. package/dist/helpers/executeStepsForContextList/main.d.ts.map +1 -1
  6. package/dist/helpers/getCodeBasePaths/main.d.ts +3 -2
  7. package/dist/helpers/getCodeBasePaths/main.d.ts.map +1 -1
  8. package/dist/helpers/getContextStatus/main.d.ts +2 -2
  9. package/dist/helpers/getContextStatus/main.d.ts.map +1 -1
  10. package/dist/helpers/getToDoContextList/main.d.ts +2 -1
  11. package/dist/helpers/getToDoContextList/main.d.ts.map +1 -1
  12. package/dist/index.cjs +160 -55
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.js +158 -56
  15. package/dist/index.js.map +1 -1
  16. package/dist/types/CommandFn.d.ts +1 -0
  17. package/dist/types/CommandFn.d.ts.map +1 -1
  18. package/dist/types/ExecFailureData.d.ts +12 -0
  19. package/dist/types/ExecFailureData.d.ts.map +1 -0
  20. package/dist/types/Logger.d.ts +8 -0
  21. package/dist/types/Logger.d.ts.map +1 -0
  22. package/dist/types/Step.d.ts +1 -0
  23. package/dist/types/Step.d.ts.map +1 -1
  24. package/dist/types/index.d.ts +2 -0
  25. package/dist/types/index.d.ts.map +1 -1
  26. package/dist/usages/runLump/main.d.ts +2 -2
  27. package/dist/usages/runLump/main.d.ts.map +1 -1
  28. package/dist/utils/createConsoleLogger/index.d.ts +2 -0
  29. package/dist/utils/createConsoleLogger/index.d.ts.map +1 -0
  30. package/dist/utils/createConsoleLogger/main.d.ts +7 -0
  31. package/dist/utils/createConsoleLogger/main.d.ts.map +1 -0
  32. package/dist/utils/formatExecFailureMessage/index.d.ts +2 -0
  33. package/dist/utils/formatExecFailureMessage/index.d.ts.map +1 -0
  34. package/dist/utils/formatExecFailureMessage/main.d.ts +6 -0
  35. package/dist/utils/formatExecFailureMessage/main.d.ts.map +1 -0
  36. package/dist/utils/index.d.ts +3 -0
  37. package/dist/utils/index.d.ts.map +1 -1
  38. package/dist/utils/noopLogger/index.d.ts +2 -0
  39. package/dist/utils/noopLogger/index.d.ts.map +1 -0
  40. package/dist/utils/noopLogger/main.d.ts +3 -0
  41. package/dist/utils/noopLogger/main.d.ts.map +1 -0
  42. package/package.json +1 -1
package/README.md CHANGED
@@ -101,7 +101,7 @@ An array of `Step` objects that are executed sequentially for each context. Each
101
101
  | Field | Type | Description |
102
102
  |---|---|---|
103
103
  | `promptFn` | `PromptFn \| undefined` | Optional. Generates the prompt string from the current context, run state, and variables. When omitted, `commandFn` receives an empty prompt string. |
104
- | `commandFn` | `CommandFn` | Required. Returns `{ executable, args }` to run a subprocess, or `null` / `undefined` to skip execution while still running `postCommandExecFn`. |
104
+ | `commandFn` | `CommandFn` | Required. Returns `{ executable, args, env? }` to run a subprocess, or `null` / `undefined` to skip execution while still running `postCommandExecFn`. Optional `env` merges over the parent process environment for that command only. |
105
105
  | `stepVariables` | `StepVariables \| undefined` | Optional extra variables passed into `promptFn`, `commandFn`, and `postCommandExecFn`. |
106
106
  | `postCommandExecFn` | `PostCommandExecFn \| undefined` | Optional hook called after the command finishes, receiving the command output. |
107
107
  | `timeoutMillis` | `number \| undefined` | Maximum time in milliseconds allowed for the command execution. Defaults to `1800000` (30 minutes). |
@@ -167,11 +167,13 @@ The array of prompt items (or dynamic prompt-generating functions) to execute fo
167
167
 
168
168
  How many contexts to include in a single branch. For example, `1` means each context gets its own branch; `5` groups up to five contexts per branch.
169
169
 
170
- #### `verbose`
170
+ #### `logger`
171
171
 
172
- **Type:** `boolean` -- **Default:** `false`
172
+ **Type:** `Logger` -- **Default:** `createConsoleLogger({})` when omitted
173
173
 
174
- When `true`, logs extra diagnostic information during workspace setup and prompt execution (e.g. branch names, commands, git status).
174
+ Optional operational logger. CLI callers should pass an explicit logger from `createCliLogger` (after OR-merging lump `verbose` with `--verbose`). Library callers omit it for quiet defaults (info/warn/error only, no verbose detail).
175
+
176
+ See [Logging](#logging) below.
175
177
 
176
178
  #### `getKeepHistoryFilePathFn`
177
179
 
@@ -284,6 +286,26 @@ An alternative **git-worktree-based** implementation is exported as `defaultSetu
284
286
 
285
287
  Cleans up the workspace after all contexts on a branch are done. The default switches back to the base branch only. A worktree variant is exported as `defaultTeardownWorkspaceFnWithWorktree`.
286
288
 
289
+ ## Logging
290
+
291
+ Operational runtime output uses a small shared **`Logger`** type exported from `@lumpcode/core`:
292
+
293
+ ```typescript
294
+ type Logger = {
295
+ error: (message: string) => void;
296
+ warn: (message: string) => void;
297
+ info: (message: string) => void;
298
+ verbose: (message: string) => void;
299
+ child: (prefix: string) => Logger;
300
+ };
301
+ ```
302
+
303
+ - **`createConsoleLogger({ verbose?, json?, prefix? })`** — console-backed implementation (`error` always prints; `verbose` gated by `verbose`; when `json: true`, non-error levels are suppressed).
304
+ - **`noopLogger`** — no-op logger for tests.
305
+ - **`formatExecFailureMessage({ label, failure })`** — user-facing shell/git failure strings.
306
+
307
+ Pass **`logger`** on `RunLumpInput`. When omitted, the engine uses `createConsoleLogger({})` (operational errors/warnings/progress only; no verbose detail unless the logger you pass enables it).
308
+
287
309
  ## Return Value
288
310
 
289
311
  `runLump` returns a `Promise` that resolves to a discriminated union:
@@ -1,11 +1,8 @@
1
- import type { Context, ContextList, LumpVariables, PostCommandExecFn, Step, Steps, SetupFn } from '../../types';
1
+ import type { CommandDescriptor, Context, ContextList, LumpVariables, PostCommandExecFn, Step, Steps, SetupFn } from '../../types';
2
2
  export type CollectedStep = {
3
3
  stepIndex: number | number[];
4
4
  prompt?: string;
5
- command: {
6
- executable: string;
7
- args: string[];
8
- } | null;
5
+ command: CommandDescriptor | null;
9
6
  postCommandExecFn?: PostCommandExecFn;
10
7
  stepVariables?: Step['stepVariables'];
11
8
  timeoutMillis?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/helpers/collectStepsForContext/main.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,OAAO,EACP,WAAW,EAEX,aAAa,EACb,iBAAiB,EAEjB,IAAI,EACJ,KAAK,EACL,OAAO,EACV,MAAM,aAAa,CAAC;AAErB,MAAM,MAAM,aAAa,GAAG;IACxB,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC;IACvD,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,aAAa,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,aAAa,CAAC;IAC7B,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,wBAAsB,sBAAsB,CACxC,MAAM,EAAE,4BAA4B,GACrC,OAAO,CAAC,aAAa,EAAE,CAAC,CAwF1B"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/helpers/collectStepsForContext/main.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,iBAAiB,EACjB,OAAO,EACP,WAAW,EAEX,aAAa,EACb,iBAAiB,EAEjB,IAAI,EACJ,KAAK,EACL,OAAO,EACV,MAAM,aAAa,CAAC;AAErB,MAAM,MAAM,aAAa,GAAG;IACxB,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAClC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,aAAa,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,aAAa,CAAC;IAC7B,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,wBAAsB,sBAAsB,CACxC,MAAM,EAAE,4BAA4B,GACrC,OAAO,CAAC,aAAa,EAAE,CAAC,CA4F1B"}
@@ -1,10 +1,10 @@
1
- import { ContextList, ContextRunState, Failure } from "../../types";
1
+ import { ContextList, ContextRunState, Failure, Logger } from "../../types";
2
2
  import type { RunLumpInput } from '../../usages';
3
3
  export type ExecuteStepsForContextListParams = Required<Pick<RunLumpInput, 'baseBranch' | 'branchFn' | 'lumpVariables' | 'steps' | 'setupFn' | 'teardownFn' | 'gitAddCommandFn' | 'gitCommitCommandFn' | 'gitPushCommandFn' | 'gitCommitMessageFn' | 'projectRoot' | 'setupWorkspaceFn' | 'teardownWorkspaceFn' | 'getKeepHistoryFilePathFn'>> & {
4
4
  contextList: ContextList;
5
- verbose?: boolean;
5
+ logger?: Logger;
6
6
  };
7
- export declare function executeStepsForContextList({ baseBranch, branchFn, lumpVariables, contextList, gitAddCommandFn, gitCommitCommandFn, gitPushCommandFn, gitCommitMessageFn, projectRoot, steps, setupFn, setupWorkspaceFn, teardownFn, teardownWorkspaceFn, getKeepHistoryFilePathFn, verbose, }: ExecuteStepsForContextListParams): Promise<Failure<{
7
+ export declare function executeStepsForContextList({ baseBranch, branchFn, lumpVariables, contextList, gitAddCommandFn, gitCommitCommandFn, gitPushCommandFn, gitCommitMessageFn, projectRoot, steps, setupFn, setupWorkspaceFn, teardownFn, teardownWorkspaceFn, getKeepHistoryFilePathFn, logger: loggerInput, }: ExecuteStepsForContextListParams): Promise<Failure<{
8
8
  message: string;
9
9
  }> | import("../../types").Success<{
10
10
  branchName: string;
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/helpers/executeStepsForContextList/main.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,WAAW,EACX,eAAe,EACf,OAAO,EAIV,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAIjD,MAAM,MAAM,gCAAgC,GAAG,QAAQ,CAAC,IAAI,CACxD,YAAY,EACV,YAAY,GACZ,UAAU,GACV,eAAe,GACf,OAAO,GACP,SAAS,GACT,YAAY,GACZ,iBAAiB,GACjB,oBAAoB,GACpB,kBAAkB,GAClB,oBAAoB,GACpB,aAAa,GACb,kBAAkB,GAClB,qBAAqB,GACrB,0BAA0B,CAC/B,CAAC,GAAG;IACD,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB,CAAA;AAED,wBAAsB,0BAA0B,CAAC,EAC7C,UAAU,EACV,QAAQ,EACR,aAAa,EACb,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,WAAW,EACX,KAAK,EACL,OAAO,EACP,gBAAgB,EAChB,UAAU,EACV,mBAAmB,EACnB,wBAAwB,EACxB,OAAO,GACV,EAAE,gCAAgC;aAyDa,MAAM;;;;;IAsNrD;AAED,MAAM,MAAM,gCAAgC,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,0BAA0B,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/helpers/executeStepsForContextList/main.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,WAAW,EACX,eAAe,EACf,OAAO,EACP,MAAM,EAIT,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAIjD,MAAM,MAAM,gCAAgC,GAAG,QAAQ,CAAC,IAAI,CACxD,YAAY,EACV,YAAY,GACZ,UAAU,GACV,eAAe,GACf,OAAO,GACP,SAAS,GACT,YAAY,GACZ,iBAAiB,GACjB,oBAAoB,GACpB,kBAAkB,GAClB,oBAAoB,GACpB,aAAa,GACb,kBAAkB,GAClB,qBAAqB,GACrB,0BAA0B,CAC/B,CAAC,GAAG;IACD,WAAW,EAAE,WAAW,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB,CAAA;AAED,wBAAsB,0BAA0B,CAAC,EAC7C,UAAU,EACV,QAAQ,EACR,aAAa,EACb,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,WAAW,EACX,KAAK,EACL,OAAO,EACP,gBAAgB,EAChB,UAAU,EACV,mBAAmB,EACnB,wBAAwB,EACxB,MAAM,EAAE,WAAW,GACtB,EAAE,gCAAgC;aAgEa,MAAM;;;;;IAyOrD;AAED,MAAM,MAAM,gCAAgC,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,0BAA0B,CAAC,CAAC,CAAC"}
@@ -1,6 +1,7 @@
1
- import { CodeBasePath } from '../../types';
2
- export declare function getCodeBasePaths({ cwd, }: {
1
+ import { CodeBasePath, Logger } from '../../types';
2
+ export declare function getCodeBasePaths({ cwd, logger: loggerInput, }: {
3
3
  cwd: string;
4
+ logger?: Logger;
4
5
  }): Promise<import("../../types").Failure<{
5
6
  message: string;
6
7
  }> | import("../../types").Success<CodeBasePath[]>>;
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/helpers/getCodeBasePaths/main.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,wBAAsB,gBAAgB,CAAC,EACnC,GAAG,GACN,EAAE;IACC,GAAG,EAAE,MAAM,CAAC;CACf;;oDAkBA"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/helpers/getCodeBasePaths/main.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAEnD,wBAAsB,gBAAgB,CAAC,EACnC,GAAG,EACH,MAAM,EAAE,WAAW,GACtB,EAAE;IACC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;;oDAoBA"}
@@ -1,4 +1,4 @@
1
- import { ContextStatus, LumpVariables } from "../../types";
1
+ import { ContextStatus, Logger, LumpVariables } from "../../types";
2
2
  import { GitCommitMessageFn } from "../../types/GitCommitMessageFn";
3
3
  export declare function getContextStatus(params: {
4
4
  contextName: string;
@@ -8,6 +8,6 @@ export declare function getContextStatus(params: {
8
8
  lumpVariables?: LumpVariables;
9
9
  contextVariables?: Record<string, string>;
10
10
  remoteName?: string;
11
- verbose?: boolean;
11
+ logger?: Logger;
12
12
  }): Promise<ContextStatus>;
13
13
  //# sourceMappingURL=main.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/helpers/getContextStatus/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAIpE,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB,GAAG,OAAO,CAAC,aAAa,CAAC,CA+EzB"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/helpers/getContextStatus/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAIpE,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,aAAa,CAAC,CA6EzB"}
@@ -1,4 +1,4 @@
1
- import { GetContextListFn, LumpVariables } from "../../types";
1
+ import { GetContextListFn, Logger, LumpVariables } from "../../types";
2
2
  import { GitCommitMessageFn } from "../../types/GitCommitMessageFn";
3
3
  export declare function getToDoContextList(params: {
4
4
  getContextListFn: GetContextListFn;
@@ -6,6 +6,7 @@ export declare function getToDoContextList(params: {
6
6
  gitCommitMessageFn: GitCommitMessageFn;
7
7
  projectRoot: string;
8
8
  baseBranch: string;
9
+ logger?: Logger;
9
10
  }): Promise<import("../../types").Failure<{
10
11
  message: string;
11
12
  }> | import("../../types").Success<import("../../types").Context[]>>;
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/helpers/getToDoContextList/main.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAGpE,wBAAsB,kBAAkB,CAAC,MAAM,EAAE;IAC7C,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACtB;;qEA6DA"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/helpers/getToDoContextList/main.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAGpE,wBAAsB,kBAAkB,CAAC,MAAM,EAAE;IAC7C,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;;qEA8DA"}
package/dist/index.cjs CHANGED
@@ -211,6 +211,78 @@ function resolveSpawnExecutable(executable, args) {
211
211
  return { executable: resolved, args };
212
212
  }
213
213
 
214
+ function formatMessage(prefix, message) {
215
+ if (!prefix)
216
+ return message;
217
+ return `${prefix} ${message}`;
218
+ }
219
+ function createConsoleLogger(input) {
220
+ const verboseEnabled = !!input.verbose;
221
+ const suppressNonError = !!input.json;
222
+ const prefix = input.prefix;
223
+ function makeLogger(currentPrefix) {
224
+ return {
225
+ error(message) {
226
+ console.error(formatMessage(currentPrefix, message));
227
+ },
228
+ warn(message) {
229
+ if (suppressNonError)
230
+ return;
231
+ console.warn(formatMessage(currentPrefix, message));
232
+ },
233
+ info(message) {
234
+ if (suppressNonError)
235
+ return;
236
+ console.log(formatMessage(currentPrefix, message));
237
+ },
238
+ verbose(message) {
239
+ if (suppressNonError || !verboseEnabled)
240
+ return;
241
+ console.log(formatMessage(currentPrefix, message));
242
+ },
243
+ child(childPrefix) {
244
+ const nextPrefix = currentPrefix ? `${currentPrefix} ${childPrefix}` : childPrefix;
245
+ return makeLogger(nextPrefix);
246
+ },
247
+ };
248
+ }
249
+ return makeLogger(prefix);
250
+ }
251
+
252
+ const noopLogger = {
253
+ error: () => { },
254
+ warn: () => { },
255
+ info: () => { },
256
+ verbose: () => { },
257
+ child: () => noopLogger,
258
+ };
259
+
260
+ function firstNonEmptyLine(value) {
261
+ if (value == null)
262
+ return undefined;
263
+ const text = typeof value === 'string' ? value : String(value);
264
+ const line = text
265
+ .split(/\r?\n/)
266
+ .map((part) => part.trim())
267
+ .find((part) => part.length > 0);
268
+ return line || undefined;
269
+ }
270
+ function shortExecDetail(data) {
271
+ return (firstNonEmptyLine(data.stderr)
272
+ ?? firstNonEmptyLine(data.info?.stderr)
273
+ ?? firstNonEmptyLine(data.stdout)
274
+ ?? firstNonEmptyLine(data.info?.stdout));
275
+ }
276
+ function formatExecFailureMessage(input) {
277
+ const { label, failure } = input;
278
+ const data = failure.data;
279
+ const detail = shortExecDetail(data) ?? data.message;
280
+ if (typeof data.code === 'number') {
281
+ return `${label} failed (exit ${data.code}): ${detail}`;
282
+ }
283
+ return `${label} failed: ${detail}`;
284
+ }
285
+
214
286
  const execAsyncBase = node_util.promisify(node_child_process.exec);
215
287
  async function execAsync(command, options) {
216
288
  const { stdout, stderr, hasErrored } = await execAsyncBase(command, { cwd: options?.cwd })
@@ -342,7 +414,11 @@ async function collectStepsForContext(params) {
342
414
  stepIndex: compositeStepIndex,
343
415
  ...(prompt.length > 0 && { prompt }),
344
416
  command: command != null
345
- ? { executable: command.executable, args: command.args }
417
+ ? {
418
+ executable: command.executable,
419
+ args: command.args,
420
+ ...(command.env != null ? { env: command.env } : {}),
421
+ }
346
422
  : null,
347
423
  postCommandExecFn,
348
424
  stepVariables,
@@ -354,16 +430,17 @@ async function collectStepsForContext(params) {
354
430
  return collectedSteps;
355
431
  }
356
432
 
357
- async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables, contextList, gitAddCommandFn, gitCommitCommandFn, gitPushCommandFn, gitCommitMessageFn, projectRoot, steps, setupFn, setupWorkspaceFn, teardownFn, teardownWorkspaceFn, getKeepHistoryFilePathFn, verbose, }) {
433
+ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables, contextList, gitAddCommandFn, gitCommitCommandFn, gitPushCommandFn, gitCommitMessageFn, projectRoot, steps, setupFn, setupWorkspaceFn, teardownFn, teardownWorkspaceFn, getKeepHistoryFilePathFn, logger: loggerInput, }) {
434
+ const logger = loggerInput ?? createConsoleLogger({});
358
435
  const contextNames = contextList.map(context => context.name);
359
- log('contextNames', contextNames);
436
+ logger.verbose(`contextNames ${JSON.stringify(contextNames)}`);
360
437
  const contextRunStateList = [];
361
438
  const branchName = await branchFn({
362
439
  contextList,
363
440
  contextRunStateList,
364
441
  lumpVariables,
365
442
  });
366
- log('branchName', branchName);
443
+ logger.verbose(`branchName ${branchName}`);
367
444
  const injectedGitAndWorkspaceFnsInput = {
368
445
  baseBranch,
369
446
  branchName,
@@ -371,22 +448,25 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
371
448
  workspacePath: '.',
372
449
  };
373
450
  const { command: setupWorkspaceCommand, workspacePath } = await setupWorkspaceFn(injectedGitAndWorkspaceFnsInput);
374
- log('setupWorkspaceCommand', setupWorkspaceCommand);
375
- log('workspacePath', workspacePath);
451
+ logger.verbose(`setupWorkspaceCommand ${setupWorkspaceCommand}`);
452
+ logger.verbose(`workspacePath ${workspacePath}`);
376
453
  if (setupWorkspaceCommand) {
377
454
  const setupWorkspaceCommandExec = await execAsync(setupWorkspaceCommand, {
378
455
  cwd: projectRoot,
379
456
  });
380
- log('setupWorkspaceCommandExec', setupWorkspaceCommandExec);
457
+ logger.verbose(`setupWorkspaceCommandExec ${JSON.stringify(setupWorkspaceCommandExec)}`);
381
458
  if (!setupWorkspaceCommandExec.success) {
382
459
  return set(setupWorkspaceCommandExec, ['data', 'message'], `Failed to setup the workspace: ${setupWorkspaceCommandExec.data.message}`);
383
460
  }
384
461
  }
385
462
  const gitStatusCommand = await execAsync(`git status`, { cwd: workspacePath });
386
- log('gitStatusCommand', gitStatusCommand.data);
463
+ logger.verbose(`gitStatusCommand ${JSON.stringify(gitStatusCommand.data)}`);
387
464
  injectedGitAndWorkspaceFnsInput.workspacePath = workspacePath;
388
465
  for (let i = 0; i < contextList.length; i++) {
389
466
  const context = contextList[i];
467
+ logger.info(contextList.length > 1
468
+ ? `Running context "${context.name}" (${i + 1}/${contextList.length})`
469
+ : `Running context "${context.name}"`);
390
470
  const setupResult = await setupFn({
391
471
  contextList,
392
472
  lumpVariables,
@@ -418,8 +498,8 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
418
498
  await walkAndExecuteSteps(subSteps, nextCallHeadIndex);
419
499
  continue;
420
500
  }
421
- log('step', step);
422
- const { commandFn, stepVariables, promptFn, postCommandExecFn, timeoutMillis = 1000 * 60 * 30, } = step;
501
+ logger.verbose(`step ${JSON.stringify(step)}`);
502
+ const { commandFn, stepVariables, promptFn, postCommandExecFn, continueOnError, timeoutMillis = 1000 * 60 * 30, } = step;
423
503
  const prompt = promptFn
424
504
  ? await promptFn({
425
505
  context,
@@ -440,23 +520,40 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
440
520
  workspacePath,
441
521
  });
442
522
  let commandResult = '';
523
+ let commandSucceeded = true;
443
524
  if (command != null) {
444
- const { executable, args } = command;
445
- log('command for prompt', executable, args);
446
- log('workspacePath', workspacePath);
525
+ const { executable, args, env } = command;
526
+ logger.verbose(`command for prompt ${executable} ${args.join(' ')}`);
527
+ if (env != null) {
528
+ logger.verbose(`command env overrides ${JSON.stringify(env)}`);
529
+ }
530
+ logger.verbose(`workspacePath ${workspacePath}`);
447
531
  const commandExec = await execBinary(executable, args, timeoutMillis, {
448
532
  stdio: ['inherit', 'pipe', 'pipe'],
449
533
  cwd: workspacePath,
534
+ ...(env != null ? { env: { ...process.env, ...env } } : {}),
450
535
  });
451
- log('commandExec', commandExec);
536
+ logger.verbose(`commandExec ${JSON.stringify(commandExec)}`);
452
537
  if (!commandExec.success) {
453
- stepWalkFailure = set(commandExec, ['data', 'message'], `Failed to run the command: ${commandExec.data.message}. Command: ${executable} ${args.join(' ')}`);
454
- return;
538
+ if (!continueOnError) {
539
+ stepWalkFailure = set(commandExec, ['data', 'message'], `Failed to run the command: ${commandExec.data.message}. Command: ${executable} ${args.join(' ')}`);
540
+ return;
541
+ }
542
+ commandSucceeded = false;
543
+ commandResult = (commandExec.data.stdout
544
+ || commandExec.data.stderr
545
+ || commandExec.data.message
546
+ || '').toString();
547
+ logger.verbose(`commandResult ${commandResult}`);
548
+ }
549
+ else {
550
+ commandResult = (commandExec.data.stdout || commandExec.data.stderr || '').toString();
551
+ logger.verbose(`commandResult ${commandResult}`);
552
+ }
553
+ if (commandSucceeded) {
554
+ const gitStatusAfterCommand = await execAsync(`git status`, { cwd: workspacePath });
555
+ logger.verbose(`gitStatusCommand ${JSON.stringify(gitStatusAfterCommand.data)}`);
455
556
  }
456
- commandResult = (commandExec.data.stdout || commandExec.data.stderr || '').toString();
457
- log('commandResult', commandResult);
458
- const gitStatusCommand = await execAsync(`git status`, { cwd: workspacePath });
459
- log('gitStatusCommand', gitStatusCommand.data);
460
557
  }
461
558
  const postCommandExecFnInput = {
462
559
  commandResult,
@@ -468,9 +565,9 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
468
565
  stepVariables,
469
566
  projectRoot,
470
567
  };
471
- log('context is', context);
568
+ logger.verbose(`context is ${JSON.stringify(context)}`);
472
569
  const keepHistoryFilePath = getKeepHistoryFilePathFn(context) || '';
473
- log('keepHistoryFilePath', keepHistoryFilePath);
570
+ logger.verbose(`keepHistoryFilePath ${keepHistoryFilePath}`);
474
571
  if (!!command && keepHistoryFilePath.length > 0) {
475
572
  const makeHistoryFileIfNotExists = async () => {
476
573
  if (await fs$1.stat(keepHistoryFilePath).then(() => true).catch(() => false))
@@ -480,13 +577,13 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
480
577
  };
481
578
  await makeHistoryFileIfNotExists();
482
579
  const historyFileContent = await fs$1.readFile(keepHistoryFilePath, 'utf-8');
483
- log('historyFileContent', historyFileContent);
580
+ logger.verbose(`historyFileContent ${historyFileContent}`);
484
581
  const history = JSON.parse(historyFileContent);
485
- log('history', history);
582
+ logger.verbose(`history ${JSON.stringify(history)}`);
486
583
  history.push({
487
584
  ...postCommandExecFnInput,
488
585
  });
489
- log('history after push', history);
586
+ logger.verbose(`history after push ${JSON.stringify(history)}`);
490
587
  await fs$1.writeFile(keepHistoryFilePath, JSON.stringify(history, null, 2), 'utf-8');
491
588
  }
492
589
  if (postCommandExecFn) {
@@ -509,7 +606,7 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
509
606
  context,
510
607
  };
511
608
  const gitAddCommand = await execAsync(gitAddCommandFn(perContextInput), { cwd: workspacePath });
512
- log('gitAddCommand', gitAddCommand);
609
+ logger.verbose(`gitAddCommand ${JSON.stringify(gitAddCommand)}`);
513
610
  if (!gitAddCommand.success) {
514
611
  return set(gitAddCommand, ['data', 'message'], `Failed to add the changes for context ${context.name}: ${gitAddCommand.data.message}`);
515
612
  }
@@ -518,21 +615,27 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
518
615
  ...perContextInput,
519
616
  commitMessage,
520
617
  }), { cwd: workspacePath });
521
- log('commitCommand', commitCommand);
618
+ logger.verbose(`commitCommand ${JSON.stringify(commitCommand)}`);
522
619
  if (!commitCommand.success) {
523
- log(`Failed to commit the changes for context ${context.name}:`, commitCommand.data);
620
+ logger.error(formatExecFailureMessage({
621
+ label: `git commit for context ${context.name}`,
622
+ failure: commitCommand,
623
+ }));
524
624
  }
525
625
  }
526
626
  const pushCommand = await execAsync(gitPushCommandFn(injectedGitAndWorkspaceFnsInput), { cwd: workspacePath });
527
- log('pushCommand', pushCommand);
627
+ logger.verbose(`pushCommand ${JSON.stringify(pushCommand)}`);
528
628
  if (!pushCommand.success) {
529
- log(`Failed to push the changes on branch ${branchName}: ${pushCommand.data.message}`);
629
+ logger.error(formatExecFailureMessage({
630
+ label: `git push on branch ${branchName}`,
631
+ failure: pushCommand,
632
+ }));
530
633
  }
531
634
  const teardownWorkspaceCommand = await teardownWorkspaceFn(injectedGitAndWorkspaceFnsInput);
532
- console.log('teardownWorkspaceCommand', teardownWorkspaceCommand);
635
+ logger.verbose(`teardownWorkspaceCommand ${teardownWorkspaceCommand}`);
533
636
  if (teardownWorkspaceCommand) {
534
637
  const teardownWorkspaceCommandExec = await execAsync(teardownWorkspaceCommand, { cwd: workspacePath });
535
- console.log('teardownWorkspaceCommandExec', teardownWorkspaceCommandExec);
638
+ logger.verbose(`teardownWorkspaceCommandExec ${JSON.stringify(teardownWorkspaceCommandExec)}`);
536
639
  if (!teardownWorkspaceCommandExec.success) {
537
640
  return set(teardownWorkspaceCommandExec, ['data', 'message'], `Failed to teardown the workspace: ${teardownWorkspaceCommandExec.data.message}`);
538
641
  }
@@ -542,14 +645,10 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
542
645
  contextNames,
543
646
  contextRunStateList,
544
647
  });
545
- function log(...params) {
546
- if (verbose) {
547
- console.log(...params);
548
- }
549
- }
550
648
  }
551
649
 
552
- async function getCodeBasePaths({ cwd, }) {
650
+ async function getCodeBasePaths({ cwd, logger: loggerInput, }) {
651
+ const logger = loggerInput ?? createConsoleLogger({});
553
652
  const allPaths = [];
554
653
  const gitignoresMap = await getGitignoresMap({ startDir: cwd, stopAtDir: cwd });
555
654
  try {
@@ -558,6 +657,7 @@ async function getCodeBasePaths({ cwd, }) {
558
657
  allPaths,
559
658
  gitignoresMap,
560
659
  projectRoot: cwd,
660
+ logger,
561
661
  });
562
662
  }
563
663
  catch (error) {
@@ -582,7 +682,7 @@ async function getGitignoresMap({ startDir, stopAtDir }) {
582
682
  }
583
683
  return gitignores;
584
684
  }
585
- async function scanDirectory({ dirPath, allPaths, gitignoresMap, projectRoot }) {
685
+ async function scanDirectory({ dirPath, allPaths, gitignoresMap, projectRoot, logger }) {
586
686
  try {
587
687
  const items = await fs__namespace$1.readdir(dirPath);
588
688
  for (const item of items) {
@@ -611,11 +711,12 @@ async function scanDirectory({ dirPath, allPaths, gitignoresMap, projectRoot })
611
711
  allPaths,
612
712
  gitignoresMap,
613
713
  projectRoot,
714
+ logger,
614
715
  });
615
716
  }
616
717
  }
617
718
  catch (error) {
618
- console.warn(`Cannot access ${itemPath}: ${error}`);
719
+ logger.warn(`Cannot access ${itemPath}: ${error}`);
619
720
  }
620
721
  }
621
722
  }
@@ -625,7 +726,7 @@ async function scanDirectory({ dirPath, allPaths, gitignoresMap, projectRoot })
625
726
  }
626
727
 
627
728
  async function getContextStatus(params) {
628
- const { contextName, gitCommitMessageFn, projectRoot, baseBranch, lumpVariables = {}, contextVariables = {}, remoteName = "origin", verbose = false, } = params;
729
+ const { contextName, gitCommitMessageFn, projectRoot, baseBranch, lumpVariables = {}, contextVariables = {}, remoteName = "origin", logger, } = params;
629
730
  const commitMessage = gitCommitMessageFn({
630
731
  context: { name: contextName, variables: contextVariables },
631
732
  lumpVariables,
@@ -637,7 +738,7 @@ async function getContextStatus(params) {
637
738
  const logResult = await execAsync(`git log --remotes=${remoteName} -F --grep=${shellSingleQuote(commitMessage)} --format=${shellSingleQuote('%H %s')}`, { cwd: projectRoot });
638
739
  if (!logResult.success)
639
740
  return 'toDo';
640
- console.log('logResult', logResult.data);
741
+ logger?.verbose(`logResult ${JSON.stringify(logResult.data)}`);
641
742
  const logResultOutput = logResult.data.stdout || logResult.data.stderr || '';
642
743
  const matchingHashes = logResultOutput
643
744
  .split('\n')
@@ -652,12 +753,10 @@ async function getContextStatus(params) {
652
753
  })
653
754
  .filter((c) => c.subject === commitMessage)
654
755
  .map((c) => c.hash);
655
- if (verbose) {
656
- console.log('remoteName', remoteName);
657
- console.log('baseBranch', baseBranch);
658
- console.log('commitMessage', commitMessage);
659
- console.log('matchingHashes', matchingHashes);
660
- }
756
+ logger?.verbose(`remoteName ${remoteName}`);
757
+ logger?.verbose(`baseBranch ${baseBranch}`);
758
+ logger?.verbose(`commitMessage ${commitMessage}`);
759
+ logger?.verbose(`matchingHashes ${JSON.stringify(matchingHashes)}`);
661
760
  if (matchingHashes.length === 0)
662
761
  return 'toDo';
663
762
  const baseRef = `${remoteName}/${baseBranch}`;
@@ -708,8 +807,8 @@ function validateContextListNames(contextList) {
708
807
  }
709
808
 
710
809
  async function getToDoContextList(params) {
711
- const { getContextListFn, lumpVariables, gitCommitMessageFn, projectRoot, baseBranch } = params;
712
- const codeBasePathsResult = await getCodeBasePaths({ cwd: projectRoot });
810
+ const { getContextListFn, lumpVariables, gitCommitMessageFn, projectRoot, baseBranch, logger } = params;
811
+ const codeBasePathsResult = await getCodeBasePaths({ cwd: projectRoot, logger });
713
812
  if (!codeBasePathsResult.success) {
714
813
  return failure({
715
814
  message: 'Failed to get code base paths',
@@ -734,6 +833,7 @@ async function getToDoContextList(params) {
734
833
  lumpVariables,
735
834
  projectRoot,
736
835
  baseBranch,
836
+ logger,
737
837
  });
738
838
  }));
739
839
  const contextStatusMap = new Map(allCtxNamesList.map((contextName, i) => [contextName, contextStatusList[i]]));
@@ -751,7 +851,7 @@ async function getToDoContextList(params) {
751
851
  .sort((a, b) => {
752
852
  return (a.options?.priority || 0) - (b.options?.priority || 0);
753
853
  });
754
- console.log('contextListToDo', contextListToDo);
854
+ logger?.verbose(`contextListToDo ${JSON.stringify(contextListToDo)}`);
755
855
  return success(contextListToDo);
756
856
  }
757
857
 
@@ -768,7 +868,7 @@ const defaultGitAddCommandFn = () => {
768
868
  return `git add .`;
769
869
  };
770
870
  const defaultGitCommitCommandFn = (input) => {
771
- return `git commit -m ${shellSingleQuote(input.commitMessage)}`;
871
+ return `git commit --allow-empty -m ${shellSingleQuote(input.commitMessage)}`;
772
872
  };
773
873
  const defaultSetupWorkspaceFn = async (input) => {
774
874
  const { baseBranch, branchName } = input;
@@ -806,13 +906,15 @@ const defaultSetupWorkspaceFnWithWorktree = async (input) => {
806
906
  };
807
907
 
808
908
  async function runLump(input) {
809
- 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, verbose = false, getKeepHistoryFilePathFn = () => undefined, } = input;
909
+ 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;
910
+ const logger = loggerInput ?? createConsoleLogger({});
810
911
  const contextListToDoResult = await getToDoContextList({
811
912
  getContextListFn,
812
913
  lumpVariables,
813
914
  projectRoot,
814
915
  baseBranch,
815
916
  gitCommitMessageFn,
917
+ logger,
816
918
  });
817
919
  if (!contextListToDoResult.success) {
818
920
  return set(contextListToDoResult, ['data', 'message'], "Error in runLump: Failed to get to do context list. Original Error: " + contextListToDoResult.data.message);
@@ -820,7 +922,7 @@ async function runLump(input) {
820
922
  const contextListToDo = contextListToDoResult.data;
821
923
  const nextContextsForBranchList = contextListToDo.slice(0, numberOfContextsPerBranch);
822
924
  if (nextContextsForBranchList.length === 0) {
823
- console.log('no next contexts for branch');
925
+ logger.verbose('no next contexts for branch');
824
926
  return success({
825
927
  result: {
826
928
  updatedGroupStatusRecord: {
@@ -847,7 +949,7 @@ async function runLump(input) {
847
949
  setupWorkspaceFn,
848
950
  teardownFn,
849
951
  teardownWorkspaceFn,
850
- verbose,
952
+ logger,
851
953
  getKeepHistoryFilePathFn,
852
954
  });
853
955
  if (!executeStepsResult.success) {
@@ -861,6 +963,7 @@ async function runLump(input) {
861
963
  exports.collectStepsForContext = collectStepsForContext;
862
964
  exports.contextStatus = contextStatus;
863
965
  exports.contextStatusSchema = contextStatusSchema;
966
+ exports.createConsoleLogger = createConsoleLogger;
864
967
  exports.decision = decision;
865
968
  exports.defaultGitAddCommandFn = defaultGitAddCommandFn;
866
969
  exports.defaultGitCommitCommandFn = defaultGitCommitCommandFn;
@@ -874,9 +977,11 @@ exports.execAsync = execAsync;
874
977
  exports.execBinary = execBinary;
875
978
  exports.executeStepsForContextList = executeStepsForContextList;
876
979
  exports.failure = failure;
980
+ exports.formatExecFailureMessage = formatExecFailureMessage;
877
981
  exports.getCodeBasePaths = getCodeBasePaths;
878
982
  exports.getContextStatus = getContextStatus;
879
983
  exports.getToDoContextList = getToDoContextList;
984
+ exports.noopLogger = noopLogger;
880
985
  exports.resolveSpawnExecutable = resolveSpawnExecutable;
881
986
  exports.runLump = runLump;
882
987
  exports.set = set;