seeclaudecode 1.0.5 → 1.0.7

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.
package/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # SeeClaudeCode
2
2
 
3
3
  Real-time visualization of your repository structure while Claude Code edits your codebase.
4
- ![SeeClaudeCode Screenshot](https://raw.githubusercontent.com/ninajlu/seeclaudecode/main/screenshot.jpg)
4
+
5
+ ![SeeClaudeCode Screenshot](https://seeclaudecode.fly.dev/screenshot.jpg)
5
6
 
6
7
  ## Features
7
8
 
@@ -819,7 +819,7 @@
819
819
  <div class="nav-links">
820
820
  <a href="#features">Features</a>
821
821
  <a href="#how-it-works">How it works</a>
822
- <a href="https://github.com/ninajlu/seeclaudecode">GitHub</a>
822
+ <!--<a href="https://github.com/ninajlu/seeclaudecode">GitHub</a>-->
823
823
  </div>
824
824
 
825
825
  <div class="nav-buttons">
@@ -841,9 +841,9 @@
841
841
  WATCH AGENTS WRITE CODE
842
842
  </div>
843
843
 
844
- <h1>See what Claude Code<br>is actually doing.</h1>
844
+ <h1>See what Claude Code<br>is <em>actually</em> doing.</h1>
845
845
 
846
- <p>SeeClaudeCode visualizes your codebase changes and Git Diff in real-time so you know what files and directories Claude Code is editing.</p>
846
+ <p>SeeClaudeCode visualizes your codebase for changes in real-time so you know what files and directories Claude Code is editing.</p>
847
847
 
848
848
  <div class="hero-buttons">
849
849
  <a href="https://www.npmjs.com/package/seeclaudecode" class="btn btn-primary btn-lg">
@@ -853,9 +853,9 @@
853
853
  <polyline points="12 5 19 12 12 19"></polyline>
854
854
  </svg>
855
855
  </a>
856
- <a href="https://github.com/ninajlu/seeclaudecode" class="btn btn-outline btn-lg">
856
+ <!--<a href="https://github.com/ninajlu/seeclaudecode" class="btn btn-outline btn-lg">
857
857
  View on GitHub
858
- </a>
858
+ </a>-->
859
859
  </div>
860
860
  </div>
861
861
 
@@ -865,7 +865,7 @@
865
865
  <div class="preview-dot red"></div>
866
866
  <div class="preview-dot yellow"></div>
867
867
  <div class="preview-dot green"></div>
868
- <span class="preview-title">seeclaudecode_monitor.exe</span>
868
+ <span class="preview-title">seeclaudecode.exe</span>
869
869
  </div>
870
870
  <div class="preview-content">
871
871
  <div class="rf-graph">
@@ -1060,8 +1060,8 @@
1060
1060
  <!-- Features -->
1061
1061
  <section id="features" class="features">
1062
1062
  <div class="section-header">
1063
- <h2>Transparency for the AI Age</h2>
1064
- <p>Development shouldn't be a black box. We bridge the gap between AI coding agents and humans.</p>
1063
+ <h2>Can't figure out what Claude Code is doing?</h2>
1064
+ <p>Don't worry! SeeClaudeCode visualizes your code base and shows you which files and folders Claude is editing in real-time.</p>
1065
1065
  </div>
1066
1066
 
1067
1067
  <div class="features-grid">
@@ -1095,8 +1095,8 @@
1095
1095
  <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon>
1096
1096
  </svg>
1097
1097
  </div>
1098
- <h3>Instant Understanding</h3>
1099
- <p>Visual cues and color-coded status indicators help non-technical builders code.</p>
1098
+ <h3>Built for Ah-Ha Moments</h3>
1099
+ <p>Visual cues and color-coded status indicators help non-technical builders learn how to code.</p>
1100
1100
  </div>
1101
1101
  </div>
1102
1102
  </section>
@@ -1106,7 +1106,7 @@
1106
1106
  <div class="terminal-container">
1107
1107
  <div class="terminal-content">
1108
1108
  <h3>Bridging the Gap</h3>
1109
- <p>Can't figure out what Claude Code is doing? SeeClaudeCode visualizes your code base and shows you which files and folders Claude is editing in real-time.</p>
1109
+ <p>Development shouldn't be a black box. We bridge the gap between AI coding agents and humans.</p>
1110
1110
 
1111
1111
  <ul class="check-list">
1112
1112
  <li>
@@ -1181,7 +1181,7 @@
1181
1181
  </div>
1182
1182
 
1183
1183
  <div class="footer-links">
1184
- <a href="https://github.com/ninajlu/seeclaudecode">GitHub</a>
1184
+ <!--<a href="https://github.com/ninajlu/seeclaudecode">GitHub</a>-->
1185
1185
  <a href="https://www.npmjs.com/package/seeclaudecode">npm</a>
1186
1186
  </div>
1187
1187
 
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "seeclaudecode",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Real-time visualization of repository structure - watch Claude Code edit your codebase",
5
5
  "main": "server.js",
6
6
  "type": "module",
package/public/app.js CHANGED
@@ -68,8 +68,17 @@ class SeeClaude {
68
68
  switch (data.type) {
69
69
  case 'init':
70
70
  data.activeFiles?.forEach(f => this.activeFiles.add(f));
71
- data.changedFiles?.forEach(f => this.changedFiles.add(f));
71
+ data.changedFiles?.forEach(f => {
72
+ this.changedFiles.add(f);
73
+ this.updateChangedPaths(f);
74
+ });
75
+ // Restore activity history from server
76
+ if (data.activities && data.activities.length > 0) {
77
+ this.activities = data.activities;
78
+ this.renderActivityFeed();
79
+ }
72
80
  this.updateActivePaths();
81
+ this.buildGraphData(); // Rebuild graph with change data
73
82
  this.render();
74
83
  break;
75
84
 
@@ -97,25 +106,86 @@ class SeeClaude {
97
106
 
98
107
  handleActiveEdit(data) {
99
108
  this.activeFiles.add(data.file);
109
+ this.changedFiles.add(data.file); // Also mark as changed immediately
100
110
  this.updateActivePaths();
111
+ this.updateChangedPaths(data.file); // Update parent paths
101
112
 
102
113
  const fileName = data.file.split('/').pop();
103
114
  document.getElementById('current-task').textContent = `Editing ${fileName}`;
104
115
 
105
- this.addActivity('edit', `Editing ${fileName}`, data.file, true);
116
+ // Use activity from server if available, otherwise create one
117
+ if (data.activity) {
118
+ // Check if we already have this activity
119
+ if (!this.activities.find(a => a.id === data.activity.id)) {
120
+ this.activities.unshift(data.activity);
121
+ if (this.activities.length > 20) this.activities.pop();
122
+ this.renderActivityFeed();
123
+ }
124
+ } else {
125
+ this.addActivity('edit', `Editing ${fileName}`, data.file, true);
126
+ }
127
+
106
128
  this.expandPathTo(data.file);
129
+
130
+ // Rebuild graph to include the new file
131
+ this.buildGraphData();
107
132
  this.render();
108
133
  }
109
134
 
135
+ updateChangedPaths(filePath) {
136
+ const normalizedPath = filePath.replace(/\\/g, '/');
137
+ const parts = normalizedPath.split('/');
138
+ let currentPath = '';
139
+ for (let i = 0; i < parts.length - 1; i++) {
140
+ currentPath = currentPath ? `${currentPath}/${parts[i]}` : parts[i];
141
+ this.changedPaths.add(currentPath);
142
+ }
143
+ }
144
+
110
145
  handleEditComplete(data) {
111
146
  this.activeFiles.clear();
112
147
  this.activePaths.clear();
113
- data.files.forEach(f => this.changedFiles.add(f));
148
+
149
+ // Use fresh changedFiles from server if available (reflects actual git status)
150
+ if (data.changedFiles) {
151
+ this.changedFiles.clear();
152
+ this.changedPaths.clear();
153
+ data.changedFiles.forEach(f => {
154
+ this.changedFiles.add(f);
155
+ this.updateChangedPaths(f);
156
+ });
157
+ } else {
158
+ // Fallback: use diff data
159
+ if (data.diff) {
160
+ [...(data.diff.modified || []), ...(data.diff.created || []), ...(data.diff.deleted || [])].forEach(f => {
161
+ this.changedFiles.add(f);
162
+ this.updateChangedPaths(f);
163
+ });
164
+ }
165
+ }
114
166
 
115
167
  document.getElementById('current-task').textContent = `Finished editing ${data.files.length} file(s)`;
116
168
 
117
- this.addActivity('complete', `Completed editing`, `${data.files.length} file(s) modified`);
169
+ // Mark all "Editing" activities as no longer active
170
+ this.activities.forEach(a => {
171
+ if (a.isActive) a.isActive = false;
172
+ });
173
+
174
+ // Use activity from server if available, otherwise create one
175
+ if (data.activity) {
176
+ if (!this.activities.find(a => a.id === data.activity.id)) {
177
+ this.activities.unshift(data.activity);
178
+ if (this.activities.length > 20) this.activities.pop();
179
+ }
180
+ this.renderActivityFeed();
181
+ } else {
182
+ this.addActivity('complete', `Completed editing`, `${data.files.length} file(s) modified`);
183
+ }
184
+
118
185
  this.updateDiffPanel(data.diff);
186
+
187
+ // Rebuild graph with updated change data
188
+ this.buildGraphData();
119
189
  this.render();
120
190
 
121
191
  setTimeout(() => {
@@ -368,6 +438,25 @@ class SeeClaude {
368
438
  const horizontalGap = 50;
369
439
  const verticalGap = 200; // Extra space for vertical file lists below directories
370
440
 
441
+ // Build a map of changed files for quick lookup by directory
442
+ const changedFilesByDir = new Map();
443
+ this.changedFiles.forEach(filePath => {
444
+ const parts = filePath.split('/');
445
+ const fileName = parts.pop();
446
+ const dirPath = parts.length > 0 ? parts.join('/') : '.';
447
+
448
+ if (!changedFilesByDir.has(dirPath)) {
449
+ changedFilesByDir.set(dirPath, []);
450
+ }
451
+ changedFilesByDir.get(dirPath).push({
452
+ name: fileName,
453
+ path: filePath,
454
+ type: 'file',
455
+ extension: fileName.split('.').pop() || '',
456
+ category: this.getFileCategoryByExt(fileName.split('.').pop() || '')
457
+ });
458
+ });
459
+
371
460
  // Count changed files in a directory (recursive)
372
461
  const countChangedFiles = (node) => {
373
462
  if (!node) return 0;
@@ -383,7 +472,13 @@ class SeeClaude {
383
472
  if (!node.children) return thisChanged;
384
473
 
385
474
  const childCount = node.children.reduce((sum, child) => sum + countChangedFiles(child), 0);
386
- return childCount;
475
+
476
+ // Also count changed files that might not be in the structure
477
+ const extraChangedFiles = changedFilesByDir.get(node.path) || [];
478
+ const structureFilePaths = new Set((node.children || []).filter(c => c.type === 'file').map(c => c.path));
479
+ const extraCount = extraChangedFiles.filter(f => !structureFilePaths.has(f.path)).length;
480
+
481
+ return childCount + extraCount;
387
482
  };
388
483
 
389
484
  // Calculate subtree width
@@ -399,6 +494,9 @@ class SeeClaude {
399
494
  return Math.max(nodeWidth, childWidths.reduce((a, b) => a + b, 0) + horizontalGap * (dirs.length - 1));
400
495
  };
401
496
 
497
+ // Track which files we've already added
498
+ const addedFilePaths = new Set();
499
+
402
500
  // Position directories and changed files
403
501
  const positionNode = (node, depth, x, y, parentId = null) => {
404
502
  if (node.type !== 'directory') return;
@@ -409,8 +507,13 @@ class SeeClaude {
409
507
  const dirs = node.children?.filter(c => c.type === 'directory') || [];
410
508
  const changedCount = countChangedFiles(node);
411
509
 
412
- // Get changed files in this directory
413
- const changedFiles = files.filter(f => this.changedFiles.has(f.path));
510
+ // Get changed files in this directory - combine structure files and any extra changed files
511
+ const structureChangedFiles = files.filter(f => this.changedFiles.has(f.path));
512
+ const structureFilePaths = new Set(files.map(f => f.path));
513
+
514
+ // Also get changed files that aren't in the structure
515
+ const extraChangedFiles = (changedFilesByDir.get(nodeId) || []).filter(f => !structureFilePaths.has(f.path));
516
+ const allChangedFiles = [...structureChangedFiles, ...extraChangedFiles];
414
517
 
415
518
  this.graphNodes.push({
416
519
  id: nodeId,
@@ -425,7 +528,7 @@ class SeeClaude {
425
528
  dirCount: dirs.length,
426
529
  childCount: node.children?.length || 0,
427
530
  changedCount,
428
- hasChangedFiles: changedFiles.length > 0
531
+ hasChangedFiles: allChangedFiles.length > 0
429
532
  });
430
533
 
431
534
  if (parentId) {
@@ -433,15 +536,18 @@ class SeeClaude {
433
536
  }
434
537
 
435
538
  // Show changed files VERTICALLY below the directory
436
- if (changedFiles.length > 0) {
539
+ if (allChangedFiles.length > 0) {
437
540
  const fileWidth = 130;
438
541
  const fileHeight = 22;
439
542
  const fileGap = 4;
440
- const visibleFiles = changedFiles.slice(0, 5); // Limit to 5 files
543
+ const visibleFiles = allChangedFiles.slice(0, 5); // Limit to 5 files
441
544
  const fileX = x; // Centered below directory
442
545
  const startY = y + nodeHeight / 2 + 30; // Start below the directory
443
546
 
444
547
  visibleFiles.forEach((file, idx) => {
548
+ if (addedFilePaths.has(file.path)) return; // Skip if already added
549
+ addedFilePaths.add(file.path);
550
+
445
551
  const fileY = startY + idx * (fileHeight + fileGap);
446
552
 
447
553
  this.graphNodes.push({
@@ -462,11 +568,11 @@ class SeeClaude {
462
568
  });
463
569
 
464
570
  // Show "+N more" indicator if there are more files
465
- if (changedFiles.length > 5) {
571
+ if (allChangedFiles.length > 5) {
466
572
  const moreY = startY + visibleFiles.length * (fileHeight + fileGap);
467
573
  this.graphNodes.push({
468
574
  id: `${nodeId}-more`,
469
- label: `+${changedFiles.length - 5} more`,
575
+ label: `+${allChangedFiles.length - 5} more`,
470
576
  path: node.path,
471
577
  x: fileX,
472
578
  y: moreY,
@@ -491,6 +597,75 @@ class SeeClaude {
491
597
  };
492
598
 
493
599
  positionNode(this.structure, 0, 0, 50);
600
+
601
+ // Add any remaining changed files that weren't in the structure at all
602
+ // (files in directories not shown in the graph)
603
+ const orphanedFiles = [];
604
+ this.changedFiles.forEach(filePath => {
605
+ if (!addedFilePaths.has(filePath)) {
606
+ const parts = filePath.split('/');
607
+ const fileName = parts.pop();
608
+ orphanedFiles.push({
609
+ name: fileName,
610
+ path: filePath,
611
+ category: this.getFileCategoryByExt(fileName.split('.').pop() || '')
612
+ });
613
+ }
614
+ });
615
+
616
+ // If there are orphaned files, add them to the root
617
+ if (orphanedFiles.length > 0) {
618
+ const rootNode = this.graphNodes.find(n => n.id === '.');
619
+ if (rootNode) {
620
+ const fileHeight = 22;
621
+ const fileGap = 4;
622
+ const startY = rootNode.y + nodeHeight / 2 + 30;
623
+
624
+ // Check if root already has changed files below it
625
+ const existingRootFiles = this.graphNodes.filter(n =>
626
+ n.isChangedFile && this.graphEdges.some(e => e.source === '.' && e.target === n.id)
627
+ );
628
+ const offsetY = existingRootFiles.length * (fileHeight + fileGap);
629
+
630
+ orphanedFiles.slice(0, 5).forEach((file, idx) => {
631
+ const fileY = startY + offsetY + idx * (fileHeight + fileGap);
632
+
633
+ this.graphNodes.push({
634
+ id: file.path,
635
+ label: file.name,
636
+ type: file.category || 'file',
637
+ path: file.path,
638
+ x: rootNode.x,
639
+ y: fileY,
640
+ category: file.category,
641
+ isDirectory: false,
642
+ isFile: true,
643
+ isChangedFile: true,
644
+ isOrphanedFile: true,
645
+ childCount: 0
646
+ });
647
+
648
+ this.graphEdges.push({ source: '.', target: file.path, hasChanges: true, isFileEdge: true });
649
+ });
650
+ }
651
+ }
652
+ }
653
+
654
+ getFileCategoryByExt(ext) {
655
+ const categories = {
656
+ code: ['js', 'ts', 'jsx', 'tsx', 'py', 'rb', 'go', 'rs', 'java', 'c', 'cpp', 'h', 'cs', 'php', 'swift', 'kt'],
657
+ style: ['css', 'scss', 'sass', 'less', 'styl'],
658
+ markup: ['html', 'htm', 'xml', 'svg'],
659
+ config: ['json', 'yaml', 'yml', 'toml', 'ini', 'env', 'config'],
660
+ docs: ['md', 'mdx', 'txt', 'rst', 'doc', 'pdf'],
661
+ data: ['sql', 'graphql', 'prisma'],
662
+ image: ['png', 'jpg', 'jpeg', 'gif', 'webp', 'ico', 'bmp']
663
+ };
664
+
665
+ for (const [category, extensions] of Object.entries(categories)) {
666
+ if (extensions.includes(ext)) return category;
667
+ }
668
+ return 'other';
494
669
  }
495
670
 
496
671
  getNodeType(node) {
package/server.js CHANGED
@@ -29,9 +29,27 @@ const state = {
29
29
  activeFiles: new Set(),
30
30
  changedFiles: new Set(),
31
31
  repoStructure: null,
32
- lastActivity: Date.now()
32
+ lastActivity: Date.now(),
33
+ activities: [] // Persist activity history
33
34
  };
34
35
 
36
+ // Add activity to history
37
+ function addActivity(type, action, description, filePath = null, isActive = false) {
38
+ const activity = {
39
+ id: Date.now(),
40
+ type,
41
+ action,
42
+ description,
43
+ filePath,
44
+ time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
45
+ isActive
46
+ };
47
+
48
+ state.activities.unshift(activity);
49
+ if (state.activities.length > 50) state.activities.pop(); // Keep last 50 activities
50
+ return activity;
51
+ }
52
+
35
53
  // Serve static files
36
54
  app.use(express.static(join(__dirname, 'public')));
37
55
 
@@ -363,14 +381,30 @@ function broadcast(data) {
363
381
  }
364
382
 
365
383
  // WebSocket connection handler
366
- wss.on('connection', (ws) => {
384
+ wss.on('connection', async (ws) => {
367
385
  console.log('📱 Client connected');
368
386
 
369
- // Send current state to new client
387
+ // Get FRESH git status - only show files with actual pending changes
388
+ let changedFiles = [];
389
+ try {
390
+ const gitStatus = await git.status();
391
+ changedFiles = [
392
+ ...gitStatus.modified,
393
+ ...gitStatus.created,
394
+ ...gitStatus.deleted
395
+ ];
396
+ // Update server state with fresh git status
397
+ state.changedFiles = new Set(changedFiles);
398
+ } catch (e) {
399
+ // Ignore git errors
400
+ }
401
+
402
+ // Send current state to new client including activity history
370
403
  ws.send(JSON.stringify({
371
404
  type: 'init',
372
405
  activeFiles: Array.from(state.activeFiles),
373
- changedFiles: Array.from(state.changedFiles)
406
+ changedFiles: changedFiles,
407
+ activities: state.activities
374
408
  }));
375
409
 
376
410
  ws.on('close', () => {
@@ -415,12 +449,17 @@ function handleFileActivity(eventType, filePath) {
415
449
  state.changedFiles.add(relativePath);
416
450
  state.lastActivity = Date.now();
417
451
 
418
- // Broadcast active edit
452
+ // Add to activity history
453
+ const fileName = relativePath.split('/').pop();
454
+ const activity = addActivity('edit', `Editing ${fileName}`, relativePath, relativePath, true);
455
+
456
+ // Broadcast active edit with activity
419
457
  broadcast({
420
458
  type: 'active',
421
459
  file: relativePath,
422
460
  event: eventType,
423
- timestamp: Date.now()
461
+ timestamp: Date.now(),
462
+ activity
424
463
  });
425
464
 
426
465
  // Clear previous timer and set new one
@@ -434,11 +473,34 @@ function handleFileActivity(eventType, filePath) {
434
473
  // Get git diff for changed files
435
474
  const diff = await getGitDiff();
436
475
 
476
+ // Update state.changedFiles to reflect ACTUAL git status (not accumulated)
477
+ state.changedFiles = new Set([
478
+ ...(diff.modified || []),
479
+ ...(diff.created || []),
480
+ ...(diff.deleted || [])
481
+ ]);
482
+
483
+ // Add completion activity
484
+ const completeActivity = addActivity(
485
+ 'complete',
486
+ 'Completed editing',
487
+ `${completedFiles.length} file(s) modified`,
488
+ null,
489
+ false
490
+ );
491
+
492
+ // Mark all "Editing" activities as no longer active
493
+ state.activities.forEach(a => {
494
+ if (a.isActive) a.isActive = false;
495
+ });
496
+
437
497
  broadcast({
438
498
  type: 'complete',
439
499
  files: completedFiles,
440
500
  diff,
441
- timestamp: Date.now()
501
+ changedFiles: Array.from(state.changedFiles), // Send fresh list
502
+ timestamp: Date.now(),
503
+ activity: completeActivity
442
504
  });
443
505
 
444
506
  console.log(`✅ Editing complete. Changed files: ${completedFiles.join(', ')}`);