@towles/tool 0.0.62 → 0.0.64
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 +50 -57
- package/src/commands/agentboard.ts +176 -0
- package/src/commands/{auto-claude.ts → auto-claude/index.ts} +18 -28
- package/src/commands/auto-claude/list.ts +114 -0
- package/src/commands/auto-claude/retry.test.ts +138 -0
- package/src/commands/auto-claude/retry.ts +139 -0
- package/src/commands/auto-claude/status.test.ts +147 -0
- package/src/commands/auto-claude/status.ts +123 -0
- package/src/commands/base.ts +7 -2
- package/src/commands/config.ts +5 -7
- package/src/commands/doctor.ts +111 -12
- package/src/commands/gh/branch.ts +4 -4
- package/src/commands/gh/pr.ts +1 -0
- package/src/commands/graph/index.ts +169 -0
- package/src/commands/graph.test.ts +1 -1
- package/src/commands/install.ts +40 -68
- package/src/commands/journal/daily-notes.ts +3 -3
- package/src/commands/journal/meeting.ts +3 -3
- package/src/commands/journal/note.ts +3 -3
- package/src/lib/auto-claude/claude-cli.ts +183 -0
- package/src/lib/auto-claude/config.test.ts +6 -8
- package/src/lib/auto-claude/config.ts +3 -4
- package/src/lib/auto-claude/index.ts +2 -3
- package/src/lib/auto-claude/labels.test.ts +85 -0
- package/src/lib/auto-claude/labels.ts +42 -0
- package/src/lib/auto-claude/pipeline-execution.test.ts +129 -33
- package/src/lib/auto-claude/pipeline.test.ts +2 -2
- package/src/lib/auto-claude/pipeline.ts +120 -36
- package/src/lib/auto-claude/prompt-templates/01_plan.prompt.md +68 -0
- package/src/lib/auto-claude/prompt-templates/{05_implement.prompt.md → 02_implement.prompt.md} +3 -2
- package/src/lib/auto-claude/prompt-templates/03_simplify.prompt.md +52 -0
- package/src/lib/auto-claude/prompt-templates/{06_review.prompt.md → 04_review.prompt.md} +29 -6
- package/src/lib/auto-claude/prompt-templates/index.test.ts +9 -42
- package/src/lib/auto-claude/prompt-templates/index.ts +13 -28
- package/src/lib/auto-claude/run-claude.test.ts +48 -68
- package/src/lib/auto-claude/shell.ts +6 -0
- package/src/lib/auto-claude/steps/create-pr.ts +89 -25
- package/src/lib/auto-claude/steps/fetch-issues.ts +4 -1
- package/src/lib/auto-claude/steps/implement.ts +9 -16
- package/src/lib/auto-claude/steps/simple-steps.ts +34 -0
- package/src/lib/auto-claude/steps/steps.test.ts +68 -63
- package/src/lib/auto-claude/templates.test.ts +91 -0
- package/src/lib/auto-claude/templates.ts +34 -0
- package/src/lib/auto-claude/test-helpers.ts +2 -1
- package/src/lib/auto-claude/utils-execution.test.ts +9 -57
- package/src/lib/auto-claude/utils.test.ts +5 -9
- package/src/lib/auto-claude/utils.ts +27 -253
- package/src/lib/graph/analyzer.test.ts +451 -0
- package/src/lib/graph/analyzer.ts +165 -0
- package/src/lib/graph/index.ts +24 -0
- package/src/lib/graph/labels.ts +87 -0
- package/src/lib/graph/parser.test.ts +150 -0
- package/src/lib/graph/parser.ts +65 -0
- package/src/lib/graph/render.ts +25 -0
- package/src/lib/graph/server.ts +70 -0
- package/src/lib/graph/sessions.ts +104 -0
- package/src/lib/graph/tools.ts +90 -0
- package/src/lib/graph/treemap.ts +211 -0
- package/src/lib/graph/types.ts +80 -0
- package/src/lib/install/claude-settings.ts +64 -0
- package/src/lib/journal/editor.ts +33 -0
- package/src/lib/journal/fs.ts +13 -0
- package/src/lib/journal/index.ts +11 -0
- package/src/lib/journal/paths.ts +106 -0
- package/src/lib/journal/{utils.ts → templates.ts} +3 -151
- package/src/utils/fs.ts +19 -0
- package/src/utils/git/exec.ts +18 -0
- package/src/utils/git/gh-cli-wrapper.test.ts +47 -8
- package/src/utils/git/gh-cli-wrapper.ts +31 -19
- package/src/utils/render.ts +3 -1
- package/src/commands/graph.ts +0 -970
- package/src/lib/auto-claude/prompt-templates/01_research.prompt.md +0 -21
- package/src/lib/auto-claude/prompt-templates/02_plan.prompt.md +0 -27
- package/src/lib/auto-claude/prompt-templates/03_plan-annotations.prompt.md +0 -15
- package/src/lib/auto-claude/prompt-templates/04_plan-implementation.prompt.md +0 -35
- package/src/lib/auto-claude/prompt-templates/07_refresh.prompt.md +0 -30
- package/src/lib/auto-claude/steps/plan-annotations.ts +0 -54
- package/src/lib/auto-claude/steps/plan-implementation.ts +0 -14
- package/src/lib/auto-claude/steps/plan.ts +0 -14
- package/src/lib/auto-claude/steps/refresh.ts +0 -114
- package/src/lib/auto-claude/steps/remove-label.ts +0 -22
- package/src/lib/auto-claude/steps/research.ts +0 -21
- package/src/lib/auto-claude/steps/review.ts +0 -14
package/src/utils/fs.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
|
|
4
|
+
export function ensureDir(dir: string): void {
|
|
5
|
+
mkdirSync(dir, { recursive: true });
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function fileExists(path: string): boolean {
|
|
9
|
+
return existsSync(path);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function readFile(path: string): string {
|
|
13
|
+
return readFileSync(path, "utf-8");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function writeFile(path: string, content: string): void {
|
|
17
|
+
ensureDir(dirname(path));
|
|
18
|
+
writeFileSync(path, content, "utf-8");
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { x } from "tinyexec";
|
|
2
|
+
|
|
3
|
+
async function exec(cmd: string, args: string[]): Promise<string> {
|
|
4
|
+
const result = await x(cmd, args, { nodeOptions: { cwd: process.cwd() }, throwOnError: true });
|
|
5
|
+
return result.stdout.trim();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function execSafe(
|
|
9
|
+
cmd: string,
|
|
10
|
+
args: string[],
|
|
11
|
+
): Promise<{ stdout: string; ok: boolean }> {
|
|
12
|
+
const result = await x(cmd, args, { nodeOptions: { cwd: process.cwd() }, throwOnError: false });
|
|
13
|
+
return { stdout: (result.stdout ?? "").trim(), ok: result.exitCode === 0 };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function git(args: string[]): Promise<string> {
|
|
17
|
+
return exec("git", args);
|
|
18
|
+
}
|
|
@@ -1,14 +1,53 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { x } from "tinyexec";
|
|
2
3
|
import { getIssues, isGithubCliInstalled } from "./gh-cli-wrapper";
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
vi.mock("tinyexec", () => ({
|
|
6
|
+
x: vi.fn().mockResolvedValue({ stdout: "[]" }),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
const mockX = vi.mocked(x);
|
|
10
|
+
|
|
11
|
+
describe("gh-cli-wrapper", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
vi.clearAllMocks();
|
|
14
|
+
mockX.mockResolvedValue({ stdout: "[]" } as never);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe("getIssues", () => {
|
|
18
|
+
it("passes --label flag when label provided", async () => {
|
|
19
|
+
await getIssues({ cwd: ".", label: "auto-claude" });
|
|
20
|
+
|
|
21
|
+
expect(mockX).toHaveBeenCalledWith("gh", expect.arrayContaining(["--label", "auto-claude"]));
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("does not pass --label flag when label not provided", async () => {
|
|
25
|
+
await getIssues({ cwd: "." });
|
|
26
|
+
|
|
27
|
+
const args = mockX.mock.calls[0]![1] as string[];
|
|
28
|
+
expect(args).not.toContain("--label");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("passes --assignee @me flag when assignedToMe is true", async () => {
|
|
32
|
+
await getIssues({ cwd: ".", assignedToMe: true });
|
|
33
|
+
|
|
34
|
+
expect(mockX).toHaveBeenCalledWith("gh", expect.arrayContaining(["--assignee", "@me"]));
|
|
35
|
+
});
|
|
8
36
|
});
|
|
9
37
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
38
|
+
describe("isGithubCliInstalled", () => {
|
|
39
|
+
it("returns true when gh CLI outputs expected string", async () => {
|
|
40
|
+
mockX.mockResolvedValue({ stdout: "gh version 2.0.0 (https://github.com/cli/cli)" } as never);
|
|
41
|
+
|
|
42
|
+
const result = await isGithubCliInstalled();
|
|
43
|
+
expect(result).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("returns false when gh CLI is not available", async () => {
|
|
47
|
+
mockX.mockRejectedValue(new Error("command not found"));
|
|
48
|
+
|
|
49
|
+
const result = await isGithubCliInstalled();
|
|
50
|
+
expect(result).toBe(false);
|
|
51
|
+
});
|
|
13
52
|
});
|
|
14
53
|
});
|
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
import stripAnsi from "strip-ansi";
|
|
2
2
|
import { x } from "tinyexec";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
import { execSafe } from "./exec.js";
|
|
5
|
+
|
|
6
|
+
export async function isGithubCliInstalled(): Promise<boolean> {
|
|
5
7
|
try {
|
|
6
|
-
const proc = await x(
|
|
7
|
-
return proc.stdout.
|
|
8
|
-
} catch
|
|
8
|
+
const proc = await x("gh", ["--version"]);
|
|
9
|
+
return proc.stdout.includes("https://github.com/cli/cli");
|
|
10
|
+
} catch {
|
|
11
|
+
// gh CLI not installed or not accessible
|
|
9
12
|
return false;
|
|
10
13
|
}
|
|
11
|
-
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function gh<T = unknown>(args: string[]): Promise<T> {
|
|
17
|
+
const result = await x("gh", args, { nodeOptions: { cwd: process.cwd() }, throwOnError: true });
|
|
18
|
+
return JSON.parse(result.stdout.trim()) as T;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function ghRaw(args: string[]): Promise<string> {
|
|
22
|
+
const result = await execSafe("gh", args);
|
|
23
|
+
return result.stdout;
|
|
24
|
+
}
|
|
12
25
|
|
|
13
26
|
export interface Issue {
|
|
14
27
|
labels: {
|
|
@@ -20,35 +33,34 @@ export interface Issue {
|
|
|
20
33
|
state: string;
|
|
21
34
|
}
|
|
22
35
|
|
|
23
|
-
export
|
|
36
|
+
export async function getIssues({
|
|
24
37
|
assignedToMe,
|
|
25
38
|
cwd,
|
|
39
|
+
label,
|
|
26
40
|
}: {
|
|
27
|
-
assignedToMe
|
|
41
|
+
assignedToMe?: boolean;
|
|
28
42
|
cwd: string;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const flags = ["issue", "list", "--json", "labels,number,title,state"];
|
|
43
|
+
label?: string;
|
|
44
|
+
}): Promise<Issue[]> {
|
|
45
|
+
const args = ["issue", "list", "--json", "labels,number,title,state"];
|
|
33
46
|
|
|
34
47
|
if (assignedToMe) {
|
|
35
|
-
|
|
36
|
-
flags.push("@me");
|
|
48
|
+
args.push("--assignee", "@me");
|
|
37
49
|
}
|
|
38
50
|
|
|
39
|
-
|
|
51
|
+
if (label) {
|
|
52
|
+
args.push("--label", label);
|
|
53
|
+
}
|
|
40
54
|
|
|
41
|
-
const result = await x(
|
|
55
|
+
const result = await x("gh", args);
|
|
42
56
|
// Setting NO_COLOR=1 didn't remove colors so had to use stripAnsi
|
|
43
57
|
const stripped = stripAnsi(result.stdout);
|
|
44
58
|
|
|
45
59
|
try {
|
|
46
|
-
|
|
60
|
+
return JSON.parse(stripped) as Issue[];
|
|
47
61
|
} catch {
|
|
48
62
|
throw new Error(
|
|
49
63
|
`Failed to parse GitHub CLI output as JSON. Raw output: ${stripped.slice(0, 200)}${stripped.length > 200 ? "..." : ""}`,
|
|
50
64
|
);
|
|
51
65
|
}
|
|
52
|
-
|
|
53
|
-
return issues;
|
|
54
|
-
};
|
|
66
|
+
}
|
package/src/utils/render.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { colors } from "consola/utils";
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export function getTerminalColumns(): number {
|
|
4
|
+
return process.stdout?.columns || 80;
|
|
5
|
+
}
|
|
4
6
|
|
|
5
7
|
export const limitText = (text: string, maxWidth: number): string => {
|
|
6
8
|
if (text.length <= maxWidth) return text;
|