lalph 0.2.19 → 0.2.20
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 +73 -69
- package/package.json +1 -1
- package/src/Agents/planner.ts +1 -0
- package/src/Editor.ts +66 -0
- package/src/PromptGen.ts +8 -2
- package/src/commands/edit.ts +6 -13
- package/src/commands/issue.ts +15 -23
- package/src/commands/plan.ts +21 -18
package/dist/cli.mjs
CHANGED
|
@@ -7235,7 +7235,7 @@ const getOption = /* @__PURE__ */ dual(2, (self, service) => {
|
|
|
7235
7235
|
* @since 4.0.0
|
|
7236
7236
|
* @category Utils
|
|
7237
7237
|
*/
|
|
7238
|
-
const merge$
|
|
7238
|
+
const merge$5 = /* @__PURE__ */ dual(2, (self, that) => {
|
|
7239
7239
|
if (self.mapUnsafe.size === 0) return that;
|
|
7240
7240
|
if (that.mapUnsafe.size === 0) return self;
|
|
7241
7241
|
const map = new Map(self.mapUnsafe);
|
|
@@ -9229,7 +9229,7 @@ const servicesWith$1 = (f) => withFiber$1((fiber) => f(fiber.services));
|
|
|
9229
9229
|
/** @internal */
|
|
9230
9230
|
const provideServices$1 = /* @__PURE__ */ dual(2, (self, services) => {
|
|
9231
9231
|
if (effectIsExit(self)) return self;
|
|
9232
|
-
return updateServices$1(self, merge$
|
|
9232
|
+
return updateServices$1(self, merge$5(services));
|
|
9233
9233
|
});
|
|
9234
9234
|
/** @internal */
|
|
9235
9235
|
const provideService$1 = function() {
|
|
@@ -12030,38 +12030,6 @@ const mergeAllEffect = (layers, memoMap, scope) => {
|
|
|
12030
12030
|
* @category zipping
|
|
12031
12031
|
*/
|
|
12032
12032
|
const mergeAll = (...layers) => fromBuild((memoMap, scope) => mergeAllEffect(layers, memoMap, scope));
|
|
12033
|
-
/**
|
|
12034
|
-
* Merges this layer with the specified layer concurrently, producing a new layer with combined input and output types.
|
|
12035
|
-
*
|
|
12036
|
-
* This is a binary version of `mergeAll` that merges exactly two layers or one layer with an array of layers.
|
|
12037
|
-
* The layers are built concurrently and their outputs are combined.
|
|
12038
|
-
*
|
|
12039
|
-
* @example
|
|
12040
|
-
* ```ts
|
|
12041
|
-
* import { Effect, Layer, ServiceMap } from "effect"
|
|
12042
|
-
*
|
|
12043
|
-
* class Database extends ServiceMap.Service<Database, {
|
|
12044
|
-
* readonly query: (sql: string) => Effect.Effect<string>
|
|
12045
|
-
* }>()("Database") {}
|
|
12046
|
-
*
|
|
12047
|
-
* class Logger extends ServiceMap.Service<Logger, {
|
|
12048
|
-
* readonly log: (msg: string) => Effect.Effect<void>
|
|
12049
|
-
* }>()("Logger") {}
|
|
12050
|
-
*
|
|
12051
|
-
* const dbLayer = Layer.succeed(Database)({
|
|
12052
|
-
* query: (sql: string) => Effect.succeed("result")
|
|
12053
|
-
* })
|
|
12054
|
-
* const loggerLayer = Layer.succeed(Logger)({
|
|
12055
|
-
* log: (msg: string) => Effect.sync(() => console.log(msg))
|
|
12056
|
-
* })
|
|
12057
|
-
*
|
|
12058
|
-
* const mergedLayer = Layer.merge(dbLayer, loggerLayer)
|
|
12059
|
-
* ```
|
|
12060
|
-
*
|
|
12061
|
-
* @since 2.0.0
|
|
12062
|
-
* @category zipping
|
|
12063
|
-
*/
|
|
12064
|
-
const merge$5 = /* @__PURE__ */ dual(2, (self, that) => mergeAll(self, ...Array.isArray(that) ? that : [that]));
|
|
12065
12033
|
const provideWith = (self, that, f) => fromBuild((memoMap, scope) => flatMap$4(Array.isArray(that) ? mergeAllEffect(that, memoMap, scope) : that.build(memoMap, scope), (context) => self.build(memoMap, scope).pipe(provideServices$1(context), map$11((merged) => f(merged, context)))));
|
|
12066
12034
|
/**
|
|
12067
12035
|
* Feeds the output services of this builder into the input of the specified
|
|
@@ -12204,7 +12172,7 @@ const provide$3 = /* @__PURE__ */ dual(2, (self, that) => provideWith(self, that
|
|
|
12204
12172
|
* @since 2.0.0
|
|
12205
12173
|
* @category utils
|
|
12206
12174
|
*/
|
|
12207
|
-
const provideMerge = /* @__PURE__ */ dual(2, (self, that) => provideWith(self, that, (self, that) => merge$
|
|
12175
|
+
const provideMerge = /* @__PURE__ */ dual(2, (self, that) => provideWith(self, that, (self, that) => merge$5(that, self)));
|
|
12208
12176
|
/**
|
|
12209
12177
|
* Constructs a layer dynamically based on the output of this layer.
|
|
12210
12178
|
*
|
|
@@ -51194,7 +51162,7 @@ const TypeId$29 = "~effect/Cache";
|
|
|
51194
51162
|
*/
|
|
51195
51163
|
const makeWith$1 = (options) => servicesWith$1((services) => {
|
|
51196
51164
|
const self = Object.create(Proto$14);
|
|
51197
|
-
self.lookup = (key) => updateServices$1(options.lookup(key), (input) => merge$
|
|
51165
|
+
self.lookup = (key) => updateServices$1(options.lookup(key), (input) => merge$5(services, input));
|
|
51198
51166
|
self.map = make$45();
|
|
51199
51167
|
self.capacity = options.capacity;
|
|
51200
51168
|
self.timeToLive = options.timeToLive ? (exit, key) => fromDurationInputUnsafe(options.timeToLive(exit, key)) : defaultTimeToLive;
|
|
@@ -59031,7 +58999,7 @@ const SpanNameGenerator$1 = /* @__PURE__ */ Reference("effect/http/HttpClient/Sp
|
|
|
59031
58999
|
/**
|
|
59032
59000
|
* @since 4.0.0
|
|
59033
59001
|
*/
|
|
59034
|
-
const layerMergedServices = (effect) => effect$1(HttpClient)(servicesWith((services) => map$8(effect, (client) => transformResponse(client, updateServices((input) => merge$
|
|
59002
|
+
const layerMergedServices = (effect) => effect$1(HttpClient)(servicesWith((services) => map$8(effect, (client) => transformResponse(client, updateServices((input) => merge$5(services, input))))));
|
|
59035
59003
|
const responseRegistry = /* @__PURE__ */ (() => {
|
|
59036
59004
|
if ("FinalizationRegistry" in globalThis && globalThis.FinalizationRegistry) {
|
|
59037
59005
|
const registry = /* @__PURE__ */ new FinalizationRegistry((controller) => {
|
|
@@ -60328,7 +60296,7 @@ const fromWebSocket = (acquire, options) => withFiber((fiber) => {
|
|
|
60328
60296
|
latch.openUnsafe();
|
|
60329
60297
|
if (opts?.onOpen) yield* opts.onOpen;
|
|
60330
60298
|
return yield* join(fiberSet).pipe(catchFilter(SocketCloseError.filterClean((_) => !closeCodeIsError(_)), (_) => void_$1));
|
|
60331
|
-
})).pipe(updateServices((input) => merge$
|
|
60299
|
+
})).pipe(updateServices((input) => merge$5(acquireContext, input)), ensuring$2(sync(() => {
|
|
60332
60300
|
latch.closeUnsafe();
|
|
60333
60301
|
currentWS = void 0;
|
|
60334
60302
|
})));
|
|
@@ -151063,8 +151031,13 @@ permission.
|
|
|
151063
151031
|
5. If any specifications need updating based on your new understanding, update them.
|
|
151064
151032
|
|
|
151065
151033
|
${prdNotes(options)}`;
|
|
151066
|
-
const planPrompt = (options) =>
|
|
151067
|
-
|
|
151034
|
+
const planPrompt = (options) => `<request><![CDATA[
|
|
151035
|
+
${options.plan}
|
|
151036
|
+
]]></request>
|
|
151037
|
+
|
|
151038
|
+
## Instructions
|
|
151039
|
+
|
|
151040
|
+
1. Your job is to create a detailed specification to fulfill the request and save it as a file.
|
|
151068
151041
|
First do some research to understand the request, then interview the user
|
|
151069
151042
|
to gather all the necessary requirements and details for the specification.
|
|
151070
151043
|
- If the user asks you to update an existing specification, find the relevant
|
|
@@ -152120,6 +152093,41 @@ const commandPlanTasks = make$35("tasks", { specificationPath }).pipe(withDescri
|
|
|
152120
152093
|
Worktree.layer.pipe(provide$3(layerProjectIdPrompt))
|
|
152121
152094
|
]))));
|
|
152122
152095
|
|
|
152096
|
+
//#endregion
|
|
152097
|
+
//#region src/shared/config.ts
|
|
152098
|
+
const configEditor = string$1("LALPH_EDITOR").pipe(orElse(() => string$1("EDITOR")), map$5(parseCommand), withDefault$3(() => ["nano"]));
|
|
152099
|
+
|
|
152100
|
+
//#endregion
|
|
152101
|
+
//#region src/Editor.ts
|
|
152102
|
+
var Editor = class extends Service$1()("lalph/Editor", { make: gen(function* () {
|
|
152103
|
+
const fs = yield* FileSystem;
|
|
152104
|
+
const editor = yield* configEditor;
|
|
152105
|
+
const spawner = yield* ChildProcessSpawner;
|
|
152106
|
+
const edit = (path) => make$23(editor[0], [...editor.slice(1), path], {
|
|
152107
|
+
stdin: "inherit",
|
|
152108
|
+
stdout: "inherit",
|
|
152109
|
+
stderr: "inherit"
|
|
152110
|
+
}).pipe(exitCode, provideService(ChildProcessSpawner, spawner), orDie$2);
|
|
152111
|
+
return {
|
|
152112
|
+
edit,
|
|
152113
|
+
editTemp: fnUntraced(function* (options) {
|
|
152114
|
+
const initialContent = options.initialContent ?? "";
|
|
152115
|
+
const file = yield* fs.makeTempFileScoped({ suffix: options.suffix ?? ".txt" });
|
|
152116
|
+
if (initialContent) yield* fs.writeFileString(file, initialContent);
|
|
152117
|
+
if ((yield* make$23(editor[0], [...editor.slice(1), file], {
|
|
152118
|
+
stdin: "inherit",
|
|
152119
|
+
stdout: "inherit",
|
|
152120
|
+
stderr: "inherit"
|
|
152121
|
+
}).pipe(exitCode)) !== 0) return yield* new NoSuchElementError();
|
|
152122
|
+
const content = (yield* fs.readFileString(file)).trim();
|
|
152123
|
+
if (content === initialContent) return yield* new NoSuchElementError();
|
|
152124
|
+
return content;
|
|
152125
|
+
}, scoped$1, provideService(ChildProcessSpawner, spawner), option$1)
|
|
152126
|
+
};
|
|
152127
|
+
}) }) {
|
|
152128
|
+
static layer = effect$1(this, this.make).pipe(provide$3(PlatformServices));
|
|
152129
|
+
};
|
|
152130
|
+
|
|
152123
152131
|
//#endregion
|
|
152124
152132
|
//#region src/commands/plan.ts
|
|
152125
152133
|
const dangerous = boolean("dangerous").pipe(withAlias("d"), withDescription$1("Enable dangerous mode (skip permission prompts) during plan generation"));
|
|
@@ -152128,30 +152136,39 @@ const commandPlan = make$35("plan", {
|
|
|
152128
152136
|
dangerous,
|
|
152129
152137
|
withNewProject
|
|
152130
152138
|
}).pipe(withDescription("Iterate on an issue plan and create PRD tasks"), withHandler(fnUntraced(function* ({ dangerous, withNewProject }) {
|
|
152139
|
+
const thePlan = yield* (yield* Editor).editTemp({ suffix: ".md" });
|
|
152140
|
+
if (isNone(thePlan)) return;
|
|
152131
152141
|
const project = withNewProject ? yield* addOrUpdateProject() : yield* selectProject;
|
|
152132
152142
|
const { specsDirectory } = yield* commandRoot;
|
|
152133
152143
|
const commandPrefix = yield* getCommandPrefix;
|
|
152134
152144
|
yield* plan({
|
|
152145
|
+
plan: thePlan.value,
|
|
152135
152146
|
specsDirectory,
|
|
152136
152147
|
targetBranch: project.targetBranch,
|
|
152137
152148
|
commandPrefix,
|
|
152138
152149
|
dangerous
|
|
152139
152150
|
}).pipe(provideService(CurrentProjectId, project.id));
|
|
152140
|
-
}, provide$1([
|
|
152151
|
+
}, provide$1([
|
|
152152
|
+
Settings.layer,
|
|
152153
|
+
CurrentIssueSource.layer,
|
|
152154
|
+
Editor.layer
|
|
152155
|
+
]))), withSubcommands([commandPlanTasks]));
|
|
152141
152156
|
const plan = fnUntraced(function* (options) {
|
|
152142
152157
|
const fs = yield* FileSystem;
|
|
152143
152158
|
const pathService = yield* Path$1;
|
|
152144
152159
|
const worktree = yield* Worktree;
|
|
152145
152160
|
const cliAgent = yield* getOrSelectCliAgent;
|
|
152146
152161
|
yield* agentPlanner({
|
|
152162
|
+
plan: options.plan,
|
|
152147
152163
|
specsDirectory: options.specsDirectory,
|
|
152148
152164
|
commandPrefix: options.commandPrefix,
|
|
152149
152165
|
dangerous: options.dangerous,
|
|
152150
152166
|
cliAgent
|
|
152151
152167
|
});
|
|
152168
|
+
const planDetails = yield* pipe(fs.readFileString(pathService.join(worktree.directory, ".lalph", "plan.json")), flatMap$2(decodeEffect(PlanDetails)), mapError$2(() => new SpecNotFound()));
|
|
152152
152169
|
yield* log$1("Converting specification into tasks");
|
|
152153
152170
|
yield* agentTasker({
|
|
152154
|
-
specificationPath:
|
|
152171
|
+
specificationPath: planDetails.specification,
|
|
152155
152172
|
specsDirectory: options.specsDirectory,
|
|
152156
152173
|
commandPrefix: options.commandPrefix,
|
|
152157
152174
|
cliAgent
|
|
@@ -152160,16 +152177,15 @@ const plan = fnUntraced(function* (options) {
|
|
|
152160
152177
|
}, scoped$1, provide$1([
|
|
152161
152178
|
PromptGen.layer,
|
|
152162
152179
|
Prd.layerProvided,
|
|
152163
|
-
Worktree.layer
|
|
152180
|
+
Worktree.layer,
|
|
152164
152181
|
Settings.layer,
|
|
152165
152182
|
CurrentIssueSource.layer
|
|
152166
152183
|
]));
|
|
152184
|
+
var SpecNotFound = class extends TaggedError("SpecNotFound") {
|
|
152185
|
+
message = "The AI agent failed to produce a specification.";
|
|
152186
|
+
};
|
|
152167
152187
|
const PlanDetails = fromJsonString(Struct({ specification: String$1 }));
|
|
152168
152188
|
|
|
152169
|
-
//#endregion
|
|
152170
|
-
//#region src/shared/config.ts
|
|
152171
|
-
const configEditor = string$1("LALPH_EDITOR").pipe(orElse(() => string$1("EDITOR")), map$5(parseCommand), withDefault$3(() => ["nano"]));
|
|
152172
|
-
|
|
152173
152189
|
//#endregion
|
|
152174
152190
|
//#region src/commands/issue.ts
|
|
152175
152191
|
const issueTemplate = `---
|
|
@@ -152190,19 +152206,13 @@ const FrontMatterSchema = toCodecJson(Struct({
|
|
|
152190
152206
|
}));
|
|
152191
152207
|
const handler$1 = flow(withHandler(fnUntraced(function* () {
|
|
152192
152208
|
const source = yield* IssueSource;
|
|
152193
|
-
const fs = yield* FileSystem;
|
|
152194
152209
|
const projectId = yield* CurrentProjectId;
|
|
152195
|
-
const
|
|
152196
|
-
|
|
152197
|
-
|
|
152198
|
-
|
|
152199
|
-
|
|
152200
|
-
|
|
152201
|
-
stderr: "inherit"
|
|
152202
|
-
}).pipe(exitCode)) !== 0) return;
|
|
152203
|
-
const content = yield* fs.readFileString(tempFile);
|
|
152204
|
-
if (content.trim() === issueTemplate.trim()) return;
|
|
152205
|
-
const lines = content.split("\n");
|
|
152210
|
+
const content = yield* (yield* Editor).editTemp({
|
|
152211
|
+
suffix: ".md",
|
|
152212
|
+
initialContent: issueTemplate
|
|
152213
|
+
});
|
|
152214
|
+
if (isNone(content)) return;
|
|
152215
|
+
const lines = content.value.split("\n");
|
|
152206
152216
|
const yamlLines = [];
|
|
152207
152217
|
let descriptionStartIndex = 0;
|
|
152208
152218
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -152225,7 +152235,7 @@ const handler$1 = flow(withHandler(fnUntraced(function* () {
|
|
|
152225
152235
|
}));
|
|
152226
152236
|
console.log(`Created issue with ID: ${created.id}`);
|
|
152227
152237
|
console.log(`URL: ${created.url}`);
|
|
152228
|
-
}, scoped$1)), provide(
|
|
152238
|
+
}, scoped$1)), provide(mergeAll(layerProjectIdPrompt, CurrentIssueSource.layer, Editor.layer)));
|
|
152229
152239
|
const commandIssue = make$35("issue").pipe(withDescription("Create a new issue in the selected issue source"), handler$1);
|
|
152230
152240
|
const commandIssueAlias = make$35("i").pipe(withDescription("Alias for 'issue' command"), handler$1);
|
|
152231
152241
|
|
|
@@ -152233,14 +152243,8 @@ const commandIssueAlias = make$35("i").pipe(withDescription("Alias for 'issue' c
|
|
|
152233
152243
|
//#region src/commands/edit.ts
|
|
152234
152244
|
const handler = withHandler(fnUntraced(function* () {
|
|
152235
152245
|
const prd = yield* Prd;
|
|
152236
|
-
|
|
152237
|
-
|
|
152238
|
-
extendEnv: true,
|
|
152239
|
-
stdin: "inherit",
|
|
152240
|
-
stdout: "inherit",
|
|
152241
|
-
stderr: "inherit"
|
|
152242
|
-
}).pipe(exitCode);
|
|
152243
|
-
}, scoped$1, provide$1(Prd.layerLocalProvided.pipe(provideMerge(layerProjectIdPrompt)))));
|
|
152246
|
+
yield* (yield* Editor).edit(prd.path);
|
|
152247
|
+
}, provide$1([Prd.layerLocalProvided.pipe(provideMerge(layerProjectIdPrompt)), Editor.layer])));
|
|
152244
152248
|
const commandEdit = make$35("edit").pipe(withDescription("Open the prd.yml file in your editor"), handler);
|
|
152245
152249
|
const commandEditAlias = make$35("e").pipe(withDescription("Alias for 'edit' command"), handler);
|
|
152246
152250
|
|
|
@@ -152250,7 +152254,7 @@ const commandSource = make$35("source").pipe(withDescription("Select the issue s
|
|
|
152250
152254
|
|
|
152251
152255
|
//#endregion
|
|
152252
152256
|
//#region package.json
|
|
152253
|
-
var version = "0.2.
|
|
152257
|
+
var version = "0.2.20";
|
|
152254
152258
|
|
|
152255
152259
|
//#endregion
|
|
152256
152260
|
//#region src/commands/projects/ls.ts
|
package/package.json
CHANGED
package/src/Agents/planner.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { Worktree } from "../Worktree.ts"
|
|
|
5
5
|
import type { CliAgent } from "../domain/CliAgent.ts"
|
|
6
6
|
|
|
7
7
|
export const agentPlanner = Effect.fnUntraced(function* (options: {
|
|
8
|
+
readonly plan: string
|
|
8
9
|
readonly specsDirectory: string
|
|
9
10
|
readonly commandPrefix: (
|
|
10
11
|
command: ChildProcess.Command,
|
package/src/Editor.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Cause, Effect, FileSystem, Layer, ServiceMap } from "effect"
|
|
2
|
+
import { configEditor } from "./shared/config.ts"
|
|
3
|
+
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
|
|
4
|
+
import { PlatformServices } from "./shared/platform.ts"
|
|
5
|
+
|
|
6
|
+
export class Editor extends ServiceMap.Service<Editor>()("lalph/Editor", {
|
|
7
|
+
make: Effect.gen(function* () {
|
|
8
|
+
const fs = yield* FileSystem.FileSystem
|
|
9
|
+
const editor = yield* configEditor
|
|
10
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
|
|
11
|
+
|
|
12
|
+
const edit = (path: string) =>
|
|
13
|
+
ChildProcess.make(editor[0]!, [...editor.slice(1), path], {
|
|
14
|
+
stdin: "inherit",
|
|
15
|
+
stdout: "inherit",
|
|
16
|
+
stderr: "inherit",
|
|
17
|
+
}).pipe(
|
|
18
|
+
ChildProcess.exitCode,
|
|
19
|
+
Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner),
|
|
20
|
+
Effect.orDie,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
const editTemp = Effect.fnUntraced(
|
|
24
|
+
function* (options: {
|
|
25
|
+
readonly initialContent?: string
|
|
26
|
+
readonly suffix?: string
|
|
27
|
+
}) {
|
|
28
|
+
const initialContent = options.initialContent ?? ""
|
|
29
|
+
const file = yield* fs.makeTempFileScoped({
|
|
30
|
+
suffix: options.suffix ?? ".txt",
|
|
31
|
+
})
|
|
32
|
+
if (initialContent) {
|
|
33
|
+
yield* fs.writeFileString(file, initialContent)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const exitCode = yield* ChildProcess.make(
|
|
37
|
+
editor[0]!,
|
|
38
|
+
[...editor.slice(1), file],
|
|
39
|
+
{
|
|
40
|
+
stdin: "inherit",
|
|
41
|
+
stdout: "inherit",
|
|
42
|
+
stderr: "inherit",
|
|
43
|
+
},
|
|
44
|
+
).pipe(ChildProcess.exitCode)
|
|
45
|
+
|
|
46
|
+
if (exitCode !== 0) {
|
|
47
|
+
return yield* new Cause.NoSuchElementError()
|
|
48
|
+
}
|
|
49
|
+
const content = (yield* fs.readFileString(file)).trim()
|
|
50
|
+
if (content === initialContent) {
|
|
51
|
+
return yield* new Cause.NoSuchElementError()
|
|
52
|
+
}
|
|
53
|
+
return content
|
|
54
|
+
},
|
|
55
|
+
Effect.scoped,
|
|
56
|
+
Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner),
|
|
57
|
+
Effect.option,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return { edit, editTemp } as const
|
|
61
|
+
}),
|
|
62
|
+
}) {
|
|
63
|
+
static layer = Layer.effect(this, this.make).pipe(
|
|
64
|
+
Layer.provide(PlatformServices),
|
|
65
|
+
)
|
|
66
|
+
}
|
package/src/PromptGen.ts
CHANGED
|
@@ -233,9 +233,15 @@ permission.
|
|
|
233
233
|
${prdNotes(options)}`
|
|
234
234
|
|
|
235
235
|
const planPrompt = (options: {
|
|
236
|
+
readonly plan: string
|
|
236
237
|
readonly specsDirectory: string
|
|
237
|
-
}) =>
|
|
238
|
-
|
|
238
|
+
}) => `<request><![CDATA[
|
|
239
|
+
${options.plan}
|
|
240
|
+
]]></request>
|
|
241
|
+
|
|
242
|
+
## Instructions
|
|
243
|
+
|
|
244
|
+
1. Your job is to create a detailed specification to fulfill the request and save it as a file.
|
|
239
245
|
First do some research to understand the request, then interview the user
|
|
240
246
|
to gather all the necessary requirements and details for the specification.
|
|
241
247
|
- If the user asks you to update an existing specification, find the relevant
|
package/src/commands/edit.ts
CHANGED
|
@@ -1,27 +1,20 @@
|
|
|
1
1
|
import { Command } from "effect/unstable/cli"
|
|
2
2
|
import { Effect, Layer } from "effect"
|
|
3
|
-
import { ChildProcess } from "effect/unstable/process"
|
|
4
3
|
import { Prd } from "../Prd.ts"
|
|
5
|
-
import { configEditor } from "../shared/config.ts"
|
|
6
4
|
import { layerProjectIdPrompt } from "../Projects.ts"
|
|
5
|
+
import { Editor } from "../Editor.ts"
|
|
7
6
|
|
|
8
7
|
const handler = Command.withHandler(
|
|
9
8
|
Effect.fnUntraced(
|
|
10
9
|
function* () {
|
|
11
10
|
const prd = yield* Prd
|
|
12
|
-
const editor = yield*
|
|
13
|
-
|
|
14
|
-
yield* ChildProcess.make(editor[0]!, [...editor.slice(1), prd.path], {
|
|
15
|
-
extendEnv: true,
|
|
16
|
-
stdin: "inherit",
|
|
17
|
-
stdout: "inherit",
|
|
18
|
-
stderr: "inherit",
|
|
19
|
-
}).pipe(ChildProcess.exitCode)
|
|
11
|
+
const editor = yield* Editor
|
|
12
|
+
yield* editor.edit(prd.path)
|
|
20
13
|
},
|
|
21
|
-
Effect.
|
|
22
|
-
Effect.provide(
|
|
14
|
+
Effect.provide([
|
|
23
15
|
Prd.layerLocalProvided.pipe(Layer.provideMerge(layerProjectIdPrompt)),
|
|
24
|
-
|
|
16
|
+
Editor.layer,
|
|
17
|
+
]),
|
|
25
18
|
),
|
|
26
19
|
)
|
|
27
20
|
|
package/src/commands/issue.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { Command } from "effect/unstable/cli"
|
|
2
2
|
import { CurrentIssueSource } from "../CurrentIssueSource.ts"
|
|
3
|
-
import { Effect,
|
|
3
|
+
import { Effect, flow, Layer, Option, Schema } from "effect"
|
|
4
4
|
import { IssueSource } from "../IssueSource.ts"
|
|
5
|
-
import { ChildProcess } from "effect/unstable/process"
|
|
6
5
|
import { PrdIssue } from "../domain/PrdIssue.ts"
|
|
7
6
|
import * as Yaml from "yaml"
|
|
8
|
-
import { configEditor } from "../shared/config.ts"
|
|
9
7
|
import { CurrentProjectId } from "../Settings.ts"
|
|
10
8
|
import { layerProjectIdPrompt } from "../Projects.ts"
|
|
9
|
+
import { Editor } from "../Editor.ts"
|
|
11
10
|
|
|
12
11
|
const issueTemplate = `---
|
|
13
12
|
title: Issue Title
|
|
@@ -33,31 +32,18 @@ const handler = flow(
|
|
|
33
32
|
Command.withHandler(
|
|
34
33
|
Effect.fnUntraced(function* () {
|
|
35
34
|
const source = yield* IssueSource
|
|
36
|
-
const fs = yield* FileSystem.FileSystem
|
|
37
35
|
const projectId = yield* CurrentProjectId
|
|
38
|
-
const
|
|
36
|
+
const editor = yield* Editor
|
|
37
|
+
|
|
38
|
+
const content = yield* editor.editTemp({
|
|
39
39
|
suffix: ".md",
|
|
40
|
+
initialContent: issueTemplate,
|
|
40
41
|
})
|
|
41
|
-
|
|
42
|
-
yield* fs.writeFileString(tempFile, issueTemplate)
|
|
43
|
-
|
|
44
|
-
const exitCode = yield* ChildProcess.make(
|
|
45
|
-
editor[0]!,
|
|
46
|
-
[...editor.slice(1), tempFile],
|
|
47
|
-
{
|
|
48
|
-
stdin: "inherit",
|
|
49
|
-
stdout: "inherit",
|
|
50
|
-
stderr: "inherit",
|
|
51
|
-
},
|
|
52
|
-
).pipe(ChildProcess.exitCode)
|
|
53
|
-
if (exitCode !== 0) return
|
|
54
|
-
|
|
55
|
-
const content = yield* fs.readFileString(tempFile)
|
|
56
|
-
if (content.trim() === issueTemplate.trim()) {
|
|
42
|
+
if (Option.isNone(content)) {
|
|
57
43
|
return
|
|
58
44
|
}
|
|
59
45
|
|
|
60
|
-
const lines = content.split("\n")
|
|
46
|
+
const lines = content.value.split("\n")
|
|
61
47
|
const yamlLines: string[] = []
|
|
62
48
|
let descriptionStartIndex = 0
|
|
63
49
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -93,7 +79,13 @@ const handler = flow(
|
|
|
93
79
|
console.log(`URL: ${created.url}`)
|
|
94
80
|
}, Effect.scoped),
|
|
95
81
|
),
|
|
96
|
-
Command.provide(
|
|
82
|
+
Command.provide(
|
|
83
|
+
Layer.mergeAll(
|
|
84
|
+
layerProjectIdPrompt,
|
|
85
|
+
CurrentIssueSource.layer,
|
|
86
|
+
Editor.layer,
|
|
87
|
+
),
|
|
88
|
+
),
|
|
97
89
|
)
|
|
98
90
|
|
|
99
91
|
export const commandIssue = Command.make("issue").pipe(
|
package/src/commands/plan.ts
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Data,
|
|
3
|
-
Effect,
|
|
4
|
-
FileSystem,
|
|
5
|
-
Layer,
|
|
6
|
-
Option,
|
|
7
|
-
Path,
|
|
8
|
-
pipe,
|
|
9
|
-
Schema,
|
|
10
|
-
} from "effect"
|
|
1
|
+
import { Data, Effect, FileSystem, Option, Path, pipe, Schema } from "effect"
|
|
11
2
|
import { PromptGen } from "../PromptGen.ts"
|
|
12
3
|
import { Prd } from "../Prd.ts"
|
|
13
4
|
import { Worktree } from "../Worktree.ts"
|
|
@@ -17,14 +8,11 @@ import { Command, Flag } from "effect/unstable/cli"
|
|
|
17
8
|
import { CurrentIssueSource } from "../CurrentIssueSource.ts"
|
|
18
9
|
import { commandRoot } from "./root.ts"
|
|
19
10
|
import { CurrentProjectId, Settings } from "../Settings.ts"
|
|
20
|
-
import {
|
|
21
|
-
addOrUpdateProject,
|
|
22
|
-
layerProjectIdPrompt,
|
|
23
|
-
selectProject,
|
|
24
|
-
} from "../Projects.ts"
|
|
11
|
+
import { addOrUpdateProject, selectProject } from "../Projects.ts"
|
|
25
12
|
import { agentPlanner } from "../Agents/planner.ts"
|
|
26
13
|
import { agentTasker } from "../Agents/tasker.ts"
|
|
27
14
|
import { commandPlanTasks } from "./plan/tasks.ts"
|
|
15
|
+
import { Editor } from "../Editor.ts"
|
|
28
16
|
|
|
29
17
|
const dangerous = Flag.boolean("dangerous").pipe(
|
|
30
18
|
Flag.withAlias("d"),
|
|
@@ -46,25 +34,36 @@ export const commandPlan = Command.make("plan", {
|
|
|
46
34
|
Command.withHandler(
|
|
47
35
|
Effect.fnUntraced(
|
|
48
36
|
function* ({ dangerous, withNewProject }) {
|
|
37
|
+
const editor = yield* Editor
|
|
38
|
+
|
|
39
|
+
const thePlan = yield* editor.editTemp({
|
|
40
|
+
suffix: ".md",
|
|
41
|
+
})
|
|
42
|
+
if (Option.isNone(thePlan)) return
|
|
43
|
+
|
|
49
44
|
const project = withNewProject
|
|
50
45
|
? yield* addOrUpdateProject()
|
|
51
46
|
: yield* selectProject
|
|
52
47
|
const { specsDirectory } = yield* commandRoot
|
|
53
48
|
const commandPrefix = yield* getCommandPrefix
|
|
49
|
+
|
|
54
50
|
yield* plan({
|
|
51
|
+
plan: thePlan.value,
|
|
55
52
|
specsDirectory,
|
|
56
53
|
targetBranch: project.targetBranch,
|
|
57
54
|
commandPrefix,
|
|
58
55
|
dangerous,
|
|
59
56
|
}).pipe(Effect.provideService(CurrentProjectId, project.id))
|
|
60
57
|
},
|
|
61
|
-
Effect.provide([Settings.layer, CurrentIssueSource.layer]),
|
|
58
|
+
Effect.provide([Settings.layer, CurrentIssueSource.layer, Editor.layer]),
|
|
62
59
|
),
|
|
63
60
|
),
|
|
64
61
|
Command.withSubcommands([commandPlanTasks]),
|
|
65
62
|
)
|
|
63
|
+
|
|
66
64
|
const plan = Effect.fnUntraced(
|
|
67
65
|
function* (options: {
|
|
66
|
+
readonly plan: string
|
|
68
67
|
readonly specsDirectory: string
|
|
69
68
|
readonly targetBranch: Option.Option<string>
|
|
70
69
|
readonly commandPrefix: (
|
|
@@ -75,23 +74,27 @@ const plan = Effect.fnUntraced(
|
|
|
75
74
|
const fs = yield* FileSystem.FileSystem
|
|
76
75
|
const pathService = yield* Path.Path
|
|
77
76
|
const worktree = yield* Worktree
|
|
77
|
+
|
|
78
78
|
const cliAgent = yield* getOrSelectCliAgent
|
|
79
79
|
|
|
80
80
|
yield* agentPlanner({
|
|
81
|
+
plan: options.plan,
|
|
81
82
|
specsDirectory: options.specsDirectory,
|
|
82
83
|
commandPrefix: options.commandPrefix,
|
|
83
84
|
dangerous: options.dangerous,
|
|
84
85
|
cliAgent,
|
|
85
86
|
})
|
|
86
87
|
|
|
87
|
-
yield* Effect.log("Converting specification into tasks")
|
|
88
88
|
const planDetails = yield* pipe(
|
|
89
89
|
fs.readFileString(
|
|
90
90
|
pathService.join(worktree.directory, ".lalph", "plan.json"),
|
|
91
91
|
),
|
|
92
92
|
Effect.flatMap(Schema.decodeEffect(PlanDetails)),
|
|
93
|
+
Effect.mapError(() => new SpecNotFound()),
|
|
93
94
|
)
|
|
94
95
|
|
|
96
|
+
yield* Effect.log("Converting specification into tasks")
|
|
97
|
+
|
|
95
98
|
yield* agentTasker({
|
|
96
99
|
specificationPath: planDetails.specification,
|
|
97
100
|
specsDirectory: options.specsDirectory,
|
|
@@ -114,7 +117,7 @@ const plan = Effect.fnUntraced(
|
|
|
114
117
|
Effect.provide([
|
|
115
118
|
PromptGen.layer,
|
|
116
119
|
Prd.layerProvided,
|
|
117
|
-
Worktree.layer
|
|
120
|
+
Worktree.layer,
|
|
118
121
|
Settings.layer,
|
|
119
122
|
CurrentIssueSource.layer,
|
|
120
123
|
]),
|