@qotaq/lalphgram 0.1.7 → 0.1.9
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/cjs/lib/StreamJsonParser.js +2 -2
- package/dist/cjs/lib/StreamJsonParser.js.map +1 -1
- package/dist/cjs/services/ChatMachine.js +9 -2
- package/dist/cjs/services/ChatMachine.js.map +1 -1
- package/dist/cjs/services/EventLoop.js +7 -1
- package/dist/cjs/services/EventLoop.js.map +1 -1
- package/dist/cjs/services/LalphConfig.js +18 -1
- package/dist/cjs/services/LalphConfig.js.map +1 -1
- package/dist/cjs/services/LinearSdkClient.js +13 -5
- package/dist/cjs/services/LinearSdkClient.js.map +1 -1
- package/dist/cjs/services/PlanSession.js +39 -6
- package/dist/cjs/services/PlanSession.js.map +1 -1
- package/dist/cjs/services/TaskTracker/GitHubIssueTracker.js +7 -3
- package/dist/cjs/services/TaskTracker/GitHubIssueTracker.js.map +1 -1
- package/dist/cjs/services/TaskTracker/LinearTracker.js +5 -1
- package/dist/cjs/services/TaskTracker/LinearTracker.js.map +1 -1
- package/dist/cjs/shim/main.js +4 -1
- package/dist/cjs/shim/main.js.map +1 -1
- package/dist/dts/lib/StreamJsonParser.d.ts +2 -1
- package/dist/dts/lib/StreamJsonParser.d.ts.map +1 -1
- package/dist/dts/services/AutoMerge.d.ts +1 -1
- package/dist/dts/services/ChatMachine.d.ts +1 -1
- package/dist/dts/services/ChatMachine.d.ts.map +1 -1
- package/dist/dts/services/EventLoop.d.ts +2 -2
- package/dist/dts/services/EventLoop.d.ts.map +1 -1
- package/dist/dts/services/LalphConfig.d.ts +1 -0
- package/dist/dts/services/LalphConfig.d.ts.map +1 -1
- package/dist/dts/services/LinearSdkClient.d.ts +1 -0
- package/dist/dts/services/LinearSdkClient.d.ts.map +1 -1
- package/dist/dts/services/PlanOverviewUploaderMap.d.ts +1 -1
- package/dist/dts/services/PlanSession.d.ts +10 -1
- package/dist/dts/services/PlanSession.d.ts.map +1 -1
- package/dist/dts/services/PullRequestTracker.d.ts +1 -1
- package/dist/dts/services/TaskTracker/GitHubIssueTracker.d.ts +2 -1
- package/dist/dts/services/TaskTracker/GitHubIssueTracker.d.ts.map +1 -1
- package/dist/dts/services/TaskTracker/LinearTracker.d.ts +2 -1
- package/dist/dts/services/TaskTracker/LinearTracker.d.ts.map +1 -1
- package/dist/dts/services/TrackerLayerMap.d.ts +1 -1
- package/dist/dts/shim/main.d.ts +1 -1
- package/dist/dts/shim/main.d.ts.map +1 -1
- package/dist/esm/lib/StreamJsonParser.js +1 -1
- package/dist/esm/lib/StreamJsonParser.js.map +1 -1
- package/dist/esm/services/ChatMachine.js +9 -2
- package/dist/esm/services/ChatMachine.js.map +1 -1
- package/dist/esm/services/EventLoop.js +7 -1
- package/dist/esm/services/EventLoop.js.map +1 -1
- package/dist/esm/services/LalphConfig.js +19 -2
- package/dist/esm/services/LalphConfig.js.map +1 -1
- package/dist/esm/services/LinearSdkClient.js +13 -5
- package/dist/esm/services/LinearSdkClient.js.map +1 -1
- package/dist/esm/services/PlanSession.js +37 -5
- package/dist/esm/services/PlanSession.js.map +1 -1
- package/dist/esm/services/TaskTracker/GitHubIssueTracker.js +7 -3
- package/dist/esm/services/TaskTracker/GitHubIssueTracker.js.map +1 -1
- package/dist/esm/services/TaskTracker/LinearTracker.js +5 -1
- package/dist/esm/services/TaskTracker/LinearTracker.js.map +1 -1
- package/dist/esm/shim/main.js +4 -1
- package/dist/esm/shim/main.js.map +1 -1
- package/package.json +1 -1
- package/src/lib/StreamJsonParser.ts +1 -1
- package/src/services/ChatMachine.ts +11 -3
- package/src/services/EventLoop.ts +5 -0
- package/src/services/LalphConfig.ts +37 -2
- package/src/services/LinearSdkClient.ts +6 -1
- package/src/services/PlanSession.ts +52 -7
- package/src/services/TaskTracker/GitHubIssueTracker.ts +10 -3
- package/src/services/TaskTracker/LinearTracker.ts +4 -1
- package/src/shim/main.ts +4 -1
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { Command, CommandExecutor, FileSystem, Path } from "@effect/platform"
|
|
6
6
|
import { Context, Data, Effect, Exit, Layer, Option, Queue, Ref, Schema, Scope, Stream } from "effect"
|
|
7
7
|
import type { ContentBlock, StreamJsonMessage } from "../lib/StreamJsonParser.js"
|
|
8
|
-
import { AskUserQuestionInput,
|
|
8
|
+
import { AskUserQuestionInput, decodeJsonMessage } from "../lib/StreamJsonParser.js"
|
|
9
9
|
import { AppContext } from "./AppContext.js"
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -84,6 +84,12 @@ export class PlanAnalysisReady extends Data.TaggedClass("PlanAnalysisReady")<{
|
|
|
84
84
|
*/
|
|
85
85
|
export class PlanAwaitingInput extends Data.TaggedClass("PlanAwaitingInput")<Record<string, never>> {}
|
|
86
86
|
|
|
87
|
+
/**
|
|
88
|
+
* @since 1.0.0
|
|
89
|
+
* @category events
|
|
90
|
+
*/
|
|
91
|
+
export class PlanTaskCreationStarted extends Data.TaggedClass("PlanTaskCreationStarted")<Record<string, never>> {}
|
|
92
|
+
|
|
87
93
|
/**
|
|
88
94
|
* @since 1.0.0
|
|
89
95
|
* @category events
|
|
@@ -97,11 +103,15 @@ export type PlanEvent =
|
|
|
97
103
|
| PlanSpecUpdated
|
|
98
104
|
| PlanAnalysisReady
|
|
99
105
|
| PlanAwaitingInput
|
|
106
|
+
| PlanTaskCreationStarted
|
|
107
|
+
|
|
108
|
+
const StdinEOF: unique symbol = Symbol.for("StdinEOF")
|
|
109
|
+
type StdinItem = Uint8Array | typeof StdinEOF
|
|
100
110
|
|
|
101
111
|
interface ActiveSession {
|
|
102
112
|
readonly process: CommandExecutor.Process
|
|
103
113
|
readonly scope: Scope.CloseableScope
|
|
104
|
-
readonly stdinQueue: Queue.Queue<
|
|
114
|
+
readonly stdinQueue: Queue.Queue<StdinItem>
|
|
105
115
|
}
|
|
106
116
|
|
|
107
117
|
/**
|
|
@@ -242,14 +252,16 @@ export const PlanSessionLive = Layer.scoped(
|
|
|
242
252
|
)
|
|
243
253
|
)
|
|
244
254
|
|
|
245
|
-
const stdinQueue = yield* Queue.unbounded<
|
|
255
|
+
const stdinQueue = yield* Queue.unbounded<StdinItem>()
|
|
246
256
|
yield* Ref.set(sessionRef, Option.some({ process, scope: processScope, stdinQueue }))
|
|
247
257
|
yield* Effect.log("Plan session process spawned").pipe(
|
|
248
258
|
Effect.annotateLogs({ tempFile })
|
|
249
259
|
)
|
|
250
260
|
|
|
251
261
|
yield* Stream.fromQueue(stdinQueue).pipe(
|
|
262
|
+
Stream.takeWhile((item): item is Uint8Array => item !== StdinEOF),
|
|
252
263
|
Stream.run(process.stdin),
|
|
264
|
+
Effect.tap(() => Effect.log("stdin stream closed")),
|
|
253
265
|
Effect.catchAll((err) => Effect.logError(`stdin write error: ${String(err)}`)),
|
|
254
266
|
Effect.forkDaemon
|
|
255
267
|
)
|
|
@@ -266,7 +278,7 @@ export const PlanSessionLive = Layer.scoped(
|
|
|
266
278
|
const pendingTextRef = yield* Ref.make<Option.Option<{ messageId: string; text: string }>>(
|
|
267
279
|
Option.none()
|
|
268
280
|
)
|
|
269
|
-
|
|
281
|
+
const stage2Ref = yield* Ref.make(false)
|
|
270
282
|
const flushPendingText = Effect.gen(function*() {
|
|
271
283
|
const pending = yield* Ref.get(pendingTextRef)
|
|
272
284
|
if (Option.isSome(pending)) {
|
|
@@ -380,6 +392,7 @@ export const PlanSessionLive = Layer.scoped(
|
|
|
380
392
|
if (msg.type === "result") {
|
|
381
393
|
yield* flushPendingText
|
|
382
394
|
yield* Ref.set(idleRef, true)
|
|
395
|
+
yield* Ref.set(stage2Ref, true)
|
|
383
396
|
yield* Queue.offer(eventQueue, new PlanAwaitingInput({}))
|
|
384
397
|
yield* Effect.log("Planner result received")
|
|
385
398
|
return
|
|
@@ -400,8 +413,33 @@ export const PlanSessionLive = Layer.scoped(
|
|
|
400
413
|
),
|
|
401
414
|
Stream.map(stripAnsi),
|
|
402
415
|
Stream.map((text) => text.replace(/\r/g, "\n")),
|
|
403
|
-
|
|
404
|
-
Stream.
|
|
416
|
+
Stream.splitLines,
|
|
417
|
+
Stream.filter((line) => line.trim().length > 0),
|
|
418
|
+
Stream.mapEffect((line) =>
|
|
419
|
+
Effect.gen(function*() {
|
|
420
|
+
const isStage2 = yield* Ref.get(stage2Ref)
|
|
421
|
+
const parsed = yield* decodeJsonMessage(line).pipe(
|
|
422
|
+
Effect.tapError((err) => {
|
|
423
|
+
if (isStage2) return Effect.void
|
|
424
|
+
return Effect.logDebug("Non-JSON stdout line, skipping").pipe(
|
|
425
|
+
Effect.annotateLogs({
|
|
426
|
+
line: line.slice(0, 300),
|
|
427
|
+
lineBytes: Array.from(line.slice(0, 100), (c) => c.charCodeAt(0).toString(16)).join(" "),
|
|
428
|
+
error: err.message
|
|
429
|
+
})
|
|
430
|
+
)
|
|
431
|
+
}),
|
|
432
|
+
Effect.option
|
|
433
|
+
)
|
|
434
|
+
if (Option.isSome(parsed)) {
|
|
435
|
+
yield* routeMessage(parsed.value)
|
|
436
|
+
} else if (isStage2) {
|
|
437
|
+
if (line.includes("[Session started]")) {
|
|
438
|
+
yield* Queue.offer(eventQueue, new PlanTaskCreationStarted({}))
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
})
|
|
442
|
+
),
|
|
405
443
|
Stream.runDrain,
|
|
406
444
|
Effect.tap(() => flushPendingText),
|
|
407
445
|
Effect.tap(() => Effect.log("stdout stream completed")),
|
|
@@ -439,6 +477,7 @@ export const PlanSessionLive = Layer.scoped(
|
|
|
439
477
|
)
|
|
440
478
|
|
|
441
479
|
yield* Effect.gen(function*() {
|
|
480
|
+
yield* Effect.log("Waiting for process exit code...")
|
|
442
481
|
const exitCode = yield* process.exitCode
|
|
443
482
|
yield* Effect.log("Plan session process exited").pipe(
|
|
444
483
|
Effect.annotateLogs({ exitCode: String(exitCode) })
|
|
@@ -519,7 +558,13 @@ export const PlanSessionLive = Layer.scoped(
|
|
|
519
558
|
}
|
|
520
559
|
const encoder = new TextEncoder()
|
|
521
560
|
yield* Ref.set(idleRef, false)
|
|
522
|
-
|
|
561
|
+
const payload = JSON.stringify({ type: "shim_approve" }) + "\n"
|
|
562
|
+
yield* Effect.log("Sending shim_approve to stdin").pipe(
|
|
563
|
+
Effect.annotateLogs({ payload: payload.trim() })
|
|
564
|
+
)
|
|
565
|
+
yield* Queue.offer(current.value.stdinQueue, encoder.encode(payload))
|
|
566
|
+
yield* Queue.offer(current.value.stdinQueue, StdinEOF)
|
|
567
|
+
yield* Effect.log("shim_approve + StdinEOF queued to stdin")
|
|
523
568
|
})
|
|
524
569
|
|
|
525
570
|
const reject = Effect.gen(function*() {
|
|
@@ -7,6 +7,7 @@ import { TaskCreated, TaskUpdated } from "../../Events.js"
|
|
|
7
7
|
import type { TaskTrackerEvent } from "../../Events.js"
|
|
8
8
|
import { TrackerIssue, TrackerIssueEvent } from "../../schemas/TrackerSchemas.js"
|
|
9
9
|
import { AppRuntimeConfig } from "../AppRuntimeConfig.js"
|
|
10
|
+
import { LalphConfig } from "../LalphConfig.js"
|
|
10
11
|
import { OctokitClient } from "../OctokitClient.js"
|
|
11
12
|
import { TaskTracker, TaskTrackerError } from "./TaskTracker.js"
|
|
12
13
|
|
|
@@ -29,7 +30,9 @@ export const GitHubIssueTrackerLive = Layer.effect(
|
|
|
29
30
|
Effect.gen(function*() {
|
|
30
31
|
const octokit = yield* OctokitClient
|
|
31
32
|
const config = yield* AppRuntimeConfig
|
|
33
|
+
const lalphConfig = yield* LalphConfig
|
|
32
34
|
const interval = Duration.seconds(config.pollIntervalSeconds)
|
|
35
|
+
const repoFullName = lalphConfig.repoFullName
|
|
33
36
|
|
|
34
37
|
const fetchRecentEvents = (since: string) =>
|
|
35
38
|
Effect.gen(function*() {
|
|
@@ -42,10 +45,14 @@ export const GitHubIssueTrackerLive = Layer.effect(
|
|
|
42
45
|
new TaskTrackerError({ message: `GitHub API request failed: ${String(err)}`, cause: err })
|
|
43
46
|
)
|
|
44
47
|
)
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
const filteredIssues = Array.filter(
|
|
49
|
+
issues,
|
|
50
|
+
(issue) => extractRepoFullName(issue.repositoryUrl) === repoFullName
|
|
51
|
+
)
|
|
52
|
+
return Array.map(filteredIssues, (issue) => {
|
|
53
|
+
const issueRepoFullName = extractRepoFullName(issue.repositoryUrl)
|
|
47
54
|
const trackerIssue = new TrackerIssue({
|
|
48
|
-
id: `${
|
|
55
|
+
id: `${issueRepoFullName}#${issue.number}`,
|
|
49
56
|
title: issue.title,
|
|
50
57
|
state: issue.state,
|
|
51
58
|
url: issue.htmlUrl,
|
|
@@ -7,6 +7,7 @@ import { TaskCreated, TaskUpdated } from "../../Events.js"
|
|
|
7
7
|
import type { TaskTrackerEvent } from "../../Events.js"
|
|
8
8
|
import { TrackerIssue, TrackerIssueEvent } from "../../schemas/TrackerSchemas.js"
|
|
9
9
|
import { AppRuntimeConfig } from "../AppRuntimeConfig.js"
|
|
10
|
+
import { LalphConfig } from "../LalphConfig.js"
|
|
10
11
|
import { LinearSdkClient } from "../LinearSdkClient.js"
|
|
11
12
|
import { TaskTracker, TaskTrackerError } from "./TaskTracker.js"
|
|
12
13
|
|
|
@@ -15,7 +16,9 @@ export const LinearTrackerLive = Layer.effect(
|
|
|
15
16
|
Effect.gen(function*() {
|
|
16
17
|
const linearClient = yield* LinearSdkClient
|
|
17
18
|
const config = yield* AppRuntimeConfig
|
|
19
|
+
const lalphConfig = yield* LalphConfig
|
|
18
20
|
const interval = Duration.seconds(config.pollIntervalSeconds)
|
|
21
|
+
const projectIds = lalphConfig.linearProjectIds
|
|
19
22
|
const todoStateIdRef = yield* Ref.make<string | null>(null)
|
|
20
23
|
|
|
21
24
|
const resolveTodoStateId = Effect.gen(function*() {
|
|
@@ -36,7 +39,7 @@ export const LinearTrackerLive = Layer.effect(
|
|
|
36
39
|
})
|
|
37
40
|
|
|
38
41
|
const fetchRecentEvents = (since: string) =>
|
|
39
|
-
linearClient.listIssues({ since }).pipe(
|
|
42
|
+
linearClient.listIssues({ since, projectIds }).pipe(
|
|
40
43
|
Effect.map((issues) =>
|
|
41
44
|
issues.map((node) => {
|
|
42
45
|
const issue = new TrackerIssue({
|
package/src/shim/main.ts
CHANGED
|
@@ -162,7 +162,10 @@ export const shimProgram = Effect.gen(function*() {
|
|
|
162
162
|
}
|
|
163
163
|
case "shim_approve": {
|
|
164
164
|
yield* writeDebug("shim_approve intercepted")
|
|
165
|
-
|
|
165
|
+
// Exit immediately — scope cleanup hangs because the stdin
|
|
166
|
+
// reader fiber is blocked on a Node.js pipe read that
|
|
167
|
+
// doesn't respond to Effect's fiber interruption.
|
|
168
|
+
yield* Effect.sync(() => process.exit(0))
|
|
166
169
|
return
|
|
167
170
|
}
|
|
168
171
|
case "shim_start": {
|