@mod-computer/cli 0.2.3 → 0.2.5

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 (75) hide show
  1. package/dist/cli.bundle.js +216 -36371
  2. package/package.json +3 -3
  3. package/dist/app.js +0 -227
  4. package/dist/cli.bundle.js.map +0 -7
  5. package/dist/cli.js +0 -132
  6. package/dist/commands/add.js +0 -245
  7. package/dist/commands/agents-run.js +0 -71
  8. package/dist/commands/auth.js +0 -259
  9. package/dist/commands/branch.js +0 -1411
  10. package/dist/commands/claude-sync.js +0 -772
  11. package/dist/commands/comment.js +0 -568
  12. package/dist/commands/diff.js +0 -182
  13. package/dist/commands/index.js +0 -73
  14. package/dist/commands/init.js +0 -597
  15. package/dist/commands/ls.js +0 -135
  16. package/dist/commands/members.js +0 -687
  17. package/dist/commands/mv.js +0 -282
  18. package/dist/commands/recover.js +0 -207
  19. package/dist/commands/rm.js +0 -257
  20. package/dist/commands/spec.js +0 -386
  21. package/dist/commands/status.js +0 -296
  22. package/dist/commands/sync.js +0 -119
  23. package/dist/commands/trace.js +0 -1752
  24. package/dist/commands/workspace.js +0 -447
  25. package/dist/components/conflict-resolution-ui.js +0 -120
  26. package/dist/components/messages.js +0 -5
  27. package/dist/components/thread.js +0 -8
  28. package/dist/config/features.js +0 -83
  29. package/dist/containers/branches-container.js +0 -140
  30. package/dist/containers/directory-container.js +0 -92
  31. package/dist/containers/thread-container.js +0 -214
  32. package/dist/containers/threads-container.js +0 -27
  33. package/dist/containers/workspaces-container.js +0 -27
  34. package/dist/daemon/conflict-resolution.js +0 -172
  35. package/dist/daemon/content-hash.js +0 -31
  36. package/dist/daemon/file-sync.js +0 -985
  37. package/dist/daemon/index.js +0 -203
  38. package/dist/daemon/mime-types.js +0 -166
  39. package/dist/daemon/offline-queue.js +0 -211
  40. package/dist/daemon/path-utils.js +0 -64
  41. package/dist/daemon/share-policy.js +0 -83
  42. package/dist/daemon/wasm-errors.js +0 -189
  43. package/dist/daemon/worker.js +0 -557
  44. package/dist/daemon-worker.js +0 -258
  45. package/dist/errors/workspace-errors.js +0 -48
  46. package/dist/lib/auth-server.js +0 -216
  47. package/dist/lib/browser.js +0 -35
  48. package/dist/lib/diff.js +0 -284
  49. package/dist/lib/formatters.js +0 -204
  50. package/dist/lib/git.js +0 -137
  51. package/dist/lib/local-fs.js +0 -201
  52. package/dist/lib/prompts.js +0 -56
  53. package/dist/lib/storage.js +0 -213
  54. package/dist/lib/trace-formatters.js +0 -314
  55. package/dist/services/add-service.js +0 -554
  56. package/dist/services/add-validation.js +0 -124
  57. package/dist/services/automatic-file-tracker.js +0 -303
  58. package/dist/services/cli-orchestrator.js +0 -227
  59. package/dist/services/feature-flags.js +0 -187
  60. package/dist/services/file-import-service.js +0 -283
  61. package/dist/services/file-transformation-service.js +0 -218
  62. package/dist/services/logger.js +0 -44
  63. package/dist/services/mod-config.js +0 -67
  64. package/dist/services/modignore-service.js +0 -328
  65. package/dist/services/sync-daemon.js +0 -244
  66. package/dist/services/thread-notification-service.js +0 -50
  67. package/dist/services/thread-service.js +0 -147
  68. package/dist/stores/use-directory-store.js +0 -96
  69. package/dist/stores/use-threads-store.js +0 -46
  70. package/dist/stores/use-workspaces-store.js +0 -54
  71. package/dist/types/add-types.js +0 -99
  72. package/dist/types/config.js +0 -16
  73. package/dist/types/index.js +0 -2
  74. package/dist/types/workspace-connection.js +0 -53
  75. package/dist/types.js +0 -1
@@ -1,772 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Claude Code Event Sync Tool
4
- * Syncs Claude Code events with Mod system (branches, threads, tasks, history)
5
- */
6
- import { readModConfig } from '../services/mod-config.js';
7
- import path from 'path';
8
- import { fileURLToPath } from 'url';
9
- const __filename = fileURLToPath(import.meta.url);
10
- const __dirname = path.dirname(__filename);
11
- function isWorkspaceHistoryFile(filePath) {
12
- if (!filePath)
13
- return false;
14
- const normalized = filePath.replace(/\\/g, '/');
15
- if (!normalized)
16
- return false;
17
- if (normalized === '.claude' || normalized === '/.claude')
18
- return false;
19
- if (normalized.startsWith('.claude/'))
20
- return false;
21
- if (normalized.includes('/.claude/'))
22
- return false;
23
- if (normalized.endsWith('/.claude'))
24
- return false;
25
- return true;
26
- }
27
- export async function claudeSyncCommand(args, repo) {
28
- // Check for --payload-stdin flag
29
- if (args.includes('--payload-stdin')) {
30
- // await processPayloadFromStdin(repo); // Commented out - needs refactoring
31
- return;
32
- }
33
- const subcommand = args[0];
34
- if (!subcommand) {
35
- console.log(`
36
- Claude Code Event Sync Tool
37
-
38
- Usage:
39
- mod claude-sync <command> [options]
40
-
41
- Commands:
42
- sync-events # Sync all tracked events to Mod system
43
- create-session <id> # Create branch/thread for specific session
44
- sync-file-updates # Sync file updates to history
45
- sync-todos # Sync todo tracking to tasks
46
- sync-conversations # Sync conversation content to threads
47
- status # Show sync status
48
- help # Show this help
49
-
50
- Examples:
51
- mod claude-sync sync-events
52
- mod claude-sync create-session 1faa82b3-6d63-4e39-8784-207d7fbf83c0
53
- mod claude-sync status
54
- `);
55
- return;
56
- }
57
- const cfg = readModConfig();
58
- if (!cfg?.workspaceId) {
59
- console.error('❌ No active workspace configured in .mod/config.json');
60
- process.exit(1);
61
- }
62
- // TODO: Replace with ModWorkspace/WorkspaceHandle approach
63
- console.error('❌ claude-sync needs refactoring to work with new ModWorkspace approach');
64
- process.exit(1);
65
- }
66
- /* COMMENTED OUT - Needs refactoring for new ModWorkspace approach
67
- const cache = getCliRepoCache(repo as any);
68
- const workspaceContext = await cache.ensureBranchContext({
69
- workspaceId: cfg.workspaceId,
70
- branchId: '', // Will be resolved by the cache
71
- waitForHydration: true
72
- });
73
- const { workspace, branchesDocId, branch } = workspaceContext;
74
-
75
- if (!workspace || !branch) {
76
- console.error('❌ Failed to resolve workspace context');
77
- process.exit(1);
78
- }
79
-
80
- const workspaceId = workspace.id;
81
- const branchId = branch.id;
82
-
83
- const workspaceService = new WorkspaceService(repo);
84
- const branchService = new BranchService(repo);
85
- const threadService = new ThreadService(repo);
86
- const taskService = new TaskService(repo);
87
- const changeLogService = new ChangeLogService(repo);
88
-
89
- const projectDir = process.cwd();
90
- const stateDir = path.join(projectDir, '.claude', 'state');
91
- const logDir = path.join(projectDir, '.claude', 'logs');
92
- const conversationsDir = path.join(projectDir, '.claude', 'conversations');
93
-
94
- switch (subcommand) {
95
- case 'sync-events':
96
- await syncAllEvents(workspaceService, branchService, threadService, taskService, changeLogService, {
97
- workspaceId, branchId, branchesDocId, projectDir, stateDir, logDir, conversationsDir
98
- });
99
- break;
100
-
101
- case 'create-session':
102
- const sessionId = args[1];
103
- if (!sessionId) {
104
- console.error('❌ Session ID required: mod claude-sync create-session <session-id>');
105
- process.exit(1);
106
- }
107
- await createSessionBranchThread(workspaceService, branchService, threadService, {
108
- workspaceId, branchId, branchesDocId, sessionId, projectDir, stateDir
109
- });
110
- break;
111
-
112
- case 'sync-file-updates':
113
- await syncFileUpdates(taskService, changeLogService, { branchId, branchesDocId, projectDir, logDir });
114
- break;
115
-
116
- case 'sync-todos':
117
- await syncTodos(taskService, { branchId, branchesDocId, projectDir, logDir });
118
- break;
119
-
120
- case 'sync-conversations':
121
- await syncConversations(threadService, { branchId, branchesDocId, projectDir, conversationsDir });
122
- break;
123
-
124
- case 'status':
125
- await showSyncStatus({ projectDir, stateDir, logDir, conversationsDir });
126
- break;
127
-
128
- case 'help':
129
- console.log(`
130
- Claude Code Event Sync Tool
131
-
132
- This tool integrates Claude Code activity with your Mod workspace by:
133
-
134
- 1. Creating new branches/threads for each Claude Code session
135
- 2. Tracking file updates in task history
136
- 3. Syncing todo tracking to Mod tasks
137
- 4. Adding conversation content to threads
138
-
139
- Commands:
140
- sync-events # Full sync of all tracked data
141
- create-session <id> # Create branch/thread for specific session
142
- sync-file-updates # Sync file updates to history
143
- sync-todos # Sync todo tracking to tasks
144
- sync-conversations # Sync conversation content to threads
145
- status # Show sync status
146
-
147
- The tool reads from:
148
- .claude/logs/events-*.jsonl # Event logs
149
- .claude/conversations/*.json # Conversation data
150
- .claude/state/sessions.json # Session mappings
151
- `);
152
- break;
153
-
154
- default:
155
- console.error(`❌ Unknown command: ${subcommand}`);
156
- console.log('Run "mod claude-sync help" for usage information');
157
- process.exit(1);
158
- }
159
- }
160
-
161
- async function syncAllEvents(
162
- workspaceService: WorkspaceService,
163
- branchService: BranchService,
164
- threadService: ThreadService,
165
- taskService: TaskService,
166
- changeLogService: ChangeLogService,
167
- context: any
168
- ) {
169
- console.log('🔄 Starting full Claude Code event sync...');
170
-
171
- // Load session mappings
172
- const sessionMapping = loadSessionMapping(context.stateDir);
173
-
174
- // Process all events
175
- const events = loadAllEvents(context.logDir);
176
- console.log(`📊 Found ${events.length} events across ${new Set(events.map(e => e.session_id)).size} sessions`);
177
-
178
- // Group events by session
179
- const eventsBySession = groupEventsBySession(events);
180
-
181
- for (const [sessionId, sessionEvents] of eventsBySession) {
182
- console.log(`\n📝 Processing session: ${sessionId}`);
183
-
184
- // Create branch/thread if not exists
185
- if (!sessionMapping[sessionId]?.threadId) {
186
- await createSessionBranchThread(workspaceService, branchService, threadService, {
187
- ...context, sessionId
188
- });
189
- }
190
-
191
- // Sync file updates
192
- await syncFileUpdatesForSession(taskService, changeLogService, {
193
- ...context, sessionId, events: sessionEvents
194
- });
195
-
196
- // Sync todos
197
- await syncTodosForSession(taskService, {
198
- ...context, sessionId, events: sessionEvents
199
- });
200
-
201
- // Sync conversation
202
- await syncConversationForSession(threadService, {
203
- ...context, sessionId
204
- });
205
- }
206
-
207
- console.log('\n✅ Full sync complete!');
208
- }
209
-
210
- async function createSessionBranchThread(
211
- workspaceService: WorkspaceService,
212
- branchService: BranchService,
213
- threadService: ThreadService,
214
- context: any
215
- ) {
216
- const { workspaceId, branchId, branchesDocId, sessionId, projectDir, stateDir } = context;
217
-
218
- console.log(`🏗️ Creating branch/thread for session: ${sessionId}`);
219
-
220
- try {
221
- // Create new branch for this session
222
- const branchName = `claude-session-${sessionId.substring(0, 8)}`;
223
- const newBranch = await branchService.createBranch(
224
- branchId, // parent branch
225
- branchName,
226
- workspaceId,
227
- 'thread',
228
- 'claude-code',
229
- branchesDocId
230
- );
231
-
232
- // Create thread for the branch
233
- const threadName = `Claude Session ${sessionId.substring(0, 8)}`;
234
- const newThread = await threadService.createThreadWithBranch(
235
- workspaceId,
236
- branchesDocId,
237
- newBranch.id,
238
- threadName
239
- );
240
-
241
- // Update session mapping
242
- updateSessionMapping(stateDir, sessionId, {
243
- threadId: newThread.id,
244
- branchId: newBranch.id,
245
- firstSeen: new Date().toISOString(),
246
- lastActivity: new Date().toISOString()
247
- });
248
-
249
- console.log(`✅ Created branch: ${branchName} (${newBranch.id})`);
250
- console.log(`✅ Created thread: ${threadName} (${newThread.id})`);
251
-
252
- } catch (error) {
253
- console.error(`❌ Failed to create branch/thread for session ${sessionId}:`, error);
254
- }
255
- }
256
-
257
- async function syncFileUpdates(
258
- taskService: TaskService,
259
- changeLogService: ChangeLogService,
260
- context: any
261
- ) {
262
- console.log('📁 Syncing file updates to history...');
263
-
264
- const events = loadAllEvents(context.logDir);
265
- const fileEvents = events.filter(e => e.tool_name === 'Write' || e.tool_name === 'Edit');
266
-
267
- console.log(`📊 Found ${fileEvents.length} file update events`);
268
-
269
- for (const event of fileEvents) {
270
- if (event.file_path) {
271
- if (!isWorkspaceHistoryFile(event.file_path)) {
272
- console.log(`⏭️ Skipping Claude internal file: ${event.file_path}`);
273
- continue;
274
- }
275
- try {
276
- // Create or get task for this session
277
- const taskId = await getOrCreateTaskForSession(taskService, context, event.session_id, {
278
- description: `File update: ${path.basename(event.file_path)}`,
279
- source: 'claude-code',
280
- sessionId: event.session_id
281
- });
282
-
283
- // Add file update to task
284
- const fileId = path.basename(event.file_path); // Simplified file ID
285
- await taskService.addFileUpdateToTask(
286
- taskId,
287
- fileId,
288
- 'unknown', // before head
289
- 'current' // after head
290
- );
291
-
292
- console.log(`✅ Tracked file update: ${event.file_path}`);
293
-
294
- } catch (error) {
295
- console.error(`❌ Failed to track file update for ${event.file_path}:`, error);
296
- }
297
- }
298
- }
299
- }
300
-
301
- async function syncTodos(
302
- taskService: TaskService,
303
- context: any
304
- ) {
305
- console.log('📋 Syncing todos to tasks...');
306
-
307
- const events = loadAllEvents(context.logDir);
308
- const todoEvents = events.filter(e => e.tool_name === 'TodoWrite');
309
-
310
- console.log(`📊 Found ${todoEvents.length} todo events`);
311
-
312
- for (const event of todoEvents) {
313
- try {
314
- const todos = event.tool_input?.todos || [];
315
-
316
- for (const todo of todos) {
317
- const taskId = await getOrCreateTaskForSession(taskService, context, event.session_id, {
318
- description: todo.content,
319
- status: todo.status === 'in_progress' ? 'in-progress' :
320
- todo.status === 'completed' ? 'complete' : 'pending',
321
- source: 'claude-code',
322
- sessionId: event.session_id,
323
- todoId: todo.id
324
- });
325
-
326
- console.log(`✅ Synced todo: ${todo.content} (${todo.status})`);
327
- }
328
-
329
- } catch (error) {
330
- console.error(`❌ Failed to sync todos for session ${event.session_id}:`, error);
331
- }
332
- }
333
- }
334
-
335
- async function syncConversations(
336
- threadService: ThreadService,
337
- context: any
338
- ) {
339
- console.log('💬 Syncing conversations to threads...');
340
-
341
- if (!fs.existsSync(context.conversationsDir)) {
342
- console.log('📁 No conversations directory found');
343
- return;
344
- }
345
-
346
- const conversationFiles = fs.readdirSync(context.conversationsDir)
347
- .filter(f => f.endsWith('.json') && !f.endsWith('-summary.json'));
348
-
349
- console.log(`📊 Found ${conversationFiles.length} conversations`);
350
-
351
- for (const file of conversationFiles) {
352
- const sessionId = file.replace('.json', '');
353
- const conversationPath = path.join(context.conversationsDir, file);
354
-
355
- try {
356
- const conversation = JSON.parse(fs.readFileSync(conversationPath, 'utf8'));
357
-
358
- // Add conversation as thread items
359
- for (const message of conversation) {
360
- if (message.role && message.content) {
361
- // This would need to be implemented based on your thread item structure
362
- console.log(`📝 Would add message: ${message.role} - ${JSON.stringify(message.content).substring(0, 100)}...`);
363
- }
364
- }
365
-
366
- console.log(`✅ Synced conversation: ${sessionId}`);
367
-
368
- } catch (error) {
369
- console.error(`❌ Failed to sync conversation ${sessionId}:`, error);
370
- }
371
- }
372
- }
373
-
374
- async function syncFileUpdatesForSession(
375
- taskService: TaskService,
376
- changeLogService: ChangeLogService,
377
- context: any
378
- ) {
379
- const { sessionId, events } = context;
380
- const fileEvents = events.filter(e => e.tool_name === 'Write' || e.tool_name === 'Edit');
381
-
382
- for (const event of fileEvents) {
383
- if (event.file_path) {
384
- if (!isWorkspaceHistoryFile(event.file_path)) {
385
- console.log(`⏭️ Skipping Claude internal file: ${event.file_path}`);
386
- continue;
387
- }
388
- try {
389
- const taskId = await getOrCreateTaskForSession(taskService, context, sessionId, {
390
- description: `File update: ${path.basename(event.file_path)}`,
391
- source: 'claude-code',
392
- sessionId: sessionId
393
- });
394
-
395
- const fileId = path.basename(event.file_path);
396
- await taskService.addFileUpdateToTask(taskId, fileId, 'unknown', 'current');
397
-
398
- } catch (error) {
399
- console.error(`❌ Failed to track file update:`, error);
400
- }
401
- }
402
- }
403
- }
404
-
405
- async function syncTodosForSession(
406
- taskService: TaskService,
407
- context: any
408
- ) {
409
- const { sessionId, events } = context;
410
- const todoEvents = events.filter(e => e.tool_name === 'TodoWrite');
411
-
412
- for (const event of todoEvents) {
413
- const todos = event.tool_input?.todos || [];
414
-
415
- for (const todo of todos) {
416
- try {
417
- await getOrCreateTaskForSession(taskService, context, sessionId, {
418
- description: todo.content,
419
- status: todo.status === 'in_progress' ? 'in-progress' :
420
- todo.status === 'completed' ? 'complete' : 'pending',
421
- source: 'claude-code',
422
- sessionId: sessionId,
423
- todoId: todo.id
424
- });
425
-
426
- } catch (error) {
427
- console.error(`❌ Failed to sync todo:`, error);
428
- }
429
- }
430
- }
431
- }
432
-
433
- async function syncConversationForSession(
434
- threadService: ThreadService,
435
- context: any
436
- ) {
437
- const { sessionId, conversationsDir } = context;
438
- const conversationPath = path.join(conversationsDir, `${sessionId}.json`);
439
-
440
- if (fs.existsSync(conversationPath)) {
441
- try {
442
- const conversation = JSON.parse(fs.readFileSync(conversationPath, 'utf8'));
443
- console.log(`📝 Synced ${conversation.length} messages for session ${sessionId}`);
444
-
445
- // TODO: Add conversation messages to thread
446
- // This would require implementing thread item creation
447
-
448
- } catch (error) {
449
- console.error(`❌ Failed to sync conversation:`, error);
450
- }
451
- }
452
- }
453
-
454
- async function getOrCreateTaskForSession(
455
- taskService: TaskService,
456
- context: any,
457
- sessionId: string,
458
- taskData: any
459
- ): Promise<string> {
460
- // This is a simplified implementation
461
- // In practice, you'd need to:
462
- // 1. Get the tasksDocId for the session's branch
463
- // 2. Check if a task already exists for this session/todo
464
- // 3. Create or update the task accordingly
465
-
466
- const task = {
467
- branchId: context.branchId, // This should be the session's branch ID
468
- description: taskData.description,
469
- metadata: {
470
- createdBy: 'claude-sync',
471
- createdAt: new Date().toISOString(),
472
- updatedAt: new Date().toISOString(),
473
- status: taskData.status || 'pending',
474
- source: taskData.source,
475
- claude: {
476
- sessionId: taskData.sessionId,
477
- todoId: taskData.todoId
478
- }
479
- }
480
- };
481
-
482
- // This would need to be implemented with proper task creation
483
- return `task-${sessionId}-${Date.now()}`;
484
- }
485
-
486
- async function showSyncStatus(context: any) {
487
- console.log('📊 Claude Code Sync Status');
488
- console.log('==========================');
489
-
490
- const { projectDir, stateDir, logDir, conversationsDir } = context;
491
-
492
- // Check event logs
493
- if (fs.existsSync(logDir)) {
494
- const logFiles = fs.readdirSync(logDir).filter(f => f.startsWith('events-'));
495
- const totalEvents = logFiles.reduce((sum, file) => {
496
- const content = fs.readFileSync(path.join(logDir, file), 'utf8');
497
- return sum + content.split('\n').filter(line => line.trim()).length;
498
- }, 0);
499
-
500
- console.log(`📝 Event Logs: ${logFiles.length} files, ${totalEvents} events`);
501
- } else {
502
- console.log('📝 Event Logs: No logs found');
503
- }
504
-
505
- // Check conversations
506
- if (fs.existsSync(conversationsDir)) {
507
- const convFiles = fs.readdirSync(conversationsDir).filter(f => f.endsWith('.json') && !f.endsWith('-summary.json'));
508
- console.log(`💬 Conversations: ${convFiles.length} tracked`);
509
- } else {
510
- console.log('💬 Conversations: None tracked');
511
- }
512
-
513
- // Check session mappings
514
- if (fs.existsSync(path.join(stateDir, 'sessions.json'))) {
515
- const sessions = JSON.parse(fs.readFileSync(path.join(stateDir, 'sessions.json'), 'utf8'));
516
- console.log(`👥 Sessions: ${Object.keys(sessions).length} tracked`);
517
- } else {
518
- console.log('👥 Sessions: None tracked');
519
- }
520
- }
521
-
522
- function loadSessionMapping(stateDir: string): SessionMapping {
523
- const mappingPath = path.join(stateDir, 'session-mapping.json');
524
- if (fs.existsSync(mappingPath)) {
525
- return JSON.parse(fs.readFileSync(mappingPath, 'utf8'));
526
- }
527
- return {};
528
- }
529
-
530
- function updateSessionMapping(stateDir: string, sessionId: string, data: any) {
531
- const mappingPath = path.join(stateDir, 'session-mapping.json');
532
- const mapping = loadSessionMapping(stateDir);
533
-
534
- mapping[sessionId] = {
535
- ...mapping[sessionId],
536
- ...data,
537
- lastActivity: new Date().toISOString()
538
- };
539
-
540
- fs.writeFileSync(mappingPath, JSON.stringify(mapping, null, 2));
541
- }
542
-
543
- function loadAllEvents(logDir: string): ClaudeEvent[] {
544
- if (!fs.existsSync(logDir)) return [];
545
-
546
- const events: ClaudeEvent[] = [];
547
- const logFiles = fs.readdirSync(logDir).filter(f => f.startsWith('events-'));
548
-
549
- for (const file of logFiles) {
550
- const content = fs.readFileSync(path.join(logDir, file), 'utf8');
551
- const lines = content.split('\n').filter(line => line.trim());
552
-
553
- for (const line of lines) {
554
- try {
555
- events.push(JSON.parse(line));
556
- } catch (error) {
557
- console.warn(`⚠️ Failed to parse event: ${line.substring(0, 100)}...`);
558
- }
559
- }
560
- }
561
-
562
- return events;
563
- }
564
-
565
- function groupEventsBySession(events: ClaudeEvent[]): Map<string, ClaudeEvent[]> {
566
- const grouped = new Map<string, ClaudeEvent[]>();
567
-
568
- for (const event of events) {
569
- if (!grouped.has(event.session_id)) {
570
- grouped.set(event.session_id, []);
571
- }
572
- grouped.get(event.session_id)!.push(event);
573
- }
574
-
575
- return grouped;
576
- }
577
-
578
- async function processPayloadFromStdin(repo: Repo): Promise<void> {
579
- try {
580
- // Read payload from stdin
581
- let payload = '';
582
- process.stdin.setEncoding('utf8');
583
-
584
- for await (const chunk of process.stdin) {
585
- payload += chunk;
586
- }
587
-
588
- if (!payload.trim()) {
589
- console.error('❌ No payload received from stdin');
590
- process.exit(1);
591
- }
592
-
593
- const eventData = JSON.parse(payload);
594
- console.log(`🔄 Processing Claude Code event: ${eventData.tool_name} (session: ${eventData.session_id?.substring(0, 8) || 'unknown'})`);
595
-
596
- // Get workspace context
597
- const cfg = readModConfig();
598
- if (!cfg?.workspaceId) {
599
- console.error('❌ No active workspace configured in .mod/config.json');
600
- process.exit(1);
601
- }
602
-
603
- // TODO: Replace with ModWorkspace/WorkspaceHandle approach
604
- console.error('❌ claude-sync needs refactoring to work with new ModWorkspace approach');
605
- process.exit(1);
606
-
607
- /*
608
- const cache = getCliRepoCache(repo as any);
609
- const workspaceContext = await cache.ensureBranchContext({
610
- workspaceId: cfg.workspaceId,
611
- branchId: '', // Will be resolved by the cache
612
- waitForHydration: true
613
- });
614
- const { workspace, branchesDocId, branch } = workspaceContext;
615
-
616
- if (!workspace || !branch) {
617
- console.error('❌ Failed to resolve workspace context');
618
- process.exit(1);
619
- }
620
-
621
- const workspaceId = workspace.id;
622
- const branchId = branch.id;
623
-
624
- // Initialize services
625
- const workspaceService = new WorkspaceService(repo);
626
- const branchService = new BranchService(repo);
627
- const threadService = new ThreadService(repo);
628
- const taskService = new TaskService(repo);
629
- const changeLogService = new ChangeLogService(repo);
630
-
631
- const projectDir = process.cwd();
632
- const stateDir = path.join(projectDir, '.claude', 'state');
633
-
634
- // Process the event based on tool type
635
- const sessionId = eventData.session_id;
636
- const toolName = eventData.tool_name;
637
- const filePath = eventData.tool_input?.file_path;
638
-
639
- if (!sessionId) {
640
- console.error('❌ No session_id in payload');
641
- process.exit(1);
642
- }
643
-
644
- // Check if this is a new session and create branch/thread if needed
645
- const sessionMappingFile = path.join(stateDir, 'session-mapping.json');
646
- let sessionMapping: SessionMapping = {};
647
-
648
- if (fs.existsSync(sessionMappingFile)) {
649
- try {
650
- sessionMapping = JSON.parse(fs.readFileSync(sessionMappingFile, 'utf8'));
651
- } catch (e) {
652
- console.warn('⚠️ Could not read session mapping file');
653
- }
654
- }
655
-
656
- const isNewSession = !sessionMapping[sessionId];
657
-
658
- if (isNewSession) {
659
- console.log(`🆕 New Claude Code session detected: ${sessionId.substring(0, 8)}`);
660
-
661
- // Create branch/thread for this session
662
- await createSessionBranchThread(workspaceService, branchService, threadService, {
663
- workspaceId, branchId, branchesDocId, sessionId, projectDir, stateDir
664
- });
665
-
666
- // Update session mapping
667
- sessionMapping[sessionId] = {
668
- firstSeen: new Date().toISOString(),
669
- lastActivity: new Date().toISOString()
670
- };
671
- } else {
672
- // Update last activity
673
- sessionMapping[sessionId].lastActivity = new Date().toISOString();
674
- }
675
-
676
- // Process specific tool events
677
- switch (toolName) {
678
- case 'Write':
679
- case 'Edit':
680
- if (filePath) {
681
- if (!isWorkspaceHistoryFile(filePath)) {
682
- console.log(`⏭️ Skipping Claude internal file: ${filePath}`);
683
- break;
684
- }
685
- console.log(`📁 File update detected: ${path.basename(filePath)}`);
686
- // Create a task for this file update
687
- const taskDescription = `File update: ${path.basename(filePath)}`;
688
- const taskId = await createTaskForSession(taskService, branchId, branchesDocId, taskDescription, sessionId);
689
- if (taskId) {
690
- console.log(`✅ Created task for file update: ${taskId}`);
691
- }
692
- }
693
- break;
694
-
695
- case 'TodoWrite':
696
- console.log(`📋 Todo update detected`);
697
- const todos = eventData.tool_input?.todos || [];
698
- for (const todo of todos) {
699
- if (todo.content) {
700
- // Map todo status to valid task status
701
- let taskStatus: 'pending' | 'in-progress' | 'complete' | 'rejected' = 'pending';
702
- if (todo.status === 'completed') taskStatus = 'complete';
703
- else if (todo.status === 'in_progress') taskStatus = 'in-progress';
704
-
705
- const taskId = await createTaskForSession(taskService, branchId, branchesDocId, todo.content, sessionId, taskStatus);
706
- if (taskId) {
707
- console.log(`✅ Created task for todo: ${todo.content} (${taskStatus})`);
708
- }
709
- }
710
- }
711
- break;
712
-
713
- default:
714
- console.log(`🔧 Tool usage: ${toolName}`);
715
- }
716
-
717
- // Save updated session mapping
718
- fs.mkdirSync(stateDir, { recursive: true });
719
- fs.writeFileSync(sessionMappingFile, JSON.stringify(sessionMapping, null, 2));
720
-
721
- console.log(`✅ Event processed successfully for session: ${sessionId.substring(0, 8)}`);
722
-
723
- } catch (error) {
724
- console.error('❌ Error processing payload:', error);
725
- process.exit(1);
726
- }
727
- }
728
-
729
- async function createTaskForSession(
730
- taskService: TaskService,
731
- branchId: string,
732
- branchesDocId: string,
733
- description: string,
734
- sessionId: string,
735
- status: 'pending' | 'in-progress' | 'complete' | 'rejected' = 'pending'
736
- ): Promise<string | null> {
737
- try {
738
- // Ensure tasks doc exists for the branch
739
- const tasksDocId = await taskService.getOrCreateTasksDocForBranch(branchId, branchesDocId);
740
-
741
- // Create task
742
- const taskDoc = await taskService.createTask(tasksDocId, {
743
- description,
744
- branchId,
745
- context: {
746
- files: [],
747
- links: []
748
- },
749
- subtasks: [],
750
- filesUpdated: [],
751
- threadItemIds: [],
752
- metadata: {
753
- createdBy: 'claude-sync',
754
- createdAt: new Date().toISOString(),
755
- updatedAt: new Date().toISOString(),
756
- status,
757
- source: 'claude-code',
758
- claude: {
759
- sessionId
760
- }
761
- }
762
- });
763
-
764
- return taskDoc.id;
765
- } catch (error) {
766
- console.error('❌ Failed to create task:', error);
767
- return null;
768
- }
769
- }
770
-
771
- // Close comment blocks
772
- */