lalph 0.3.137 → 0.3.139

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lalph",
3
3
  "type": "module",
4
- "version": "0.3.137",
4
+ "version": "0.3.139",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -20,7 +20,6 @@
20
20
  "url": "https://github.com/tim-smart/lalph.git"
21
21
  },
22
22
  "dependencies": {
23
- "better-sqlite3": "^12.10.0",
24
23
  "tree-sitter": "^0.21.1",
25
24
  "tree-sitter-javascript": "^0.23.1",
26
25
  "tree-sitter-typescript": "^0.23.2"
@@ -37,23 +36,23 @@
37
36
  "devDependencies": {
38
37
  "@changesets/changelog-github": "^0.7.0",
39
38
  "@changesets/cli": "^2.31.0",
40
- "@effect/ai-openai": "4.0.0-beta.78",
41
- "@effect/ai-openai-compat": "4.0.0-beta.79",
42
- "@effect/language-service": "^0.86.2",
43
- "@effect/platform-node": "4.0.0-beta.78",
44
- "@linear/sdk": "^86.0.0",
39
+ "@effect/ai-openai": "4.0.0-beta.94",
40
+ "@effect/ai-openai-compat": "4.0.0-beta.94",
41
+ "@effect/language-service": "^0.86.4",
42
+ "@effect/platform-node": "4.0.0-beta.94",
43
+ "@linear/sdk": "^88.0.0",
45
44
  "@octokit/plugin-rest-endpoint-methods": "^17.0.0",
46
45
  "@octokit/types": "^16.0.0",
47
- "@typescript/native-preview": "7.0.0-dev.20260610.1",
48
- "clanka": "^0.2.68",
46
+ "@typescript/native-preview": "7.0.0-dev.20260707.2",
47
+ "clanka": "^0.2.69",
49
48
  "concurrently": "^10.0.3",
50
- "effect": "4.0.0-beta.78",
49
+ "effect": "4.0.0-beta.94",
51
50
  "husky": "^9.1.7",
52
- "lint-staged": "^17.0.7",
51
+ "lint-staged": "^17.0.8",
53
52
  "octokit": "^5.0.5",
54
- "oxlint": "^1.69.0",
55
- "prettier": "^3.8.4",
56
- "tsdown": "^0.22.2",
53
+ "oxlint": "^1.73.0",
54
+ "prettier": "^3.9.4",
55
+ "tsdown": "^0.22.3",
57
56
  "typescript": "^6.0.3",
58
57
  "yaml": "^2.9.0"
59
58
  },
package/src/GitFlow.ts CHANGED
@@ -8,6 +8,7 @@ import { Atom } from "effect/unstable/reactivity"
8
8
  import { parseBranch } from "./shared/git.ts"
9
9
  import { AtomRegistry } from "effect/unstable/reactivity"
10
10
  import { CurrentProjectId } from "./Settings.ts"
11
+ import type { PrdIssue } from "./domain/PrdIssue.ts"
11
12
 
12
13
  // @effect-diagnostics-next-line leakingRequirements:off
13
14
  export class GitFlow extends Context.Service<
@@ -130,8 +131,12 @@ export const GitFlowCommit = Layer.effect(
130
131
  requiresGithubPr: false,
131
132
  branch: `lalph/worker-${workerState.id}`,
132
133
 
133
- setupInstructions: () =>
134
- `You are already on a new branch for this task. You do not need to checkout any other branches.`,
134
+ setupInstructions: ({ githubPrNumber }) =>
135
+ githubPrNumber
136
+ ? `You are already on a new branch for this task. Github PR #${githubPrNumber} has been detected.
137
+ - **REVIEW ALL FEEDBACK** in the .lalph/feedback.md file.
138
+ - Do not checkout any other branches.`
139
+ : `You are already on a new branch for this task. You do not need to checkout any other branches.`,
135
140
 
136
141
  commitInstructions: (
137
142
  options,
@@ -185,13 +190,18 @@ But you **do not** need to git push your changes or switch branches.
185
190
  const source = yield* IssueSource
186
191
  const projectId = yield* CurrentProjectId
187
192
  const issue = yield* source.findById(projectId, options.issueId)
188
- if (!issue || issue.state !== "in-review") {
193
+ let nextStatus: PrdIssue["state"] = "done"
194
+ if (!issue) {
195
+ return
196
+ } else if (issue.state === "in-progress") {
197
+ nextStatus = "todo"
198
+ } else if (issue.state !== "in-review") {
189
199
  return
190
200
  }
191
201
  yield* source.updateIssue({
192
202
  projectId,
193
203
  issueId: options.issueId,
194
- state: "done",
204
+ state: nextStatus,
195
205
  })
196
206
  }),
197
207
  })
package/src/Github/Cli.ts CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  GithubPullRequestData,
15
15
  ReviewComment,
16
16
  } from "../domain/GithubComment.ts"
17
+ import { parseBranch } from "../shared/git.ts"
17
18
 
18
19
  export class GithubCli extends Context.Service<GithubCli>()(
19
20
  "lalph/Github/Cli",
@@ -61,6 +62,28 @@ export class GithubCli extends Context.Service<GithubCli>()(
61
62
  ),
62
63
  )
63
64
 
65
+ const openPrForBranch = (targetBranch: string) => {
66
+ const branch = parseBranch(targetBranch).branch
67
+ return ChildProcess.make`gh pr list --head ${branch} --state open --limit 1 --json number,state`.pipe(
68
+ spawner.string,
69
+ Effect.flatMap(Schema.decodeEffect(OpenPullRequestsFromJson)),
70
+ Effect.option,
71
+ Effect.map(
72
+ Option.match({
73
+ onNone: () => Option.none<OpenPullRequest>(),
74
+ onSome: (prs) =>
75
+ prs.length > 0
76
+ ? Option.some(prs[0]!)
77
+ : Option.none<OpenPullRequest>(),
78
+ }),
79
+ ),
80
+ Effect.provideService(
81
+ ChildProcessSpawner.ChildProcessSpawner,
82
+ spawner,
83
+ ),
84
+ )
85
+ }
86
+
64
87
  const prFeedbackMd = (pr: number) =>
65
88
  reviewComments(pr).pipe(
66
89
  Effect.map(({ comments, reviewThreads, reviews }) => {
@@ -130,7 +153,13 @@ ${generalCommentsXml}
130
153
  }),
131
154
  )
132
155
 
133
- return { owner, repo, reviewComments, prFeedbackMd } as const
156
+ return {
157
+ owner,
158
+ repo,
159
+ reviewComments,
160
+ openPrForBranch,
161
+ prFeedbackMd,
162
+ } as const
134
163
  }),
135
164
  },
136
165
  ) {
@@ -181,6 +210,16 @@ const renderGeneralComment = (
181
210
 
182
211
  const PullRequestDataFromJson = Schema.fromJsonString(GithubPullRequestData)
183
212
 
213
+ const OpenPullRequest = Schema.Struct({
214
+ number: Schema.Finite,
215
+ state: Schema.String,
216
+ })
217
+ type OpenPullRequest = Schema.Schema.Type<typeof OpenPullRequest>
218
+
219
+ const OpenPullRequestsFromJson = Schema.fromJsonString(
220
+ Schema.Array(OpenPullRequest),
221
+ )
222
+
184
223
  const githubReviewCommentsQuery = `
185
224
  query FetchPRComments($owner: String!, $repo: String!, $pr: Int!) {
186
225
  repository(owner: $owner, name: $repo) {
@@ -110,6 +110,7 @@ export class TokenManager extends Context.Service<TokenManager>()(
110
110
  NodeHttpServer.layer(createServer, {
111
111
  port: 34338,
112
112
  disablePreemptiveShutdown: true,
113
+ gracefulShutdownTimeout: 0,
113
114
  }),
114
115
  ),
115
116
  Layer.build,
@@ -176,17 +176,26 @@ const run = Effect.fnUntraced(
176
176
 
177
177
  yield* Deferred.completeWith(options.startedDeferred, Effect.void)
178
178
 
179
- if (gitFlow.requiresGithubPr && chosenTask.githubPrNumber) {
180
- yield* worktree.exec`gh pr checkout ${chosenTask.githubPrNumber}`
181
- const feedback = yield* gh.prFeedbackMd(chosenTask.githubPrNumber)
182
- yield* fs.writeFileString(
183
- pathService.join(worktree.directory, ".lalph", "feedback.md"),
184
- feedback,
185
- )
179
+ let githubPrNumber = chosenTask.githubPrNumber ?? undefined
180
+ if (gitFlow.requiresGithubPr && githubPrNumber) {
181
+ yield* worktree.exec`gh pr checkout ${githubPrNumber}`
186
182
  } else if (gitFlow.requiresGithubPr) {
187
183
  const branchName = `lalph/${taskId.replace(/#/g, "").replace(/[^a-zA-Z0-9-_]/g, "-")}`
188
184
  yield* worktree.exec`git branch -D ${branchName}`
189
185
  yield* worktree.exec`git checkout -b ${branchName}`
186
+ } else if (Option.isSome(options.targetBranch)) {
187
+ const prState = yield* gh.openPrForBranch(options.targetBranch.value)
188
+ if (Option.isSome(prState) && prState.value.state === "OPEN") {
189
+ githubPrNumber = prState.value.number
190
+ }
191
+ }
192
+
193
+ if (githubPrNumber !== undefined) {
194
+ const feedback = yield* gh.prFeedbackMd(githubPrNumber)
195
+ yield* fs.writeFileString(
196
+ pathService.join(worktree.directory, ".lalph", "feedback.md"),
197
+ feedback,
198
+ )
190
199
  }
191
200
 
192
201
  const taskPreset = Option.getOrElse(
@@ -232,14 +241,14 @@ const run = Effect.fnUntraced(
232
241
  specsDirectory: options.specsDirectory,
233
242
  targetBranch: Option.getOrUndefined(options.targetBranch),
234
243
  task: chosenTask.prd,
235
- githubPrNumber: chosenTask.githubPrNumber ?? undefined,
244
+ githubPrNumber,
236
245
  gitFlow,
237
246
  })
238
247
  : promptGen.promptClanka({
239
248
  specsDirectory: options.specsDirectory,
240
249
  targetBranch: Option.getOrUndefined(options.targetBranch),
241
250
  task: chosenTask.prd,
242
- githubPrNumber: chosenTask.githubPrNumber ?? undefined,
251
+ githubPrNumber,
243
252
  gitFlow,
244
253
  })
245
254
 
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  Data,
3
+ Duration,
3
4
  PlatformError,
4
5
  Schema,
5
6
  SchemaTransformation,
@@ -33,6 +34,12 @@ const escapeLeadingHyphen = (text: string) => {
33
34
  return text
34
35
  }
35
36
 
37
+ const claudeInterruptOptions = {
38
+ stdin: "ignore",
39
+ killSignal: "SIGINT",
40
+ forceKillAfter: Duration.seconds(2),
41
+ } as const
42
+
36
43
  const clanka = new CliAgent({
37
44
  id: "clanka",
38
45
  name: "clanka",
@@ -137,7 +144,7 @@ ${prompt}`
137
144
  {
138
145
  stdout: "pipe",
139
146
  stderr: "pipe",
140
- stdin: "inherit",
147
+ ...claudeInterruptOptions,
141
148
  },
142
149
  ),
143
150
  outputTransformer: claudeOutputTransformer,
@@ -153,7 +160,7 @@ ${prompt}`,
153
160
  {
154
161
  stdout: "inherit",
155
162
  stderr: "inherit",
156
- stdin: "inherit",
163
+ ...claudeInterruptOptions,
157
164
  },
158
165
  ),
159
166
  })
package/src/shared/git.ts CHANGED
@@ -8,7 +8,9 @@ export const parseBranch = (
8
8
  if (!ref.startsWith("origin/") && !ref.startsWith("upstream/")) {
9
9
  return { remote: "origin", branch: ref, branchWithRemote: `origin/${ref}` }
10
10
  }
11
- const [remote, branch] = ref.split("/", 2) as [string, string]
11
+ const separator = ref.indexOf("/")
12
+ const remote = ref.slice(0, separator)
13
+ const branch = ref.slice(separator + 1)
12
14
  const branchWithRemote = `${remote}/${branch}`
13
15
  return { remote, branch, branchWithRemote } as const
14
16
  }
package/dist/cli.d.mts DELETED
@@ -1 +0,0 @@
1
- export { };