lalph 0.3.89 → 0.3.90

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.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/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.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/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.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/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.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/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.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/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.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/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.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/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.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/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.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/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.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/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.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/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.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/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.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/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]);
@@ -216785,11 +216785,16 @@ const needsTemplateEscaping = (text) => {
216785
216785
  const normalizePatchEscapedQuotes = (text) => text.includes("*** Begin Patch") ? text.replace(/\\"([A-Za-z0-9_$.-]+)\\"/g, "\"$1\"") : text;
216786
216786
  const escapeTemplateLiteralContent = (text) => {
216787
216787
  const normalized = normalizePatchEscapedQuotes(text);
216788
+ const isPatchContent = normalized.includes("*** Begin Patch");
216788
216789
  if (!needsTemplateEscaping(normalized)) return normalized;
216789
216790
  let out = "";
216790
216791
  for (let i = 0; i < normalized.length; i++) {
216791
216792
  const char = normalized[i];
216792
216793
  if (char === "\\") {
216794
+ if (!isPatchContent && (normalized[i + 1] === "`" || normalized[i + 1] === "$" && normalized[i + 2] === "{")) {
216795
+ out += "\\";
216796
+ continue;
216797
+ }
216793
216798
  out += "\\\\";
216794
216799
  continue;
216795
216800
  }
@@ -216876,47 +216881,25 @@ const fixCallTemplateArgument = (script, functionName) => {
216876
216881
  }
216877
216882
  return out;
216878
216883
  };
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) => {
216884
+ const fixCallObjectPropertyTemplate = (script, functionName, propertyName) => {
216902
216885
  let out = script;
216903
216886
  let cursor = 0;
216904
216887
  while (cursor < out.length) {
216905
- const callStart = findNextIdentifier(out, "writeFile", cursor);
216888
+ const callStart = findNextIdentifier(out, functionName, cursor);
216906
216889
  if (callStart === -1) break;
216907
- const openParen = skipWhitespace(out, callStart + 9);
216890
+ const openParen = skipWhitespace(out, callStart + functionName.length);
216908
216891
  if (out[openParen] !== "(") {
216909
- cursor = callStart + 9;
216892
+ cursor = callStart + functionName.length;
216910
216893
  continue;
216911
216894
  }
216912
- const contentKey = findNextIdentifier(out, "content", openParen + 1);
216913
- if (contentKey === -1) {
216895
+ const propertyKey = findNextIdentifier(out, propertyName, openParen + 1);
216896
+ if (propertyKey === -1) {
216914
216897
  cursor = openParen + 1;
216915
216898
  continue;
216916
216899
  }
216917
- const colon = skipWhitespace(out, contentKey + 7);
216900
+ const colon = skipWhitespace(out, propertyKey + propertyName.length);
216918
216901
  if (out[colon] !== ":") {
216919
- cursor = contentKey + 7;
216902
+ cursor = propertyKey + propertyName.length;
216920
216903
  continue;
216921
216904
  }
216922
216905
  const templateStart = skipWhitespace(out, colon + 1);
@@ -216940,25 +216923,47 @@ const fixWriteFileContentTemplates = (script) => {
216940
216923
  }
216941
216924
  return out;
216942
216925
  };
216943
- const collectWriteFileContentIdentifiers = (script) => {
216926
+ const collectCallArgumentIdentifiers = (script, functionName) => {
216944
216927
  const out = /* @__PURE__ */ new Set();
216945
216928
  let cursor = 0;
216946
216929
  while (cursor < script.length) {
216947
- const callStart = findNextIdentifier(script, "writeFile", cursor);
216930
+ const callStart = findNextIdentifier(script, functionName, cursor);
216948
216931
  if (callStart === -1) break;
216949
- const openParen = skipWhitespace(script, callStart + 9);
216932
+ const openParen = skipWhitespace(script, callStart + functionName.length);
216950
216933
  if (script[openParen] !== "(") {
216951
- cursor = callStart + 9;
216934
+ cursor = callStart + functionName.length;
216952
216935
  continue;
216953
216936
  }
216954
- const contentKey = findNextIdentifier(script, "content", openParen + 1);
216955
- if (contentKey === -1) {
216937
+ const identifier = parseIdentifier(script, skipWhitespace(script, openParen + 1));
216938
+ if (identifier === void 0) {
216956
216939
  cursor = openParen + 1;
216957
216940
  continue;
216958
216941
  }
216959
- const afterContent = skipWhitespace(script, contentKey + 7);
216960
- if (script[afterContent] === ":") {
216961
- const valueStart = skipWhitespace(script, afterContent + 1);
216942
+ const argumentEnd = skipWhitespace(script, identifier.end);
216943
+ if (script[argumentEnd] === ")" || script[argumentEnd] === ",") out.add(identifier.name);
216944
+ cursor = identifier.end;
216945
+ }
216946
+ return out;
216947
+ };
216948
+ const collectCallObjectPropertyIdentifiers = (script, functionName, propertyName) => {
216949
+ const out = /* @__PURE__ */ new Set();
216950
+ let cursor = 0;
216951
+ while (cursor < script.length) {
216952
+ const callStart = findNextIdentifier(script, functionName, cursor);
216953
+ if (callStart === -1) break;
216954
+ const openParen = skipWhitespace(script, callStart + functionName.length);
216955
+ if (script[openParen] !== "(") {
216956
+ cursor = callStart + functionName.length;
216957
+ continue;
216958
+ }
216959
+ const propertyKey = findNextIdentifier(script, propertyName, openParen + 1);
216960
+ if (propertyKey === -1) {
216961
+ cursor = openParen + 1;
216962
+ continue;
216963
+ }
216964
+ const afterProperty = skipWhitespace(script, propertyKey + propertyName.length);
216965
+ if (script[afterProperty] === ":") {
216966
+ const valueStart = skipWhitespace(script, afterProperty + 1);
216962
216967
  const identifier = parseIdentifier(script, valueStart);
216963
216968
  if (identifier !== void 0) {
216964
216969
  const valueEnd = skipWhitespace(script, identifier.end);
@@ -216967,15 +216972,17 @@ const collectWriteFileContentIdentifiers = (script) => {
216967
216972
  cursor = valueStart + 1;
216968
216973
  continue;
216969
216974
  }
216970
- if (script[afterContent] === "}" || script[afterContent] === ",") {
216971
- out.add("content");
216972
- cursor = afterContent + 1;
216975
+ if (script[afterProperty] === "}" || script[afterProperty] === ",") {
216976
+ out.add(propertyName);
216977
+ cursor = afterProperty + 1;
216973
216978
  continue;
216974
216979
  }
216975
- cursor = afterContent + 1;
216980
+ cursor = afterProperty + 1;
216976
216981
  }
216977
216982
  return out;
216978
216983
  };
216984
+ const callObjectPropertyTargets = [["writeFile", "content"], ["updateTask", "description"]];
216985
+ const fixTargetCallObjectPropertyTemplates = (script) => callObjectPropertyTargets.reduce((current, [functionName, propertyName]) => fixCallObjectPropertyTemplate(current, functionName, propertyName), script);
216979
216986
  const fixAssignedTemplate = (script, variableName) => {
216980
216987
  let out = script;
216981
216988
  let cursor = 0;
@@ -217018,15 +217025,15 @@ const fixAssignedTemplate = (script, variableName) => {
217018
217025
  const fixAssignedTemplatesForToolCalls = (script) => {
217019
217026
  const identifiers = /* @__PURE__ */ new Set();
217020
217027
  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);
217028
+ for (const [functionName, propertyName] of callObjectPropertyTargets) for (const identifier of collectCallObjectPropertyIdentifiers(script, functionName, propertyName)) identifiers.add(identifier);
217022
217029
  if (script.includes("*** Begin Patch")) identifiers.add("patch");
217023
217030
  let out = script;
217024
217031
  for (const identifier of identifiers) out = fixAssignedTemplate(out, identifier);
217025
217032
  return out;
217026
217033
  };
217027
- const preprocessScript = (script) => fixAssignedTemplatesForToolCalls(["applyPatch", "taskComplete"].reduce((current, functionName) => fixCallTemplateArgument(current, functionName), fixWriteFileContentTemplates(script)));
217034
+ const preprocessScript = (script) => fixAssignedTemplatesForToolCalls(["applyPatch", "taskComplete"].reduce((current, functionName) => fixCallTemplateArgument(current, functionName), fixTargetCallObjectPropertyTemplates(script)));
217028
217035
  //#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
217036
+ //#region node_modules/.pnpm/clanka@0.2.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/node_modules/clanka/dist/AgentExecutor.js
217030
217037
  /**
217031
217038
  * @since 1.0.0
217032
217039
  */
@@ -217211,7 +217218,7 @@ var QueueWriteStream = class extends Writable {
217211
217218
  }
217212
217219
  };
217213
217220
  //#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
217221
+ //#region node_modules/.pnpm/clanka@0.2.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/node_modules/clanka/dist/ScriptExtraction.js
217215
217222
  const stripWrappingCodeFence = (script) => {
217216
217223
  const lines = script.split(/\r?\n/);
217217
217224
  if (lines.length < 2) return script;
@@ -218760,7 +218767,7 @@ const applySpanTransformer = (transformer, response, options) => {
218760
218767
  });
218761
218768
  };
218762
218769
  //#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
218770
+ //#region node_modules/.pnpm/clanka@0.2.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/node_modules/clanka/dist/Agent.js
218764
218771
  /**
218765
218772
  * @since 1.0.0
218766
218773
  */
@@ -230090,7 +230097,7 @@ const transformToolCallParams = /* @__PURE__ */ fnUntraced(function* (tools, too
230090
230097
  })));
230091
230098
  });
230092
230099
  //#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
230100
+ //#region node_modules/.pnpm/clanka@0.2.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/node_modules/clanka/dist/CodexAuth.js
230094
230101
  /**
230095
230102
  * @since 1.0.0
230096
230103
  */
@@ -230310,7 +230317,7 @@ var CodexAuth = class CodexAuth extends Service$1()("clanka/CodexAuth") {
230310
230317
  static layerClient = this.layerClientNoDeps.pipe(provide$3(CodexAuth.layer));
230311
230318
  };
230312
230319
  //#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
230320
+ //#region node_modules/.pnpm/clanka@0.2.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/node_modules/clanka/dist/Codex.js
230314
230321
  /**
230315
230322
  * @since 1.0.0
230316
230323
  */
@@ -231622,7 +231629,7 @@ const getUsageDetailNumber = (details, field) => {
231622
231629
  return typeof value === "number" ? value : void 0;
231623
231630
  };
231624
231631
  //#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
231632
+ //#region node_modules/.pnpm/clanka@0.2.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/node_modules/clanka/dist/CopilotAuth.js
231626
231633
  /**
231627
231634
  * @since 1.0.0
231628
231635
  */
@@ -231813,7 +231820,7 @@ var GithubCopilotAuth = class GithubCopilotAuth extends Service$1()("clanka/Gith
231813
231820
  static layerClient = this.layerClientNoDeps.pipe(provide$3(GithubCopilotAuth.layer));
231814
231821
  };
231815
231822
  //#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
231823
+ //#region node_modules/.pnpm/clanka@0.2.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/node_modules/clanka/dist/Copilot.js
231817
231824
  /**
231818
231825
  * @since 1.0.0
231819
231826
  */
@@ -232233,7 +232240,7 @@ Object.defineProperties(createChalk.prototype, styles);
232233
232240
  const chalk = createChalk();
232234
232241
  createChalk({ level: stderrColor ? stderrColor.level : 0 });
232235
232242
  //#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
232243
+ //#region node_modules/.pnpm/clanka@0.2.18_@effect+ai-openai-compat@4.0.0-beta.35_effect@https+++pkg.pr.new+Effect-T_500c9c5f2cf95b88472d3c06656714c8/node_modules/clanka/dist/OutputFormatter.js
232237
232244
  /**
232238
232245
  * @since 1.0.0
232239
232246
  */
@@ -240892,10 +240899,17 @@ const runClanka = fnUntraced(function* (options) {
240892
240899
  tools: options.mode === "ralph" ? void 0 : options.mode === "choose" ? TaskChooseTools : TaskTools
240893
240900
  }).pipe(merge$6(ClankaModels.get(options.model)))), provide$1([layerUndici, TaskToolsHandlers]));
240894
240901
  //#endregion
240902
+ //#region src/domain/CurrentTask.ts
240903
+ const CurrentTask = taggedEnum();
240904
+ //#endregion
240895
240905
  //#region src/Agents/worker.ts
240896
240906
  const agentWorker = fnUntraced(function* (options) {
240897
240907
  const pathService = yield* Path$1;
240898
240908
  const worktree = yield* Worktree;
240909
+ const prdFilePath = CurrentTask.$match(options.currentTask, {
240910
+ task: () => pathService.join(".lalph", "prd.yml"),
240911
+ ralph: () => void 0
240912
+ });
240899
240913
  if (!options.preset.cliAgent.command) {
240900
240914
  yield* runClanka({
240901
240915
  directory: worktree.directory,
@@ -240917,13 +240931,16 @@ ${research}`
240917
240931
  stallTimeout: options.stallTimeout,
240918
240932
  maxContext: options.maxContext,
240919
240933
  steer: options.steer,
240920
- mode: options.ralph ? "ralph" : "default"
240934
+ mode: CurrentTask.$match(options.currentTask, {
240935
+ task: () => "default",
240936
+ ralph: () => "ralph"
240937
+ })
240921
240938
  });
240922
240939
  return ExitCode(0);
240923
240940
  }
240924
240941
  return yield* pipe$1(options.preset.cliAgent.command({
240925
240942
  prompt: options.prompt,
240926
- prdFilePath: options.ralph ? void 0 : pathService.join(".lalph", "prd.yml"),
240943
+ prdFilePath,
240927
240944
  extraArgs: options.preset.extraArgs
240928
240945
  }), setCwd(worktree.directory), options.preset.withCommandPrefix).pipe(worktree.execWithStallTimeout({
240929
240946
  cliAgent: options.preset.cliAgent,
@@ -241113,12 +241130,20 @@ const agentReviewer = fnUntraced(function* (options) {
241113
241130
  const worktree = yield* Worktree;
241114
241131
  const promptGen = yield* PromptGen;
241115
241132
  const gitFlow = yield* GitFlow;
241133
+ const mode = CurrentTask.$match(options.currentTask, {
241134
+ task: () => "default",
241135
+ ralph: () => "ralph"
241136
+ });
241137
+ const system = CurrentTask.$match(options.currentTask, {
241138
+ task: () => promptGen.systemClanka(options),
241139
+ ralph: () => void 0
241140
+ });
241116
241141
  const customInstructions = yield* pipe$1(fs.readFileString(pathService.join(worktree.directory, "LALPH_REVIEW.md")), option$1);
241117
241142
  if (!options.preset.cliAgent.command) {
241118
241143
  yield* runClanka({
241119
241144
  directory: worktree.directory,
241120
241145
  model: options.preset.extraArgs.join(" "),
241121
- system: options.ralph ? void 0 : promptGen.systemClanka(options),
241146
+ system,
241122
241147
  prompt: match$10(customInstructions, {
241123
241148
  onNone: () => promptGen.promptReview({
241124
241149
  prompt: options.instructions,
@@ -241131,7 +241156,7 @@ const agentReviewer = fnUntraced(function* (options) {
241131
241156
  })
241132
241157
  }),
241133
241158
  stallTimeout: options.stallTimeout,
241134
- mode: options.ralph ? "ralph" : "default"
241159
+ mode
241135
241160
  });
241136
241161
  return ExitCode(0);
241137
241162
  }
@@ -241160,32 +241185,48 @@ const agentTimeout = fnUntraced(function* (options) {
241160
241185
  const pathService = yield* Path$1;
241161
241186
  const worktree = yield* Worktree;
241162
241187
  const promptGen = yield* PromptGen;
241188
+ const timeoutMode = CurrentTask.$match(options.currentTask, {
241189
+ task: ({ task }) => ({
241190
+ mode: "default",
241191
+ system: promptGen.systemClanka(options),
241192
+ clankaPrompt: promptGen.promptTimeoutClanka({
241193
+ taskId: task.id,
241194
+ specsDirectory: options.specsDirectory
241195
+ }),
241196
+ cliPrompt: promptGen.promptTimeout({
241197
+ taskId: task.id,
241198
+ specsDirectory: options.specsDirectory
241199
+ }),
241200
+ prdFilePath: pathService.join(".lalph", "prd.yml")
241201
+ }),
241202
+ ralph: ({ task, specFile }) => ({
241203
+ mode: "ralph",
241204
+ system: void 0,
241205
+ clankaPrompt: promptGen.promptTimeoutRalph({
241206
+ task,
241207
+ specFile
241208
+ }),
241209
+ cliPrompt: promptGen.promptTimeoutRalph({
241210
+ task,
241211
+ specFile
241212
+ }),
241213
+ prdFilePath: void 0
241214
+ })
241215
+ });
241163
241216
  if (!options.preset.cliAgent.command) {
241164
241217
  yield* runClanka({
241165
241218
  directory: worktree.directory,
241166
241219
  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
- }),
241220
+ system: timeoutMode.system,
241221
+ prompt: timeoutMode.clankaPrompt,
241175
241222
  stallTimeout: options.stallTimeout,
241176
- mode: options.task._tag === "ralph" ? "ralph" : "default"
241223
+ mode: timeoutMode.mode
241177
241224
  });
241178
241225
  return ExitCode(0);
241179
241226
  }
241180
241227
  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"),
241228
+ prompt: timeoutMode.cliPrompt,
241229
+ prdFilePath: timeoutMode.prdFilePath,
241189
241230
  extraArgs: options.preset.extraArgs
241190
241231
  }), setCwd(worktree.directory), options.preset.withCommandPrefix).pipe(worktree.execWithStallTimeout({
241191
241232
  cliAgent: options.preset.cliAgent,
@@ -241330,7 +241371,7 @@ const run = fnUntraced(function* (options) {
241330
241371
  prompt: instructions,
241331
241372
  research: researchResult,
241332
241373
  steer,
241333
- ralph: false
241374
+ currentTask: CurrentTask.task({ task: chosenTask.prd })
241334
241375
  }).pipe(provideService$2(CurrentTaskRef, issueRef), catchStallInReview, withSpan$1("Main.agentWorker"))}`);
241335
241376
  if (options.review) {
241336
241377
  yield* source.updateIssue({
@@ -241344,7 +241385,7 @@ const run = fnUntraced(function* (options) {
241344
241385
  stallTimeout: options.stallTimeout,
241345
241386
  preset: taskPreset,
241346
241387
  instructions,
241347
- ralph: false
241388
+ currentTask: CurrentTask.task({ task: chosenTask.prd })
241348
241389
  }).pipe(catchStallInReview, withSpan$1("Main.agentReviewer"));
241349
241390
  yield* source.updateIssue({
241350
241391
  projectId,
@@ -241356,10 +241397,7 @@ const run = fnUntraced(function* (options) {
241356
241397
  specsDirectory: options.specsDirectory,
241357
241398
  stallTimeout: options.stallTimeout,
241358
241399
  preset: taskPreset,
241359
- task: {
241360
- _tag: "task",
241361
- task: chosenTask.prd
241362
- }
241400
+ currentTask: CurrentTask.task({ task: chosenTask.prd })
241363
241401
  })), 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
241402
  yield* gitFlow.postWork({
241365
241403
  worktree,
@@ -241409,7 +241447,10 @@ const runRalph = fnUntraced(function* (options) {
241409
241447
  prompt: instructions,
241410
241448
  research: researchResult,
241411
241449
  maxContext: options.maxContext,
241412
- ralph: true
241450
+ currentTask: CurrentTask.ralph({
241451
+ task: chosenTask,
241452
+ specFile: options.specFile
241453
+ })
241413
241454
  }).pipe(withSpan$1("Main.worker"))}`);
241414
241455
  if (options.review) {
241415
241456
  registry.update(currentWorker.state, (s) => s.transitionTo(WorkerStatus.Reviewing({ issueId: "ralph" })));
@@ -241418,18 +241459,20 @@ const runRalph = fnUntraced(function* (options) {
241418
241459
  stallTimeout: options.stallTimeout,
241419
241460
  preset,
241420
241461
  instructions,
241421
- ralph: true
241462
+ currentTask: CurrentTask.ralph({
241463
+ task: chosenTask,
241464
+ specFile: options.specFile
241465
+ })
241422
241466
  }).pipe(withSpan$1("Main.review"));
241423
241467
  }
241424
241468
  }).pipe(timeout(options.runTimeout), tapErrorTag("TimeoutError", () => agentTimeout({
241425
241469
  specsDirectory: "",
241426
241470
  stallTimeout: options.stallTimeout,
241427
241471
  preset,
241428
- task: {
241429
- _tag: "ralph",
241472
+ currentTask: CurrentTask.ralph({
241430
241473
  task: chosenTask,
241431
241474
  specFile: options.specFile
241432
- }
241475
+ })
241433
241476
  })));
241434
241477
  yield* gitFlow.postWork({
241435
241478
  worktree,
@@ -241437,35 +241480,41 @@ const runRalph = fnUntraced(function* (options) {
241437
241480
  issueId: ""
241438
241481
  });
241439
241482
  }, scoped$1, provide$1(SemanticSearchLayer.pipe(provideMerge([Prd.layerNoop, Worktree.layer])), { local: true }));
241483
+ var RalphSpecMissing = class extends TaggedError("RalphSpecMissing") {
241484
+ message = `Project "${this.projectId}" is configured with gitFlow="ralph" but is missing "ralphSpec". Run 'lalph projects edit' and set "Path to Ralph spec file".`;
241485
+ };
241440
241486
  const runProject = fnUntraced(function* (options) {
241441
241487
  const isFinite = Number.isFinite(options.iterations);
241442
241488
  const iterationsDisplay = isFinite ? options.iterations : "unlimited";
241443
241489
  const semaphore = makeUnsafe$9(options.project.concurrency);
241444
241490
  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({
241491
+ let executionMode;
241492
+ if (options.project.gitFlow === "ralph") {
241493
+ if (!options.project.ralphSpec) return yield* new RalphSpecMissing({ projectId: options.project.id });
241494
+ executionMode = {
241495
+ _tag: "ralph",
241496
+ specFile: options.project.ralphSpec
241497
+ };
241498
+ } else executionMode = {
241499
+ _tag: "standard",
241500
+ gitFlow: options.project.gitFlow
241501
+ };
241502
+ const resolveGitFlowLayer = () => {
241503
+ if (executionMode._tag === "ralph") return GitFlowRalph;
241504
+ if (executionMode.gitFlow === "commit") return GitFlowCommit;
241505
+ return GitFlowPR;
241506
+ };
241507
+ const resolveRunEffect = (startedDeferred) => {
241508
+ if (executionMode._tag === "ralph") return runRalph({
241461
241509
  targetBranch: options.project.targetBranch,
241462
241510
  stallTimeout: options.stallTimeout,
241463
241511
  runTimeout: options.runTimeout,
241464
- maxContext: options.maxContext,
241465
241512
  review: options.project.reviewAgent,
241466
241513
  research: options.project.researchAgent,
241467
- specFile: options.project.ralphSpec
241468
- }) : run({
241514
+ specFile: executionMode.specFile,
241515
+ maxContext: options.maxContext
241516
+ });
241517
+ return run({
241469
241518
  startedDeferred,
241470
241519
  targetBranch: options.project.targetBranch,
241471
241520
  specsDirectory: options.specsDirectory,
@@ -241473,27 +241522,48 @@ const runProject = fnUntraced(function* (options) {
241473
241522
  runTimeout: options.runTimeout,
241474
241523
  review: options.project.reviewAgent,
241475
241524
  research: options.project.researchAgent
241476
- })).pipe(provide$1(gitFlowLayer, { local: true }), withWorkerState(options.project.id))), catchTags$1({
241525
+ });
241526
+ };
241527
+ const handleNoMoreWork = (currentIteration, setIterations) => {
241528
+ if (executionMode._tag === "ralph") return void_$2;
241529
+ if (isFinite) {
241530
+ setIterations(currentIteration);
241531
+ return log$1(`No more work to process, ending after ${currentIteration} iteration(s).`);
241532
+ }
241533
+ return andThen(size$3(fibers) <= 1 ? log$1("No more work to process, waiting 30 seconds...") : void_$2, sleep(seconds(30)));
241534
+ };
241535
+ yield* resetInProgress.pipe(withSpan$1("Main.resetInProgress"));
241536
+ yield* log$1(`Executing ${iterationsDisplay} iteration(s) with concurrency ${options.project.concurrency}`);
241537
+ let iterations = options.iterations;
241538
+ let iteration = 0;
241539
+ let quit = false;
241540
+ yield* mount(activeWorkerLoggingAtom);
241541
+ while (true) {
241542
+ yield* semaphore.take(1);
241543
+ if (quit || isFinite && iteration >= iterations) break;
241544
+ const currentIteration = iteration;
241545
+ const startedDeferred = yield* make$85();
241546
+ let ralphDone = false;
241547
+ const gitFlowLayer = resolveGitFlowLayer();
241548
+ const fiber = yield* checkForWork(options.project).pipe(andThen(resolveRunEffect(startedDeferred).pipe(provide$1(gitFlowLayer, { local: true }), withWorkerState(options.project.id))), catchTags$1({
241477
241549
  ChosenTaskNotFound(_error) {
241478
- if (isRalph) {
241550
+ if (executionMode._tag !== "ralph") {
241479
241551
  ralphDone = true;
241480
- return log$1(`No more work to process for Ralph, ending after ${currentIteration + 1} iteration(s).`);
241552
+ return void_$2;
241481
241553
  }
241482
- return void_$2;
241554
+ return log$1(`No more work to process for Ralph, ending after ${currentIteration + 1} iteration(s).`);
241483
241555
  },
241484
241556
  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)));
241557
+ return handleNoMoreWork(currentIteration, (newIterations) => {
241558
+ iterations = newIterations;
241559
+ });
241490
241560
  },
241491
241561
  QuitError(_error) {
241492
241562
  quit = true;
241493
241563
  return void_$2;
241494
241564
  }
241495
241565
  }), 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) {
241566
+ if (executionMode._tag === "ralph") {
241497
241567
  yield* await_(fiber);
241498
241568
  if (ralphDone) break;
241499
241569
  } else yield* _await(startedDeferred);
@@ -241844,7 +241914,7 @@ const commandEdit = make$58("edit").pipe(withDescription("Open the selected proj
241844
241914
  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
241915
  //#endregion
241846
241916
  //#region package.json
241847
- var version = "0.3.89";
241917
+ var version = "0.3.90";
241848
241918
  //#endregion
241849
241919
  //#region src/commands/projects/ls.ts
241850
241920
  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.90",
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.18",
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),
@@ -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>()