@towles/tool 0.0.107 → 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,164 @@
|
|
|
1
|
+
import { existsSync, watch } from "node:fs";
|
|
2
|
+
import type { FSWatcher } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import type { SessionData } from "../shared";
|
|
5
|
+
|
|
6
|
+
// --- Shell helper (for git commands only) ---
|
|
7
|
+
|
|
8
|
+
export function shell(cmd: string[]): string {
|
|
9
|
+
try {
|
|
10
|
+
const result = Bun.spawnSync(cmd, { stdout: "pipe", stderr: "pipe" });
|
|
11
|
+
return result.stdout.toString().trim();
|
|
12
|
+
} catch {
|
|
13
|
+
return "";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// --- Git helpers ---
|
|
18
|
+
|
|
19
|
+
export interface GitInfo {
|
|
20
|
+
branch: string;
|
|
21
|
+
dirty: boolean;
|
|
22
|
+
isWorktree: boolean;
|
|
23
|
+
filesChanged: number;
|
|
24
|
+
linesAdded: number;
|
|
25
|
+
linesRemoved: number;
|
|
26
|
+
/** Positive = ahead of origin/main, negative = behind */
|
|
27
|
+
commitsDelta: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const gitInfoCache = new Map<string, { info: GitInfo; ts: number }>();
|
|
31
|
+
const GIT_CACHE_TTL_MS = 5000;
|
|
32
|
+
|
|
33
|
+
export function getGitInfo(dir: string): GitInfo {
|
|
34
|
+
const empty: GitInfo = {
|
|
35
|
+
branch: "",
|
|
36
|
+
dirty: false,
|
|
37
|
+
isWorktree: false,
|
|
38
|
+
filesChanged: 0,
|
|
39
|
+
linesAdded: 0,
|
|
40
|
+
linesRemoved: 0,
|
|
41
|
+
commitsDelta: 0,
|
|
42
|
+
};
|
|
43
|
+
if (!dir) return empty;
|
|
44
|
+
|
|
45
|
+
const cached = gitInfoCache.get(dir);
|
|
46
|
+
if (cached && Date.now() - cached.ts < GIT_CACHE_TTL_MS) return cached.info;
|
|
47
|
+
|
|
48
|
+
const branch = shell(["git", "-C", dir, "rev-parse", "--abbrev-ref", "HEAD"]);
|
|
49
|
+
if (!branch) return empty;
|
|
50
|
+
const gitDir = shell(["git", "-C", dir, "rev-parse", "--git-dir"]);
|
|
51
|
+
const statusOut = shell(["git", "-C", dir, "status", "--porcelain"]);
|
|
52
|
+
|
|
53
|
+
// Uncommitted diff stats (unstaged + staged)
|
|
54
|
+
let linesAdded = 0;
|
|
55
|
+
let linesRemoved = 0;
|
|
56
|
+
for (const cmd of [
|
|
57
|
+
["git", "-C", dir, "diff", "--numstat"],
|
|
58
|
+
["git", "-C", dir, "diff", "--cached", "--numstat"],
|
|
59
|
+
]) {
|
|
60
|
+
const out = shell(cmd);
|
|
61
|
+
if (!out) continue;
|
|
62
|
+
for (const line of out.split("\n")) {
|
|
63
|
+
const [added, removed] = line.split("\t");
|
|
64
|
+
if (added === "-" || removed === "-") continue; // binary
|
|
65
|
+
linesAdded += Number(added) || 0;
|
|
66
|
+
linesRemoved += Number(removed) || 0;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Commits ahead/behind origin/main
|
|
71
|
+
// Commits ahead(+) or behind(-) origin/main
|
|
72
|
+
let commitsDelta = 0;
|
|
73
|
+
const originMain = shell(["git", "-C", dir, "rev-parse", "--verify", "origin/main"])
|
|
74
|
+
? "origin/main"
|
|
75
|
+
: "origin/master";
|
|
76
|
+
const aheadBehind = shell([
|
|
77
|
+
"git",
|
|
78
|
+
"-C",
|
|
79
|
+
dir,
|
|
80
|
+
"rev-list",
|
|
81
|
+
"--left-right",
|
|
82
|
+
"--count",
|
|
83
|
+
`${originMain}...HEAD`,
|
|
84
|
+
]);
|
|
85
|
+
if (aheadBehind) {
|
|
86
|
+
const [behind, ahead] = aheadBehind.split("\t");
|
|
87
|
+
commitsDelta = (Number(ahead) || 0) - (Number(behind) || 0);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const filesChanged = statusOut ? statusOut.split("\n").filter(Boolean).length : 0;
|
|
91
|
+
|
|
92
|
+
const info: GitInfo = {
|
|
93
|
+
branch,
|
|
94
|
+
dirty: statusOut.length > 0,
|
|
95
|
+
isWorktree: gitDir.includes("/worktrees/"),
|
|
96
|
+
filesChanged,
|
|
97
|
+
linesAdded,
|
|
98
|
+
linesRemoved,
|
|
99
|
+
commitsDelta,
|
|
100
|
+
};
|
|
101
|
+
gitInfoCache.set(dir, { info, ts: Date.now() });
|
|
102
|
+
return info;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function invalidateGitCache(dir?: string): void {
|
|
106
|
+
if (dir) gitInfoCache.delete(dir);
|
|
107
|
+
else gitInfoCache.clear();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// --- Git HEAD file watchers ---
|
|
111
|
+
|
|
112
|
+
const gitHeadWatchers = new Map<string, FSWatcher>();
|
|
113
|
+
|
|
114
|
+
function resolveGitHeadPath(dir: string): string | null {
|
|
115
|
+
if (!dir) return null;
|
|
116
|
+
const gitDir = shell(["git", "-C", dir, "rev-parse", "--git-dir"]);
|
|
117
|
+
if (!gitDir) return null;
|
|
118
|
+
const absGitDir = gitDir.startsWith("/") ? gitDir : join(dir, gitDir);
|
|
119
|
+
const headPath = join(absGitDir, "HEAD");
|
|
120
|
+
return existsSync(headPath) ? headPath : null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
124
|
+
|
|
125
|
+
function onGitHeadChange(broadcastFn: () => void): void {
|
|
126
|
+
if (debounceTimer) return;
|
|
127
|
+
debounceTimer = setTimeout(() => {
|
|
128
|
+
debounceTimer = null;
|
|
129
|
+
invalidateGitCache();
|
|
130
|
+
broadcastFn();
|
|
131
|
+
}, 200);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function syncGitWatchers(sessions: SessionData[], broadcastFn: () => void): void {
|
|
135
|
+
const currentDirs = new Set<string>();
|
|
136
|
+
for (const s of sessions) {
|
|
137
|
+
if (s.dir) currentDirs.add(s.dir);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
for (const [dir, watcher] of gitHeadWatchers) {
|
|
141
|
+
if (!currentDirs.has(dir)) {
|
|
142
|
+
watcher.close();
|
|
143
|
+
gitHeadWatchers.delete(dir);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
for (const dir of currentDirs) {
|
|
148
|
+
if (gitHeadWatchers.has(dir)) continue;
|
|
149
|
+
const headPath = resolveGitHeadPath(dir);
|
|
150
|
+
if (!headPath) continue;
|
|
151
|
+
try {
|
|
152
|
+
const watcher = watch(headPath, () => onGitHeadChange(broadcastFn));
|
|
153
|
+
gitHeadWatchers.set(dir, watcher);
|
|
154
|
+
} catch {
|
|
155
|
+
/* ignore */
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function teardownGitWatchers(): void {
|
|
161
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
162
|
+
for (const watcher of gitHeadWatchers.values()) watcher.close();
|
|
163
|
+
gitHeadWatchers.clear();
|
|
164
|
+
}
|