@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.
Files changed (76) hide show
  1. package/README.md +7 -1
  2. package/package.json +2 -1
  3. package/plugins/tt-agentboard/README.md +160 -0
  4. package/plugins/tt-agentboard/apps/server/package.json +20 -0
  5. package/plugins/tt-agentboard/apps/server/src/main.ts +60 -0
  6. package/plugins/tt-agentboard/apps/tui/build.ts +11 -0
  7. package/plugins/tt-agentboard/apps/tui/bunfig.toml +1 -0
  8. package/plugins/tt-agentboard/apps/tui/package.json +23 -0
  9. package/plugins/tt-agentboard/apps/tui/scripts/sessionizer.sh +36 -0
  10. package/plugins/tt-agentboard/apps/tui/src/components/DetailPanel.tsx +350 -0
  11. package/plugins/tt-agentboard/apps/tui/src/components/DiffStats.tsx +33 -0
  12. package/plugins/tt-agentboard/apps/tui/src/components/SessionCard.tsx +177 -0
  13. package/plugins/tt-agentboard/apps/tui/src/components/StatusBar.tsx +49 -0
  14. package/plugins/tt-agentboard/apps/tui/src/constants.ts +46 -0
  15. package/plugins/tt-agentboard/apps/tui/src/detail-panel-height.ts +21 -0
  16. package/plugins/tt-agentboard/apps/tui/src/index.tsx +880 -0
  17. package/plugins/tt-agentboard/apps/tui/src/mux-context.ts +61 -0
  18. package/plugins/tt-agentboard/apps/tui/tsconfig.json +15 -0
  19. package/plugins/tt-agentboard/bun.lock +444 -0
  20. package/plugins/tt-agentboard/package.json +26 -0
  21. package/plugins/tt-agentboard/packages/mux-tmux/package.json +14 -0
  22. package/plugins/tt-agentboard/packages/mux-tmux/src/client.ts +550 -0
  23. package/plugins/tt-agentboard/packages/mux-tmux/src/index.ts +18 -0
  24. package/plugins/tt-agentboard/packages/mux-tmux/src/provider.ts +259 -0
  25. package/plugins/tt-agentboard/packages/mux-tmux/tsconfig.json +13 -0
  26. package/plugins/tt-agentboard/packages/runtime/package.json +14 -0
  27. package/plugins/tt-agentboard/packages/runtime/src/agents/tracker.ts +233 -0
  28. package/plugins/tt-agentboard/packages/runtime/src/agents/watchers/amp.ts +316 -0
  29. package/plugins/tt-agentboard/packages/runtime/src/agents/watchers/claude-code.ts +374 -0
  30. package/plugins/tt-agentboard/packages/runtime/src/agents/watchers/codex.ts +364 -0
  31. package/plugins/tt-agentboard/packages/runtime/src/agents/watchers/opencode.ts +249 -0
  32. package/plugins/tt-agentboard/packages/runtime/src/config.ts +70 -0
  33. package/plugins/tt-agentboard/packages/runtime/src/contracts/agent-watcher.ts +38 -0
  34. package/plugins/tt-agentboard/packages/runtime/src/contracts/agent.ts +16 -0
  35. package/plugins/tt-agentboard/packages/runtime/src/contracts/index.ts +3 -0
  36. package/plugins/tt-agentboard/packages/runtime/src/contracts/mux.ts +148 -0
  37. package/plugins/tt-agentboard/packages/runtime/src/debug.ts +19 -0
  38. package/plugins/tt-agentboard/packages/runtime/src/index.ts +69 -0
  39. package/plugins/tt-agentboard/packages/runtime/src/mux/detect.ts +20 -0
  40. package/plugins/tt-agentboard/packages/runtime/src/mux/registry.ts +45 -0
  41. package/plugins/tt-agentboard/packages/runtime/src/plugins/loader.ts +152 -0
  42. package/plugins/tt-agentboard/packages/runtime/src/server/context.ts +112 -0
  43. package/plugins/tt-agentboard/packages/runtime/src/server/git-info.ts +164 -0
  44. package/plugins/tt-agentboard/packages/runtime/src/server/index.ts +1753 -0
  45. package/plugins/tt-agentboard/packages/runtime/src/server/launcher.ts +71 -0
  46. package/plugins/tt-agentboard/packages/runtime/src/server/metadata-store.ts +86 -0
  47. package/plugins/tt-agentboard/packages/runtime/src/server/pane-scanner.ts +327 -0
  48. package/plugins/tt-agentboard/packages/runtime/src/server/port-scanner.ts +155 -0
  49. package/plugins/tt-agentboard/packages/runtime/src/server/session-order.ts +127 -0
  50. package/plugins/tt-agentboard/packages/runtime/src/server/sidebar-manager.ts +232 -0
  51. package/plugins/tt-agentboard/packages/runtime/src/server/sidebar-width-sync.ts +66 -0
  52. package/plugins/tt-agentboard/packages/runtime/src/shared.ts +179 -0
  53. package/plugins/tt-agentboard/packages/runtime/src/themes.ts +750 -0
  54. package/plugins/tt-agentboard/packages/runtime/test/config.test.ts +83 -0
  55. package/plugins/tt-agentboard/packages/runtime/test/tracker.test.ts +172 -0
  56. package/plugins/tt-agentboard/packages/runtime/tsconfig.json +13 -0
  57. package/plugins/tt-agentboard/tsconfig.json +19 -0
  58. package/plugins/tt-auto-claude/.claude-plugin/plugin.json +8 -0
  59. package/plugins/tt-auto-claude/commands/create-issue.md +20 -0
  60. package/plugins/tt-auto-claude/commands/list.md +21 -0
  61. package/plugins/tt-auto-claude/skills/auto-claude/SKILL.md +71 -0
  62. package/plugins/tt-core/.claude-plugin/plugin.json +8 -0
  63. package/plugins/tt-core/README.md +18 -0
  64. package/plugins/tt-core/commands/improve-architecture.md +66 -0
  65. package/plugins/tt-core/commands/interview-me.md +38 -0
  66. package/plugins/tt-core/commands/prd-to-issues.md +49 -0
  67. package/plugins/tt-core/commands/refine-text.md +30 -0
  68. package/plugins/tt-core/commands/task.md +37 -0
  69. package/plugins/tt-core/commands/tdd.md +69 -0
  70. package/plugins/tt-core/commands/write-prd.md +69 -0
  71. package/plugins/tt-core/promptfooconfig.interview-me.yaml +155 -0
  72. package/plugins/tt-core/promptfooconfig.refine-text.yaml +242 -0
  73. package/plugins/tt-core/promptfooconfig.tdd.yaml +144 -0
  74. package/plugins/tt-core/promptfooconfig.write-prd.yaml +145 -0
  75. package/plugins/tt-core/skills/towles-tool/SKILL.md +35 -0
  76. 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,8 @@
1
+ {
2
+ "name": "tt-ac",
3
+ "description": "Auto-Claude pipeline: automated issue-to-PR workflows using Claude Code",
4
+ "version": "0.0.108",
5
+ "author": {
6
+ "name": "Chris Towles"
7
+ }
8
+ }
@@ -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,8 @@
1
+ {
2
+ "name": "tt",
3
+ "description": "Core dev workflow commands: interview-me, write-prd, prd-to-issues, tdd, improve-architecture, refine-text, task.",
4
+ "version": "0.0.108",
5
+ "author": {
6
+ "name": "Chris Towles"
7
+ }
8
+ }
@@ -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