lalph 0.2.9 → 0.2.11
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 +180 -178
- package/package.json +1 -1
- package/src/GitFlow.ts +8 -2
- package/src/PromptGen.ts +3 -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 +2 -21
package/dist/cli.mjs
CHANGED
|
@@ -151008,7 +151008,9 @@ Once you have completed your review, you should:
|
|
|
151008
151008
|
|
|
151009
151009
|
${options.gitFlow.reviewInstructions}
|
|
151010
151010
|
|
|
151011
|
-
|
|
151011
|
+
**Everything should be done without interaction or asking for permission.**
|
|
151012
|
+
|
|
151013
|
+
# Previous instructions (only for context, do not repeat)
|
|
151012
151014
|
|
|
151013
151015
|
${options.prompt}`;
|
|
151014
151016
|
const promptReviewCustom = (options) => `${options.prompt}
|
|
@@ -151166,6 +151168,139 @@ const withWorkerState = (projectId) => (effect) => AtomRegistry.use((registry) =
|
|
|
151166
151168
|
}));
|
|
151167
151169
|
});
|
|
151168
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
|
+
|
|
151169
151304
|
//#endregion
|
|
151170
151305
|
//#region src/Worktree.ts
|
|
151171
151306
|
var Worktree = class extends Service$1()("lalph/Worktree", { make: gen(function* () {
|
|
@@ -151186,17 +151321,16 @@ var Worktree = class extends Service$1()("lalph/Worktree", { make: gen(function*
|
|
|
151186
151321
|
}));
|
|
151187
151322
|
yield* make$23`git worktree add ${directory} -d HEAD`.pipe(exitCode);
|
|
151188
151323
|
yield* fs.makeDirectory(pathService.join(directory, ".lalph"), { recursive: true });
|
|
151189
|
-
const
|
|
151190
|
-
yield*
|
|
151191
|
-
|
|
151192
|
-
|
|
151193
|
-
|
|
151194
|
-
|
|
151195
|
-
})`${setupPath}`.pipe(exitCode);
|
|
151324
|
+
const execHelpers = yield* makeExecHelpers({ directory });
|
|
151325
|
+
yield* setupWorktree({
|
|
151326
|
+
directory,
|
|
151327
|
+
exec: execHelpers.exec,
|
|
151328
|
+
pathService
|
|
151329
|
+
});
|
|
151196
151330
|
return {
|
|
151197
151331
|
directory,
|
|
151198
151332
|
inExisting,
|
|
151199
|
-
...
|
|
151333
|
+
...execHelpers
|
|
151200
151334
|
};
|
|
151201
151335
|
}).pipe(withSpan("Worktree.build")) }) {
|
|
151202
151336
|
static layer = effect$1(this, this.make);
|
|
@@ -151221,11 +151355,31 @@ const seedSetupScript = fnUntraced(function* (setupPath) {
|
|
|
151221
151355
|
yield* fs.writeFileString(setupPath, setupScriptTemplate(baseBranch));
|
|
151222
151356
|
yield* fs.chmod(setupPath, 493);
|
|
151223
151357
|
});
|
|
151224
|
-
const
|
|
151358
|
+
const setupWorktree = fnUntraced(function* (options) {
|
|
151225
151359
|
const fs = yield* FileSystem;
|
|
151226
|
-
|
|
151227
|
-
|
|
151228
|
-
|
|
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;
|
|
151229
151383
|
});
|
|
151230
151384
|
const discoverBaseBranch = gen(function* () {
|
|
151231
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()));
|
|
@@ -151241,13 +151395,6 @@ git checkout origin/${baseBranch}
|
|
|
151241
151395
|
|
|
151242
151396
|
# Seeded by lalph. Customize this to prepare new worktrees.
|
|
151243
151397
|
`;
|
|
151244
|
-
const checkoutScriptTemplate = `#!/usr/bin/env bash
|
|
151245
|
-
set -euo pipefail
|
|
151246
|
-
|
|
151247
|
-
pnpm install || true
|
|
151248
|
-
|
|
151249
|
-
# Seeded by lalph. Customize this to prepare branches after checkout.
|
|
151250
|
-
`;
|
|
151251
151398
|
const makeExecHelpers = fnUntraced(function* (options) {
|
|
151252
151399
|
const spawner = yield* ChildProcessSpawner;
|
|
151253
151400
|
const provide = provideService(ChildProcessSpawner, spawner);
|
|
@@ -151557,19 +151704,6 @@ const makeWaitForFile = gen(function* () {
|
|
|
151557
151704
|
return (directory, name) => pipe(fs.watch(directory), filter$2((e) => pathService.basename(e.path) === name), runHead);
|
|
151558
151705
|
});
|
|
151559
151706
|
|
|
151560
|
-
//#endregion
|
|
151561
|
-
//#region src/shared/git.ts
|
|
151562
|
-
const parseBranch = (ref) => {
|
|
151563
|
-
const parts = ref.split("/");
|
|
151564
|
-
const remote = parts.length > 1 ? parts[0] : "origin";
|
|
151565
|
-
const branch = parts.length > 1 ? parts.slice(1).join("/") : parts[0];
|
|
151566
|
-
return {
|
|
151567
|
-
remote,
|
|
151568
|
-
branch,
|
|
151569
|
-
branchWithRemote: `${remote}/${branch}`
|
|
151570
|
-
};
|
|
151571
|
-
};
|
|
151572
|
-
|
|
151573
151707
|
//#endregion
|
|
151574
151708
|
//#region src/GitFlow.ts
|
|
151575
151709
|
var GitFlow = class extends Service$1()("lalph/GitFlow") {};
|
|
@@ -151584,7 +151718,10 @@ const GitFlowPR = succeed$2(GitFlow, GitFlow.of({
|
|
|
151584
151718
|
- **DO NOT** commit any of the files in the \`.lalph\` directory.
|
|
151585
151719
|
- You have full permission to push branches, create PRs or create git commits.`,
|
|
151586
151720
|
reviewInstructions: `You are already on the PR branch with their changes.
|
|
151587
|
-
After making any changes, commit and push them to the same pull request
|
|
151721
|
+
After making any changes, commit and push them to the same pull request.
|
|
151722
|
+
|
|
151723
|
+
- **DO NOT** commit any of the files in the \`.lalph\` directory.
|
|
151724
|
+
- You have full permission to push branches, create PRs or create git commits.`,
|
|
151588
151725
|
postWork: () => void_$1,
|
|
151589
151726
|
autoMerge: fnUntraced(function* (options) {
|
|
151590
151727
|
const prd = yield* Prd;
|
|
@@ -151613,7 +151750,10 @@ const GitFlowCommit = effect$1(GitFlow, gen(function* () {
|
|
|
151613
151750
|
commitInstructions: () => `When you have completed your changes, **you must** commit them to the current local branch. Do not git push your changes or switch branches.
|
|
151614
151751
|
- **DO NOT** commit any of the files in the \`.lalph\` directory.`,
|
|
151615
151752
|
reviewInstructions: `You are already on the branch with their changes.
|
|
151616
|
-
After making any changes, commit them to the same branch. Do not git push your changes or switch branches
|
|
151753
|
+
After making any changes, commit them to the same branch. Do not git push your changes or switch branches.
|
|
151754
|
+
|
|
151755
|
+
- **DO NOT** commit any of the files in the \`.lalph\` directory.
|
|
151756
|
+
- You have full permission to create git commits.`,
|
|
151617
151757
|
postWork: fnUntraced(function* ({ worktree, targetBranch, issueId }) {
|
|
151618
151758
|
if (!targetBranch) return yield* logWarning("GitFlowCommit: No target branch specified, skipping postWork.");
|
|
151619
151759
|
const prd = yield* Prd;
|
|
@@ -151722,126 +151862,6 @@ const agentTimeout = fnUntraced(function* (options) {
|
|
|
151722
151862
|
}));
|
|
151723
151863
|
});
|
|
151724
151864
|
|
|
151725
|
-
//#endregion
|
|
151726
|
-
//#region src/Projects.ts
|
|
151727
|
-
const layerProjectIdPrompt = effect$1(CurrentProjectId, gen(function* () {
|
|
151728
|
-
return (yield* selectProject).id;
|
|
151729
|
-
})).pipe(provide$3(Settings.layer), provide$3(CurrentIssueSource.layer));
|
|
151730
|
-
const allProjects = new Setting("projects", Array$1(Project$1));
|
|
151731
|
-
const getAllProjects = Settings.get(allProjects).pipe(map$8(getOrElse$1(() => [])));
|
|
151732
|
-
const projectById = fnUntraced(function* (projectId) {
|
|
151733
|
-
const projects = yield* getAllProjects;
|
|
151734
|
-
return findFirst$3(projects, (p) => p.id === projectId);
|
|
151735
|
-
});
|
|
151736
|
-
const allProjectsAtom = (function() {
|
|
151737
|
-
const read = Settings.runtime.atom(fnUntraced(function* () {
|
|
151738
|
-
const projects = yield* (yield* Settings).get(allProjects);
|
|
151739
|
-
return getOrElse$1(projects, () => []);
|
|
151740
|
-
}));
|
|
151741
|
-
const set = Settings.runtime.fn()(fnUntraced(function* (value, get) {
|
|
151742
|
-
yield* (yield* Settings).set(allProjects, some$2(value));
|
|
151743
|
-
get.refresh(read);
|
|
151744
|
-
}));
|
|
151745
|
-
return writable((get) => {
|
|
151746
|
-
get.mount(set);
|
|
151747
|
-
return get(read);
|
|
151748
|
-
}, (ctx, value) => {
|
|
151749
|
-
ctx.set(set, value);
|
|
151750
|
-
}, (r) => r(read));
|
|
151751
|
-
})();
|
|
151752
|
-
const projectAtom = family((projectId) => {
|
|
151753
|
-
const read = make(fnUntraced(function* (get) {
|
|
151754
|
-
const projects = yield* get.result(allProjectsAtom);
|
|
151755
|
-
return findFirst$3(projects, (p) => p.id === projectId);
|
|
151756
|
-
}));
|
|
151757
|
-
const set = Settings.runtime.fn()(fnUntraced(function* (value, get) {
|
|
151758
|
-
const projects = yield* get.result(allProjectsAtom);
|
|
151759
|
-
const updatedProjects = match$7(value, {
|
|
151760
|
-
onNone: () => filter$6(projects, (p) => p.id !== projectId),
|
|
151761
|
-
onSome: (project) => map$12(projects, (p) => p.id === projectId ? project : p)
|
|
151762
|
-
});
|
|
151763
|
-
get.set(allProjectsAtom, updatedProjects);
|
|
151764
|
-
}));
|
|
151765
|
-
return writable((get) => {
|
|
151766
|
-
get.mount(set);
|
|
151767
|
-
return get(read);
|
|
151768
|
-
}, (ctx, value) => {
|
|
151769
|
-
ctx.set(set, value);
|
|
151770
|
-
}, (refresh) => refresh(read));
|
|
151771
|
-
});
|
|
151772
|
-
const selectProject = gen(function* () {
|
|
151773
|
-
const projects = yield* getAllProjects;
|
|
151774
|
-
if (projects.length === 0) return yield* welcomeWizard;
|
|
151775
|
-
else if (projects.length === 1) {
|
|
151776
|
-
const project = projects[0];
|
|
151777
|
-
yield* log$1(`Using project: ${project.id}`);
|
|
151778
|
-
return project;
|
|
151779
|
-
}
|
|
151780
|
-
return yield* autoComplete({
|
|
151781
|
-
message: "Select a project:",
|
|
151782
|
-
choices: projects.map((p) => ({
|
|
151783
|
-
title: p.id,
|
|
151784
|
-
value: p
|
|
151785
|
-
}))
|
|
151786
|
-
});
|
|
151787
|
-
});
|
|
151788
|
-
const welcomeWizard = gen(function* () {
|
|
151789
|
-
const welcome = [
|
|
151790
|
-
" .--.",
|
|
151791
|
-
" |^()^| lalph",
|
|
151792
|
-
" '--'",
|
|
151793
|
-
"",
|
|
151794
|
-
"Welcome! Let's add your first project.",
|
|
151795
|
-
"Projects let you configure how lalph runs tasks.",
|
|
151796
|
-
""
|
|
151797
|
-
].join("\n");
|
|
151798
|
-
console.log(welcome);
|
|
151799
|
-
return yield* addOrUpdateProject();
|
|
151800
|
-
});
|
|
151801
|
-
const addOrUpdateProject = fnUntraced(function* (existing) {
|
|
151802
|
-
const projects = yield* getAllProjects;
|
|
151803
|
-
const id = existing ? existing.id : yield* text$2({
|
|
151804
|
-
message: "Project name",
|
|
151805
|
-
validate(input) {
|
|
151806
|
-
input = input.trim();
|
|
151807
|
-
if (input.length === 0) return fail$4("Project name cannot be empty");
|
|
151808
|
-
else if (projects.some((p) => p.id === input)) return fail$4("Project already exists");
|
|
151809
|
-
return succeed$1(input);
|
|
151810
|
-
}
|
|
151811
|
-
});
|
|
151812
|
-
const concurrency = yield* integer$2({
|
|
151813
|
-
message: "Concurrency (number of tasks to run in parallel)",
|
|
151814
|
-
min: 1
|
|
151815
|
-
});
|
|
151816
|
-
const targetBranch = pipe(yield* text$2({ message: "Target branch (leave empty to use HEAD)" }), trim, liftPredicate(isNonEmpty));
|
|
151817
|
-
const gitFlow = yield* select({
|
|
151818
|
-
message: "Git flow",
|
|
151819
|
-
choices: [{
|
|
151820
|
-
title: "Pull Request",
|
|
151821
|
-
description: "Create a pull request for each task",
|
|
151822
|
-
value: "pr"
|
|
151823
|
-
}, {
|
|
151824
|
-
title: "Commit",
|
|
151825
|
-
description: "Tasks are committed directly to the target branch",
|
|
151826
|
-
value: "commit"
|
|
151827
|
-
}]
|
|
151828
|
-
});
|
|
151829
|
-
const reviewAgent = yield* toggle({ message: "Enable review agent?" });
|
|
151830
|
-
const project = new Project$1({
|
|
151831
|
-
id: ProjectId.makeUnsafe(id),
|
|
151832
|
-
enabled: existing ? existing.enabled : true,
|
|
151833
|
-
concurrency,
|
|
151834
|
-
targetBranch,
|
|
151835
|
-
gitFlow,
|
|
151836
|
-
reviewAgent
|
|
151837
|
-
});
|
|
151838
|
-
yield* Settings.set(allProjects, some$2(existing ? projects.map((p) => p.id === project.id ? project : p) : [...projects, project]));
|
|
151839
|
-
const source = yield* IssueSource;
|
|
151840
|
-
yield* source.reset.pipe(provideService(CurrentProjectId, project.id));
|
|
151841
|
-
yield* source.settings(project.id);
|
|
151842
|
-
return project;
|
|
151843
|
-
});
|
|
151844
|
-
|
|
151845
151865
|
//#endregion
|
|
151846
151866
|
//#region src/commands/root.ts
|
|
151847
151867
|
const run = fnUntraced(function* (options) {
|
|
@@ -151856,19 +151876,6 @@ const run = fnUntraced(function* (options) {
|
|
|
151856
151876
|
const gitFlow = yield* GitFlow;
|
|
151857
151877
|
const currentWorker = yield* CurrentWorkerState;
|
|
151858
151878
|
const registry = yield* AtomRegistry;
|
|
151859
|
-
if (isSome(options.targetBranch)) {
|
|
151860
|
-
const parsed = parseBranch(options.targetBranch.value);
|
|
151861
|
-
if ((yield* worktree.exec`git checkout ${parsed.branchWithRemote}`) !== 0) {
|
|
151862
|
-
yield* worktree.exec`git checkout -b ${parsed.branch}`;
|
|
151863
|
-
yield* worktree.exec`git push -u ${parsed.remote} ${parsed.branch}`;
|
|
151864
|
-
}
|
|
151865
|
-
}
|
|
151866
|
-
if (gitFlow.branch) {
|
|
151867
|
-
yield* worktree.exec`git branch -D ${gitFlow.branch}`;
|
|
151868
|
-
yield* worktree.exec`git checkout -b ${gitFlow.branch}`;
|
|
151869
|
-
}
|
|
151870
|
-
const checkoutScript = pathService.resolve("scripts", "checkout-setup.sh");
|
|
151871
|
-
if (yield* fs.exists(checkoutScript)) yield* worktree.exec`${checkoutScript}`;
|
|
151872
151879
|
yield* addFinalizer(fnUntraced(function* () {
|
|
151873
151880
|
const currentBranchName = yield* worktree.currentBranch(worktree.directory).pipe(option$1, map$8(getOrUndefined));
|
|
151874
151881
|
if (!currentBranchName) return;
|
|
@@ -151971,7 +151978,7 @@ const runProject = fnUntraced(function* (options) {
|
|
|
151971
151978
|
commandPrefix: options.commandPrefix,
|
|
151972
151979
|
review: options.project.reviewAgent
|
|
151973
151980
|
}).pipe(provide$1(options.project.gitFlow === "commit" ? GitFlowCommit : GitFlowPR, { local: true }), withWorkerState(options.project.id))), catchTags({
|
|
151974
|
-
NoMoreWork(
|
|
151981
|
+
NoMoreWork(_error) {
|
|
151975
151982
|
if (isFinite) {
|
|
151976
151983
|
iterations = currentIteration;
|
|
151977
151984
|
return log$1(`No more work to process, ending after ${currentIteration} iteration(s).`);
|
|
@@ -151979,7 +151986,7 @@ const runProject = fnUntraced(function* (options) {
|
|
|
151979
151986
|
const log = size$3(fibers) <= 1 ? log$1("No more work to process, waiting 30 seconds...") : void_$1;
|
|
151980
151987
|
return andThen(log, sleep(seconds(30)));
|
|
151981
151988
|
},
|
|
151982
|
-
QuitError(
|
|
151989
|
+
QuitError(_error) {
|
|
151983
151990
|
quit = true;
|
|
151984
151991
|
return void_$1;
|
|
151985
151992
|
}
|
|
@@ -152086,7 +152093,7 @@ const commandPlanTasks = make$35("tasks", { specificationPath }).pipe(withDescri
|
|
|
152086
152093
|
Settings.layer,
|
|
152087
152094
|
PromptGen.layer,
|
|
152088
152095
|
Prd.layerProvided.pipe(provide$3(layerProjectIdPrompt)),
|
|
152089
|
-
Worktree.layer
|
|
152096
|
+
Worktree.layer.pipe(provide$3(layerProjectIdPrompt))
|
|
152090
152097
|
]))));
|
|
152091
152098
|
|
|
152092
152099
|
//#endregion
|
|
@@ -152112,11 +152119,6 @@ const plan = fnUntraced(function* (options) {
|
|
|
152112
152119
|
const pathService = yield* Path$1;
|
|
152113
152120
|
const worktree = yield* Worktree;
|
|
152114
152121
|
const cliAgent = yield* getOrSelectCliAgent;
|
|
152115
|
-
const exec = (template, ...args) => exitCode(make$23({
|
|
152116
|
-
cwd: worktree.directory,
|
|
152117
|
-
extendEnv: true
|
|
152118
|
-
})(template, ...args));
|
|
152119
|
-
if (isSome(options.targetBranch)) yield* exec`git checkout ${options.targetBranch.value.includes("/") ? options.targetBranch.value : `origin/${options.targetBranch.value}`}`;
|
|
152120
152122
|
yield* agentPlanner({
|
|
152121
152123
|
specsDirectory: options.specsDirectory,
|
|
152122
152124
|
commandPrefix: options.commandPrefix,
|
|
@@ -152134,7 +152136,7 @@ const plan = fnUntraced(function* (options) {
|
|
|
152134
152136
|
}, scoped$1, provide$1([
|
|
152135
152137
|
PromptGen.layer,
|
|
152136
152138
|
Prd.layerProvided,
|
|
152137
|
-
Worktree.layer,
|
|
152139
|
+
Worktree.layer.pipe(provide$3(layerProjectIdPrompt)),
|
|
152138
152140
|
Settings.layer,
|
|
152139
152141
|
CurrentIssueSource.layer
|
|
152140
152142
|
]));
|
|
@@ -152224,7 +152226,7 @@ const commandSource = make$35("source").pipe(withDescription("Select the issue s
|
|
|
152224
152226
|
|
|
152225
152227
|
//#endregion
|
|
152226
152228
|
//#region package.json
|
|
152227
|
-
var version = "0.2.
|
|
152229
|
+
var version = "0.2.11";
|
|
152228
152230
|
|
|
152229
152231
|
//#endregion
|
|
152230
152232
|
//#region src/commands/projects/ls.ts
|
package/package.json
CHANGED
package/src/GitFlow.ts
CHANGED
|
@@ -74,7 +74,10 @@ export const GitFlowPR = Layer.succeed(
|
|
|
74
74
|
- You have full permission to push branches, create PRs or create git commits.`,
|
|
75
75
|
|
|
76
76
|
reviewInstructions: `You are already on the PR branch with their changes.
|
|
77
|
-
After making any changes, commit and push them to the same pull request
|
|
77
|
+
After making any changes, commit and push them to the same pull request.
|
|
78
|
+
|
|
79
|
+
- **DO NOT** commit any of the files in the \`.lalph\` directory.
|
|
80
|
+
- You have full permission to push branches, create PRs or create git commits.`,
|
|
78
81
|
|
|
79
82
|
postWork: () => Effect.void,
|
|
80
83
|
autoMerge: Effect.fnUntraced(function* (options) {
|
|
@@ -126,7 +129,10 @@ export const GitFlowCommit = Layer.effect(
|
|
|
126
129
|
- **DO NOT** commit any of the files in the \`.lalph\` directory.`,
|
|
127
130
|
|
|
128
131
|
reviewInstructions: `You are already on the branch with their changes.
|
|
129
|
-
After making any changes, commit them to the same branch. Do not git push your changes or switch branches
|
|
132
|
+
After making any changes, commit them to the same branch. Do not git push your changes or switch branches.
|
|
133
|
+
|
|
134
|
+
- **DO NOT** commit any of the files in the \`.lalph\` directory.
|
|
135
|
+
- You have full permission to create git commits.`,
|
|
130
136
|
|
|
131
137
|
postWork: Effect.fnUntraced(function* ({
|
|
132
138
|
worktree,
|
package/src/PromptGen.ts
CHANGED
|
@@ -198,7 +198,9 @@ Once you have completed your review, you should:
|
|
|
198
198
|
|
|
199
199
|
${options.gitFlow.reviewInstructions}
|
|
200
200
|
|
|
201
|
-
|
|
201
|
+
**Everything should be done without interaction or asking for permission.**
|
|
202
|
+
|
|
203
|
+
# Previous instructions (only for context, do not repeat)
|
|
202
204
|
|
|
203
205
|
${options.prompt}`
|
|
204
206
|
|
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* () {
|
|
@@ -284,7 +265,7 @@ const runProject = Effect.fnUntraced(
|
|
|
284
265
|
),
|
|
285
266
|
),
|
|
286
267
|
Effect.catchTags({
|
|
287
|
-
NoMoreWork(
|
|
268
|
+
NoMoreWork(_error) {
|
|
288
269
|
if (isFinite) {
|
|
289
270
|
// If we have a finite number of iterations, we exit when no more
|
|
290
271
|
// work is found
|
|
@@ -299,7 +280,7 @@ const runProject = Effect.fnUntraced(
|
|
|
299
280
|
: Effect.void
|
|
300
281
|
return Effect.andThen(log, Effect.sleep(Duration.seconds(30)))
|
|
301
282
|
},
|
|
302
|
-
QuitError(
|
|
283
|
+
QuitError(_error) {
|
|
303
284
|
quit = true
|
|
304
285
|
return Effect.void
|
|
305
286
|
},
|