lalph 0.3.4 → 0.3.6

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
@@ -144005,7 +144005,11 @@ var IssuesNode = class extends Class("IssuesNode")({
144005
144005
  }) {
144006
144006
  blockedBy = this.inverseRelations.nodes.filter((relation) => relation.type === "blocks" && incompleteStates.has(relation.issue.state.type));
144007
144007
  };
144008
- const incompleteStates = new Set(["unstarted", "started"]);
144008
+ const incompleteStates = new Set([
144009
+ "backlog",
144010
+ "unstarted",
144011
+ "started"
144012
+ ]);
144009
144013
  const LinearIssueData = Struct({ issue: IssuesNode });
144010
144014
  var Issues = class extends Class("Issues")({ nodes: Array$1(IssuesNode) }) {};
144011
144015
  var LinearIssuesData = class extends Class("LinearIssuesData")({ issues: Issues }) {};
@@ -151362,13 +151366,34 @@ ${options.plan}
151362
151366
  accordingly.
151363
151367
  2. Add a detailed implementation plan to the specification, breaking down the work into
151364
151368
  smaller, manageable tasks.
151365
- 3. Start two subagents with a copy of this prompt.
151366
- - The first subagent will review the plan and provide feedback or improvements.
151367
- - The second subagent will look over the implementation plan, and ensure each task is
151368
- small, atomic and independently shippable. It also **NEEDS TO** make sure task
151369
+ 3. Start two subagents to review the plan:
151370
+ - The first subagent will recieve the following prompt:
151371
+ \`\`\`
151372
+ Your job is to thoroughly review the specification created for the request,
151373
+ recommend improvements, and ensure every detail is covered.
151374
+
151375
+ Below is the original request.
151376
+
151377
+ ---
151378
+
151379
+ {insert original prompt here}
151380
+ \`\`\`
151381
+
151382
+ - The second subagent will receive the following prompt:
151383
+ \`\`\`
151384
+ Your job is to look over the implementation plan, and ensure each task is
151385
+ small, atomic and independently shippable. You also **NEED TO** make sure task
151369
151386
  can be completed without failing validation checks (typechecks, linting, tests).
151370
151387
  If a task will only pass validations when combined with another, the subagent should
151371
151388
  combine the work into one task.
151389
+
151390
+ Below is the original request.
151391
+
151392
+ ---
151393
+
151394
+ {insert original prompt here}
151395
+ \`\`\`
151396
+
151372
151397
  4. Write the specification details to a \`.lalph/plan.json\` file using the following format:
151373
151398
  \`\`\`json
151374
151399
  {
@@ -151421,6 +151446,9 @@ ${prdNotes(options)}`;
151421
151446
  var RunnerStalled = class extends TaggedError("RunnerStalled") {
151422
151447
  message = "The runner has stalled due to inactivity.";
151423
151448
  };
151449
+ var TaskStateChanged = class extends TaggedError("TaskStateChanged") {
151450
+ message = `Task "${this.issueId}" moved to "${this.state}", cancelling run.`;
151451
+ };
151424
151452
 
151425
151453
  //#endregion
151426
151454
  //#region src/domain/WorkerState.ts
@@ -152175,7 +152203,7 @@ const run = fnUntraced(function* (options) {
152175
152203
  yield* fs.writeFileString(pathService.join(worktree.directory, ".lalph", "feedback.md"), feedback);
152176
152204
  }
152177
152205
  const taskPreset = getOrElse$1(yield* source.issueCliAgentPreset(chosenTask.prd), () => preset);
152178
- yield* gen(function* () {
152206
+ if (yield* gen(function* () {
152179
152207
  registry.update(currentWorker.state, (s) => s.transitionTo(WorkerStatus.Working({ issueId: taskId })));
152180
152208
  const instructions = (yield* PromptGen).prompt({
152181
152209
  specsDirectory: options.specsDirectory,
@@ -152204,7 +152232,7 @@ const run = fnUntraced(function* (options) {
152204
152232
  stallTimeout: options.stallTimeout,
152205
152233
  preset: taskPreset,
152206
152234
  task: chosenTask.prd
152207
- })));
152235
+ })), raceFirst(watchTaskState({ issueId: taskId })), as(false), catchTag("TaskStateChanged", (error) => log$1(`Task ${error.issueId} moved to ${error.state}; cancelling run.`).pipe(as(true))))) return;
152208
152236
  yield* gitFlow.postWork({
152209
152237
  worktree,
152210
152238
  targetBranch: getOrUndefined(options.targetBranch),
@@ -152297,6 +152325,22 @@ const commandRoot = make$35("lalph", {
152297
152325
  layer,
152298
152326
  layer$17
152299
152327
  ]))));
152328
+ const watchTaskState = fnUntraced(function* (options) {
152329
+ const registry = yield* AtomRegistry;
152330
+ const projectId = yield* CurrentProjectId;
152331
+ return yield* toStreamResult(registry, currentIssuesAtom(projectId)).pipe(runForEach((issues) => {
152332
+ const issue = issues.find((entry) => entry.id === options.issueId);
152333
+ if (!issue) return fail$4(new TaskStateChanged({
152334
+ issueId: options.issueId,
152335
+ state: "missing"
152336
+ }));
152337
+ if (issue.state === "in-progress" || issue.state === "in-review") return void_$1;
152338
+ return fail$4(new TaskStateChanged({
152339
+ issueId: options.issueId,
152340
+ state: issue.state
152341
+ }));
152342
+ }), withSpan("Main.watchTaskState"));
152343
+ });
152300
152344
 
152301
152345
  //#endregion
152302
152346
  //#region src/Agents/planner.ts
@@ -152513,7 +152557,7 @@ const commandSource = make$35("source").pipe(withDescription("Select the issue s
152513
152557
 
152514
152558
  //#endregion
152515
152559
  //#region package.json
152516
- var version = "0.3.4";
152560
+ var version = "0.3.6";
152517
152561
 
152518
152562
  //#endregion
152519
152563
  //#region src/commands/projects/ls.ts
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lalph",
3
3
  "type": "module",
4
- "version": "0.3.4",
4
+ "version": "0.3.6",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
package/src/PromptGen.ts CHANGED
@@ -255,13 +255,34 @@ ${options.plan}
255
255
  accordingly.
256
256
  2. Add a detailed implementation plan to the specification, breaking down the work into
257
257
  smaller, manageable tasks.
258
- 3. Start two subagents with a copy of this prompt.
259
- - The first subagent will review the plan and provide feedback or improvements.
260
- - The second subagent will look over the implementation plan, and ensure each task is
261
- small, atomic and independently shippable. It also **NEEDS TO** make sure task
258
+ 3. Start two subagents to review the plan:
259
+ - The first subagent will recieve the following prompt:
260
+ \`\`\`
261
+ Your job is to thoroughly review the specification created for the request,
262
+ recommend improvements, and ensure every detail is covered.
263
+
264
+ Below is the original request.
265
+
266
+ ---
267
+
268
+ {insert original prompt here}
269
+ \`\`\`
270
+
271
+ - The second subagent will receive the following prompt:
272
+ \`\`\`
273
+ Your job is to look over the implementation plan, and ensure each task is
274
+ small, atomic and independently shippable. You also **NEED TO** make sure task
262
275
  can be completed without failing validation checks (typechecks, linting, tests).
263
276
  If a task will only pass validations when combined with another, the subagent should
264
277
  combine the work into one task.
278
+
279
+ Below is the original request.
280
+
281
+ ---
282
+
283
+ {insert original prompt here}
284
+ \`\`\`
285
+
265
286
  4. Write the specification details to a \`.lalph/plan.json\` file using the following format:
266
287
  \`\`\`json
267
288
  {
@@ -8,6 +8,7 @@ import {
8
8
  Iterable,
9
9
  Option,
10
10
  Path,
11
+ Stream,
11
12
  } from "effect"
12
13
  import { PromptGen } from "../PromptGen.ts"
13
14
  import { Prd } from "../Prd.ts"
@@ -17,12 +18,13 @@ import { IssueSource } from "../IssueSource.ts"
17
18
  import {
18
19
  checkForWork,
19
20
  CurrentIssueSource,
21
+ currentIssuesAtom,
20
22
  resetInProgress,
21
23
  } from "../CurrentIssueSource.ts"
22
24
  import { GithubCli } from "../Github/Cli.ts"
23
25
  import { agentWorker } from "../Agents/worker.ts"
24
26
  import { agentChooser } from "../Agents/chooser.ts"
25
- import { RunnerStalled } from "../domain/Errors.ts"
27
+ import { RunnerStalled, TaskStateChanged } from "../domain/Errors.ts"
26
28
  import { agentReviewer } from "../Agents/reviewer.ts"
27
29
  import { agentTimeout } from "../Agents/timeout.ts"
28
30
  import { CurrentProjectId, Settings } from "../Settings.ts"
@@ -135,7 +137,7 @@ const run = Effect.fnUntraced(
135
137
  () => preset,
136
138
  )
137
139
 
138
- yield* Effect.gen(function* () {
140
+ const cancelled = yield* Effect.gen(function* () {
139
141
  //
140
142
  // 2. Work on task
141
143
  // -----------------------
@@ -185,8 +187,17 @@ const run = Effect.fnUntraced(
185
187
  task: chosenTask.prd,
186
188
  }),
187
189
  ),
190
+ Effect.raceFirst(watchTaskState({ issueId: taskId })),
191
+ Effect.as(false),
192
+ Effect.catchTag("TaskStateChanged", (error) =>
193
+ Effect.log(
194
+ `Task ${error.issueId} moved to ${error.state}; cancelling run.`,
195
+ ).pipe(Effect.as(true)),
196
+ ),
188
197
  )
189
198
 
199
+ if (cancelled) return
200
+
190
201
  yield* gitFlow.postWork({
191
202
  worktree,
192
203
  targetBranch: Option.getOrUndefined(options.targetBranch),
@@ -396,3 +407,37 @@ export const commandRoot = Command.make("lalph", {
396
407
  ),
397
408
  ),
398
409
  )
410
+
411
+ const watchTaskState = Effect.fnUntraced(function* (options: {
412
+ readonly issueId: string
413
+ }) {
414
+ const registry = yield* AtomRegistry.AtomRegistry
415
+ const projectId = yield* CurrentProjectId
416
+
417
+ return yield* AtomRegistry.toStreamResult(
418
+ registry,
419
+ currentIssuesAtom(projectId),
420
+ ).pipe(
421
+ Stream.runForEach((issues) => {
422
+ const issue = issues.find((entry) => entry.id === options.issueId)
423
+ if (!issue) {
424
+ return Effect.fail(
425
+ new TaskStateChanged({
426
+ issueId: options.issueId,
427
+ state: "missing",
428
+ }),
429
+ )
430
+ }
431
+ if (issue.state === "in-progress" || issue.state === "in-review") {
432
+ return Effect.void
433
+ }
434
+ return Effect.fail(
435
+ new TaskStateChanged({
436
+ issueId: options.issueId,
437
+ state: issue.state,
438
+ }),
439
+ )
440
+ }),
441
+ Effect.withSpan("Main.watchTaskState"),
442
+ )
443
+ })
@@ -1,5 +1,13 @@
1
1
  import { Data } from "effect"
2
+ import type { PrdIssue } from "./PrdIssue.ts"
2
3
 
3
4
  export class RunnerStalled extends Data.TaggedError("RunnerStalled") {
4
5
  readonly message = "The runner has stalled due to inactivity."
5
6
  }
7
+
8
+ export class TaskStateChanged extends Data.TaggedError("TaskStateChanged")<{
9
+ readonly issueId: string
10
+ readonly state: PrdIssue["state"] | "missing"
11
+ }> {
12
+ readonly message = `Task "${this.issueId}" moved to "${this.state}", cancelling run.`
13
+ }
@@ -54,7 +54,7 @@ export class IssuesNode extends S.Class<IssuesNode>("IssuesNode")({
54
54
  incompleteStates.has(relation.issue.state.type),
55
55
  )
56
56
  }
57
- const incompleteStates = new Set<Type>(["unstarted", "started"])
57
+ const incompleteStates = new Set<Type>(["backlog", "unstarted", "started"])
58
58
 
59
59
  export const LinearIssueData = S.Struct({
60
60
  issue: IssuesNode,