tide-commander 0.52.0

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 (140) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +364 -0
  3. package/dist/assets/characters/Textures/colormap.png +0 -0
  4. package/dist/assets/characters/character-female-a.glb +0 -0
  5. package/dist/assets/characters/character-female-b.glb +0 -0
  6. package/dist/assets/characters/character-female-c.glb +0 -0
  7. package/dist/assets/characters/character-female-d.glb +0 -0
  8. package/dist/assets/characters/character-female-e.glb +0 -0
  9. package/dist/assets/characters/character-female-f.glb +0 -0
  10. package/dist/assets/characters/character-male-a-processed.gltf +11862 -0
  11. package/dist/assets/characters/character-male-a.glb +0 -0
  12. package/dist/assets/characters/character-male-b.glb +0 -0
  13. package/dist/assets/characters/character-male-c.glb +0 -0
  14. package/dist/assets/characters/character-male-d.glb +0 -0
  15. package/dist/assets/characters/character-male-e.glb +0 -0
  16. package/dist/assets/characters/character-male-f.glb +0 -0
  17. package/dist/assets/icons/icon-192.png +0 -0
  18. package/dist/assets/icons/icon-512.png +0 -0
  19. package/dist/assets/landing-Cc0MDBAK.css +1 -0
  20. package/dist/assets/main-BIpLsrUu.css +1 -0
  21. package/dist/assets/main-DMTRw3br.js +276 -0
  22. package/dist/assets/textures/concrete_floor_worn_001_diff_1k.jpg +0 -0
  23. package/dist/assets/textures/logo-blanco.png +0 -0
  24. package/dist/assets/vendor-react-uS-d4TUT.js +17 -0
  25. package/dist/assets/vendor-three-4iQNXcoo.js +3828 -0
  26. package/dist/assets/web-BZdi2lG9.js +1 -0
  27. package/dist/assets/web-yHsOO1Qb.js +1 -0
  28. package/dist/index.html +38 -0
  29. package/dist/manifest.json +39 -0
  30. package/dist/src/packages/landing/index.html +463 -0
  31. package/dist/src/packages/server/app.js +87 -0
  32. package/dist/src/packages/server/auth/index.js +121 -0
  33. package/dist/src/packages/server/claude/backend.js +578 -0
  34. package/dist/src/packages/server/claude/index.js +8 -0
  35. package/dist/src/packages/server/claude/runner/internal-events.js +22 -0
  36. package/dist/src/packages/server/claude/runner/process-lifecycle.js +208 -0
  37. package/dist/src/packages/server/claude/runner/recovery-store.js +72 -0
  38. package/dist/src/packages/server/claude/runner/resource-monitor.js +51 -0
  39. package/dist/src/packages/server/claude/runner/restart-policy.js +69 -0
  40. package/dist/src/packages/server/claude/runner/stdout-pipeline.js +153 -0
  41. package/dist/src/packages/server/claude/runner/watchdog.js +114 -0
  42. package/dist/src/packages/server/claude/runner.js +310 -0
  43. package/dist/src/packages/server/claude/session-loader.js +898 -0
  44. package/dist/src/packages/server/claude/types.js +5 -0
  45. package/dist/src/packages/server/cli.js +113 -0
  46. package/dist/src/packages/server/codex/backend.js +119 -0
  47. package/dist/src/packages/server/codex/index.js +2 -0
  48. package/dist/src/packages/server/codex/json-event-parser.js +612 -0
  49. package/dist/src/packages/server/data/builtin-skills/bitbucket-pr.js +298 -0
  50. package/dist/src/packages/server/data/builtin-skills/full-notifications.js +49 -0
  51. package/dist/src/packages/server/data/builtin-skills/git-captain.js +304 -0
  52. package/dist/src/packages/server/data/builtin-skills/index.js +61 -0
  53. package/dist/src/packages/server/data/builtin-skills/pm2-logs.js +354 -0
  54. package/dist/src/packages/server/data/builtin-skills/send-message-to-agent.js +51 -0
  55. package/dist/src/packages/server/data/builtin-skills/server-logs.js +124 -0
  56. package/dist/src/packages/server/data/builtin-skills/streaming-exec.js +94 -0
  57. package/dist/src/packages/server/data/builtin-skills/types.js +4 -0
  58. package/dist/src/packages/server/data/builtin-skills.js +6 -0
  59. package/dist/src/packages/server/data/index.js +890 -0
  60. package/dist/src/packages/server/data/snapshots.js +371 -0
  61. package/dist/src/packages/server/index.js +96 -0
  62. package/dist/src/packages/server/prompts/tide-commander.js +13 -0
  63. package/dist/src/packages/server/routes/agents.js +406 -0
  64. package/dist/src/packages/server/routes/config.js +347 -0
  65. package/dist/src/packages/server/routes/custom-models.js +170 -0
  66. package/dist/src/packages/server/routes/exec.js +269 -0
  67. package/dist/src/packages/server/routes/files.js +995 -0
  68. package/dist/src/packages/server/routes/index.js +38 -0
  69. package/dist/src/packages/server/routes/notifications.js +81 -0
  70. package/dist/src/packages/server/routes/permissions.js +115 -0
  71. package/dist/src/packages/server/routes/snapshots.js +224 -0
  72. package/dist/src/packages/server/routes/stt.js +99 -0
  73. package/dist/src/packages/server/routes/tts.js +166 -0
  74. package/dist/src/packages/server/routes/voice-assistant.js +310 -0
  75. package/dist/src/packages/server/runtime/claude-runtime-provider.js +10 -0
  76. package/dist/src/packages/server/runtime/codex-runtime-provider.js +11 -0
  77. package/dist/src/packages/server/runtime/index.js +2 -0
  78. package/dist/src/packages/server/runtime/types.js +6 -0
  79. package/dist/src/packages/server/services/agent-lifecycle-service.js +82 -0
  80. package/dist/src/packages/server/services/agent-service.js +410 -0
  81. package/dist/src/packages/server/services/boss-message-service.js +430 -0
  82. package/dist/src/packages/server/services/boss-service.js +553 -0
  83. package/dist/src/packages/server/services/building-service.js +867 -0
  84. package/dist/src/packages/server/services/claude-service.js +5 -0
  85. package/dist/src/packages/server/services/custom-class-service.js +323 -0
  86. package/dist/src/packages/server/services/database-service.js +914 -0
  87. package/dist/src/packages/server/services/docker-service.js +865 -0
  88. package/dist/src/packages/server/services/fileTracker.js +242 -0
  89. package/dist/src/packages/server/services/index.js +21 -0
  90. package/dist/src/packages/server/services/permission-service.js +258 -0
  91. package/dist/src/packages/server/services/pm2-service.js +435 -0
  92. package/dist/src/packages/server/services/runtime-command-execution.js +168 -0
  93. package/dist/src/packages/server/services/runtime-events.js +357 -0
  94. package/dist/src/packages/server/services/runtime-service.js +308 -0
  95. package/dist/src/packages/server/services/runtime-status-sync.js +104 -0
  96. package/dist/src/packages/server/services/runtime-subagents.js +50 -0
  97. package/dist/src/packages/server/services/runtime-watchdog.js +74 -0
  98. package/dist/src/packages/server/services/secrets-service.js +206 -0
  99. package/dist/src/packages/server/services/skill-service.js +508 -0
  100. package/dist/src/packages/server/services/subordinate-context-service.js +223 -0
  101. package/dist/src/packages/server/services/supervisor-claude.js +132 -0
  102. package/dist/src/packages/server/services/supervisor-prompts.js +80 -0
  103. package/dist/src/packages/server/services/supervisor-service.js +659 -0
  104. package/dist/src/packages/server/services/work-plan-service.js +476 -0
  105. package/dist/src/packages/server/setup.js +86 -0
  106. package/dist/src/packages/server/utils/index.js +4 -0
  107. package/dist/src/packages/server/utils/logger.js +302 -0
  108. package/dist/src/packages/server/utils/string.js +39 -0
  109. package/dist/src/packages/server/utils/tool-formatting.js +139 -0
  110. package/dist/src/packages/server/utils/unicode.js +46 -0
  111. package/dist/src/packages/server/websocket/handler.js +290 -0
  112. package/dist/src/packages/server/websocket/handlers/agent-handler.js +515 -0
  113. package/dist/src/packages/server/websocket/handlers/boss-handler.js +116 -0
  114. package/dist/src/packages/server/websocket/handlers/boss-response-handler.js +250 -0
  115. package/dist/src/packages/server/websocket/handlers/building-handler.js +298 -0
  116. package/dist/src/packages/server/websocket/handlers/command-handler.js +217 -0
  117. package/dist/src/packages/server/websocket/handlers/custom-class-handler.js +68 -0
  118. package/dist/src/packages/server/websocket/handlers/database-handler.js +223 -0
  119. package/dist/src/packages/server/websocket/handlers/notification-handler.js +25 -0
  120. package/dist/src/packages/server/websocket/handlers/permission-handler.js +21 -0
  121. package/dist/src/packages/server/websocket/handlers/secrets-handler.js +61 -0
  122. package/dist/src/packages/server/websocket/handlers/skill-handler.js +148 -0
  123. package/dist/src/packages/server/websocket/handlers/supervisor-handler.js +44 -0
  124. package/dist/src/packages/server/websocket/handlers/sync-handler.js +19 -0
  125. package/dist/src/packages/server/websocket/handlers/types.js +4 -0
  126. package/dist/src/packages/server/websocket/listeners/boss-listeners.js +21 -0
  127. package/dist/src/packages/server/websocket/listeners/index.js +32 -0
  128. package/dist/src/packages/server/websocket/listeners/permission-listeners.js +19 -0
  129. package/dist/src/packages/server/websocket/listeners/runtime-listeners.js +196 -0
  130. package/dist/src/packages/server/websocket/listeners/skill-listeners.js +51 -0
  131. package/dist/src/packages/server/websocket/listeners/supervisor-listeners.js +37 -0
  132. package/dist/src/packages/shared/agent-types.js +54 -0
  133. package/dist/src/packages/shared/building-types.js +43 -0
  134. package/dist/src/packages/shared/common-types.js +1 -0
  135. package/dist/src/packages/shared/database-types.js +8 -0
  136. package/dist/src/packages/shared/types/snapshot.js +7 -0
  137. package/dist/src/packages/shared/types.js +12 -0
  138. package/dist/src/packages/shared/websocket-messages.js +1 -0
  139. package/dist/sw.js +37 -0
  140. package/package.json +90 -0
@@ -0,0 +1,476 @@
1
+ /**
2
+ * Work Plan Service
3
+ * Manages work plans created by boss agents, including parsing, storage, and execution
4
+ */
5
+ import * as agentService from './agent-service.js';
6
+ import * as bossService from './boss-service.js';
7
+ import { logger, generateId } from '../utils/index.js';
8
+ const log = logger.boss || console;
9
+ // In-memory storage for work plans and analysis requests
10
+ const workPlans = new Map();
11
+ const analysisRequests = new Map();
12
+ const listeners = new Set();
13
+ // ============================================================================
14
+ // Event System
15
+ // ============================================================================
16
+ export function subscribe(listener) {
17
+ listeners.add(listener);
18
+ return () => listeners.delete(listener);
19
+ }
20
+ function emit(event, data) {
21
+ listeners.forEach((listener) => listener(event, data));
22
+ }
23
+ // ============================================================================
24
+ // Work Plan Management
25
+ // ============================================================================
26
+ /**
27
+ * Create a work plan from a draft (parsed from boss response)
28
+ */
29
+ export function createWorkPlan(bossId, draft) {
30
+ const now = Date.now();
31
+ const planId = generateId();
32
+ // Convert draft phases to full phases with proper status
33
+ const phases = draft.phases.map((phase) => ({
34
+ id: phase.id,
35
+ name: phase.name,
36
+ execution: phase.execution,
37
+ dependsOn: phase.dependsOn,
38
+ status: 'pending',
39
+ tasks: phase.tasks.map((task) => ({
40
+ id: task.id,
41
+ description: task.description,
42
+ suggestedClass: task.suggestedClass,
43
+ assignedAgentId: task.assignToAgent,
44
+ priority: task.priority,
45
+ blockedBy: task.blockedBy,
46
+ status: 'pending',
47
+ })),
48
+ }));
49
+ // Calculate total tasks and parallelizable tasks
50
+ const allTasks = phases.flatMap((p) => p.tasks);
51
+ const parallelPhases = phases.filter((p) => p.execution === 'parallel');
52
+ const parallelizableTasks = parallelPhases.flatMap((p) => p.tasks.map((t) => t.id));
53
+ const workPlan = {
54
+ id: planId,
55
+ name: draft.name,
56
+ description: draft.description,
57
+ phases,
58
+ createdBy: bossId,
59
+ createdAt: now,
60
+ updatedAt: now,
61
+ status: 'draft',
62
+ totalTasks: allTasks.length,
63
+ completedTasks: 0,
64
+ parallelizableTasks,
65
+ };
66
+ workPlans.set(planId, workPlan);
67
+ log.log?.(`📋 Created work plan "${workPlan.name}" with ${workPlan.totalTasks} tasks`);
68
+ emit('work_plan_created', workPlan);
69
+ return workPlan;
70
+ }
71
+ /**
72
+ * Get a work plan by ID
73
+ */
74
+ export function getWorkPlan(planId) {
75
+ return workPlans.get(planId) || null;
76
+ }
77
+ /**
78
+ * Get all work plans for a boss
79
+ */
80
+ export function getWorkPlansForBoss(bossId) {
81
+ return Array.from(workPlans.values()).filter((p) => p.createdBy === bossId);
82
+ }
83
+ /**
84
+ * Get all work plans
85
+ */
86
+ export function getAllWorkPlans() {
87
+ return Array.from(workPlans.values());
88
+ }
89
+ /**
90
+ * Approve a work plan for execution
91
+ */
92
+ export function approveWorkPlan(planId) {
93
+ const plan = workPlans.get(planId);
94
+ if (!plan)
95
+ return null;
96
+ if (plan.status !== 'draft') {
97
+ log.log?.(`⚠️ Cannot approve plan "${plan.name}" - status is ${plan.status}`);
98
+ return null;
99
+ }
100
+ plan.status = 'approved';
101
+ plan.updatedAt = Date.now();
102
+ emit('work_plan_updated', plan);
103
+ log.log?.(`✅ Approved work plan "${plan.name}"`);
104
+ return plan;
105
+ }
106
+ /**
107
+ * Start executing a work plan
108
+ */
109
+ export function executeWorkPlan(planId) {
110
+ const plan = workPlans.get(planId);
111
+ if (!plan)
112
+ return null;
113
+ if (plan.status !== 'approved') {
114
+ log.log?.(`⚠️ Cannot execute plan "${plan.name}" - status is ${plan.status}`);
115
+ return null;
116
+ }
117
+ plan.status = 'executing';
118
+ plan.updatedAt = Date.now();
119
+ // Start first phase(s) that have no dependencies
120
+ const readyPhases = plan.phases.filter((p) => p.dependsOn.length === 0);
121
+ for (const phase of readyPhases) {
122
+ startPhase(plan, phase);
123
+ }
124
+ emit('work_plan_updated', plan);
125
+ log.log?.(`🚀 Started executing work plan "${plan.name}"`);
126
+ return plan;
127
+ }
128
+ /**
129
+ * Pause a work plan
130
+ */
131
+ export function pauseWorkPlan(planId) {
132
+ const plan = workPlans.get(planId);
133
+ if (!plan)
134
+ return null;
135
+ if (plan.status !== 'executing') {
136
+ log.log?.(`⚠️ Cannot pause plan "${plan.name}" - status is ${plan.status}`);
137
+ return null;
138
+ }
139
+ plan.status = 'paused';
140
+ plan.updatedAt = Date.now();
141
+ emit('work_plan_updated', plan);
142
+ log.log?.(`⏸️ Paused work plan "${plan.name}"`);
143
+ return plan;
144
+ }
145
+ /**
146
+ * Cancel a work plan
147
+ */
148
+ export function cancelWorkPlan(planId) {
149
+ const plan = workPlans.get(planId);
150
+ if (!plan)
151
+ return null;
152
+ plan.status = 'cancelled';
153
+ plan.updatedAt = Date.now();
154
+ // Cancel all pending/in_progress tasks
155
+ for (const phase of plan.phases) {
156
+ if (phase.status === 'pending' || phase.status === 'in_progress') {
157
+ phase.status = 'cancelled';
158
+ }
159
+ for (const task of phase.tasks) {
160
+ if (task.status === 'pending' || task.status === 'in_progress') {
161
+ task.status = 'cancelled';
162
+ }
163
+ }
164
+ }
165
+ emit('work_plan_updated', plan);
166
+ log.log?.(`❌ Cancelled work plan "${plan.name}"`);
167
+ return plan;
168
+ }
169
+ /**
170
+ * Delete a work plan
171
+ */
172
+ export function deleteWorkPlan(planId) {
173
+ const plan = workPlans.get(planId);
174
+ if (!plan)
175
+ return false;
176
+ workPlans.delete(planId);
177
+ emit('work_plan_deleted', { id: planId });
178
+ log.log?.(`🗑️ Deleted work plan "${plan.name}"`);
179
+ return true;
180
+ }
181
+ /**
182
+ * Start a phase in a work plan
183
+ */
184
+ function startPhase(plan, phase) {
185
+ phase.status = 'in_progress';
186
+ phase.startedAt = Date.now();
187
+ // Start tasks that have no blockers
188
+ const readyTasks = phase.tasks.filter((t) => t.blockedBy.length === 0);
189
+ if (phase.execution === 'parallel') {
190
+ // Start all ready tasks in parallel
191
+ for (const task of readyTasks) {
192
+ startTask(plan, phase, task);
193
+ }
194
+ }
195
+ else {
196
+ // Sequential - start only the first ready task
197
+ if (readyTasks.length > 0) {
198
+ startTask(plan, phase, readyTasks[0]);
199
+ }
200
+ }
201
+ }
202
+ /**
203
+ * Start a task - auto-assign agent if needed and delegate
204
+ */
205
+ function startTask(plan, phase, task) {
206
+ task.status = 'in_progress';
207
+ task.startedAt = Date.now();
208
+ // Auto-assign agent if not already assigned
209
+ if (!task.assignedAgentId) {
210
+ const agent = findBestAgentForTask(plan.createdBy, task);
211
+ if (agent) {
212
+ task.assignedAgentId = agent.id;
213
+ task.assignedAgentName = agent.name;
214
+ }
215
+ }
216
+ if (task.assignedAgentId) {
217
+ const agent = agentService.getAgent(task.assignedAgentId);
218
+ task.assignedAgentName = agent?.name;
219
+ log.log?.(`🎯 Started task "${task.description}" → ${task.assignedAgentName || task.assignedAgentId}`);
220
+ // Emit delegation event for the handler to pick up
221
+ emit('task_started', {
222
+ planId: plan.id,
223
+ phaseId: phase.id,
224
+ task,
225
+ agentId: task.assignedAgentId,
226
+ });
227
+ }
228
+ else {
229
+ log.log?.(`⚠️ No agent available for task "${task.description}"`);
230
+ task.status = 'blocked';
231
+ }
232
+ plan.updatedAt = Date.now();
233
+ emit('work_plan_updated', plan);
234
+ }
235
+ /**
236
+ * Find the best available agent for a task based on class and availability
237
+ */
238
+ function findBestAgentForTask(bossId, task) {
239
+ const subordinates = bossService.getSubordinates(bossId);
240
+ // First, try to find an idle agent of the suggested class
241
+ const idealAgent = subordinates.find((a) => a.class === task.suggestedClass && a.status === 'idle');
242
+ if (idealAgent) {
243
+ return { id: idealAgent.id, name: idealAgent.name };
244
+ }
245
+ // If no idle agent of the right class, find any idle agent
246
+ const anyIdleAgent = subordinates.find((a) => a.status === 'idle');
247
+ if (anyIdleAgent) {
248
+ return { id: anyIdleAgent.id, name: anyIdleAgent.name };
249
+ }
250
+ // If all agents are busy, find the one with lowest context usage of the right class
251
+ const sameClassAgents = subordinates.filter((a) => a.class === task.suggestedClass);
252
+ if (sameClassAgents.length > 0) {
253
+ const lowestContext = sameClassAgents.reduce((a, b) => (a.contextUsed / a.contextLimit) < (b.contextUsed / b.contextLimit) ? a : b);
254
+ return { id: lowestContext.id, name: lowestContext.name };
255
+ }
256
+ // Fallback to any agent with lowest context
257
+ if (subordinates.length > 0) {
258
+ const lowestContext = subordinates.reduce((a, b) => (a.contextUsed / a.contextLimit) < (b.contextUsed / b.contextLimit) ? a : b);
259
+ return { id: lowestContext.id, name: lowestContext.name };
260
+ }
261
+ return null;
262
+ }
263
+ /**
264
+ * Mark a task as completed and check for next tasks/phases
265
+ */
266
+ export function completeTask(planId, taskId, result) {
267
+ const plan = workPlans.get(planId);
268
+ if (!plan)
269
+ return null;
270
+ // Find the task
271
+ let targetTask = null;
272
+ let targetPhase = null;
273
+ for (const phase of plan.phases) {
274
+ const task = phase.tasks.find((t) => t.id === taskId);
275
+ if (task) {
276
+ targetTask = task;
277
+ targetPhase = phase;
278
+ break;
279
+ }
280
+ }
281
+ if (!targetTask || !targetPhase) {
282
+ log.log?.(`⚠️ Task ${taskId} not found in plan ${planId}`);
283
+ return null;
284
+ }
285
+ // Mark task complete
286
+ targetTask.status = 'completed';
287
+ targetTask.completedAt = Date.now();
288
+ targetTask.result = result;
289
+ plan.completedTasks++;
290
+ log.log?.(`✅ Completed task "${targetTask.description}"`);
291
+ // Check for tasks that were blocked by this one
292
+ for (const phase of plan.phases) {
293
+ for (const task of phase.tasks) {
294
+ if (task.status === 'pending' && task.blockedBy.includes(taskId)) {
295
+ // Remove this task from blockers
296
+ task.blockedBy = task.blockedBy.filter((id) => id !== taskId);
297
+ // If no more blockers and phase is in progress, start the task
298
+ if (task.blockedBy.length === 0 && phase.status === 'in_progress') {
299
+ if (phase.execution === 'parallel') {
300
+ startTask(plan, phase, task);
301
+ }
302
+ else {
303
+ // Sequential - only start if no other task is in progress
304
+ const inProgressTask = phase.tasks.find((t) => t.status === 'in_progress');
305
+ if (!inProgressTask) {
306
+ startTask(plan, phase, task);
307
+ }
308
+ }
309
+ }
310
+ }
311
+ }
312
+ }
313
+ // Check if phase is complete
314
+ const allTasksComplete = targetPhase.tasks.every((t) => t.status === 'completed');
315
+ if (allTasksComplete) {
316
+ targetPhase.status = 'completed';
317
+ targetPhase.completedAt = Date.now();
318
+ log.log?.(`✅ Completed phase "${targetPhase.name}"`);
319
+ // Check for phases that depend on this one
320
+ for (const phase of plan.phases) {
321
+ if (phase.status === 'pending' && phase.dependsOn.includes(targetPhase.id)) {
322
+ // Remove this phase from dependencies
323
+ phase.dependsOn = phase.dependsOn.filter((id) => id !== targetPhase.id);
324
+ // If no more dependencies, start the phase
325
+ if (phase.dependsOn.length === 0) {
326
+ startPhase(plan, phase);
327
+ }
328
+ }
329
+ }
330
+ }
331
+ // Check if entire plan is complete
332
+ const allPhasesComplete = plan.phases.every((p) => p.status === 'completed');
333
+ if (allPhasesComplete) {
334
+ plan.status = 'completed';
335
+ log.log?.(`🎉 Completed work plan "${plan.name}"`);
336
+ }
337
+ plan.updatedAt = Date.now();
338
+ emit('work_plan_updated', plan);
339
+ return plan;
340
+ }
341
+ // ============================================================================
342
+ // Analysis Request Management
343
+ // ============================================================================
344
+ /**
345
+ * Create an analysis request from a draft (parsed from boss response)
346
+ */
347
+ export function createAnalysisRequest(bossId, draft) {
348
+ const now = Date.now();
349
+ const requestId = generateId();
350
+ const agent = agentService.getAgent(draft.targetAgent);
351
+ const request = {
352
+ id: requestId,
353
+ targetAgentId: draft.targetAgent,
354
+ targetAgentName: agent?.name,
355
+ query: draft.query,
356
+ focus: draft.focus,
357
+ status: 'pending',
358
+ requestedAt: now,
359
+ };
360
+ analysisRequests.set(requestId, request);
361
+ log.log?.(`🔍 Created analysis request for ${request.targetAgentName || request.targetAgentId}`);
362
+ emit('analysis_request_created', request);
363
+ return request;
364
+ }
365
+ /**
366
+ * Get an analysis request by ID
367
+ */
368
+ export function getAnalysisRequest(requestId) {
369
+ return analysisRequests.get(requestId) || null;
370
+ }
371
+ /**
372
+ * Start an analysis request (send to agent)
373
+ */
374
+ export function startAnalysisRequest(requestId) {
375
+ const request = analysisRequests.get(requestId);
376
+ if (!request)
377
+ return null;
378
+ request.status = 'in_progress';
379
+ emit('analysis_request_started', request);
380
+ return request;
381
+ }
382
+ /**
383
+ * Complete an analysis request with results
384
+ */
385
+ export function completeAnalysisRequest(requestId, result) {
386
+ const request = analysisRequests.get(requestId);
387
+ if (!request)
388
+ return null;
389
+ request.status = 'completed';
390
+ request.result = result;
391
+ request.completedAt = Date.now();
392
+ log.log?.(`✅ Completed analysis request from ${request.targetAgentName || request.targetAgentId}`);
393
+ emit('analysis_request_completed', request);
394
+ return request;
395
+ }
396
+ /**
397
+ * Get all pending analysis requests for a boss's subordinates
398
+ */
399
+ export function getPendingAnalysisRequests(bossId) {
400
+ const subordinateIds = bossService.getSubordinates(bossId).map((a) => a.id);
401
+ return Array.from(analysisRequests.values()).filter((r) => r.status === 'pending' && subordinateIds.includes(r.targetAgentId));
402
+ }
403
+ // ============================================================================
404
+ // Parsing Utilities
405
+ // ============================================================================
406
+ /**
407
+ * Parse work-plan block from boss response
408
+ */
409
+ export function parseWorkPlanBlock(content) {
410
+ const match = content.match(/```work-plan\s*([\s\S]*?)```/);
411
+ if (!match)
412
+ return null;
413
+ try {
414
+ const json = match[1].trim();
415
+ const draft = JSON.parse(json);
416
+ // Validate required fields
417
+ if (!draft.name || !draft.phases || !Array.isArray(draft.phases)) {
418
+ log.log?.('⚠️ Invalid work-plan: missing required fields');
419
+ return null;
420
+ }
421
+ return draft;
422
+ }
423
+ catch (err) {
424
+ log.log?.('⚠️ Failed to parse work-plan JSON:', err);
425
+ return null;
426
+ }
427
+ }
428
+ /**
429
+ * Parse analysis-request block from boss response
430
+ */
431
+ export function parseAnalysisRequestBlock(content) {
432
+ const match = content.match(/```analysis-request\s*([\s\S]*?)```/);
433
+ if (!match)
434
+ return [];
435
+ try {
436
+ const json = match[1].trim();
437
+ const drafts = JSON.parse(json);
438
+ if (!Array.isArray(drafts)) {
439
+ return [drafts];
440
+ }
441
+ // Validate each request
442
+ return drafts.filter((d) => d.targetAgent && d.query);
443
+ }
444
+ catch (err) {
445
+ log.log?.('⚠️ Failed to parse analysis-request JSON:', err);
446
+ return [];
447
+ }
448
+ }
449
+ // ============================================================================
450
+ // Conversion Utilities
451
+ // ============================================================================
452
+ /**
453
+ * Convert work plan tasks to delegation format for immediate execution
454
+ */
455
+ export function convertTasksToDelegations(plan) {
456
+ const delegations = [];
457
+ // Get tasks that are ready to execute (in phases with no dependencies)
458
+ for (const phase of plan.phases) {
459
+ if (phase.dependsOn.length > 0)
460
+ continue; // Skip phases with dependencies
461
+ for (const task of phase.tasks) {
462
+ if (task.blockedBy.length > 0)
463
+ continue; // Skip tasks with blockers
464
+ if (!task.assignedAgentId)
465
+ continue; // Skip unassigned tasks
466
+ delegations.push({
467
+ selectedAgentId: task.assignedAgentId,
468
+ selectedAgentName: task.assignedAgentName || task.assignedAgentId,
469
+ taskCommand: task.description,
470
+ reasoning: `Work plan task: ${phase.name} (${phase.execution})`,
471
+ confidence: task.priority === 'high' ? 'high' : task.priority === 'medium' ? 'medium' : 'low',
472
+ });
473
+ }
474
+ }
475
+ return delegations;
476
+ }
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Tide Commander - Setup Script
4
+ * Run with: npm run setup
5
+ */
6
+ import { execSync } from 'child_process';
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+ import * as os from 'os';
10
+ const TIDE_HOOKS_DIR = path.join(os.homedir(), '.tide-commander', 'hooks');
11
+ const CLAUDE_SETTINGS_DIR = path.join(os.homedir(), '.claude');
12
+ const CLAUDE_SETTINGS_FILE = path.join(CLAUDE_SETTINGS_DIR, 'settings.json');
13
+ console.log('🌊 Tide Commander - Setup');
14
+ console.log('='.repeat(40));
15
+ // Check for dependencies
16
+ console.log('\nChecking dependencies...');
17
+ // Check jq (for hook script)
18
+ try {
19
+ execSync('which jq', { stdio: 'pipe' });
20
+ console.log('✓ jq is installed');
21
+ }
22
+ catch {
23
+ console.error('✗ jq is not installed');
24
+ console.error(' Install with: sudo apt install jq (or brew install jq)');
25
+ process.exit(1);
26
+ }
27
+ // Check Claude Code
28
+ try {
29
+ execSync('which claude', { stdio: 'pipe' });
30
+ console.log('✓ Claude Code CLI is installed');
31
+ }
32
+ catch {
33
+ console.error('✗ Claude Code CLI is not installed');
34
+ console.error(' Install from: https://github.com/anthropics/claude-code');
35
+ process.exit(1);
36
+ }
37
+ // Create directories
38
+ console.log('\nSetting up directories...');
39
+ fs.mkdirSync(TIDE_HOOKS_DIR, { recursive: true });
40
+ console.log(`✓ Created ${TIDE_HOOKS_DIR}`);
41
+ fs.mkdirSync(CLAUDE_SETTINGS_DIR, { recursive: true });
42
+ console.log(`✓ Created ${CLAUDE_SETTINGS_DIR}`);
43
+ // Copy hook script
44
+ console.log('\nInstalling hooks...');
45
+ const hookScriptSource = path.join(process.cwd(), 'hooks', 'tide-hook.sh');
46
+ const hookScriptDest = path.join(TIDE_HOOKS_DIR, 'tide-hook.sh');
47
+ if (fs.existsSync(hookScriptSource)) {
48
+ fs.copyFileSync(hookScriptSource, hookScriptDest);
49
+ fs.chmodSync(hookScriptDest, '755');
50
+ console.log(`✓ Installed hook script to ${hookScriptDest}`);
51
+ }
52
+ else {
53
+ console.error(`✗ Hook script not found at ${hookScriptSource}`);
54
+ process.exit(1);
55
+ }
56
+ // Update Claude settings
57
+ console.log('\nConfiguring Claude Code...');
58
+ let settings = {};
59
+ if (fs.existsSync(CLAUDE_SETTINGS_FILE)) {
60
+ // Backup existing settings
61
+ const backupFile = `${CLAUDE_SETTINGS_FILE}.backup`;
62
+ fs.copyFileSync(CLAUDE_SETTINGS_FILE, backupFile);
63
+ console.log(`✓ Backed up settings to ${backupFile}`);
64
+ try {
65
+ settings = JSON.parse(fs.readFileSync(CLAUDE_SETTINGS_FILE, 'utf-8'));
66
+ }
67
+ catch {
68
+ console.warn(' Warning: Could not parse existing settings, starting fresh');
69
+ }
70
+ }
71
+ // Add hooks
72
+ settings.hooks = {
73
+ ...settings.hooks,
74
+ PreToolUse: hookScriptDest,
75
+ PostToolUse: hookScriptDest,
76
+ Stop: hookScriptDest,
77
+ UserPromptSubmit: hookScriptDest,
78
+ };
79
+ fs.writeFileSync(CLAUDE_SETTINGS_FILE, JSON.stringify(settings, null, 2));
80
+ console.log(`✓ Updated ${CLAUDE_SETTINGS_FILE}`);
81
+ // Done
82
+ console.log('\n' + '='.repeat(40));
83
+ console.log('🎉 Setup complete!\n');
84
+ console.log('To start Tide Commander:');
85
+ console.log(' npm run dev\n');
86
+ console.log('Then open http://localhost:5173 in your browser (or your configured VITE_PORT)');
@@ -0,0 +1,4 @@
1
+ export * from './logger.js';
2
+ export * from './unicode.js';
3
+ export * from './string.js';
4
+ export * from './tool-formatting.js';