aicodeman 0.2.8

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 (246) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +403 -0
  3. package/dist/ai-checker-base.d.ts +175 -0
  4. package/dist/ai-checker-base.d.ts.map +1 -0
  5. package/dist/ai-checker-base.js +424 -0
  6. package/dist/ai-checker-base.js.map +1 -0
  7. package/dist/ai-idle-checker.d.ts +53 -0
  8. package/dist/ai-idle-checker.d.ts.map +1 -0
  9. package/dist/ai-idle-checker.js +141 -0
  10. package/dist/ai-idle-checker.js.map +1 -0
  11. package/dist/ai-plan-checker.d.ts +52 -0
  12. package/dist/ai-plan-checker.d.ts.map +1 -0
  13. package/dist/ai-plan-checker.js +103 -0
  14. package/dist/ai-plan-checker.js.map +1 -0
  15. package/dist/bash-tool-parser.d.ts +191 -0
  16. package/dist/bash-tool-parser.d.ts.map +1 -0
  17. package/dist/bash-tool-parser.js +598 -0
  18. package/dist/bash-tool-parser.js.map +1 -0
  19. package/dist/cli.d.ts +12 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +460 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/config/buffer-limits.d.ts +59 -0
  24. package/dist/config/buffer-limits.d.ts.map +1 -0
  25. package/dist/config/buffer-limits.js +74 -0
  26. package/dist/config/buffer-limits.js.map +1 -0
  27. package/dist/config/map-limits.d.ts +40 -0
  28. package/dist/config/map-limits.d.ts.map +1 -0
  29. package/dist/config/map-limits.js +52 -0
  30. package/dist/config/map-limits.js.map +1 -0
  31. package/dist/file-stream-manager.d.ts +148 -0
  32. package/dist/file-stream-manager.d.ts.map +1 -0
  33. package/dist/file-stream-manager.js +351 -0
  34. package/dist/file-stream-manager.js.map +1 -0
  35. package/dist/hooks-config.d.ts +31 -0
  36. package/dist/hooks-config.d.ts.map +1 -0
  37. package/dist/hooks-config.js +115 -0
  38. package/dist/hooks-config.js.map +1 -0
  39. package/dist/image-watcher.d.ts +86 -0
  40. package/dist/image-watcher.d.ts.map +1 -0
  41. package/dist/image-watcher.js +275 -0
  42. package/dist/image-watcher.js.map +1 -0
  43. package/dist/index.d.ts +11 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +54 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/mux-factory.d.ts +13 -0
  48. package/dist/mux-factory.d.ts.map +1 -0
  49. package/dist/mux-factory.js +19 -0
  50. package/dist/mux-factory.js.map +1 -0
  51. package/dist/mux-interface.d.ts +145 -0
  52. package/dist/mux-interface.d.ts.map +1 -0
  53. package/dist/mux-interface.js +9 -0
  54. package/dist/mux-interface.js.map +1 -0
  55. package/dist/plan-orchestrator.d.ts +123 -0
  56. package/dist/plan-orchestrator.d.ts.map +1 -0
  57. package/dist/plan-orchestrator.js +500 -0
  58. package/dist/plan-orchestrator.js.map +1 -0
  59. package/dist/prompts/index.d.ts +9 -0
  60. package/dist/prompts/index.d.ts.map +1 -0
  61. package/dist/prompts/index.js +9 -0
  62. package/dist/prompts/index.js.map +1 -0
  63. package/dist/prompts/planner.d.ts +14 -0
  64. package/dist/prompts/planner.d.ts.map +1 -0
  65. package/dist/prompts/planner.js +83 -0
  66. package/dist/prompts/planner.js.map +1 -0
  67. package/dist/prompts/research-agent.d.ts +10 -0
  68. package/dist/prompts/research-agent.d.ts.map +1 -0
  69. package/dist/prompts/research-agent.js +143 -0
  70. package/dist/prompts/research-agent.js.map +1 -0
  71. package/dist/push-store.d.ts +41 -0
  72. package/dist/push-store.d.ts.map +1 -0
  73. package/dist/push-store.js +168 -0
  74. package/dist/push-store.js.map +1 -0
  75. package/dist/ralph-config.d.ts +67 -0
  76. package/dist/ralph-config.d.ts.map +1 -0
  77. package/dist/ralph-config.js +134 -0
  78. package/dist/ralph-config.js.map +1 -0
  79. package/dist/ralph-loop.d.ts +124 -0
  80. package/dist/ralph-loop.d.ts.map +1 -0
  81. package/dist/ralph-loop.js +418 -0
  82. package/dist/ralph-loop.js.map +1 -0
  83. package/dist/ralph-tracker.d.ts +1081 -0
  84. package/dist/ralph-tracker.d.ts.map +1 -0
  85. package/dist/ralph-tracker.js +3343 -0
  86. package/dist/ralph-tracker.js.map +1 -0
  87. package/dist/respawn-controller.d.ts +1182 -0
  88. package/dist/respawn-controller.d.ts.map +1 -0
  89. package/dist/respawn-controller.js +2754 -0
  90. package/dist/respawn-controller.js.map +1 -0
  91. package/dist/run-summary.d.ts +123 -0
  92. package/dist/run-summary.d.ts.map +1 -0
  93. package/dist/run-summary.js +325 -0
  94. package/dist/run-summary.js.map +1 -0
  95. package/dist/session-lifecycle-log.d.ts +36 -0
  96. package/dist/session-lifecycle-log.d.ts.map +1 -0
  97. package/dist/session-lifecycle-log.js +101 -0
  98. package/dist/session-lifecycle-log.js.map +1 -0
  99. package/dist/session-manager.d.ts +97 -0
  100. package/dist/session-manager.d.ts.map +1 -0
  101. package/dist/session-manager.js +224 -0
  102. package/dist/session-manager.js.map +1 -0
  103. package/dist/session.d.ts +686 -0
  104. package/dist/session.d.ts.map +1 -0
  105. package/dist/session.js +2025 -0
  106. package/dist/session.js.map +1 -0
  107. package/dist/state-store.d.ts +189 -0
  108. package/dist/state-store.d.ts.map +1 -0
  109. package/dist/state-store.js +730 -0
  110. package/dist/state-store.js.map +1 -0
  111. package/dist/subagent-watcher.d.ts +345 -0
  112. package/dist/subagent-watcher.d.ts.map +1 -0
  113. package/dist/subagent-watcher.js +1469 -0
  114. package/dist/subagent-watcher.js.map +1 -0
  115. package/dist/task-queue.d.ts +108 -0
  116. package/dist/task-queue.d.ts.map +1 -0
  117. package/dist/task-queue.js +235 -0
  118. package/dist/task-queue.js.map +1 -0
  119. package/dist/task-tracker.d.ts +306 -0
  120. package/dist/task-tracker.d.ts.map +1 -0
  121. package/dist/task-tracker.js +488 -0
  122. package/dist/task-tracker.js.map +1 -0
  123. package/dist/task.d.ts +73 -0
  124. package/dist/task.d.ts.map +1 -0
  125. package/dist/task.js +177 -0
  126. package/dist/task.js.map +1 -0
  127. package/dist/team-watcher.d.ts +53 -0
  128. package/dist/team-watcher.d.ts.map +1 -0
  129. package/dist/team-watcher.js +313 -0
  130. package/dist/team-watcher.js.map +1 -0
  131. package/dist/templates/case-template.md +461 -0
  132. package/dist/templates/claude-md.d.ts +26 -0
  133. package/dist/templates/claude-md.d.ts.map +1 -0
  134. package/dist/templates/claude-md.js +74 -0
  135. package/dist/templates/claude-md.js.map +1 -0
  136. package/dist/tmux-manager.d.ts +181 -0
  137. package/dist/tmux-manager.d.ts.map +1 -0
  138. package/dist/tmux-manager.js +1405 -0
  139. package/dist/tmux-manager.js.map +1 -0
  140. package/dist/transcript-watcher.d.ts +110 -0
  141. package/dist/transcript-watcher.d.ts.map +1 -0
  142. package/dist/transcript-watcher.js +338 -0
  143. package/dist/transcript-watcher.js.map +1 -0
  144. package/dist/tunnel-manager.d.ts +54 -0
  145. package/dist/tunnel-manager.d.ts.map +1 -0
  146. package/dist/tunnel-manager.js +251 -0
  147. package/dist/tunnel-manager.js.map +1 -0
  148. package/dist/types.d.ts +1139 -0
  149. package/dist/types.d.ts.map +1 -0
  150. package/dist/types.js +215 -0
  151. package/dist/types.js.map +1 -0
  152. package/dist/utils/buffer-accumulator.d.ts +111 -0
  153. package/dist/utils/buffer-accumulator.d.ts.map +1 -0
  154. package/dist/utils/buffer-accumulator.js +172 -0
  155. package/dist/utils/buffer-accumulator.js.map +1 -0
  156. package/dist/utils/claude-cli-resolver.d.ts +26 -0
  157. package/dist/utils/claude-cli-resolver.d.ts.map +1 -0
  158. package/dist/utils/claude-cli-resolver.js +78 -0
  159. package/dist/utils/claude-cli-resolver.js.map +1 -0
  160. package/dist/utils/cleanup-manager.d.ts +165 -0
  161. package/dist/utils/cleanup-manager.d.ts.map +1 -0
  162. package/dist/utils/cleanup-manager.js +274 -0
  163. package/dist/utils/cleanup-manager.js.map +1 -0
  164. package/dist/utils/index.d.ts +19 -0
  165. package/dist/utils/index.d.ts.map +1 -0
  166. package/dist/utils/index.js +19 -0
  167. package/dist/utils/index.js.map +1 -0
  168. package/dist/utils/lru-map.d.ts +140 -0
  169. package/dist/utils/lru-map.d.ts.map +1 -0
  170. package/dist/utils/lru-map.js +234 -0
  171. package/dist/utils/lru-map.js.map +1 -0
  172. package/dist/utils/nice-wrapper.d.ts +13 -0
  173. package/dist/utils/nice-wrapper.d.ts.map +1 -0
  174. package/dist/utils/nice-wrapper.js +17 -0
  175. package/dist/utils/nice-wrapper.js.map +1 -0
  176. package/dist/utils/opencode-cli-resolver.d.ts +21 -0
  177. package/dist/utils/opencode-cli-resolver.d.ts.map +1 -0
  178. package/dist/utils/opencode-cli-resolver.js +67 -0
  179. package/dist/utils/opencode-cli-resolver.js.map +1 -0
  180. package/dist/utils/regex-patterns.d.ts +64 -0
  181. package/dist/utils/regex-patterns.d.ts.map +1 -0
  182. package/dist/utils/regex-patterns.js +74 -0
  183. package/dist/utils/regex-patterns.js.map +1 -0
  184. package/dist/utils/stale-expiration-map.d.ts +159 -0
  185. package/dist/utils/stale-expiration-map.d.ts.map +1 -0
  186. package/dist/utils/stale-expiration-map.js +277 -0
  187. package/dist/utils/stale-expiration-map.js.map +1 -0
  188. package/dist/utils/string-similarity.d.ts +108 -0
  189. package/dist/utils/string-similarity.d.ts.map +1 -0
  190. package/dist/utils/string-similarity.js +189 -0
  191. package/dist/utils/string-similarity.js.map +1 -0
  192. package/dist/utils/token-validation.d.ts +39 -0
  193. package/dist/utils/token-validation.d.ts.map +1 -0
  194. package/dist/utils/token-validation.js +59 -0
  195. package/dist/utils/token-validation.js.map +1 -0
  196. package/dist/utils/type-safety.d.ts +33 -0
  197. package/dist/utils/type-safety.d.ts.map +1 -0
  198. package/dist/utils/type-safety.js +35 -0
  199. package/dist/utils/type-safety.js.map +1 -0
  200. package/dist/web/public/app.js +491 -0
  201. package/dist/web/public/app.js.br +0 -0
  202. package/dist/web/public/app.js.gz +0 -0
  203. package/dist/web/public/index.html +1675 -0
  204. package/dist/web/public/index.html.br +0 -0
  205. package/dist/web/public/index.html.gz +0 -0
  206. package/dist/web/public/manifest.json +8 -0
  207. package/dist/web/public/mobile.css +1 -0
  208. package/dist/web/public/mobile.css.br +0 -0
  209. package/dist/web/public/mobile.css.gz +0 -0
  210. package/dist/web/public/ralph-wizard.js +1037 -0
  211. package/dist/web/public/ralph-wizard.js.br +0 -0
  212. package/dist/web/public/ralph-wizard.js.gz +0 -0
  213. package/dist/web/public/styles.css +1 -0
  214. package/dist/web/public/styles.css.br +0 -0
  215. package/dist/web/public/styles.css.gz +0 -0
  216. package/dist/web/public/sw.js +67 -0
  217. package/dist/web/public/sw.js.br +0 -0
  218. package/dist/web/public/sw.js.gz +0 -0
  219. package/dist/web/public/upload.html +155 -0
  220. package/dist/web/public/upload.html.br +0 -0
  221. package/dist/web/public/upload.html.gz +0 -0
  222. package/dist/web/public/vendor/xterm-addon-fit.min.js +1 -0
  223. package/dist/web/public/vendor/xterm-addon-fit.min.js.br +0 -0
  224. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  225. package/dist/web/public/vendor/xterm-addon-unicode11.min.js +1 -0
  226. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.br +0 -0
  227. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  228. package/dist/web/public/vendor/xterm-addon-webgl.min.js +2 -0
  229. package/dist/web/public/vendor/xterm-addon-webgl.min.js.br +0 -0
  230. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  231. package/dist/web/public/vendor/xterm.css +209 -0
  232. package/dist/web/public/vendor/xterm.css.br +0 -0
  233. package/dist/web/public/vendor/xterm.css.gz +0 -0
  234. package/dist/web/public/vendor/xterm.min.js +9 -0
  235. package/dist/web/public/vendor/xterm.min.js.br +0 -0
  236. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  237. package/dist/web/schemas.d.ts +479 -0
  238. package/dist/web/schemas.d.ts.map +1 -0
  239. package/dist/web/schemas.js +448 -0
  240. package/dist/web/schemas.js.map +1 -0
  241. package/dist/web/server.d.ts +207 -0
  242. package/dist/web/server.d.ts.map +1 -0
  243. package/dist/web/server.js +5784 -0
  244. package/dist/web/server.js.map +1 -0
  245. package/package.json +110 -0
  246. package/scripts/postinstall.js +390 -0
@@ -0,0 +1,115 @@
1
+ /**
2
+ * @fileoverview Claude Code hooks configuration generator
3
+ *
4
+ * Generates .claude/settings.local.json with hook definitions that POST
5
+ * to Codeman's /api/hook-event endpoint when Claude Code fires
6
+ * notification or stop hooks. Uses $CODEMAN_API_URL and
7
+ * $CODEMAN_SESSION_ID env vars (set on every managed session) so the
8
+ * config is static per case directory.
9
+ */
10
+ import { existsSync } from 'node:fs';
11
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
12
+ import { join } from 'node:path';
13
+ /**
14
+ * Generates the hooks section for .claude/settings.local.json
15
+ *
16
+ * The hook commands read stdin JSON from Claude Code (contains tool_name,
17
+ * tool_input, etc.) and forward it as the `data` field to Codeman's API.
18
+ * Env vars are resolved at runtime by the shell, so the config is static
19
+ * per case directory.
20
+ */
21
+ export function generateHooksConfig() {
22
+ // Read Claude Code's stdin JSON and forward it as the data field.
23
+ // Falls back to empty object if stdin is unavailable or malformed.
24
+ const curlCmd = (event) => `HOOK_DATA=$(cat 2>/dev/null || echo '{}'); ` +
25
+ `printf '{"event":"${event}","sessionId":"%s","data":%s}' "$CODEMAN_SESSION_ID" "$HOOK_DATA" | ` +
26
+ `curl -s -X POST "$CODEMAN_API_URL/api/hook-event" ` +
27
+ `-H 'Content-Type: application/json' ` +
28
+ `--data @- ` +
29
+ `2>/dev/null || true`;
30
+ return {
31
+ hooks: {
32
+ Notification: [
33
+ {
34
+ matcher: 'idle_prompt',
35
+ hooks: [{ type: 'command', command: curlCmd('idle_prompt'), timeout: 10000 }],
36
+ },
37
+ {
38
+ matcher: 'permission_prompt',
39
+ hooks: [{ type: 'command', command: curlCmd('permission_prompt'), timeout: 10000 }],
40
+ },
41
+ {
42
+ matcher: 'elicitation_dialog',
43
+ hooks: [{ type: 'command', command: curlCmd('elicitation_dialog'), timeout: 10000 }],
44
+ },
45
+ ],
46
+ Stop: [
47
+ {
48
+ hooks: [{ type: 'command', command: curlCmd('stop'), timeout: 10000 }],
49
+ },
50
+ ],
51
+ TeammateIdle: [
52
+ {
53
+ hooks: [{ type: 'command', command: curlCmd('teammate_idle'), timeout: 10000 }],
54
+ },
55
+ ],
56
+ TaskCompleted: [
57
+ {
58
+ hooks: [{ type: 'command', command: curlCmd('task_completed'), timeout: 10000 }],
59
+ },
60
+ ],
61
+ },
62
+ };
63
+ }
64
+ /**
65
+ * Updates env vars in .claude/settings.local.json for the given case path.
66
+ * Merges with existing env field; removes vars set to empty string.
67
+ */
68
+ export async function updateCaseEnvVars(casePath, envVars) {
69
+ const claudeDir = join(casePath, '.claude');
70
+ if (!existsSync(claudeDir)) {
71
+ await mkdir(claudeDir, { recursive: true });
72
+ }
73
+ const settingsPath = join(claudeDir, 'settings.local.json');
74
+ let existing = {};
75
+ try {
76
+ existing = JSON.parse(await readFile(settingsPath, 'utf-8'));
77
+ }
78
+ catch {
79
+ existing = {};
80
+ }
81
+ const currentEnv = existing.env || {};
82
+ for (const [key, value] of Object.entries(envVars)) {
83
+ if (value) {
84
+ currentEnv[key] = value;
85
+ }
86
+ else {
87
+ delete currentEnv[key];
88
+ }
89
+ }
90
+ existing.env = currentEnv;
91
+ await writeFile(settingsPath, JSON.stringify(existing, null, 2) + '\n');
92
+ }
93
+ /**
94
+ * Writes hooks config to .claude/settings.local.json in the given case path.
95
+ * Merges with existing file content, only touching the `hooks` key.
96
+ */
97
+ export async function writeHooksConfig(casePath) {
98
+ const claudeDir = join(casePath, '.claude');
99
+ if (!existsSync(claudeDir)) {
100
+ await mkdir(claudeDir, { recursive: true });
101
+ }
102
+ const settingsPath = join(claudeDir, 'settings.local.json');
103
+ let existing = {};
104
+ try {
105
+ existing = JSON.parse(await readFile(settingsPath, 'utf-8'));
106
+ }
107
+ catch {
108
+ // If file is malformed or doesn't exist, start fresh
109
+ existing = {};
110
+ }
111
+ const hooksConfig = generateHooksConfig();
112
+ const merged = { ...existing, ...hooksConfig };
113
+ await writeFile(settingsPath, JSON.stringify(merged, null, 2) + '\n');
114
+ }
115
+ //# sourceMappingURL=hooks-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks-config.js","sourceRoot":"","sources":["../src/hooks-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * @fileoverview Image Watcher - Detects new image files in session working directories
3
+ *
4
+ * Watches session working directories for new image files (screenshots, generated images)
5
+ * and emits events to trigger automatic popup display in the web UI.
6
+ *
7
+ * Uses chokidar for reliable cross-platform file watching with awaitWriteFinish
8
+ * to ensure files are fully written before emitting detection events.
9
+ */
10
+ import { EventEmitter } from 'node:events';
11
+ import type { ImageDetectedEvent } from './types.js';
12
+ export interface ImageWatcherEvents {
13
+ 'image:detected': (event: ImageDetectedEvent) => void;
14
+ 'image:error': (error: Error, sessionId?: string) => void;
15
+ }
16
+ /**
17
+ * Watches session working directories for new image files.
18
+ *
19
+ * Follows the SubagentWatcher pattern: extends EventEmitter, manages
20
+ * file watchers in Maps, emits typed events.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * const watcher = new ImageWatcher();
25
+ * watcher.on('image:detected', (event) => {
26
+ * console.log(`New image in session ${event.sessionId}: ${event.fileName}`);
27
+ * });
28
+ * watcher.watchSession('session-123', '/path/to/working/dir');
29
+ * ```
30
+ */
31
+ export declare class ImageWatcher extends EventEmitter {
32
+ /** Map of sessionId -> FSWatcher for per-session directory watching */
33
+ private sessionWatchers;
34
+ /** Map of sessionId -> working directory path */
35
+ private sessionDirs;
36
+ /** Debounce timers for rapid image creation (keyed by filePath) */
37
+ private debounceTimers;
38
+ /** Track which session owns each debounce timer (for cleanup) */
39
+ private timerToSession;
40
+ /** Per-session burst tracking: sessionId -> { count, windowStart } */
41
+ private burstTrackers;
42
+ /** Whether the watcher is currently running */
43
+ private _isRunning;
44
+ constructor();
45
+ /**
46
+ * Check if the watcher is currently running
47
+ */
48
+ isRunning(): boolean;
49
+ /**
50
+ * Start the image watcher.
51
+ * After calling start(), use watchSession() to add directories to monitor.
52
+ */
53
+ start(): void;
54
+ /**
55
+ * Stop the image watcher and clean up all resources.
56
+ */
57
+ stop(): void;
58
+ /**
59
+ * Start watching a session's working directory for new images.
60
+ *
61
+ * @param sessionId - Codeman session ID
62
+ * @param workingDir - Path to the session's working directory
63
+ */
64
+ watchSession(sessionId: string, workingDir: string): void;
65
+ /**
66
+ * Stop watching a session's working directory.
67
+ *
68
+ * @param sessionId - Codeman session ID to stop watching
69
+ */
70
+ unwatchSession(sessionId: string): void;
71
+ /**
72
+ * Get list of currently watched session IDs.
73
+ */
74
+ getWatchedSessions(): string[];
75
+ /**
76
+ * Handle a new file being detected.
77
+ * Verifies it's an image and emits the detection event.
78
+ */
79
+ private handleNewFile;
80
+ /**
81
+ * Emit the image:detected event with file metadata.
82
+ */
83
+ private emitImageDetected;
84
+ }
85
+ export declare const imageWatcher: ImageWatcher;
86
+ //# sourceMappingURL=image-watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-watcher.d.ts","sourceRoot":"","sources":["../src/image-watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIrD,MAAM,WAAW,kBAAkB;IACjC,gBAAgB,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACtD,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3D;AAwBD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAC5C,uEAAuE;IACvE,OAAO,CAAC,eAAe,CAAgC;IAEvD,iDAAiD;IACjD,OAAO,CAAC,WAAW,CAA6B;IAEhD,mEAAmE;IACnE,OAAO,CAAC,cAAc,CAAqC;IAE3D,iEAAiE;IACjE,OAAO,CAAC,cAAc,CAA6B;IAEnD,sEAAsE;IACtE,OAAO,CAAC,aAAa,CAA6D;IAElF,+CAA+C;IAC/C,OAAO,CAAC,UAAU,CAAS;;IAS3B;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;;OAGG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,IAAI,IAAI,IAAI;IAuBZ;;;;;OAKG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IA8DzD;;;;OAIG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IA+BvC;;OAEG;IACH,kBAAkB,IAAI,MAAM,EAAE;IAM9B;;;OAGG;IACH,OAAO,CAAC,aAAa;IA4CrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAuB1B;AAGD,eAAO,MAAM,YAAY,cAAqB,CAAC"}
@@ -0,0 +1,275 @@
1
+ /**
2
+ * @fileoverview Image Watcher - Detects new image files in session working directories
3
+ *
4
+ * Watches session working directories for new image files (screenshots, generated images)
5
+ * and emits events to trigger automatic popup display in the web UI.
6
+ *
7
+ * Uses chokidar for reliable cross-platform file watching with awaitWriteFinish
8
+ * to ensure files are fully written before emitting detection events.
9
+ */
10
+ import { EventEmitter } from 'node:events';
11
+ import { watch } from 'chokidar';
12
+ import { basename, extname, relative } from 'node:path';
13
+ import { statSync } from 'node:fs';
14
+ // ========== Constants ==========
15
+ /** Supported image file extensions (lowercase) */
16
+ const IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.svg']);
17
+ /** Time to wait for file writes to stabilize (ms) */
18
+ const STABILITY_THRESHOLD_MS = 500;
19
+ /** Poll interval for checking file write stability (ms) */
20
+ const POLL_INTERVAL_MS = 100;
21
+ /** Debounce delay for rapid image creation (ms) */
22
+ const DEBOUNCE_DELAY_MS = 200;
23
+ /** Max images emitted per session within the burst window before throttling */
24
+ const BURST_LIMIT = 20;
25
+ /** Time window for burst detection (ms) — resets after this period of quiet */
26
+ const BURST_WINDOW_MS = 10_000;
27
+ // ========== ImageWatcher Class ==========
28
+ /**
29
+ * Watches session working directories for new image files.
30
+ *
31
+ * Follows the SubagentWatcher pattern: extends EventEmitter, manages
32
+ * file watchers in Maps, emits typed events.
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const watcher = new ImageWatcher();
37
+ * watcher.on('image:detected', (event) => {
38
+ * console.log(`New image in session ${event.sessionId}: ${event.fileName}`);
39
+ * });
40
+ * watcher.watchSession('session-123', '/path/to/working/dir');
41
+ * ```
42
+ */
43
+ export class ImageWatcher extends EventEmitter {
44
+ /** Map of sessionId -> FSWatcher for per-session directory watching */
45
+ sessionWatchers = new Map();
46
+ /** Map of sessionId -> working directory path */
47
+ sessionDirs = new Map();
48
+ /** Debounce timers for rapid image creation (keyed by filePath) */
49
+ debounceTimers = new Map();
50
+ /** Track which session owns each debounce timer (for cleanup) */
51
+ timerToSession = new Map();
52
+ /** Per-session burst tracking: sessionId -> { count, windowStart } */
53
+ burstTrackers = new Map();
54
+ /** Whether the watcher is currently running */
55
+ _isRunning = false;
56
+ constructor() {
57
+ super();
58
+ this.setMaxListeners(50);
59
+ }
60
+ // ========== Public API ==========
61
+ /**
62
+ * Check if the watcher is currently running
63
+ */
64
+ isRunning() {
65
+ return this._isRunning;
66
+ }
67
+ /**
68
+ * Start the image watcher.
69
+ * After calling start(), use watchSession() to add directories to monitor.
70
+ */
71
+ start() {
72
+ if (this._isRunning)
73
+ return;
74
+ this._isRunning = true;
75
+ }
76
+ /**
77
+ * Stop the image watcher and clean up all resources.
78
+ */
79
+ stop() {
80
+ this._isRunning = false;
81
+ // Close all session watchers
82
+ for (const [sessionId, watcher] of this.sessionWatchers) {
83
+ try {
84
+ watcher.close();
85
+ }
86
+ catch (error) {
87
+ this.emit('image:error', error instanceof Error ? error : new Error(String(error)), sessionId);
88
+ }
89
+ }
90
+ this.sessionWatchers.clear();
91
+ this.sessionDirs.clear();
92
+ // Clear all debounce timers
93
+ for (const timer of this.debounceTimers.values()) {
94
+ clearTimeout(timer);
95
+ }
96
+ this.debounceTimers.clear();
97
+ this.timerToSession.clear();
98
+ this.burstTrackers.clear();
99
+ }
100
+ /**
101
+ * Start watching a session's working directory for new images.
102
+ *
103
+ * @param sessionId - Codeman session ID
104
+ * @param workingDir - Path to the session's working directory
105
+ */
106
+ watchSession(sessionId, workingDir) {
107
+ if (!this._isRunning) {
108
+ this.start();
109
+ }
110
+ // Don't double-watch the same session
111
+ if (this.sessionWatchers.has(sessionId)) {
112
+ // If working directory changed, unwatch old and watch new
113
+ if (this.sessionDirs.get(sessionId) !== workingDir) {
114
+ this.unwatchSession(sessionId);
115
+ }
116
+ else {
117
+ return;
118
+ }
119
+ }
120
+ this.sessionDirs.set(sessionId, workingDir);
121
+ try {
122
+ // Create chokidar watcher for the directory
123
+ const watcher = watch(workingDir, {
124
+ // Only detect NEW files, not existing ones
125
+ ignoreInitial: true,
126
+ // Wait for file writes to stabilize before emitting
127
+ awaitWriteFinish: {
128
+ stabilityThreshold: STABILITY_THRESHOLD_MS,
129
+ pollInterval: POLL_INTERVAL_MS,
130
+ },
131
+ // Watch all subdirectories (images may be saved in src/, assets/, etc.)
132
+ // Ignore common heavy directories for performance
133
+ ignored: (path) => {
134
+ // Skip node_modules, .git, and other heavy directories
135
+ if (path.includes('/node_modules/') ||
136
+ path.includes('/.git/') ||
137
+ path.includes('/dist/') ||
138
+ path.includes('/.next/')) {
139
+ return true;
140
+ }
141
+ const ext = extname(path).toLowerCase();
142
+ // Don't ignore directories (needed for watching to work)
143
+ // Ignore files that aren't images
144
+ return ext !== '' && !IMAGE_EXTENSIONS.has(ext);
145
+ },
146
+ });
147
+ // Handle new file detection
148
+ watcher.on('add', (filePath) => {
149
+ this.handleNewFile(sessionId, filePath);
150
+ });
151
+ // Handle watcher errors
152
+ watcher.on('error', (error) => {
153
+ this.emit('image:error', error, sessionId);
154
+ });
155
+ this.sessionWatchers.set(sessionId, watcher);
156
+ }
157
+ catch (error) {
158
+ this.emit('image:error', error instanceof Error ? error : new Error(String(error)), sessionId);
159
+ }
160
+ }
161
+ /**
162
+ * Stop watching a session's working directory.
163
+ *
164
+ * @param sessionId - Codeman session ID to stop watching
165
+ */
166
+ unwatchSession(sessionId) {
167
+ const watcher = this.sessionWatchers.get(sessionId);
168
+ if (watcher) {
169
+ try {
170
+ watcher.close();
171
+ }
172
+ catch (error) {
173
+ this.emit('image:error', error instanceof Error ? error : new Error(String(error)), sessionId);
174
+ }
175
+ this.sessionWatchers.delete(sessionId);
176
+ }
177
+ this.sessionDirs.delete(sessionId);
178
+ // Clear any pending debounce timers for this session
179
+ // Collect keys first to avoid iterator invalidation during deletion
180
+ const toDelete = [];
181
+ for (const [filePath, ownerId] of this.timerToSession) {
182
+ if (ownerId === sessionId) {
183
+ toDelete.push(filePath);
184
+ }
185
+ }
186
+ for (const filePath of toDelete) {
187
+ const timer = this.debounceTimers.get(filePath);
188
+ if (timer) {
189
+ clearTimeout(timer);
190
+ this.debounceTimers.delete(filePath);
191
+ }
192
+ this.timerToSession.delete(filePath);
193
+ }
194
+ this.burstTrackers.delete(sessionId);
195
+ }
196
+ /**
197
+ * Get list of currently watched session IDs.
198
+ */
199
+ getWatchedSessions() {
200
+ return Array.from(this.sessionWatchers.keys());
201
+ }
202
+ // ========== Private Methods ==========
203
+ /**
204
+ * Handle a new file being detected.
205
+ * Verifies it's an image and emits the detection event.
206
+ */
207
+ handleNewFile(sessionId, filePath) {
208
+ const ext = extname(filePath).toLowerCase();
209
+ // Double-check it's an image extension
210
+ if (!IMAGE_EXTENSIONS.has(ext)) {
211
+ return;
212
+ }
213
+ // Burst limit: skip if too many images detected for this session in a short window
214
+ const now = Date.now();
215
+ let burst = this.burstTrackers.get(sessionId);
216
+ if (burst) {
217
+ if (now - burst.windowStart > BURST_WINDOW_MS) {
218
+ // Window expired, reset
219
+ burst = { count: 0, windowStart: now };
220
+ this.burstTrackers.set(sessionId, burst);
221
+ }
222
+ if (burst.count >= BURST_LIMIT) {
223
+ return; // Throttled — too many images in this window
224
+ }
225
+ }
226
+ else {
227
+ burst = { count: 0, windowStart: now };
228
+ this.burstTrackers.set(sessionId, burst);
229
+ }
230
+ // Debounce rapid file creation (e.g., multiple screenshots quickly)
231
+ const existingTimer = this.debounceTimers.get(filePath);
232
+ if (existingTimer) {
233
+ clearTimeout(existingTimer);
234
+ }
235
+ const timer = setTimeout(() => {
236
+ this.debounceTimers.delete(filePath);
237
+ this.timerToSession.delete(filePath);
238
+ this.emitImageDetected(sessionId, filePath);
239
+ // Increment burst count on actual emission (not on detection)
240
+ const b = this.burstTrackers.get(sessionId);
241
+ if (b)
242
+ b.count++;
243
+ }, DEBOUNCE_DELAY_MS);
244
+ this.debounceTimers.set(filePath, timer);
245
+ this.timerToSession.set(filePath, sessionId);
246
+ }
247
+ /**
248
+ * Emit the image:detected event with file metadata.
249
+ */
250
+ emitImageDetected(sessionId, filePath) {
251
+ try {
252
+ const stat = statSync(filePath);
253
+ const fileName = basename(filePath);
254
+ const workingDir = this.sessionDirs.get(sessionId);
255
+ // Compute relative path from working directory (for file-raw endpoint)
256
+ const relativePath = workingDir ? relative(workingDir, filePath) : fileName;
257
+ const event = {
258
+ sessionId,
259
+ filePath,
260
+ relativePath,
261
+ fileName,
262
+ timestamp: Date.now(),
263
+ size: stat.size,
264
+ };
265
+ this.emit('image:detected', event);
266
+ }
267
+ catch (error) {
268
+ // File may have been deleted between detection and stat
269
+ this.emit('image:error', error instanceof Error ? error : new Error(String(error)), sessionId);
270
+ }
271
+ }
272
+ }
273
+ // Export singleton instance for convenience
274
+ export const imageWatcher = new ImageWatcher();
275
+ //# sourceMappingURL=image-watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-watcher.js","sourceRoot":"","sources":["../src/image-watcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @fileoverview Codeman CLI entry point
4
+ *
5
+ * This is the main executable entry point for the Codeman CLI.
6
+ * It sets up global error handlers and invokes the CLI parser.
7
+ *
8
+ * @module index
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG"}
package/dist/index.js ADDED
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @fileoverview Codeman CLI entry point
4
+ *
5
+ * This is the main executable entry point for the Codeman CLI.
6
+ * It sets up global error handlers and invokes the CLI parser.
7
+ *
8
+ * @module index
9
+ */
10
+ import { program } from './cli.js';
11
+ // Detect if we're running the web server (long-lived process)
12
+ // In web mode, we should NOT exit on transient errors — log and continue
13
+ const isWebMode = process.argv.includes('web');
14
+ // Track consecutive unhandled errors in web mode — restart after too many
15
+ let consecutiveErrors = 0;
16
+ const MAX_CONSECUTIVE_ERRORS = 5;
17
+ const ERROR_RESET_MS = 60000; // Reset counter after 1 minute of no errors
18
+ let errorResetTimer = null;
19
+ function trackError() {
20
+ consecutiveErrors++;
21
+ if (errorResetTimer)
22
+ clearTimeout(errorResetTimer);
23
+ errorResetTimer = setTimeout(() => {
24
+ consecutiveErrors = 0;
25
+ }, ERROR_RESET_MS);
26
+ if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
27
+ console.error(`[FATAL] ${MAX_CONSECUTIVE_ERRORS} consecutive unhandled errors — exiting for systemd restart`);
28
+ process.exit(1);
29
+ }
30
+ }
31
+ // Handle uncaught errors
32
+ process.on('uncaughtException', (err) => {
33
+ console.error('Uncaught exception:', err.message);
34
+ if (isWebMode) {
35
+ console.error('[RECOVERED] Server continuing after uncaught exception:', err.stack);
36
+ trackError();
37
+ }
38
+ else {
39
+ process.exit(1);
40
+ }
41
+ });
42
+ process.on('unhandledRejection', (reason) => {
43
+ console.error('Unhandled rejection:', reason);
44
+ if (isWebMode) {
45
+ console.error('[RECOVERED] Server continuing after unhandled rejection');
46
+ trackError();
47
+ }
48
+ else {
49
+ process.exit(1);
50
+ }
51
+ });
52
+ // Run CLI
53
+ program.parse();
54
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,8DAA8D;AAC9D,yEAAyE;AACzE,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAE/C,0EAA0E;AAC1E,IAAI,iBAAiB,GAAG,CAAC,CAAC;AAC1B,MAAM,sBAAsB,GAAG,CAAC,CAAC;AACjC,MAAM,cAAc,GAAG,KAAK,CAAC,CAAC,4CAA4C;AAC1E,IAAI,eAAe,GAAyC,IAAI,CAAC;AAEjE,SAAS,UAAU;IACjB,iBAAiB,EAAE,CAAC;IACpB,IAAI,eAAe;QAAE,YAAY,CAAC,eAAe,CAAC,CAAC;IACnD,eAAe,GAAG,UAAU,CAAC,GAAG,EAAE;QAChC,iBAAiB,GAAG,CAAC,CAAC;IACxB,CAAC,EAAE,cAAc,CAAC,CAAC;IAEnB,IAAI,iBAAiB,IAAI,sBAAsB,EAAE,CAAC;QAChD,OAAO,CAAC,KAAK,CAAC,WAAW,sBAAsB,6DAA6D,CAAC,CAAC;QAC9G,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,yBAAyB;AACzB,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,GAAG,EAAE,EAAE;IACtC,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IAClD,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,yDAAyD,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QACpF,UAAU,EAAE,CAAC;IACf,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,MAAM,EAAE,EAAE;IAC1C,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC;IAC9C,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,yDAAyD,CAAC,CAAC;QACzE,UAAU,EAAE,CAAC;IACf,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,UAAU;AACV,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @fileoverview Factory for creating the terminal multiplexer (tmux).
3
+ *
4
+ * @module mux-factory
5
+ */
6
+ import type { TerminalMultiplexer } from './mux-interface.js';
7
+ /**
8
+ * Create a TerminalMultiplexer instance.
9
+ *
10
+ * Requires tmux to be installed. Throws with install instructions if not found.
11
+ */
12
+ export declare function createMultiplexer(): TerminalMultiplexer;
13
+ //# sourceMappingURL=mux-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mux-factory.d.ts","sourceRoot":"","sources":["../src/mux-factory.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAG9D;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,mBAAmB,CAOvD"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @fileoverview Factory for creating the terminal multiplexer (tmux).
3
+ *
4
+ * @module mux-factory
5
+ */
6
+ import { TmuxManager } from './tmux-manager.js';
7
+ /**
8
+ * Create a TerminalMultiplexer instance.
9
+ *
10
+ * Requires tmux to be installed. Throws with install instructions if not found.
11
+ */
12
+ export function createMultiplexer() {
13
+ if (!TmuxManager.isTmuxAvailable()) {
14
+ throw new Error('tmux not found. Install: sudo apt install tmux');
15
+ }
16
+ console.log('[MuxFactory] Using tmux backend');
17
+ return new TmuxManager();
18
+ }
19
+ //# sourceMappingURL=mux-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mux-factory.js","sourceRoot":"","sources":["../src/mux-factory.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;;;GAIG;AACH,MAAM,UAAU,iBAAiB;IAC/B,IAAI,CAAC,WAAW,CAAC,eAAe,EAAE,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;IAC/C,OAAO,IAAI,WAAW,EAAE,CAAC;AAC3B,CAAC"}