lalph 0.2.10 → 0.2.12
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 +175 -178
- package/package.json +1 -1
- package/src/Worktree.ts +55 -24
- package/src/commands/plan/tasks.ts +1 -1
- package/src/commands/plan.ts +17 -22
- package/src/commands/root.ts +6 -23
package/dist/cli.mjs
CHANGED
|
@@ -151168,6 +151168,139 @@ const withWorkerState = (projectId) => (effect) => AtomRegistry.use((registry) =
|
|
|
151168
151168
|
}));
|
|
151169
151169
|
});
|
|
151170
151170
|
|
|
151171
|
+
//#endregion
|
|
151172
|
+
//#region src/Projects.ts
|
|
151173
|
+
const layerProjectIdPrompt = effect$1(CurrentProjectId, gen(function* () {
|
|
151174
|
+
return (yield* selectProject).id;
|
|
151175
|
+
})).pipe(provide$3(Settings.layer), provide$3(CurrentIssueSource.layer));
|
|
151176
|
+
const allProjects = new Setting("projects", Array$1(Project$1));
|
|
151177
|
+
const getAllProjects = Settings.get(allProjects).pipe(map$8(getOrElse$1(() => [])));
|
|
151178
|
+
const projectById = fnUntraced(function* (projectId) {
|
|
151179
|
+
const projects = yield* getAllProjects;
|
|
151180
|
+
return findFirst$3(projects, (p) => p.id === projectId);
|
|
151181
|
+
});
|
|
151182
|
+
const allProjectsAtom = (function() {
|
|
151183
|
+
const read = Settings.runtime.atom(fnUntraced(function* () {
|
|
151184
|
+
const projects = yield* (yield* Settings).get(allProjects);
|
|
151185
|
+
return getOrElse$1(projects, () => []);
|
|
151186
|
+
}));
|
|
151187
|
+
const set = Settings.runtime.fn()(fnUntraced(function* (value, get) {
|
|
151188
|
+
yield* (yield* Settings).set(allProjects, some$2(value));
|
|
151189
|
+
get.refresh(read);
|
|
151190
|
+
}));
|
|
151191
|
+
return writable((get) => {
|
|
151192
|
+
get.mount(set);
|
|
151193
|
+
return get(read);
|
|
151194
|
+
}, (ctx, value) => {
|
|
151195
|
+
ctx.set(set, value);
|
|
151196
|
+
}, (r) => r(read));
|
|
151197
|
+
})();
|
|
151198
|
+
const projectAtom = family((projectId) => {
|
|
151199
|
+
const read = make(fnUntraced(function* (get) {
|
|
151200
|
+
const projects = yield* get.result(allProjectsAtom);
|
|
151201
|
+
return findFirst$3(projects, (p) => p.id === projectId);
|
|
151202
|
+
}));
|
|
151203
|
+
const set = Settings.runtime.fn()(fnUntraced(function* (value, get) {
|
|
151204
|
+
const projects = yield* get.result(allProjectsAtom);
|
|
151205
|
+
const updatedProjects = match$7(value, {
|
|
151206
|
+
onNone: () => filter$6(projects, (p) => p.id !== projectId),
|
|
151207
|
+
onSome: (project) => map$12(projects, (p) => p.id === projectId ? project : p)
|
|
151208
|
+
});
|
|
151209
|
+
get.set(allProjectsAtom, updatedProjects);
|
|
151210
|
+
}));
|
|
151211
|
+
return writable((get) => {
|
|
151212
|
+
get.mount(set);
|
|
151213
|
+
return get(read);
|
|
151214
|
+
}, (ctx, value) => {
|
|
151215
|
+
ctx.set(set, value);
|
|
151216
|
+
}, (refresh) => refresh(read));
|
|
151217
|
+
});
|
|
151218
|
+
const selectProject = gen(function* () {
|
|
151219
|
+
const projects = yield* getAllProjects;
|
|
151220
|
+
if (projects.length === 0) return yield* welcomeWizard;
|
|
151221
|
+
else if (projects.length === 1) {
|
|
151222
|
+
const project = projects[0];
|
|
151223
|
+
yield* log$1(`Using project: ${project.id}`);
|
|
151224
|
+
return project;
|
|
151225
|
+
}
|
|
151226
|
+
return yield* autoComplete({
|
|
151227
|
+
message: "Select a project:",
|
|
151228
|
+
choices: projects.map((p) => ({
|
|
151229
|
+
title: p.id,
|
|
151230
|
+
value: p
|
|
151231
|
+
}))
|
|
151232
|
+
});
|
|
151233
|
+
});
|
|
151234
|
+
const welcomeWizard = gen(function* () {
|
|
151235
|
+
const welcome = [
|
|
151236
|
+
" .--.",
|
|
151237
|
+
" |^()^| lalph",
|
|
151238
|
+
" '--'",
|
|
151239
|
+
"",
|
|
151240
|
+
"Welcome! Let's add your first project.",
|
|
151241
|
+
"Projects let you configure how lalph runs tasks.",
|
|
151242
|
+
""
|
|
151243
|
+
].join("\n");
|
|
151244
|
+
console.log(welcome);
|
|
151245
|
+
return yield* addOrUpdateProject();
|
|
151246
|
+
});
|
|
151247
|
+
const addOrUpdateProject = fnUntraced(function* (existing) {
|
|
151248
|
+
const projects = yield* getAllProjects;
|
|
151249
|
+
const id = existing ? existing.id : yield* text$2({
|
|
151250
|
+
message: "Project name",
|
|
151251
|
+
validate(input) {
|
|
151252
|
+
input = input.trim();
|
|
151253
|
+
if (input.length === 0) return fail$4("Project name cannot be empty");
|
|
151254
|
+
else if (projects.some((p) => p.id === input)) return fail$4("Project already exists");
|
|
151255
|
+
return succeed$1(input);
|
|
151256
|
+
}
|
|
151257
|
+
});
|
|
151258
|
+
const concurrency = yield* integer$2({
|
|
151259
|
+
message: "Concurrency (number of tasks to run in parallel)",
|
|
151260
|
+
min: 1
|
|
151261
|
+
});
|
|
151262
|
+
const targetBranch = pipe(yield* text$2({ message: "Target branch (leave empty to use HEAD)" }), trim, liftPredicate(isNonEmpty));
|
|
151263
|
+
const gitFlow = yield* select({
|
|
151264
|
+
message: "Git flow",
|
|
151265
|
+
choices: [{
|
|
151266
|
+
title: "Pull Request",
|
|
151267
|
+
description: "Create a pull request for each task",
|
|
151268
|
+
value: "pr"
|
|
151269
|
+
}, {
|
|
151270
|
+
title: "Commit",
|
|
151271
|
+
description: "Tasks are committed directly to the target branch",
|
|
151272
|
+
value: "commit"
|
|
151273
|
+
}]
|
|
151274
|
+
});
|
|
151275
|
+
const reviewAgent = yield* toggle({ message: "Enable review agent?" });
|
|
151276
|
+
const project = new Project$1({
|
|
151277
|
+
id: ProjectId.makeUnsafe(id),
|
|
151278
|
+
enabled: existing ? existing.enabled : true,
|
|
151279
|
+
concurrency,
|
|
151280
|
+
targetBranch,
|
|
151281
|
+
gitFlow,
|
|
151282
|
+
reviewAgent
|
|
151283
|
+
});
|
|
151284
|
+
yield* Settings.set(allProjects, some$2(existing ? projects.map((p) => p.id === project.id ? project : p) : [...projects, project]));
|
|
151285
|
+
const source = yield* IssueSource;
|
|
151286
|
+
yield* source.reset.pipe(provideService(CurrentProjectId, project.id));
|
|
151287
|
+
yield* source.settings(project.id);
|
|
151288
|
+
return project;
|
|
151289
|
+
});
|
|
151290
|
+
|
|
151291
|
+
//#endregion
|
|
151292
|
+
//#region src/shared/git.ts
|
|
151293
|
+
const parseBranch = (ref) => {
|
|
151294
|
+
const parts = ref.split("/");
|
|
151295
|
+
const remote = parts.length > 1 ? parts[0] : "origin";
|
|
151296
|
+
const branch = parts.length > 1 ? parts.slice(1).join("/") : parts[0];
|
|
151297
|
+
return {
|
|
151298
|
+
remote,
|
|
151299
|
+
branch,
|
|
151300
|
+
branchWithRemote: `${remote}/${branch}`
|
|
151301
|
+
};
|
|
151302
|
+
};
|
|
151303
|
+
|
|
151171
151304
|
//#endregion
|
|
151172
151305
|
//#region src/Worktree.ts
|
|
151173
151306
|
var Worktree = class extends Service$1()("lalph/Worktree", { make: gen(function* () {
|
|
@@ -151188,17 +151321,16 @@ var Worktree = class extends Service$1()("lalph/Worktree", { make: gen(function*
|
|
|
151188
151321
|
}));
|
|
151189
151322
|
yield* make$23`git worktree add ${directory} -d HEAD`.pipe(exitCode);
|
|
151190
151323
|
yield* fs.makeDirectory(pathService.join(directory, ".lalph"), { recursive: true });
|
|
151191
|
-
const
|
|
151192
|
-
yield*
|
|
151193
|
-
|
|
151194
|
-
|
|
151195
|
-
|
|
151196
|
-
|
|
151197
|
-
})`${setupPath}`.pipe(exitCode);
|
|
151324
|
+
const execHelpers = yield* makeExecHelpers({ directory });
|
|
151325
|
+
yield* setupWorktree({
|
|
151326
|
+
directory,
|
|
151327
|
+
exec: execHelpers.exec,
|
|
151328
|
+
pathService
|
|
151329
|
+
});
|
|
151198
151330
|
return {
|
|
151199
151331
|
directory,
|
|
151200
151332
|
inExisting,
|
|
151201
|
-
...
|
|
151333
|
+
...execHelpers
|
|
151202
151334
|
};
|
|
151203
151335
|
}).pipe(withSpan("Worktree.build")) }) {
|
|
151204
151336
|
static layer = effect$1(this, this.make);
|
|
@@ -151223,11 +151355,31 @@ const seedSetupScript = fnUntraced(function* (setupPath) {
|
|
|
151223
151355
|
yield* fs.writeFileString(setupPath, setupScriptTemplate(baseBranch));
|
|
151224
151356
|
yield* fs.chmod(setupPath, 493);
|
|
151225
151357
|
});
|
|
151226
|
-
const
|
|
151358
|
+
const setupWorktree = fnUntraced(function* (options) {
|
|
151227
151359
|
const fs = yield* FileSystem;
|
|
151228
|
-
|
|
151229
|
-
|
|
151230
|
-
|
|
151360
|
+
const targetBranch = yield* getTargetBranch;
|
|
151361
|
+
const shouldUseWorktree = isSome(targetBranch);
|
|
151362
|
+
if (shouldUseWorktree) {
|
|
151363
|
+
const parsed = parseBranch(targetBranch.value);
|
|
151364
|
+
if ((yield* options.exec`git checkout ${parsed.branchWithRemote}`) !== 0) {
|
|
151365
|
+
yield* options.exec`git checkout -b ${parsed.branch}`;
|
|
151366
|
+
yield* options.exec`git push -u ${parsed.remote} ${parsed.branch}`;
|
|
151367
|
+
}
|
|
151368
|
+
}
|
|
151369
|
+
const setupPath = shouldUseWorktree ? options.pathService.join(options.directory, "scripts", "worktree-setup.sh") : options.pathService.resolve("scripts", "worktree-setup.sh");
|
|
151370
|
+
yield* seedSetupScript(setupPath);
|
|
151371
|
+
if (yield* fs.exists(setupPath)) {
|
|
151372
|
+
const setupCwd = shouldUseWorktree ? options.directory : options.pathService.resolve(".");
|
|
151373
|
+
yield* make$23({
|
|
151374
|
+
cwd: setupCwd,
|
|
151375
|
+
shell: process.env.SHELL ?? true
|
|
151376
|
+
})`${setupPath}`.pipe(exitCode);
|
|
151377
|
+
}
|
|
151378
|
+
});
|
|
151379
|
+
const getTargetBranch = gen(function* () {
|
|
151380
|
+
const project = yield* projectById(yield* CurrentProjectId);
|
|
151381
|
+
if (isNone(project)) return none$4();
|
|
151382
|
+
return project.value.targetBranch;
|
|
151231
151383
|
});
|
|
151232
151384
|
const discoverBaseBranch = gen(function* () {
|
|
151233
151385
|
const originHead = yield* make$23`git symbolic-ref --short refs/remotes/origin/HEAD`.pipe(string, catch_$1((_) => succeed$1("")), map$8((output) => output.trim()));
|
|
@@ -151243,13 +151395,6 @@ git checkout origin/${baseBranch}
|
|
|
151243
151395
|
|
|
151244
151396
|
# Seeded by lalph. Customize this to prepare new worktrees.
|
|
151245
151397
|
`;
|
|
151246
|
-
const checkoutScriptTemplate = `#!/usr/bin/env bash
|
|
151247
|
-
set -euo pipefail
|
|
151248
|
-
|
|
151249
|
-
pnpm install || true
|
|
151250
|
-
|
|
151251
|
-
# Seeded by lalph. Customize this to prepare branches after checkout.
|
|
151252
|
-
`;
|
|
151253
151398
|
const makeExecHelpers = fnUntraced(function* (options) {
|
|
151254
151399
|
const spawner = yield* ChildProcessSpawner;
|
|
151255
151400
|
const provide = provideService(ChildProcessSpawner, spawner);
|
|
@@ -151559,19 +151704,6 @@ const makeWaitForFile = gen(function* () {
|
|
|
151559
151704
|
return (directory, name) => pipe(fs.watch(directory), filter$2((e) => pathService.basename(e.path) === name), runHead);
|
|
151560
151705
|
});
|
|
151561
151706
|
|
|
151562
|
-
//#endregion
|
|
151563
|
-
//#region src/shared/git.ts
|
|
151564
|
-
const parseBranch = (ref) => {
|
|
151565
|
-
const parts = ref.split("/");
|
|
151566
|
-
const remote = parts.length > 1 ? parts[0] : "origin";
|
|
151567
|
-
const branch = parts.length > 1 ? parts.slice(1).join("/") : parts[0];
|
|
151568
|
-
return {
|
|
151569
|
-
remote,
|
|
151570
|
-
branch,
|
|
151571
|
-
branchWithRemote: `${remote}/${branch}`
|
|
151572
|
-
};
|
|
151573
|
-
};
|
|
151574
|
-
|
|
151575
151707
|
//#endregion
|
|
151576
151708
|
//#region src/GitFlow.ts
|
|
151577
151709
|
var GitFlow = class extends Service$1()("lalph/GitFlow") {};
|
|
@@ -151730,126 +151862,6 @@ const agentTimeout = fnUntraced(function* (options) {
|
|
|
151730
151862
|
}));
|
|
151731
151863
|
});
|
|
151732
151864
|
|
|
151733
|
-
//#endregion
|
|
151734
|
-
//#region src/Projects.ts
|
|
151735
|
-
const layerProjectIdPrompt = effect$1(CurrentProjectId, gen(function* () {
|
|
151736
|
-
return (yield* selectProject).id;
|
|
151737
|
-
})).pipe(provide$3(Settings.layer), provide$3(CurrentIssueSource.layer));
|
|
151738
|
-
const allProjects = new Setting("projects", Array$1(Project$1));
|
|
151739
|
-
const getAllProjects = Settings.get(allProjects).pipe(map$8(getOrElse$1(() => [])));
|
|
151740
|
-
const projectById = fnUntraced(function* (projectId) {
|
|
151741
|
-
const projects = yield* getAllProjects;
|
|
151742
|
-
return findFirst$3(projects, (p) => p.id === projectId);
|
|
151743
|
-
});
|
|
151744
|
-
const allProjectsAtom = (function() {
|
|
151745
|
-
const read = Settings.runtime.atom(fnUntraced(function* () {
|
|
151746
|
-
const projects = yield* (yield* Settings).get(allProjects);
|
|
151747
|
-
return getOrElse$1(projects, () => []);
|
|
151748
|
-
}));
|
|
151749
|
-
const set = Settings.runtime.fn()(fnUntraced(function* (value, get) {
|
|
151750
|
-
yield* (yield* Settings).set(allProjects, some$2(value));
|
|
151751
|
-
get.refresh(read);
|
|
151752
|
-
}));
|
|
151753
|
-
return writable((get) => {
|
|
151754
|
-
get.mount(set);
|
|
151755
|
-
return get(read);
|
|
151756
|
-
}, (ctx, value) => {
|
|
151757
|
-
ctx.set(set, value);
|
|
151758
|
-
}, (r) => r(read));
|
|
151759
|
-
})();
|
|
151760
|
-
const projectAtom = family((projectId) => {
|
|
151761
|
-
const read = make(fnUntraced(function* (get) {
|
|
151762
|
-
const projects = yield* get.result(allProjectsAtom);
|
|
151763
|
-
return findFirst$3(projects, (p) => p.id === projectId);
|
|
151764
|
-
}));
|
|
151765
|
-
const set = Settings.runtime.fn()(fnUntraced(function* (value, get) {
|
|
151766
|
-
const projects = yield* get.result(allProjectsAtom);
|
|
151767
|
-
const updatedProjects = match$7(value, {
|
|
151768
|
-
onNone: () => filter$6(projects, (p) => p.id !== projectId),
|
|
151769
|
-
onSome: (project) => map$12(projects, (p) => p.id === projectId ? project : p)
|
|
151770
|
-
});
|
|
151771
|
-
get.set(allProjectsAtom, updatedProjects);
|
|
151772
|
-
}));
|
|
151773
|
-
return writable((get) => {
|
|
151774
|
-
get.mount(set);
|
|
151775
|
-
return get(read);
|
|
151776
|
-
}, (ctx, value) => {
|
|
151777
|
-
ctx.set(set, value);
|
|
151778
|
-
}, (refresh) => refresh(read));
|
|
151779
|
-
});
|
|
151780
|
-
const selectProject = gen(function* () {
|
|
151781
|
-
const projects = yield* getAllProjects;
|
|
151782
|
-
if (projects.length === 0) return yield* welcomeWizard;
|
|
151783
|
-
else if (projects.length === 1) {
|
|
151784
|
-
const project = projects[0];
|
|
151785
|
-
yield* log$1(`Using project: ${project.id}`);
|
|
151786
|
-
return project;
|
|
151787
|
-
}
|
|
151788
|
-
return yield* autoComplete({
|
|
151789
|
-
message: "Select a project:",
|
|
151790
|
-
choices: projects.map((p) => ({
|
|
151791
|
-
title: p.id,
|
|
151792
|
-
value: p
|
|
151793
|
-
}))
|
|
151794
|
-
});
|
|
151795
|
-
});
|
|
151796
|
-
const welcomeWizard = gen(function* () {
|
|
151797
|
-
const welcome = [
|
|
151798
|
-
" .--.",
|
|
151799
|
-
" |^()^| lalph",
|
|
151800
|
-
" '--'",
|
|
151801
|
-
"",
|
|
151802
|
-
"Welcome! Let's add your first project.",
|
|
151803
|
-
"Projects let you configure how lalph runs tasks.",
|
|
151804
|
-
""
|
|
151805
|
-
].join("\n");
|
|
151806
|
-
console.log(welcome);
|
|
151807
|
-
return yield* addOrUpdateProject();
|
|
151808
|
-
});
|
|
151809
|
-
const addOrUpdateProject = fnUntraced(function* (existing) {
|
|
151810
|
-
const projects = yield* getAllProjects;
|
|
151811
|
-
const id = existing ? existing.id : yield* text$2({
|
|
151812
|
-
message: "Project name",
|
|
151813
|
-
validate(input) {
|
|
151814
|
-
input = input.trim();
|
|
151815
|
-
if (input.length === 0) return fail$4("Project name cannot be empty");
|
|
151816
|
-
else if (projects.some((p) => p.id === input)) return fail$4("Project already exists");
|
|
151817
|
-
return succeed$1(input);
|
|
151818
|
-
}
|
|
151819
|
-
});
|
|
151820
|
-
const concurrency = yield* integer$2({
|
|
151821
|
-
message: "Concurrency (number of tasks to run in parallel)",
|
|
151822
|
-
min: 1
|
|
151823
|
-
});
|
|
151824
|
-
const targetBranch = pipe(yield* text$2({ message: "Target branch (leave empty to use HEAD)" }), trim, liftPredicate(isNonEmpty));
|
|
151825
|
-
const gitFlow = yield* select({
|
|
151826
|
-
message: "Git flow",
|
|
151827
|
-
choices: [{
|
|
151828
|
-
title: "Pull Request",
|
|
151829
|
-
description: "Create a pull request for each task",
|
|
151830
|
-
value: "pr"
|
|
151831
|
-
}, {
|
|
151832
|
-
title: "Commit",
|
|
151833
|
-
description: "Tasks are committed directly to the target branch",
|
|
151834
|
-
value: "commit"
|
|
151835
|
-
}]
|
|
151836
|
-
});
|
|
151837
|
-
const reviewAgent = yield* toggle({ message: "Enable review agent?" });
|
|
151838
|
-
const project = new Project$1({
|
|
151839
|
-
id: ProjectId.makeUnsafe(id),
|
|
151840
|
-
enabled: existing ? existing.enabled : true,
|
|
151841
|
-
concurrency,
|
|
151842
|
-
targetBranch,
|
|
151843
|
-
gitFlow,
|
|
151844
|
-
reviewAgent
|
|
151845
|
-
});
|
|
151846
|
-
yield* Settings.set(allProjects, some$2(existing ? projects.map((p) => p.id === project.id ? project : p) : [...projects, project]));
|
|
151847
|
-
const source = yield* IssueSource;
|
|
151848
|
-
yield* source.reset.pipe(provideService(CurrentProjectId, project.id));
|
|
151849
|
-
yield* source.settings(project.id);
|
|
151850
|
-
return project;
|
|
151851
|
-
});
|
|
151852
|
-
|
|
151853
151865
|
//#endregion
|
|
151854
151866
|
//#region src/commands/root.ts
|
|
151855
151867
|
const run = fnUntraced(function* (options) {
|
|
@@ -151864,19 +151876,6 @@ const run = fnUntraced(function* (options) {
|
|
|
151864
151876
|
const gitFlow = yield* GitFlow;
|
|
151865
151877
|
const currentWorker = yield* CurrentWorkerState;
|
|
151866
151878
|
const registry = yield* AtomRegistry;
|
|
151867
|
-
if (isSome(options.targetBranch)) {
|
|
151868
|
-
const parsed = parseBranch(options.targetBranch.value);
|
|
151869
|
-
if ((yield* worktree.exec`git checkout ${parsed.branchWithRemote}`) !== 0) {
|
|
151870
|
-
yield* worktree.exec`git checkout -b ${parsed.branch}`;
|
|
151871
|
-
yield* worktree.exec`git push -u ${parsed.remote} ${parsed.branch}`;
|
|
151872
|
-
}
|
|
151873
|
-
}
|
|
151874
|
-
if (gitFlow.branch) {
|
|
151875
|
-
yield* worktree.exec`git branch -D ${gitFlow.branch}`;
|
|
151876
|
-
yield* worktree.exec`git checkout -b ${gitFlow.branch}`;
|
|
151877
|
-
}
|
|
151878
|
-
const checkoutScript = pathService.resolve("scripts", "checkout-setup.sh");
|
|
151879
|
-
if (yield* fs.exists(checkoutScript)) yield* worktree.exec`${checkoutScript}`;
|
|
151880
151879
|
yield* addFinalizer(fnUntraced(function* () {
|
|
151881
151880
|
const currentBranchName = yield* worktree.currentBranch(worktree.directory).pipe(option$1, map$8(getOrUndefined));
|
|
151882
151881
|
if (!currentBranchName) return;
|
|
@@ -151886,9 +151885,12 @@ const run = fnUntraced(function* (options) {
|
|
|
151886
151885
|
let taskId = void 0;
|
|
151887
151886
|
yield* addFinalizer(fnUntraced(function* (exit) {
|
|
151888
151887
|
if (exit._tag === "Success") return;
|
|
151889
|
-
|
|
151890
|
-
|
|
151891
|
-
|
|
151888
|
+
if (taskId) yield* source.updateIssue({
|
|
151889
|
+
projectId,
|
|
151890
|
+
issueId: taskId,
|
|
151891
|
+
state: "todo"
|
|
151892
|
+
});
|
|
151893
|
+
else yield* (yield* Prd).revertUpdatedIssues;
|
|
151892
151894
|
}, ignore()));
|
|
151893
151895
|
registry.update(currentWorker.state, (s) => s.transitionTo(WorkerStatus.ChoosingTask()));
|
|
151894
151896
|
const chosenTask = yield* agentChooser({
|
|
@@ -151979,7 +151981,7 @@ const runProject = fnUntraced(function* (options) {
|
|
|
151979
151981
|
commandPrefix: options.commandPrefix,
|
|
151980
151982
|
review: options.project.reviewAgent
|
|
151981
151983
|
}).pipe(provide$1(options.project.gitFlow === "commit" ? GitFlowCommit : GitFlowPR, { local: true }), withWorkerState(options.project.id))), catchTags({
|
|
151982
|
-
NoMoreWork(
|
|
151984
|
+
NoMoreWork(_error) {
|
|
151983
151985
|
if (isFinite) {
|
|
151984
151986
|
iterations = currentIteration;
|
|
151985
151987
|
return log$1(`No more work to process, ending after ${currentIteration} iteration(s).`);
|
|
@@ -151987,7 +151989,7 @@ const runProject = fnUntraced(function* (options) {
|
|
|
151987
151989
|
const log = size$3(fibers) <= 1 ? log$1("No more work to process, waiting 30 seconds...") : void_$1;
|
|
151988
151990
|
return andThen(log, sleep(seconds(30)));
|
|
151989
151991
|
},
|
|
151990
|
-
QuitError(
|
|
151992
|
+
QuitError(_error) {
|
|
151991
151993
|
quit = true;
|
|
151992
151994
|
return void_$1;
|
|
151993
151995
|
}
|
|
@@ -152094,7 +152096,7 @@ const commandPlanTasks = make$35("tasks", { specificationPath }).pipe(withDescri
|
|
|
152094
152096
|
Settings.layer,
|
|
152095
152097
|
PromptGen.layer,
|
|
152096
152098
|
Prd.layerProvided.pipe(provide$3(layerProjectIdPrompt)),
|
|
152097
|
-
Worktree.layer
|
|
152099
|
+
Worktree.layer.pipe(provide$3(layerProjectIdPrompt))
|
|
152098
152100
|
]))));
|
|
152099
152101
|
|
|
152100
152102
|
//#endregion
|
|
@@ -152120,11 +152122,6 @@ const plan = fnUntraced(function* (options) {
|
|
|
152120
152122
|
const pathService = yield* Path$1;
|
|
152121
152123
|
const worktree = yield* Worktree;
|
|
152122
152124
|
const cliAgent = yield* getOrSelectCliAgent;
|
|
152123
|
-
const exec = (template, ...args) => exitCode(make$23({
|
|
152124
|
-
cwd: worktree.directory,
|
|
152125
|
-
extendEnv: true
|
|
152126
|
-
})(template, ...args));
|
|
152127
|
-
if (isSome(options.targetBranch)) yield* exec`git checkout ${options.targetBranch.value.includes("/") ? options.targetBranch.value : `origin/${options.targetBranch.value}`}`;
|
|
152128
152125
|
yield* agentPlanner({
|
|
152129
152126
|
specsDirectory: options.specsDirectory,
|
|
152130
152127
|
commandPrefix: options.commandPrefix,
|
|
@@ -152142,7 +152139,7 @@ const plan = fnUntraced(function* (options) {
|
|
|
152142
152139
|
}, scoped$1, provide$1([
|
|
152143
152140
|
PromptGen.layer,
|
|
152144
152141
|
Prd.layerProvided,
|
|
152145
|
-
Worktree.layer,
|
|
152142
|
+
Worktree.layer.pipe(provide$3(layerProjectIdPrompt)),
|
|
152146
152143
|
Settings.layer,
|
|
152147
152144
|
CurrentIssueSource.layer
|
|
152148
152145
|
]));
|
|
@@ -152232,7 +152229,7 @@ const commandSource = make$35("source").pipe(withDescription("Select the issue s
|
|
|
152232
152229
|
|
|
152233
152230
|
//#endregion
|
|
152234
152231
|
//#region package.json
|
|
152235
|
-
var version = "0.2.
|
|
152232
|
+
var version = "0.2.12";
|
|
152236
152233
|
|
|
152237
152234
|
//#endregion
|
|
152238
152235
|
//#region src/commands/projects/ls.ts
|
package/package.json
CHANGED
package/src/Worktree.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
Layer,
|
|
10
10
|
Option,
|
|
11
11
|
Path,
|
|
12
|
+
PlatformError,
|
|
12
13
|
Schema,
|
|
13
14
|
ServiceMap,
|
|
14
15
|
Stream,
|
|
@@ -18,6 +19,9 @@ import { RunnerStalled } from "./domain/Errors.ts"
|
|
|
18
19
|
import type { CliAgent } from "./domain/CliAgent.ts"
|
|
19
20
|
import { constWorkerMaxOutputChunks, CurrentWorkerState } from "./Workers.ts"
|
|
20
21
|
import { AtomRegistry } from "effect/unstable/reactivity"
|
|
22
|
+
import { CurrentProjectId } from "./Settings.ts"
|
|
23
|
+
import { projectById } from "./Projects.ts"
|
|
24
|
+
import { parseBranch } from "./shared/git.ts"
|
|
21
25
|
|
|
22
26
|
export class Worktree extends ServiceMap.Service<Worktree>()("lalph/Worktree", {
|
|
23
27
|
make: Effect.gen(function* () {
|
|
@@ -52,22 +56,17 @@ export class Worktree extends ServiceMap.Service<Worktree>()("lalph/Worktree", {
|
|
|
52
56
|
recursive: true,
|
|
53
57
|
})
|
|
54
58
|
|
|
55
|
-
const
|
|
56
|
-
yield*
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
yield* ChildProcess.make({
|
|
62
|
-
cwd: directory,
|
|
63
|
-
shell: process.env.SHELL ?? true,
|
|
64
|
-
})`${setupPath}`.pipe(ChildProcess.exitCode)
|
|
65
|
-
}
|
|
59
|
+
const execHelpers = yield* makeExecHelpers({ directory })
|
|
60
|
+
yield* setupWorktree({
|
|
61
|
+
directory,
|
|
62
|
+
exec: execHelpers.exec,
|
|
63
|
+
pathService,
|
|
64
|
+
})
|
|
66
65
|
|
|
67
66
|
return {
|
|
68
67
|
directory,
|
|
69
68
|
inExisting,
|
|
70
|
-
...
|
|
69
|
+
...execHelpers,
|
|
71
70
|
} as const
|
|
72
71
|
}).pipe(Effect.withSpan("Worktree.build")),
|
|
73
72
|
}) {
|
|
@@ -107,13 +106,53 @@ const seedSetupScript = Effect.fnUntraced(function* (setupPath: string) {
|
|
|
107
106
|
yield* fs.chmod(setupPath, 0o755)
|
|
108
107
|
})
|
|
109
108
|
|
|
110
|
-
const
|
|
109
|
+
const setupWorktree = Effect.fnUntraced(function* (options: {
|
|
110
|
+
readonly directory: string
|
|
111
|
+
readonly exec: (
|
|
112
|
+
template: TemplateStringsArray,
|
|
113
|
+
...args: Array<string | number | boolean>
|
|
114
|
+
) => Effect.Effect<ChildProcessSpawner.ExitCode, PlatformError.PlatformError>
|
|
115
|
+
readonly pathService: Path.Path
|
|
116
|
+
}) {
|
|
111
117
|
const fs = yield* FileSystem.FileSystem
|
|
118
|
+
const targetBranch = yield* getTargetBranch
|
|
119
|
+
const shouldUseWorktree = Option.isSome(targetBranch)
|
|
120
|
+
|
|
121
|
+
if (shouldUseWorktree) {
|
|
122
|
+
const parsed = parseBranch(targetBranch.value)
|
|
123
|
+
const code = yield* options.exec`git checkout ${parsed.branchWithRemote}`
|
|
124
|
+
if (code !== 0) {
|
|
125
|
+
yield* options.exec`git checkout -b ${parsed.branch}`
|
|
126
|
+
yield* options.exec`git push -u ${parsed.remote} ${parsed.branch}`
|
|
127
|
+
}
|
|
128
|
+
}
|
|
112
129
|
|
|
113
|
-
|
|
130
|
+
const setupPath = shouldUseWorktree
|
|
131
|
+
? options.pathService.join(
|
|
132
|
+
options.directory,
|
|
133
|
+
"scripts",
|
|
134
|
+
"worktree-setup.sh",
|
|
135
|
+
)
|
|
136
|
+
: options.pathService.resolve("scripts", "worktree-setup.sh")
|
|
137
|
+
yield* seedSetupScript(setupPath)
|
|
138
|
+
if (yield* fs.exists(setupPath)) {
|
|
139
|
+
const setupCwd = shouldUseWorktree
|
|
140
|
+
? options.directory
|
|
141
|
+
: options.pathService.resolve(".")
|
|
142
|
+
yield* ChildProcess.make({
|
|
143
|
+
cwd: setupCwd,
|
|
144
|
+
shell: process.env.SHELL ?? true,
|
|
145
|
+
})`${setupPath}`.pipe(ChildProcess.exitCode)
|
|
146
|
+
}
|
|
147
|
+
})
|
|
114
148
|
|
|
115
|
-
|
|
116
|
-
yield*
|
|
149
|
+
const getTargetBranch = Effect.gen(function* () {
|
|
150
|
+
const projectId = yield* CurrentProjectId
|
|
151
|
+
const project = yield* projectById(projectId)
|
|
152
|
+
if (Option.isNone(project)) {
|
|
153
|
+
return Option.none<string>()
|
|
154
|
+
}
|
|
155
|
+
return project.value.targetBranch
|
|
117
156
|
})
|
|
118
157
|
|
|
119
158
|
const discoverBaseBranch = Effect.gen(function* () {
|
|
@@ -148,14 +187,6 @@ git checkout origin/${baseBranch}
|
|
|
148
187
|
|
|
149
188
|
# Seeded by lalph. Customize this to prepare new worktrees.
|
|
150
189
|
`
|
|
151
|
-
const checkoutScriptTemplate = `#!/usr/bin/env bash
|
|
152
|
-
set -euo pipefail
|
|
153
|
-
|
|
154
|
-
pnpm install || true
|
|
155
|
-
|
|
156
|
-
# Seeded by lalph. Customize this to prepare branches after checkout.
|
|
157
|
-
`
|
|
158
|
-
|
|
159
190
|
const makeExecHelpers = Effect.fnUntraced(function* (options: {
|
|
160
191
|
readonly directory: string
|
|
161
192
|
}) {
|
package/src/commands/plan.ts
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Data,
|
|
3
|
+
Effect,
|
|
4
|
+
FileSystem,
|
|
5
|
+
Layer,
|
|
6
|
+
Option,
|
|
7
|
+
Path,
|
|
8
|
+
pipe,
|
|
9
|
+
Schema,
|
|
10
|
+
} from "effect"
|
|
2
11
|
import { PromptGen } from "../PromptGen.ts"
|
|
3
12
|
import { Prd } from "../Prd.ts"
|
|
4
|
-
import { ChildProcess } from "effect/unstable/process"
|
|
5
13
|
import { Worktree } from "../Worktree.ts"
|
|
14
|
+
import type { ChildProcess } from "effect/unstable/process"
|
|
6
15
|
import { getCommandPrefix, getOrSelectCliAgent } from "./agent.ts"
|
|
7
16
|
import { Command, Flag } from "effect/unstable/cli"
|
|
8
17
|
import { CurrentIssueSource } from "../CurrentIssueSource.ts"
|
|
9
18
|
import { commandRoot } from "./root.ts"
|
|
10
19
|
import { CurrentProjectId, Settings } from "../Settings.ts"
|
|
11
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
addOrUpdateProject,
|
|
22
|
+
layerProjectIdPrompt,
|
|
23
|
+
selectProject,
|
|
24
|
+
} from "../Projects.ts"
|
|
12
25
|
import { agentPlanner } from "../Agents/planner.ts"
|
|
13
26
|
import { agentTasker } from "../Agents/tasker.ts"
|
|
14
27
|
import { commandPlanTasks } from "./plan/tasks.ts"
|
|
@@ -64,24 +77,6 @@ const plan = Effect.fnUntraced(
|
|
|
64
77
|
const worktree = yield* Worktree
|
|
65
78
|
const cliAgent = yield* getOrSelectCliAgent
|
|
66
79
|
|
|
67
|
-
const exec = (
|
|
68
|
-
template: TemplateStringsArray,
|
|
69
|
-
...args: Array<string | number | boolean>
|
|
70
|
-
) =>
|
|
71
|
-
ChildProcess.exitCode(
|
|
72
|
-
ChildProcess.make({
|
|
73
|
-
cwd: worktree.directory,
|
|
74
|
-
extendEnv: true,
|
|
75
|
-
})(template, ...args),
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
if (Option.isSome(options.targetBranch)) {
|
|
79
|
-
const targetWithRemote = options.targetBranch.value.includes("/")
|
|
80
|
-
? options.targetBranch.value
|
|
81
|
-
: `origin/${options.targetBranch.value}`
|
|
82
|
-
yield* exec`git checkout ${targetWithRemote}`
|
|
83
|
-
}
|
|
84
|
-
|
|
85
80
|
yield* agentPlanner({
|
|
86
81
|
specsDirectory: options.specsDirectory,
|
|
87
82
|
commandPrefix: options.commandPrefix,
|
|
@@ -119,7 +114,7 @@ const plan = Effect.fnUntraced(
|
|
|
119
114
|
Effect.provide([
|
|
120
115
|
PromptGen.layer,
|
|
121
116
|
Prd.layerProvided,
|
|
122
|
-
Worktree.layer,
|
|
117
|
+
Worktree.layer.pipe(Layer.provide(layerProjectIdPrompt)),
|
|
123
118
|
Settings.layer,
|
|
124
119
|
CurrentIssueSource.layer,
|
|
125
120
|
]),
|
package/src/commands/root.ts
CHANGED
|
@@ -36,7 +36,6 @@ import {
|
|
|
36
36
|
} from "../Workers.ts"
|
|
37
37
|
import { WorkerStatus } from "../domain/WorkerState.ts"
|
|
38
38
|
import { GitFlow, GitFlowCommit, GitFlowPR } from "../GitFlow.ts"
|
|
39
|
-
import { parseBranch } from "../shared/git.ts"
|
|
40
39
|
import { getAllProjects, welcomeWizard } from "../Projects.ts"
|
|
41
40
|
import type { Project } from "../domain/Project.ts"
|
|
42
41
|
|
|
@@ -66,24 +65,6 @@ const run = Effect.fnUntraced(
|
|
|
66
65
|
const currentWorker = yield* CurrentWorkerState
|
|
67
66
|
const registry = yield* AtomRegistry.AtomRegistry
|
|
68
67
|
|
|
69
|
-
if (Option.isSome(options.targetBranch)) {
|
|
70
|
-
const parsed = parseBranch(options.targetBranch.value)
|
|
71
|
-
const code = yield* worktree.exec`git checkout ${parsed.branchWithRemote}`
|
|
72
|
-
if (code !== 0) {
|
|
73
|
-
yield* worktree.exec`git checkout -b ${parsed.branch}`
|
|
74
|
-
yield* worktree.exec`git push -u ${parsed.remote} ${parsed.branch}`
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
if (gitFlow.branch) {
|
|
78
|
-
yield* worktree.exec`git branch -D ${gitFlow.branch}`
|
|
79
|
-
yield* worktree.exec`git checkout -b ${gitFlow.branch}`
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const checkoutScript = pathService.resolve("scripts", "checkout-setup.sh")
|
|
83
|
-
if (yield* fs.exists(checkoutScript)) {
|
|
84
|
-
yield* worktree.exec`${checkoutScript}`
|
|
85
|
-
}
|
|
86
|
-
|
|
87
68
|
// ensure cleanup of branch after run
|
|
88
69
|
yield* Effect.addFinalizer(
|
|
89
70
|
Effect.fnUntraced(function* () {
|
|
@@ -105,12 +86,14 @@ const run = Effect.fnUntraced(
|
|
|
105
86
|
yield* Effect.addFinalizer(
|
|
106
87
|
Effect.fnUntraced(function* (exit) {
|
|
107
88
|
if (exit._tag === "Success") return
|
|
108
|
-
const prd = yield* Prd
|
|
109
89
|
if (taskId) {
|
|
110
|
-
yield*
|
|
90
|
+
yield* source.updateIssue({
|
|
91
|
+
projectId,
|
|
111
92
|
issueId: taskId,
|
|
93
|
+
state: "todo",
|
|
112
94
|
})
|
|
113
95
|
} else {
|
|
96
|
+
const prd = yield* Prd
|
|
114
97
|
yield* prd.revertUpdatedIssues
|
|
115
98
|
}
|
|
116
99
|
}, Effect.ignore()),
|
|
@@ -284,7 +267,7 @@ const runProject = Effect.fnUntraced(
|
|
|
284
267
|
),
|
|
285
268
|
),
|
|
286
269
|
Effect.catchTags({
|
|
287
|
-
NoMoreWork(
|
|
270
|
+
NoMoreWork(_error) {
|
|
288
271
|
if (isFinite) {
|
|
289
272
|
// If we have a finite number of iterations, we exit when no more
|
|
290
273
|
// work is found
|
|
@@ -299,7 +282,7 @@ const runProject = Effect.fnUntraced(
|
|
|
299
282
|
: Effect.void
|
|
300
283
|
return Effect.andThen(log, Effect.sleep(Duration.seconds(30)))
|
|
301
284
|
},
|
|
302
|
-
QuitError(
|
|
285
|
+
QuitError(_error) {
|
|
303
286
|
quit = true
|
|
304
287
|
return Effect.void
|
|
305
288
|
},
|