lalph 0.2.20 → 0.3.0
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.mjs +794 -470
- package/package.json +3 -3
- package/src/Agents/chooser.ts +8 -8
- package/src/Agents/planner.ts +5 -7
- package/src/Agents/reviewer.ts +6 -8
- package/src/Agents/tasker.ts +6 -8
- package/src/Agents/timeout.ts +6 -8
- package/src/Agents/worker.ts +6 -8
- package/src/CurrentIssueSource.ts +12 -0
- package/src/GitFlow.ts +13 -11
- package/src/Github/Cli.ts +5 -5
- package/src/Github.ts +43 -1
- package/src/IssueSource.ts +18 -1
- package/src/Linear.ts +52 -1
- package/src/Presets.ts +165 -0
- package/src/Projects.ts +7 -1
- package/src/PromptGen.ts +9 -6
- package/src/Worktree.ts +4 -4
- package/src/cli.ts +4 -3
- package/src/commands/agents/add.ts +11 -0
- package/src/commands/agents/edit.ts +25 -0
- package/src/commands/agents/ls.ts +43 -0
- package/src/commands/agents/rm.ts +26 -0
- package/src/commands/agents.ts +22 -0
- package/src/commands/issue.ts +18 -22
- package/src/commands/plan/tasks.ts +6 -6
- package/src/commands/plan.ts +4 -13
- package/src/commands/root.ts +13 -21
- package/src/domain/CliAgent.ts +120 -51
- package/src/domain/CliAgentPreset.ts +55 -0
- package/src/commands/agent.ts +0 -70
package/src/Presets.ts
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { Array, Effect, Option, Schema } from "effect"
|
|
2
|
+
import { Setting, Settings } from "./Settings.ts"
|
|
3
|
+
import { CliAgentPreset, CliAgentPresetId } from "./domain/CliAgentPreset.ts"
|
|
4
|
+
import { Prompt } from "effect/unstable/cli"
|
|
5
|
+
import { allCliAgents, type AnyCliAgent } from "./domain/CliAgent.ts"
|
|
6
|
+
import { parseCommand } from "./shared/child-process.ts"
|
|
7
|
+
import { IssueSource } from "./IssueSource.ts"
|
|
8
|
+
|
|
9
|
+
export const allCliAgentPresets = new Setting(
|
|
10
|
+
"cliAgentPresets",
|
|
11
|
+
Schema.Array(CliAgentPreset),
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
export const getAllCliAgentPresets = Settings.get(allCliAgentPresets).pipe(
|
|
15
|
+
Effect.map(Option.getOrElse((): ReadonlyArray<CliAgentPreset> => [])),
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
export const getPresetsWithMetadata = <S extends Schema.Top>(
|
|
19
|
+
source: string,
|
|
20
|
+
schema: S,
|
|
21
|
+
) =>
|
|
22
|
+
getAllCliAgentPresets.pipe(
|
|
23
|
+
Effect.flatMap(
|
|
24
|
+
Effect.forEach((preset) =>
|
|
25
|
+
preset.decodeMetadata(source, schema).pipe(
|
|
26
|
+
Effect.map(
|
|
27
|
+
Option.map((metadata) => ({
|
|
28
|
+
preset,
|
|
29
|
+
metadata,
|
|
30
|
+
})),
|
|
31
|
+
),
|
|
32
|
+
),
|
|
33
|
+
),
|
|
34
|
+
),
|
|
35
|
+
Effect.map(Array.getSomes),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
export const cliAgentPresetById = Effect.fnUntraced(function* (
|
|
39
|
+
presetId: CliAgentPresetId,
|
|
40
|
+
) {
|
|
41
|
+
const presets = yield* getAllCliAgentPresets
|
|
42
|
+
return Array.findFirst(presets, (p) => p.id === presetId)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
export const getDefaultCliAgentPreset = Effect.gen(function* () {
|
|
46
|
+
const presets = yield* getAllCliAgentPresets
|
|
47
|
+
const preset = presets.find((p) => p.id === CliAgentPreset.defaultId)
|
|
48
|
+
return preset ?? (yield* welcomeWizard)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
export const welcomeWizard = Effect.gen(function* () {
|
|
52
|
+
const welcome = [
|
|
53
|
+
" .--.",
|
|
54
|
+
" |^()^| lalph",
|
|
55
|
+
" '--'",
|
|
56
|
+
"",
|
|
57
|
+
"Let's setup your default AI agent preset.",
|
|
58
|
+
"AI agent presets let you configure what cli agent lalph",
|
|
59
|
+
"uses to run tasks.",
|
|
60
|
+
"",
|
|
61
|
+
].join("\n")
|
|
62
|
+
console.log(welcome)
|
|
63
|
+
return yield* addOrUpdatePreset({
|
|
64
|
+
idOverride: CliAgentPreset.defaultId,
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
export const selectCliAgentPreset = Effect.gen(function* () {
|
|
69
|
+
const presets = yield* getAllCliAgentPresets
|
|
70
|
+
if (presets.length === 0) {
|
|
71
|
+
return yield* welcomeWizard
|
|
72
|
+
} else if (presets.length === 1) {
|
|
73
|
+
const preset = presets[0]!
|
|
74
|
+
yield* Effect.log(`Using agent preset: ${preset.id}`)
|
|
75
|
+
return preset
|
|
76
|
+
}
|
|
77
|
+
const selection = yield* Prompt.autoComplete({
|
|
78
|
+
message: "Select a preset:",
|
|
79
|
+
choices: presets.map((p) => ({
|
|
80
|
+
title: p.id,
|
|
81
|
+
value: p,
|
|
82
|
+
})),
|
|
83
|
+
})
|
|
84
|
+
return selection!
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
export const addOrUpdatePreset = Effect.fnUntraced(function* (options?: {
|
|
88
|
+
readonly existing?: CliAgentPreset
|
|
89
|
+
readonly idOverride?: CliAgentPresetId
|
|
90
|
+
}) {
|
|
91
|
+
const presets = yield* getAllCliAgentPresets
|
|
92
|
+
|
|
93
|
+
const id = options?.existing
|
|
94
|
+
? options.existing.id
|
|
95
|
+
: (options?.idOverride ??
|
|
96
|
+
CliAgentPresetId.makeUnsafe(
|
|
97
|
+
yield* Prompt.text({
|
|
98
|
+
message: "Preset name",
|
|
99
|
+
validate(input) {
|
|
100
|
+
input = input.trim()
|
|
101
|
+
if (input.length === 0) {
|
|
102
|
+
return Effect.fail("Preset name cannot be empty")
|
|
103
|
+
} else if (presets.some((p) => p.id === input)) {
|
|
104
|
+
return Effect.fail("Preset already exists")
|
|
105
|
+
}
|
|
106
|
+
return Effect.succeed(input)
|
|
107
|
+
},
|
|
108
|
+
}),
|
|
109
|
+
))
|
|
110
|
+
|
|
111
|
+
const cliAgent = yield* selectCliAgent(options?.existing?.cliAgent.id)
|
|
112
|
+
const extraArgs = yield* Prompt.text({
|
|
113
|
+
message: "Extra arguments? (leave empty for none)",
|
|
114
|
+
default: options?.existing?.extraArgs.join(" ") ?? "",
|
|
115
|
+
})
|
|
116
|
+
.asEffect()
|
|
117
|
+
.pipe(Effect.map(parseCommand))
|
|
118
|
+
const commandPrefix = yield* promptForCommandPrefix(
|
|
119
|
+
options?.existing?.commandPrefix,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
let preset = new CliAgentPreset({
|
|
123
|
+
id,
|
|
124
|
+
cliAgent,
|
|
125
|
+
commandPrefix,
|
|
126
|
+
extraArgs,
|
|
127
|
+
sourceMetadata: {},
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
if (id !== CliAgentPreset.defaultId) {
|
|
131
|
+
const source = yield* IssueSource
|
|
132
|
+
preset = yield* source.updateCliAgentPreset(preset)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
yield* Settings.set(
|
|
136
|
+
allCliAgentPresets,
|
|
137
|
+
Option.some(
|
|
138
|
+
options?.existing
|
|
139
|
+
? presets.map((p) => (p.id === preset.id ? preset : p))
|
|
140
|
+
: [...presets, preset],
|
|
141
|
+
),
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
return preset
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
const selectCliAgent = (initial?: AnyCliAgent["id"]) =>
|
|
148
|
+
Prompt.select({
|
|
149
|
+
message: "Select the CLI agent to use",
|
|
150
|
+
choices: allCliAgents.map((agent) => ({
|
|
151
|
+
title: agent.name,
|
|
152
|
+
value: agent,
|
|
153
|
+
selected: agent.id === initial,
|
|
154
|
+
})),
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
const promptForCommandPrefix = Effect.fnUntraced(function* (
|
|
158
|
+
initial?: ReadonlyArray<string>,
|
|
159
|
+
) {
|
|
160
|
+
const prefix = yield* Prompt.text({
|
|
161
|
+
message: "Command prefix? (leave empty for none)",
|
|
162
|
+
default: initial ? initial.join(" ") : "",
|
|
163
|
+
})
|
|
164
|
+
return parseCommand(prefix)
|
|
165
|
+
})
|
package/src/Projects.ts
CHANGED
|
@@ -135,7 +135,7 @@ export const welcomeWizard = Effect.gen(function* () {
|
|
|
135
135
|
" |^()^| lalph",
|
|
136
136
|
" '--'",
|
|
137
137
|
"",
|
|
138
|
-
"
|
|
138
|
+
"Let's add your first project.",
|
|
139
139
|
"Projects let you configure how lalph runs tasks.",
|
|
140
140
|
"",
|
|
141
141
|
].join("\n")
|
|
@@ -168,6 +168,9 @@ export const addOrUpdateProject = Effect.fnUntraced(function* (
|
|
|
168
168
|
const targetBranch = pipe(
|
|
169
169
|
yield* Prompt.text({
|
|
170
170
|
message: "Target branch (leave empty to use HEAD)",
|
|
171
|
+
default: existing
|
|
172
|
+
? Option.getOrElse(existing.targetBranch, () => "")
|
|
173
|
+
: "",
|
|
171
174
|
}),
|
|
172
175
|
String.trim,
|
|
173
176
|
Option.liftPredicate(String.isNonEmpty),
|
|
@@ -179,16 +182,19 @@ export const addOrUpdateProject = Effect.fnUntraced(function* (
|
|
|
179
182
|
title: "Pull Request",
|
|
180
183
|
description: "Create a pull request for each task",
|
|
181
184
|
value: "pr",
|
|
185
|
+
selected: existing ? existing.gitFlow === "pr" : false,
|
|
182
186
|
},
|
|
183
187
|
{
|
|
184
188
|
title: "Commit",
|
|
185
189
|
description: "Tasks are committed directly to the target branch",
|
|
186
190
|
value: "commit",
|
|
191
|
+
selected: existing ? existing.gitFlow === "pr" : false,
|
|
187
192
|
},
|
|
188
193
|
] as const,
|
|
189
194
|
})
|
|
190
195
|
const reviewAgent = yield* Prompt.toggle({
|
|
191
196
|
message: "Enable review agent?",
|
|
197
|
+
initial: existing ? existing.reviewAgent : true,
|
|
192
198
|
})
|
|
193
199
|
|
|
194
200
|
const project = new Project({
|
package/src/PromptGen.ts
CHANGED
|
@@ -156,9 +156,12 @@ ${options.task.description}
|
|
|
156
156
|
|
|
157
157
|
# Instructions
|
|
158
158
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
Your job is to implement the task described above.
|
|
160
|
+
|
|
161
|
+
1. Carefully study the prd.yml file to understand the context of the task, and
|
|
162
|
+
discover any key learnings from previous work.
|
|
163
|
+
Also read the ${options.specsDirectory}/README.md file (if available), to see
|
|
164
|
+
if any previous specifications could assist you.
|
|
162
165
|
2. ${options.gitFlow.setupInstructions(options)}
|
|
163
166
|
3. Implement the task.
|
|
164
167
|
- If this task is a research task, **do not** make any code changes yet.
|
|
@@ -172,6 +175,7 @@ ${options.task.description}
|
|
|
172
175
|
5. ${options.gitFlow.commitInstructions({
|
|
173
176
|
githubPrInstructions: sourceMeta.githubPrInstructions,
|
|
174
177
|
githubPrNumber: options.githubPrNumber,
|
|
178
|
+
taskId: options.task.id ?? "unknown",
|
|
175
179
|
targetBranch: options.targetBranch,
|
|
176
180
|
})}
|
|
177
181
|
6. **After ${options.gitFlow.requiresGithubPr ? "pushing" : "committing"}**
|
|
@@ -235,9 +239,9 @@ ${prdNotes(options)}`
|
|
|
235
239
|
const planPrompt = (options: {
|
|
236
240
|
readonly plan: string
|
|
237
241
|
readonly specsDirectory: string
|
|
238
|
-
}) => `<request
|
|
242
|
+
}) => `<request>
|
|
239
243
|
${options.plan}
|
|
240
|
-
|
|
244
|
+
</request>
|
|
241
245
|
|
|
242
246
|
## Instructions
|
|
243
247
|
|
|
@@ -260,7 +264,6 @@ ${options.plan}
|
|
|
260
264
|
}
|
|
261
265
|
\`\`\`
|
|
262
266
|
5. Present the saved specification for review (include the full text in your response).
|
|
263
|
-
If any corrections are needed, update the specification and adjust the plan tasks accordingly.
|
|
264
267
|
|
|
265
268
|
**Important:** You are only creating or updating a plan, not implementing any tasks yet.
|
|
266
269
|
|
package/src/Worktree.ts
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "effect"
|
|
17
17
|
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
|
|
18
18
|
import { RunnerStalled } from "./domain/Errors.ts"
|
|
19
|
-
import type {
|
|
19
|
+
import type { AnyCliAgent } from "./domain/CliAgent.ts"
|
|
20
20
|
import { constWorkerMaxOutputChunks, CurrentWorkerState } from "./Workers.ts"
|
|
21
21
|
import { AtomRegistry } from "effect/unstable/reactivity"
|
|
22
22
|
import { CurrentProjectId } from "./Settings.ts"
|
|
@@ -194,7 +194,7 @@ const makeExecHelpers = Effect.fnUntraced(function* (options: {
|
|
|
194
194
|
provide,
|
|
195
195
|
)
|
|
196
196
|
|
|
197
|
-
const execWithOutput = (options: { readonly cliAgent:
|
|
197
|
+
const execWithOutput = (options: { readonly cliAgent: AnyCliAgent }) =>
|
|
198
198
|
Effect.fnUntraced(function* (command: ChildProcess.Command) {
|
|
199
199
|
const handle = yield* provide(command.asEffect())
|
|
200
200
|
|
|
@@ -213,7 +213,7 @@ const makeExecHelpers = Effect.fnUntraced(function* (options: {
|
|
|
213
213
|
return yield* handle.exitCode
|
|
214
214
|
}, Effect.scoped)
|
|
215
215
|
|
|
216
|
-
const execWithWorkerOutput = (options: { readonly cliAgent:
|
|
216
|
+
const execWithWorkerOutput = (options: { readonly cliAgent: AnyCliAgent }) =>
|
|
217
217
|
Effect.fnUntraced(function* (command: ChildProcess.Command) {
|
|
218
218
|
const registry = yield* AtomRegistry.AtomRegistry
|
|
219
219
|
const worker = yield* CurrentWorkerState
|
|
@@ -244,7 +244,7 @@ const makeExecHelpers = Effect.fnUntraced(function* (options: {
|
|
|
244
244
|
|
|
245
245
|
const execWithStallTimeout = (options: {
|
|
246
246
|
readonly stallTimeout: Duration.Duration
|
|
247
|
-
readonly cliAgent:
|
|
247
|
+
readonly cliAgent: AnyCliAgent
|
|
248
248
|
}) =>
|
|
249
249
|
Effect.fnUntraced(function* (command: ChildProcess.Command) {
|
|
250
250
|
const registry = yield* AtomRegistry.AtomRegistry
|
package/src/cli.ts
CHANGED
|
@@ -9,7 +9,6 @@ import { commandPlan } from "./commands/plan.ts"
|
|
|
9
9
|
import { commandIssue, commandIssueAlias } from "./commands/issue.ts"
|
|
10
10
|
import { commandEdit, commandEditAlias } from "./commands/edit.ts"
|
|
11
11
|
import { commandSource } from "./commands/source.ts"
|
|
12
|
-
import { commandAgent } from "./commands/agent.ts"
|
|
13
12
|
import PackageJson from "../package.json" with { type: "json" }
|
|
14
13
|
import { TracingLayer } from "./Tracing.ts"
|
|
15
14
|
import { MinimumLogLevel } from "effect/References"
|
|
@@ -17,6 +16,7 @@ import { atomRuntime, lalphMemoMap } from "./shared/runtime.ts"
|
|
|
17
16
|
import { PlatformServices } from "./shared/platform.ts"
|
|
18
17
|
import { commandProjects, commandProjectsAlias } from "./commands/projects.ts"
|
|
19
18
|
import { commandSh } from "./commands/sh.ts"
|
|
19
|
+
import { commandAgents, commandAgentsAlias } from "./commands/agents.ts"
|
|
20
20
|
|
|
21
21
|
commandRoot.pipe(
|
|
22
22
|
Command.withSubcommands([
|
|
@@ -25,10 +25,11 @@ commandRoot.pipe(
|
|
|
25
25
|
commandEdit,
|
|
26
26
|
commandSh,
|
|
27
27
|
commandSource,
|
|
28
|
-
|
|
28
|
+
commandAgents,
|
|
29
29
|
commandProjects,
|
|
30
|
-
|
|
30
|
+
commandAgentsAlias,
|
|
31
31
|
commandEditAlias,
|
|
32
|
+
commandIssueAlias,
|
|
32
33
|
commandProjectsAlias,
|
|
33
34
|
]),
|
|
34
35
|
Command.provide(Settings.layer),
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Command } from "effect/unstable/cli"
|
|
2
|
+
import { CurrentIssueSource } from "../../CurrentIssueSource.ts"
|
|
3
|
+
import { Settings } from "../../Settings.ts"
|
|
4
|
+
import { addOrUpdatePreset } from "../../Presets.ts"
|
|
5
|
+
|
|
6
|
+
export const commandAgentsAdd = Command.make("add").pipe(
|
|
7
|
+
Command.withDescription("Add a new CLI agent preset"),
|
|
8
|
+
Command.withHandler(() => addOrUpdatePreset()),
|
|
9
|
+
Command.provide(Settings.layer),
|
|
10
|
+
Command.provide(CurrentIssueSource.layer),
|
|
11
|
+
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Effect } from "effect"
|
|
2
|
+
import { Command } from "effect/unstable/cli"
|
|
3
|
+
import { Settings } from "../../Settings.ts"
|
|
4
|
+
import { CurrentIssueSource } from "../../CurrentIssueSource.ts"
|
|
5
|
+
import {
|
|
6
|
+
addOrUpdatePreset,
|
|
7
|
+
getAllCliAgentPresets,
|
|
8
|
+
selectCliAgentPreset,
|
|
9
|
+
} from "../../Presets.ts"
|
|
10
|
+
|
|
11
|
+
export const commandAgentsEdit = Command.make("edit").pipe(
|
|
12
|
+
Command.withDescription("Modify a CLI agent preset"),
|
|
13
|
+
Command.withHandler(
|
|
14
|
+
Effect.fnUntraced(function* () {
|
|
15
|
+
const projects = yield* getAllCliAgentPresets
|
|
16
|
+
if (projects.length === 0) {
|
|
17
|
+
return yield* Effect.log("No presets available to edit.")
|
|
18
|
+
}
|
|
19
|
+
const preset = yield* selectCliAgentPreset
|
|
20
|
+
yield* addOrUpdatePreset({ existing: preset })
|
|
21
|
+
}),
|
|
22
|
+
),
|
|
23
|
+
Command.provide(Settings.layer),
|
|
24
|
+
Command.provide(CurrentIssueSource.layer),
|
|
25
|
+
)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Effect } from "effect"
|
|
2
|
+
import { Command } from "effect/unstable/cli"
|
|
3
|
+
import { IssueSource } from "../../IssueSource.ts"
|
|
4
|
+
import { CurrentIssueSource } from "../../CurrentIssueSource.ts"
|
|
5
|
+
import { Settings } from "../../Settings.ts"
|
|
6
|
+
import { getAllCliAgentPresets } from "../../Presets.ts"
|
|
7
|
+
|
|
8
|
+
export const commandAgentsLs = Command.make("ls").pipe(
|
|
9
|
+
Command.withDescription("List all configured CLI agent presets"),
|
|
10
|
+
Command.withHandler(
|
|
11
|
+
Effect.fnUntraced(function* () {
|
|
12
|
+
const meta = yield* CurrentIssueSource
|
|
13
|
+
const source = yield* IssueSource
|
|
14
|
+
|
|
15
|
+
console.log("Issue source:", meta.name)
|
|
16
|
+
console.log("")
|
|
17
|
+
|
|
18
|
+
const presets = yield* getAllCliAgentPresets
|
|
19
|
+
|
|
20
|
+
if (presets.length === 0) {
|
|
21
|
+
console.log(
|
|
22
|
+
"No presets configured yet. Run 'lalph agents add' to get started.",
|
|
23
|
+
)
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
for (const preset of presets) {
|
|
28
|
+
console.log(`Preset: ${preset.id}`)
|
|
29
|
+
yield* source.cliAgentPresetInfo(preset)
|
|
30
|
+
console.log(` CLI agent: ${preset.cliAgent.name}`)
|
|
31
|
+
if (preset.extraArgs.length > 0) {
|
|
32
|
+
console.log(` Extra args: ${preset.extraArgs.join(" ")}`)
|
|
33
|
+
}
|
|
34
|
+
if (preset.commandPrefix.length > 0) {
|
|
35
|
+
console.log(` Command prefix: ${preset.commandPrefix.join(" ")}`)
|
|
36
|
+
}
|
|
37
|
+
console.log("")
|
|
38
|
+
}
|
|
39
|
+
}),
|
|
40
|
+
),
|
|
41
|
+
Command.provide(Settings.layer),
|
|
42
|
+
Command.provide(CurrentIssueSource.layer),
|
|
43
|
+
)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Effect, Option } from "effect"
|
|
2
|
+
import { Command } from "effect/unstable/cli"
|
|
3
|
+
import { Settings } from "../../Settings.ts"
|
|
4
|
+
import { CurrentIssueSource } from "../../CurrentIssueSource.ts"
|
|
5
|
+
import {
|
|
6
|
+
allCliAgentPresets,
|
|
7
|
+
getAllCliAgentPresets,
|
|
8
|
+
selectCliAgentPreset,
|
|
9
|
+
} from "../../Presets.ts"
|
|
10
|
+
|
|
11
|
+
export const commandAgentsRm = Command.make("rm").pipe(
|
|
12
|
+
Command.withDescription("Remove a CLI agent preset"),
|
|
13
|
+
Command.withHandler(
|
|
14
|
+
Effect.fnUntraced(function* () {
|
|
15
|
+
const presets = yield* getAllCliAgentPresets
|
|
16
|
+
if (presets.length === 0) {
|
|
17
|
+
return yield* Effect.log("There are no presets to remove.")
|
|
18
|
+
}
|
|
19
|
+
const preset = yield* selectCliAgentPreset
|
|
20
|
+
const newPresets = presets.filter((p) => p.id !== preset.id)
|
|
21
|
+
yield* Settings.set(allCliAgentPresets, Option.some(newPresets))
|
|
22
|
+
}),
|
|
23
|
+
),
|
|
24
|
+
Command.provide(Settings.layer),
|
|
25
|
+
Command.provide(CurrentIssueSource.layer),
|
|
26
|
+
)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Command } from "effect/unstable/cli"
|
|
2
|
+
import { commandAgentsLs } from "./agents/ls.ts"
|
|
3
|
+
import { commandAgentsAdd } from "./agents/add.ts"
|
|
4
|
+
import { commandAgentsRm } from "./agents/rm.ts"
|
|
5
|
+
import { commandAgentsEdit } from "./agents/edit.ts"
|
|
6
|
+
|
|
7
|
+
const subcommands = Command.withSubcommands([
|
|
8
|
+
commandAgentsLs,
|
|
9
|
+
commandAgentsAdd,
|
|
10
|
+
commandAgentsEdit,
|
|
11
|
+
commandAgentsRm,
|
|
12
|
+
])
|
|
13
|
+
|
|
14
|
+
export const commandAgents = Command.make("agents").pipe(
|
|
15
|
+
Command.withDescription("Manage CLI agent presets"),
|
|
16
|
+
subcommands,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
export const commandAgentsAlias = Command.make("a").pipe(
|
|
20
|
+
Command.withDescription("Alias for 'agents' command"),
|
|
21
|
+
subcommands,
|
|
22
|
+
)
|
package/src/commands/issue.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from "effect/unstable/cli"
|
|
2
2
|
import { CurrentIssueSource } from "../CurrentIssueSource.ts"
|
|
3
|
-
import { Effect, flow,
|
|
3
|
+
import { Effect, flow, Option, Schema } from "effect"
|
|
4
4
|
import { IssueSource } from "../IssueSource.ts"
|
|
5
5
|
import { PrdIssue } from "../domain/PrdIssue.ts"
|
|
6
6
|
import * as Yaml from "yaml"
|
|
@@ -31,8 +31,6 @@ const FrontMatterSchema = Schema.toCodecJson(
|
|
|
31
31
|
const handler = flow(
|
|
32
32
|
Command.withHandler(
|
|
33
33
|
Effect.fnUntraced(function* () {
|
|
34
|
-
const source = yield* IssueSource
|
|
35
|
-
const projectId = yield* CurrentProjectId
|
|
36
34
|
const editor = yield* Editor
|
|
37
35
|
|
|
38
36
|
const content = yield* editor.editTemp({
|
|
@@ -66,26 +64,24 @@ const handler = flow(
|
|
|
66
64
|
)
|
|
67
65
|
const description = lines.slice(descriptionStartIndex).join("\n").trim()
|
|
68
66
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
CurrentIssueSource.layer,
|
|
86
|
-
Editor.layer,
|
|
87
|
-
),
|
|
67
|
+
yield* Effect.gen(function* () {
|
|
68
|
+
const source = yield* IssueSource
|
|
69
|
+
const projectId = yield* CurrentProjectId
|
|
70
|
+
const created = yield* source.createIssue(
|
|
71
|
+
projectId,
|
|
72
|
+
new PrdIssue({
|
|
73
|
+
id: null,
|
|
74
|
+
...frontMatter,
|
|
75
|
+
description,
|
|
76
|
+
state: "todo",
|
|
77
|
+
}),
|
|
78
|
+
)
|
|
79
|
+
console.log(`Created issue with ID: ${created.id}`)
|
|
80
|
+
console.log(`URL: ${created.url}`)
|
|
81
|
+
}).pipe(Effect.provide([layerProjectIdPrompt, CurrentIssueSource.layer]))
|
|
82
|
+
}),
|
|
88
83
|
),
|
|
84
|
+
Command.provide(Editor.layer),
|
|
89
85
|
)
|
|
90
86
|
|
|
91
87
|
export const commandIssue = Command.make("issue").pipe(
|
|
@@ -6,8 +6,9 @@ import { layerProjectIdPrompt } from "../../Projects.ts"
|
|
|
6
6
|
import { PromptGen } from "../../PromptGen.ts"
|
|
7
7
|
import { Settings } from "../../Settings.ts"
|
|
8
8
|
import { Worktree } from "../../Worktree.ts"
|
|
9
|
-
import { getCommandPrefix, getOrSelectCliAgent } from "../agent.ts"
|
|
10
9
|
import { commandRoot } from "../root.ts"
|
|
10
|
+
import { getDefaultCliAgentPreset } from "../../Presets.ts"
|
|
11
|
+
import { CurrentIssueSource } from "../../CurrentIssueSource.ts"
|
|
11
12
|
|
|
12
13
|
const specificationPath = Argument.path("spec", {
|
|
13
14
|
pathType: "file",
|
|
@@ -29,8 +30,7 @@ export const commandPlanTasks = Command.make("tasks", {
|
|
|
29
30
|
const fs = yield* FileSystem.FileSystem
|
|
30
31
|
const pathService = yield* Path.Path
|
|
31
32
|
const worktree = yield* Worktree
|
|
32
|
-
const
|
|
33
|
-
const commandPrefix = yield* getCommandPrefix
|
|
33
|
+
const preset = yield* getDefaultCliAgentPreset
|
|
34
34
|
|
|
35
35
|
const content = yield* fs.readFileString(specificationPath)
|
|
36
36
|
const relative = pathService.relative(
|
|
@@ -46,15 +46,15 @@ export const commandPlanTasks = Command.make("tasks", {
|
|
|
46
46
|
yield* agentTasker({
|
|
47
47
|
specsDirectory,
|
|
48
48
|
specificationPath: relative,
|
|
49
|
-
|
|
50
|
-
cliAgent,
|
|
49
|
+
preset,
|
|
51
50
|
})
|
|
52
51
|
},
|
|
53
52
|
Effect.provide([
|
|
54
53
|
Settings.layer,
|
|
55
54
|
PromptGen.layer,
|
|
56
55
|
Prd.layerProvided.pipe(Layer.provide(layerProjectIdPrompt)),
|
|
57
|
-
|
|
56
|
+
CurrentIssueSource.layer,
|
|
57
|
+
Settings.layer,
|
|
58
58
|
]),
|
|
59
59
|
),
|
|
60
60
|
),
|
package/src/commands/plan.ts
CHANGED
|
@@ -2,8 +2,6 @@ import { Data, Effect, FileSystem, Option, Path, pipe, Schema } from "effect"
|
|
|
2
2
|
import { PromptGen } from "../PromptGen.ts"
|
|
3
3
|
import { Prd } from "../Prd.ts"
|
|
4
4
|
import { Worktree } from "../Worktree.ts"
|
|
5
|
-
import type { ChildProcess } from "effect/unstable/process"
|
|
6
|
-
import { getCommandPrefix, getOrSelectCliAgent } from "./agent.ts"
|
|
7
5
|
import { Command, Flag } from "effect/unstable/cli"
|
|
8
6
|
import { CurrentIssueSource } from "../CurrentIssueSource.ts"
|
|
9
7
|
import { commandRoot } from "./root.ts"
|
|
@@ -13,6 +11,7 @@ import { agentPlanner } from "../Agents/planner.ts"
|
|
|
13
11
|
import { agentTasker } from "../Agents/tasker.ts"
|
|
14
12
|
import { commandPlanTasks } from "./plan/tasks.ts"
|
|
15
13
|
import { Editor } from "../Editor.ts"
|
|
14
|
+
import { getDefaultCliAgentPreset } from "../Presets.ts"
|
|
16
15
|
|
|
17
16
|
const dangerous = Flag.boolean("dangerous").pipe(
|
|
18
17
|
Flag.withAlias("d"),
|
|
@@ -45,13 +44,11 @@ export const commandPlan = Command.make("plan", {
|
|
|
45
44
|
? yield* addOrUpdateProject()
|
|
46
45
|
: yield* selectProject
|
|
47
46
|
const { specsDirectory } = yield* commandRoot
|
|
48
|
-
const commandPrefix = yield* getCommandPrefix
|
|
49
47
|
|
|
50
48
|
yield* plan({
|
|
51
49
|
plan: thePlan.value,
|
|
52
50
|
specsDirectory,
|
|
53
51
|
targetBranch: project.targetBranch,
|
|
54
|
-
commandPrefix,
|
|
55
52
|
dangerous,
|
|
56
53
|
}).pipe(Effect.provideService(CurrentProjectId, project.id))
|
|
57
54
|
},
|
|
@@ -66,23 +63,18 @@ const plan = Effect.fnUntraced(
|
|
|
66
63
|
readonly plan: string
|
|
67
64
|
readonly specsDirectory: string
|
|
68
65
|
readonly targetBranch: Option.Option<string>
|
|
69
|
-
readonly commandPrefix: (
|
|
70
|
-
command: ChildProcess.Command,
|
|
71
|
-
) => ChildProcess.Command
|
|
72
66
|
readonly dangerous: boolean
|
|
73
67
|
}) {
|
|
74
68
|
const fs = yield* FileSystem.FileSystem
|
|
75
69
|
const pathService = yield* Path.Path
|
|
76
70
|
const worktree = yield* Worktree
|
|
77
|
-
|
|
78
|
-
const cliAgent = yield* getOrSelectCliAgent
|
|
71
|
+
const preset = yield* getDefaultCliAgentPreset
|
|
79
72
|
|
|
80
73
|
yield* agentPlanner({
|
|
81
74
|
plan: options.plan,
|
|
82
75
|
specsDirectory: options.specsDirectory,
|
|
83
|
-
commandPrefix: options.commandPrefix,
|
|
84
76
|
dangerous: options.dangerous,
|
|
85
|
-
|
|
77
|
+
preset,
|
|
86
78
|
})
|
|
87
79
|
|
|
88
80
|
const planDetails = yield* pipe(
|
|
@@ -98,8 +90,7 @@ const plan = Effect.fnUntraced(
|
|
|
98
90
|
yield* agentTasker({
|
|
99
91
|
specificationPath: planDetails.specification,
|
|
100
92
|
specsDirectory: options.specsDirectory,
|
|
101
|
-
|
|
102
|
-
cliAgent,
|
|
93
|
+
preset,
|
|
103
94
|
})
|
|
104
95
|
|
|
105
96
|
if (!worktree.inExisting) {
|