lalph 0.2.4 → 0.2.5

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.2.4",
4
+ "version": "0.2.5",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -1,4 +1,14 @@
1
- import { Effect, Layer, Option, pipe, Schema, ServiceMap } from "effect"
1
+ import {
2
+ Cause,
3
+ Effect,
4
+ Layer,
5
+ Option,
6
+ pipe,
7
+ Schedule,
8
+ Schema,
9
+ ScopedRef,
10
+ ServiceMap,
11
+ } from "effect"
2
12
  import { CurrentProjectId, Setting, Settings } from "./Settings.ts"
3
13
  import { LinearIssueSource } from "./Linear.ts"
4
14
  import { Prompt } from "effect/unstable/cli"
@@ -9,6 +19,7 @@ import { atomRuntime } from "./shared/runtime.ts"
9
19
  import { Atom, Reactivity } from "effect/unstable/reactivity"
10
20
  import type { PrdIssue } from "./domain/PrdIssue.ts"
11
21
  import type { ProjectId } from "./domain/Project.ts"
22
+ import type { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"
12
23
 
13
24
  const issueSources: ReadonlyArray<typeof CurrentIssueSource.Service> = [
14
25
  {
@@ -66,14 +77,72 @@ export class CurrentIssueSource extends ServiceMap.Service<
66
77
  static layer = Layer.effectServices(
67
78
  Effect.gen(function* () {
68
79
  const source = yield* getOrSelectIssueSource
69
- const services = yield* Layer.build(source.layer).pipe(
80
+ const build = Layer.build(source.layer).pipe(
81
+ Effect.map(ServiceMap.get(IssueSource)),
70
82
  Effect.withSpan("CurrentIssueSource.build"),
71
83
  )
72
- return ServiceMap.add(services, CurrentIssueSource, source)
84
+ const ref = yield* ScopedRef.fromAcquire(build)
85
+ const services = yield* Effect.services<
86
+ Settings | ChildProcessSpawner | Prompt.Environment
87
+ >()
88
+ const refresh = ScopedRef.set(ref, build).pipe(
89
+ Effect.provideServices(services),
90
+ )
91
+
92
+ const proxy = IssueSource.of({
93
+ issues: (projectId) =>
94
+ ScopedRef.get(ref).pipe(
95
+ Effect.flatMap((source) => source.issues(projectId)),
96
+ Effect.tapErrorTag("IssueSourceError", (e) =>
97
+ Effect.logWarning(
98
+ "Rebuilding issue source due to error",
99
+ Cause.fail(e),
100
+ ).pipe(Effect.andThen(Effect.ignore(refresh))),
101
+ ),
102
+ Effect.retry(refreshSchedule),
103
+ ),
104
+ createIssue: (projectId, options) =>
105
+ ScopedRef.get(ref).pipe(
106
+ Effect.flatMap((source) => source.createIssue(projectId, options)),
107
+ ),
108
+ updateIssue: (options) =>
109
+ ScopedRef.get(ref).pipe(
110
+ Effect.flatMap((source) => source.updateIssue(options)),
111
+ ),
112
+ cancelIssue: (projectId, issueId) =>
113
+ ScopedRef.get(ref).pipe(
114
+ Effect.flatMap((source) => source.cancelIssue(projectId, issueId)),
115
+ ),
116
+ reset: ScopedRef.get(ref).pipe(
117
+ Effect.flatMap((source) => source.reset),
118
+ ),
119
+ settings: (projectId) =>
120
+ ScopedRef.get(ref).pipe(
121
+ Effect.flatMap((source) => source.settings(projectId)),
122
+ ),
123
+ info: (projectId) =>
124
+ ScopedRef.get(ref).pipe(
125
+ Effect.flatMap((source) => source.info(projectId)),
126
+ ),
127
+ ensureInProgress: (projectId, issueId) =>
128
+ ScopedRef.get(ref).pipe(
129
+ Effect.flatMap((source) =>
130
+ source.ensureInProgress(projectId, issueId),
131
+ ),
132
+ ),
133
+ })
134
+
135
+ return IssueSource.serviceMap(proxy).pipe(
136
+ ServiceMap.add(CurrentIssueSource, source),
137
+ )
73
138
  }),
74
139
  ).pipe(Layer.provide([Settings.layer, PlatformServices]))
75
140
  }
76
141
 
142
+ const refreshSchedule = Schedule.exponential(100, 1.5).pipe(
143
+ Schedule.either(Schedule.spaced("30 seconds")),
144
+ )
145
+
77
146
  // Atoms
78
147
 
79
148
  export const issueSourceRuntime = atomRuntime(
@@ -85,9 +154,10 @@ export const currentIssuesAtom = Atom.family((projectId: ProjectId) =>
85
154
  issueSourceRuntime.atom(
86
155
  Effect.fnUntraced(function* (get) {
87
156
  const source = yield* IssueSource
88
- const issues = yield* source
89
- .issues(projectId)
90
- .pipe(Effect.withSpan("currentIssuesAtom.refresh"))
157
+ const issues = yield* pipe(
158
+ source.issues(projectId),
159
+ Effect.withSpan("currentIssuesAtom.refresh"),
160
+ )
91
161
  const handle = setTimeout(() => {
92
162
  get.refreshSelf()
93
163
  }, 30_000)
@@ -85,4 +85,6 @@ export class IssueSourceError extends Schema.ErrorClass<IssueSourceError>(
85
85
  )({
86
86
  _tag: Schema.tag("IssueSourceError"),
87
87
  cause: Schema.Defect,
88
- }) {}
88
+ }) {
89
+ readonly message = "An error occurred in the IssueSource"
90
+ }
@@ -0,0 +1,6 @@
1
+ import { Schema } from "effect"
2
+
3
+ export class LinearError extends Schema.ErrorClass("lalph/LinearError")({
4
+ _tag: Schema.tag("LinearError"),
5
+ cause: Schema.Defect,
6
+ }) {}
package/src/Prd.ts CHANGED
@@ -14,7 +14,7 @@ import { Worktree } from "./Worktree.ts"
14
14
  import { PrdIssue } from "./domain/PrdIssue.ts"
15
15
  import { IssueSource, IssueSourceError } from "./IssueSource.ts"
16
16
  import { AtomRegistry, Reactivity } from "effect/unstable/reactivity"
17
- import { CurrentIssueSource, currentIssuesAtom } from "./IssueSources.ts"
17
+ import { CurrentIssueSource, currentIssuesAtom } from "./CurrentIssueSource.ts"
18
18
  import { CurrentProjectId } from "./Settings.ts"
19
19
 
20
20
  export class Prd extends ServiceMap.Service<
package/src/Projects.ts CHANGED
@@ -14,7 +14,7 @@ import { AsyncResult, Atom } from "effect/unstable/reactivity"
14
14
  import { CurrentProjectId, Setting, Settings } from "./Settings.ts"
15
15
  import { Prompt } from "effect/unstable/cli"
16
16
  import { IssueSource } from "./IssueSource.ts"
17
- import { CurrentIssueSource } from "./IssueSources.ts"
17
+ import { CurrentIssueSource } from "./CurrentIssueSource.ts"
18
18
 
19
19
  export const layerProjectIdPrompt = Layer.effect(
20
20
  CurrentProjectId,
package/src/PromptGen.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Effect, Layer, ServiceMap } from "effect"
2
2
  import { PrdIssue } from "./domain/PrdIssue.ts"
3
- import { CurrentIssueSource } from "./IssueSources.ts"
3
+ import { CurrentIssueSource } from "./CurrentIssueSource.ts"
4
4
  import type { GitFlow } from "./GitFlow.ts"
5
5
 
6
6
  export class PromptGen extends ServiceMap.Service<PromptGen>()(
@@ -1,5 +1,5 @@
1
1
  import { Command } from "effect/unstable/cli"
2
- import { CurrentIssueSource } from "../IssueSources.ts"
2
+ import { CurrentIssueSource } from "../CurrentIssueSource.ts"
3
3
  import { Effect, FileSystem, flow, Layer, Schema } from "effect"
4
4
  import { IssueSource } from "../IssueSource.ts"
5
5
  import { ChildProcess } from "effect/unstable/process"
@@ -5,7 +5,7 @@ import { ChildProcess } from "effect/unstable/process"
5
5
  import { Worktree } from "../Worktree.ts"
6
6
  import { getCommandPrefix, getOrSelectCliAgent } from "./agent.ts"
7
7
  import { Command, Flag } from "effect/unstable/cli"
8
- import { CurrentIssueSource } from "../IssueSources.ts"
8
+ import { CurrentIssueSource } from "../CurrentIssueSource.ts"
9
9
  import { commandRoot } from "./root.ts"
10
10
  import { CurrentProjectId, Settings } from "../Settings.ts"
11
11
  import { addOrUpdateProject, selectProject } from "../Projects.ts"
@@ -1,6 +1,6 @@
1
1
  import { Command } from "effect/unstable/cli"
2
2
  import { addOrUpdateProject } from "../../Projects.ts"
3
- import { CurrentIssueSource } from "../../IssueSources.ts"
3
+ import { CurrentIssueSource } from "../../CurrentIssueSource.ts"
4
4
  import { Settings } from "../../Settings.ts"
5
5
 
6
6
  export const commandProjectsAdd = Command.make("add").pipe(
@@ -6,7 +6,7 @@ import {
6
6
  selectProject,
7
7
  } from "../../Projects.ts"
8
8
  import { Settings } from "../../Settings.ts"
9
- import { CurrentIssueSource } from "../../IssueSources.ts"
9
+ import { CurrentIssueSource } from "../../CurrentIssueSource.ts"
10
10
 
11
11
  export const commandProjectsEdit = Command.make("edit").pipe(
12
12
  Command.withDescription("Modify a project"),
@@ -1,7 +1,7 @@
1
1
  import { Effect, Option } from "effect"
2
2
  import { Command } from "effect/unstable/cli"
3
3
  import { IssueSource } from "../../IssueSource.ts"
4
- import { CurrentIssueSource } from "../../IssueSources.ts"
4
+ import { CurrentIssueSource } from "../../CurrentIssueSource.ts"
5
5
  import { getAllProjects } from "../../Projects.ts"
6
6
  import { Settings } from "../../Settings.ts"
7
7
 
@@ -2,7 +2,7 @@ import { Effect, Option } from "effect"
2
2
  import { Command } from "effect/unstable/cli"
3
3
  import { allProjects, getAllProjects, selectProject } from "../../Projects.ts"
4
4
  import { Settings } from "../../Settings.ts"
5
- import { CurrentIssueSource } from "../../IssueSources.ts"
5
+ import { CurrentIssueSource } from "../../CurrentIssueSource.ts"
6
6
 
7
7
  export const commandProjectsRm = Command.make("rm").pipe(
8
8
  Command.withDescription("Remove a project"),
@@ -1,12 +1,10 @@
1
1
  import {
2
- Cause,
3
2
  Config,
4
3
  Deferred,
5
4
  Duration,
6
5
  Effect,
7
6
  FiberSet,
8
7
  FileSystem,
9
- Filter,
10
8
  Iterable,
11
9
  Option,
12
10
  Path,
@@ -22,7 +20,7 @@ import {
22
20
  checkForWork,
23
21
  CurrentIssueSource,
24
22
  resetInProgress,
25
- } from "../IssueSources.ts"
23
+ } from "../CurrentIssueSource.ts"
26
24
  import { GithubCli } from "../Github/Cli.ts"
27
25
  import { agentWorker } from "../Agents/worker.ts"
28
26
  import { agentChooser } from "../Agents/chooser.ts"
@@ -280,16 +278,6 @@ const runProject = Effect.fnUntraced(
280
278
  withWorkerState(options.project.id),
281
279
  ),
282
280
  ),
283
- Effect.catchFilter(
284
- (e) =>
285
- e._tag === "NoMoreWork" || e._tag === "QuitError"
286
- ? Filter.fail(e)
287
- : e,
288
- (e) =>
289
- Effect.logWarning(Cause.fail(e)).pipe(
290
- Effect.andThen(Effect.sleep(Duration.seconds(10))),
291
- ),
292
- ),
293
281
  Effect.catchTags({
294
282
  NoMoreWork(_) {
295
283
  if (isFinite) {
@@ -311,6 +299,11 @@ const runProject = Effect.fnUntraced(
311
299
  return Effect.void
312
300
  },
313
301
  }),
302
+ Effect.catchCause((cause) =>
303
+ Effect.logWarning(cause).pipe(
304
+ Effect.andThen(Effect.sleep(Duration.seconds(10))),
305
+ ),
306
+ ),
314
307
  Effect.ensuring(semaphore.release(1)),
315
308
  Effect.ensuring(Deferred.completeWith(startedDeferred, Effect.void)),
316
309
  FiberSet.run(fibers),
@@ -1,5 +1,5 @@
1
1
  import { Command } from "effect/unstable/cli"
2
- import { selectIssueSource } from "../IssueSources.ts"
2
+ import { selectIssueSource } from "../CurrentIssueSource.ts"
3
3
  import { Settings } from "../Settings.ts"
4
4
 
5
5
  export const commandSource = Command.make("source").pipe(