@towles/tool 0.0.59 → 0.0.60
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/package.json +1 -1
- package/src/commands/gh/branch.test.ts +107 -108
- package/src/commands/gh/branch.ts +39 -36
- package/src/lib/auto-claude/pipeline-execution.test.ts +195 -0
- package/src/lib/auto-claude/prompt-templates/01_research.prompt.md +21 -0
- package/src/lib/auto-claude/prompt-templates/02_plan.prompt.md +27 -0
- package/src/lib/auto-claude/prompt-templates/03_plan-annotations.prompt.md +15 -0
- package/src/lib/auto-claude/prompt-templates/04_plan-implementation.prompt.md +35 -0
- package/src/lib/auto-claude/prompt-templates/05_implement.prompt.md +36 -0
- package/src/lib/auto-claude/prompt-templates/06_review.prompt.md +32 -0
- package/src/lib/auto-claude/prompt-templates/07_refresh.prompt.md +30 -0
- package/src/lib/auto-claude/prompt-templates/CLAUDE.md +12 -0
- package/src/lib/auto-claude/prompt-templates/index.test.ts +2 -2
- package/src/lib/auto-claude/prompt-templates/index.ts +7 -7
- package/src/lib/auto-claude/run-claude.test.ts +160 -0
- package/src/lib/auto-claude/steps/steps.test.ts +330 -0
- package/src/lib/auto-claude/test-helpers.ts +86 -0
- package/src/lib/auto-claude/utils-execution.test.ts +152 -0
- package/src/lib/auto-claude/utils.test.ts +7 -7
- package/src/lib/auto-claude/utils.ts +2 -1
- package/src/utils/git/branch-name.test.ts +83 -0
- package/src/utils/git/branch-name.ts +10 -0
- package/src/lib/auto-claude/prompt-templates/01-prompt-research.md +0 -28
- package/src/lib/auto-claude/prompt-templates/02-prompt-plan.md +0 -28
- package/src/lib/auto-claude/prompt-templates/03-prompt-plan-annotations.md +0 -21
- package/src/lib/auto-claude/prompt-templates/04-prompt-plan-implementation.md +0 -33
- package/src/lib/auto-claude/prompt-templates/05-prompt-implement.md +0 -31
- package/src/lib/auto-claude/prompt-templates/06-prompt-review.md +0 -30
- package/src/lib/auto-claude/prompt-templates/07-prompt-refresh.md +0 -39
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
import type { ClaudeResult, IssueContext } from "./utils";
|
|
7
|
+
|
|
8
|
+
export interface TestRepo {
|
|
9
|
+
dir: string;
|
|
10
|
+
cleanup: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Creates a local git repo with a single empty commit on `main`.
|
|
15
|
+
*/
|
|
16
|
+
export function createTestRepo(prefix = "ac-test-"): TestRepo {
|
|
17
|
+
const dir = mkdtempSync(join(tmpdir(), prefix));
|
|
18
|
+
execSync("git init", { cwd: dir, stdio: "ignore" });
|
|
19
|
+
execSync("git config user.email 'test@test.com'", { cwd: dir, stdio: "ignore" });
|
|
20
|
+
execSync("git config user.name 'Test'", { cwd: dir, stdio: "ignore" });
|
|
21
|
+
execSync("git commit --allow-empty -m 'init'", { cwd: dir, stdio: "ignore" });
|
|
22
|
+
execSync("git branch -M main", { cwd: dir, stdio: "ignore" });
|
|
23
|
+
return { dir, cleanup: () => rmSync(dir, { recursive: true, force: true }) };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Creates a local git repo backed by a bare clone acting as `origin`.
|
|
28
|
+
*/
|
|
29
|
+
export function createTestRepoWithRemote(prefix = "ac-test-remote-"): TestRepo {
|
|
30
|
+
const base = mkdtempSync(join(tmpdir(), prefix));
|
|
31
|
+
const workDir = join(base, "work");
|
|
32
|
+
const bareDir = join(base, "bare.git");
|
|
33
|
+
|
|
34
|
+
mkdirSync(workDir);
|
|
35
|
+
execSync("git init", { cwd: workDir, stdio: "ignore" });
|
|
36
|
+
execSync("git config user.email 'test@test.com'", { cwd: workDir, stdio: "ignore" });
|
|
37
|
+
execSync("git config user.name 'Test'", { cwd: workDir, stdio: "ignore" });
|
|
38
|
+
execSync("git commit --allow-empty -m 'init'", { cwd: workDir, stdio: "ignore" });
|
|
39
|
+
execSync("git branch -M main", { cwd: workDir, stdio: "ignore" });
|
|
40
|
+
execSync(`git clone --bare "${workDir}" "${bareDir}"`, { stdio: "ignore" });
|
|
41
|
+
execSync(`git remote add origin "${bareDir}"`, { cwd: workDir, stdio: "ignore" });
|
|
42
|
+
execSync("git push -u origin main", { cwd: workDir, stdio: "ignore" });
|
|
43
|
+
|
|
44
|
+
return { dir: workDir, cleanup: () => rmSync(base, { recursive: true, force: true }) };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Builds a minimal IssueContext for testing.
|
|
49
|
+
*/
|
|
50
|
+
export function buildTestContext(dir: string, issueNumber = 1): IssueContext {
|
|
51
|
+
const issueDirRel = `.auto-claude/issue-${issueNumber}`;
|
|
52
|
+
return {
|
|
53
|
+
number: issueNumber,
|
|
54
|
+
title: "Test Issue",
|
|
55
|
+
body: "Test body",
|
|
56
|
+
repo: "test/repo",
|
|
57
|
+
scopePath: ".",
|
|
58
|
+
issueDir: join(dir, issueDirRel),
|
|
59
|
+
issueDirRel,
|
|
60
|
+
branch: `feature/${issueNumber}-test-issue`,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Returns a JSON string matching a successful ClaudeResult.
|
|
66
|
+
*/
|
|
67
|
+
export function successClaudeJson(result = "done"): string {
|
|
68
|
+
return JSON.stringify({
|
|
69
|
+
result,
|
|
70
|
+
is_error: false,
|
|
71
|
+
total_cost_usd: 0.01,
|
|
72
|
+
num_turns: 2,
|
|
73
|
+
} satisfies ClaudeResult);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Returns a JSON string matching a failed ClaudeResult.
|
|
78
|
+
*/
|
|
79
|
+
export function errorClaudeJson(result = "failed"): string {
|
|
80
|
+
return JSON.stringify({
|
|
81
|
+
result,
|
|
82
|
+
is_error: true,
|
|
83
|
+
total_cost_usd: 0,
|
|
84
|
+
num_turns: 0,
|
|
85
|
+
} satisfies ClaudeResult);
|
|
86
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
import consola from "consola";
|
|
6
|
+
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
7
|
+
|
|
8
|
+
import { initConfig } from "./config";
|
|
9
|
+
import { createTestRepo, createTestRepoWithRemote } from "./test-helpers";
|
|
10
|
+
import type { TestRepo } from "./test-helpers";
|
|
11
|
+
|
|
12
|
+
consola.level = -999;
|
|
13
|
+
|
|
14
|
+
// ── Shell helpers: execSafe, git ──
|
|
15
|
+
|
|
16
|
+
describe("shell helpers (real execution)", () => {
|
|
17
|
+
let originalCwd: string;
|
|
18
|
+
let repo: TestRepo;
|
|
19
|
+
|
|
20
|
+
beforeAll(async () => {
|
|
21
|
+
originalCwd = process.cwd();
|
|
22
|
+
repo = createTestRepo();
|
|
23
|
+
process.chdir(repo.dir);
|
|
24
|
+
await initConfig({ repo: "test/repo", mainBranch: "main" });
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterAll(() => {
|
|
28
|
+
process.chdir(originalCwd);
|
|
29
|
+
repo.cleanup();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("execSafe returns ok:true for a successful command", async () => {
|
|
33
|
+
const { execSafe } = await import("./utils");
|
|
34
|
+
const result = await execSafe("echo", ["hello"]);
|
|
35
|
+
expect(result.ok).toBe(true);
|
|
36
|
+
expect(result.stdout).toBe("hello");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("execSafe returns ok:false for a failing command", async () => {
|
|
40
|
+
const { execSafe } = await import("./utils");
|
|
41
|
+
const result = await execSafe("git", ["checkout", "nonexistent-branch-xyz"]);
|
|
42
|
+
expect(result.ok).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("git() runs real git commands", async () => {
|
|
46
|
+
const { git } = await import("./utils");
|
|
47
|
+
const status = await git(["status", "--porcelain"]);
|
|
48
|
+
expect(typeof status).toBe("string");
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// ── ensureBranch: real git with remote ──
|
|
53
|
+
|
|
54
|
+
describe("ensureBranch (real git)", () => {
|
|
55
|
+
let originalCwd: string;
|
|
56
|
+
let repo: TestRepo;
|
|
57
|
+
|
|
58
|
+
beforeEach(async () => {
|
|
59
|
+
originalCwd = process.cwd();
|
|
60
|
+
repo = createTestRepoWithRemote();
|
|
61
|
+
process.chdir(repo.dir);
|
|
62
|
+
await initConfig({ repo: "test/repo", mainBranch: "main", remote: "origin" });
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
afterEach(() => {
|
|
66
|
+
process.chdir(originalCwd);
|
|
67
|
+
repo.cleanup();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("creates a new branch from main when branch doesn't exist", async () => {
|
|
71
|
+
const { ensureBranch, git } = await import("./utils");
|
|
72
|
+
|
|
73
|
+
await ensureBranch("feature/42-new-branch");
|
|
74
|
+
|
|
75
|
+
const currentBranch = await git(["branch", "--show-current"]);
|
|
76
|
+
expect(currentBranch).toBe("feature/42-new-branch");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("checks out an existing local branch", async () => {
|
|
80
|
+
const { ensureBranch, git } = await import("./utils");
|
|
81
|
+
|
|
82
|
+
execSync("git checkout -b feature/existing-branch", { cwd: repo.dir, stdio: "ignore" });
|
|
83
|
+
execSync("git checkout main", { cwd: repo.dir, stdio: "ignore" });
|
|
84
|
+
|
|
85
|
+
await ensureBranch("feature/existing-branch");
|
|
86
|
+
|
|
87
|
+
const currentBranch = await git(["branch", "--show-current"]);
|
|
88
|
+
expect(currentBranch).toBe("feature/existing-branch");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("can checkout after branch creation", async () => {
|
|
92
|
+
const { ensureBranch, git } = await import("./utils");
|
|
93
|
+
|
|
94
|
+
await ensureBranch("feature/99-test-checkout");
|
|
95
|
+
const branch1 = await git(["branch", "--show-current"]);
|
|
96
|
+
expect(branch1).toBe("feature/99-test-checkout");
|
|
97
|
+
|
|
98
|
+
execSync("git checkout main", { cwd: repo.dir, stdio: "ignore" });
|
|
99
|
+
await ensureBranch("feature/99-test-checkout");
|
|
100
|
+
const branch2 = await git(["branch", "--show-current"]);
|
|
101
|
+
expect(branch2).toBe("feature/99-test-checkout");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// ── commitArtifacts: real git ──
|
|
106
|
+
|
|
107
|
+
describe("commitArtifacts (real git)", () => {
|
|
108
|
+
let originalCwd: string;
|
|
109
|
+
let repo: TestRepo;
|
|
110
|
+
|
|
111
|
+
beforeEach(async () => {
|
|
112
|
+
originalCwd = process.cwd();
|
|
113
|
+
repo = createTestRepo();
|
|
114
|
+
process.chdir(repo.dir);
|
|
115
|
+
await initConfig({ repo: "test/repo", mainBranch: "main" });
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
afterEach(() => {
|
|
119
|
+
process.chdir(originalCwd);
|
|
120
|
+
repo.cleanup();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("commits staged files in the issue directory", async () => {
|
|
124
|
+
const { commitArtifacts, buildIssueContext } = await import("./utils");
|
|
125
|
+
|
|
126
|
+
const ctx = buildIssueContext({ number: 1, title: "Test", body: "body" }, "test/repo", ".");
|
|
127
|
+
|
|
128
|
+
const issueDir = join(repo.dir, ctx.issueDirRel);
|
|
129
|
+
mkdirSync(issueDir, { recursive: true });
|
|
130
|
+
writeFileSync(join(issueDir, "test.md"), "# Test artifact");
|
|
131
|
+
|
|
132
|
+
await commitArtifacts(ctx, "test commit");
|
|
133
|
+
|
|
134
|
+
const log = execSync("git log --oneline", { cwd: repo.dir, encoding: "utf-8" });
|
|
135
|
+
expect(log).toContain("test commit");
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("does not commit when no changes are staged", async () => {
|
|
139
|
+
const { commitArtifacts, buildIssueContext } = await import("./utils");
|
|
140
|
+
|
|
141
|
+
const ctx = buildIssueContext({ number: 2, title: "Test", body: "body" }, "test/repo", ".");
|
|
142
|
+
|
|
143
|
+
const issueDir = join(repo.dir, ctx.issueDirRel);
|
|
144
|
+
mkdirSync(issueDir, { recursive: true });
|
|
145
|
+
|
|
146
|
+
const logBefore = execSync("git log --oneline", { cwd: repo.dir, encoding: "utf-8" });
|
|
147
|
+
await commitArtifacts(ctx, "should not appear");
|
|
148
|
+
const logAfter = execSync("git log --oneline", { cwd: repo.dir, encoding: "utf-8" });
|
|
149
|
+
|
|
150
|
+
expect(logAfter).toBe(logBefore);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -32,12 +32,12 @@ describe("buildIssueContext", () => {
|
|
|
32
32
|
expect(ctx.scopePath).toBe("src/");
|
|
33
33
|
expect(ctx.issueDirRel).toBe(".auto-claude/issue-42");
|
|
34
34
|
expect(ctx.issueDir).toContain(".auto-claude/issue-42");
|
|
35
|
-
expect(ctx.branch).toBe("
|
|
35
|
+
expect(ctx.branch).toBe("feature/42-fix-the-bug");
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
it("should derive branch name from issue
|
|
38
|
+
it("should derive branch name from issue title", () => {
|
|
39
39
|
const ctx = buildIssueContext({ number: 7, title: "t", body: "" }, "r", ".");
|
|
40
|
-
expect(ctx.branch).toBe("
|
|
40
|
+
expect(ctx.branch).toBe("feature/7-t");
|
|
41
41
|
});
|
|
42
42
|
});
|
|
43
43
|
|
|
@@ -69,14 +69,14 @@ describe("resolveTemplate", () => {
|
|
|
69
69
|
const issueDir = join(tmpDir, "issue-99");
|
|
70
70
|
mkdirSync(issueDir, { recursive: true });
|
|
71
71
|
|
|
72
|
-
const result = resolveTemplate("
|
|
72
|
+
const result = resolveTemplate("01_research.prompt.md", tokens, issueDir);
|
|
73
73
|
|
|
74
74
|
// Should return a relative path
|
|
75
75
|
expect(result).toContain("issue-99");
|
|
76
|
-
expect(result).toContain("
|
|
76
|
+
expect(result).toContain("01_research.prompt.md");
|
|
77
77
|
|
|
78
78
|
// Resolved file should exist and have tokens replaced
|
|
79
|
-
const content = readFileSync(join(issueDir, "
|
|
79
|
+
const content = readFileSync(join(issueDir, "01_research.prompt.md"), "utf-8");
|
|
80
80
|
expect(content).toContain("src/");
|
|
81
81
|
expect(content).toContain(".auto-claude/issue-99");
|
|
82
82
|
expect(content).not.toContain("{{SCOPE_PATH}}");
|
|
@@ -120,7 +120,7 @@ describe("buildContextFromArtifacts", () => {
|
|
|
120
120
|
expect(ctx.title).toBe("My Great Feature");
|
|
121
121
|
expect(ctx.body).toContain("This is the body of the issue.");
|
|
122
122
|
expect(ctx.repo).toBe("test/repo");
|
|
123
|
-
expect(ctx.branch).toBe("
|
|
123
|
+
expect(ctx.branch).toBe("feature/77-my-great-feature");
|
|
124
124
|
});
|
|
125
125
|
|
|
126
126
|
it("should fallback title when heading is missing", async () => {
|
|
@@ -6,6 +6,7 @@ import consola from "consola";
|
|
|
6
6
|
import pc from "picocolors";
|
|
7
7
|
import { x } from "tinyexec";
|
|
8
8
|
|
|
9
|
+
import { createBranchNameFromIssue } from "../../utils/git/branch-name.js";
|
|
9
10
|
import { getConfig } from "./config.js";
|
|
10
11
|
import { ARTIFACTS } from "./prompt-templates/index.js";
|
|
11
12
|
|
|
@@ -196,7 +197,7 @@ export function buildIssueContext(
|
|
|
196
197
|
scopePath,
|
|
197
198
|
issueDir: join(process.cwd(), issueDirRel),
|
|
198
199
|
issueDirRel,
|
|
199
|
-
branch:
|
|
200
|
+
branch: createBranchNameFromIssue({ number: issue.number, title: issue.title }),
|
|
200
201
|
};
|
|
201
202
|
}
|
|
202
203
|
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { createBranchNameFromIssue } from "./branch-name";
|
|
3
|
+
|
|
4
|
+
describe("createBranchNameFromIssue", () => {
|
|
5
|
+
it("creates branch name from issue with basic title", () => {
|
|
6
|
+
const branchName = createBranchNameFromIssue({
|
|
7
|
+
number: 4,
|
|
8
|
+
title: "Long Issue Title - with a lot of words and stuff ",
|
|
9
|
+
});
|
|
10
|
+
expect(branchName).toBe("feature/4-long-issue-title-with-a-lot-of-words-and-stuff");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("handles special characters in title", () => {
|
|
14
|
+
const branchName = createBranchNameFromIssue({
|
|
15
|
+
number: 123,
|
|
16
|
+
title: "Fix bug: @user reported $100 issue!",
|
|
17
|
+
});
|
|
18
|
+
expect(branchName).toBe("feature/123-fix-bug-user-reported-100-issue");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("handles title with only numbers", () => {
|
|
22
|
+
const branchName = createBranchNameFromIssue({ number: 42, title: "123 456" });
|
|
23
|
+
expect(branchName).toBe("feature/42-123-456");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("trims trailing dashes", () => {
|
|
27
|
+
const branchName = createBranchNameFromIssue({ number: 7, title: "Update docs ---" });
|
|
28
|
+
expect(branchName).toBe("feature/7-update-docs");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("handles unicode characters", () => {
|
|
32
|
+
const branchName = createBranchNameFromIssue({ number: 99, title: "Fix für Übersetzung" });
|
|
33
|
+
expect(branchName).toBe("feature/99-fix-f-r-bersetzung");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("handles empty-ish title", () => {
|
|
37
|
+
const branchName = createBranchNameFromIssue({ number: 1, title: " " });
|
|
38
|
+
expect(branchName).toBe("feature/1-");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("handles title with underscores", () => {
|
|
42
|
+
const branchName = createBranchNameFromIssue({ number: 50, title: "snake_case_title" });
|
|
43
|
+
expect(branchName).toBe("feature/50-snake_case_title");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("handles very long titles", () => {
|
|
47
|
+
const branchName = createBranchNameFromIssue({
|
|
48
|
+
number: 200,
|
|
49
|
+
title: "This is a very long issue title that goes on and on with many words",
|
|
50
|
+
});
|
|
51
|
+
expect(branchName).toBe(
|
|
52
|
+
"feature/200-this-is-a-very-long-issue-title-that-goes-on-and-on-with-many-words",
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("collapses multiple consecutive dashes", () => {
|
|
57
|
+
const branchName = createBranchNameFromIssue({
|
|
58
|
+
number: 15,
|
|
59
|
+
title: "Fix multiple spaces",
|
|
60
|
+
});
|
|
61
|
+
expect(branchName).toBe("feature/15-fix-multiple-spaces");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("handles title with brackets and parentheses", () => {
|
|
65
|
+
const branchName = createBranchNameFromIssue({
|
|
66
|
+
number: 33,
|
|
67
|
+
title: "[Bug] Fix (critical) issue",
|
|
68
|
+
});
|
|
69
|
+
expect(branchName).toBe("feature/33--bug-fix-critical-issue");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("produces same result whether called with minimal or extra fields", () => {
|
|
73
|
+
const minimal = createBranchNameFromIssue({ number: 90, title: "Add e2e tests" });
|
|
74
|
+
const withExtras = createBranchNameFromIssue({
|
|
75
|
+
number: 90,
|
|
76
|
+
title: "Add e2e tests",
|
|
77
|
+
state: "open",
|
|
78
|
+
labels: [],
|
|
79
|
+
} as { number: number; title: string });
|
|
80
|
+
expect(minimal).toBe(withExtras);
|
|
81
|
+
expect(minimal).toBe("feature/90-add-e2e-tests");
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function createBranchNameFromIssue(issue: { number: number; title: string }): string {
|
|
2
|
+
let slug = issue.title.toLowerCase();
|
|
3
|
+
slug = slug.trim();
|
|
4
|
+
slug = slug.replaceAll(" ", "-");
|
|
5
|
+
slug = slug.replace(/[^0-9a-zA-Z_-]/g, "-");
|
|
6
|
+
slug = slug.replace(/-+/g, "-");
|
|
7
|
+
slug = slug.replace(/-+$/, "");
|
|
8
|
+
|
|
9
|
+
return `feature/${issue.number}-${slug}`;
|
|
10
|
+
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
You are a senior developer researching a codebase to prepare for implementing a GitHub issue.
|
|
2
|
-
|
|
3
|
-
## Your task
|
|
4
|
-
|
|
5
|
-
Read the issue description in @{{ISSUE_DIR}}/initial-ramblings.md and then **research the codebase in depth** to understand what would be involved in implementing it.
|
|
6
|
-
|
|
7
|
-
**CRITICAL RULES:**
|
|
8
|
-
|
|
9
|
-
- Do **NOT** implement the issue. Do not create, modify, or delete any project source files.
|
|
10
|
-
- Your **ONLY** deliverable is writing the file @{{ISSUE_DIR}}/research.md.
|
|
11
|
-
- If the issue seems trivial, research it anyway — document the relevant files, patterns, and context.
|
|
12
|
-
|
|
13
|
-
## Where to look
|
|
14
|
-
|
|
15
|
-
The code for this project lives primarily at `{{SCOPE_PATH}}/`. Start your investigation there but explore any related files across the monorepo.
|
|
16
|
-
|
|
17
|
-
Read every relevant file in full. Understand how the system works deeply — its architecture, data flow, and all its specificities. Do not skim. Do not stop researching until you have a thorough understanding of every part of the codebase that this issue touches.
|
|
18
|
-
|
|
19
|
-
## What to write in @{{ISSUE_DIR}}/research.md
|
|
20
|
-
|
|
21
|
-
1. **Relevant files** — every file that would need to be read or modified, with brief descriptions of what each does
|
|
22
|
-
2. **Existing patterns** — how similar features are currently implemented in this codebase (naming conventions, folder structure, component patterns, API patterns)
|
|
23
|
-
3. **Dependencies** — libraries, utilities, shared code, and services that are relevant
|
|
24
|
-
4. **Potential impact areas** — what else might break or need updating (tests, types, imports, configs)
|
|
25
|
-
5. **Edge cases and constraints** — anything tricky that the implementation should watch out for
|
|
26
|
-
6. **Reference implementations** — if there's a similar feature already built, document it as a reference
|
|
27
|
-
|
|
28
|
-
Be thorough. Keep researching until you have complete understanding — missing information here means a worse plan later.
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
You are a senior developer planning the implementation for a GitHub issue.
|
|
2
|
-
|
|
3
|
-
Read the issue in @{{ISSUE_DIR}}/initial-ramblings.md and the research in @{{ISSUE_DIR}}/research.md.
|
|
4
|
-
|
|
5
|
-
**CRITICAL RULES:**
|
|
6
|
-
|
|
7
|
-
- Do **NOT** implement the issue. Do not create, modify, or delete any project source files.
|
|
8
|
-
- Your **ONLY** deliverable is writing the file @{{ISSUE_DIR}}/plan.md.
|
|
9
|
-
- Read the actual source files before suggesting changes. Base the plan on what the code actually does, not assumptions.
|
|
10
|
-
|
|
11
|
-
The code for this project lives primarily at `{{SCOPE_PATH}}/`.
|
|
12
|
-
|
|
13
|
-
Write @{{ISSUE_DIR}}/plan.md containing:
|
|
14
|
-
|
|
15
|
-
1. **Summary** — what we're building and why (1-2 paragraphs)
|
|
16
|
-
2. **Approach** — the high-level technical approach chosen
|
|
17
|
-
3. **Architectural decisions** — any significant choices made and why (e.g., which component pattern, state management approach, API structure)
|
|
18
|
-
4. **Key code snippets** — include concrete code examples showing the important parts of the implementation (function signatures, component structure, schema changes, etc.)
|
|
19
|
-
5. **Scope boundaries** — what is explicitly out of scope to keep the change focused
|
|
20
|
-
6. **Risks** — anything that could go wrong or needs special attention during implementation
|
|
21
|
-
7. **Alternative approaches** — a brief section listing other valid ways to solve this problem. For each alternative, include: the approach name, a one-liner on how it works, and why the chosen approach was preferred. Consider industry best practices, common patterns, and obvious alternatives. This section is for PR reviewers only — it will NOT be used in the implementation plan.
|
|
22
|
-
|
|
23
|
-
**Design principles to follow:**
|
|
24
|
-
|
|
25
|
-
- Fixing a known issue later instead of now is not simplicity — if the plan touches an area with a known bug, address it.
|
|
26
|
-
- Adding a second primitive for something we already have a primitive for is not simplicity — reuse existing abstractions.
|
|
27
|
-
|
|
28
|
-
Keep it concise and focused on decisions, not on repeating the research.
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
You are a senior developer revising a plan based on reviewer feedback.
|
|
2
|
-
|
|
3
|
-
Read the current plan in @{{ISSUE_DIR}}/plan.md and the reviewer's annotations in @{{ISSUE_DIR}}/plan-annotations.md.
|
|
4
|
-
|
|
5
|
-
The original issue is described in @{{ISSUE_DIR}}/initial-ramblings.md and the research is in @{{ISSUE_DIR}}/research.md.
|
|
6
|
-
|
|
7
|
-
**CRITICAL RULES:**
|
|
8
|
-
|
|
9
|
-
- Do **NOT** implement the issue. Do not create, modify, or delete any project source files.
|
|
10
|
-
- Your **ONLY** deliverable is updating @{{ISSUE_DIR}}/plan.md to address the annotations.
|
|
11
|
-
|
|
12
|
-
The code for this project lives primarily at `{{SCOPE_PATH}}/`.
|
|
13
|
-
|
|
14
|
-
## Instructions
|
|
15
|
-
|
|
16
|
-
- Read every annotation carefully.
|
|
17
|
-
- Address all the notes — update the plan to incorporate the feedback.
|
|
18
|
-
- If an annotation asks a question, answer it in the plan.
|
|
19
|
-
- If an annotation suggests a different approach, evaluate it and update the plan accordingly.
|
|
20
|
-
- If an annotation points out something missing, add it.
|
|
21
|
-
- Keep the same structure and formatting of the plan, just improve its content.
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
You are breaking down a plan into a detailed, ordered implementation checklist.
|
|
2
|
-
|
|
3
|
-
Read the issue in @{{ISSUE_DIR}}/initial-ramblings.md, the research in @{{ISSUE_DIR}}/research.md, and the plan in @{{ISSUE_DIR}}/plan.md.
|
|
4
|
-
|
|
5
|
-
**CRITICAL RULES:**
|
|
6
|
-
|
|
7
|
-
- Do **NOT** implement the issue. Do not create, modify, or delete any project source files.
|
|
8
|
-
- Your **ONLY** deliverable is writing the file @{{ISSUE_DIR}}/plan-implementation.md.
|
|
9
|
-
|
|
10
|
-
The code for this project lives primarily at `{{SCOPE_PATH}}/`.
|
|
11
|
-
|
|
12
|
-
Write @{{ISSUE_DIR}}/plan-implementation.md with:
|
|
13
|
-
|
|
14
|
-
- An **ordered task list using markdown checkboxes** (`- [ ]` for each task)
|
|
15
|
-
- Each task should be small enough to implement in one focused session
|
|
16
|
-
- For each task:
|
|
17
|
-
- **Files to modify** — specific file paths
|
|
18
|
-
- **What to change** — concrete description of the changes (not vague like "update the component")
|
|
19
|
-
- **Acceptance criteria** — how to verify this task is done correctly
|
|
20
|
-
- Tasks should be ordered so each builds on the previous (no forward dependencies)
|
|
21
|
-
- Include any necessary setup tasks (new files to create, dependencies to add, etc.)
|
|
22
|
-
- The final task should always be "verify everything works together"
|
|
23
|
-
|
|
24
|
-
IMPORTANT: Every task MUST use the `- [ ]` checkbox format. Example:
|
|
25
|
-
|
|
26
|
-
```
|
|
27
|
-
- [ ] **Task 1: Create the API endpoint**
|
|
28
|
-
- Files: `src/server/routes/foo.ts`
|
|
29
|
-
- Changes: Add GET /api/foo endpoint that returns...
|
|
30
|
-
- Acceptance: Endpoint responds with 200 and correct shape
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Be specific enough that a developer could follow this checklist without needing to re-read the research or plan.
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
You are an implementation agent. Your job is to follow the checklist in @{{ISSUE_DIR}}/plan-implementation.md task by task.
|
|
2
|
-
|
|
3
|
-
The original issue is described in @{{ISSUE_DIR}}/initial-ramblings.md — this is background context only. Do NOT decide on your own whether the issue is "done". Your ONLY source of truth is the checklist.
|
|
4
|
-
|
|
5
|
-
The code for this project lives primarily at `{{SCOPE_PATH}}/`.
|
|
6
|
-
|
|
7
|
-
## How to work
|
|
8
|
-
|
|
9
|
-
1. Read @{{ISSUE_DIR}}/plan-implementation.md — Find the highest priority (`- [ ]`) task to work. This should be the one YOU decide has the highest priority. Not necessarily the first one
|
|
10
|
-
2. Execute that task (edit files, run commands, whatever the task says)
|
|
11
|
-
3. After completing it, update @{{ISSUE_DIR}}/plan-implementation.md to change `- [ ]` to `- [x]` for that task
|
|
12
|
-
4. Make a git commit ex: `feat(scope): description` or `fix(scope): description`
|
|
13
|
-
5. Move to the next unchecked task. Repeat until all tasks are done or you run out of turns.
|
|
14
|
-
|
|
15
|
-
Do NOT push to remote — the pipeline handles that.
|
|
16
|
-
Do NOT stop until all tasks and phases are completed.
|
|
17
|
-
|
|
18
|
-
## When ALL checkboxes are `- [x]`
|
|
19
|
-
|
|
20
|
-
Write @{{ISSUE_DIR}}/completed-summary.md with a brief summary of everything that was implemented. This file signals completion — do NOT create it if ANY tasks remain unchecked.
|
|
21
|
-
|
|
22
|
-
## Code quality rules
|
|
23
|
-
|
|
24
|
-
- Do not add unnecessary comments or jsdocs to code you write.
|
|
25
|
-
- Do not use `any` or `unknown` types — use proper typing.
|
|
26
|
-
- Follow existing codebase patterns and conventions (check nearby files for reference).
|
|
27
|
-
- Do NOT skip tasks just because the end result already looks correct.
|
|
28
|
-
- Follow the checklist literally — if a task says "commit", make a commit. If it says "verify", run the verification.
|
|
29
|
-
- If you encounter something unexpected, use your best judgment and proceed.
|
|
30
|
-
- Fixing a known issue later instead of now is not simplicity — if you see a bug or problem in the area you're working on, fix it.
|
|
31
|
-
- Adding a second primitive for something we already have a primitive for is not simplicity — reuse existing abstractions instead of creating parallel ones (exceptions might exist, but don't use it as an easy way out).
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
You are reviewing code changes for the issue described in @{{ISSUE_DIR}}/initial-ramblings.md. The implementation plan in @{{ISSUE_DIR}}/plan-implementation.md describes what should have been done.
|
|
2
|
-
|
|
3
|
-
Review the changes by running `git diff {{MAIN_BRANCH}}...HEAD` and check for:
|
|
4
|
-
|
|
5
|
-
1. **Correctness** — do the changes actually implement what the plan describes?
|
|
6
|
-
2. **Missing imports** — are all imports present and correct?
|
|
7
|
-
3. **Type errors** — any obvious TypeScript issues?
|
|
8
|
-
4. **Unused code** — variables, imports, or functions that were added but never used?
|
|
9
|
-
5. **Pattern consistency** — do the changes follow the existing codebase patterns?
|
|
10
|
-
6. **Security issues** — any command injection, XSS, SQL injection, or other vulnerabilities?
|
|
11
|
-
7. **Edge cases** — anything that could break under unusual input or conditions?
|
|
12
|
-
8. **Incomplete work** — TODO comments, placeholder values, or unfinished implementations?
|
|
13
|
-
|
|
14
|
-
If you find issues:
|
|
15
|
-
|
|
16
|
-
- Fix them directly (edit the files)
|
|
17
|
-
- Make a separate commit for the fixes: `fix(scope): review fixes for issue #N`
|
|
18
|
-
|
|
19
|
-
Write your review summary to @{{ISSUE_DIR}}/review.md with:
|
|
20
|
-
|
|
21
|
-
- **Status**: PASS or PASS WITH FIXES
|
|
22
|
-
- **Issues found** (if any) — what was wrong and what you fixed
|
|
23
|
-
- **Confidence level** — how confident you are the implementation is correct (high/medium/low)
|
|
24
|
-
- **Notes** — anything the PR reviewer should pay attention to
|
|
25
|
-
- **Recommended follow-ups** — only include this section if you discovered genuinely valuable insights during the review. These should be high-signal recommendations, not minor noise. Examples of what qualifies:
|
|
26
|
-
- An existing primitive/utility that almost does what's needed but requires small changes to be reusable across the codebase
|
|
27
|
-
- A pattern inconsistency across the codebase that this change exposed
|
|
28
|
-
- A related improvement that would significantly benefit from the deep context gained during this issue
|
|
29
|
-
- Technical debt that directly affects the area touched by this change
|
|
30
|
-
For each follow-up, write it as a potential issue title + 1-2 sentence description of what and why. Omit this section entirely if there's nothing worth flagging.
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
You are updating an existing auto-claude branch to be compatible with the latest {{MAIN_BRANCH}}.
|
|
2
|
-
|
|
3
|
-
## Context
|
|
4
|
-
|
|
5
|
-
Read these files to understand what this PR is doing:
|
|
6
|
-
|
|
7
|
-
- Original issue: @{{ISSUE_DIR}}/initial-ramblings.md
|
|
8
|
-
- Implementation plan: @{{ISSUE_DIR}}/plan-implementation.md
|
|
9
|
-
- What was implemented: @{{ISSUE_DIR}}/completed-summary.md
|
|
10
|
-
|
|
11
|
-
## Your task
|
|
12
|
-
|
|
13
|
-
1. Run `git diff {{MAIN_BRANCH}}...HEAD` to see what this PR currently changes vs {{MAIN_BRANCH}}
|
|
14
|
-
2. Run `git log {{MAIN_BRANCH}}..HEAD --oneline` to see the PR's commit history
|
|
15
|
-
3. Run `git diff HEAD...{{MAIN_BRANCH}}` to see what {{MAIN_BRANCH}} has changed since this branch diverged
|
|
16
|
-
4. Analyze whether the PR's changes are still valid:
|
|
17
|
-
- Do imports still resolve? Have moved/renamed files been accounted for?
|
|
18
|
-
- Do APIs/types used by the PR still exist and have the same signatures?
|
|
19
|
-
- Are there conflicts with new code on {{MAIN_BRANCH}} that touches the same areas?
|
|
20
|
-
5. If you find issues:
|
|
21
|
-
- Fix the code directly to work with current {{MAIN_BRANCH}}
|
|
22
|
-
- Run type-check (`pnpm type-check`) if applicable
|
|
23
|
-
- Commit fixes: `fix(scope): adapt to {{MAIN_BRANCH}} changes`
|
|
24
|
-
6. If everything looks fine, note that no changes were needed.
|
|
25
|
-
|
|
26
|
-
The code for this project lives primarily at `{{SCOPE_PATH}}/`.
|
|
27
|
-
|
|
28
|
-
**CRITICAL RULES:**
|
|
29
|
-
|
|
30
|
-
- Do NOT re-implement the feature. Only fix what's broken due to {{MAIN_BRANCH}} changes.
|
|
31
|
-
- If {{MAIN_BRANCH}} has changed so fundamentally that the PR's approach is no longer viable (e.g., the module was refactored, APIs were replaced entirely), do NOT attempt to force a fix. Report `NEEDS-ATTENTION` with a clear explanation of what changed and why the current implementation can't be salvaged with minor fixes.
|
|
32
|
-
- Do NOT push to remote — the pipeline handles that.
|
|
33
|
-
- Do NOT modify the plan or research files.
|
|
34
|
-
|
|
35
|
-
Write a summary of what you did to @{{ISSUE_DIR}}/refresh-summary.md with:
|
|
36
|
-
|
|
37
|
-
- **Status**: UP-TO-DATE, ADAPTED, or NEEDS-ATTENTION
|
|
38
|
-
- **Changes made** (if any) — what broke and how you fixed it
|
|
39
|
-
- **Risk areas** — anything a reviewer should double-check
|