lalph 0.3.44 → 0.3.45
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 +33427 -29455
- package/package.json +7 -8
- package/src/Agents/chooser.ts +35 -1
- package/src/Agents/reviewer.ts +25 -0
- package/src/Agents/tasker.ts +16 -0
- package/src/Agents/timeout.ts +17 -0
- package/src/Agents/worker.ts +15 -0
- package/src/Clanka.ts +6 -3
- package/src/ClankaModels.ts +59 -40
- package/src/Presets.ts +0 -18
- package/src/PromptGen.ts +28 -14
- package/src/commands/agents/ls.ts +0 -1
- package/src/commands/plan/tasks.ts +3 -1
- package/src/commands/plan.ts +8 -1
- package/src/commands/root.ts +14 -276
- package/src/domain/CliAgent.ts +30 -2
- package/src/domain/CliAgentPreset.ts +0 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lalph",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.45",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -22,18 +22,17 @@
|
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@changesets/changelog-github": "^0.6.0",
|
|
24
24
|
"@changesets/cli": "^2.30.0",
|
|
25
|
-
"@effect/ai-openai": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/ai-openai@d440fd4",
|
|
26
25
|
"@effect/language-service": "^0.79.0",
|
|
27
|
-
"@effect/platform-node": "4.0.0-beta.
|
|
28
|
-
"@linear/sdk": "^
|
|
26
|
+
"@effect/platform-node": "4.0.0-beta.30",
|
|
27
|
+
"@linear/sdk": "^77.0.0",
|
|
29
28
|
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
|
|
30
29
|
"@octokit/types": "^16.0.0",
|
|
31
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
32
|
-
"clanka": "^0.0.
|
|
30
|
+
"@typescript/native-preview": "7.0.0-dev.20260310.1",
|
|
31
|
+
"clanka": "^0.0.15",
|
|
33
32
|
"concurrently": "^9.2.1",
|
|
34
|
-
"effect": "4.0.0-beta.
|
|
33
|
+
"effect": "4.0.0-beta.30",
|
|
35
34
|
"husky": "^9.1.7",
|
|
36
|
-
"lint-staged": "^16.3.
|
|
35
|
+
"lint-staged": "^16.3.3",
|
|
37
36
|
"octokit": "^5.0.5",
|
|
38
37
|
"oxlint": "^1.52.0",
|
|
39
38
|
"prettier": "^3.8.1",
|
package/src/Agents/chooser.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Data,
|
|
3
|
+
Deferred,
|
|
4
|
+
Duration,
|
|
5
|
+
Effect,
|
|
6
|
+
FileSystem,
|
|
7
|
+
Path,
|
|
8
|
+
pipe,
|
|
9
|
+
Schema,
|
|
10
|
+
} from "effect"
|
|
2
11
|
import { PromptGen } from "../PromptGen.ts"
|
|
3
12
|
import { Prd } from "../Prd.ts"
|
|
4
13
|
import { ChildProcess } from "effect/unstable/process"
|
|
@@ -7,6 +16,8 @@ import { RunnerStalled } from "../domain/Errors.ts"
|
|
|
7
16
|
import { makeWaitForFile } from "../shared/fs.ts"
|
|
8
17
|
import { GitFlow } from "../GitFlow.ts"
|
|
9
18
|
import type { CliAgentPreset } from "../domain/CliAgentPreset.ts"
|
|
19
|
+
import { runClanka } from "../Clanka.ts"
|
|
20
|
+
import { ChosenTaskDeferred } from "../TaskTools.ts"
|
|
10
21
|
|
|
11
22
|
export const agentChooser = Effect.fnUntraced(function* (options: {
|
|
12
23
|
readonly stallTimeout: Duration.Duration
|
|
@@ -20,6 +31,29 @@ export const agentChooser = Effect.fnUntraced(function* (options: {
|
|
|
20
31
|
const gitFlow = yield* GitFlow
|
|
21
32
|
const waitForFile = yield* makeWaitForFile
|
|
22
33
|
|
|
34
|
+
// use clanka
|
|
35
|
+
if (!options.preset.cliAgent.command) {
|
|
36
|
+
const deferred = ChosenTaskDeferred.of(Deferred.makeUnsafe())
|
|
37
|
+
const result = yield* runClanka({
|
|
38
|
+
directory: worktree.directory,
|
|
39
|
+
model: options.preset.extraArgs.join(" "),
|
|
40
|
+
prompt: promptGen.promptChooseClanka({ gitFlow }),
|
|
41
|
+
stallTimeout: options.stallTimeout,
|
|
42
|
+
withChoose: true,
|
|
43
|
+
}).pipe(
|
|
44
|
+
Effect.provideService(ChosenTaskDeferred, deferred),
|
|
45
|
+
Effect.flatMap(() => Effect.fail(new ChosenTaskNotFound())),
|
|
46
|
+
Effect.raceFirst(Deferred.await(deferred)),
|
|
47
|
+
)
|
|
48
|
+
const prdTask = yield* prd.findById(result.taskId)
|
|
49
|
+
if (!prdTask) throw new ChosenTaskNotFound()
|
|
50
|
+
return {
|
|
51
|
+
id: result.taskId,
|
|
52
|
+
githubPrNumber: result.githubPrNumber ?? null,
|
|
53
|
+
prd: prdTask,
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
23
57
|
const taskJsonCreated = waitForFile(
|
|
24
58
|
pathService.join(worktree.directory, ".lalph"),
|
|
25
59
|
"task.json",
|
package/src/Agents/reviewer.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { ChildProcess } from "effect/unstable/process"
|
|
|
4
4
|
import { Worktree } from "../Worktree.ts"
|
|
5
5
|
import { GitFlow } from "../GitFlow.ts"
|
|
6
6
|
import type { CliAgentPreset } from "../domain/CliAgentPreset.ts"
|
|
7
|
+
import { ExitCode } from "effect/unstable/process/ChildProcessSpawner"
|
|
8
|
+
import { runClanka } from "../Clanka.ts"
|
|
7
9
|
|
|
8
10
|
export const agentReviewer = Effect.fnUntraced(function* (options: {
|
|
9
11
|
readonly specsDirectory: string
|
|
@@ -22,6 +24,29 @@ export const agentReviewer = Effect.fnUntraced(function* (options: {
|
|
|
22
24
|
Effect.option,
|
|
23
25
|
)
|
|
24
26
|
|
|
27
|
+
// use clanka
|
|
28
|
+
if (!options.preset.cliAgent.command) {
|
|
29
|
+
yield* runClanka({
|
|
30
|
+
directory: worktree.directory,
|
|
31
|
+
model: options.preset.extraArgs.join(" "),
|
|
32
|
+
system: promptGen.systemClanka(options),
|
|
33
|
+
prompt: Option.match(customInstructions, {
|
|
34
|
+
onNone: () =>
|
|
35
|
+
promptGen.promptReview({
|
|
36
|
+
prompt: options.instructions,
|
|
37
|
+
gitFlow,
|
|
38
|
+
}),
|
|
39
|
+
onSome: (prompt) =>
|
|
40
|
+
promptGen.promptReviewCustom({
|
|
41
|
+
prompt,
|
|
42
|
+
specsDirectory: options.specsDirectory,
|
|
43
|
+
}),
|
|
44
|
+
}),
|
|
45
|
+
stallTimeout: options.stallTimeout,
|
|
46
|
+
})
|
|
47
|
+
return ExitCode(0)
|
|
48
|
+
}
|
|
49
|
+
|
|
25
50
|
const cliCommand = pipe(
|
|
26
51
|
options.preset.cliAgent.command({
|
|
27
52
|
prompt: Option.match(customInstructions, {
|
package/src/Agents/tasker.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { PromptGen } from "../PromptGen.ts"
|
|
|
3
3
|
import { ChildProcess } from "effect/unstable/process"
|
|
4
4
|
import { Worktree } from "../Worktree.ts"
|
|
5
5
|
import type { CliAgentPreset } from "../domain/CliAgentPreset.ts"
|
|
6
|
+
import { ExitCode } from "effect/unstable/process/ChildProcessSpawner"
|
|
7
|
+
import { runClanka } from "../Clanka.ts"
|
|
6
8
|
|
|
7
9
|
export const agentTasker = Effect.fnUntraced(function* (options: {
|
|
8
10
|
readonly specsDirectory: string
|
|
@@ -13,6 +15,20 @@ export const agentTasker = Effect.fnUntraced(function* (options: {
|
|
|
13
15
|
const worktree = yield* Worktree
|
|
14
16
|
const promptGen = yield* PromptGen
|
|
15
17
|
|
|
18
|
+
// use clanka
|
|
19
|
+
if (!options.preset.cliAgent.command) {
|
|
20
|
+
yield* runClanka({
|
|
21
|
+
directory: worktree.directory,
|
|
22
|
+
model: options.preset.extraArgs.join(" "),
|
|
23
|
+
system: promptGen.systemClanka(options),
|
|
24
|
+
prompt: promptGen.promptPlanTasksClanka({
|
|
25
|
+
specsDirectory: options.specsDirectory,
|
|
26
|
+
specificationPath: options.specificationPath,
|
|
27
|
+
}),
|
|
28
|
+
})
|
|
29
|
+
return ExitCode(0)
|
|
30
|
+
}
|
|
31
|
+
|
|
16
32
|
return yield* pipe(
|
|
17
33
|
options.preset.cliAgent.command({
|
|
18
34
|
prompt: promptGen.promptPlanTasks({
|
package/src/Agents/timeout.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { ChildProcess } from "effect/unstable/process"
|
|
|
4
4
|
import { Worktree } from "../Worktree.ts"
|
|
5
5
|
import type { PrdIssue } from "../domain/PrdIssue.ts"
|
|
6
6
|
import type { CliAgentPreset } from "../domain/CliAgentPreset.ts"
|
|
7
|
+
import { ExitCode } from "effect/unstable/process/ChildProcessSpawner"
|
|
8
|
+
import { runClanka } from "../Clanka.ts"
|
|
7
9
|
|
|
8
10
|
export const agentTimeout = Effect.fnUntraced(function* (options: {
|
|
9
11
|
readonly specsDirectory: string
|
|
@@ -15,6 +17,21 @@ export const agentTimeout = Effect.fnUntraced(function* (options: {
|
|
|
15
17
|
const worktree = yield* Worktree
|
|
16
18
|
const promptGen = yield* PromptGen
|
|
17
19
|
|
|
20
|
+
// use clanka
|
|
21
|
+
if (!options.preset.cliAgent.command) {
|
|
22
|
+
yield* runClanka({
|
|
23
|
+
directory: worktree.directory,
|
|
24
|
+
model: options.preset.extraArgs.join(" "),
|
|
25
|
+
system: promptGen.systemClanka(options),
|
|
26
|
+
prompt: promptGen.promptTimeoutClanka({
|
|
27
|
+
taskId: options.task.id!,
|
|
28
|
+
specsDirectory: options.specsDirectory,
|
|
29
|
+
}),
|
|
30
|
+
stallTimeout: options.stallTimeout,
|
|
31
|
+
})
|
|
32
|
+
return ExitCode(0)
|
|
33
|
+
}
|
|
34
|
+
|
|
18
35
|
const timeoutCommand = pipe(
|
|
19
36
|
options.preset.cliAgent.command({
|
|
20
37
|
prompt: promptGen.promptTimeout({
|
package/src/Agents/worker.ts
CHANGED
|
@@ -2,15 +2,30 @@ import { Duration, Effect, Path, pipe } from "effect"
|
|
|
2
2
|
import { ChildProcess } from "effect/unstable/process"
|
|
3
3
|
import { Worktree } from "../Worktree.ts"
|
|
4
4
|
import type { CliAgentPreset } from "../domain/CliAgentPreset.ts"
|
|
5
|
+
import { runClanka } from "../Clanka.ts"
|
|
6
|
+
import { ExitCode } from "effect/unstable/process/ChildProcessSpawner"
|
|
5
7
|
|
|
6
8
|
export const agentWorker = Effect.fnUntraced(function* (options: {
|
|
7
9
|
readonly stallTimeout: Duration.Duration
|
|
8
10
|
readonly preset: CliAgentPreset
|
|
11
|
+
readonly system?: string
|
|
9
12
|
readonly prompt: string
|
|
10
13
|
}) {
|
|
11
14
|
const pathService = yield* Path.Path
|
|
12
15
|
const worktree = yield* Worktree
|
|
13
16
|
|
|
17
|
+
// use clanka
|
|
18
|
+
if (!options.preset.cliAgent.command) {
|
|
19
|
+
yield* runClanka({
|
|
20
|
+
directory: worktree.directory,
|
|
21
|
+
model: options.preset.extraArgs.join(" "),
|
|
22
|
+
system: options.system,
|
|
23
|
+
prompt: options.prompt,
|
|
24
|
+
stallTimeout: options.stallTimeout,
|
|
25
|
+
})
|
|
26
|
+
return ExitCode(0)
|
|
27
|
+
}
|
|
28
|
+
|
|
14
29
|
const cliCommand = pipe(
|
|
15
30
|
options.preset.cliAgent.command({
|
|
16
31
|
prompt: options.prompt,
|
package/src/Clanka.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
TaskToolsHandlers,
|
|
6
6
|
TaskToolsWithChoose,
|
|
7
7
|
} from "./TaskTools.ts"
|
|
8
|
-
import { clankaSubagent } from "./ClankaModels.ts"
|
|
8
|
+
import { ClankaModels, clankaSubagent } from "./ClankaModels.ts"
|
|
9
9
|
import { withStallTimeout } from "./shared/stream.ts"
|
|
10
10
|
import type { AiError } from "effect/unstable/ai"
|
|
11
11
|
import type { RunnerStalled } from "./domain/Errors.ts"
|
|
@@ -14,18 +14,21 @@ export const runClanka = Effect.fnUntraced(
|
|
|
14
14
|
/** The working directory to run the agent in */
|
|
15
15
|
function* (options: {
|
|
16
16
|
readonly directory: string
|
|
17
|
+
readonly model: string
|
|
17
18
|
readonly prompt: string
|
|
18
19
|
readonly system?: string | undefined
|
|
19
20
|
readonly stallTimeout?: Duration.Input | undefined
|
|
20
21
|
readonly withChoose?: boolean | undefined
|
|
21
22
|
}) {
|
|
23
|
+
const models = yield* ClankaModels
|
|
22
24
|
const agent = yield* Agent.make({
|
|
23
25
|
...options,
|
|
24
26
|
tools: options.withChoose
|
|
25
27
|
? TaskToolsWithChoose
|
|
26
28
|
: (TaskTools as unknown as typeof TaskToolsWithChoose),
|
|
27
|
-
subagentModel: clankaSubagent,
|
|
28
|
-
})
|
|
29
|
+
subagentModel: clankaSubagent(models, options.model),
|
|
30
|
+
}).pipe(Effect.provide(models.get(options.model)))
|
|
31
|
+
|
|
29
32
|
let stream = options.stallTimeout
|
|
30
33
|
? withStallTimeout(options.stallTimeout)(agent.output)
|
|
31
34
|
: agent.output
|
package/src/ClankaModels.ts
CHANGED
|
@@ -1,53 +1,72 @@
|
|
|
1
|
+
// oxlint-disable typescript/no-explicit-any
|
|
1
2
|
import { NodeHttpClient } from "@effect/platform-node"
|
|
2
|
-
import { Codex } from "clanka"
|
|
3
|
-
import { Layer, LayerMap,
|
|
3
|
+
import { Codex, Copilot } from "clanka"
|
|
4
|
+
import { Effect, flow, Layer, LayerMap, Schema } from "effect"
|
|
4
5
|
import { layerKvs } from "./Kvs.ts"
|
|
5
|
-
import { OpenAiClient, OpenAiLanguageModel } from "@effect/ai-openai"
|
|
6
6
|
|
|
7
|
-
export const
|
|
8
|
-
|
|
9
|
-
PlatformError.PlatformError
|
|
10
|
-
> = Codex.layer.pipe(
|
|
11
|
-
Layer.provide(NodeHttpClient.layerUndici),
|
|
12
|
-
Layer.provide(layerKvs),
|
|
7
|
+
export const ModelServices = NodeHttpClient.layerUndici.pipe(
|
|
8
|
+
Layer.merge(layerKvs),
|
|
13
9
|
)
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
summary: "auto",
|
|
26
|
-
},
|
|
27
|
-
}).pipe(Layer.provideMerge(CodexLayer)),
|
|
28
|
-
"gpt-5.4-medium": OpenAiLanguageModel.model("gpt-5.4", {
|
|
29
|
-
reasoning: {
|
|
30
|
-
effort: "high",
|
|
31
|
-
summary: "auto",
|
|
32
|
-
},
|
|
33
|
-
}).pipe(Layer.provideMerge(CodexLayer)),
|
|
34
|
-
} as const
|
|
35
|
-
|
|
36
|
-
export type ClankaModel = keyof typeof clankaModels
|
|
37
|
-
export const ClankaModel = Schema.Literals(
|
|
38
|
-
Object.keys(clankaModels) as ClankaModel[],
|
|
11
|
+
const Reasoning = Schema.Literals(["low", "medium", "high", "xhigh"])
|
|
12
|
+
const parseInput = flow(
|
|
13
|
+
Schema.decodeUnknownEffect(
|
|
14
|
+
Schema.Tuple([
|
|
15
|
+
Schema.Literals(["openai", "copilot"]),
|
|
16
|
+
Schema.String,
|
|
17
|
+
Reasoning,
|
|
18
|
+
]),
|
|
19
|
+
),
|
|
20
|
+
Effect.orDie,
|
|
39
21
|
)
|
|
40
22
|
|
|
41
|
-
export const clankaSubagent =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
23
|
+
export const clankaSubagent = Effect.fnUntraced(function* (
|
|
24
|
+
models: ClankaModels["Service"],
|
|
25
|
+
input: string,
|
|
26
|
+
) {
|
|
27
|
+
const [provider, model] = yield* parseInput(input.split("/"))
|
|
28
|
+
return models.get(`${provider}/${model}/low`)
|
|
29
|
+
}, Layer.unwrap)
|
|
47
30
|
|
|
48
31
|
export class ClankaModels extends LayerMap.Service<ClankaModels>()(
|
|
49
32
|
"lalph/ClankaModels",
|
|
50
33
|
{
|
|
51
|
-
|
|
34
|
+
dependencies: [ModelServices],
|
|
35
|
+
lookup: Effect.fnUntraced(function* (input: string) {
|
|
36
|
+
const [provider, model, reasoning] = yield* parseInput(input.split("/"))
|
|
37
|
+
switch (provider) {
|
|
38
|
+
case "openai": {
|
|
39
|
+
return Codex.model(model, {
|
|
40
|
+
reasoning: {
|
|
41
|
+
effort: reasoning,
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
case "copilot": {
|
|
46
|
+
return Copilot.model(model, {
|
|
47
|
+
...reasoningToCopilotConfig(model, reasoning),
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}, Layer.unwrap),
|
|
52
52
|
},
|
|
53
53
|
) {}
|
|
54
|
+
|
|
55
|
+
const reasoningToCopilotConfig = (
|
|
56
|
+
model: string,
|
|
57
|
+
reasoning: typeof Reasoning.Type,
|
|
58
|
+
) => {
|
|
59
|
+
if (model.startsWith("claude")) {
|
|
60
|
+
switch (reasoning) {
|
|
61
|
+
case "low":
|
|
62
|
+
return {}
|
|
63
|
+
case "medium":
|
|
64
|
+
return { reasoningEffort: 4000 }
|
|
65
|
+
case "high":
|
|
66
|
+
return { thinking_budget: 16000 }
|
|
67
|
+
case "xhigh":
|
|
68
|
+
return { thinking_budget: 31999 }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return { reasoningEffort: reasoning }
|
|
72
|
+
}
|
package/src/Presets.ts
CHANGED
|
@@ -5,7 +5,6 @@ import { Prompt } from "effect/unstable/cli"
|
|
|
5
5
|
import { allCliAgents, type AnyCliAgent } from "./domain/CliAgent.ts"
|
|
6
6
|
import { parseCommand } from "./shared/child-process.ts"
|
|
7
7
|
import { IssueSource } from "./IssueSource.ts"
|
|
8
|
-
import { ClankaModel, clankaModels } from "./ClankaModels.ts"
|
|
9
8
|
|
|
10
9
|
export const allCliAgentPresets = new Setting(
|
|
11
10
|
"cliAgentPresets",
|
|
@@ -120,29 +119,12 @@ export const addOrUpdatePreset = Effect.fnUntraced(function* (options?: {
|
|
|
120
119
|
options?.existing?.commandPrefix,
|
|
121
120
|
)
|
|
122
121
|
|
|
123
|
-
const clankaModel = yield* Prompt.select<ClankaModel | undefined>({
|
|
124
|
-
message: "clanka model?",
|
|
125
|
-
choices: [
|
|
126
|
-
{
|
|
127
|
-
title: "none",
|
|
128
|
-
value: undefined,
|
|
129
|
-
selected: options?.existing?.clankaModel === undefined,
|
|
130
|
-
},
|
|
131
|
-
...(Object.keys(clankaModels) as Array<ClankaModel>).map((key) => ({
|
|
132
|
-
title: key,
|
|
133
|
-
value: key,
|
|
134
|
-
selected: options?.existing?.clankaModel === key,
|
|
135
|
-
})),
|
|
136
|
-
],
|
|
137
|
-
})
|
|
138
|
-
|
|
139
122
|
let preset = new CliAgentPreset({
|
|
140
123
|
id,
|
|
141
124
|
cliAgent,
|
|
142
125
|
commandPrefix,
|
|
143
126
|
extraArgs,
|
|
144
127
|
sourceMetadata: {},
|
|
145
|
-
...(clankaModel ? { clankaModel } : {}),
|
|
146
128
|
})
|
|
147
129
|
|
|
148
130
|
if (id !== CliAgentPreset.defaultId) {
|
package/src/PromptGen.ts
CHANGED
|
@@ -90,19 +90,17 @@ Set \`githubPrNumber\` to the PR number if one exists, otherwise use \`null\`.
|
|
|
90
90
|
|
|
91
91
|
const promptChooseClanka = (options: {
|
|
92
92
|
readonly gitFlow: GitFlow["Service"]
|
|
93
|
-
}) => `Your job is to choose the next task to work on
|
|
93
|
+
}) => `Your job is to choose the next task to work on using "listEligibleTasks".
|
|
94
94
|
**DO NOT** implement the task yet.
|
|
95
95
|
|
|
96
96
|
The following instructions should be done without interaction or asking for permission.
|
|
97
97
|
|
|
98
|
-
- Decide which single task to work on next from
|
|
98
|
+
- Decide which single task to work on next from "listEligibleTasks". This should
|
|
99
99
|
be the task YOU decide as the most important to work on next, not just the
|
|
100
|
-
first task in the list
|
|
101
|
-
- Only start tasks that are in a "todo" state.
|
|
102
|
-
- You **cannot** start tasks unless they have an empty \`blockedBy\` field.${
|
|
100
|
+
first task in the list.${
|
|
103
101
|
options.gitFlow.requiresGithubPr
|
|
104
102
|
? `
|
|
105
|
-
- Check if there is an open Github PR for the chosen task. If there is, note the PR number for inclusion
|
|
103
|
+
- Check if there is an open Github PR for the chosen task. If there is, note the PR number for inclusion when calling "chooseTask".
|
|
106
104
|
- Only include "open" PRs that are not yet merged.
|
|
107
105
|
- The pull request will contain the task id in the title or description.`
|
|
108
106
|
: ""
|
|
@@ -227,16 +225,16 @@ ${keyInformation(options)}`
|
|
|
227
225
|
readonly specsDirectory: string
|
|
228
226
|
readonly githubPrNumber: number | undefined
|
|
229
227
|
readonly gitFlow: GitFlow["Service"]
|
|
230
|
-
}) =>
|
|
228
|
+
}) => `# ${options.task.title}
|
|
231
229
|
|
|
232
|
-
ID: ${options.task.id}
|
|
233
|
-
Task: ${options.task.title}
|
|
234
|
-
Description:
|
|
230
|
+
Task ID: ${options.task.id}
|
|
235
231
|
|
|
236
232
|
${options.task.description}
|
|
237
233
|
|
|
238
234
|
# Workflow
|
|
239
235
|
|
|
236
|
+
All steps must be done before the task can be considered complete.
|
|
237
|
+
|
|
240
238
|
1. Carefully study the current task list to understand the context of the task, and
|
|
241
239
|
discover any key learnings from previous work.
|
|
242
240
|
Also read the ${options.specsDirectory}/README.md file (if available), to see
|
|
@@ -324,11 +322,9 @@ permission.
|
|
|
324
322
|
1. Investigate why you think the task took too long. Research the codebase
|
|
325
323
|
further to understand what is needed to complete the task.
|
|
326
324
|
2. Mark the original task as "done" by updating its \`state\`.
|
|
327
|
-
3. Break down the task into smaller tasks and add them to the
|
|
325
|
+
3. Break down the task into smaller tasks and add them to the task list.
|
|
328
326
|
Read the "### Adding tasks" section below **extremely carefully** for guidelines on creating tasks.
|
|
329
|
-
|
|
330
|
-
to wait 5 seconds after adding tasks to the prd.yml file to allow the system
|
|
331
|
-
to assign ids to the new tasks before you can setup dependencies.
|
|
327
|
+
- Make sure to setup task dependencies using the \`blockedBy\` field as needed.
|
|
332
328
|
5. If any specifications need updating based on your new understanding, update them.`
|
|
333
329
|
|
|
334
330
|
const planPrompt = (options: {
|
|
@@ -415,6 +411,23 @@ setup dependencies between the tasks using the \`blockedBy\` field.
|
|
|
415
411
|
|
|
416
412
|
${prdNotes(options)}`
|
|
417
413
|
|
|
414
|
+
const promptPlanTasksClanka = (options: {
|
|
415
|
+
readonly specsDirectory: string
|
|
416
|
+
readonly specificationPath: string
|
|
417
|
+
}) => `Your job is to convert the implementation plan in the specification file at
|
|
418
|
+
\`${options.specificationPath}\` into tasks.
|
|
419
|
+
|
|
420
|
+
Before starting, read the entire task list to understand the context of existing tasks
|
|
421
|
+
and to ensure you do not create duplicate tasks.
|
|
422
|
+
|
|
423
|
+
Make sure each task is small, atomic and independently shippable without failing
|
|
424
|
+
validation checks (typechecks, linting, tests).
|
|
425
|
+
Each task should include a reference to the specification file in its description.
|
|
426
|
+
|
|
427
|
+
Make sure to setup dependencies between the tasks using the \`blockedBy\` field.
|
|
428
|
+
|
|
429
|
+
**Important:** You are only creating or updating a plan, not implementing any tasks yet.`
|
|
430
|
+
|
|
418
431
|
return {
|
|
419
432
|
promptChoose,
|
|
420
433
|
promptChooseClanka,
|
|
@@ -426,6 +439,7 @@ ${prdNotes(options)}`
|
|
|
426
439
|
promptTimeoutClanka,
|
|
427
440
|
planPrompt,
|
|
428
441
|
promptPlanTasks,
|
|
442
|
+
promptPlanTasksClanka,
|
|
429
443
|
systemClanka,
|
|
430
444
|
} as const
|
|
431
445
|
}),
|
|
@@ -30,7 +30,6 @@ export const commandAgentsLs = Command.make("ls").pipe(
|
|
|
30
30
|
console.log(`Preset: ${preset.id}`)
|
|
31
31
|
yield* source.cliAgentPresetInfo(preset)
|
|
32
32
|
console.log(` CLI agent: ${preset.cliAgent.name}`)
|
|
33
|
-
console.log(` Clanka model: ${preset.clankaModel ?? "none"}`)
|
|
34
33
|
if (preset.extraArgs.length > 0) {
|
|
35
34
|
console.log(` Extra args: ${preset.extraArgs.join(" ")}`)
|
|
36
35
|
}
|
|
@@ -10,6 +10,7 @@ import { commandRoot } from "../root.ts"
|
|
|
10
10
|
import { selectCliAgentPreset } from "../../Presets.ts"
|
|
11
11
|
import { CurrentIssueSource } from "../../CurrentIssueSource.ts"
|
|
12
12
|
import type { CliAgentPreset } from "../../domain/CliAgentPreset.ts"
|
|
13
|
+
import { ClankaModels } from "../../ClankaModels.ts"
|
|
13
14
|
|
|
14
15
|
const specificationPath = Argument.path("spec", {
|
|
15
16
|
pathType: "file",
|
|
@@ -70,8 +71,9 @@ const generateTasks = Effect.fnUntraced(
|
|
|
70
71
|
})
|
|
71
72
|
},
|
|
72
73
|
Effect.provide([
|
|
74
|
+
ClankaModels.layer,
|
|
73
75
|
Settings.layer,
|
|
74
76
|
PromptGen.layer,
|
|
75
|
-
Prd.layerProvided.pipe(Layer.
|
|
77
|
+
Prd.layerProvided.pipe(Layer.provideMerge(layerProjectIdPrompt)),
|
|
76
78
|
]),
|
|
77
79
|
)
|
package/src/commands/plan.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { selectCliAgentPreset } from "../Presets.ts"
|
|
|
15
15
|
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
|
|
16
16
|
import { parseBranch } from "../shared/git.ts"
|
|
17
17
|
import type { CliAgentPreset } from "../domain/CliAgentPreset.ts"
|
|
18
|
+
import { ClankaModels } from "../ClankaModels.ts"
|
|
18
19
|
|
|
19
20
|
const dangerous = Flag.boolean("dangerous").pipe(
|
|
20
21
|
Flag.withAlias("d"),
|
|
@@ -74,7 +75,13 @@ export const commandPlan = Command.make("plan", {
|
|
|
74
75
|
dangerous,
|
|
75
76
|
preset,
|
|
76
77
|
}).pipe(Effect.provideService(CurrentProjectId, project.id))
|
|
77
|
-
}).pipe(
|
|
78
|
+
}).pipe(
|
|
79
|
+
Effect.provide([
|
|
80
|
+
Settings.layer,
|
|
81
|
+
CurrentIssueSource.layer,
|
|
82
|
+
ClankaModels.layer,
|
|
83
|
+
]),
|
|
84
|
+
)
|
|
78
85
|
}, Effect.provide(Editor.layer)),
|
|
79
86
|
),
|
|
80
87
|
Command.withSubcommands([commandPlanTasks]),
|