lalph 0.3.89 → 0.3.91

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -181596,7 +181596,7 @@ var ji = Bt, Ii = Object.assign(Qe, { sync: Bt }), zi = Ut, Bi = Object.assign(e
181596
181596
  });
181597
181597
  Ze.glob = Ze;
181598
181598
  //#endregion
181599
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/ApplyPatch.js
181599
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/ApplyPatch.js
181600
181600
  /**
181601
181601
  * @since 1.0.0
181602
181602
  */
@@ -196469,7 +196469,7 @@ var StreamableHTTPClientTransport = class {
196469
196469
  }
196470
196470
  };
196471
196471
  //#endregion
196472
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/McpClient.js
196472
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/McpClient.js
196473
196473
  /**
196474
196474
  * @since 1.0.0
196475
196475
  */
@@ -196514,7 +196514,7 @@ const layer$13 = effect$1(McpClient, gen(function* () {
196514
196514
  });
196515
196515
  }));
196516
196516
  //#endregion
196517
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/ExaSearch.js
196517
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/ExaSearch.js
196518
196518
  /**
196519
196519
  * @since 1.0.0
196520
196520
  */
@@ -211464,7 +211464,7 @@ var require_lib = /* @__PURE__ */ __commonJSMin$1(((exports) => {
211464
211464
  exports.impl = impl;
211465
211465
  }));
211466
211466
  //#endregion
211467
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/WebToMarkdown.js
211467
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/WebToMarkdown.js
211468
211468
  /**
211469
211469
  * @since 1.0.0
211470
211470
  */
@@ -215082,7 +215082,7 @@ const mapProviderResults = (inputLength, results) => {
215082
215082
  return succeed$3(embeddings);
215083
215083
  };
215084
215084
  //#endregion
215085
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/ChunkRepo.js
215085
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/ChunkRepo.js
215086
215086
  /**
215087
215087
  * @since 1.0.0
215088
215088
  * @category Models
@@ -215213,7 +215213,7 @@ const layer$10 = effect$1(ChunkRepo, gen(function* () {
215213
215213
  });
215214
215214
  }));
215215
215215
  //#endregion
215216
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/CodeChunker.js
215216
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/CodeChunker.js
215217
215217
  /**
215218
215218
  * @since 1.0.0
215219
215219
  */
@@ -215806,7 +215806,7 @@ const run$1 = /* @__PURE__ */ make$25({});
215806
215806
  */
215807
215807
  const layer$7 = (options) => effectDiscard(run$1(options));
215808
215808
  //#endregion
215809
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/internal/sqlite-vector.js
215809
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/internal/sqlite-vector.js
215810
215810
  /**
215811
215811
  * Binary extension for each platform
215812
215812
  */
@@ -215923,7 +215923,7 @@ function getExtensionPath() {
215923
215923
  throw new ExtensionNotFoundError(`SQLite Vector extension not found for platform: ${getCurrentPlatform()}\n\nThe platform-specific package "${getPlatformPackageName()}" is not installed.\nThis usually happens when:\n 1. Your platform is not supported\n 2. npm failed to install optional dependencies\n 3. You're installing with --no-optional flag\n\nTry running: npm install --force`);
215924
215924
  }
215925
215925
  //#endregion
215926
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/Sqlite.js
215926
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/Sqlite.js
215927
215927
  /**
215928
215928
  * @since 1.0.0
215929
215929
  */
@@ -215951,7 +215951,7 @@ const SqliteLayer = (database) => layer$7({ loader: fromRecord({ "0001_create_ch
215951
215951
  yield* fs.makeDirectory(directory, { recursive: true });
215952
215952
  }))));
215953
215953
  //#endregion
215954
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/SemanticSearch.js
215954
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/SemanticSearch.js
215955
215955
  /**
215956
215956
  * @since 1.0.0
215957
215957
  */
@@ -216093,7 +216093,7 @@ const maybeRemoveFile = (path) => serviceOption(SemanticSearch).pipe(flatMap$4(m
216093
216093
  onSome: (service) => service.removeFile(path)
216094
216094
  })));
216095
216095
  //#endregion
216096
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/AgentTools.js
216096
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/AgentTools.js
216097
216097
  /**
216098
216098
  * @since 1.0.0
216099
216099
  */
@@ -216444,7 +216444,7 @@ const AgentToolHandlers = AgentToolHandlersNoDeps.pipe(provide$3([layer$12, laye
216444
216444
  AgentToolHandlersNoDeps.pipe(provide$3([mock(ExaSearch)({}), mock(WebToMarkdown)({})]));
216445
216445
  var ApplyPatchError = class extends TaggedClass$2("ApplyPatchError") {};
216446
216446
  //#endregion
216447
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/TypeBuilder.js
216447
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/TypeBuilder.js
216448
216448
  const resolveDocumentation = resolveAt("documentation");
216449
216449
  const identifierPattern = /^[$A-Z_a-z][$0-9A-Z_a-z]*$/u;
216450
216450
  const Precedence = {
@@ -216717,7 +216717,7 @@ const render = (schema, options) => {
216717
216717
  return printNode({ text: documentation === void 0 ? rendered.text : `${renderJsDoc(documentation, 0, printerOptions)}${printerOptions.newLine}${rendered.text}` }, printerOptions);
216718
216718
  };
216719
216719
  //#endregion
216720
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/ToolkitRenderer.js
216720
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/ToolkitRenderer.js
216721
216721
  /**
216722
216722
  * @since 1.0.0
216723
216723
  */
@@ -216739,7 +216739,7 @@ declare function ${name}(${params}): Promise<${render(tool.successSchema)}>`);
216739
216739
  }) });
216740
216740
  };
216741
216741
  //#endregion
216742
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/ScriptPreprocessing.js
216742
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/ScriptPreprocessing.js
216743
216743
  const isIdentifierChar = (char) => char !== void 0 && /[A-Za-z0-9_$]/.test(char);
216744
216744
  const isIdentifierStartChar = (char) => char !== void 0 && /[A-Za-z_$]/.test(char);
216745
216745
  const hasIdentifierBoundary = (text, index, length) => !isIdentifierChar(text[index - 1]) && !isIdentifierChar(text[index + length]);
@@ -216765,6 +216765,76 @@ const parseIdentifier = (text, start) => {
216765
216765
  end
216766
216766
  };
216767
216767
  };
216768
+ const findPreviousNonWhitespace = (text, from) => {
216769
+ let i = from;
216770
+ while (i >= 0 && /\s/.test(text[i])) i--;
216771
+ return i;
216772
+ };
216773
+ const findNextNonWhitespace = (text, from) => {
216774
+ let i = from;
216775
+ while (i < text.length && /\s/.test(text[i])) i++;
216776
+ return i;
216777
+ };
216778
+ const findObjectValueTerminator = (text, start) => {
216779
+ let parenDepth = 0;
216780
+ let bracketDepth = 0;
216781
+ let braceDepth = 0;
216782
+ let stringDelimiter;
216783
+ for (let i = start; i < text.length; i++) {
216784
+ const char = text[i];
216785
+ if (stringDelimiter !== void 0) {
216786
+ if (char === stringDelimiter && !isEscaped(text, i)) stringDelimiter = void 0;
216787
+ continue;
216788
+ }
216789
+ if (char === "\"" || char === "'" || char === "`") {
216790
+ stringDelimiter = char;
216791
+ continue;
216792
+ }
216793
+ if (char === "(") {
216794
+ parenDepth++;
216795
+ continue;
216796
+ }
216797
+ if (char === ")") {
216798
+ if (parenDepth > 0) parenDepth--;
216799
+ continue;
216800
+ }
216801
+ if (char === "[") {
216802
+ bracketDepth++;
216803
+ continue;
216804
+ }
216805
+ if (char === "]") {
216806
+ if (bracketDepth > 0) bracketDepth--;
216807
+ continue;
216808
+ }
216809
+ if (char === "{") {
216810
+ braceDepth++;
216811
+ continue;
216812
+ }
216813
+ if (char === "}") {
216814
+ if (parenDepth === 0 && bracketDepth === 0 && braceDepth === 0) return i;
216815
+ if (braceDepth > 0) braceDepth--;
216816
+ continue;
216817
+ }
216818
+ if (char === "," && parenDepth === 0 && bracketDepth === 0 && braceDepth === 0) return i;
216819
+ }
216820
+ return -1;
216821
+ };
216822
+ const collectExpressionIdentifiers = (text, start, end) => {
216823
+ const out = /* @__PURE__ */ new Set();
216824
+ let cursor = start;
216825
+ while (cursor < end) {
216826
+ const identifier = parseIdentifier(text, cursor);
216827
+ if (identifier === void 0) {
216828
+ cursor++;
216829
+ continue;
216830
+ }
216831
+ const previousNonWhitespace = findPreviousNonWhitespace(text, cursor - 1);
216832
+ const nextNonWhitespace = findNextNonWhitespace(text, identifier.end);
216833
+ if (text[previousNonWhitespace] !== "." && text[nextNonWhitespace] !== "." && text[nextNonWhitespace] !== "(") out.add(identifier.name);
216834
+ cursor = identifier.end;
216835
+ }
216836
+ return out;
216837
+ };
216768
216838
  const isEscaped = (text, index) => {
216769
216839
  let slashCount = 0;
216770
216840
  let i = index - 1;
@@ -216782,14 +216852,26 @@ const needsTemplateEscaping = (text) => {
216782
216852
  }
216783
216853
  return false;
216784
216854
  };
216785
- const normalizePatchEscapedQuotes = (text) => text.includes("*** Begin Patch") ? text.replace(/\\"([A-Za-z0-9_$.-]+)\\"/g, "\"$1\"") : text;
216855
+ const normalizePatchEscapedQuotes = (text) => text.includes("*** Begin Patch") ? text.replace(/\\"([A-Za-z0-9_$.-]+)\\"/g, (match, content, index) => {
216856
+ const previous = text[findPreviousNonWhitespace(text, index - 1)];
216857
+ const next = text[findNextNonWhitespace(text, index + match.length)];
216858
+ if (previous === "{" || previous === "[" || previous === ":" || previous === "," || next === ":" || next === "}" || next === "]" || next === ",") return match;
216859
+ return `"${content}"`;
216860
+ }) : text;
216861
+ const normalizeNonPatchEscapedTemplateMarkers = (text) => text.replace(/\\{2,}(?=`|\$\{)/g, "\\").replace(/(^|\s)\\+(?=\.[A-Za-z0-9_-]+\/)/g, "$1");
216786
216862
  const escapeTemplateLiteralContent = (text) => {
216787
- const normalized = normalizePatchEscapedQuotes(text);
216788
- if (!needsTemplateEscaping(normalized)) return normalized;
216863
+ const normalizedPatchQuotes = normalizePatchEscapedQuotes(text);
216864
+ const isPatchContent = normalizedPatchQuotes.includes("*** Begin Patch");
216865
+ const normalized = isPatchContent ? normalizedPatchQuotes : normalizeNonPatchEscapedTemplateMarkers(normalizedPatchQuotes);
216866
+ if (!needsTemplateEscaping(normalized) && !(isPatchContent && normalized.includes("\\"))) return normalized;
216789
216867
  let out = "";
216790
216868
  for (let i = 0; i < normalized.length; i++) {
216791
216869
  const char = normalized[i];
216792
216870
  if (char === "\\") {
216871
+ if (!isPatchContent && (normalized[i + 1] === "`" || normalized[i + 1] === "$" && normalized[i + 2] === "{")) {
216872
+ out += "\\";
216873
+ continue;
216874
+ }
216793
216875
  out += "\\\\";
216794
216876
  continue;
216795
216877
  }
@@ -216809,6 +216891,10 @@ const findTemplateEnd = (text, start, isTerminator) => {
216809
216891
  let end = -1;
216810
216892
  for (let i = start + 1; i < text.length; i++) {
216811
216893
  if (text[i] !== "`" || isEscaped(text, i)) continue;
216894
+ if (isTerminator(text[i + 1])) {
216895
+ end = i;
216896
+ continue;
216897
+ }
216812
216898
  if (isTerminator(text[skipWhitespace(text, i + 1)])) end = i;
216813
216899
  }
216814
216900
  return end;
@@ -216876,47 +216962,25 @@ const fixCallTemplateArgument = (script, functionName) => {
216876
216962
  }
216877
216963
  return out;
216878
216964
  };
216879
- const collectCallArgumentIdentifiers = (script, functionName) => {
216880
- const out = /* @__PURE__ */ new Set();
216881
- let cursor = 0;
216882
- while (cursor < script.length) {
216883
- const callStart = findNextIdentifier(script, functionName, cursor);
216884
- if (callStart === -1) break;
216885
- const openParen = skipWhitespace(script, callStart + functionName.length);
216886
- if (script[openParen] !== "(") {
216887
- cursor = callStart + functionName.length;
216888
- continue;
216889
- }
216890
- const identifier = parseIdentifier(script, skipWhitespace(script, openParen + 1));
216891
- if (identifier === void 0) {
216892
- cursor = openParen + 1;
216893
- continue;
216894
- }
216895
- const argumentEnd = skipWhitespace(script, identifier.end);
216896
- if (script[argumentEnd] === ")" || script[argumentEnd] === ",") out.add(identifier.name);
216897
- cursor = identifier.end;
216898
- }
216899
- return out;
216900
- };
216901
- const fixWriteFileContentTemplates = (script) => {
216965
+ const fixCallObjectPropertyTemplate = (script, functionName, propertyName) => {
216902
216966
  let out = script;
216903
216967
  let cursor = 0;
216904
216968
  while (cursor < out.length) {
216905
- const callStart = findNextIdentifier(out, "writeFile", cursor);
216969
+ const callStart = findNextIdentifier(out, functionName, cursor);
216906
216970
  if (callStart === -1) break;
216907
- const openParen = skipWhitespace(out, callStart + 9);
216971
+ const openParen = skipWhitespace(out, callStart + functionName.length);
216908
216972
  if (out[openParen] !== "(") {
216909
- cursor = callStart + 9;
216973
+ cursor = callStart + functionName.length;
216910
216974
  continue;
216911
216975
  }
216912
- const contentKey = findNextIdentifier(out, "content", openParen + 1);
216913
- if (contentKey === -1) {
216976
+ const propertyKey = findNextIdentifier(out, propertyName, openParen + 1);
216977
+ if (propertyKey === -1) {
216914
216978
  cursor = openParen + 1;
216915
216979
  continue;
216916
216980
  }
216917
- const colon = skipWhitespace(out, contentKey + 7);
216981
+ const colon = skipWhitespace(out, propertyKey + propertyName.length);
216918
216982
  if (out[colon] !== ":") {
216919
- cursor = contentKey + 7;
216983
+ cursor = propertyKey + propertyName.length;
216920
216984
  continue;
216921
216985
  }
216922
216986
  const templateStart = skipWhitespace(out, colon + 1);
@@ -216940,42 +217004,63 @@ const fixWriteFileContentTemplates = (script) => {
216940
217004
  }
216941
217005
  return out;
216942
217006
  };
216943
- const collectWriteFileContentIdentifiers = (script) => {
217007
+ const collectCallArgumentIdentifiers = (script, functionName) => {
216944
217008
  const out = /* @__PURE__ */ new Set();
216945
217009
  let cursor = 0;
216946
217010
  while (cursor < script.length) {
216947
- const callStart = findNextIdentifier(script, "writeFile", cursor);
217011
+ const callStart = findNextIdentifier(script, functionName, cursor);
216948
217012
  if (callStart === -1) break;
216949
- const openParen = skipWhitespace(script, callStart + 9);
217013
+ const openParen = skipWhitespace(script, callStart + functionName.length);
216950
217014
  if (script[openParen] !== "(") {
216951
- cursor = callStart + 9;
217015
+ cursor = callStart + functionName.length;
216952
217016
  continue;
216953
217017
  }
216954
- const contentKey = findNextIdentifier(script, "content", openParen + 1);
216955
- if (contentKey === -1) {
217018
+ const identifier = parseIdentifier(script, skipWhitespace(script, openParen + 1));
217019
+ if (identifier === void 0) {
216956
217020
  cursor = openParen + 1;
216957
217021
  continue;
216958
217022
  }
216959
- const afterContent = skipWhitespace(script, contentKey + 7);
216960
- if (script[afterContent] === ":") {
216961
- const valueStart = skipWhitespace(script, afterContent + 1);
216962
- const identifier = parseIdentifier(script, valueStart);
216963
- if (identifier !== void 0) {
216964
- const valueEnd = skipWhitespace(script, identifier.end);
216965
- if (script[valueEnd] === "}" || script[valueEnd] === ",") out.add(identifier.name);
216966
- }
217023
+ const argumentEnd = skipWhitespace(script, identifier.end);
217024
+ if (script[argumentEnd] === ")" || script[argumentEnd] === ",") out.add(identifier.name);
217025
+ cursor = identifier.end;
217026
+ }
217027
+ return out;
217028
+ };
217029
+ const collectCallObjectPropertyIdentifiers = (script, functionName, propertyName) => {
217030
+ const out = /* @__PURE__ */ new Set();
217031
+ let cursor = 0;
217032
+ while (cursor < script.length) {
217033
+ const callStart = findNextIdentifier(script, functionName, cursor);
217034
+ if (callStart === -1) break;
217035
+ const openParen = skipWhitespace(script, callStart + functionName.length);
217036
+ if (script[openParen] !== "(") {
217037
+ cursor = callStart + functionName.length;
217038
+ continue;
217039
+ }
217040
+ const propertyKey = findNextIdentifier(script, propertyName, openParen + 1);
217041
+ if (propertyKey === -1) {
217042
+ cursor = openParen + 1;
217043
+ continue;
217044
+ }
217045
+ const afterProperty = skipWhitespace(script, propertyKey + propertyName.length);
217046
+ if (script[afterProperty] === ":") {
217047
+ const valueStart = skipWhitespace(script, afterProperty + 1);
217048
+ const valueEnd = findObjectValueTerminator(script, valueStart);
217049
+ if (valueEnd !== -1) for (const identifier of collectExpressionIdentifiers(script, valueStart, valueEnd)) out.add(identifier);
216967
217050
  cursor = valueStart + 1;
216968
217051
  continue;
216969
217052
  }
216970
- if (script[afterContent] === "}" || script[afterContent] === ",") {
216971
- out.add("content");
216972
- cursor = afterContent + 1;
217053
+ if (script[afterProperty] === "}" || script[afterProperty] === ",") {
217054
+ out.add(propertyName);
217055
+ cursor = afterProperty + 1;
216973
217056
  continue;
216974
217057
  }
216975
- cursor = afterContent + 1;
217058
+ cursor = afterProperty + 1;
216976
217059
  }
216977
217060
  return out;
216978
217061
  };
217062
+ const callObjectPropertyTargets = [["writeFile", "content"], ["updateTask", "description"]];
217063
+ const fixTargetCallObjectPropertyTemplates = (script) => callObjectPropertyTargets.reduce((current, [functionName, propertyName]) => fixCallObjectPropertyTemplate(current, functionName, propertyName), script);
216979
217064
  const fixAssignedTemplate = (script, variableName) => {
216980
217065
  let out = script;
216981
217066
  let cursor = 0;
@@ -216999,7 +217084,7 @@ const fixAssignedTemplate = (script, variableName) => {
216999
217084
  cursor = templateStart + 1;
217000
217085
  continue;
217001
217086
  }
217002
- const templateEnd = findTemplateEnd(out, templateStart, (char) => char === void 0 || char === ";" || char === "," || char === ")" || char === "}" || char === "]");
217087
+ const templateEnd = findTemplateEnd(out, templateStart, (char) => char === void 0 || char === "\n" || char === "\r" || char === ";" || char === "," || char === ")" || char === "}" || char === "]");
217003
217088
  if (templateEnd === -1) {
217004
217089
  cursor = templateStart + 1;
217005
217090
  continue;
@@ -217018,15 +217103,15 @@ const fixAssignedTemplate = (script, variableName) => {
217018
217103
  const fixAssignedTemplatesForToolCalls = (script) => {
217019
217104
  const identifiers = /* @__PURE__ */ new Set();
217020
217105
  for (const functionName of ["applyPatch", "taskComplete"]) for (const identifier of collectCallArgumentIdentifiers(script, functionName)) identifiers.add(identifier);
217021
- for (const identifier of collectWriteFileContentIdentifiers(script)) identifiers.add(identifier);
217106
+ for (const [functionName, propertyName] of callObjectPropertyTargets) for (const identifier of collectCallObjectPropertyIdentifiers(script, functionName, propertyName)) identifiers.add(identifier);
217022
217107
  if (script.includes("*** Begin Patch")) identifiers.add("patch");
217023
217108
  let out = script;
217024
217109
  for (const identifier of identifiers) out = fixAssignedTemplate(out, identifier);
217025
217110
  return out;
217026
217111
  };
217027
- const preprocessScript = (script) => fixAssignedTemplatesForToolCalls(["applyPatch", "taskComplete"].reduce((current, functionName) => fixCallTemplateArgument(current, functionName), fixWriteFileContentTemplates(script)));
217112
+ const preprocessScript = (script) => fixAssignedTemplatesForToolCalls(["applyPatch", "taskComplete"].reduce((current, functionName) => fixCallTemplateArgument(current, functionName), fixTargetCallObjectPropertyTemplates(script)));
217028
217113
  //#endregion
217029
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/AgentExecutor.js
217114
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/AgentExecutor.js
217030
217115
  /**
217031
217116
  * @since 1.0.0
217032
217117
  */
@@ -217211,7 +217296,7 @@ var QueueWriteStream = class extends Writable {
217211
217296
  }
217212
217297
  };
217213
217298
  //#endregion
217214
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/ScriptExtraction.js
217299
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/ScriptExtraction.js
217215
217300
  const stripWrappingCodeFence = (script) => {
217216
217301
  const lines = script.split(/\r?\n/);
217217
217302
  if (lines.length < 2) return script;
@@ -218760,7 +218845,7 @@ const applySpanTransformer = (transformer, response, options) => {
218760
218845
  });
218761
218846
  };
218762
218847
  //#endregion
218763
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/Agent.js
218848
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/Agent.js
218764
218849
  /**
218765
218850
  * @since 1.0.0
218766
218851
  */
@@ -230090,7 +230175,7 @@ const transformToolCallParams = /* @__PURE__ */ fnUntraced(function* (tools, too
230090
230175
  })));
230091
230176
  });
230092
230177
  //#endregion
230093
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/CodexAuth.js
230178
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/CodexAuth.js
230094
230179
  /**
230095
230180
  * @since 1.0.0
230096
230181
  */
@@ -230310,7 +230395,7 @@ var CodexAuth = class CodexAuth extends Service$1()("clanka/CodexAuth") {
230310
230395
  static layerClient = this.layerClientNoDeps.pipe(provide$3(CodexAuth.layer));
230311
230396
  };
230312
230397
  //#endregion
230313
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/Codex.js
230398
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/Codex.js
230314
230399
  /**
230315
230400
  * @since 1.0.0
230316
230401
  */
@@ -231622,7 +231707,7 @@ const getUsageDetailNumber = (details, field) => {
231622
231707
  return typeof value === "number" ? value : void 0;
231623
231708
  };
231624
231709
  //#endregion
231625
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/CopilotAuth.js
231710
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/CopilotAuth.js
231626
231711
  /**
231627
231712
  * @since 1.0.0
231628
231713
  */
@@ -231813,7 +231898,7 @@ var GithubCopilotAuth = class GithubCopilotAuth extends Service$1()("clanka/Gith
231813
231898
  static layerClient = this.layerClientNoDeps.pipe(provide$3(GithubCopilotAuth.layer));
231814
231899
  };
231815
231900
  //#endregion
231816
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/Copilot.js
231901
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/Copilot.js
231817
231902
  /**
231818
231903
  * @since 1.0.0
231819
231904
  */
@@ -232233,7 +232318,7 @@ Object.defineProperties(createChalk.prototype, styles);
232233
232318
  const chalk = createChalk();
232234
232319
  createChalk({ level: stderrColor ? stderrColor.level : 0 });
232235
232320
  //#endregion
232236
- //#region node_modules/.pnpm/clanka@0.2.17_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_c137ee36ffe489921e912f65ea0addc5/node_modules/clanka/dist/OutputFormatter.js
232321
+ //#region node_modules/.pnpm/clanka@0.2.19_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_3ecf2271901c4565ca12cd64815ad5c2/node_modules/clanka/dist/OutputFormatter.js
232237
232322
  /**
232238
232323
  * @since 1.0.0
232239
232324
  */
@@ -240890,12 +240975,19 @@ const runClanka = fnUntraced(function* (options) {
240890
240975
  }, scoped$1, (effect, options) => provide$1(effect, layerLocal({
240891
240976
  directory: options.directory,
240892
240977
  tools: options.mode === "ralph" ? void 0 : options.mode === "choose" ? TaskChooseTools : TaskTools
240893
- }).pipe(merge$6(ClankaModels.get(options.model)))), provide$1([layerUndici, TaskToolsHandlers]));
240978
+ }).pipe(merge$6(ClankaModels.get(options.model))), { local: true }), provide$1([layerUndici, TaskToolsHandlers]));
240979
+ //#endregion
240980
+ //#region src/domain/CurrentTask.ts
240981
+ const CurrentTask = taggedEnum();
240894
240982
  //#endregion
240895
240983
  //#region src/Agents/worker.ts
240896
240984
  const agentWorker = fnUntraced(function* (options) {
240897
240985
  const pathService = yield* Path$1;
240898
240986
  const worktree = yield* Worktree;
240987
+ const prdFilePath = CurrentTask.$match(options.currentTask, {
240988
+ task: () => pathService.join(".lalph", "prd.yml"),
240989
+ ralph: () => void 0
240990
+ });
240899
240991
  if (!options.preset.cliAgent.command) {
240900
240992
  yield* runClanka({
240901
240993
  directory: worktree.directory,
@@ -240917,13 +241009,16 @@ ${research}`
240917
241009
  stallTimeout: options.stallTimeout,
240918
241010
  maxContext: options.maxContext,
240919
241011
  steer: options.steer,
240920
- mode: options.ralph ? "ralph" : "default"
241012
+ mode: CurrentTask.$match(options.currentTask, {
241013
+ task: () => "default",
241014
+ ralph: () => "ralph"
241015
+ })
240921
241016
  });
240922
241017
  return ExitCode(0);
240923
241018
  }
240924
241019
  return yield* pipe$1(options.preset.cliAgent.command({
240925
241020
  prompt: options.prompt,
240926
- prdFilePath: options.ralph ? void 0 : pathService.join(".lalph", "prd.yml"),
241021
+ prdFilePath,
240927
241022
  extraArgs: options.preset.extraArgs
240928
241023
  }), setCwd(worktree.directory), options.preset.withCommandPrefix).pipe(worktree.execWithStallTimeout({
240929
241024
  cliAgent: options.preset.cliAgent,
@@ -241113,12 +241208,20 @@ const agentReviewer = fnUntraced(function* (options) {
241113
241208
  const worktree = yield* Worktree;
241114
241209
  const promptGen = yield* PromptGen;
241115
241210
  const gitFlow = yield* GitFlow;
241211
+ const mode = CurrentTask.$match(options.currentTask, {
241212
+ task: () => "default",
241213
+ ralph: () => "ralph"
241214
+ });
241215
+ const system = CurrentTask.$match(options.currentTask, {
241216
+ task: () => promptGen.systemClanka(options),
241217
+ ralph: () => void 0
241218
+ });
241116
241219
  const customInstructions = yield* pipe$1(fs.readFileString(pathService.join(worktree.directory, "LALPH_REVIEW.md")), option$1);
241117
241220
  if (!options.preset.cliAgent.command) {
241118
241221
  yield* runClanka({
241119
241222
  directory: worktree.directory,
241120
241223
  model: options.preset.extraArgs.join(" "),
241121
- system: options.ralph ? void 0 : promptGen.systemClanka(options),
241224
+ system,
241122
241225
  prompt: match$10(customInstructions, {
241123
241226
  onNone: () => promptGen.promptReview({
241124
241227
  prompt: options.instructions,
@@ -241131,7 +241234,7 @@ const agentReviewer = fnUntraced(function* (options) {
241131
241234
  })
241132
241235
  }),
241133
241236
  stallTimeout: options.stallTimeout,
241134
- mode: options.ralph ? "ralph" : "default"
241237
+ mode
241135
241238
  });
241136
241239
  return ExitCode(0);
241137
241240
  }
@@ -241160,32 +241263,48 @@ const agentTimeout = fnUntraced(function* (options) {
241160
241263
  const pathService = yield* Path$1;
241161
241264
  const worktree = yield* Worktree;
241162
241265
  const promptGen = yield* PromptGen;
241266
+ const timeoutMode = CurrentTask.$match(options.currentTask, {
241267
+ task: ({ task }) => ({
241268
+ mode: "default",
241269
+ system: promptGen.systemClanka(options),
241270
+ clankaPrompt: promptGen.promptTimeoutClanka({
241271
+ taskId: task.id,
241272
+ specsDirectory: options.specsDirectory
241273
+ }),
241274
+ cliPrompt: promptGen.promptTimeout({
241275
+ taskId: task.id,
241276
+ specsDirectory: options.specsDirectory
241277
+ }),
241278
+ prdFilePath: pathService.join(".lalph", "prd.yml")
241279
+ }),
241280
+ ralph: ({ task, specFile }) => ({
241281
+ mode: "ralph",
241282
+ system: void 0,
241283
+ clankaPrompt: promptGen.promptTimeoutRalph({
241284
+ task,
241285
+ specFile
241286
+ }),
241287
+ cliPrompt: promptGen.promptTimeoutRalph({
241288
+ task,
241289
+ specFile
241290
+ }),
241291
+ prdFilePath: void 0
241292
+ })
241293
+ });
241163
241294
  if (!options.preset.cliAgent.command) {
241164
241295
  yield* runClanka({
241165
241296
  directory: worktree.directory,
241166
241297
  model: options.preset.extraArgs.join(" "),
241167
- system: options.task._tag === "ralph" ? void 0 : promptGen.systemClanka(options),
241168
- prompt: options.task._tag === "ralph" ? promptGen.promptTimeoutRalph({
241169
- task: options.task.task,
241170
- specFile: options.task.specFile
241171
- }) : promptGen.promptTimeoutClanka({
241172
- taskId: options.task.task.id,
241173
- specsDirectory: options.specsDirectory
241174
- }),
241298
+ system: timeoutMode.system,
241299
+ prompt: timeoutMode.clankaPrompt,
241175
241300
  stallTimeout: options.stallTimeout,
241176
- mode: options.task._tag === "ralph" ? "ralph" : "default"
241301
+ mode: timeoutMode.mode
241177
241302
  });
241178
241303
  return ExitCode(0);
241179
241304
  }
241180
241305
  return yield* pipe$1(options.preset.cliAgent.command({
241181
- prompt: options.task._tag === "ralph" ? promptGen.promptTimeoutRalph({
241182
- task: options.task.task,
241183
- specFile: options.task.specFile
241184
- }) : promptGen.promptTimeout({
241185
- taskId: options.task.task.id,
241186
- specsDirectory: options.specsDirectory
241187
- }),
241188
- prdFilePath: options.task._tag === "ralph" ? void 0 : pathService.join(".lalph", "prd.yml"),
241306
+ prompt: timeoutMode.cliPrompt,
241307
+ prdFilePath: timeoutMode.prdFilePath,
241189
241308
  extraArgs: options.preset.extraArgs
241190
241309
  }), setCwd(worktree.directory), options.preset.withCommandPrefix).pipe(worktree.execWithStallTimeout({
241191
241310
  cliAgent: options.preset.cliAgent,
@@ -241330,7 +241449,7 @@ const run = fnUntraced(function* (options) {
241330
241449
  prompt: instructions,
241331
241450
  research: researchResult,
241332
241451
  steer,
241333
- ralph: false
241452
+ currentTask: CurrentTask.task({ task: chosenTask.prd })
241334
241453
  }).pipe(provideService$2(CurrentTaskRef, issueRef), catchStallInReview, withSpan$1("Main.agentWorker"))}`);
241335
241454
  if (options.review) {
241336
241455
  yield* source.updateIssue({
@@ -241344,7 +241463,7 @@ const run = fnUntraced(function* (options) {
241344
241463
  stallTimeout: options.stallTimeout,
241345
241464
  preset: taskPreset,
241346
241465
  instructions,
241347
- ralph: false
241466
+ currentTask: CurrentTask.task({ task: chosenTask.prd })
241348
241467
  }).pipe(catchStallInReview, withSpan$1("Main.agentReviewer"));
241349
241468
  yield* source.updateIssue({
241350
241469
  projectId,
@@ -241356,10 +241475,7 @@ const run = fnUntraced(function* (options) {
241356
241475
  specsDirectory: options.specsDirectory,
241357
241476
  stallTimeout: options.stallTimeout,
241358
241477
  preset: taskPreset,
241359
- task: {
241360
- _tag: "task",
241361
- task: chosenTask.prd
241362
- }
241478
+ currentTask: CurrentTask.task({ task: chosenTask.prd })
241363
241479
  })), raceFirst(watchTaskState({ issueId: taskId })), as$1(false), catchTag$1("TaskStateChanged", (error) => log$1(`Task ${error.issueId} moved to ${error.state}; cancelling run.`).pipe(as$1(true))))) return;
241364
241480
  yield* gitFlow.postWork({
241365
241481
  worktree,
@@ -241409,7 +241525,10 @@ const runRalph = fnUntraced(function* (options) {
241409
241525
  prompt: instructions,
241410
241526
  research: researchResult,
241411
241527
  maxContext: options.maxContext,
241412
- ralph: true
241528
+ currentTask: CurrentTask.ralph({
241529
+ task: chosenTask,
241530
+ specFile: options.specFile
241531
+ })
241413
241532
  }).pipe(withSpan$1("Main.worker"))}`);
241414
241533
  if (options.review) {
241415
241534
  registry.update(currentWorker.state, (s) => s.transitionTo(WorkerStatus.Reviewing({ issueId: "ralph" })));
@@ -241418,18 +241537,20 @@ const runRalph = fnUntraced(function* (options) {
241418
241537
  stallTimeout: options.stallTimeout,
241419
241538
  preset,
241420
241539
  instructions,
241421
- ralph: true
241540
+ currentTask: CurrentTask.ralph({
241541
+ task: chosenTask,
241542
+ specFile: options.specFile
241543
+ })
241422
241544
  }).pipe(withSpan$1("Main.review"));
241423
241545
  }
241424
241546
  }).pipe(timeout(options.runTimeout), tapErrorTag("TimeoutError", () => agentTimeout({
241425
241547
  specsDirectory: "",
241426
241548
  stallTimeout: options.stallTimeout,
241427
241549
  preset,
241428
- task: {
241429
- _tag: "ralph",
241550
+ currentTask: CurrentTask.ralph({
241430
241551
  task: chosenTask,
241431
241552
  specFile: options.specFile
241432
- }
241553
+ })
241433
241554
  })));
241434
241555
  yield* gitFlow.postWork({
241435
241556
  worktree,
@@ -241437,35 +241558,41 @@ const runRalph = fnUntraced(function* (options) {
241437
241558
  issueId: ""
241438
241559
  });
241439
241560
  }, scoped$1, provide$1(SemanticSearchLayer.pipe(provideMerge([Prd.layerNoop, Worktree.layer])), { local: true }));
241561
+ var RalphSpecMissing = class extends TaggedError("RalphSpecMissing") {
241562
+ message = `Project "${this.projectId}" is configured with gitFlow="ralph" but is missing "ralphSpec". Run 'lalph projects edit' and set "Path to Ralph spec file".`;
241563
+ };
241440
241564
  const runProject = fnUntraced(function* (options) {
241441
241565
  const isFinite = Number.isFinite(options.iterations);
241442
241566
  const iterationsDisplay = isFinite ? options.iterations : "unlimited";
241443
241567
  const semaphore = makeUnsafe$9(options.project.concurrency);
241444
241568
  const fibers = yield* make$54();
241445
- yield* resetInProgress.pipe(withSpan$1("Main.resetInProgress"));
241446
- yield* log$1(`Executing ${iterationsDisplay} iteration(s) with concurrency ${options.project.concurrency}`);
241447
- let iterations = options.iterations;
241448
- let iteration = 0;
241449
- let quit = false;
241450
- yield* mount(activeWorkerLoggingAtom);
241451
- while (true) {
241452
- yield* semaphore.take(1);
241453
- if (quit || isFinite && iteration >= iterations) break;
241454
- const currentIteration = iteration;
241455
- const startedDeferred = yield* make$85();
241456
- let ralphDone = false;
241457
- const gitFlow = options.project.gitFlow;
241458
- const isRalph = gitFlow === "ralph";
241459
- const gitFlowLayer = gitFlow === "commit" ? GitFlowCommit : gitFlow === "ralph" ? GitFlowRalph : GitFlowPR;
241460
- const fiber = yield* checkForWork(options.project).pipe(andThen(unify(isRalph ? runRalph({
241569
+ let executionMode;
241570
+ if (options.project.gitFlow === "ralph") {
241571
+ if (!options.project.ralphSpec) return yield* new RalphSpecMissing({ projectId: options.project.id });
241572
+ executionMode = {
241573
+ _tag: "ralph",
241574
+ specFile: options.project.ralphSpec
241575
+ };
241576
+ } else executionMode = {
241577
+ _tag: "standard",
241578
+ gitFlow: options.project.gitFlow
241579
+ };
241580
+ const resolveGitFlowLayer = () => {
241581
+ if (executionMode._tag === "ralph") return GitFlowRalph;
241582
+ if (executionMode.gitFlow === "commit") return GitFlowCommit;
241583
+ return GitFlowPR;
241584
+ };
241585
+ const resolveRunEffect = (startedDeferred) => {
241586
+ if (executionMode._tag === "ralph") return runRalph({
241461
241587
  targetBranch: options.project.targetBranch,
241462
241588
  stallTimeout: options.stallTimeout,
241463
241589
  runTimeout: options.runTimeout,
241464
- maxContext: options.maxContext,
241465
241590
  review: options.project.reviewAgent,
241466
241591
  research: options.project.researchAgent,
241467
- specFile: options.project.ralphSpec
241468
- }) : run({
241592
+ specFile: executionMode.specFile,
241593
+ maxContext: options.maxContext
241594
+ });
241595
+ return run({
241469
241596
  startedDeferred,
241470
241597
  targetBranch: options.project.targetBranch,
241471
241598
  specsDirectory: options.specsDirectory,
@@ -241473,27 +241600,48 @@ const runProject = fnUntraced(function* (options) {
241473
241600
  runTimeout: options.runTimeout,
241474
241601
  review: options.project.reviewAgent,
241475
241602
  research: options.project.researchAgent
241476
- })).pipe(provide$1(gitFlowLayer, { local: true }), withWorkerState(options.project.id))), catchTags$1({
241603
+ });
241604
+ };
241605
+ const handleNoMoreWork = (currentIteration, setIterations) => {
241606
+ if (executionMode._tag === "ralph") return void_$2;
241607
+ if (isFinite) {
241608
+ setIterations(currentIteration);
241609
+ return log$1(`No more work to process, ending after ${currentIteration} iteration(s).`);
241610
+ }
241611
+ return andThen(size$3(fibers) <= 1 ? log$1("No more work to process, waiting 30 seconds...") : void_$2, sleep(seconds(30)));
241612
+ };
241613
+ yield* resetInProgress.pipe(withSpan$1("Main.resetInProgress"));
241614
+ yield* log$1(`Executing ${iterationsDisplay} iteration(s) with concurrency ${options.project.concurrency}`);
241615
+ let iterations = options.iterations;
241616
+ let iteration = 0;
241617
+ let quit = false;
241618
+ yield* mount(activeWorkerLoggingAtom);
241619
+ while (true) {
241620
+ yield* semaphore.take(1);
241621
+ if (quit || isFinite && iteration >= iterations) break;
241622
+ const currentIteration = iteration;
241623
+ const startedDeferred = yield* make$85();
241624
+ let ralphDone = false;
241625
+ const gitFlowLayer = resolveGitFlowLayer();
241626
+ const fiber = yield* checkForWork(options.project).pipe(andThen(resolveRunEffect(startedDeferred).pipe(provide$1(gitFlowLayer, { local: true }), withWorkerState(options.project.id))), catchTags$1({
241477
241627
  ChosenTaskNotFound(_error) {
241478
- if (isRalph) {
241628
+ if (executionMode._tag !== "ralph") {
241479
241629
  ralphDone = true;
241480
- return log$1(`No more work to process for Ralph, ending after ${currentIteration + 1} iteration(s).`);
241630
+ return void_$2;
241481
241631
  }
241482
- return void_$2;
241632
+ return log$1(`No more work to process for Ralph, ending after ${currentIteration + 1} iteration(s).`);
241483
241633
  },
241484
241634
  NoMoreWork(_error) {
241485
- if (isFinite) {
241486
- iterations = currentIteration;
241487
- return log$1(`No more work to process, ending after ${currentIteration} iteration(s).`);
241488
- }
241489
- return andThen(size$3(fibers) <= 1 ? log$1("No more work to process, waiting 30 seconds...") : void_$2, sleep(seconds(30)));
241635
+ return handleNoMoreWork(currentIteration, (newIterations) => {
241636
+ iterations = newIterations;
241637
+ });
241490
241638
  },
241491
241639
  QuitError(_error) {
241492
241640
  quit = true;
241493
241641
  return void_$2;
241494
241642
  }
241495
241643
  }), catchCause$1((cause) => logWarning(cause).pipe(andThen(sleep(seconds(10))))), ensuring$2(semaphore.release(1)), ensuring$2(completeWith(startedDeferred, void_$2)), run$2(fibers));
241496
- if (isRalph) {
241644
+ if (executionMode._tag === "ralph") {
241497
241645
  yield* await_(fiber);
241498
241646
  if (ralphDone) break;
241499
241647
  } else yield* _await(startedDeferred);
@@ -241844,7 +241992,7 @@ const commandEdit = make$58("edit").pipe(withDescription("Open the selected proj
241844
241992
  const commandSource = make$58("source").pipe(withDescription("Select the issue source to use (e.g. GitHub Issues or Linear). This applies to all projects."), withHandler(() => selectIssueSource), provide(Settings.layer));
241845
241993
  //#endregion
241846
241994
  //#region package.json
241847
- var version = "0.3.89";
241995
+ var version = "0.3.91";
241848
241996
  //#endregion
241849
241997
  //#region src/commands/projects/ls.ts
241850
241998
  const commandProjectsLs = make$58("ls").pipe(withDescription("List configured projects and how they run (enabled state, concurrency, branch, git flow, review agent)."), withHandler(fnUntraced(function* () {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lalph",
3
3
  "type": "module",
4
- "version": "0.3.89",
4
+ "version": "0.3.91",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -45,7 +45,7 @@
45
45
  "@octokit/plugin-rest-endpoint-methods": "^17.0.0",
46
46
  "@octokit/types": "^16.0.0",
47
47
  "@typescript/native-preview": "7.0.0-dev.20260319.1",
48
- "clanka": "^0.2.17",
48
+ "clanka": "^0.2.19",
49
49
  "concurrently": "^9.2.1",
50
50
  "effect": "https://pkg.pr.new/Effect-TS/effect-smol/effect@89c3e98",
51
51
  "husky": "^9.1.7",
@@ -6,13 +6,14 @@ import { GitFlow } from "../GitFlow.ts"
6
6
  import type { CliAgentPreset } from "../domain/CliAgentPreset.ts"
7
7
  import { ExitCode } from "effect/unstable/process/ChildProcessSpawner"
8
8
  import { runClanka } from "../Clanka.ts"
9
+ import { CurrentTask } from "../domain/CurrentTask.ts"
9
10
 
10
11
  export const agentReviewer = Effect.fnUntraced(function* (options: {
11
12
  readonly specsDirectory: string
12
13
  readonly stallTimeout: Duration.Duration
13
14
  readonly preset: CliAgentPreset
14
15
  readonly instructions: string
15
- readonly ralph: boolean
16
+ readonly currentTask: CurrentTask
16
17
  }) {
17
18
  const fs = yield* FileSystem.FileSystem
18
19
  const pathService = yield* Path.Path
@@ -20,6 +21,16 @@ export const agentReviewer = Effect.fnUntraced(function* (options: {
20
21
  const promptGen = yield* PromptGen
21
22
  const gitFlow = yield* GitFlow
22
23
 
24
+ const mode = CurrentTask.$match(options.currentTask, {
25
+ task: () => "default" as const,
26
+ ralph: () => "ralph" as const,
27
+ })
28
+
29
+ const system = CurrentTask.$match(options.currentTask, {
30
+ task: () => promptGen.systemClanka(options),
31
+ ralph: () => undefined,
32
+ })
33
+
23
34
  const customInstructions = yield* pipe(
24
35
  fs.readFileString(pathService.join(worktree.directory, "LALPH_REVIEW.md")),
25
36
  Effect.option,
@@ -30,7 +41,7 @@ export const agentReviewer = Effect.fnUntraced(function* (options: {
30
41
  yield* runClanka({
31
42
  directory: worktree.directory,
32
43
  model: options.preset.extraArgs.join(" "),
33
- system: options.ralph ? undefined : promptGen.systemClanka(options),
44
+ system,
34
45
  prompt: Option.match(customInstructions, {
35
46
  onNone: () =>
36
47
  promptGen.promptReview({
@@ -45,7 +56,7 @@ export const agentReviewer = Effect.fnUntraced(function* (options: {
45
56
  }),
46
57
  }),
47
58
  stallTimeout: options.stallTimeout,
48
- mode: options.ralph ? "ralph" : "default",
59
+ mode,
49
60
  })
50
61
  return ExitCode(0)
51
62
  }
@@ -2,71 +2,61 @@ import { Duration, Effect, Path, pipe } from "effect"
2
2
  import { PromptGen } from "../PromptGen.ts"
3
3
  import { ChildProcess } from "effect/unstable/process"
4
4
  import { Worktree } from "../Worktree.ts"
5
- import type { PrdIssue } from "../domain/PrdIssue.ts"
6
5
  import type { CliAgentPreset } from "../domain/CliAgentPreset.ts"
7
6
  import { ExitCode } from "effect/unstable/process/ChildProcessSpawner"
8
7
  import { runClanka } from "../Clanka.ts"
8
+ import { CurrentTask } from "../domain/CurrentTask.ts"
9
9
 
10
10
  export const agentTimeout = Effect.fnUntraced(function* (options: {
11
11
  readonly specsDirectory: string
12
12
  readonly stallTimeout: Duration.Duration
13
13
  readonly preset: CliAgentPreset
14
- readonly task:
15
- | {
16
- readonly _tag: "task"
17
- readonly task: PrdIssue
18
- }
19
- | {
20
- readonly _tag: "ralph"
21
- readonly task: string
22
- readonly specFile: string
23
- }
14
+ readonly currentTask: CurrentTask
24
15
  }) {
25
16
  const pathService = yield* Path.Path
26
17
  const worktree = yield* Worktree
27
18
  const promptGen = yield* PromptGen
28
19
 
20
+ const timeoutMode = CurrentTask.$match(options.currentTask, {
21
+ task: ({ task }) => ({
22
+ mode: "default" as const,
23
+ system: promptGen.systemClanka(options),
24
+ clankaPrompt: promptGen.promptTimeoutClanka({
25
+ taskId: task.id!,
26
+ specsDirectory: options.specsDirectory,
27
+ }),
28
+ cliPrompt: promptGen.promptTimeout({
29
+ taskId: task.id!,
30
+ specsDirectory: options.specsDirectory,
31
+ }),
32
+ prdFilePath: pathService.join(".lalph", "prd.yml"),
33
+ }),
34
+ ralph: ({ task, specFile }) => ({
35
+ mode: "ralph" as const,
36
+ system: undefined,
37
+ clankaPrompt: promptGen.promptTimeoutRalph({ task, specFile }),
38
+ cliPrompt: promptGen.promptTimeoutRalph({ task, specFile }),
39
+ prdFilePath: undefined,
40
+ }),
41
+ })
42
+
29
43
  // use clanka
30
44
  if (!options.preset.cliAgent.command) {
31
45
  yield* runClanka({
32
46
  directory: worktree.directory,
33
47
  model: options.preset.extraArgs.join(" "),
34
- system:
35
- options.task._tag === "ralph"
36
- ? undefined
37
- : promptGen.systemClanka(options),
38
- prompt:
39
- options.task._tag === "ralph"
40
- ? promptGen.promptTimeoutRalph({
41
- task: options.task.task,
42
- specFile: options.task.specFile,
43
- })
44
- : promptGen.promptTimeoutClanka({
45
- taskId: options.task.task.id!,
46
- specsDirectory: options.specsDirectory,
47
- }),
48
+ system: timeoutMode.system,
49
+ prompt: timeoutMode.clankaPrompt,
48
50
  stallTimeout: options.stallTimeout,
49
- mode: options.task._tag === "ralph" ? "ralph" : "default",
51
+ mode: timeoutMode.mode,
50
52
  })
51
53
  return ExitCode(0)
52
54
  }
53
55
 
54
56
  const timeoutCommand = pipe(
55
57
  options.preset.cliAgent.command({
56
- prompt:
57
- options.task._tag === "ralph"
58
- ? promptGen.promptTimeoutRalph({
59
- task: options.task.task,
60
- specFile: options.task.specFile,
61
- })
62
- : promptGen.promptTimeout({
63
- taskId: options.task.task.id!,
64
- specsDirectory: options.specsDirectory,
65
- }),
66
- prdFilePath:
67
- options.task._tag === "ralph"
68
- ? undefined
69
- : pathService.join(".lalph", "prd.yml"),
58
+ prompt: timeoutMode.cliPrompt,
59
+ prdFilePath: timeoutMode.prdFilePath,
70
60
  extraArgs: options.preset.extraArgs,
71
61
  }),
72
62
  ChildProcess.setCwd(worktree.directory),
@@ -5,6 +5,7 @@ import type { CliAgentPreset } from "../domain/CliAgentPreset.ts"
5
5
  import { runClanka } from "../Clanka.ts"
6
6
  import { ExitCode } from "effect/unstable/process/ChildProcessSpawner"
7
7
  import { Prompt } from "effect/unstable/ai"
8
+ import { CurrentTask } from "../domain/CurrentTask.ts"
8
9
 
9
10
  export const agentWorker = Effect.fnUntraced(function* (options: {
10
11
  readonly stallTimeout: Duration.Duration
@@ -14,11 +15,16 @@ export const agentWorker = Effect.fnUntraced(function* (options: {
14
15
  readonly research: Option.Option<string>
15
16
  readonly steer?: Stream.Stream<string>
16
17
  readonly maxContext?: number | undefined
17
- readonly ralph: boolean
18
+ readonly currentTask: CurrentTask
18
19
  }) {
19
20
  const pathService = yield* Path.Path
20
21
  const worktree = yield* Worktree
21
22
 
23
+ const prdFilePath = CurrentTask.$match(options.currentTask, {
24
+ task: () => pathService.join(".lalph", "prd.yml"),
25
+ ralph: () => undefined,
26
+ })
27
+
22
28
  // use clanka
23
29
  if (!options.preset.cliAgent.command) {
24
30
  yield* runClanka({
@@ -45,7 +51,10 @@ ${research}`,
45
51
  stallTimeout: options.stallTimeout,
46
52
  maxContext: options.maxContext,
47
53
  steer: options.steer,
48
- mode: options.ralph ? "ralph" : "default",
54
+ mode: CurrentTask.$match(options.currentTask, {
55
+ task: () => "default" as const,
56
+ ralph: () => "ralph" as const,
57
+ }),
49
58
  })
50
59
  return ExitCode(0)
51
60
  }
@@ -53,9 +62,7 @@ ${research}`,
53
62
  const cliCommand = pipe(
54
63
  options.preset.cliAgent.command({
55
64
  prompt: options.prompt,
56
- prdFilePath: options.ralph
57
- ? undefined
58
- : pathService.join(".lalph", "prd.yml"),
65
+ prdFilePath,
59
66
  extraArgs: options.preset.extraArgs,
60
67
  }),
61
68
  ChildProcess.setCwd(worktree.directory),
package/src/Clanka.ts CHANGED
@@ -130,6 +130,7 @@ export const runClanka = Effect.fnUntraced(
130
130
  ? TaskChooseTools
131
131
  : TaskTools,
132
132
  }).pipe(Layer.merge(ClankaModels.get(options.model))),
133
+ { local: true },
133
134
  ),
134
135
  Effect.provide([NodeHttpClient.layerUndici, TaskToolsHandlers]),
135
136
  )
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  Config,
3
+ Data,
3
4
  Deferred,
4
5
  Duration,
5
6
  Effect,
@@ -18,7 +19,6 @@ import {
18
19
  Scope,
19
20
  Semaphore,
20
21
  Stream,
21
- Unify,
22
22
  } from "effect"
23
23
  import { PromptGen } from "../PromptGen.ts"
24
24
  import { Prd } from "../Prd.ts"
@@ -66,6 +66,7 @@ import type { OutputFormatter } from "clanka"
66
66
  import { ClankaMuxerLayer, SemanticSearchLayer } from "../Clanka.ts"
67
67
  import { agentResearcher } from "../Agents/researcher.ts"
68
68
  import { agentChooserRalph } from "../Agents/chooserRalph.ts"
69
+ import { CurrentTask } from "../domain/CurrentTask.ts"
69
70
 
70
71
  // Main iteration run logic
71
72
 
@@ -268,7 +269,7 @@ const run = Effect.fnUntraced(
268
269
  prompt: instructions,
269
270
  research: researchResult,
270
271
  steer,
271
- ralph: false,
272
+ currentTask: CurrentTask.task({ task: chosenTask.prd }),
272
273
  }).pipe(
273
274
  Effect.provideService(CurrentTaskRef, issueRef),
274
275
  catchStallInReview,
@@ -295,7 +296,7 @@ const run = Effect.fnUntraced(
295
296
  stallTimeout: options.stallTimeout,
296
297
  preset: taskPreset,
297
298
  instructions,
298
- ralph: false,
299
+ currentTask: CurrentTask.task({ task: chosenTask.prd }),
299
300
  }).pipe(catchStallInReview, Effect.withSpan("Main.agentReviewer"))
300
301
 
301
302
  yield* source.updateIssue({
@@ -311,7 +312,7 @@ const run = Effect.fnUntraced(
311
312
  specsDirectory: options.specsDirectory,
312
313
  stallTimeout: options.stallTimeout,
313
314
  preset: taskPreset,
314
- task: { _tag: "task", task: chosenTask.prd },
315
+ currentTask: CurrentTask.task({ task: chosenTask.prd }),
315
316
  }),
316
317
  ),
317
318
  Effect.raceFirst(watchTaskState({ issueId: taskId })),
@@ -470,7 +471,10 @@ const runRalph = Effect.fnUntraced(
470
471
  prompt: instructions,
471
472
  research: researchResult,
472
473
  maxContext: options.maxContext,
473
- ralph: true,
474
+ currentTask: CurrentTask.ralph({
475
+ task: chosenTask,
476
+ specFile: options.specFile,
477
+ }),
474
478
  }).pipe(Effect.withSpan("Main.worker"))
475
479
  yield* Effect.log(`Agent exited with code: ${exitCode}`)
476
480
 
@@ -487,7 +491,10 @@ const runRalph = Effect.fnUntraced(
487
491
  stallTimeout: options.stallTimeout,
488
492
  preset,
489
493
  instructions,
490
- ralph: true,
494
+ currentTask: CurrentTask.ralph({
495
+ task: chosenTask,
496
+ specFile: options.specFile,
497
+ }),
491
498
  }).pipe(Effect.withSpan("Main.review"))
492
499
  }
493
500
  }).pipe(
@@ -497,7 +504,10 @@ const runRalph = Effect.fnUntraced(
497
504
  specsDirectory: "",
498
505
  stallTimeout: options.stallTimeout,
499
506
  preset,
500
- task: { _tag: "ralph", task: chosenTask, specFile: options.specFile },
507
+ currentTask: CurrentTask.ralph({
508
+ task: chosenTask,
509
+ specFile: options.specFile,
510
+ }),
501
511
  }),
502
512
  ),
503
513
  )
@@ -517,6 +527,22 @@ const runRalph = Effect.fnUntraced(
517
527
  ),
518
528
  )
519
529
 
530
+ class RalphSpecMissing extends Data.TaggedError("RalphSpecMissing")<{
531
+ readonly projectId: Project["id"]
532
+ }> {
533
+ readonly message = `Project "${this.projectId}" is configured with gitFlow="ralph" but is missing "ralphSpec". Run 'lalph projects edit' and set "Path to Ralph spec file".`
534
+ }
535
+
536
+ type ProjectExecutionMode =
537
+ | {
538
+ readonly _tag: "standard"
539
+ readonly gitFlow: "pr" | "commit"
540
+ }
541
+ | {
542
+ readonly _tag: "ralph"
543
+ readonly specFile: string
544
+ }
545
+
520
546
  const runProject = Effect.fnUntraced(
521
547
  function* (options: {
522
548
  readonly iterations: number
@@ -531,6 +557,79 @@ const runProject = Effect.fnUntraced(
531
557
  const semaphore = Semaphore.makeUnsafe(options.project.concurrency)
532
558
  const fibers = yield* FiberSet.make()
533
559
 
560
+ let executionMode: ProjectExecutionMode
561
+ if (options.project.gitFlow === "ralph") {
562
+ if (!options.project.ralphSpec) {
563
+ return yield* new RalphSpecMissing({
564
+ projectId: options.project.id,
565
+ })
566
+ }
567
+ executionMode = {
568
+ _tag: "ralph",
569
+ specFile: options.project.ralphSpec,
570
+ }
571
+ } else {
572
+ executionMode = {
573
+ _tag: "standard",
574
+ gitFlow: options.project.gitFlow,
575
+ }
576
+ }
577
+
578
+ const resolveGitFlowLayer = () => {
579
+ if (executionMode._tag === "ralph") {
580
+ return GitFlowRalph
581
+ }
582
+ if (executionMode.gitFlow === "commit") {
583
+ return GitFlowCommit
584
+ }
585
+ return GitFlowPR
586
+ }
587
+
588
+ const resolveRunEffect = (startedDeferred: Deferred.Deferred<void>) => {
589
+ if (executionMode._tag === "ralph") {
590
+ return runRalph({
591
+ targetBranch: options.project.targetBranch,
592
+ stallTimeout: options.stallTimeout,
593
+ runTimeout: options.runTimeout,
594
+ review: options.project.reviewAgent,
595
+ research: options.project.researchAgent,
596
+ specFile: executionMode.specFile,
597
+ maxContext: options.maxContext,
598
+ })
599
+ }
600
+ return run({
601
+ startedDeferred,
602
+ targetBranch: options.project.targetBranch,
603
+ specsDirectory: options.specsDirectory,
604
+ stallTimeout: options.stallTimeout,
605
+ runTimeout: options.runTimeout,
606
+ review: options.project.reviewAgent,
607
+ research: options.project.researchAgent,
608
+ })
609
+ }
610
+
611
+ const handleNoMoreWork = (
612
+ currentIteration: number,
613
+ setIterations: (iterations: number) => void,
614
+ ) => {
615
+ if (executionMode._tag === "ralph") {
616
+ return Effect.void
617
+ }
618
+ if (isFinite) {
619
+ // If we have a finite number of iterations, we exit when no more
620
+ // work is found
621
+ setIterations(currentIteration)
622
+ return Effect.log(
623
+ `No more work to process, ending after ${currentIteration} iteration(s).`,
624
+ )
625
+ }
626
+ const log =
627
+ Iterable.size(fibers) <= 1
628
+ ? Effect.log("No more work to process, waiting 30 seconds...")
629
+ : Effect.void
630
+ return Effect.andThen(log, Effect.sleep(Duration.seconds(30)))
631
+ }
632
+
534
633
  yield* resetInProgress.pipe(Effect.withSpan("Main.resetInProgress"))
535
634
 
536
635
  yield* Effect.log(
@@ -554,65 +653,28 @@ const runProject = Effect.fnUntraced(
554
653
  const startedDeferred = yield* Deferred.make<void>()
555
654
  let ralphDone = false
556
655
 
557
- const gitFlow = options.project.gitFlow
558
- const isRalph = gitFlow === "ralph"
559
- const gitFlowLayer =
560
- gitFlow === "commit"
561
- ? GitFlowCommit
562
- : gitFlow === "ralph"
563
- ? GitFlowRalph
564
- : GitFlowPR
656
+ const gitFlowLayer = resolveGitFlowLayer()
565
657
  const fiber = yield* checkForWork(options.project).pipe(
566
658
  Effect.andThen(
567
- Unify.unify(
568
- isRalph
569
- ? runRalph({
570
- targetBranch: options.project.targetBranch,
571
- stallTimeout: options.stallTimeout,
572
- runTimeout: options.runTimeout,
573
- maxContext: options.maxContext,
574
- review: options.project.reviewAgent,
575
- research: options.project.researchAgent,
576
- specFile: options.project.ralphSpec!,
577
- })
578
- : run({
579
- startedDeferred,
580
- targetBranch: options.project.targetBranch,
581
- specsDirectory: options.specsDirectory,
582
- stallTimeout: options.stallTimeout,
583
- runTimeout: options.runTimeout,
584
- review: options.project.reviewAgent,
585
- research: options.project.researchAgent,
586
- }),
587
- ).pipe(
659
+ resolveRunEffect(startedDeferred).pipe(
588
660
  Effect.provide(gitFlowLayer, { local: true }),
589
661
  withWorkerState(options.project.id),
590
662
  ),
591
663
  ),
592
664
  Effect.catchTags({
593
665
  ChosenTaskNotFound(_error) {
594
- if (isRalph) {
666
+ if (executionMode._tag !== "ralph") {
595
667
  ralphDone = true
596
- return Effect.log(
597
- `No more work to process for Ralph, ending after ${currentIteration + 1} iteration(s).`,
598
- )
668
+ return Effect.void
599
669
  }
600
- return Effect.void
670
+ return Effect.log(
671
+ `No more work to process for Ralph, ending after ${currentIteration + 1} iteration(s).`,
672
+ )
601
673
  },
602
674
  NoMoreWork(_error) {
603
- if (isFinite) {
604
- // If we have a finite number of iterations, we exit when no more
605
- // work is found
606
- iterations = currentIteration
607
- return Effect.log(
608
- `No more work to process, ending after ${currentIteration} iteration(s).`,
609
- )
610
- }
611
- const log =
612
- Iterable.size(fibers) <= 1
613
- ? Effect.log("No more work to process, waiting 30 seconds...")
614
- : Effect.void
615
- return Effect.andThen(log, Effect.sleep(Duration.seconds(30)))
675
+ return handleNoMoreWork(currentIteration, (newIterations) => {
676
+ iterations = newIterations
677
+ })
616
678
  },
617
679
  QuitError(_error) {
618
680
  quit = true
@@ -628,12 +690,14 @@ const runProject = Effect.fnUntraced(
628
690
  Effect.ensuring(Deferred.completeWith(startedDeferred, Effect.void)),
629
691
  FiberSet.run(fibers),
630
692
  )
631
- if (isRalph) {
693
+
694
+ if (executionMode._tag === "ralph") {
632
695
  yield* Fiber.await(fiber)
633
696
  if (ralphDone) break
634
697
  } else {
635
698
  yield* Deferred.await(startedDeferred)
636
699
  }
700
+
637
701
  iteration++
638
702
  }
639
703
 
@@ -0,0 +1,14 @@
1
+ import { Data } from "effect"
2
+ import type { PrdIssue } from "./PrdIssue.ts"
3
+
4
+ export type CurrentTask = Data.TaggedEnum<{
5
+ task: {
6
+ readonly task: PrdIssue
7
+ }
8
+ ralph: {
9
+ readonly task: string
10
+ readonly specFile: string
11
+ }
12
+ }>
13
+
14
+ export const CurrentTask = Data.taggedEnum<CurrentTask>()