@scrylog/cli 0.3.0 → 0.3.2

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 (195) hide show
  1. package/dist/assets/highlights-eq9cgrbb.scm +604 -0
  2. package/dist/assets/highlights-ghv9g403.scm +205 -0
  3. package/dist/assets/highlights-hk7bwhj4.scm +284 -0
  4. package/dist/assets/highlights-r812a2qc.scm +150 -0
  5. package/dist/assets/highlights-x6tmsnaa.scm +115 -0
  6. package/dist/assets/injections-73j83es3.scm +27 -0
  7. package/dist/assets/parser.worker-xyhf83rf.js +4563 -0
  8. package/dist/assets/tree-sitter-3jzf13jk.wasm +0 -0
  9. package/dist/assets/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
  10. package/dist/assets/tree-sitter-markdown-411r6y9b.wasm +0 -0
  11. package/dist/assets/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
  12. package/dist/assets/tree-sitter-typescript-zxjzwt75.wasm +0 -0
  13. package/dist/assets/tree-sitter-zig-e78zbjpm.wasm +0 -0
  14. package/dist/cli/index.js +499 -0
  15. package/dist/cli/index.js.map +61 -0
  16. package/dist/daemon/index.js +670 -0
  17. package/dist/daemon/index.js.map +1475 -0
  18. package/dist/hooks/claude.js +6 -0
  19. package/dist/hooks/claude.js.map +11 -0
  20. package/dist/hooks/codex.js +10 -0
  21. package/dist/hooks/codex.js.map +11 -0
  22. package/dist/mcp/index.js +100 -0
  23. package/dist/mcp/index.js.map +231 -0
  24. package/dist/oc/index.js +21 -0
  25. package/dist/oc/index.js.map +10 -0
  26. package/package.json +16 -13
  27. package/src/api/docs/sessions.keys.ts +0 -6
  28. package/src/api/docs/sessions.ts +0 -82
  29. package/src/api/docs/system.keys.ts +0 -5
  30. package/src/api/docs/system.ts +0 -69
  31. package/src/api/docs/usage.keys.ts +0 -3
  32. package/src/api/docs/usage.ts +0 -24
  33. package/src/api/docs/webhooks.keys.ts +0 -5
  34. package/src/api/docs/webhooks.ts +0 -78
  35. package/src/api/index.ts +0 -18
  36. package/src/api/lib/auth.ts +0 -43
  37. package/src/api/lib/errors.ts +0 -28
  38. package/src/api/lib/route.ts +0 -18
  39. package/src/api/models/claude.ts +0 -4
  40. package/src/api/models/codex.ts +0 -4
  41. package/src/api/models/opencode.ts +0 -11
  42. package/src/api/routes/sessions.ts +0 -51
  43. package/src/api/routes/system.ts +0 -106
  44. package/src/api/routes/usage.ts +0 -15
  45. package/src/api/routes/webhooks/claude.ts +0 -149
  46. package/src/api/routes/webhooks/codex.ts +0 -175
  47. package/src/api/routes/webhooks/opencode.ts +0 -112
  48. package/src/api/routes.ts +0 -93
  49. package/src/api/schemas/common.ts +0 -29
  50. package/src/api/schemas/sessions.ts +0 -86
  51. package/src/api/schemas/usage.ts +0 -51
  52. package/src/api/schemas/webhooks/claude.ts +0 -32
  53. package/src/api/schemas/webhooks/codex.ts +0 -34
  54. package/src/api/schemas/webhooks/opencode.ts +0 -29
  55. package/src/api/services/claude-session.ts +0 -9
  56. package/src/api/services/codex-session.ts +0 -8
  57. package/src/api/services/opencode-session.ts +0 -73
  58. package/src/classifier.ts +0 -206
  59. package/src/cli/commands.ts +0 -714
  60. package/src/cli/index.ts +0 -18
  61. package/src/cli/program.ts +0 -175
  62. package/src/cli/prompt.ts +0 -44
  63. package/src/daemon/cli.ts +0 -27
  64. package/src/daemon/collectors/claude/README.md +0 -108
  65. package/src/daemon/collectors/claude/collector.ts +0 -423
  66. package/src/daemon/collectors/claude/discovery.ts +0 -127
  67. package/src/daemon/collectors/claude/hooks.ts +0 -181
  68. package/src/daemon/collectors/claude/index.ts +0 -8
  69. package/src/daemon/collectors/claude/jsonl.ts +0 -85
  70. package/src/daemon/collectors/claude/transcript.ts +0 -246
  71. package/src/daemon/collectors/codex/README.md +0 -147
  72. package/src/daemon/collectors/codex/collector.ts +0 -661
  73. package/src/daemon/collectors/codex/discovery.ts +0 -104
  74. package/src/daemon/collectors/codex/hooks.ts +0 -179
  75. package/src/daemon/collectors/codex/index.ts +0 -7
  76. package/src/daemon/collectors/codex/jsonl.ts +0 -108
  77. package/src/daemon/collectors/codex/transcript.ts +0 -299
  78. package/src/daemon/collectors/git.ts +0 -353
  79. package/src/daemon/collectors/herdr/collector.ts +0 -128
  80. package/src/daemon/collectors/herdr/index.ts +0 -2
  81. package/src/daemon/collectors/herdr/normalization.ts +0 -111
  82. package/src/daemon/collectors/opencode/README.md +0 -106
  83. package/src/daemon/collectors/opencode/collector.ts +0 -545
  84. package/src/daemon/collectors/opencode/discovery.ts +0 -257
  85. package/src/daemon/collectors/opencode/index.ts +0 -5
  86. package/src/daemon/collectors/opencode/session-builder.ts +0 -59
  87. package/src/daemon/collectors/opencode-db.ts +0 -294
  88. package/src/daemon/collectors/process.ts +0 -201
  89. package/src/daemon/collectors/tmux/README.md +0 -128
  90. package/src/daemon/collectors/tmux/collector.ts +0 -492
  91. package/src/daemon/collectors/tmux/enrichment.ts +0 -242
  92. package/src/daemon/collectors/tmux/index.ts +0 -4
  93. package/src/daemon/collectors/tmux/parse.ts +0 -128
  94. package/src/daemon/collectors/usage.ts +0 -259
  95. package/src/daemon/index.ts +0 -209
  96. package/src/daemon/scheduler.ts +0 -86
  97. package/src/daemon/store.ts +0 -630
  98. package/src/detect.ts +0 -143
  99. package/src/hooks/claude.ts +0 -596
  100. package/src/hooks/codex.ts +0 -517
  101. package/src/legacy/classifier.ts +0 -215
  102. package/src/legacy/detect.ts +0 -143
  103. package/src/mcp/client-server.ts +0 -21
  104. package/src/mcp/http-handler.ts +0 -126
  105. package/src/mcp/index.ts +0 -61
  106. package/src/mcp/resources/config-current.ts +0 -19
  107. package/src/mcp/resources/index.ts +0 -11
  108. package/src/mcp/resources/sessions-active.ts +0 -19
  109. package/src/mcp/resources/sessions-all.ts +0 -19
  110. package/src/mcp/schemas/sessions.ts +0 -3
  111. package/src/mcp/server.ts +0 -17
  112. package/src/mcp/services/api-adapter.ts +0 -48
  113. package/src/mcp/services/data-source.ts +0 -22
  114. package/src/mcp/services/store-adapter.ts +0 -40
  115. package/src/mcp/tools/get-active-sessions.ts +0 -16
  116. package/src/mcp/tools/get-session-children.ts +0 -24
  117. package/src/mcp/tools/get-session-messages.ts +0 -24
  118. package/src/mcp/tools/get-session.ts +0 -23
  119. package/src/mcp/tools/index.ts +0 -15
  120. package/src/mcp/tools/list-sessions.ts +0 -16
  121. package/src/oc/index.ts +0 -302
  122. package/src/service/manager.ts +0 -284
  123. package/src/shared/config.ts +0 -511
  124. package/src/shared/ipc.ts +0 -80
  125. package/src/shared/logger.ts +0 -248
  126. package/src/shared/paths.ts +0 -73
  127. package/src/shared/platform.ts +0 -110
  128. package/src/shared/session-exclusions.ts +0 -5
  129. package/src/shared/session-view.ts +0 -302
  130. package/src/shared/terminal-capabilities.ts +0 -130
  131. package/src/shared/types.ts +0 -330
  132. package/src/shared/utils.ts +0 -339
  133. package/src/shared/version.ts +0 -44
  134. package/src/tui/App.tsx +0 -240
  135. package/src/tui/client.ts +0 -163
  136. package/src/tui/components/BindingsModal.tsx +0 -82
  137. package/src/tui/components/DashboardHeader.tsx +0 -256
  138. package/src/tui/components/KeyBar.tsx +0 -67
  139. package/src/tui/components/SessionDetailModal.tsx +0 -169
  140. package/src/tui/components/SessionsView.tsx +0 -184
  141. package/src/tui/components/TabBar.tsx +0 -40
  142. package/src/tui/components/UsageSummaryPane.tsx +0 -272
  143. package/src/tui/components/UsageView.tsx +0 -242
  144. package/src/tui/components/tool-icons.ts +0 -24
  145. package/src/tui/index.tsx +0 -106
  146. package/src/tui/keybindings.ts +0 -49
  147. package/src/tui/model.ts +0 -295
  148. package/src/tui/state.ts +0 -77
  149. package/src/tui/theme.ts +0 -110
  150. package/src/types.ts +0 -113
  151. package/src/usage/README.md +0 -323
  152. package/src/usage/api.ts +0 -99
  153. package/src/usage/client.ts +0 -3
  154. package/src/usage/clients/anthropic-oauth-usage.ts +0 -258
  155. package/src/usage/clients/anthropic-web-usage.ts +0 -329
  156. package/src/usage/clients/claude-cli-probe.ts +0 -513
  157. package/src/usage/clients/codex-app-server.ts +0 -354
  158. package/src/usage/clients/codex-oauth-usage.ts +0 -240
  159. package/src/usage/clients/opencode-go-api.ts +0 -100
  160. package/src/usage/clients/opencode-go-usage.ts +0 -577
  161. package/src/usage/errors.ts +0 -33
  162. package/src/usage/index.ts +0 -20
  163. package/src/usage/local/opencode-go-local.ts +0 -194
  164. package/src/usage/mappers.ts +0 -282
  165. package/src/usage/planner.ts +0 -118
  166. package/src/usage/providers/claude.ts +0 -233
  167. package/src/usage/providers/codex.ts +0 -115
  168. package/src/usage/providers/opencode-go.ts +0 -115
  169. package/src/usage/resolver.ts +0 -4
  170. package/src/usage/resolvers/browser-cookies.ts +0 -808
  171. package/src/usage/resolvers/claude-auth.ts +0 -208
  172. package/src/usage/resolvers/claude-cli.ts +0 -24
  173. package/src/usage/resolvers/claude-desktop-auth.ts +0 -170
  174. package/src/usage/resolvers/claude-web.ts +0 -82
  175. package/src/usage/resolvers/codex-auth.ts +0 -174
  176. package/src/usage/resolvers/keychain-coordinator.ts +0 -397
  177. package/src/usage/resolvers/opencode-go-web.ts +0 -55
  178. package/src/usage/types.ts +0 -225
  179. package/src/usage/web-session-cache.ts +0 -37
  180. /package/{src/tui → dist/cli}/assets/providers/SOURCES.md +0 -0
  181. /package/{src/tui → dist/cli}/assets/providers/claude-terminal.png +0 -0
  182. /package/{src/tui → dist/cli}/assets/providers/claude-terminal.svg +0 -0
  183. /package/{src/tui → dist/cli}/assets/providers/claude.png +0 -0
  184. /package/{src/tui → dist/cli}/assets/providers/copilot.png +0 -0
  185. /package/{src/tui → dist/cli}/assets/providers/copilot.svg +0 -0
  186. /package/{src/tui → dist/cli}/assets/providers/openai-terminal.png +0 -0
  187. /package/{src/tui → dist/cli}/assets/providers/openai-terminal.svg +0 -0
  188. /package/{src/tui → dist/cli}/assets/providers/openai.png +0 -0
  189. /package/{src/tui → dist/cli}/assets/providers/opencode-terminal.png +0 -0
  190. /package/{src/tui → dist/cli}/assets/providers/opencode-terminal.svg +0 -0
  191. /package/{src/tui → dist/cli}/assets/providers/opencode.png +0 -0
  192. /package/{src/tui → dist/cli}/assets/providers/shell.png +0 -0
  193. /package/{src/tui → dist/cli}/assets/providers/shell.svg +0 -0
  194. /package/{src/tui → dist/cli}/assets/providers/unknown.png +0 -0
  195. /package/{src/tui → dist/cli}/assets/providers/unknown.svg +0 -0
@@ -1,201 +0,0 @@
1
- import type { ScrylogConfig } from "../../shared/config";
2
- import type { ProcessInfo } from "../../shared/types";
3
- import { parseIntSafe, runCommand, maybeRunCommand } from "../../shared/utils";
4
- import { IS_WINDOWS } from "../../shared/platform";
5
- import type { DaemonStore } from "../store";
6
- import type { Collector } from "../scheduler";
7
- import type { Logger } from "../../shared/logger";
8
- import { NOOP_LOGGER } from "../../shared/logger";
9
-
10
- export function collectProcesses(timeoutMs?: number): ProcessInfo[] {
11
- if (IS_WINDOWS) {
12
- const output = runCommand(["wmic", "process", "get", "ProcessId,ParentProcessId,CommandLine", "/format:csv"], timeoutMs);
13
- return parseWindowsProcesses(output);
14
- }
15
- const output = runCommand(["ps", "-ax", "-o", "pid=,ppid=,state=,etime=,comm=,command="], timeoutMs);
16
- return parseProcesses(output);
17
- }
18
-
19
- export function parseProcesses(output: string): ProcessInfo[] {
20
- return output
21
- .split(/\r?\n/)
22
- .filter(Boolean)
23
- .map((line) => {
24
- const match = line.match(/^\s*(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/);
25
- if (!match) {
26
- return null;
27
- }
28
-
29
- return {
30
- pid: parseIntSafe(match[1] ?? ""),
31
- ppid: parseIntSafe(match[2] ?? ""),
32
- state: match[3] ?? "",
33
- elapsed: match[4] ?? "",
34
- comm: match[5] ?? "",
35
- command: match[6] ?? "",
36
- } satisfies ProcessInfo;
37
- })
38
- .filter((p): p is ProcessInfo => Boolean(p));
39
- }
40
-
41
- export function parseWindowsProcesses(output: string): ProcessInfo[] {
42
- const lines = output.split(/\r?\n/).filter(Boolean);
43
- const result: ProcessInfo[] = [];
44
-
45
- for (const line of lines) {
46
- // WMIC CSV format: Node,CommandLine,ParentProcessId,ProcessId
47
- // CommandLine may contain commas, so split from the right.
48
- const parts = line.split(",");
49
- if (parts.length < 4) continue;
50
-
51
- const pidStr = parts[parts.length - 1]?.trim();
52
- const ppidStr = parts[parts.length - 2]?.trim();
53
- const command = parts.slice(1, parts.length - 2).join(",").trim();
54
- const pid = parseIntSafe(pidStr ?? "", 0);
55
- const ppid = parseIntSafe(ppidStr ?? "", 0);
56
- if (!pid) continue;
57
-
58
- result.push({
59
- pid,
60
- ppid,
61
- state: "?",
62
- elapsed: "",
63
- comm: "",
64
- command,
65
- });
66
- }
67
-
68
- return result;
69
- }
70
-
71
- export function buildProcessTree(rootPid: number, processes: ProcessInfo[]): ProcessInfo[] {
72
- if (!rootPid) {
73
- return [];
74
- }
75
-
76
- const byParent = new Map<number, ProcessInfo[]>();
77
- const byPid = new Map<number, ProcessInfo>();
78
-
79
- for (const process of processes) {
80
- byPid.set(process.pid, process);
81
- const existing = byParent.get(process.ppid);
82
- if (existing) {
83
- existing.push(process);
84
- } else {
85
- byParent.set(process.ppid, [process]);
86
- }
87
- }
88
-
89
- if (!byPid.has(rootPid)) {
90
- return [];
91
- }
92
-
93
- const seen = new Set<number>();
94
- const ordered: ProcessInfo[] = [];
95
- const queue: number[] = [rootPid];
96
-
97
- while (queue.length > 0) {
98
- const pid = queue.shift()!;
99
- if (seen.has(pid)) {
100
- continue;
101
- }
102
- seen.add(pid);
103
-
104
- const process = byPid.get(pid);
105
- if (process) {
106
- ordered.push(process);
107
- }
108
-
109
- const children = byParent.get(pid) ?? [];
110
- children.sort((a, b) => a.pid - b.pid);
111
- for (const child of children) {
112
- queue.push(child.pid);
113
- }
114
- }
115
-
116
- return ordered;
117
- }
118
-
119
- /**
120
- * Walk the process tree upward from childPid until we find a PID that exists
121
- * in the candidate set. Returns the matched PID or undefined.
122
- * Prevents infinite loops via a visited set.
123
- */
124
- export function findAncestorInSet(
125
- childPid: number,
126
- candidateSet: ReadonlySet<number>,
127
- processes: ProcessInfo[],
128
- ): number | undefined {
129
- const byPid = new Map(processes.map((p) => [p.pid, p]));
130
- const visited = new Set<number>();
131
- let current = childPid;
132
-
133
- while (current > 1) {
134
- if (visited.has(current)) break;
135
- visited.add(current);
136
-
137
- if (candidateSet.has(current)) return current;
138
-
139
- const info = byPid.get(current);
140
- if (!info) break;
141
- current = info.ppid;
142
- }
143
-
144
- return undefined;
145
- }
146
-
147
- /**
148
- * Collector that periodically refreshes the process list.
149
- * Exposes the latest snapshot for other collectors to use.
150
- */
151
- export class ProcessCollector implements Collector {
152
- name = "process";
153
- private timer: ReturnType<typeof setInterval> | null = null;
154
- private _processes: ProcessInfo[] = [];
155
- private logger: Logger = NOOP_LOGGER;
156
-
157
- get processes(): ProcessInfo[] {
158
- return this._processes;
159
- }
160
-
161
- start(store: DaemonStore, config: ScrylogConfig, logger: Logger): void {
162
- this.logger = logger;
163
- logger.info("process", "Collector started", { event: "collector.started", collector: this.name, intervalMs: config.daemon.pollIntervalMs });
164
-
165
- const refresh = () => {
166
- if (IS_WINDOWS) {
167
- const result = maybeRunCommand(["wmic", "process", "get", "ProcessId,ParentProcessId,CommandLine", "/format:csv"], config.daemon.commandTimeoutMs);
168
- if (result.ok) {
169
- this._processes = parseWindowsProcesses(result.output);
170
- logger.debug("process", "Process list refreshed", { event: "collector.poll_completed", collector: this.name, count: this._processes.length });
171
- store.setCollectorStatus(this.name, { running: true, lastRunAt: Date.now() });
172
- } else {
173
- logger.warn("process", "wmic command failed", { event: "collector.poll_failed", collector: this.name, errorMessage: result.error });
174
- store.setCollectorStatus(this.name, { lastError: result.error });
175
- }
176
- return;
177
- }
178
-
179
- const result = maybeRunCommand(["ps", "-ax", "-o", "pid=,ppid=,state=,etime=,comm=,command="], config.daemon.commandTimeoutMs);
180
- if (result.ok) {
181
- this._processes = parseProcesses(result.output);
182
- logger.debug("process", "Process list refreshed", { event: "collector.poll_completed", collector: this.name, count: this._processes.length });
183
- store.setCollectorStatus(this.name, { running: true, lastRunAt: Date.now() });
184
- } else {
185
- logger.warn("process", "ps command failed", { event: "collector.poll_failed", collector: this.name, errorMessage: result.error });
186
- store.setCollectorStatus(this.name, { lastError: result.error });
187
- }
188
- };
189
-
190
- refresh();
191
- this.timer = setInterval(refresh, config.daemon.pollIntervalMs);
192
- }
193
-
194
- stop(): void {
195
- if (this.timer) {
196
- clearInterval(this.timer);
197
- this.timer = null;
198
- }
199
- this.logger.info("process", "Collector stopped", { event: "collector.stopped", collector: this.name });
200
- }
201
- }
@@ -1,128 +0,0 @@
1
- # Tmux Collector
2
-
3
- Scans all tmux panes, classifies their contents and process trees, and produces
4
- `SessionModel` entries. Acts as a **fallback layer** that enriches sessions
5
- already tracked by the Claude/OpenCode SDK collectors with pane context
6
- (session, window, pane IDs, process tree, screen sample, working directory).
7
-
8
- ## Data Flow
9
-
10
- ```
11
- tmux list-sessions tmux list-panes -a tmux capture-pane
12
- │ │ │
13
- │ │ │
14
- ▼ ▼ ▼
15
- ┌──────────────────────────────────────────────────────────┐
16
- │ parse.ts │
17
- │ collectTmuxState() ──► parseSessions() + parsePanes() │
18
- │ capturePane() │
19
- └──────────────────────────┬───────────────────────────────┘
20
- │ TmuxCollectionResult
21
- │
22
- ▼
23
- ┌──────────────────────────────────────────────────────────┐
24
- │ enrichment.ts │
25
- │ isClaudeManagedSession() │ isOpenCodeManagedSession() │
26
- │ detectClaudePermissionPrompt() │
27
- │ paneModelToSession() │
28
- │ shouldHideOpenCodePane() │ looksLikeAgent() │
29
- │ clearStaleClaudeTmuxBindings() │
30
- │ clearStaleOpenCodeTmuxBindings() │
31
- └──────────────────────────┬───────────────────────────────┘
32
- │
33
- ▼
34
- ┌──────────────────────────────────────────────────────────┐
35
- │ collector.ts │
36
- │ │
37
- │ for each pane: │
38
- │ processTree ◄── ProcessCollector │
39
- │ │
40
- │ Matching hierarchy (first match wins): │
41
- │ ┌──────────────────────────────────────────┐ │
42
- │ │ M0 @opencode-session pane option │──► enrich │
43
- │ │ (plugin-written, authoritative) │ │
44
- │ │ │ │
45
- │ │ M1 @claude-session pane option │──► enrich │
46
- │ │ (hook-written, authoritative) │ │
47
- │ │ + detectClaudePermissionPrompt() │ │
48
- │ │ │ │
49
- │ │ M1.5 @codex-session pane option │──► enrich │
50
- │ │ (hook-written, authoritative) │ │
51
- │ │ │ │
52
- │ │ M2 Claude pane candidate │──► fallback│
53
- │ │ (heuristic, no option set yet) │ │
54
- │ │ │ │
55
- │ │ M3 OpenCode pane candidate │──► fallback│
56
- │ │ (heuristic, no option set yet) │ │
57
- │ │ │ │
58
- │ │ M4 SDK PID ancestry match │──► enrich │
59
- │ │ (via store.getSdkPids()) │ │
60
- │ │ │ │
61
- │ │ M5 URL-from-args match │──► enrich │
62
- │ │ (findOpencodeInTree from process) │ │
63
- │ │ │ │
64
- │ │ M6 Generic pane │──► fallback│
65
- │ │ (classifyPane() from legacy/) │ │
66
- │ └──────────────────────────────────────────┘ │
67
- │ │
68
- │ store.upsertSession() / store.setSessions() │
69
- │ (setSessions preserves non-fallback, replaces fallback) │
70
- └──────────────────────────────────────────────────────────┘
71
- │
72
- ▼
73
- DaemonStore ──► /api/events SSE ──► TUI
74
- ```
75
-
76
- ## Modules
77
-
78
- | File | Responsibility | Key Exports |
79
- |------|-------------|-------------|
80
- | `parse.ts` | Run `tmux list-sessions` and `tmux list-panes -a`; parse tab-delimited output; capture pane screen samples | `collectTmuxState()`, `parseSessions()`, `parsePanes()`, `capturePane()`, `PANE_FORMAT`, `SESSION_FORMAT`, `TmuxCollectionResult` |
81
- | `enrichment.ts` | Predicates to identify managed sessions (Claude/Codex/OpenCode); detect permission prompts from pane sample; decide whether to hide a pane; convert a classified pane into a `SessionModel` | `isClaudeManagedSession()`, `isCodexManagedSession()`, `isOpenCodeManagedSession()`, `detectClaudePermissionPrompt()`, `detectCodexPermissionPrompt()`, `isCodexPaneCandidate()`, `shouldHideOpenCodePane()`, `paneModelToSession()`, `managedStableForMs()`, `clearStaleClaudeTmuxBindings()`, `clearStaleCodexTmuxBindings()`, `clearStaleOpenCodeTmuxBindings()` |
82
- | `collector.ts` | Iterate over all panes; build process trees; run the 7-tier matching hierarchy; prune stale tmux bindings | `TmuxCollector` (implements `Collector`) |
83
- | `index.ts` | Public re-exports | `TmuxCollector`, enrichment + parse functions |
84
-
85
- ## Matching Hierarchy Detail
86
-
87
- The collector processes each pane independently. For every pane it builds the
88
- process tree (descendants of `pane_pid`) and then attempts each match in order:
89
-
90
- | Tier | Match | What Happens | Rationale |
91
- |------|-------|--------------|-----------|
92
- | **M0** | `@opencode-session` pane user option present | Enrich the matching SDK session with tmux metadata (pane ID, process tree, sample, etc.) | Exact session ID from the scrylog OpenCode plugin — authoritative |
93
- | **M1** | `@claude-session` pane user option present | Enrich the matching Claude session with tmux metadata + apply `detectClaudePermissionPrompt()` only if not hook-managed | Exact session ID from scrylog-claude-hook — authoritative |
94
- | **M1.5** | `@codex-session` pane user option present | Enrich the matching Codex session with tmux metadata + apply `detectCodexPermissionPrompt()` only if not hook-managed | Exact session ID from scrylog-codex-hook — authoritative |
95
- | **M2** | Process tree / command contains `claude` (but no pane option yet) | Create a **fallback** `SessionModel` with `detectionSource: "fallback-detection"` | Heuristic; may be upgraded to M1 later when the hook fires |
96
- | **M3** | Process tree / command indicates OpenCode (but no pane option yet) | Create a **fallback** `SessionModel` | Heuristic; may be upgraded to M0 later when the plugin fires |
97
- | **M4** | A PID in the process tree is an ancestor of a known SDK PID (`store.getSdkPids()`) | Enrich all SDK sessions belonging to that instance URL | PID-ancestry deduplication — prevents duplicate rows for the same physical pane |
98
- | **M5** | A process in the tree is an OpenCode server with a derivable URL (`findOpencodeInTree()`) | Enrich all SDK sessions for that instance URL | URL-based fallback when `registerSdkPid` hasn't happened yet |
99
- | **M6** | None of the above match | Create a **fallback** `SessionModel` via `classifyPane()` from `legacy/classifier.ts` | Generic shell/editor pane |
100
-
101
- **Important:** Fallback sessions (`detectionSource: "fallback-detection"`) are
102
- replaced wholesale each poll cycle. Enriched sessions (M0, M1, M4, M5) are
103
- upserted and persist across polls.
104
-
105
- ## `stableForMs` for Managed Sessions
106
-
107
- When a Claude/OpenCode session is enriched with tmux data, `stableForMs` is
108
- computed from the session's own `stateChangedAt` / `lastActivity` — **not** from
109
- the pane classifier's heuristic timer. This prevents the TUI timer from jumping
110
- to minutes the instant tmux enrichment happens.
111
-
112
- ## Interactions
113
-
114
- | Component | How |
115
- |-----------|-----|
116
- | `ProcessCollector` | `buildProcessTree(panePid, allProcesses)` — gets full descendant tree of the pane's root process |
117
- | `GitCollector` | Resolves `currentPath` → `project` for every pane before classification |
118
- | `ClaudeCollector` | Supplies hook-managed sessions that M1 matches against; `store.isLocalCommand()` filters excluded batch-run sessions |
119
- | `CodexCollector` | Supplies hook-managed sessions that M1.5 matches against |
120
- | `OpenCodeCollector` | Supplies SDK sessions that M0/M4/M5 match against; `store.registerSdkPid()` populates the PID map used in M4 |
121
- | `legacy/classifier.ts` | `classifyPane()` — M6 fallback heuristics for tool/state/summary detection |
122
- | `Scheduler` | Calls `start()` → sets up poll interval from `config.daemon.pollIntervalMs` |
123
-
124
- ## Detection Sources
125
-
126
- This collector **does not** produce its own `detectionSource`. It sets:
127
- - `source: "tmux"` on enriched sessions (they keep their original `detectionSource`)
128
- - `source: "tmux"` + `detectionSource: "fallback-detection"` on M2/M3/M6 fallback sessions