lalph 0.3.104 → 0.3.106

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.104",
4
+ "version": "0.3.106",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -37,24 +37,24 @@
37
37
  "devDependencies": {
38
38
  "@changesets/changelog-github": "^0.6.0",
39
39
  "@changesets/cli": "^2.30.0",
40
- "@effect/ai-openai": "4.0.0-beta.37",
41
- "@effect/ai-openai-compat": "4.0.0-beta.37",
42
- "@effect/language-service": "^0.81.0",
43
- "@effect/platform-node": "4.0.0-beta.37",
40
+ "@effect/ai-openai": "4.0.0-beta.38",
41
+ "@effect/ai-openai-compat": "4.0.0-beta.38",
42
+ "@effect/language-service": "^0.82.0",
43
+ "@effect/platform-node": "4.0.0-beta.38",
44
44
  "@linear/sdk": "^78.0.0",
45
45
  "@octokit/plugin-rest-endpoint-methods": "^17.0.0",
46
46
  "@octokit/types": "^16.0.0",
47
- "@typescript/native-preview": "7.0.0-dev.20260322.1",
48
- "clanka": "^0.2.33",
47
+ "@typescript/native-preview": "7.0.0-dev.20260323.1",
48
+ "clanka": "^0.2.34",
49
49
  "concurrently": "^9.2.1",
50
- "effect": "4.0.0-beta.37",
50
+ "effect": "4.0.0-beta.38",
51
51
  "husky": "^9.1.7",
52
52
  "lint-staged": "^16.4.0",
53
53
  "octokit": "^5.0.5",
54
54
  "oxlint": "^1.56.0",
55
55
  "prettier": "^3.8.1",
56
56
  "tsdown": "^0.21.4",
57
- "typescript": "^5.9.3",
57
+ "typescript": "^6.0.2",
58
58
  "yaml": "^2.8.3"
59
59
  },
60
60
  "lint-staged": {
@@ -3,6 +3,7 @@ import {
3
3
  Effect,
4
4
  Layer,
5
5
  Option,
6
+ Redacted,
6
7
  Schedule,
7
8
  Schema,
8
9
  Semaphore,
@@ -16,7 +17,9 @@ import {
16
17
  HttpClientResponse,
17
18
  } from "effect/unstable/http"
18
19
  import { KeyValueStore } from "effect/unstable/persistence"
20
+ import { Prompt } from "effect/unstable/cli"
19
21
  import { layerKvs } from "../Kvs.ts"
22
+ import type { QuitError } from "effect/Terminal"
20
23
 
21
24
  const clientId = "Ov23liJMtg6leTI1Vu6m"
22
25
 
@@ -24,6 +27,7 @@ export class TokenManager extends ServiceMap.Service<TokenManager>()(
24
27
  "lalph/Github/TokenManager",
25
28
  {
26
29
  make: Effect.gen(function* () {
30
+ const promptEnv = yield* Effect.services<Prompt.Environment>()
27
31
  const kvs = KeyValueStore.prefix(
28
32
  yield* KeyValueStore.KeyValueStore,
29
33
  "github.accessToken",
@@ -54,16 +58,26 @@ export class TokenManager extends ServiceMap.Service<TokenManager>()(
54
58
  ),
55
59
  })
56
60
 
61
+ const promptPat = Effect.gen(function* () {
62
+ return yield* Prompt.password({
63
+ message:
64
+ "GitHub PAT with repo, read:user, read:project scopes (leave empty for OAuth)",
65
+ validate: (value) => Effect.succeed(value.trim()),
66
+ })
67
+ }).pipe(Effect.provideServices(promptEnv))
68
+
57
69
  const getNoLock: Effect.Effect<
58
70
  AccessToken,
59
- HttpClientError.HttpClientError | Schema.SchemaError
71
+ HttpClientError.HttpClientError | QuitError | Schema.SchemaError
60
72
  > = Effect.gen(function* () {
61
- if (Option.isNone(currentToken)) {
62
- const newToken = yield* deviceCode
63
- yield* set(Option.some(newToken))
64
- return newToken
73
+ if (Option.isSome(currentToken)) {
74
+ return currentToken.value
65
75
  }
66
- return currentToken.value
76
+ const token = Redacted.value(yield* promptPat)
77
+ const accessToken =
78
+ token.length > 0 ? new AccessToken({ token }) : yield* deviceCode
79
+ yield* set(Option.some(accessToken))
80
+ return accessToken
67
81
  })
68
82
  const get = Semaphore.makeUnsafe(1).withPermit(getNoLock)
69
83
 
@@ -11,7 +11,7 @@ import {
11
11
  SubscriptionRef,
12
12
  pipe,
13
13
  } from "effect"
14
- import type { PrdIssue } from "./domain/PrdIssue.ts"
14
+ import { PrdIssue } from "./domain/PrdIssue.ts"
15
15
  import type { ProjectId } from "./domain/Project.ts"
16
16
  import type { CurrentProjectId, Settings } from "./Settings.ts"
17
17
  import type { CliAgentPreset } from "./domain/CliAgentPreset.ts"
@@ -127,16 +127,20 @@ export class IssueSource extends ServiceMap.Service<
127
127
 
128
128
  const update = Effect.fnUntraced(function* (
129
129
  projectId: ProjectId,
130
- issues: ReadonlyArray<PrdIssue>,
130
+ f: (_: ReadonlyArray<PrdIssue>) => ReadonlyArray<PrdIssue>,
131
131
  ) {
132
132
  const ref = yield* ScopedCache.get(refs, projectId)
133
- yield* SubscriptionRef.set(ref, IssuesChange.Internal({ issues }))
133
+ yield* SubscriptionRef.update(ref, (change) =>
134
+ IssuesChange.Internal({
135
+ issues: f(change.issues),
136
+ }),
137
+ )
134
138
  })
135
139
 
136
140
  const updateIssues = (projectId: ProjectId) =>
137
141
  pipe(
138
142
  impl.issues(projectId),
139
- Effect.tap((issues) => update(projectId, issues)),
143
+ Effect.tap((issues) => update(projectId, () => issues)),
140
144
  )
141
145
 
142
146
  return IssueSource.of({
@@ -151,17 +155,49 @@ export class IssueSource extends ServiceMap.Service<
151
155
  createIssue: (projectId, issue) =>
152
156
  pipe(
153
157
  impl.createIssue(projectId, issue),
154
- Effect.tap(updateIssues(projectId)),
158
+ Effect.tap((createdIssue) =>
159
+ update(projectId, (issues) => {
160
+ const nextIssue = issue.update({ id: createdIssue.id })
161
+ const index = issues.findIndex(
162
+ (current) => current.id === createdIssue.id,
163
+ )
164
+ if (index === -1) {
165
+ return [...issues, nextIssue]
166
+ }
167
+ return issues.map((current, i) =>
168
+ i === index ? nextIssue : current,
169
+ )
170
+ }),
171
+ ),
155
172
  ),
156
173
  updateIssue: (options) =>
157
174
  pipe(
158
175
  impl.updateIssue(options),
159
- Effect.tap(updateIssues(options.projectId)),
176
+ Effect.tap(() =>
177
+ update(options.projectId, (issues) =>
178
+ issues.map((issue) =>
179
+ issue.id === options.issueId
180
+ ? new PrdIssue({
181
+ ...issue,
182
+ title: options.title ?? issue.title,
183
+ description: options.description ?? issue.description,
184
+ state: options.state ?? issue.state,
185
+ blockedBy: options.blockedBy ?? issue.blockedBy,
186
+ autoMerge: options.autoMerge ?? issue.autoMerge,
187
+ })
188
+ : issue,
189
+ ),
190
+ ),
191
+ ),
160
192
  ),
161
193
  cancelIssue: (projectId, issueId) =>
162
194
  pipe(
163
195
  impl.cancelIssue(projectId, issueId),
164
- Effect.tap(updateIssues(projectId)),
196
+ Effect.tap(() =>
197
+ update(projectId, (issues) =>
198
+ issues.filter((issue) => issue.id !== issueId),
199
+ ),
200
+ ),
165
201
  ),
166
202
  })
167
203
  })
package/src/Prd.ts CHANGED
@@ -239,6 +239,7 @@ export class Prd extends ServiceMap.Service<
239
239
  )
240
240
 
241
241
  yield* SubscriptionRef.changes(issuesRef).pipe(
242
+ Stream.filter((change) => change._tag === "External"),
242
243
  Stream.runForEach((s) => updateSync(s.issues)),
243
244
  Effect.forkScoped,
244
245
  )
@@ -36,7 +36,9 @@ export class PullRequestComments extends S.Class<PullRequestComments>(
36
36
 
37
37
  export class PullRequest extends S.Class<PullRequest>("PullRequest")({
38
38
  url: S.String,
39
- reviewDecision: S.Null,
39
+ reviewDecision: S.NullOr(
40
+ S.Literals(["APPROVED", "CHANGES_REQUESTED", "REVIEW_REQUIRED"]),
41
+ ),
40
42
  reviews: S.suspend(() => Reviews),
41
43
  reviewThreads: S.suspend(() => ReviewThreads),
42
44
  comments: PullRequestComments,