autodev-cli 1.4.0 → 1.4.3

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 (237) hide show
  1. package/bin/autodev.js +0 -0
  2. package/out/agentBackup/archive.d.ts +44 -0
  3. package/out/agentBackup/archive.js +131 -0
  4. package/out/agentBackup/archive.js.map +1 -0
  5. package/out/agentBackup/export.d.ts +18 -0
  6. package/out/agentBackup/export.js +92 -0
  7. package/out/agentBackup/export.js.map +1 -0
  8. package/out/agentBackup/import.d.ts +21 -0
  9. package/out/agentBackup/import.js +40 -0
  10. package/out/agentBackup/import.js.map +1 -0
  11. package/out/agentBackup/index.d.ts +6 -0
  12. package/out/agentBackup/index.js +11 -0
  13. package/out/agentBackup/index.js.map +1 -0
  14. package/out/agentBackup/layout.d.ts +30 -0
  15. package/out/agentBackup/layout.js +126 -0
  16. package/out/agentBackup/layout.js.map +1 -0
  17. package/out/agentBackup/manifest.d.ts +24 -0
  18. package/out/agentBackup/manifest.js +70 -0
  19. package/out/agentBackup/manifest.js.map +1 -0
  20. package/out/agentBackup/opencodeDb.d.ts +20 -0
  21. package/out/agentBackup/opencodeDb.js +213 -0
  22. package/out/agentBackup/opencodeDb.js.map +1 -0
  23. package/out/agentBackup/sessionProviders.d.ts +35 -0
  24. package/out/agentBackup/sessionProviders.js +263 -0
  25. package/out/agentBackup/sessionProviders.js.map +1 -0
  26. package/out/agentBackup/upload.d.ts +9 -0
  27. package/out/agentBackup/upload.js +121 -0
  28. package/out/agentBackup/upload.js.map +1 -0
  29. package/out/cli.d.ts +1 -0
  30. package/out/cli.js +8 -0
  31. package/out/cli.js.map +1 -1
  32. package/out/cliExit.d.ts +34 -0
  33. package/out/cliExit.js +159 -0
  34. package/out/cliExit.js.map +1 -0
  35. package/out/commands/config.d.ts +2 -0
  36. package/out/commands/config.js +7 -7
  37. package/out/commands/config.js.map +1 -1
  38. package/out/commands/connect.d.ts +2 -0
  39. package/out/commands/connect.js +11 -0
  40. package/out/commands/connect.js.map +1 -1
  41. package/out/commands/export.d.ts +2 -0
  42. package/out/commands/export.js +79 -0
  43. package/out/commands/export.js.map +1 -0
  44. package/out/commands/import.d.ts +2 -0
  45. package/out/commands/import.js +92 -0
  46. package/out/commands/import.js.map +1 -0
  47. package/out/commands/init.d.ts +16 -0
  48. package/out/commands/init.js +9 -5
  49. package/out/commands/init.js.map +1 -1
  50. package/out/commands/resume.d.ts +2 -0
  51. package/out/commands/resume.js +65 -0
  52. package/out/commands/resume.js.map +1 -0
  53. package/out/commands/sessions.d.ts +2 -0
  54. package/out/commands/sessions.js +64 -0
  55. package/out/commands/sessions.js.map +1 -0
  56. package/out/commands/start.d.ts +2 -0
  57. package/out/commands/start.js +40 -7
  58. package/out/commands/start.js.map +1 -1
  59. package/out/commands/status.d.ts +2 -0
  60. package/out/commands/status.js +3 -3
  61. package/out/commands/status.js.map +1 -1
  62. package/out/commands/tailOutput.d.ts +12 -0
  63. package/out/commands/up.d.ts +3 -0
  64. package/out/configManager.d.ts +42 -0
  65. package/out/configManager.js +430 -0
  66. package/out/configManager.js.map +1 -0
  67. package/out/connect.d.ts +4 -0
  68. package/out/connect.js +7 -7
  69. package/out/connect.js.map +1 -1
  70. package/out/core/adapters.d.ts +34 -0
  71. package/out/core/adapters.js +84 -0
  72. package/out/core/adapters.js.map +1 -0
  73. package/out/core/commandHelpers.d.ts +12 -0
  74. package/out/core/commandHelpers.js +96 -0
  75. package/out/core/commandHelpers.js.map +1 -0
  76. package/out/core/projectMcp.d.ts +25 -0
  77. package/out/core/projectMcp.js +144 -0
  78. package/out/core/projectMcp.js.map +1 -0
  79. package/out/core/provider/BaseProvider.d.ts +14 -0
  80. package/out/core/provider/BaseProvider.js +25 -0
  81. package/out/core/provider/BaseProvider.js.map +1 -0
  82. package/out/core/provider/ProviderRegistry.d.ts +12 -0
  83. package/out/core/provider/ProviderRegistry.js +40 -0
  84. package/out/core/provider/ProviderRegistry.js.map +1 -0
  85. package/out/core/provider/contract.d.ts +62 -0
  86. package/out/core/provider/contract.js +9 -0
  87. package/out/core/provider/contract.js.map +1 -0
  88. package/out/core/provider/implementations.d.ts +54 -0
  89. package/out/core/provider/implementations.js +147 -0
  90. package/out/core/provider/implementations.js.map +1 -0
  91. package/out/core/settingsLoader.d.ts +221 -0
  92. package/out/core/settingsLoader.js +176 -0
  93. package/out/core/settingsLoader.js.map +1 -0
  94. package/out/discordGateway.d.ts +26 -0
  95. package/out/discordGateway.js +230 -0
  96. package/out/discordGateway.js.map +1 -0
  97. package/out/discordPoller.d.ts +28 -0
  98. package/out/discordPoller.js +247 -0
  99. package/out/discordPoller.js.map +1 -0
  100. package/out/dispatcher.d.ts +12 -0
  101. package/out/dispatcher.js +214 -0
  102. package/out/dispatcher.js.map +1 -0
  103. package/out/emailPoller.d.ts +42 -0
  104. package/out/emailPoller.js +221 -0
  105. package/out/emailPoller.js.map +1 -0
  106. package/out/git/gitService.d.ts +36 -0
  107. package/out/git/gitService.js +165 -0
  108. package/out/git/gitService.js.map +1 -0
  109. package/out/hookEventNormalizer.d.ts +39 -0
  110. package/out/hookEventNormalizer.js +397 -0
  111. package/out/hookEventNormalizer.js.map +1 -0
  112. package/out/hooksManager.d.ts +25 -0
  113. package/out/hooksManager.js +471 -0
  114. package/out/hooksManager.js.map +1 -0
  115. package/out/launchIde.d.ts +14 -0
  116. package/out/logger.d.ts +12 -0
  117. package/out/mcpEmailTest.d.ts +29 -0
  118. package/out/mcpEmailTest.js +245 -0
  119. package/out/mcpEmailTest.js.map +1 -0
  120. package/out/mcpInstallCheck.d.ts +23 -0
  121. package/out/mcpInstallCheck.js +219 -0
  122. package/out/mcpInstallCheck.js.map +1 -0
  123. package/out/mcpManager.d.ts +35 -0
  124. package/out/mcpManager.js +371 -0
  125. package/out/mcpManager.js.map +1 -0
  126. package/out/messageBuilder.d.ts +54 -0
  127. package/out/messageBuilder.js +373 -0
  128. package/out/messageBuilder.js.map +1 -0
  129. package/out/openCodeHooksManager.d.ts +23 -0
  130. package/out/openCodeHooksManager.js +511 -0
  131. package/out/openCodeHooksManager.js.map +1 -0
  132. package/out/periodicActions.d.ts +63 -0
  133. package/out/periodicActions.js +237 -0
  134. package/out/periodicActions.js.map +1 -0
  135. package/out/profileBuilder.d.ts +29 -0
  136. package/out/profileBuilder.js +366 -0
  137. package/out/profileBuilder.js.map +1 -0
  138. package/out/prompt.d.ts +12 -0
  139. package/out/prompt.js +18 -0
  140. package/out/prompt.js.map +1 -0
  141. package/out/protocolSections.d.ts +26 -0
  142. package/out/protocolSections.js +209 -0
  143. package/out/protocolSections.js.map +1 -0
  144. package/out/providers/claudeCliProvider.d.ts +71 -0
  145. package/out/providers/claudeCliProvider.js +425 -0
  146. package/out/providers/claudeCliProvider.js.map +1 -0
  147. package/out/providers/claudeTuiProvider.d.ts +23 -0
  148. package/out/providers/claudeTuiProvider.js +296 -0
  149. package/out/providers/claudeTuiProvider.js.map +1 -0
  150. package/out/providers/copilotCliProvider.d.ts +16 -0
  151. package/out/providers/copilotCliProvider.js +44 -0
  152. package/out/providers/copilotCliProvider.js.map +1 -0
  153. package/out/providers/copilotSdkProvider.d.ts +12 -0
  154. package/out/providers/copilotSdkProvider.js +445 -0
  155. package/out/providers/copilotSdkProvider.js.map +1 -0
  156. package/out/providers/grokTuiProvider.d.ts +14 -0
  157. package/out/providers/grokTuiProvider.js +271 -0
  158. package/out/providers/grokTuiProvider.js.map +1 -0
  159. package/out/providers/opencodeCliProvider.d.ts +29 -0
  160. package/out/providers/opencodeCliProvider.js +199 -0
  161. package/out/providers/opencodeCliProvider.js.map +1 -0
  162. package/out/providers/opencodeSdkProvider.d.ts +22 -0
  163. package/out/providers/opencodeSdkProvider.js +557 -0
  164. package/out/providers/opencodeSdkProvider.js.map +1 -0
  165. package/out/providers.d.ts +9 -0
  166. package/out/providers.js +44 -0
  167. package/out/providers.js.map +1 -0
  168. package/out/rateLimit.d.ts +18 -0
  169. package/out/rateLimit.js +90 -0
  170. package/out/rateLimit.js.map +1 -0
  171. package/out/rdp/auth.d.ts +55 -0
  172. package/out/rdp/auth.js +197 -0
  173. package/out/rdp/auth.js.map +1 -0
  174. package/out/rdp/bridge.d.ts +86 -0
  175. package/out/rdp/bridge.js +1398 -0
  176. package/out/rdp/bridge.js.map +1 -0
  177. package/out/rdp/constants.d.ts +86 -0
  178. package/out/rdp/constants.js +182 -0
  179. package/out/rdp/constants.js.map +1 -0
  180. package/out/rdp/index.d.ts +7 -0
  181. package/out/rdp/index.js +14 -0
  182. package/out/rdp/index.js.map +1 -0
  183. package/out/rdp/session.d.ts +30 -0
  184. package/out/rdp/session.js +196 -0
  185. package/out/rdp/session.js.map +1 -0
  186. package/out/rdp/types.d.ts +27 -0
  187. package/out/rdp/types.js +6 -0
  188. package/out/rdp/types.js.map +1 -0
  189. package/out/sdk/index.d.ts +22 -0
  190. package/out/sdk/index.js +81 -0
  191. package/out/sdk/index.js.map +1 -0
  192. package/out/sessionState.d.ts +54 -0
  193. package/out/sessionState.js +284 -0
  194. package/out/sessionState.js.map +1 -0
  195. package/out/sessions.d.ts +11 -0
  196. package/out/sessions.js +32 -0
  197. package/out/sessions.js.map +1 -0
  198. package/out/taskLoop.d.ts +152 -0
  199. package/out/taskLoop.js +2505 -0
  200. package/out/taskLoop.js.map +1 -0
  201. package/out/todo.d.ts +42 -0
  202. package/out/todo.js +311 -0
  203. package/out/todo.js.map +1 -0
  204. package/out/todoWriteManager.d.ts +26 -0
  205. package/out/todoWriteManager.js +44 -0
  206. package/out/todoWriteManager.js.map +1 -0
  207. package/out/vnc/auth.d.ts +52 -0
  208. package/out/vnc/auth.js +181 -0
  209. package/out/vnc/auth.js.map +1 -0
  210. package/out/vnc/bridge.d.ts +40 -0
  211. package/out/vnc/bridge.js +540 -0
  212. package/out/vnc/bridge.js.map +1 -0
  213. package/out/vnc/constants.d.ts +8 -0
  214. package/out/vnc/constants.js +34 -0
  215. package/out/vnc/constants.js.map +1 -0
  216. package/out/vnc/des.d.ts +6 -0
  217. package/out/vnc/des.js +93 -0
  218. package/out/vnc/des.js.map +1 -0
  219. package/out/vnc/index.d.ts +7 -0
  220. package/out/vnc/index.js +13 -0
  221. package/out/vnc/index.js.map +1 -0
  222. package/out/vnc/session.d.ts +18 -0
  223. package/out/vnc/session.js +193 -0
  224. package/out/vnc/session.js.map +1 -0
  225. package/out/vnc/types.d.ts +16 -0
  226. package/out/vnc/types.js +6 -0
  227. package/out/vnc/types.js.map +1 -0
  228. package/out/webSocketPoller.d.ts +95 -0
  229. package/out/webSocketPoller.js +986 -0
  230. package/out/webSocketPoller.js.map +1 -0
  231. package/out/webhook.d.ts +37 -0
  232. package/out/webhook.js +265 -0
  233. package/out/webhook.js.map +1 -0
  234. package/out/webhookPoller.d.ts +40 -0
  235. package/out/webhookPoller.js +378 -0
  236. package/out/webhookPoller.js.map +1 -0
  237. package/package.json +54 -41
@@ -0,0 +1,511 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.isOpenCodeHooksInstalled = isOpenCodeHooksInstalled;
37
+ exports.installOpenCodeHooks = installOpenCodeHooks;
38
+ exports.isOpenCodeCliActive = isOpenCodeCliActive;
39
+ exports.openCodeExitedCleanly = openCodeExitedCleanly;
40
+ exports.getOpenCodeSessionIdFromHooks = getOpenCodeSessionIdFromHooks;
41
+ exports.uninstallOpenCodeHooks = uninstallOpenCodeHooks;
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ // ---------------------------------------------------------------------------
45
+ // OpenCode hooks manager — installs/removes an OpenCode plugin that appends
46
+ // hook events to <workspaceRoot>/.autodev/hooks-events.jsonl in the same
47
+ // format as the Claude Code hooks, so the task loop can stream them to
48
+ // Pixel Office.
49
+ //
50
+ // The plugin is placed at <workspaceRoot>/.opencode/plugins/autodev-hooks.ts
51
+ // OpenCode discovers plugins in that directory automatically.
52
+ // ---------------------------------------------------------------------------
53
+ const PLUGIN_FILENAME = 'autodev-hooks.ts';
54
+ const PLUGIN_MARKER = '// __autodev_opencode_hooks__';
55
+ function pluginDir(workspaceRoot) {
56
+ return path.join(workspaceRoot, '.opencode', 'plugins');
57
+ }
58
+ function pluginPath(workspaceRoot) {
59
+ return path.join(pluginDir(workspaceRoot), PLUGIN_FILENAME);
60
+ }
61
+ // ---------------------------------------------------------------------------
62
+ // Plugin content — TypeScript executed by OpenCode/Bun at runtime.
63
+ // The JSONL path is baked in at install time (workspace-scoped, forward
64
+ // slashes only so Bun/Windows path handling doesn't break the string).
65
+ // ---------------------------------------------------------------------------
66
+ function buildPluginContent(workspaceRoot) {
67
+ // Bake in the workspace-scoped JSONL dir with forward slashes so the
68
+ // plugin always writes to the right place regardless of the process cwd.
69
+ const autodevDir = JSON.stringify(path.join(workspaceRoot, '.autodev').replace(/\\/g, '/'));
70
+ return `${PLUGIN_MARKER}
71
+ // AutoDev hooks plugin for OpenCode — auto-generated, do not edit.
72
+ // Streams tool/session events to Pixel Office via <workspaceRoot>/.autodev/
73
+ import { appendFileSync, mkdirSync, existsSync } from 'fs';
74
+ import { join } from 'path';
75
+
76
+ // Dir is baked in at install time (workspace-scoped, always forward slashes)
77
+ const AUTODEV_DIR: string = ${autodevDir};
78
+ // Global file for session-ID discovery; per-session files for state tracking
79
+ const GLOBAL_JSONL = join(AUTODEV_DIR, 'hooks-events.jsonl');
80
+
81
+ function sessionJsonlPath(sessionId: string | null): string | null {
82
+ if (!sessionId) { return null; }
83
+ // Replace chars that are unsafe in filenames but keep the ID readable
84
+ const safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, '_');
85
+ return join(AUTODEV_DIR, \`hooks-events-\${safe}.jsonl\`);
86
+ }
87
+
88
+ // High-frequency / large-payload events we intentionally skip in the generic catch-all
89
+ // (tool events have dedicated hooks; these are too noisy or already handled explicitly)
90
+ const SKIP_EVENTS = new Set([
91
+ 'message.part.delta',
92
+ 'message.part.removed',
93
+ 'message.part.updated', // replaced by session.next.text.ended (simpler, same data)
94
+ 'message.updated', // role-tracking no longer needed
95
+ 'message.removed',
96
+ 'session.diff',
97
+ // Explicit named hooks — don't double-log
98
+ 'tool.execute.before',
99
+ 'tool.execute.after',
100
+ 'permission.asked',
101
+ 'permission.replied',
102
+ 'tui.prompt.append',
103
+ 'tui.command.execute',
104
+ 'tui.toast.show',
105
+ 'command.executed',
106
+ 'file.edited',
107
+ // session.next.* lifecycle markers with no payload — skip the noisy ones
108
+ 'session.next.step.started',
109
+ 'session.next.reasoning.started',
110
+ 'session.next.text.started',
111
+ 'session.next.tool.input.started',
112
+ 'session.next.tool.input.ended',
113
+ 'session.next.tool.called',
114
+ 'session.next.tool.success',
115
+ // session.next.tool.failed is NOT skipped — captures error message for failed tool calls
116
+ ]);
117
+
118
+ const SESSION_MAP: Record<string, string> = {
119
+ 'session.created': 'SessionStart',
120
+ 'session.idle': 'Stop',
121
+ 'session.error': 'StopFailure',
122
+ 'session.deleted': 'SessionEnd',
123
+ 'session.compacted': 'PostCompact',
124
+ 'session.updated': 'SessionStatus',
125
+ 'session.status': 'SessionStatus',
126
+ 'message.removed': 'MessageRemoved',
127
+ 'todo.updated': 'TaskCreated',
128
+ 'command.executed': 'CommandExecuted',
129
+ 'file.edited': 'FileEdited',
130
+ 'file.watcher.updated': 'FileWatcherUpdated',
131
+ 'permission.asked': 'PermissionAsked',
132
+ 'permission.replied': 'PermissionReplied',
133
+ 'server.connected': 'ServerConnected',
134
+ 'lsp.updated': 'LspUpdated',
135
+ 'installation.updated': 'InstallationUpdated',
136
+ 'tui.prompt.append': 'TuiPromptAppend',
137
+ 'tui.command.execute':'TuiCommandExecute',
138
+ 'tui.toast.show': 'TuiToastShow',
139
+ // session.next.* meaningful events
140
+ 'session.next.prompted': 'UserMessage',
141
+ 'session.next.step.ended': 'StepEnded',
142
+ 'session.next.model.switched': 'ModelSwitched',
143
+ 'session.next.agent.switched': 'AgentSwitched',
144
+ 'session.next.reasoning.ended':'Reasoning',
145
+ };
146
+
147
+ function appendEvent(ev: Record<string, unknown>): void {
148
+ try {
149
+ if (!existsSync(AUTODEV_DIR)) { mkdirSync(AUTODEV_DIR, { recursive: true }); }
150
+ ev['timestamp'] = new Date().toISOString();
151
+ const line = JSON.stringify(ev) + '\\n';
152
+ // Always write to global file (used for session-ID discovery)
153
+ appendFileSync(GLOBAL_JSONL, line, 'utf8');
154
+ // Also write to per-session file when we know the session ID
155
+ const sid = typeof ev['session_id'] === 'string' ? ev['session_id'] as string : null;
156
+ const perFile = sessionJsonlPath(sid);
157
+ if (perFile) { appendFileSync(perFile, line, 'utf8'); }
158
+ } catch { }
159
+ }
160
+
161
+ // Helper to extract session ID from various event shapes
162
+ function extractSessionId(input: any): string | null {
163
+ return input?.sessionID ?? input?.session_id ?? input?.properties?.sessionID ?? null;
164
+ }
165
+
166
+ // Accumulated assistant text per session.
167
+ // session.next.text.ended fires once per step with the complete generated text.
168
+ // Multi-step sessions accumulate across steps and flush as AgentMessage on session.idle.
169
+ const sessionText = new Map<string, string>();
170
+
171
+ export const AutodevHooksPlugin = async () => ({
172
+ // -------------------------------------------------------------------------
173
+ // Tool lifecycle — explicit named hooks (these do NOT fire via generic 'event')
174
+ // -------------------------------------------------------------------------
175
+ 'tool.execute.before': async (input: any, output?: any) => {
176
+ appendEvent({
177
+ hook_event_name: 'PreToolUse',
178
+ provider: 'opencode',
179
+ tool_name: input?.tool ?? 'unknown',
180
+ tool_input: output?.args ?? input?.args ?? null,
181
+ session_id: extractSessionId(input),
182
+ });
183
+ },
184
+
185
+ 'tool.execute.after': async (input: any, output?: any) => {
186
+ const rawOut = output?.output ?? output?.result ?? output?.text;
187
+ const outText = typeof rawOut === 'string' ? rawOut.slice(0, 400) : null;
188
+ appendEvent({
189
+ hook_event_name: 'PostToolUse',
190
+ provider: 'opencode',
191
+ tool_name: input?.tool ?? 'unknown',
192
+ tool_input: input?.args ?? null,
193
+ tool_output: outText != null ? { title: output?.title ?? null, text: outText } : null,
194
+ session_id: extractSessionId(input),
195
+ });
196
+ },
197
+
198
+ // -------------------------------------------------------------------------
199
+ // Permission hooks — explicit so we always capture even if generic 'event'
200
+ // doesn't fire for them. Critical for detecting blocked/waiting states.
201
+ // -------------------------------------------------------------------------
202
+ 'permission.asked': async (input: any, output?: any) => {
203
+ appendEvent({
204
+ hook_event_name: 'PermissionAsked',
205
+ provider: 'opencode',
206
+ session_id: extractSessionId(input),
207
+ tool_name: input?.tool ?? null,
208
+ tool_input: input?.args ?? null,
209
+ message: input?.message ?? input?.description ?? null,
210
+ });
211
+ },
212
+
213
+ 'permission.replied': async (input: any, output?: any) => {
214
+ appendEvent({
215
+ hook_event_name: 'PermissionReplied',
216
+ provider: 'opencode',
217
+ session_id: extractSessionId(input),
218
+ tool_name: input?.tool ?? null,
219
+ granted: input?.granted ?? output?.granted ?? null,
220
+ });
221
+ },
222
+
223
+ // -------------------------------------------------------------------------
224
+ // TUI events — explicit named hooks
225
+ // -------------------------------------------------------------------------
226
+ 'tui.prompt.append': async (input: any) => {
227
+ appendEvent({
228
+ hook_event_name: 'TuiPromptAppend',
229
+ provider: 'opencode',
230
+ session_id: extractSessionId(input),
231
+ message: input?.text ?? null,
232
+ });
233
+ },
234
+
235
+ 'tui.command.execute': async (input: any) => {
236
+ appendEvent({
237
+ hook_event_name: 'TuiCommandExecute',
238
+ provider: 'opencode',
239
+ session_id: extractSessionId(input),
240
+ message: input?.command ?? input?.name ?? null,
241
+ });
242
+ },
243
+
244
+ 'tui.toast.show': async (input: any) => {
245
+ appendEvent({
246
+ hook_event_name: 'TuiToastShow',
247
+ provider: 'opencode',
248
+ session_id: extractSessionId(input),
249
+ message: input?.message ?? input?.text ?? null,
250
+ });
251
+ },
252
+
253
+ // -------------------------------------------------------------------------
254
+ // Command + file events — explicit named hooks
255
+ // -------------------------------------------------------------------------
256
+ 'command.executed': async (input: any) => {
257
+ appendEvent({
258
+ hook_event_name: 'CommandExecuted',
259
+ provider: 'opencode',
260
+ session_id: extractSessionId(input),
261
+ message: input?.command ?? input?.name ?? null,
262
+ });
263
+ },
264
+
265
+ 'file.edited': async (input: any) => {
266
+ appendEvent({
267
+ hook_event_name: 'FileEdited',
268
+ provider: 'opencode',
269
+ session_id: extractSessionId(input),
270
+ message: input?.file ?? input?.path ?? null,
271
+ });
272
+ },
273
+
274
+ // -------------------------------------------------------------------------
275
+ // Generic catch-all for remaining events (session/message/todo/lsp/server…)
276
+ // SKIP_EVENTS excludes high-noise events and events already handled above.
277
+ // message.part.updated (type:'text') is handled here to accumulate the AI's
278
+ // response text; it's flushed as a single AgentMessage on session.idle.
279
+ // -------------------------------------------------------------------------
280
+ 'event': async (ctx: any) => {
281
+ const evt = ctx?.event ?? ctx ?? {};
282
+ const t: string = evt?.type ?? '';
283
+
284
+ if (!t || SKIP_EVENTS.has(t)) { return; }
285
+
286
+ const props = evt?.properties ?? {};
287
+ const sessionId = props?.sessionID ?? props?.id ?? null;
288
+ const errMsg = props?.error?.message ?? null;
289
+
290
+ // --- session.next.text.ended: accumulate assistant response text per session ---
291
+ // Fires once per step with the complete generated text. Flushed on session.idle.
292
+ if (t === 'session.next.text.ended') {
293
+ const text = props?.text ?? null;
294
+ if (sessionId && typeof text === 'string' && text.trim()) {
295
+ const prev = sessionText.get(sessionId) ?? '';
296
+ sessionText.set(sessionId, prev ? prev + '\\n\\n' + text : text);
297
+ }
298
+ return; // never emit directly — flushed as AgentMessage on session.idle
299
+ }
300
+
301
+ // --- session.next.prompted: the user's prompt text sent to the model ---
302
+ if (t === 'session.next.prompted') {
303
+ const promptText = props?.prompt?.text ?? null;
304
+ appendEvent({
305
+ hook_event_name: 'UserMessage',
306
+ provider: 'opencode',
307
+ session_id: sessionId,
308
+ message: typeof promptText === 'string' ? promptText.slice(0, 2000) : null,
309
+ });
310
+ return;
311
+ }
312
+
313
+ // --- session.idle: flush accumulated assistant text as AgentMessage ---
314
+ if (t === 'session.idle' && sessionId && sessionText.has(sessionId)) {
315
+ const text = sessionText.get(sessionId)!;
316
+ sessionText.delete(sessionId);
317
+ if (text.trim()) {
318
+ appendEvent({
319
+ hook_event_name: 'AgentMessage',
320
+ provider: 'opencode',
321
+ session_id: sessionId,
322
+ message: text.slice(0, 3000),
323
+ });
324
+ }
325
+ }
326
+
327
+ // --- session.next.step.ended: token usage + cost ---
328
+ if (t === 'session.next.step.ended') {
329
+ const tokens = props?.tokens ?? null;
330
+ const cost = props?.cost ?? null;
331
+ appendEvent({
332
+ hook_event_name: 'StepEnded',
333
+ provider: 'opencode',
334
+ session_id: sessionId,
335
+ message: props?.finish ?? null,
336
+ tokens: tokens,
337
+ cost: cost,
338
+ });
339
+ return;
340
+ }
341
+
342
+ // Generic catch-all — extract message from known session.next.* fields
343
+ const agentName = props?.agent ?? null;
344
+ const modelId = props?.model?.id ?? props?.model?.modelID ?? null;
345
+ const reasonText = props?.text ?? null; // session.next.reasoning.ended
346
+ const promptText = props?.prompt?.text ?? null; // fallback
347
+ const message = errMsg
348
+ ?? reasonText
349
+ ?? promptText
350
+ ?? (agentName && modelId ? \`\${agentName} (\${modelId})\`
351
+ : agentName ? agentName
352
+ : modelId ? modelId
353
+ : null);
354
+
355
+ appendEvent({
356
+ hook_event_name: SESSION_MAP[t] ?? t,
357
+ provider: 'opencode',
358
+ session_id: sessionId,
359
+ message: message,
360
+ });
361
+ },
362
+ });
363
+ `;
364
+ }
365
+ // ---------------------------------------------------------------------------
366
+ // Public API
367
+ // ---------------------------------------------------------------------------
368
+ function isOpenCodeHooksInstalled(workspaceRoot) {
369
+ try {
370
+ const content = fs.readFileSync(pluginPath(workspaceRoot), 'utf8');
371
+ // Check both the marker AND that the dir is workspace-scoped (not a stale install).
372
+ const expectedDir = path.join(workspaceRoot, '.autodev').replace(/\\/g, '/');
373
+ return content.includes(PLUGIN_MARKER) && content.includes(expectedDir);
374
+ }
375
+ catch {
376
+ return false;
377
+ }
378
+ }
379
+ function installOpenCodeHooks(workspaceRoot) {
380
+ const dir = pluginDir(workspaceRoot);
381
+ fs.mkdirSync(dir, { recursive: true });
382
+ fs.writeFileSync(pluginPath(workspaceRoot), buildPluginContent(workspaceRoot), 'utf8');
383
+ }
384
+ /**
385
+ * Return true if an opencode process is actively writing to the hooks JSONL.
386
+ * Prefers the per-session file when sessionId is known; falls back to the global file.
387
+ */
388
+ function isOpenCodeCliActive(workspaceRoot, windowMs = 90_000, sessionId) {
389
+ const safeId = sessionId ? sessionId.replace(/[^a-zA-Z0-9_-]/g, '_') : undefined;
390
+ const perFile = safeId ? path.join(workspaceRoot, '.autodev', `hooks-events-${safeId}.jsonl`) : undefined;
391
+ const jsonlFile = (perFile && fs.existsSync(perFile))
392
+ ? perFile
393
+ : path.join(workspaceRoot, '.autodev', 'hooks-events.jsonl');
394
+ try {
395
+ const stat = fs.statSync(jsonlFile);
396
+ if (Date.now() - stat.mtimeMs > windowMs) {
397
+ return false;
398
+ } // stale file
399
+ const lines = fs.readFileSync(jsonlFile, 'utf8').split('\n').filter(Boolean);
400
+ for (let i = lines.length - 1; i >= 0; i--) {
401
+ try {
402
+ const ev = JSON.parse(lines[i]);
403
+ if (ev.provider !== 'opencode') {
404
+ continue;
405
+ }
406
+ // If reading global file, filter to the requested session
407
+ if (!perFile && sessionId && ev.session_id && ev.session_id !== sessionId) {
408
+ continue;
409
+ }
410
+ const name = ev.hook_event_name ?? '';
411
+ if (name === 'Stop' || name === 'StopFailure' || name === 'SessionEnd' || name === 'server.instance.disposed') {
412
+ return false;
413
+ }
414
+ return true;
415
+ }
416
+ catch {
417
+ continue;
418
+ }
419
+ }
420
+ return false;
421
+ }
422
+ catch {
423
+ return false;
424
+ }
425
+ }
426
+ /**
427
+ * Return true if the most recent opencode session exited cleanly (Stop event present).
428
+ * Prefers the per-session file when sessionId is known.
429
+ */
430
+ function openCodeExitedCleanly(workspaceRoot, sessionId) {
431
+ const safeId = sessionId ? sessionId.replace(/[^a-zA-Z0-9_-]/g, '_') : undefined;
432
+ const perFile = safeId ? path.join(workspaceRoot, '.autodev', `hooks-events-${safeId}.jsonl`) : undefined;
433
+ const jsonlFile = (perFile && fs.existsSync(perFile))
434
+ ? perFile
435
+ : path.join(workspaceRoot, '.autodev', 'hooks-events.jsonl');
436
+ try {
437
+ const lines = fs.readFileSync(jsonlFile, 'utf8').split('\n').filter(Boolean);
438
+ for (let i = lines.length - 1; i >= 0; i--) {
439
+ try {
440
+ const ev = JSON.parse(lines[i]);
441
+ if (ev.provider !== 'opencode') {
442
+ continue;
443
+ }
444
+ if (!perFile && sessionId && ev.session_id && ev.session_id !== sessionId) {
445
+ continue;
446
+ }
447
+ const name = ev.hook_event_name ?? '';
448
+ if (name === 'Stop' || name === 'StopFailure' || name === 'SessionEnd' || name === 'server.instance.disposed') {
449
+ return true;
450
+ }
451
+ return false;
452
+ }
453
+ catch {
454
+ continue;
455
+ }
456
+ }
457
+ }
458
+ catch { /* file absent */ }
459
+ return false;
460
+ }
461
+ /**
462
+ * Read the workspace-scoped hooks-events.jsonl and return the session ID from
463
+ * the most recent OpenCode session event.
464
+ * Returns undefined if the file is absent or no session event has been seen yet.
465
+ *
466
+ * @param notBefore Only consider events with a timestamp >= this ISO string or
467
+ * epoch-ms number. Pass the task-start time to avoid picking
468
+ * up stale/foreign sessions from before this dispatch.
469
+ */
470
+ function getOpenCodeSessionIdFromHooks(workspaceRoot, notBefore) {
471
+ const jsonlFile = path.join(workspaceRoot, '.autodev', 'hooks-events.jsonl');
472
+ const cutoff = notBefore
473
+ ? (typeof notBefore === 'number' ? new Date(notBefore).toISOString() : notBefore)
474
+ : undefined;
475
+ try {
476
+ const lines = fs.readFileSync(jsonlFile, 'utf8').split('\n').filter(Boolean);
477
+ // Walk backwards — most recent event first
478
+ for (let i = lines.length - 1; i >= 0; i--) {
479
+ try {
480
+ const ev = JSON.parse(lines[i]);
481
+ if (ev.provider !== 'opencode') {
482
+ continue;
483
+ }
484
+ if (cutoff && ev.timestamp && ev.timestamp < cutoff) {
485
+ break;
486
+ } // events are chronological
487
+ const sid = ev.session_id ?? undefined;
488
+ if (sid) {
489
+ return sid;
490
+ }
491
+ }
492
+ catch { /* malformed line */ }
493
+ }
494
+ }
495
+ catch { /* file absent */ }
496
+ return undefined;
497
+ }
498
+ function uninstallOpenCodeHooks(workspaceRoot) {
499
+ const p = pluginPath(workspaceRoot);
500
+ if (!fs.existsSync(p)) {
501
+ return;
502
+ }
503
+ try {
504
+ const content = fs.readFileSync(p, 'utf8');
505
+ if (content.includes(PLUGIN_MARKER)) {
506
+ fs.unlinkSync(p);
507
+ }
508
+ }
509
+ catch { /* ignore */ }
510
+ }
511
+ //# sourceMappingURL=openCodeHooksManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openCodeHooksManager.js","sourceRoot":"","sources":["../src/openCodeHooksManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ import { AutodevSettings } from './core/settingsLoader';
2
+ export interface PeriodicActionDef {
3
+ /** Unique stable ID. Used as HTML element id prefix and counter key. */
4
+ id: string;
5
+ /** Sidebar label shown to the user. */
6
+ label: string;
7
+ /** Key in AutodevSettings that holds the interval (0 = disabled). */
8
+ settingKey: keyof AutodevSettings;
9
+ /** Log icon (emoji) used in the extension host log. */
10
+ icon: string;
11
+ /**
12
+ * How the action is executed when due:
13
+ * - 'prompt' — send `prompt` text to the AI via sendToAi (default)
14
+ * - 'compact' — run provider-specific /compact tool (no AI prompt sent)
15
+ * - 'pruneTodo' — move completed [x] tasks from TODO.md into DONE.md
16
+ */
17
+ type?: 'prompt' | 'compact' | 'pruneTodo';
18
+ /** Prompt text sent to the agent (only used when type === 'prompt'). */
19
+ prompt: string;
20
+ }
21
+ export declare const PERIODIC_ACTIONS: PeriodicActionDef[];
22
+ interface ActionState {
23
+ /** Tasks completed since this action was last triggered (or since loop start). */
24
+ counter: number;
25
+ /** Loop iteration number when this action was last triggered (0 = never). */
26
+ lastTriggeredAt: number;
27
+ /** Wall-clock ISO string when last triggered. */
28
+ lastTriggeredTime: string | null;
29
+ }
30
+ export declare class PeriodicActionManager {
31
+ private readonly _state;
32
+ private _ensureState;
33
+ /** Reset all counters (call at loop start). */
34
+ reset(): void;
35
+ /**
36
+ * Reset all counters and persist the cleared state to .autodev/loop-state.json.
37
+ * Call at loop start.
38
+ */
39
+ resetAndPersist(workspaceRoot?: string): void;
40
+ /**
41
+ * Increment all action counters by 1.
42
+ * @param iteration Current loop iteration number (used for debug state).
43
+ * @param workspaceRoot If provided, persists loop-state.json to .autodev/.
44
+ */
45
+ increment(iteration: number, workspaceRoot?: string): void;
46
+ /** Returns the actions whose counter has reached or exceeded the configured interval. */
47
+ getDue(settings: AutodevSettings): PeriodicActionDef[];
48
+ /**
49
+ * Reset the counter for a specific action after it has been handled.
50
+ * Records the iteration + timestamp it was triggered at.
51
+ */
52
+ markHandled(id: string, iteration: number, workspaceRoot?: string): void;
53
+ /** Returns the current counter value for an action (for debug / display). */
54
+ getCount(id: string): number;
55
+ /** Snapshot of all action states — used for debug persistence. */
56
+ snapshot(): Record<string, ActionState>;
57
+ /**
58
+ * Write .autodev/loop-state.json with current iteration + per-action counters.
59
+ * Safe — silently ignores write errors.
60
+ */
61
+ private _persist;
62
+ }
63
+ export {};