@towles/tool 0.0.106 → 0.0.108
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/README.md +7 -1
- package/package.json +2 -1
- package/plugins/tt-agentboard/README.md +160 -0
- package/plugins/tt-agentboard/apps/server/package.json +20 -0
- package/plugins/tt-agentboard/apps/server/src/main.ts +60 -0
- package/plugins/tt-agentboard/apps/tui/build.ts +11 -0
- package/plugins/tt-agentboard/apps/tui/bunfig.toml +1 -0
- package/plugins/tt-agentboard/apps/tui/package.json +23 -0
- package/plugins/tt-agentboard/apps/tui/scripts/sessionizer.sh +36 -0
- package/plugins/tt-agentboard/apps/tui/src/components/DetailPanel.tsx +350 -0
- package/plugins/tt-agentboard/apps/tui/src/components/DiffStats.tsx +33 -0
- package/plugins/tt-agentboard/apps/tui/src/components/SessionCard.tsx +177 -0
- package/plugins/tt-agentboard/apps/tui/src/components/StatusBar.tsx +49 -0
- package/plugins/tt-agentboard/apps/tui/src/constants.ts +46 -0
- package/plugins/tt-agentboard/apps/tui/src/detail-panel-height.ts +21 -0
- package/plugins/tt-agentboard/apps/tui/src/index.tsx +880 -0
- package/plugins/tt-agentboard/apps/tui/src/mux-context.ts +61 -0
- package/plugins/tt-agentboard/apps/tui/tsconfig.json +15 -0
- package/plugins/tt-agentboard/bun.lock +444 -0
- package/plugins/tt-agentboard/package.json +26 -0
- package/plugins/tt-agentboard/packages/mux-tmux/package.json +14 -0
- package/plugins/tt-agentboard/packages/mux-tmux/src/client.ts +550 -0
- package/plugins/tt-agentboard/packages/mux-tmux/src/index.ts +18 -0
- package/plugins/tt-agentboard/packages/mux-tmux/src/provider.ts +259 -0
- package/plugins/tt-agentboard/packages/mux-tmux/tsconfig.json +13 -0
- package/plugins/tt-agentboard/packages/runtime/package.json +14 -0
- package/plugins/tt-agentboard/packages/runtime/src/agents/tracker.ts +233 -0
- package/plugins/tt-agentboard/packages/runtime/src/agents/watchers/amp.ts +316 -0
- package/plugins/tt-agentboard/packages/runtime/src/agents/watchers/claude-code.ts +374 -0
- package/plugins/tt-agentboard/packages/runtime/src/agents/watchers/codex.ts +364 -0
- package/plugins/tt-agentboard/packages/runtime/src/agents/watchers/opencode.ts +249 -0
- package/plugins/tt-agentboard/packages/runtime/src/config.ts +70 -0
- package/plugins/tt-agentboard/packages/runtime/src/contracts/agent-watcher.ts +38 -0
- package/plugins/tt-agentboard/packages/runtime/src/contracts/agent.ts +16 -0
- package/plugins/tt-agentboard/packages/runtime/src/contracts/index.ts +3 -0
- package/plugins/tt-agentboard/packages/runtime/src/contracts/mux.ts +148 -0
- package/plugins/tt-agentboard/packages/runtime/src/debug.ts +19 -0
- package/plugins/tt-agentboard/packages/runtime/src/index.ts +69 -0
- package/plugins/tt-agentboard/packages/runtime/src/mux/detect.ts +20 -0
- package/plugins/tt-agentboard/packages/runtime/src/mux/registry.ts +45 -0
- package/plugins/tt-agentboard/packages/runtime/src/plugins/loader.ts +152 -0
- package/plugins/tt-agentboard/packages/runtime/src/server/context.ts +112 -0
- package/plugins/tt-agentboard/packages/runtime/src/server/git-info.ts +164 -0
- package/plugins/tt-agentboard/packages/runtime/src/server/index.ts +1753 -0
- package/plugins/tt-agentboard/packages/runtime/src/server/launcher.ts +71 -0
- package/plugins/tt-agentboard/packages/runtime/src/server/metadata-store.ts +86 -0
- package/plugins/tt-agentboard/packages/runtime/src/server/pane-scanner.ts +327 -0
- package/plugins/tt-agentboard/packages/runtime/src/server/port-scanner.ts +155 -0
- package/plugins/tt-agentboard/packages/runtime/src/server/session-order.ts +127 -0
- package/plugins/tt-agentboard/packages/runtime/src/server/sidebar-manager.ts +232 -0
- package/plugins/tt-agentboard/packages/runtime/src/server/sidebar-width-sync.ts +66 -0
- package/plugins/tt-agentboard/packages/runtime/src/shared.ts +179 -0
- package/plugins/tt-agentboard/packages/runtime/src/themes.ts +750 -0
- package/plugins/tt-agentboard/packages/runtime/test/config.test.ts +83 -0
- package/plugins/tt-agentboard/packages/runtime/test/tracker.test.ts +172 -0
- package/plugins/tt-agentboard/packages/runtime/tsconfig.json +13 -0
- package/plugins/tt-agentboard/tsconfig.json +19 -0
- package/plugins/tt-auto-claude/.claude-plugin/plugin.json +8 -0
- package/plugins/tt-auto-claude/commands/create-issue.md +20 -0
- package/plugins/tt-auto-claude/commands/list.md +21 -0
- package/plugins/tt-auto-claude/skills/auto-claude/SKILL.md +71 -0
- package/plugins/tt-core/.claude-plugin/plugin.json +8 -0
- package/plugins/tt-core/README.md +18 -0
- package/plugins/tt-core/commands/improve-architecture.md +66 -0
- package/plugins/tt-core/commands/interview-me.md +38 -0
- package/plugins/tt-core/commands/prd-to-issues.md +49 -0
- package/plugins/tt-core/commands/refine-text.md +30 -0
- package/plugins/tt-core/commands/task.md +37 -0
- package/plugins/tt-core/commands/tdd.md +69 -0
- package/plugins/tt-core/commands/write-prd.md +69 -0
- package/plugins/tt-core/promptfooconfig.interview-me.yaml +155 -0
- package/plugins/tt-core/promptfooconfig.refine-text.yaml +242 -0
- package/plugins/tt-core/promptfooconfig.tdd.yaml +144 -0
- package/plugins/tt-core/promptfooconfig.write-prd.yaml +145 -0
- package/plugins/tt-core/skills/towles-tool/SKILL.md +35 -0
- package/src/commands/agentboard.ts +19 -2
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
2
|
+
import { mkdirSync, rmSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { loadConfig, saveConfig } from "../src/config";
|
|
5
|
+
|
|
6
|
+
const TEST_HOME = join(import.meta.dir, ".test-home");
|
|
7
|
+
const CONFIG_DIR = join(TEST_HOME, ".config", "towles-tool", "agentboard");
|
|
8
|
+
const CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
9
|
+
|
|
10
|
+
describe("config", () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
rmSync(TEST_HOME, { recursive: true, force: true });
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
rmSync(TEST_HOME, { recursive: true, force: true });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe("loadConfig", () => {
|
|
20
|
+
it("returns defaults when no config file exists", () => {
|
|
21
|
+
const config = loadConfig(TEST_HOME);
|
|
22
|
+
expect(config.plugins).toEqual([]);
|
|
23
|
+
expect(config.port).toBeUndefined();
|
|
24
|
+
expect(config.theme).toBeUndefined();
|
|
25
|
+
expect(config.sidebarWidth).toBeUndefined();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("reads config from disk", () => {
|
|
29
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
30
|
+
writeFileSync(
|
|
31
|
+
CONFIG_FILE,
|
|
32
|
+
JSON.stringify({
|
|
33
|
+
port: 4201,
|
|
34
|
+
theme: "tokyo-night",
|
|
35
|
+
sidebarWidth: 30,
|
|
36
|
+
plugins: ["my-plugin"],
|
|
37
|
+
}),
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const config = loadConfig(TEST_HOME);
|
|
41
|
+
expect(config.port).toBe(4201);
|
|
42
|
+
expect(config.theme).toBe("tokyo-night");
|
|
43
|
+
expect(config.sidebarWidth).toBe(30);
|
|
44
|
+
expect(config.plugins).toEqual(["my-plugin"]);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("handles malformed JSON gracefully", () => {
|
|
48
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
49
|
+
writeFileSync(CONFIG_FILE, "not json {{{");
|
|
50
|
+
|
|
51
|
+
const config = loadConfig(TEST_HOME);
|
|
52
|
+
expect(config.plugins).toEqual([]);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe("saveConfig", () => {
|
|
57
|
+
it("creates config directory and file", () => {
|
|
58
|
+
saveConfig({ theme: "gruvbox-dark" }, TEST_HOME);
|
|
59
|
+
|
|
60
|
+
expect(existsSync(CONFIG_FILE)).toBe(true);
|
|
61
|
+
const saved = JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
62
|
+
expect(saved.theme).toBe("gruvbox-dark");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("merges with existing config", () => {
|
|
66
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
67
|
+
writeFileSync(
|
|
68
|
+
CONFIG_FILE,
|
|
69
|
+
JSON.stringify({
|
|
70
|
+
theme: "nord",
|
|
71
|
+
sidebarWidth: 28,
|
|
72
|
+
plugins: [],
|
|
73
|
+
}),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
saveConfig({ sidebarWidth: 32 }, TEST_HOME);
|
|
77
|
+
|
|
78
|
+
const saved = JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
79
|
+
expect(saved.theme).toBe("nord");
|
|
80
|
+
expect(saved.sidebarWidth).toBe(32);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "bun:test";
|
|
2
|
+
import { AgentTracker, instanceKey } from "../src/agents/tracker";
|
|
3
|
+
import type { AgentEvent } from "../src/contracts/agent";
|
|
4
|
+
|
|
5
|
+
function makeEvent(overrides: Partial<AgentEvent> = {}): AgentEvent {
|
|
6
|
+
return {
|
|
7
|
+
agent: "claude-code",
|
|
8
|
+
session: "main",
|
|
9
|
+
status: "running",
|
|
10
|
+
ts: Date.now(),
|
|
11
|
+
...overrides,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe("instanceKey", () => {
|
|
16
|
+
it("returns agent name when no threadId", () => {
|
|
17
|
+
expect(instanceKey("claude-code")).toBe("claude-code");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("returns agent:threadId when threadId provided", () => {
|
|
21
|
+
expect(instanceKey("claude-code", "abc123")).toBe("claude-code:abc123");
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("AgentTracker", () => {
|
|
26
|
+
let tracker: AgentTracker;
|
|
27
|
+
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
tracker = new AgentTracker();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe("applyEvent / getState", () => {
|
|
33
|
+
it("returns null for unknown session", () => {
|
|
34
|
+
expect(tracker.getState("unknown")).toBeNull();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("tracks a single agent event", () => {
|
|
38
|
+
const event = makeEvent({ status: "running" });
|
|
39
|
+
tracker.applyEvent(event);
|
|
40
|
+
|
|
41
|
+
const state = tracker.getState("main");
|
|
42
|
+
expect(state).not.toBeNull();
|
|
43
|
+
expect(state!.status).toBe("running");
|
|
44
|
+
expect(state!.agent).toBe("claude-code");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("returns highest priority agent", () => {
|
|
48
|
+
tracker.applyEvent(makeEvent({ agent: "amp", status: "idle" }));
|
|
49
|
+
tracker.applyEvent(makeEvent({ agent: "claude-code", status: "running" }));
|
|
50
|
+
|
|
51
|
+
const state = tracker.getState("main");
|
|
52
|
+
expect(state!.status).toBe("running");
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe("getAgents", () => {
|
|
57
|
+
it("returns empty array for unknown session", () => {
|
|
58
|
+
expect(tracker.getAgents("unknown")).toEqual([]);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("returns all agents for a session sorted by ts desc", () => {
|
|
62
|
+
tracker.applyEvent(makeEvent({ agent: "amp", ts: 100 }));
|
|
63
|
+
tracker.applyEvent(makeEvent({ agent: "claude-code", ts: 200 }));
|
|
64
|
+
|
|
65
|
+
const agents = tracker.getAgents("main");
|
|
66
|
+
expect(agents).toHaveLength(2);
|
|
67
|
+
expect(agents[0]!.agent).toBe("claude-code");
|
|
68
|
+
expect(agents[1]!.agent).toBe("amp");
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("getEventTimestamps", () => {
|
|
73
|
+
it("tracks event timestamps per session", () => {
|
|
74
|
+
tracker.applyEvent(makeEvent({ ts: 100 }));
|
|
75
|
+
tracker.applyEvent(makeEvent({ ts: 200 }));
|
|
76
|
+
|
|
77
|
+
const timestamps = tracker.getEventTimestamps("main");
|
|
78
|
+
expect(timestamps).toEqual([100, 200]);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("caps at 30 timestamps", () => {
|
|
82
|
+
for (let i = 0; i < 40; i++) {
|
|
83
|
+
tracker.applyEvent(makeEvent({ ts: i }));
|
|
84
|
+
}
|
|
85
|
+
const timestamps = tracker.getEventTimestamps("main");
|
|
86
|
+
expect(timestamps).toHaveLength(30);
|
|
87
|
+
expect(timestamps[0]).toBe(10);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe("unseen tracking", () => {
|
|
92
|
+
it("marks terminal status as unseen when session not active", () => {
|
|
93
|
+
tracker.applyEvent(makeEvent({ status: "done" }));
|
|
94
|
+
expect(tracker.isUnseen("main")).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("marks seen on focus", () => {
|
|
98
|
+
tracker.applyEvent(makeEvent({ status: "done" }));
|
|
99
|
+
tracker.handleFocus("main");
|
|
100
|
+
expect(tracker.isUnseen("main")).toBe(false);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("markSeen clears unseen flags", () => {
|
|
104
|
+
tracker.applyEvent(makeEvent({ status: "error" }));
|
|
105
|
+
expect(tracker.markSeen("main")).toBe(true);
|
|
106
|
+
expect(tracker.isUnseen("main")).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe("dismiss", () => {
|
|
111
|
+
it("removes an agent instance", () => {
|
|
112
|
+
tracker.applyEvent(makeEvent({ agent: "claude-code" }));
|
|
113
|
+
const removed = tracker.dismiss("main", "claude-code");
|
|
114
|
+
expect(removed).toBe(true);
|
|
115
|
+
expect(tracker.getState("main")).toBeNull();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("returns false for non-existent agent", () => {
|
|
119
|
+
expect(tracker.dismiss("main", "nonexistent")).toBe(false);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe("pruneStuck", () => {
|
|
124
|
+
it("prunes running agents older than timeout", () => {
|
|
125
|
+
tracker.applyEvent(makeEvent({ status: "running", ts: Date.now() - 10_000 }));
|
|
126
|
+
tracker.pruneStuck(5_000);
|
|
127
|
+
expect(tracker.getState("main")).toBeNull();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("does not prune pinned agents", () => {
|
|
131
|
+
tracker.applyEvent(makeEvent({ status: "running", ts: Date.now() - 10_000 }));
|
|
132
|
+
tracker.setPinnedInstances("main", ["claude-code"]);
|
|
133
|
+
tracker.pruneStuck(5_000);
|
|
134
|
+
expect(tracker.getState("main")).not.toBeNull();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe("pruneTerminal", () => {
|
|
139
|
+
it("prunes seen terminal agents after timeout", () => {
|
|
140
|
+
const event = makeEvent({ status: "done", ts: Date.now() - 6 * 60 * 1000 });
|
|
141
|
+
tracker.applyEvent(event);
|
|
142
|
+
tracker.markSeen("main");
|
|
143
|
+
tracker.pruneTerminal();
|
|
144
|
+
expect(tracker.getState("main")).toBeNull();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("does not prune unseen terminal agents", () => {
|
|
148
|
+
tracker.applyEvent(makeEvent({ status: "done", ts: Date.now() - 6 * 60 * 1000 }));
|
|
149
|
+
// Don't mark seen
|
|
150
|
+
tracker.pruneTerminal();
|
|
151
|
+
expect(tracker.getState("main")).not.toBeNull();
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe("multi-thread support", () => {
|
|
156
|
+
it("tracks multiple threads for the same agent", () => {
|
|
157
|
+
tracker.applyEvent(makeEvent({ threadId: "t1", status: "running" }));
|
|
158
|
+
tracker.applyEvent(makeEvent({ threadId: "t2", status: "done" }));
|
|
159
|
+
|
|
160
|
+
const agents = tracker.getAgents("main");
|
|
161
|
+
expect(agents).toHaveLength(2);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("getState returns highest priority across threads", () => {
|
|
165
|
+
tracker.applyEvent(makeEvent({ threadId: "t1", status: "done" }));
|
|
166
|
+
tracker.applyEvent(makeEvent({ threadId: "t2", status: "running" }));
|
|
167
|
+
|
|
168
|
+
const state = tracker.getState("main");
|
|
169
|
+
expect(state!.status).toBe("running");
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": ["ESNext"],
|
|
4
|
+
"target": "ESNext",
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"noEmit": true,
|
|
10
|
+
"types": ["bun-types"]
|
|
11
|
+
},
|
|
12
|
+
"include": ["src/**/*", "test/**/*"]
|
|
13
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["ES2022"],
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "Bundler",
|
|
7
|
+
"resolveJsonModule": true,
|
|
8
|
+
"strict": true,
|
|
9
|
+
"strictNullChecks": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"jsx": "preserve",
|
|
15
|
+
"jsxImportSource": "solid-js",
|
|
16
|
+
"types": ["@types/bun"]
|
|
17
|
+
},
|
|
18
|
+
"include": ["apps/**/*.ts", "apps/**/*.tsx", "packages/**/*.ts"]
|
|
19
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Create a GitHub issue with the auto-claude label for AI-driven work
|
|
3
|
+
allowed-tools: Bash(gh *), AskUserQuestion(*)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Create a GitHub issue with the `auto-claude` label for Claude Code pipeline work.
|
|
7
|
+
|
|
8
|
+
1. Get repo: `gh repo view --json nameWithOwner --jq '.nameWithOwner'`
|
|
9
|
+
2. Fetch labels: `gh label list --repo <repo> --json name --jq '.[].name'`
|
|
10
|
+
3. AskUserQuestion (up to 4 at once): title, description, extra labels (multi-select from repo labels)
|
|
11
|
+
4. `gh issue create`:
|
|
12
|
+
- Always include `auto-claude` label + any extras
|
|
13
|
+
- Prefix title with conventional type (`feat:`, `fix:`, `refactor:`, `research:`, `chore:`)
|
|
14
|
+
- Body: `## Summary`, `## Type`, `## Notes` sections
|
|
15
|
+
- If `auto-claude` label missing, create it first:
|
|
16
|
+
`gh label create "auto-claude" --repo <repo> --description "Issue for Claude Code auto-claude pipeline" --color "7C3AED"`
|
|
17
|
+
5. **Batch**: multiple issues → create in parallel with appropriate prefix/labels each
|
|
18
|
+
6. Report all issue URLs in a table
|
|
19
|
+
|
|
20
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: List open issues with the auto-claude label in the current repo
|
|
3
|
+
allowed-tools: Bash(gh *)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
List open issues across all auto-claude pipeline states in the current repo.
|
|
7
|
+
|
|
8
|
+
1. Get repo: `gh repo view --json nameWithOwner --jq '.nameWithOwner'`
|
|
9
|
+
2. `gh issue list --repo <repo> --state open --json number,title,labels,assignees,state --limit 50` for each label:
|
|
10
|
+
- `auto-claude` (queued)
|
|
11
|
+
- `auto-claude-in-progress`
|
|
12
|
+
- `auto-claude-failed`
|
|
13
|
+
- `auto-claude-review`
|
|
14
|
+
3. Deduplicate across queries. Display as a table sorted by issue number:
|
|
15
|
+
|
|
16
|
+
| # | Title | Status | Labels | Assignee |
|
|
17
|
+
| --- | ----- | ------ | ------ | -------- |
|
|
18
|
+
|
|
19
|
+
Status derived from pipeline label. Labels column excludes pipeline labels. Assignee shows login or `—`.
|
|
20
|
+
|
|
21
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: auto-claude
|
|
3
|
+
description: Use the auto-claude pipeline (`tt auto-claude` / `tt ac`) for automated issue-to-PR workflows — labels a GitHub issue, then runs plan → implement → simplify → review autonomously.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Auto-Claude Pipeline
|
|
7
|
+
|
|
8
|
+
Automated issue-to-PR pipeline. Label a GitHub issue with `auto-claude` and the pipeline runs Claude Code locally through 4 steps: **plan → implement → simplify → review**.
|
|
9
|
+
|
|
10
|
+
## Pipeline Steps
|
|
11
|
+
|
|
12
|
+
1. **Plan** — Research, planning, and annotations. Produces `plan.md`.
|
|
13
|
+
2. **Implement** — Executes the plan: writes code, tests, commits. Produces `completed-summary.md`.
|
|
14
|
+
3. **Simplify** — Code-simplify pass: removes dead code, simplifies logic. Produces `simplify-summary.md`.
|
|
15
|
+
4. **Review** — Automated review outputs `PASS` or `FAIL` on first line of `review.md`.
|
|
16
|
+
|
|
17
|
+
## Label Flow
|
|
18
|
+
|
|
19
|
+
1. Issue labelled `auto-claude` triggers the pipeline.
|
|
20
|
+
2. Pipeline removes `auto-claude`, adds `auto-claude-in-progress`.
|
|
21
|
+
3. On success: removes `auto-claude-in-progress`, adds `auto-claude-review`, creates PR.
|
|
22
|
+
4. On failure: removes `auto-claude-in-progress`, adds `auto-claude-failed`.
|
|
23
|
+
|
|
24
|
+
## Retry Behavior
|
|
25
|
+
|
|
26
|
+
If review outputs FAIL, the pipeline loops back to **implement → simplify → review** (clearing previous artifacts). Configurable via `maxReviewRetries` (default 2), so up to 3 total attempts.
|
|
27
|
+
|
|
28
|
+
## CLI Commands
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Process specific issue
|
|
32
|
+
tt auto-claude --issue 42
|
|
33
|
+
tt ac --issue 42
|
|
34
|
+
|
|
35
|
+
# Stop after planning step (review before implementation)
|
|
36
|
+
tt ac --issue 42 --until plan
|
|
37
|
+
|
|
38
|
+
# Rebase stale PR branch onto current main
|
|
39
|
+
tt ac --refresh --issue 42
|
|
40
|
+
|
|
41
|
+
# Reset state for an issue (force restart)
|
|
42
|
+
tt ac --reset 42
|
|
43
|
+
|
|
44
|
+
# Start polling loop (default 30min interval)
|
|
45
|
+
tt ac --loop
|
|
46
|
+
|
|
47
|
+
# Custom interval and limit
|
|
48
|
+
tt ac --loop --interval 15 --limit 3
|
|
49
|
+
|
|
50
|
+
# Interactively pick an auto-claude issue to process
|
|
51
|
+
tt ac list
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Config
|
|
55
|
+
|
|
56
|
+
Auto-detects repo and main branch from cwd. Key settings:
|
|
57
|
+
|
|
58
|
+
| Field | Default | Description |
|
|
59
|
+
| ------------------------ | ------------- | ----------------------------------- |
|
|
60
|
+
| `triggerLabel` | `auto-claude` | Label that triggers the pipeline |
|
|
61
|
+
| `model` | `opus` | Claude model to use |
|
|
62
|
+
| `maxReviewRetries` | `2` | Review failure retries |
|
|
63
|
+
| `loopIntervalMinutes` | `30` | Polling interval for loop mode |
|
|
64
|
+
| `maxImplementIterations` | `5` | Max Claude turns per implement step |
|
|
65
|
+
|
|
66
|
+
## Conventions
|
|
67
|
+
|
|
68
|
+
- Artifacts: `.auto-claude/issue-{N}/`
|
|
69
|
+
- Branch naming: `auto-claude/issue-{N}`
|
|
70
|
+
- Steps are idempotent — check for output artifact before running
|
|
71
|
+
- `--until <step>` pauses pipeline after the named step
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# tt-core
|
|
2
|
+
|
|
3
|
+
Core workflow automation commands for Claude Code.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
| Command | Description |
|
|
8
|
+
| ------------- | --------------------------------------------- |
|
|
9
|
+
| `/tt:plan` | Interview user and create implementation plan |
|
|
10
|
+
| `/tt:improve` | Explore codebase and suggest improvements |
|
|
11
|
+
| `/tt:refine` | Fix grammar/spelling in files |
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
claude plugin marketplace add ChrisTowles/towles-tool
|
|
17
|
+
claude plugin enable tt@towles-tool
|
|
18
|
+
```
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Analyze codebase architecture for agent-friendliness and structural quality, then suggest improvements
|
|
3
|
+
allowed-tools: AskUserQuestion(*), Read(*), Glob(*), Grep(*), Agent(*)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Analyze codebase architecture for agent-friendliness and structural quality. Diagnose first, present options — never start refactoring immediately.
|
|
7
|
+
|
|
8
|
+
$ARGUMENTS
|
|
9
|
+
|
|
10
|
+
## Process
|
|
11
|
+
|
|
12
|
+
### 1. Analyze
|
|
13
|
+
|
|
14
|
+
Diagnose from the user's description directly. Use `Agent` with `subagent_type=Explore` only when you need to examine actual files.
|
|
15
|
+
|
|
16
|
+
Anti-patterns to look for:
|
|
17
|
+
|
|
18
|
+
- **Scattered concepts** — features requiring many files to understand
|
|
19
|
+
- **Tight coupling** — modules that can't be tested/changed independently
|
|
20
|
+
- **Missing boundaries** — no clear interfaces between subsystems
|
|
21
|
+
- **Impure functions** — side effects mixed into business logic
|
|
22
|
+
- **God classes** — classes doing too much, imported everywhere
|
|
23
|
+
- **Circular dependencies** — modules importing each other
|
|
24
|
+
- **Inconsistent patterns** — mixed error handling, logging, data access
|
|
25
|
+
- **Test gaps** — untested critical paths
|
|
26
|
+
- **Large files** — >300 lines is a smell
|
|
27
|
+
|
|
28
|
+
If architecture is already clean, say so. Do not invent problems.
|
|
29
|
+
|
|
30
|
+
### 2. Diagnose
|
|
31
|
+
|
|
32
|
+
For each issue, classify:
|
|
33
|
+
|
|
34
|
+
- **Impact**: How much does this hurt comprehension and AI agent collaboration?
|
|
35
|
+
- **Effort**: small/medium/large
|
|
36
|
+
- **Risk**: What could break?
|
|
37
|
+
|
|
38
|
+
Prioritize high impact + low effort. Explain which fixes unlock further improvements.
|
|
39
|
+
|
|
40
|
+
### 3. Present
|
|
41
|
+
|
|
42
|
+
Present 5-15 specific improvements, each with:
|
|
43
|
+
|
|
44
|
+
- Concrete problem description
|
|
45
|
+
- Specific proposed fix
|
|
46
|
+
- Affected areas
|
|
47
|
+
- Effort estimate
|
|
48
|
+
|
|
49
|
+
No code snippets — describe changes in plain language. Use `AskUserQuestion` with multiSelect so the user can choose which to pursue.
|
|
50
|
+
|
|
51
|
+
### 4. Output
|
|
52
|
+
|
|
53
|
+
For selected improvements, ask preference: plan in `docs/plans/YYYY-MM-DD-architecture-improvements.md` or GitHub issues.
|
|
54
|
+
|
|
55
|
+
## Philosophy
|
|
56
|
+
|
|
57
|
+
> "If you have a garbage code base, the AI will produce garbage within that code base."
|
|
58
|
+
|
|
59
|
+
Focus on changes that help both humans and AI agents:
|
|
60
|
+
|
|
61
|
+
- Larger, self-contained modules over scattered files
|
|
62
|
+
- Thin interfaces between modules
|
|
63
|
+
- Pure functions extracted from side-effectful code
|
|
64
|
+
- Co-located tests
|
|
65
|
+
- Standardized patterns across the codebase
|
|
66
|
+
- Incremental migration over big-bang rewrites
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Interview me relentlessly about an idea or plan until every gap is resolved. Use before writing code.
|
|
3
|
+
allowed-tools: AskUserQuestion(*)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Relentless Idea Interview
|
|
7
|
+
|
|
8
|
+
You are a ruthless product interviewer. Your job is to find every gap, ambiguity, and unresolved dependency in my idea before any code gets written.
|
|
9
|
+
|
|
10
|
+
$ARGUMENTS
|
|
11
|
+
|
|
12
|
+
## Process
|
|
13
|
+
|
|
14
|
+
1. **Read the idea** — If given a file or description, read it fully first.
|
|
15
|
+
2. **Ask questions in batches** — 3-5 per round via `AskUserQuestion`, covering 3+ domains:
|
|
16
|
+
- User intent / target audience
|
|
17
|
+
- Edge cases and failure modes — always ask "what happens when X goes wrong?" (conflicts, timeouts, partial failures)
|
|
18
|
+
- Data model — core entities, fields, relationships, state changes
|
|
19
|
+
- Integrations and dependencies
|
|
20
|
+
- Security, privacy, compliance (HIPAA, GDPR, PCI)
|
|
21
|
+
- Performance and scale (volumes, latency)
|
|
22
|
+
- Scope and prioritization
|
|
23
|
+
3. **Summarize after each round** — Restate your understanding of what's been decided so far, then ask the next batch.
|
|
24
|
+
4. **Keep going** — Expect 5-10+ rounds. Dig deeper on every answer.
|
|
25
|
+
5. **Wrap up** — When resolved, produce:
|
|
26
|
+
- **Problem statement** (1-2 sentences)
|
|
27
|
+
- **Decided**: locked-in decisions
|
|
28
|
+
- **Out of scope**: explicit exclusions
|
|
29
|
+
- **Open questions**: anything unresolved (near zero)
|
|
30
|
+
|
|
31
|
+
## Rules
|
|
32
|
+
|
|
33
|
+
- **Never propose solutions** — do not suggest or name specific technologies, libraries, services, frameworks, patterns, or implementation approaches, even as examples in your questions. Ask about requirements and constraints, not tools. Only ask questions.
|
|
34
|
+
- **Never assume** — always confirm.
|
|
35
|
+
- **If an answer is vague, push harder** — demand specific numbers, concrete examples, exact definitions. Do not accept "the usual" or "a lot."
|
|
36
|
+
- **Surface risks early** — sensitive data, compliance, ambitious scope go in your first batch.
|
|
37
|
+
- **Be concrete** — specific scenarios, entities, failure modes. No generic "what are your requirements?"
|
|
38
|
+
- **Challenge scope vs. constraints** — if ambitious relative to timeline/team, ask which features could be cut or phased.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Break a PRD into vertical-slice GitHub issues with dependency ordering
|
|
3
|
+
allowed-tools: AskUserQuestion(*), Read(*), Glob(*), Bash(gh *), Bash(git *)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Convert a PRD into independent, parallel-friendly GitHub issues.
|
|
7
|
+
|
|
8
|
+
$ARGUMENTS
|
|
9
|
+
|
|
10
|
+
## Process
|
|
11
|
+
|
|
12
|
+
1. **Find the PRD** — Read given path or check `docs/plans/`. If none, suggest `/tt:write-prd`.
|
|
13
|
+
2. **Get repo and labels**:
|
|
14
|
+
- `gh repo view --json nameWithOwner --jq '.nameWithOwner'`
|
|
15
|
+
- `gh label list --json name --jq '.[].name'`
|
|
16
|
+
3. **Draft vertical slices** — testable functionality, riskiest unknowns early, parallelizable, clear acceptance criteria.
|
|
17
|
+
4. **Present** via `AskUserQuestion` — titles, summaries, blocking relationships. Get approval first.
|
|
18
|
+
5. **Create issues** with `gh issue create`:
|
|
19
|
+
- Prefix: `feat:`, `fix:`, `refactor:`, `chore:`
|
|
20
|
+
- Add repo labels, dependency info (Blocked by / Blocks)
|
|
21
|
+
- Create in dependency order (blockers first)
|
|
22
|
+
6. **Report** — Table with URLs and dependency graph.
|
|
23
|
+
|
|
24
|
+
## Issue Body Template
|
|
25
|
+
|
|
26
|
+
```markdown
|
|
27
|
+
## Summary
|
|
28
|
+
|
|
29
|
+
[What this delivers]
|
|
30
|
+
|
|
31
|
+
## Context
|
|
32
|
+
|
|
33
|
+
From PRD: [link or reference]
|
|
34
|
+
|
|
35
|
+
## Acceptance Criteria
|
|
36
|
+
|
|
37
|
+
- [ ] ...
|
|
38
|
+
|
|
39
|
+
## Dependencies
|
|
40
|
+
|
|
41
|
+
- Blocked by: #N (if any)
|
|
42
|
+
- Blocks: #M (if any)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Rules
|
|
46
|
+
|
|
47
|
+
- Vertical slices, not horizontal layers.
|
|
48
|
+
- Each issue completable in a single session.
|
|
49
|
+
- Riskiest slices first in ordering.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Fix grammar, spelling, and cut filler in writing. Use when asked to "proofread", "edit my writing", "fix grammar", or "clean up this text".
|
|
3
|
+
allowed-tools: Read(*), Edit(*)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are a professional copy editor. Fix errors and cut filler — nothing more. Never rewrite or rephrase working sentences.
|
|
7
|
+
|
|
8
|
+
File to edit: $ARGUMENTS
|
|
9
|
+
|
|
10
|
+
## Edits to Apply
|
|
11
|
+
|
|
12
|
+
1. **Fix spelling and typos**
|
|
13
|
+
2. **Fix grammar** — agreement, apostrophes, tense
|
|
14
|
+
3. **Fix punctuation**
|
|
15
|
+
4. **Cut filler words** — "in order to" → "to", doubled adjectives → pick one. Just delete filler.
|
|
16
|
+
5. **Passive → active** — only when actor is named ("was updated by X" → "X updated")
|
|
17
|
+
|
|
18
|
+
## Preserve
|
|
19
|
+
|
|
20
|
+
- Casual language (slang, "gonna", "kinda") — this is voice, keep it
|
|
21
|
+
- Rhetorical questions, deliberate fragments — keep them
|
|
22
|
+
- Code fences, inline code, URLs, markdown links — never modify
|
|
23
|
+
- Headings, labels, bullet structure — preserve exactly
|
|
24
|
+
- Never add content, never reorder or restructure
|
|
25
|
+
|
|
26
|
+
## Output
|
|
27
|
+
|
|
28
|
+
Output ONLY the corrected text. No introduction, no sign-off, no list of changes.
|
|
29
|
+
|
|
30
|
+
When used with a file, edit the file directly using the Edit tool.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Create, list, and manage Claude Code tasks in the current session. Use when asked to "create tasks", "track work", "list tasks", or "mark task done".
|
|
3
|
+
allowed-tools: TaskCreate(*), TaskUpdate(*), TaskList(*), TaskGet(*)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Manage tasks for the current Claude Code session. Break work into trackable steps.
|
|
7
|
+
|
|
8
|
+
$ARGUMENTS
|
|
9
|
+
|
|
10
|
+
## Behavior
|
|
11
|
+
|
|
12
|
+
### No arguments — interactive mode
|
|
13
|
+
|
|
14
|
+
Ask what the user is working on, then break it into 3-7 concrete tasks. Create all tasks via TaskCreate. Print a summary table.
|
|
15
|
+
|
|
16
|
+
### With arguments — direct creation
|
|
17
|
+
|
|
18
|
+
Parse the arguments as task descriptions. If a single sentence, create one task. If comma-separated or bulleted, create multiple tasks.
|
|
19
|
+
|
|
20
|
+
### Subcommands
|
|
21
|
+
|
|
22
|
+
| Input | Action | Example |
|
|
23
|
+
| ----------------- | ------------------------------------- | ----------------------------------- |
|
|
24
|
+
| `list` | TaskList — show all tasks with status | `/tt:task list` |
|
|
25
|
+
| `done <id>` | TaskUpdate — mark task completed | `/tt:task done 1` |
|
|
26
|
+
| `start <id>` | TaskUpdate — mark task in_progress | `/tt:task start 2` |
|
|
27
|
+
| `cancel <id>` | TaskUpdate — mark task cancelled | `/tt:task cancel 3` |
|
|
28
|
+
| _(anything else)_ | Create task(s) from the text | `/tt:task fix login bug, add tests` |
|
|
29
|
+
|
|
30
|
+
## Rules
|
|
31
|
+
|
|
32
|
+
- Task descriptions should be concrete and actionable — "add X" not "think about X"
|
|
33
|
+
- When creating multiple tasks, order them by dependency (do first → do last)
|
|
34
|
+
- After creating tasks, print a numbered summary so the user can reference them
|
|
35
|
+
- When marking done, confirm which task was completed
|
|
36
|
+
- Keep task names short (under 80 chars) — details go in the description field
|
|
37
|
+
- Subcommand routing is strict: `list` alone triggers TaskList. `done`, `start`, `cancel` must be followed by a numeric ID. Any other pattern — including `done <non-numeric text>` — is treated as task creation text
|