seeclaudecode 1.0.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.
- package/.github/workflows/publish.yml +27 -0
- package/README.md +138 -0
- package/bin/cli.js +300 -0
- package/package.json +40 -0
- package/public/app.js +1282 -0
- package/public/index.html +209 -0
- package/public/styles.css +1725 -0
- package/server.js +458 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
publish:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
id-token: write
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- uses: actions/setup-node@v4
|
|
19
|
+
with:
|
|
20
|
+
node-version: '20'
|
|
21
|
+
registry-url: 'https://registry.npmjs.org'
|
|
22
|
+
|
|
23
|
+
- run: npm install
|
|
24
|
+
|
|
25
|
+
- run: npm publish --access public
|
|
26
|
+
env:
|
|
27
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# SeeClaudeCode
|
|
2
|
+
|
|
3
|
+
Real-time visualization of your repository structure while Claude Code edits your codebase.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Architecture Graph** - Interactive diagram showing your project's directory structure with pan/zoom
|
|
8
|
+
- **Sunburst View** - Radial visualization of your codebase hierarchy
|
|
9
|
+
- **File Explorer** - Traditional tree view with change tracking
|
|
10
|
+
- **Real-time Git Diff** - See exactly what changed in any file or directory
|
|
11
|
+
- **Live Highlighting**:
|
|
12
|
+
- **Yellow pulse** - Files with pending changes (git diff)
|
|
13
|
+
- **Green pulse** - Files currently being edited
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g seeclaudecode
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Interactive mode - prompts for directory selection
|
|
25
|
+
seeclaudecode
|
|
26
|
+
|
|
27
|
+
# Monitor specific directory
|
|
28
|
+
seeclaudecode ./my-project
|
|
29
|
+
|
|
30
|
+
# Monitor with absolute path
|
|
31
|
+
seeclaudecode /path/to/repo
|
|
32
|
+
|
|
33
|
+
# Use custom port
|
|
34
|
+
seeclaudecode . --port 4000
|
|
35
|
+
|
|
36
|
+
# Don't auto-open browser
|
|
37
|
+
seeclaudecode . --no-open
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
When started without a directory argument, you'll see an interactive menu:
|
|
41
|
+
```
|
|
42
|
+
Select a directory to monitor:
|
|
43
|
+
|
|
44
|
+
1) Current directory
|
|
45
|
+
/Users/you/projects
|
|
46
|
+
2) my-app
|
|
47
|
+
/Users/you/projects/my-app
|
|
48
|
+
|
|
49
|
+
Or enter a custom path
|
|
50
|
+
|
|
51
|
+
📁 Directory:
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Switching Directories
|
|
55
|
+
|
|
56
|
+
While running, you can switch to a different directory at any time:
|
|
57
|
+
- Type a new path and press Enter to switch
|
|
58
|
+
- Type `quit` or `exit` to stop
|
|
59
|
+
|
|
60
|
+
## Options
|
|
61
|
+
|
|
62
|
+
| Option | Description |
|
|
63
|
+
|--------|-------------|
|
|
64
|
+
| `-p, --port <port>` | Port to run server on (default: 3847) |
|
|
65
|
+
| `-n, --no-open` | Don't automatically open browser |
|
|
66
|
+
| `-h, --help` | Show help message |
|
|
67
|
+
| `-v, --version` | Show version number |
|
|
68
|
+
|
|
69
|
+
## How It Works
|
|
70
|
+
|
|
71
|
+
1. Start SeeClaudeCode (it will prompt for a directory or use the one you specify)
|
|
72
|
+
2. Open the displayed URL in your browser
|
|
73
|
+
3. Run Claude Code in the same directory
|
|
74
|
+
4. Watch as files pulse and highlight when Claude makes changes
|
|
75
|
+
5. Click on any file or directory to see the git diff
|
|
76
|
+
6. Switch directories anytime by typing a new path
|
|
77
|
+
|
|
78
|
+
## Views
|
|
79
|
+
|
|
80
|
+
### Architecture Graph
|
|
81
|
+
An interactive node-based diagram showing your project structure. Changed files appear below their parent directories with pulsing yellow highlights.
|
|
82
|
+
|
|
83
|
+
### Sunburst View
|
|
84
|
+
A radial visualization where each ring represents a directory level. Click on segments to see diffs for that directory.
|
|
85
|
+
|
|
86
|
+
### File Explorer
|
|
87
|
+
A traditional tree view showing all files with color-coded change indicators.
|
|
88
|
+
|
|
89
|
+
## Visual Indicators
|
|
90
|
+
|
|
91
|
+
| Indicator | Meaning |
|
|
92
|
+
|-----------|---------|
|
|
93
|
+
| Yellow pulsing | Pending changes (in git diff) |
|
|
94
|
+
| Green pulsing | Currently being edited |
|
|
95
|
+
| Purple nodes | Directories |
|
|
96
|
+
| Green nodes | Code files (.js, .ts, .py, etc.) |
|
|
97
|
+
| Pink nodes | Style files (.css, .scss) |
|
|
98
|
+
| Orange nodes | Config files (.json, .yaml) |
|
|
99
|
+
| Blue nodes | Documentation (.md, .txt) |
|
|
100
|
+
|
|
101
|
+
## Requirements
|
|
102
|
+
|
|
103
|
+
- Node.js 18.0.0 or higher
|
|
104
|
+
- Git (for diff functionality)
|
|
105
|
+
|
|
106
|
+
## Development
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# Clone and install
|
|
110
|
+
git clone <repo-url>
|
|
111
|
+
cd seeclaudecode
|
|
112
|
+
npm install
|
|
113
|
+
|
|
114
|
+
# Run locally
|
|
115
|
+
npm start /path/to/repo
|
|
116
|
+
|
|
117
|
+
# Link for global testing
|
|
118
|
+
npm link
|
|
119
|
+
seeclaudecode /path/to/repo
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Architecture
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
seeclaudecode/
|
|
126
|
+
├── bin/
|
|
127
|
+
│ └── cli.js # CLI entry point
|
|
128
|
+
├── public/
|
|
129
|
+
│ ├── index.html # Main HTML structure
|
|
130
|
+
│ ├── styles.css # Styling and animations
|
|
131
|
+
│ └── app.js # Frontend visualization logic
|
|
132
|
+
├── server.js # Express server with WebSocket & file watching
|
|
133
|
+
└── package.json
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
MIT
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname, join, resolve } from 'path';
|
|
5
|
+
import { existsSync, statSync, readdirSync } from 'fs';
|
|
6
|
+
import { spawn } from 'child_process';
|
|
7
|
+
import { createInterface } from 'readline';
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = dirname(__filename);
|
|
11
|
+
|
|
12
|
+
// Parse arguments
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
let targetDir = null; // Don't default to cwd - prompt instead
|
|
15
|
+
let port = 3847;
|
|
16
|
+
let openBrowser = true;
|
|
17
|
+
|
|
18
|
+
// Help text
|
|
19
|
+
const helpText = `
|
|
20
|
+
SeeClaudeCode - Watch Claude Code edit your codebase in real-time
|
|
21
|
+
|
|
22
|
+
Usage:
|
|
23
|
+
seeclaudecode [directory] [options]
|
|
24
|
+
|
|
25
|
+
Arguments:
|
|
26
|
+
directory Directory to monitor (will prompt if not provided)
|
|
27
|
+
|
|
28
|
+
Options:
|
|
29
|
+
-p, --port <port> Port to run server on (default: 3847)
|
|
30
|
+
-n, --no-open Don't automatically open browser
|
|
31
|
+
-h, --help Show this help message
|
|
32
|
+
-v, --version Show version number
|
|
33
|
+
|
|
34
|
+
Examples:
|
|
35
|
+
seeclaudecode # Prompts for directory
|
|
36
|
+
seeclaudecode ./my-project # Monitor specific directory
|
|
37
|
+
seeclaudecode /path/to/repo # Monitor absolute path
|
|
38
|
+
seeclaudecode . --port 4000 # Use custom port
|
|
39
|
+
|
|
40
|
+
While running:
|
|
41
|
+
Type a new path and press Enter to switch directories
|
|
42
|
+
Type 'quit' or 'exit' to stop
|
|
43
|
+
|
|
44
|
+
Once running, open the displayed URL in your browser to see:
|
|
45
|
+
- Architecture graph of your codebase
|
|
46
|
+
- Sunburst visualization
|
|
47
|
+
- File explorer with change tracking
|
|
48
|
+
- Real-time git diff viewer
|
|
49
|
+
- Yellow pulsing highlights for pending changes
|
|
50
|
+
- Green pulsing for files being actively edited
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
// Parse arguments
|
|
54
|
+
for (let i = 0; i < args.length; i++) {
|
|
55
|
+
const arg = args[i];
|
|
56
|
+
|
|
57
|
+
if (arg === '-h' || arg === '--help') {
|
|
58
|
+
console.log(helpText);
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (arg === '-v' || arg === '--version') {
|
|
63
|
+
const pkg = await import('../package.json', { with: { type: 'json' } });
|
|
64
|
+
console.log(`seeclaudecode v${pkg.default.version}`);
|
|
65
|
+
process.exit(0);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (arg === '-p' || arg === '--port') {
|
|
69
|
+
port = parseInt(args[++i], 10);
|
|
70
|
+
if (isNaN(port)) {
|
|
71
|
+
console.error('Error: Invalid port number');
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (arg === '-n' || arg === '--no-open') {
|
|
78
|
+
openBrowser = false;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!arg.startsWith('-')) {
|
|
83
|
+
targetDir = resolve(arg);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Create readline interface
|
|
88
|
+
const rl = createInterface({
|
|
89
|
+
input: process.stdin,
|
|
90
|
+
output: process.stdout
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Promisified question
|
|
94
|
+
function question(prompt) {
|
|
95
|
+
return new Promise((resolve) => {
|
|
96
|
+
rl.question(prompt, resolve);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Validate directory
|
|
101
|
+
function validateDirectory(dir) {
|
|
102
|
+
const resolved = resolve(dir);
|
|
103
|
+
if (!existsSync(resolved)) {
|
|
104
|
+
return { valid: false, error: `Directory does not exist: ${resolved}` };
|
|
105
|
+
}
|
|
106
|
+
if (!statSync(resolved).isDirectory()) {
|
|
107
|
+
return { valid: false, error: `Path is not a directory: ${resolved}` };
|
|
108
|
+
}
|
|
109
|
+
return { valid: true, path: resolved };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Get suggested directories (recent git repos, common project locations)
|
|
113
|
+
function getSuggestedDirs() {
|
|
114
|
+
const suggestions = [];
|
|
115
|
+
const cwd = process.cwd();
|
|
116
|
+
|
|
117
|
+
// Add current directory
|
|
118
|
+
suggestions.push({ path: cwd, label: 'Current directory' });
|
|
119
|
+
|
|
120
|
+
// Check for subdirectories that look like projects
|
|
121
|
+
try {
|
|
122
|
+
const entries = readdirSync(cwd, { withFileTypes: true });
|
|
123
|
+
for (const entry of entries) {
|
|
124
|
+
if (entry.isDirectory() && !entry.name.startsWith('.') && !entry.name.startsWith('node_modules')) {
|
|
125
|
+
const subPath = join(cwd, entry.name);
|
|
126
|
+
// Check if it's a git repo or has package.json
|
|
127
|
+
if (existsSync(join(subPath, '.git')) || existsSync(join(subPath, 'package.json'))) {
|
|
128
|
+
suggestions.push({ path: subPath, label: entry.name });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} catch (e) {
|
|
133
|
+
// Ignore errors
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return suggestions.slice(0, 5); // Max 5 suggestions
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Print banner
|
|
140
|
+
function printBanner() {
|
|
141
|
+
console.log(`
|
|
142
|
+
\x1b[36m╔═══════════════════════════════════════════════════════════╗
|
|
143
|
+
║ ║
|
|
144
|
+
║ \x1b[33m👁️ SeeClaudeCode\x1b[36m ║
|
|
145
|
+
║ \x1b[90mWatch Claude Code edit your codebase in real-time\x1b[36m ║
|
|
146
|
+
║ ║
|
|
147
|
+
╚═══════════════════════════════════════════════════════════╝\x1b[0m
|
|
148
|
+
`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Prompt for directory
|
|
152
|
+
async function promptForDirectory() {
|
|
153
|
+
const suggestions = getSuggestedDirs();
|
|
154
|
+
|
|
155
|
+
console.log('\x1b[36mSelect a directory to monitor:\x1b[0m\n');
|
|
156
|
+
|
|
157
|
+
suggestions.forEach((s, i) => {
|
|
158
|
+
console.log(` \x1b[33m${i + 1})\x1b[0m ${s.label}`);
|
|
159
|
+
if (s.label !== s.path) {
|
|
160
|
+
console.log(` \x1b[90m${s.path}\x1b[0m`);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
console.log(`\n \x1b[90mOr enter a custom path\x1b[0m\n`);
|
|
165
|
+
|
|
166
|
+
while (true) {
|
|
167
|
+
const answer = await question('\x1b[36m📁 Directory:\x1b[0m ');
|
|
168
|
+
|
|
169
|
+
// Check if it's a number selection
|
|
170
|
+
const num = parseInt(answer, 10);
|
|
171
|
+
if (!isNaN(num) && num >= 1 && num <= suggestions.length) {
|
|
172
|
+
return suggestions[num - 1].path;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Treat as a path
|
|
176
|
+
if (answer.trim()) {
|
|
177
|
+
const result = validateDirectory(answer.trim());
|
|
178
|
+
if (result.valid) {
|
|
179
|
+
return result.path;
|
|
180
|
+
}
|
|
181
|
+
console.log(`\x1b[31m${result.error}\x1b[0m`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Server management
|
|
187
|
+
let server = null;
|
|
188
|
+
let currentDir = null;
|
|
189
|
+
|
|
190
|
+
function startServer(dir) {
|
|
191
|
+
currentDir = dir;
|
|
192
|
+
|
|
193
|
+
console.log(`\n\x1b[90m📁 Monitoring:\x1b[0m ${dir}`);
|
|
194
|
+
console.log(`\x1b[90m🌐 Server:\x1b[0m http://localhost:${port}`);
|
|
195
|
+
console.log(`\n\x1b[90m💡 Type a new path to switch directories, or 'quit' to exit\x1b[0m\n`);
|
|
196
|
+
|
|
197
|
+
const serverPath = join(__dirname, '..', 'server.js');
|
|
198
|
+
server = spawn('node', [serverPath, dir], {
|
|
199
|
+
stdio: ['pipe', 'inherit', 'inherit'],
|
|
200
|
+
env: { ...process.env, PORT: port.toString() }
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
server.on('close', (code) => {
|
|
204
|
+
if (code !== null && code !== 0) {
|
|
205
|
+
console.error(`\x1b[31mServer exited with code ${code}\x1b[0m`);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
return server;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function stopServer() {
|
|
213
|
+
if (server) {
|
|
214
|
+
server.kill('SIGTERM');
|
|
215
|
+
server = null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function restartServer(newDir) {
|
|
220
|
+
console.log(`\n\x1b[33m🔄 Switching to: ${newDir}\x1b[0m`);
|
|
221
|
+
stopServer();
|
|
222
|
+
|
|
223
|
+
// Brief delay before restarting
|
|
224
|
+
setTimeout(() => {
|
|
225
|
+
startServer(newDir);
|
|
226
|
+
}, 500);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Main
|
|
230
|
+
async function main() {
|
|
231
|
+
printBanner();
|
|
232
|
+
|
|
233
|
+
// If directory wasn't provided as argument, prompt for it
|
|
234
|
+
if (!targetDir) {
|
|
235
|
+
targetDir = await promptForDirectory();
|
|
236
|
+
} else {
|
|
237
|
+
// Validate provided directory
|
|
238
|
+
const result = validateDirectory(targetDir);
|
|
239
|
+
if (!result.valid) {
|
|
240
|
+
console.error(`\x1b[31mError: ${result.error}\x1b[0m`);
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
targetDir = result.path;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Start the server
|
|
247
|
+
startServer(targetDir);
|
|
248
|
+
|
|
249
|
+
// Open browser after a short delay
|
|
250
|
+
if (openBrowser) {
|
|
251
|
+
setTimeout(async () => {
|
|
252
|
+
try {
|
|
253
|
+
const open = (await import('open')).default;
|
|
254
|
+
await open(`http://localhost:${port}`);
|
|
255
|
+
} catch (e) {
|
|
256
|
+
// Silently fail if can't open browser
|
|
257
|
+
}
|
|
258
|
+
}, 1500);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Listen for directory changes
|
|
262
|
+
rl.on('line', (input) => {
|
|
263
|
+
const trimmed = input.trim();
|
|
264
|
+
|
|
265
|
+
if (trimmed === 'quit' || trimmed === 'exit') {
|
|
266
|
+
console.log('\n\x1b[90mShutting down...\x1b[0m');
|
|
267
|
+
stopServer();
|
|
268
|
+
rl.close();
|
|
269
|
+
process.exit(0);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (trimmed) {
|
|
273
|
+
const result = validateDirectory(trimmed);
|
|
274
|
+
if (result.valid) {
|
|
275
|
+
restartServer(result.path);
|
|
276
|
+
} else {
|
|
277
|
+
console.log(`\x1b[31m${result.error}\x1b[0m`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// Handle exit
|
|
283
|
+
process.on('SIGINT', () => {
|
|
284
|
+
console.log('\n\x1b[90mShutting down...\x1b[0m');
|
|
285
|
+
stopServer();
|
|
286
|
+
rl.close();
|
|
287
|
+
process.exit(0);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
process.on('SIGTERM', () => {
|
|
291
|
+
stopServer();
|
|
292
|
+
rl.close();
|
|
293
|
+
process.exit(0);
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
main().catch((err) => {
|
|
298
|
+
console.error(err);
|
|
299
|
+
process.exit(1);
|
|
300
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "seeclaudecode",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Real-time visualization of repository structure - watch Claude Code edit your codebase",
|
|
5
|
+
"main": "server.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"seeclaudecode": "./bin/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node server.js",
|
|
12
|
+
"dev": "node server.js"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"chokidar": "^3.5.3",
|
|
16
|
+
"express": "^4.18.2",
|
|
17
|
+
"open": "^10.0.0",
|
|
18
|
+
"simple-git": "^3.22.0",
|
|
19
|
+
"ws": "^8.16.0"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"visualization",
|
|
23
|
+
"repository",
|
|
24
|
+
"claude-code",
|
|
25
|
+
"claude",
|
|
26
|
+
"developer-tools",
|
|
27
|
+
"git",
|
|
28
|
+
"diff",
|
|
29
|
+
"real-time"
|
|
30
|
+
],
|
|
31
|
+
"author": "",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": ""
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
}
|
|
40
|
+
}
|