@llm4ts/shell 0.6.0 → 0.6.1
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/Cli.d.ts +2 -2
- package/dist/Cli.js +3 -3
- package/dist/Cli.js.map +1 -1
- package/dist/FlowCatalog.d.ts.map +1 -1
- package/dist/FlowCatalog.js +6 -2
- package/dist/FlowCatalog.js.map +1 -1
- package/flows/implement.js +29 -0
- package/flows/issue-pr.js +71 -0
- package/flows/judge-suite.js +48 -0
- package/flows/local.js +40 -0
- package/flows/modernize-bench.js +263 -0
- package/flows/modernize-extract.js +454 -0
- package/flows/modernize-implement.js +249 -0
- package/flows/modernize-review.js +237 -0
- package/flows/modernize-seed.js +189 -0
- package/flows/modernize-survey.js +0 -0
- package/flows/modernize-verify.js +435 -0
- package/flows/sdd.js +114 -0
- package/package.json +5 -5
- package/src/Cli.ts +3 -3
- package/src/FlowCatalog.ts +8 -2
- package/flows/implement.ts +0 -38
- package/flows/issue-pr.ts +0 -108
- package/flows/judge-suite.ts +0 -63
- package/flows/local.ts +0 -59
- package/flows/modernize-bench.ts +0 -368
- package/flows/modernize-extract.ts +0 -642
- package/flows/modernize-implement.ts +0 -340
- package/flows/modernize-review.ts +0 -351
- package/flows/modernize-seed.ts +0 -266
- package/flows/modernize-survey.ts +0 -0
- package/flows/modernize-verify.ts +0 -618
- package/flows/sdd.ts +0 -181
package/flows/sdd.ts
DELETED
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
// Spec-driven development: write a specification, encode it as red tests, implement to green.
|
|
2
|
-
import { join } from "node:path"
|
|
3
|
-
import * as Effect from "effect/Effect"
|
|
4
|
-
import { makeChat } from "@llm4ts/flow/Chat"
|
|
5
|
-
import { FlowAborted } from "@llm4ts/flow/FlowError"
|
|
6
|
-
import { Plan, defaultPlanPath } from "@llm4ts/flow/Plan"
|
|
7
|
-
import { implementTaskLoop, stage } from "@llm4ts/flow/PlanExecution"
|
|
8
|
-
import { defaultPlanInstructions, planFrom, writeBrief } from "@llm4ts/flow/Planner"
|
|
9
|
-
import { makePlanStore } from "@llm4ts/flow/Persistence"
|
|
10
|
-
import {
|
|
11
|
-
lintCommand,
|
|
12
|
-
minimalReviewers,
|
|
13
|
-
reviewAndFixLoop,
|
|
14
|
-
type ReviewResult
|
|
15
|
-
} from "@llm4ts/flow/Review"
|
|
16
|
-
import { asReadOnly, coderFromEnv, gemini, withModel } from "@llm4ts/runner/Connectors"
|
|
17
|
-
import { resolveFlowInput } from "@llm4ts/runner/FlowArgs"
|
|
18
|
-
import { runFlowMain, runNode } from "@llm4ts/runner/FlowRunner"
|
|
19
|
-
import { nodePlainFileStore } from "@llm4ts/runner/NodePlainFileStore"
|
|
20
|
-
import { nodeProcessExecutor } from "@llm4ts/runner/NodeProcessExecutor"
|
|
21
|
-
|
|
22
|
-
const proModel = process.env.LLM4TS_REASONING_MODEL ?? "gemini-3-pro-preview"
|
|
23
|
-
const flashModel = process.env.LLM4TS_CODER_MODEL ?? "gemini-2.5-flash"
|
|
24
|
-
|
|
25
|
-
const specInstructions = [
|
|
26
|
-
"Turn the change request into a precise repository specification with context, goals,",
|
|
27
|
-
"non-goals, and a numbered list of testable Given/When/Then acceptance criteria.",
|
|
28
|
-
"Explore the repository as needed. Return Markdown prose, not a task list."
|
|
29
|
-
].join("\n")
|
|
30
|
-
|
|
31
|
-
const planInstructions = [
|
|
32
|
-
defaultPlanInstructions,
|
|
33
|
-
"",
|
|
34
|
-
"The request below is a specification with numbered acceptance criteria.",
|
|
35
|
-
"The first task must encode those criteria as tests without changing production code.",
|
|
36
|
-
"Every later task implements production behavior toward making those tests pass."
|
|
37
|
-
].join("\n")
|
|
38
|
-
|
|
39
|
-
const verificationFailure = (result: ReviewResult): FlowAborted =>
|
|
40
|
-
FlowAborted.make({
|
|
41
|
-
message: [
|
|
42
|
-
"acceptance criteria not met:",
|
|
43
|
-
...result.issues.map((issue) => `${issue.title}\n${issue.description}`)
|
|
44
|
-
].join("\n\n")
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
const program = Effect.gen(function* () {
|
|
48
|
-
const input = yield* resolveFlowInput(
|
|
49
|
-
"Add due dates, mark overdue items in list output, and show items due today."
|
|
50
|
-
)
|
|
51
|
-
const explicitCoder = process.env.LLM4TS_CODER?.trim()
|
|
52
|
-
const coder =
|
|
53
|
-
explicitCoder === undefined || explicitCoder.length === 0
|
|
54
|
-
? withModel(gemini, flashModel)
|
|
55
|
-
: coderFromEnv(process.env)
|
|
56
|
-
const reasoning =
|
|
57
|
-
explicitCoder === undefined || explicitCoder.length === 0
|
|
58
|
-
? asReadOnly(withModel(gemini, proModel))
|
|
59
|
-
: asReadOnly(coder)
|
|
60
|
-
const reviewer =
|
|
61
|
-
explicitCoder === undefined || explicitCoder.length === 0
|
|
62
|
-
? asReadOnly(withModel(gemini, flashModel))
|
|
63
|
-
: asReadOnly(coder)
|
|
64
|
-
const store = makePlanStore(nodePlainFileStore)
|
|
65
|
-
const planPath = join(input.workDir, defaultPlanPath(input.prompt))
|
|
66
|
-
|
|
67
|
-
yield* runNode(
|
|
68
|
-
{
|
|
69
|
-
workDir: input.workDir,
|
|
70
|
-
workspace: input.workspace,
|
|
71
|
-
userPrompt: input.prompt,
|
|
72
|
-
coder,
|
|
73
|
-
reasoning,
|
|
74
|
-
reviewers: [reviewer],
|
|
75
|
-
environment: process.env
|
|
76
|
-
},
|
|
77
|
-
(context) =>
|
|
78
|
-
Effect.gen(function* () {
|
|
79
|
-
const plan = yield* stage(
|
|
80
|
-
context.events,
|
|
81
|
-
"specification and plan",
|
|
82
|
-
store.recoverOrCreate(
|
|
83
|
-
planPath,
|
|
84
|
-
Effect.gen(function* () {
|
|
85
|
-
const spec = yield* writeBrief(context.reasoning, input.prompt, specInstructions)
|
|
86
|
-
const planned = yield* planFrom(context.reasoning, spec, planInstructions)
|
|
87
|
-
return Plan.make({ ...planned, brief: spec })
|
|
88
|
-
})
|
|
89
|
-
)
|
|
90
|
-
)
|
|
91
|
-
const spec =
|
|
92
|
-
plan.brief === undefined || plan.brief.length === 0
|
|
93
|
-
? yield* writeBrief(context.reasoning, input.prompt, specInstructions)
|
|
94
|
-
: plan.brief
|
|
95
|
-
const planWithSpec =
|
|
96
|
-
plan.brief === undefined || plan.brief.length === 0
|
|
97
|
-
? Plan.make({ ...plan, brief: spec })
|
|
98
|
-
: plan
|
|
99
|
-
if (planWithSpec !== plan) {
|
|
100
|
-
yield* store.save(planPath, planWithSpec)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
yield* stage(context.events, "branch", context.git.checkoutOrCreate(planWithSpec.epicId))
|
|
104
|
-
const specPath = join(input.workDir, `specs/${planWithSpec.epicId}.md`)
|
|
105
|
-
yield* stage(
|
|
106
|
-
context.events,
|
|
107
|
-
"commit specification",
|
|
108
|
-
nodePlainFileStore
|
|
109
|
-
.read(specPath)
|
|
110
|
-
.pipe(
|
|
111
|
-
Effect.flatMap((existing) =>
|
|
112
|
-
existing === undefined
|
|
113
|
-
? nodePlainFileStore
|
|
114
|
-
.writeAtomic(specPath, `${spec}\n`)
|
|
115
|
-
.pipe(
|
|
116
|
-
Effect.andThen(
|
|
117
|
-
context.git.commitAll(`${planWithSpec.epicId}: specification`)
|
|
118
|
-
),
|
|
119
|
-
Effect.asVoid
|
|
120
|
-
)
|
|
121
|
-
: Effect.void
|
|
122
|
-
)
|
|
123
|
-
)
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
const coderChat = yield* makeChat(context.coder, {
|
|
127
|
-
system:
|
|
128
|
-
"Implement one task at a time. The committed specification is the contract; do not weaken its tests."
|
|
129
|
-
})
|
|
130
|
-
const testGate = lintCommand(
|
|
131
|
-
nodeProcessExecutor,
|
|
132
|
-
context.events,
|
|
133
|
-
["mvn", "-q", "test"],
|
|
134
|
-
input.workDir
|
|
135
|
-
)
|
|
136
|
-
const compileGate = lintCommand(
|
|
137
|
-
nodeProcessExecutor,
|
|
138
|
-
context.events,
|
|
139
|
-
["mvn", "-q", "test-compile"],
|
|
140
|
-
input.workDir
|
|
141
|
-
)
|
|
142
|
-
const firstTitle = planWithSpec.tasks[0]?.title
|
|
143
|
-
|
|
144
|
-
yield* implementTaskLoop(store, context.events, planPath, planWithSpec, (task) =>
|
|
145
|
-
Effect.gen(function* () {
|
|
146
|
-
const testsTask = task.title === firstTitle
|
|
147
|
-
yield* coderChat.ask(planWithSpec.taskPrompt(task))
|
|
148
|
-
yield* reviewAndFixLoop({
|
|
149
|
-
reviewers: minimalReviewers,
|
|
150
|
-
reviewerService: context.reviewers[0] ?? context.reasoning,
|
|
151
|
-
coder: coderChat,
|
|
152
|
-
taskTitle: task.title,
|
|
153
|
-
currentDiff: context.git.diffAll,
|
|
154
|
-
events: context.events,
|
|
155
|
-
lint: testsTask ? compileGate : testGate,
|
|
156
|
-
parallelism: 1
|
|
157
|
-
})
|
|
158
|
-
if (testsTask) {
|
|
159
|
-
const red = yield* testGate
|
|
160
|
-
if (red.isClean) {
|
|
161
|
-
return yield* FlowAborted.make({
|
|
162
|
-
message:
|
|
163
|
-
"the new tests pass before implementation; the specification is not encoded by a red test"
|
|
164
|
-
})
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
yield* context.git.commitAll(`${planWithSpec.epicId}: ${task.title}`)
|
|
168
|
-
})
|
|
169
|
-
)
|
|
170
|
-
yield* stage(
|
|
171
|
-
context.events,
|
|
172
|
-
"verify acceptance criteria",
|
|
173
|
-
Effect.flatMap(testGate, (result) =>
|
|
174
|
-
result.isClean ? Effect.void : Effect.fail(verificationFailure(result))
|
|
175
|
-
)
|
|
176
|
-
)
|
|
177
|
-
})
|
|
178
|
-
)
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
runFlowMain(program)
|