lalph 0.3.61 → 0.3.63

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
@@ -87793,6 +87793,7 @@ var Project$2 = class extends Class$1("lalph/Project")({
87793
87793
  targetBranch: Option(String$1),
87794
87794
  concurrency: Int.check(isGreaterThanOrEqualTo(1)),
87795
87795
  gitFlow: Literals(["pr", "commit"]),
87796
+ researchAgent: Boolean$2.pipe(withDecodingDefault(() => false)),
87796
87797
  reviewAgent: Boolean$2
87797
87798
  }) {};
87798
87799
  //#endregion
@@ -177948,7 +177949,7 @@ var ji = Bt, Ii = Object.assign(Qe, { sync: Bt }), zi = Ut, Bi = Object.assign(e
177948
177949
  });
177949
177950
  Ze.glob = Ze;
177950
177951
  //#endregion
177951
- //#region node_modules/.pnpm/clanka@0.1.8_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-op_c1c0d9126463b1cd141896b41f79abdf/node_modules/clanka/dist/ApplyPatch.js
177952
+ //#region node_modules/.pnpm/clanka@0.1.11_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-o_f035d538562fa33a1854fb44b7d1653e/node_modules/clanka/dist/ApplyPatch.js
177952
177953
  /**
177953
177954
  * @since 1.0.0
177954
177955
  */
@@ -192821,7 +192822,7 @@ var StreamableHTTPClientTransport = class {
192821
192822
  }
192822
192823
  };
192823
192824
  //#endregion
192824
- //#region node_modules/.pnpm/clanka@0.1.8_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-op_c1c0d9126463b1cd141896b41f79abdf/node_modules/clanka/dist/McpClient.js
192825
+ //#region node_modules/.pnpm/clanka@0.1.11_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-o_f035d538562fa33a1854fb44b7d1653e/node_modules/clanka/dist/McpClient.js
192825
192826
  /**
192826
192827
  * @since 1.0.0
192827
192828
  */
@@ -192866,7 +192867,7 @@ const layer$7 = effect$1(McpClient, gen(function* () {
192866
192867
  });
192867
192868
  }));
192868
192869
  //#endregion
192869
- //#region node_modules/.pnpm/clanka@0.1.8_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-op_c1c0d9126463b1cd141896b41f79abdf/node_modules/clanka/dist/ExaSearch.js
192870
+ //#region node_modules/.pnpm/clanka@0.1.11_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-o_f035d538562fa33a1854fb44b7d1653e/node_modules/clanka/dist/ExaSearch.js
192870
192871
  /**
192871
192872
  * @since 1.0.0
192872
192873
  */
@@ -207816,7 +207817,7 @@ var require_lib = /* @__PURE__ */ __commonJSMin$1(((exports) => {
207816
207817
  exports.impl = impl;
207817
207818
  }));
207818
207819
  //#endregion
207819
- //#region node_modules/.pnpm/clanka@0.1.8_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-op_c1c0d9126463b1cd141896b41f79abdf/node_modules/clanka/dist/WebToMarkdown.js
207820
+ //#region node_modules/.pnpm/clanka@0.1.11_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-o_f035d538562fa33a1854fb44b7d1653e/node_modules/clanka/dist/WebToMarkdown.js
207820
207821
  /**
207821
207822
  * @since 1.0.0
207822
207823
  */
@@ -210898,7 +210899,7 @@ const unsafeSecureJsonParse = (text) => {
210898
210899
  }
210899
210900
  };
210900
210901
  //#endregion
210901
- //#region node_modules/.pnpm/clanka@0.1.8_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-op_c1c0d9126463b1cd141896b41f79abdf/node_modules/clanka/dist/AgentTools.js
210902
+ //#region node_modules/.pnpm/clanka@0.1.11_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-o_f035d538562fa33a1854fb44b7d1653e/node_modules/clanka/dist/AgentTools.js
210902
210903
  /**
210903
210904
  * @since 1.0.0
210904
210905
  */
@@ -211104,7 +211105,7 @@ const AgentToolHandlersNoDeps = AgentTools.toLayer(gen(function* () {
211104
211105
  const timeout = millis(options.timeoutMs ?? 12e4);
211105
211106
  yield* logInfo(`Calling "bash"`).pipe(annotateLogs({
211106
211107
  ...options,
211107
- timeout
211108
+ timeoutMs: format$3(timeout)
211108
211109
  }));
211109
211110
  const cwd = yield* CurrentDirectory;
211110
211111
  return yield* execute(make$39("bash", ["-c", options.command], {
@@ -211112,7 +211113,7 @@ const AgentToolHandlersNoDeps = AgentTools.toLayer(gen(function* () {
211112
211113
  stdin: "ignore"
211113
211114
  })).pipe(timeoutOrElse({
211114
211115
  duration: timeout,
211115
- onTimeout: () => die$2(/* @__PURE__ */ new Error(`Command timed out after ${timeout}`))
211116
+ onTimeout: () => die$2(/* @__PURE__ */ new Error(`Command timed out after ${format$3(timeout)}`))
211116
211117
  }));
211117
211118
  }, orDie$2),
211118
211119
  gh: fn("AgentTools.gh")(function* (args) {
@@ -211248,7 +211249,7 @@ const AgentToolHandlers = AgentToolHandlersNoDeps.pipe(provide$3([layer$6, layer
211248
211249
  AgentToolHandlersNoDeps.pipe(provide$3([mock(ExaSearch)({}), mock(WebToMarkdown)({})]));
211249
211250
  var ApplyPatchError = class extends TaggedClass$1("ApplyPatchError") {};
211250
211251
  //#endregion
211251
- //#region node_modules/.pnpm/clanka@0.1.8_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-op_c1c0d9126463b1cd141896b41f79abdf/node_modules/clanka/dist/TypeBuilder.js
211252
+ //#region node_modules/.pnpm/clanka@0.1.11_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-o_f035d538562fa33a1854fb44b7d1653e/node_modules/clanka/dist/TypeBuilder.js
211252
211253
  const resolveDocumentation = resolveAt("documentation");
211253
211254
  const identifierPattern = /^[$A-Z_a-z][$0-9A-Z_a-z]*$/u;
211254
211255
  const Precedence = {
@@ -211521,7 +211522,7 @@ const render = (schema, options) => {
211521
211522
  return printNode({ text: documentation === void 0 ? rendered.text : `${renderJsDoc(documentation, 0, printerOptions)}${printerOptions.newLine}${rendered.text}` }, printerOptions);
211522
211523
  };
211523
211524
  //#endregion
211524
- //#region node_modules/.pnpm/clanka@0.1.8_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-op_c1c0d9126463b1cd141896b41f79abdf/node_modules/clanka/dist/ToolkitRenderer.js
211525
+ //#region node_modules/.pnpm/clanka@0.1.11_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-o_f035d538562fa33a1854fb44b7d1653e/node_modules/clanka/dist/ToolkitRenderer.js
211525
211526
  /**
211526
211527
  * @since 1.0.0
211527
211528
  */
@@ -211543,7 +211544,7 @@ declare function ${name}(${params}): Promise<${render(tool.successSchema)}>`);
211543
211544
  }) });
211544
211545
  };
211545
211546
  //#endregion
211546
- //#region node_modules/.pnpm/clanka@0.1.8_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-op_c1c0d9126463b1cd141896b41f79abdf/node_modules/clanka/dist/AgentExecutor.js
211547
+ //#region node_modules/.pnpm/clanka@0.1.11_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-o_f035d538562fa33a1854fb44b7d1653e/node_modules/clanka/dist/AgentExecutor.js
211547
211548
  /**
211548
211549
  * @since 1.0.0
211549
211550
  */
@@ -211697,7 +211698,7 @@ var QueueWriteStream = class extends Writable {
211697
211698
  }
211698
211699
  };
211699
211700
  //#endregion
211700
- //#region node_modules/.pnpm/clanka@0.1.8_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-op_c1c0d9126463b1cd141896b41f79abdf/node_modules/clanka/dist/ScriptExtraction.js
211701
+ //#region node_modules/.pnpm/clanka@0.1.11_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-o_f035d538562fa33a1854fb44b7d1653e/node_modules/clanka/dist/ScriptExtraction.js
211701
211702
  const stripWrappingCodeFence = (script) => {
211702
211703
  const lines = script.split(/\r?\n/);
211703
211704
  if (lines.length < 2) return script;
@@ -213080,7 +213081,7 @@ const applySpanTransformer = (transformer, response, options) => {
213080
213081
  });
213081
213082
  };
213082
213083
  //#endregion
213083
- //#region node_modules/.pnpm/clanka@0.1.8_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-op_c1c0d9126463b1cd141896b41f79abdf/node_modules/clanka/dist/Agent.js
213084
+ //#region node_modules/.pnpm/clanka@0.1.11_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-o_f035d538562fa33a1854fb44b7d1653e/node_modules/clanka/dist/Agent.js
213084
213085
  /**
213085
213086
  * @since 1.0.0
213086
213087
  */
@@ -213230,7 +213231,7 @@ ${content}
213230
213231
  const result = yield* executeScript(currentScript);
213231
213232
  update(prompt, concat([{
213232
213233
  role: modelConfig.supportsAssistantPrefill ? "assistant" : "user",
213233
- content: `Javascript output:\n\n${result}`
213234
+ content: `Console output from executing javascript code:\n\n${result}`
213234
213235
  }]));
213235
213236
  currentScript = "";
213236
213237
  }
@@ -213409,7 +213410,8 @@ ${options.agentsMd}
213409
213410
  const generateSystemTools = (toolsDts, multi) => {
213410
213411
  return `${multi ? generateSystemMulti(toolsDts) : generateSystemSingle(toolsDts)}
213411
213412
 
213412
- Here is how you would read a file:
213413
+ For example, here is how you would read a file. First you would respond with
213414
+ javascript code that uses the "readFile" function:
213413
213415
 
213414
213416
  \`\`\`
213415
213417
  const content = await readFile({
@@ -213420,10 +213422,10 @@ const content = await readFile({
213420
213422
  console.log(JSON.parse(content))
213421
213423
  \`\`\`
213422
213424
 
213423
- And the output would look like this:
213425
+ And then you will revieve back the console output:
213424
213426
 
213425
213427
  \`\`\`
213426
- Javascript output:
213428
+ Console output from executing javascript code:
213427
213429
 
213428
213430
  [22:44:53.054] INFO (#47): Calling "readFile" { path: 'package.json' }
213429
213431
  {
@@ -213446,10 +213448,12 @@ const systemToolsCommon = (toolsDts) => `- Use \`console.log\` to print any outp
213446
213448
  - Top level await is supported.
213447
213449
  - AVOID passing scripts into the "bash" function, and instead write javascript.
213448
213450
  - PREFER the "search" function over "rg" for finding information or code
213451
+ - Do as much work as possible in a single script, using \`Promise.all\` to run multiple functions in parallel.
213449
213452
  - Variables **are not shared** between executions, so you must include all necessary code in each script you execute.
213450
213453
  - Make use of the "delegate" tool to delegate exploration and small research tasks. You can delegate multiple tasks in parallel with Promise.all
213451
213454
 
213452
213455
  **When you have fully completed your task**, call the "taskComplete" function with the final output.
213456
+ DO NOT output the final result without wrapping it with "taskComplete".
213453
213457
  Make sure every detail of the task is done before calling "taskComplete".
213454
213458
 
213455
213459
  You have the following functions available to you:
@@ -224220,7 +224224,7 @@ const transformToolCallParams = /* @__PURE__ */ fnUntraced(function* (tools, too
224220
224224
  })));
224221
224225
  });
224222
224226
  //#endregion
224223
- //#region node_modules/.pnpm/clanka@0.1.8_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-op_c1c0d9126463b1cd141896b41f79abdf/node_modules/clanka/dist/CodexAuth.js
224227
+ //#region node_modules/.pnpm/clanka@0.1.11_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-o_f035d538562fa33a1854fb44b7d1653e/node_modules/clanka/dist/CodexAuth.js
224224
224228
  /**
224225
224229
  * @since 1.0.0
224226
224230
  */
@@ -224440,7 +224444,7 @@ var CodexAuth = class CodexAuth extends Service$1()("clanka/CodexAuth") {
224440
224444
  static layerClient = this.layerClientNoDeps.pipe(provide$3(CodexAuth.layer));
224441
224445
  };
224442
224446
  //#endregion
224443
- //#region node_modules/.pnpm/clanka@0.1.8_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-op_c1c0d9126463b1cd141896b41f79abdf/node_modules/clanka/dist/Codex.js
224447
+ //#region node_modules/.pnpm/clanka@0.1.11_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-o_f035d538562fa33a1854fb44b7d1653e/node_modules/clanka/dist/Codex.js
224444
224448
  /**
224445
224449
  * @since 1.0.0
224446
224450
  */
@@ -225759,7 +225763,7 @@ const getUsageDetailNumber = (details, field) => {
225759
225763
  return typeof value === "number" ? value : void 0;
225760
225764
  };
225761
225765
  //#endregion
225762
- //#region node_modules/.pnpm/clanka@0.1.8_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-op_c1c0d9126463b1cd141896b41f79abdf/node_modules/clanka/dist/CopilotAuth.js
225766
+ //#region node_modules/.pnpm/clanka@0.1.11_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-o_f035d538562fa33a1854fb44b7d1653e/node_modules/clanka/dist/CopilotAuth.js
225763
225767
  /**
225764
225768
  * @since 1.0.0
225765
225769
  */
@@ -225950,7 +225954,7 @@ var GithubCopilotAuth = class GithubCopilotAuth extends Service$1()("clanka/Gith
225950
225954
  static layerClient = this.layerClientNoDeps.pipe(provide$3(GithubCopilotAuth.layer));
225951
225955
  };
225952
225956
  //#endregion
225953
- //#region node_modules/.pnpm/clanka@0.1.8_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-op_c1c0d9126463b1cd141896b41f79abdf/node_modules/clanka/dist/Copilot.js
225957
+ //#region node_modules/.pnpm/clanka@0.1.11_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-o_f035d538562fa33a1854fb44b7d1653e/node_modules/clanka/dist/Copilot.js
225954
225958
  /**
225955
225959
  * @since 1.0.0
225956
225960
  */
@@ -226373,7 +226377,7 @@ Object.defineProperties(createChalk.prototype, styles);
226373
226377
  const chalk = createChalk();
226374
226378
  createChalk({ level: stderrColor ? stderrColor.level : 0 });
226375
226379
  //#endregion
226376
- //#region node_modules/.pnpm/clanka@0.1.8_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-op_c1c0d9126463b1cd141896b41f79abdf/node_modules/clanka/dist/OutputFormatter.js
226380
+ //#region node_modules/.pnpm/clanka@0.1.11_@effect+ai-openai-compat@4.0.0-beta.31_effect@4.0.0-beta.31__@effect+ai-o_f035d538562fa33a1854fb44b7d1653e/node_modules/clanka/dist/OutputFormatter.js
226377
226381
  /**
226378
226382
  * @since 1.0.0
226379
226383
  */
@@ -234123,6 +234127,21 @@ All steps must be done before the task can be considered complete.${options.task
234123
234127
  5. **After ${options.gitFlow.requiresGithubPr ? "pushing" : "committing"}** your changes, update current task to reflect any changes in the task state.
234124
234128
  - Rewrite the notes in the description to include only the key discoveries and information that could speed up future work on other tasks. Make sure to preserve important information such as specification file references.
234125
234129
  - If you believe the task is complete, update the \`state\` to "in-review".`;
234130
+ const promptResearch = (options) => `Your job is to gather all the necessary information and details to complete the task described below. Do not make any code changes yet, your job is just to research and gather information.
234131
+
234132
+ In the final report:
234133
+
234134
+ - Include key file names, line numbers, and code snippets that are relevant to the task.
234135
+ - Any key discoveries that will help with implementing the task.
234136
+ - Any other information that will help speed up the implementation of the task.
234137
+ - You DO NOT need to add your report to the task description, just include it in your final output.
234138
+
234139
+ # Task details
234140
+
234141
+ ID: ${options.task.id}
234142
+ Title: ${options.task.title}
234143
+
234144
+ ${options.task.description}`;
234126
234145
  const promptReview = (options) => `A previous engineer has completed a task from the instructions below.
234127
234146
 
234128
234147
  You job is to meticulously review their work to ensure it meets the task requirements,
@@ -234268,6 +234287,7 @@ Make sure to setup dependencies between the tasks using the \`blockedBy\` field.
234268
234287
  promptChooseClanka,
234269
234288
  prompt,
234270
234289
  promptClanka,
234290
+ promptResearch,
234271
234291
  promptReview,
234272
234292
  promptReviewCustom,
234273
234293
  promptTimeout,
@@ -234437,6 +234457,10 @@ const addOrUpdateProject = fnUntraced(function* (existing) {
234437
234457
  selected: existing ? existing.gitFlow === "commit" : false
234438
234458
  }]
234439
234459
  });
234460
+ const researchAgent = yield* toggle({
234461
+ message: "Enable research agent?",
234462
+ initial: existing ? existing.researchAgent : true
234463
+ });
234440
234464
  const reviewAgent = yield* toggle({
234441
234465
  message: "Enable review agent?",
234442
234466
  initial: existing ? existing.reviewAgent : true
@@ -234447,6 +234471,7 @@ const addOrUpdateProject = fnUntraced(function* (existing) {
234447
234471
  concurrency,
234448
234472
  targetBranch,
234449
234473
  gitFlow,
234474
+ researchAgent,
234450
234475
  reviewAgent
234451
234476
  });
234452
234477
  yield* Settings.set(allProjects, some$2(existing ? projects.map((p) => p.id === project.id ? project : p) : [...projects, project]));
@@ -234900,7 +234925,7 @@ const runClanka = fnUntraced(function* (options) {
234900
234925
  yield* log$1(`Received steer message: ${message}`);
234901
234926
  yield* agent.steer(message);
234902
234927
  }, fromEffectDrain)), runDrain, forkScoped);
234903
- yield* stream.pipe(runDrain, catchTag$1("AgentFinished", () => void_$1));
234928
+ return yield* stream.pipe(runDrain, as$1(""), catchTag$1("AgentFinished", (e) => succeed$3(e.summary)));
234904
234929
  }, scoped$1, (effect, options) => provide$1(effect, layerLocal({
234905
234930
  directory: options.directory,
234906
234931
  tools: options.withChoose ? TaskChooseTools : TaskTools
@@ -234915,7 +234940,19 @@ const agentWorker = fnUntraced(function* (options) {
234915
234940
  directory: worktree.directory,
234916
234941
  model: options.preset.extraArgs.join(" "),
234917
234942
  system: options.system,
234918
- prompt: options.prompt,
234943
+ prompt: match$8(options.research, {
234944
+ onNone: () => options.prompt,
234945
+ onSome: (research) => make$7([{
234946
+ role: "user",
234947
+ content: options.prompt
234948
+ }, {
234949
+ role: "user",
234950
+ content: `You have already researched the above task, **AVOID DOING MORE RESEARCH** unless information is missing. Have a bias for action.
234951
+ Here is your research report:
234952
+
234953
+ ${research}`
234954
+ }])
234955
+ }),
234919
234956
  stallTimeout: options.stallTimeout,
234920
234957
  steer: options.steer
234921
234958
  });
@@ -235160,6 +235197,20 @@ const agentTimeout = fnUntraced(function* (options) {
235160
235197
  }));
235161
235198
  });
235162
235199
  //#endregion
235200
+ //#region src/Agents/researcher.ts
235201
+ const agentResearcher = fnUntraced(function* (options) {
235202
+ const worktree = yield* Worktree;
235203
+ const promptGen = yield* PromptGen;
235204
+ if (options.preset.cliAgent.command) return none$4();
235205
+ return yield* runClanka({
235206
+ directory: worktree.directory,
235207
+ model: options.preset.extraArgs.join(" "),
235208
+ system: promptGen.systemClanka(options),
235209
+ prompt: promptGen.promptResearch({ task: options.task }),
235210
+ stallTimeout: options.stallTimeout
235211
+ }).pipe(asSome);
235212
+ });
235213
+ //#endregion
235163
235214
  //#region src/commands/root.ts
235164
235215
  const run = fnUntraced(function* (options) {
235165
235216
  const projectId = yield* CurrentProjectId;
@@ -235223,6 +235274,13 @@ const run = fnUntraced(function* (options) {
235223
235274
  }));
235224
235275
  if (yield* gen(function* () {
235225
235276
  registry.update(currentWorker.state, (s) => s.transitionTo(WorkerStatus.Working({ issueId: taskId })));
235277
+ let researchResult = none$4();
235278
+ if (options.research) researchResult = yield* agentResearcher({
235279
+ task: chosenTask.prd,
235280
+ specsDirectory: options.specsDirectory,
235281
+ stallTimeout: options.stallTimeout,
235282
+ preset: taskPreset
235283
+ });
235226
235284
  const promptGen = yield* PromptGen;
235227
235285
  const instructions = taskPreset.cliAgent.command ? promptGen.prompt({
235228
235286
  specsDirectory: options.specsDirectory,
@@ -235246,6 +235304,7 @@ const run = fnUntraced(function* (options) {
235246
235304
  stallTimeout: options.stallTimeout,
235247
235305
  preset: taskPreset,
235248
235306
  prompt: instructions,
235307
+ research: researchResult,
235249
235308
  steer
235250
235309
  }).pipe(provideService$2(CurrentTaskRef, issueRef), catchStallInReview, withSpan("Main.agentWorker"))}`);
235251
235310
  if (options.review) {
@@ -235297,7 +235356,8 @@ const runProject = fnUntraced(function* (options) {
235297
235356
  specsDirectory: options.specsDirectory,
235298
235357
  stallTimeout: options.stallTimeout,
235299
235358
  runTimeout: options.runTimeout,
235300
- review: options.project.reviewAgent
235359
+ review: options.project.reviewAgent,
235360
+ research: options.project.researchAgent
235301
235361
  }).pipe(provide$1(options.project.gitFlow === "commit" ? GitFlowCommit : GitFlowPR, { local: true }), withWorkerState(options.project.id))), catchTags$1({
235302
235362
  NoMoreWork(_error) {
235303
235363
  if (isFinite) {
@@ -235359,10 +235419,10 @@ const commandRoot = make$49("lalph", {
235359
235419
  const watchTaskState = fnUntraced(function* (options) {
235360
235420
  return yield* toStreamResult(yield* AtomRegistry, currentIssuesAtom(yield* CurrentProjectId)).pipe(retry$1(forever$1), orDie, debounce(seconds(10)), runForEach((issues) => {
235361
235421
  const issue = issues.find((entry) => entry.id === options.issueId);
235362
- if (!issue || issue.state === "in-progress" || issue.state === "in-review") return void_$1;
235422
+ if (issue?.state === "in-progress" || issue?.state === "in-review") return void_$1;
235363
235423
  return fail$6(new TaskStateChanged({
235364
235424
  issueId: options.issueId,
235365
- state: issue.state
235425
+ state: issue?.state ?? "missing"
235366
235426
  }));
235367
235427
  }), withSpan("Main.watchTaskState"));
235368
235428
  });
@@ -235650,7 +235710,7 @@ const commandEdit = make$49("edit").pipe(withDescription("Open the selected proj
235650
235710
  const commandSource = make$49("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));
235651
235711
  //#endregion
235652
235712
  //#region package.json
235653
- var version = "0.3.61";
235713
+ var version = "0.3.63";
235654
235714
  //#endregion
235655
235715
  //#region src/commands/projects/ls.ts
235656
235716
  const commandProjectsLs = make$49("ls").pipe(withDescription("List configured projects and how they run (enabled state, concurrency, branch, git flow, review agent)."), withHandler(fnUntraced(function* () {
@@ -235670,6 +235730,7 @@ const commandProjectsLs = make$49("ls").pipe(withDescription("List configured pr
235670
235730
  console.log(` Concurrency: ${project.concurrency}`);
235671
235731
  if (isSome(project.targetBranch)) console.log(` Target Branch: ${project.targetBranch.value}`);
235672
235732
  console.log(` Git flow: ${project.gitFlow === "pr" ? "Pull Request" : "Commit"}`);
235733
+ console.log(` Research agent: ${project.researchAgent ? "Enabled" : "Disabled"}`);
235673
235734
  console.log(` Review agent: ${project.reviewAgent ? "Enabled" : "Disabled"}`);
235674
235735
  console.log("");
235675
235736
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lalph",
3
3
  "type": "module",
4
- "version": "0.3.61",
4
+ "version": "0.3.63",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -29,12 +29,12 @@
29
29
  "@linear/sdk": "^77.0.0",
30
30
  "@octokit/plugin-rest-endpoint-methods": "^17.0.0",
31
31
  "@octokit/types": "^16.0.0",
32
- "@typescript/native-preview": "7.0.0-dev.20260313.1",
33
- "clanka": "^0.1.8",
32
+ "@typescript/native-preview": "7.0.0-dev.20260314.1",
33
+ "clanka": "^0.1.11",
34
34
  "concurrently": "^9.2.1",
35
35
  "effect": "4.0.0-beta.31",
36
36
  "husky": "^9.1.7",
37
- "lint-staged": "^16.3.3",
37
+ "lint-staged": "^16.4.0",
38
38
  "octokit": "^5.0.5",
39
39
  "oxlint": "^1.55.0",
40
40
  "prettier": "^3.8.1",
@@ -0,0 +1,31 @@
1
+ import { Duration, Effect, Option } from "effect"
2
+ import { PromptGen } from "../PromptGen.ts"
3
+ import { Worktree } from "../Worktree.ts"
4
+ import type { CliAgentPreset } from "../domain/CliAgentPreset.ts"
5
+ import { runClanka } from "../Clanka.ts"
6
+ import type { PrdIssue } from "../domain/PrdIssue.ts"
7
+
8
+ export const agentResearcher = Effect.fnUntraced(function* (options: {
9
+ readonly task: PrdIssue
10
+ readonly specsDirectory: string
11
+ readonly stallTimeout: Duration.Duration
12
+ readonly preset: CliAgentPreset
13
+ }) {
14
+ const worktree = yield* Worktree
15
+ const promptGen = yield* PromptGen
16
+
17
+ // use clanka
18
+ if (options.preset.cliAgent.command) {
19
+ return Option.none<string>()
20
+ }
21
+
22
+ return yield* runClanka({
23
+ directory: worktree.directory,
24
+ model: options.preset.extraArgs.join(" "),
25
+ system: promptGen.systemClanka(options),
26
+ prompt: promptGen.promptResearch({
27
+ task: options.task,
28
+ }),
29
+ stallTimeout: options.stallTimeout,
30
+ }).pipe(Effect.asSome)
31
+ })
@@ -1,15 +1,17 @@
1
- import { Duration, Effect, Path, pipe, Stream } from "effect"
1
+ import { Duration, Effect, Option, Path, pipe, Stream } from "effect"
2
2
  import { ChildProcess } from "effect/unstable/process"
3
3
  import { Worktree } from "../Worktree.ts"
4
4
  import type { CliAgentPreset } from "../domain/CliAgentPreset.ts"
5
5
  import { runClanka } from "../Clanka.ts"
6
6
  import { ExitCode } from "effect/unstable/process/ChildProcessSpawner"
7
+ import { Prompt } from "effect/unstable/ai"
7
8
 
8
9
  export const agentWorker = Effect.fnUntraced(function* (options: {
9
10
  readonly stallTimeout: Duration.Duration
10
11
  readonly preset: CliAgentPreset
11
12
  readonly system?: string
12
13
  readonly prompt: string
14
+ readonly research: Option.Option<string>
13
15
  readonly steer?: Stream.Stream<string>
14
16
  }) {
15
17
  const pathService = yield* Path.Path
@@ -21,7 +23,23 @@ export const agentWorker = Effect.fnUntraced(function* (options: {
21
23
  directory: worktree.directory,
22
24
  model: options.preset.extraArgs.join(" "),
23
25
  system: options.system,
24
- prompt: options.prompt,
26
+ prompt: Option.match(options.research, {
27
+ onNone: () => options.prompt,
28
+ onSome: (research) =>
29
+ Prompt.make([
30
+ {
31
+ role: "user",
32
+ content: options.prompt,
33
+ },
34
+ {
35
+ role: "user",
36
+ content: `You have already researched the above task, **AVOID DOING MORE RESEARCH** unless information is missing. Have a bias for action.
37
+ Here is your research report:
38
+
39
+ ${research}`,
40
+ },
41
+ ]),
42
+ }),
25
43
  stallTimeout: options.stallTimeout,
26
44
  steer: options.steer,
27
45
  })
package/src/Clanka.ts CHANGED
@@ -4,6 +4,7 @@ import { TaskChooseTools, TaskTools, TaskToolsHandlers } from "./TaskTools.ts"
4
4
  import { ClankaModels } from "./ClankaModels.ts"
5
5
  import { withStallTimeout } from "./shared/stream.ts"
6
6
  import { NodeHttpClient } from "@effect/platform-node"
7
+ import type { Prompt } from "effect/unstable/ai"
7
8
 
8
9
  export const ClankaMuxerLayer = Layer.effectDiscard(
9
10
  Effect.gen(function* () {
@@ -17,7 +18,7 @@ export const runClanka = Effect.fnUntraced(
17
18
  function* (options: {
18
19
  readonly directory: string
19
20
  readonly model: string
20
- readonly prompt: string
21
+ readonly prompt: Prompt.RawInput
21
22
  readonly system?: string | undefined
22
23
  readonly stallTimeout?: Duration.Input | undefined
23
24
  readonly steer?: Stream.Stream<string> | undefined
@@ -54,9 +55,10 @@ export const runClanka = Effect.fnUntraced(
54
55
  )
55
56
  }
56
57
 
57
- yield* stream.pipe(
58
+ return yield* stream.pipe(
58
59
  Stream.runDrain,
59
- Effect.catchTag("AgentFinished", () => Effect.void),
60
+ Effect.as(""),
61
+ Effect.catchTag("AgentFinished", (e) => Effect.succeed(e.summary)),
60
62
  )
61
63
  },
62
64
  Effect.scoped,
package/src/Projects.ts CHANGED
@@ -123,6 +123,11 @@ export const addOrUpdateProject = Effect.fnUntraced(function* (
123
123
  },
124
124
  ] as const,
125
125
  })
126
+
127
+ const researchAgent = yield* Prompt.toggle({
128
+ message: "Enable research agent?",
129
+ initial: existing ? existing.researchAgent : true,
130
+ })
126
131
  const reviewAgent = yield* Prompt.toggle({
127
132
  message: "Enable review agent?",
128
133
  initial: existing ? existing.reviewAgent : true,
@@ -134,6 +139,7 @@ export const addOrUpdateProject = Effect.fnUntraced(function* (
134
139
  concurrency,
135
140
  targetBranch,
136
141
  gitFlow,
142
+ researchAgent,
137
143
  reviewAgent,
138
144
  })
139
145
  yield* Settings.set(
package/src/PromptGen.ts CHANGED
@@ -254,6 +254,24 @@ All steps must be done before the task can be considered complete.${
254
254
  - Rewrite the notes in the description to include only the key discoveries and information that could speed up future work on other tasks. Make sure to preserve important information such as specification file references.
255
255
  - If you believe the task is complete, update the \`state\` to "in-review".`
256
256
 
257
+ const promptResearch = (options: {
258
+ readonly task: PrdIssue
259
+ }) => `Your job is to gather all the necessary information and details to complete the task described below. Do not make any code changes yet, your job is just to research and gather information.
260
+
261
+ In the final report:
262
+
263
+ - Include key file names, line numbers, and code snippets that are relevant to the task.
264
+ - Any key discoveries that will help with implementing the task.
265
+ - Any other information that will help speed up the implementation of the task.
266
+ - You DO NOT need to add your report to the task description, just include it in your final output.
267
+
268
+ # Task details
269
+
270
+ ID: ${options.task.id}
271
+ Title: ${options.task.title}
272
+
273
+ ${options.task.description}`
274
+
257
275
  const promptReview = (options: {
258
276
  readonly prompt: string
259
277
  readonly gitFlow: GitFlow["Service"]
@@ -431,6 +449,7 @@ Make sure to setup dependencies between the tasks using the \`blockedBy\` field.
431
449
  promptChooseClanka,
432
450
  prompt,
433
451
  promptClanka,
452
+ promptResearch,
434
453
  promptReview,
435
454
  promptReviewCustom,
436
455
  promptTimeout,
@@ -35,6 +35,9 @@ export const commandProjectsLs = Command.make("ls").pipe(
35
35
  console.log(
36
36
  ` Git flow: ${project.gitFlow === "pr" ? "Pull Request" : "Commit"}`,
37
37
  )
38
+ console.log(
39
+ ` Research agent: ${project.researchAgent ? "Enabled" : "Disabled"}`,
40
+ )
38
41
  console.log(
39
42
  ` Review agent: ${project.reviewAgent ? "Enabled" : "Disabled"}`,
40
43
  )
@@ -55,6 +55,7 @@ import type { PrdIssue } from "../domain/PrdIssue.ts"
55
55
  import { CurrentTaskRef } from "../TaskTools.ts"
56
56
  import type { OutputFormatter } from "clanka"
57
57
  import { ClankaMuxerLayer } from "../Clanka.ts"
58
+ import { agentResearcher } from "../Agents/researcher.ts"
58
59
 
59
60
  // Main iteration run logic
60
61
 
@@ -65,6 +66,7 @@ const run = Effect.fnUntraced(
65
66
  readonly specsDirectory: string
66
67
  readonly stallTimeout: Duration.Duration
67
68
  readonly runTimeout: Duration.Duration
69
+ readonly research: boolean
68
70
  readonly review: boolean
69
71
  }): Effect.fn.Return<
70
72
  void,
@@ -212,6 +214,16 @@ const run = Effect.fnUntraced(
212
214
  s.transitionTo(WorkerStatus.Working({ issueId: taskId })),
213
215
  )
214
216
 
217
+ let researchResult = Option.none<string>()
218
+ if (options.research) {
219
+ researchResult = yield* agentResearcher({
220
+ task: chosenTask.prd,
221
+ specsDirectory: options.specsDirectory,
222
+ stallTimeout: options.stallTimeout,
223
+ preset: taskPreset,
224
+ })
225
+ }
226
+
215
227
  const promptGen = yield* PromptGen
216
228
  const instructions = taskPreset.cliAgent.command
217
229
  ? promptGen.prompt({
@@ -243,6 +255,7 @@ const run = Effect.fnUntraced(
243
255
  stallTimeout: options.stallTimeout,
244
256
  preset: taskPreset,
245
257
  prompt: instructions,
258
+ research: researchResult,
246
259
  steer,
247
260
  }).pipe(
248
261
  Effect.provideService(CurrentTaskRef, issueRef),
@@ -352,6 +365,7 @@ const runProject = Effect.fnUntraced(
352
365
  stallTimeout: options.stallTimeout,
353
366
  runTimeout: options.runTimeout,
354
367
  review: options.project.reviewAgent,
368
+ research: options.project.researchAgent,
355
369
  }).pipe(
356
370
  Effect.provide(
357
371
  options.project.gitFlow === "commit" ? GitFlowCommit : GitFlowPR,
@@ -522,17 +536,13 @@ const watchTaskState = Effect.fnUntraced(function* (options: {
522
536
  Stream.debounce(Duration.seconds(10)),
523
537
  Stream.runForEach((issues) => {
524
538
  const issue = issues.find((entry) => entry.id === options.issueId)
525
- if (
526
- !issue ||
527
- issue.state === "in-progress" ||
528
- issue.state === "in-review"
529
- ) {
539
+ if (issue?.state === "in-progress" || issue?.state === "in-review") {
530
540
  return Effect.void
531
541
  }
532
542
  return Effect.fail(
533
543
  new TaskStateChanged({
534
544
  issueId: options.issueId,
535
- state: issue.state,
545
+ state: issue?.state ?? "missing",
536
546
  }),
537
547
  )
538
548
  }),
@@ -9,5 +9,6 @@ export class Project extends Schema.Class<Project>("lalph/Project")({
9
9
  targetBranch: Schema.Option(Schema.String),
10
10
  concurrency: Schema.Int.check(Schema.isGreaterThanOrEqualTo(1)),
11
11
  gitFlow: Schema.Literals(["pr", "commit"]),
12
+ researchAgent: Schema.Boolean.pipe(Schema.withDecodingDefault(() => false)),
12
13
  reviewAgent: Schema.Boolean,
13
14
  }) {}