@qotaq/lalphgram 0.1.2 → 0.1.4
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/README.md +12 -20
- package/dist/cjs/Main.js +18 -5
- package/dist/cjs/Main.js.map +1 -1
- package/dist/cjs/lib/AnalysisPrompts.js +2 -0
- package/dist/cjs/lib/AnalysisPrompts.js.map +1 -1
- package/dist/cjs/schemas/ProjectSchemas.js +4 -1
- package/dist/cjs/schemas/ProjectSchemas.js.map +1 -1
- package/dist/cjs/services/AutoMerge.js +6 -4
- package/dist/cjs/services/AutoMerge.js.map +1 -1
- package/dist/cjs/services/ChatMachine.js +3 -2
- package/dist/cjs/services/ChatMachine.js.map +1 -1
- package/dist/cjs/services/GitHubClient.js +2 -1
- package/dist/cjs/services/GitHubClient.js.map +1 -1
- package/dist/cjs/services/OctokitClient.js +5 -1
- package/dist/cjs/services/OctokitClient.js.map +1 -1
- package/dist/cjs/services/PlanSession.js +5 -3
- package/dist/cjs/services/PlanSession.js.map +1 -1
- package/dist/dts/lib/AnalysisPrompts.d.ts.map +1 -1
- package/dist/dts/schemas/ProjectSchemas.d.ts +8 -0
- package/dist/dts/schemas/ProjectSchemas.d.ts.map +1 -1
- package/dist/dts/services/AutoMerge.d.ts.map +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/GitHubClient.d.ts +4 -0
- package/dist/dts/services/GitHubClient.d.ts.map +1 -1
- package/dist/dts/services/OctokitClient.d.ts +4 -0
- package/dist/dts/services/OctokitClient.d.ts.map +1 -1
- package/dist/dts/services/PlanSession.d.ts +1 -1
- package/dist/dts/services/PlanSession.d.ts.map +1 -1
- package/dist/dts/services/TrackerLayerMap.d.ts +1 -1
- package/dist/esm/Main.js +19 -6
- package/dist/esm/Main.js.map +1 -1
- package/dist/esm/lib/AnalysisPrompts.js +2 -0
- package/dist/esm/lib/AnalysisPrompts.js.map +1 -1
- package/dist/esm/schemas/ProjectSchemas.js +4 -1
- package/dist/esm/schemas/ProjectSchemas.js.map +1 -1
- package/dist/esm/services/AutoMerge.js +6 -4
- package/dist/esm/services/AutoMerge.js.map +1 -1
- package/dist/esm/services/ChatMachine.js +3 -2
- package/dist/esm/services/ChatMachine.js.map +1 -1
- package/dist/esm/services/GitHubClient.js +2 -1
- package/dist/esm/services/GitHubClient.js.map +1 -1
- package/dist/esm/services/OctokitClient.js +5 -1
- package/dist/esm/services/OctokitClient.js.map +1 -1
- package/dist/esm/services/PlanSession.js +5 -3
- package/dist/esm/services/PlanSession.js.map +1 -1
- package/package.json +1 -1
- package/src/Main.ts +18 -6
- package/src/lib/AnalysisPrompts.ts +2 -0
- package/src/schemas/ProjectSchemas.ts +2 -1
- package/src/services/AutoMerge.ts +25 -5
- package/src/services/ChatMachine.ts +8 -4
- package/src/services/GitHubClient.ts +3 -1
- package/src/services/OctokitClient.ts +3 -1
- package/src/services/PlanSession.ts +9 -4
package/src/Main.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { Command as CliCommand, Options, Prompt } from "@effect/cli"
|
|
7
7
|
import { Command as PlatformCommand, FetchHttpClient, FileSystem, Path } from "@effect/platform"
|
|
8
8
|
import { NodeContext, NodeRuntime } from "@effect/platform-node"
|
|
9
|
-
import { Config, Console, Effect, Layer, Logger, LogLevel, Option, Stream } from "effect"
|
|
9
|
+
import { Config, Console, Effect, Layer, Logger, LogLevel, Option, Schema, Stream } from "effect"
|
|
10
10
|
import { fileURLToPath } from "node:url"
|
|
11
11
|
import { AppContext, AppContextLive } from "./services/AppContext.js"
|
|
12
12
|
import { AppRuntimeConfig, RuntimeConfig } from "./services/AppRuntimeConfig.js"
|
|
@@ -17,7 +17,7 @@ import { PlanCommandBuilder } from "./services/PlanSession.js"
|
|
|
17
17
|
import { TelegramConfig, TelegramConfigLive, TelegramConfigSchema } from "./services/TelegramConfig.js"
|
|
18
18
|
|
|
19
19
|
const lalphNotifyCommand = CliCommand.make(
|
|
20
|
-
"
|
|
20
|
+
"lalphgram",
|
|
21
21
|
{
|
|
22
22
|
interval: Options.integer("interval").pipe(
|
|
23
23
|
Options.withDefault(30),
|
|
@@ -156,9 +156,14 @@ const lalphNotifyCommand = CliCommand.make(
|
|
|
156
156
|
})
|
|
157
157
|
).pipe(CliCommand.withDescription("Zero-config notification service using lalph project config"))
|
|
158
158
|
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
const PackageJsonVersion = Schema.Struct({ version: Schema.String })
|
|
160
|
+
|
|
161
|
+
const readVersion = Effect.gen(function*() {
|
|
162
|
+
const fs = yield* FileSystem.FileSystem
|
|
163
|
+
const pathService = yield* Path.Path
|
|
164
|
+
const dir = pathService.dirname(fileURLToPath(import.meta.url))
|
|
165
|
+
const content = yield* fs.readFileString(pathService.join(dir, "..", "..", "package.json"))
|
|
166
|
+
return Schema.decodeUnknownSync(PackageJsonVersion)(JSON.parse(content)).version
|
|
162
167
|
})
|
|
163
168
|
|
|
164
169
|
const logLevelLayer = Layer.unwrapEffect(
|
|
@@ -168,7 +173,14 @@ const logLevelLayer = Layer.unwrapEffect(
|
|
|
168
173
|
)
|
|
169
174
|
)
|
|
170
175
|
|
|
171
|
-
Effect.
|
|
176
|
+
Effect.gen(function*() {
|
|
177
|
+
const version = yield* readVersion
|
|
178
|
+
const cli = CliCommand.run(lalphNotifyCommand, {
|
|
179
|
+
name: "lalphgram",
|
|
180
|
+
version
|
|
181
|
+
})
|
|
182
|
+
yield* Effect.suspend(() => cli(process.argv))
|
|
183
|
+
}).pipe(
|
|
172
184
|
Effect.provide(TelegramConfigLive),
|
|
173
185
|
Effect.provide(AppContextLive),
|
|
174
186
|
Effect.provide(FetchHttpClient.layer),
|
|
@@ -13,6 +13,7 @@ export const getAnalysisPrompt = (planType: string): string => {
|
|
|
13
13
|
switch (planType) {
|
|
14
14
|
case "Feature":
|
|
15
15
|
return `\
|
|
16
|
+
|
|
16
17
|
Now analyze the spec you just wrote. Create the following files:
|
|
17
18
|
|
|
18
19
|
1. \`.specs/analysis.md\` — high-level design summary in plain text:
|
|
@@ -28,6 +29,7 @@ Now analyze the spec you just wrote. Create the following files:
|
|
|
28
29
|
- **Act**: the single effect/function call under test
|
|
29
30
|
- **Assert**: expected outcome and why this case matters
|
|
30
31
|
|
|
32
|
+
Be extremely concise. Sacrifice grammar for the sake of concision.
|
|
31
33
|
Do NOT display file contents in your response. The system will send them to the user.`
|
|
32
34
|
case "Bug":
|
|
33
35
|
return `\
|
|
@@ -14,5 +14,6 @@ export class LalphProject extends Schema.Class<LalphProject>("LalphProject")({
|
|
|
14
14
|
targetBranch: Schema.Option(Schema.String),
|
|
15
15
|
concurrency: Schema.Int.pipe(Schema.positive()),
|
|
16
16
|
gitFlow: Schema.Literal("pr", "commit"),
|
|
17
|
-
reviewAgent: Schema.Boolean
|
|
17
|
+
reviewAgent: Schema.Boolean,
|
|
18
|
+
labelFilter: Schema.optionalWith(Schema.String, { default: () => "lalph" })
|
|
18
19
|
}) {}
|
|
@@ -43,11 +43,31 @@ const makeRepoFromFullName = (fullName: string) =>
|
|
|
43
43
|
html_url: ""
|
|
44
44
|
})
|
|
45
45
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
const isBillingFailure = (checkRun: {
|
|
47
|
+
readonly conclusion: string | null
|
|
48
|
+
readonly output: { readonly summary: string | null } | null
|
|
49
|
+
}) =>
|
|
50
|
+
checkRun.conclusion === "failure" &&
|
|
51
|
+
checkRun.output?.summary !== null &&
|
|
52
|
+
checkRun.output?.summary !== undefined &&
|
|
53
|
+
(checkRun.output.summary.includes("account payments have failed") ||
|
|
54
|
+
checkRun.output.summary.includes("spending limit"))
|
|
55
|
+
|
|
56
|
+
const isCISuccess = (
|
|
57
|
+
state: string,
|
|
58
|
+
checkRuns: ReadonlyArray<{
|
|
59
|
+
readonly conclusion: string | null
|
|
60
|
+
readonly output: { readonly summary: string | null } | null
|
|
61
|
+
}>
|
|
62
|
+
) => {
|
|
63
|
+
const nonBillingRuns = Array.filter(checkRuns, (cr) => !isBillingFailure(cr))
|
|
64
|
+
if (nonBillingRuns.length === 0) return state !== "failure" || checkRuns.length > 0
|
|
65
|
+
const allChecksCompleted = Array.every(nonBillingRuns, (cr) => cr.conclusion !== null)
|
|
66
|
+
const allChecksPassed = Array.every(
|
|
67
|
+
nonBillingRuns,
|
|
68
|
+
(cr) => cr.conclusion === "success" || cr.conclusion === "skipped"
|
|
69
|
+
)
|
|
70
|
+
return allChecksCompleted && allChecksPassed
|
|
51
71
|
}
|
|
52
72
|
|
|
53
73
|
/**
|
|
@@ -471,11 +471,15 @@ export const chatMachine = Machine.make(
|
|
|
471
471
|
yield* Effect.log("Plan collection done, starting session").pipe(
|
|
472
472
|
Effect.annotateLogs("planText", joinedText)
|
|
473
473
|
)
|
|
474
|
-
const
|
|
475
|
-
Effect.
|
|
476
|
-
Effect.orElseSucceed(() => 1)
|
|
474
|
+
const projects = yield* projectStore.listProjects.pipe(
|
|
475
|
+
Effect.orElseSucceed((): ReadonlyArray<{ id: string; labelFilter: string }> => [])
|
|
477
476
|
)
|
|
478
|
-
|
|
477
|
+
const currentProject = projects.find((p) => p.id === state.projectId)
|
|
478
|
+
yield* planSession.start(
|
|
479
|
+
joinedText,
|
|
480
|
+
projects.length > 1 ? state.projectId : undefined,
|
|
481
|
+
projects.length > 1 ? currentProject?.labelFilter : undefined
|
|
482
|
+
).pipe(
|
|
479
483
|
Effect.tapError((err) => notifier.sendMessage(`Plan error: ${err.message}`)),
|
|
480
484
|
Effect.orElseSucceed(() => undefined)
|
|
481
485
|
)
|
|
@@ -25,6 +25,7 @@ export interface GitHubCheckRun {
|
|
|
25
25
|
readonly status: string
|
|
26
26
|
readonly conclusion: string | null
|
|
27
27
|
readonly html_url: string
|
|
28
|
+
readonly output: { readonly title: string | null; readonly summary: string | null } | null
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
/**
|
|
@@ -242,7 +243,8 @@ export const GitHubClientLive = Layer.effect(
|
|
|
242
243
|
name: cr.name,
|
|
243
244
|
status: cr.status,
|
|
244
245
|
conclusion: cr.conclusion,
|
|
245
|
-
html_url: cr.htmlUrl
|
|
246
|
+
html_url: cr.htmlUrl,
|
|
247
|
+
output: cr.output
|
|
246
248
|
}))
|
|
247
249
|
})),
|
|
248
250
|
Effect.mapError((err) =>
|
|
@@ -105,6 +105,7 @@ export interface OctokitCheckRun {
|
|
|
105
105
|
readonly status: string
|
|
106
106
|
readonly conclusion: string | null
|
|
107
107
|
readonly htmlUrl: string
|
|
108
|
+
readonly output: { readonly title: string | null; readonly summary: string | null } | null
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
/**
|
|
@@ -548,7 +549,8 @@ export const OctokitClientLive = Layer.effect(
|
|
|
548
549
|
name: cr.name,
|
|
549
550
|
status: cr.status,
|
|
550
551
|
conclusion: cr.conclusion ?? null,
|
|
551
|
-
htmlUrl: cr.html_url ?? ""
|
|
552
|
+
htmlUrl: cr.html_url ?? "",
|
|
553
|
+
output: cr.output ? { title: cr.output.title ?? null, summary: cr.output.summary ?? null } : null
|
|
552
554
|
}))
|
|
553
555
|
)
|
|
554
556
|
)
|
|
@@ -109,7 +109,11 @@ interface ActiveSession {
|
|
|
109
109
|
* @category services
|
|
110
110
|
*/
|
|
111
111
|
export interface PlanSessionService {
|
|
112
|
-
readonly start: (
|
|
112
|
+
readonly start: (
|
|
113
|
+
planText: string,
|
|
114
|
+
projectId?: string | undefined,
|
|
115
|
+
labelFilter?: string | undefined
|
|
116
|
+
) => Effect.Effect<void, PlanSessionError>
|
|
113
117
|
readonly answer: (text: string) => Effect.Effect<void, PlanSessionError>
|
|
114
118
|
readonly sendFollowUp: (text: string) => Effect.Effect<void, PlanSessionError>
|
|
115
119
|
readonly interrupt: (text: string) => Effect.Effect<void, PlanSessionError>
|
|
@@ -196,7 +200,7 @@ export const PlanSessionLive = Layer.scoped(
|
|
|
196
200
|
|
|
197
201
|
yield* Effect.addFinalizer(() => closeActiveSession)
|
|
198
202
|
|
|
199
|
-
const start = (planText: string, projectId?: string | undefined) =>
|
|
203
|
+
const start = (planText: string, projectId?: string | undefined, labelFilter?: string | undefined) =>
|
|
200
204
|
Effect.gen(function*() {
|
|
201
205
|
const current = yield* Ref.get(sessionRef)
|
|
202
206
|
if (Option.isSome(current)) {
|
|
@@ -251,8 +255,9 @@ export const PlanSessionLive = Layer.scoped(
|
|
|
251
255
|
if (projectId != null) {
|
|
252
256
|
const encoder = new TextEncoder()
|
|
253
257
|
yield* Queue.offer(stdinQueue, encoder.encode(projectId + "\n"))
|
|
254
|
-
yield*
|
|
255
|
-
|
|
258
|
+
yield* Queue.offer(stdinQueue, encoder.encode((labelFilter ?? "") + "\n"))
|
|
259
|
+
yield* Effect.log("Pre-answered project and label prompts").pipe(
|
|
260
|
+
Effect.annotateLogs({ projectId, labelFilter: labelFilter ?? "(empty)" })
|
|
256
261
|
)
|
|
257
262
|
}
|
|
258
263
|
|