viewcc 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/LICENSE +21 -0
- package/README.md +160 -0
- package/bin/claude-viz.js +9 -0
- package/dist/assets/index-BQSevtzO.js +110 -0
- package/dist/assets/index-lL8g_3Ei.css +1 -0
- package/dist/data/graph-data.json +388 -0
- package/dist/index.html +23 -0
- package/lib/cli.d.ts +3 -0
- package/lib/cli.d.ts.map +1 -0
- package/lib/cli.js +132 -0
- package/lib/cli.js.map +1 -0
- package/lib/scanner.d.ts +15 -0
- package/lib/scanner.d.ts.map +1 -0
- package/lib/scanner.js +330 -0
- package/lib/scanner.js.map +1 -0
- package/lib/server.d.ts +12 -0
- package/lib/server.d.ts.map +1 -0
- package/lib/server.js +79 -0
- package/lib/server.js.map +1 -0
- package/lib/utils.d.ts +15 -0
- package/lib/utils.d.ts.map +1 -0
- package/lib/utils.js +67 -0
- package/lib/utils.js.map +1 -0
- package/package.json +59 -0
package/lib/scanner.js
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { glob } from 'glob';
|
|
4
|
+
/**
|
|
5
|
+
* Parse YAML front matter from markdown content using regex
|
|
6
|
+
*/
|
|
7
|
+
function parseYamlFrontmatter(content) {
|
|
8
|
+
const yamlPattern = /^---\s*\n(.*?)\n---\s*\n/s;
|
|
9
|
+
const match = content.match(yamlPattern);
|
|
10
|
+
if (!match) {
|
|
11
|
+
return {};
|
|
12
|
+
}
|
|
13
|
+
const yamlContent = match[1];
|
|
14
|
+
const result = {};
|
|
15
|
+
let currentKey = null;
|
|
16
|
+
let currentList = [];
|
|
17
|
+
for (const line of yamlContent.split('\n')) {
|
|
18
|
+
// Skip empty lines
|
|
19
|
+
if (!line.trim()) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
// Check for list item
|
|
23
|
+
const listMatch = line.match(/^\s+-\s+(.+)$/);
|
|
24
|
+
if (listMatch && currentKey) {
|
|
25
|
+
currentList.push(listMatch[1].trim());
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
// Check for key-value pair
|
|
29
|
+
const kvMatch = line.match(/^(\w+):\s*(.*)$/);
|
|
30
|
+
if (kvMatch) {
|
|
31
|
+
// Save previous list if exists
|
|
32
|
+
if (currentKey && currentList.length > 0) {
|
|
33
|
+
result[currentKey] = currentList;
|
|
34
|
+
currentList = [];
|
|
35
|
+
}
|
|
36
|
+
const key = kvMatch[1];
|
|
37
|
+
let value = kvMatch[2].trim();
|
|
38
|
+
if (value) {
|
|
39
|
+
// Handle quoted strings
|
|
40
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
41
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
42
|
+
value = value.slice(1, -1);
|
|
43
|
+
}
|
|
44
|
+
result[key] = value;
|
|
45
|
+
currentKey = null;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// Likely a list follows
|
|
49
|
+
currentKey = key;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Save final list if exists
|
|
54
|
+
if (currentKey && currentList.length > 0) {
|
|
55
|
+
result[currentKey] = currentList;
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Extract content after YAML front matter
|
|
61
|
+
*/
|
|
62
|
+
function extractBodyContent(content) {
|
|
63
|
+
const yamlPattern = /^---\s*\n.*?\n---\s*\n/s;
|
|
64
|
+
return content.replace(yamlPattern, '').trim();
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Parse a field that can be either a string (comma-separated) or array
|
|
68
|
+
*/
|
|
69
|
+
function parseListField(value) {
|
|
70
|
+
if (Array.isArray(value)) {
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
if (typeof value === 'string') {
|
|
74
|
+
return value.split(',').map(s => s.trim()).filter(Boolean);
|
|
75
|
+
}
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Scan agents directory for agent definitions
|
|
80
|
+
*/
|
|
81
|
+
async function scanAgents(claudePath) {
|
|
82
|
+
const agents = [];
|
|
83
|
+
const agentsDir = path.join(claudePath, 'agents');
|
|
84
|
+
try {
|
|
85
|
+
await fs.access(agentsDir);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return agents;
|
|
89
|
+
}
|
|
90
|
+
const agentFiles = await glob('*.md', { cwd: agentsDir });
|
|
91
|
+
for (const file of agentFiles) {
|
|
92
|
+
try {
|
|
93
|
+
const filePath = path.join(agentsDir, file);
|
|
94
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
95
|
+
const metadata = parseYamlFrontmatter(content);
|
|
96
|
+
const body = extractBodyContent(content);
|
|
97
|
+
const tools = parseListField(metadata.tools);
|
|
98
|
+
const subagents = parseListField(metadata.subagents);
|
|
99
|
+
const skills = parseListField(metadata.skills);
|
|
100
|
+
const basename = path.parse(file).name;
|
|
101
|
+
const relativePath = path.relative(path.dirname(claudePath), filePath);
|
|
102
|
+
const agent = {
|
|
103
|
+
id: `agent:${basename}`,
|
|
104
|
+
type: 'agent',
|
|
105
|
+
name: metadata.name || basename,
|
|
106
|
+
description: metadata.description || '',
|
|
107
|
+
tools,
|
|
108
|
+
model: metadata.model || '',
|
|
109
|
+
subagents,
|
|
110
|
+
skills,
|
|
111
|
+
filePath: relativePath,
|
|
112
|
+
systemPrompt: body.length > 500 ? body.slice(0, 500) + '...' : body
|
|
113
|
+
};
|
|
114
|
+
agents.push(agent);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
console.warn(`Warning: Failed to parse ${file}: ${error.message}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return agents;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Scan skills directory for skill definitions
|
|
124
|
+
*/
|
|
125
|
+
async function scanSkills(claudePath) {
|
|
126
|
+
const skills = [];
|
|
127
|
+
const skillsDir = path.join(claudePath, 'skills');
|
|
128
|
+
try {
|
|
129
|
+
await fs.access(skillsDir);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return skills;
|
|
133
|
+
}
|
|
134
|
+
const entries = await fs.readdir(skillsDir, { withFileTypes: true });
|
|
135
|
+
for (const entry of entries) {
|
|
136
|
+
if (!entry.isDirectory()) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const skillDir = path.join(skillsDir, entry.name);
|
|
140
|
+
const skillFile = path.join(skillDir, 'SKILL.md');
|
|
141
|
+
try {
|
|
142
|
+
await fs.access(skillFile);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
const content = await fs.readFile(skillFile, 'utf-8');
|
|
149
|
+
const metadata = parseYamlFrontmatter(content);
|
|
150
|
+
const body = extractBodyContent(content);
|
|
151
|
+
// Extract trigger patterns from body
|
|
152
|
+
const triggers = [];
|
|
153
|
+
const triggerMatch = body.match(/##\s*(?:Triggers?|사용\s*시점).*?\n(.*?)(?=\n##|\Z)/is);
|
|
154
|
+
if (triggerMatch) {
|
|
155
|
+
const triggerLines = triggerMatch[1].trim().split('\n');
|
|
156
|
+
triggers.push(...triggerLines
|
|
157
|
+
.filter(line => line.trim().startsWith('-'))
|
|
158
|
+
.map(line => line.replace(/^-\s*/, '').trim())
|
|
159
|
+
.slice(0, 5) // Limit to 5 triggers
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
const relativePath = path.relative(path.dirname(claudePath), skillFile);
|
|
163
|
+
const hasScripts = await fs.access(path.join(skillDir, 'scripts'))
|
|
164
|
+
.then(() => true)
|
|
165
|
+
.catch(() => false);
|
|
166
|
+
const hasWebapp = await fs.access(path.join(skillDir, 'webapp'))
|
|
167
|
+
.then(() => true)
|
|
168
|
+
.catch(() => false);
|
|
169
|
+
const skill = {
|
|
170
|
+
id: `skill:${entry.name}`,
|
|
171
|
+
type: 'skill',
|
|
172
|
+
name: metadata.name || entry.name,
|
|
173
|
+
description: metadata.description || '',
|
|
174
|
+
triggers,
|
|
175
|
+
filePath: relativePath,
|
|
176
|
+
hasScripts,
|
|
177
|
+
hasWebapp
|
|
178
|
+
};
|
|
179
|
+
skills.push(skill);
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
console.warn(`Warning: Failed to parse ${skillFile}: ${error.message}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return skills;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Scan commands directory for slash command definitions
|
|
189
|
+
*/
|
|
190
|
+
async function scanCommands(claudePath) {
|
|
191
|
+
const commands = [];
|
|
192
|
+
const commandsDir = path.join(claudePath, 'commands');
|
|
193
|
+
try {
|
|
194
|
+
await fs.access(commandsDir);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return commands;
|
|
198
|
+
}
|
|
199
|
+
const commandFiles = await glob('*.md', { cwd: commandsDir });
|
|
200
|
+
for (const file of commandFiles) {
|
|
201
|
+
try {
|
|
202
|
+
const filePath = path.join(commandsDir, file);
|
|
203
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
204
|
+
const metadata = parseYamlFrontmatter(content);
|
|
205
|
+
const basename = path.parse(file).name;
|
|
206
|
+
const relativePath = path.relative(path.dirname(claudePath), filePath);
|
|
207
|
+
const command = {
|
|
208
|
+
id: `skill:${basename}`,
|
|
209
|
+
type: 'skill',
|
|
210
|
+
subtype: 'command',
|
|
211
|
+
name: basename,
|
|
212
|
+
description: metadata.description || '',
|
|
213
|
+
argumentHint: metadata['argument-hint'] || '',
|
|
214
|
+
filePath: relativePath,
|
|
215
|
+
hasScripts: false,
|
|
216
|
+
hasWebapp: false,
|
|
217
|
+
triggers: []
|
|
218
|
+
};
|
|
219
|
+
commands.push(command);
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
console.warn(`Warning: Failed to parse ${file}: ${error.message}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return commands;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Find relationships between agents and skills
|
|
229
|
+
*/
|
|
230
|
+
async function findRelationships(agents, skills, claudePath) {
|
|
231
|
+
const edges = [];
|
|
232
|
+
const addedEdges = new Set();
|
|
233
|
+
function addEdge(source, target, type) {
|
|
234
|
+
const edgeKey = `${source}->${target}`;
|
|
235
|
+
if (!addedEdges.has(edgeKey)) {
|
|
236
|
+
edges.push({ source, target, type });
|
|
237
|
+
addedEdges.add(edgeKey);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// Build lookup maps
|
|
241
|
+
const agentMap = new Map();
|
|
242
|
+
for (const agent of agents) {
|
|
243
|
+
agentMap.set(agent.name.toLowerCase(), agent.id);
|
|
244
|
+
agentMap.set(agent.id.replace('agent:', '').toLowerCase(), agent.id);
|
|
245
|
+
}
|
|
246
|
+
const skillMap = new Map();
|
|
247
|
+
for (const skill of skills) {
|
|
248
|
+
skillMap.set(skill.name.toLowerCase(), skill.id);
|
|
249
|
+
skillMap.set(skill.id.replace('skill:', '').toLowerCase(), skill.id);
|
|
250
|
+
}
|
|
251
|
+
// Process agent relationships from YAML metadata
|
|
252
|
+
for (const agent of agents) {
|
|
253
|
+
// Agent -> Subagent relationships
|
|
254
|
+
for (const subagentName of agent.subagents) {
|
|
255
|
+
const subagentKey = subagentName.toLowerCase().trim();
|
|
256
|
+
const subagentId = agentMap.get(subagentKey);
|
|
257
|
+
if (subagentId) {
|
|
258
|
+
addEdge(agent.id, subagentId, 'calls');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// Agent -> Skill relationships
|
|
262
|
+
for (const skillName of agent.skills) {
|
|
263
|
+
const skillKey = skillName.toLowerCase().trim();
|
|
264
|
+
const skillId = skillMap.get(skillKey);
|
|
265
|
+
if (skillId) {
|
|
266
|
+
addEdge(agent.id, skillId, 'uses');
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// Check agent files for skill references (content-based)
|
|
271
|
+
for (const agent of agents) {
|
|
272
|
+
const agentFilePath = path.join(path.dirname(claudePath), agent.filePath);
|
|
273
|
+
try {
|
|
274
|
+
const content = (await fs.readFile(agentFilePath, 'utf-8')).toLowerCase();
|
|
275
|
+
for (const skill of skills) {
|
|
276
|
+
const skillName = skill.name.toLowerCase();
|
|
277
|
+
const skillId = skill.id.replace('skill:', '');
|
|
278
|
+
if (content.includes(skillName) || content.includes(skillId)) {
|
|
279
|
+
addEdge(agent.id, skill.id, 'uses');
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
// Skip if file can't be read
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return edges;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Scan a Claude Code project and generate graph data
|
|
291
|
+
*/
|
|
292
|
+
export async function scanProject(projectPath, outputPath) {
|
|
293
|
+
const project = path.resolve(projectPath);
|
|
294
|
+
const claudePath = path.join(project, '.claude');
|
|
295
|
+
// Verify .claude directory exists
|
|
296
|
+
try {
|
|
297
|
+
await fs.access(claudePath);
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
throw new Error(`No .claude folder found in ${project}`);
|
|
301
|
+
}
|
|
302
|
+
// Scan all components
|
|
303
|
+
const agents = await scanAgents(claudePath);
|
|
304
|
+
const skills = await scanSkills(claudePath);
|
|
305
|
+
const commands = await scanCommands(claudePath);
|
|
306
|
+
// Combine skills and commands
|
|
307
|
+
const allSkills = [...skills, ...commands];
|
|
308
|
+
// Find relationships
|
|
309
|
+
const edges = await findRelationships(agents, allSkills, claudePath);
|
|
310
|
+
// Build result
|
|
311
|
+
const result = {
|
|
312
|
+
nodes: [...agents, ...allSkills],
|
|
313
|
+
edges,
|
|
314
|
+
metadata: {
|
|
315
|
+
generatedAt: new Date().toISOString(),
|
|
316
|
+
projectPath: project,
|
|
317
|
+
projectName: path.basename(project),
|
|
318
|
+
agentCount: agents.length,
|
|
319
|
+
skillCount: skills.length,
|
|
320
|
+
commandCount: commands.length,
|
|
321
|
+
edgeCount: edges.length
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
// Write output
|
|
325
|
+
const outputDir = path.dirname(outputPath);
|
|
326
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
327
|
+
await fs.writeFile(outputPath, JSON.stringify(result, null, 2), 'utf-8');
|
|
328
|
+
return result.metadata;
|
|
329
|
+
}
|
|
330
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAoD5B;;GAEG;AACH,SAAS,oBAAoB,CAAC,OAAe;IAC3C,MAAM,WAAW,GAAG,2BAA2B,CAAC;IAChD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAEzC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,MAAM,MAAM,GAAwB,EAAE,CAAC;IACvC,IAAI,UAAU,GAAkB,IAAI,CAAC;IACrC,IAAI,WAAW,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,mBAAmB;QACnB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YACjB,SAAS;QACX,CAAC;QAED,sBAAsB;QACtB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAC9C,IAAI,SAAS,IAAI,UAAU,EAAE,CAAC;YAC5B,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YACtC,SAAS;QACX,CAAC;QAED,2BAA2B;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAC9C,IAAI,OAAO,EAAE,CAAC;YACZ,+BAA+B;YAC/B,IAAI,UAAU,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzC,MAAM,CAAC,UAAU,CAAC,GAAG,WAAW,CAAC;gBACjC,WAAW,GAAG,EAAE,CAAC;YACnB,CAAC;YAED,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YACvB,IAAI,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAE9B,IAAI,KAAK,EAAE,CAAC;gBACV,wBAAwB;gBACxB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;oBAC9C,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;oBACnD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC7B,CAAC;gBACD,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;gBACpB,UAAU,GAAG,IAAI,CAAC;YACpB,CAAC;iBAAM,CAAC;gBACN,wBAAwB;gBACxB,UAAU,GAAG,GAAG,CAAC;YACnB,CAAC;QACH,CAAC;IACH,CAAC;IAED,4BAA4B;IAC5B,IAAI,UAAU,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzC,MAAM,CAAC,UAAU,CAAC,GAAG,WAAW,CAAC;IACnC,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CAAC,OAAe;IACzC,MAAM,WAAW,GAAG,yBAAyB,CAAC;IAC9C,OAAO,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AACjD,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,KAAU;IAChC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,UAAU,CAAC,UAAkB;IAC1C,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAElD,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;IAE1D,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YAC5C,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACrD,MAAM,QAAQ,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;YAC/C,MAAM,IAAI,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;YAEzC,MAAM,KAAK,GAAG,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAC7C,MAAM,SAAS,GAAG,cAAc,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;YACrD,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAE/C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;YACvC,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,CAAC;YAEvE,MAAM,KAAK,GAAc;gBACvB,EAAE,EAAE,SAAS,QAAQ,EAAE;gBACvB,IAAI,EAAE,OAAO;gBACb,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,QAAQ;gBAC/B,WAAW,EAAE,QAAQ,CAAC,WAAW,IAAI,EAAE;gBACvC,KAAK;gBACL,KAAK,EAAE,QAAQ,CAAC,KAAK,IAAI,EAAE;gBAC3B,SAAS;gBACT,MAAM;gBACN,QAAQ,EAAE,YAAY;gBACtB,YAAY,EAAE,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI;aACpE,CAAC;YAEF,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CAAC,4BAA4B,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,UAAU,CAAC,UAAkB;IAC1C,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAElD,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAErE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACzB,SAAS;QACX,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAClD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAElD,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YACtD,MAAM,QAAQ,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;YAC/C,MAAM,IAAI,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;YAEzC,qCAAqC;YACrC,MAAM,QAAQ,GAAa,EAAE,CAAC;YAC9B,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,mDAAmD,CAAC,CAAC;YACrF,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,YAAY,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACxD,QAAQ,CAAC,IAAI,CACX,GAAG,YAAY;qBACZ,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;qBAC3C,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;qBAC7C,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,sBAAsB;iBACtC,CAAC;YACJ,CAAC;YAED,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC,CAAC;YACxE,MAAM,UAAU,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;iBAC/D,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;iBAChB,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;YACtB,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;iBAC7D,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;iBAChB,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;YAEtB,MAAM,KAAK,GAAc;gBACvB,EAAE,EAAE,SAAS,KAAK,CAAC,IAAI,EAAE;gBACzB,IAAI,EAAE,OAAO;gBACb,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI;gBACjC,WAAW,EAAE,QAAQ,CAAC,WAAW,IAAI,EAAE;gBACvC,QAAQ;gBACR,QAAQ,EAAE,YAAY;gBACtB,UAAU;gBACV,SAAS;aACV,CAAC;YAEF,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CAAC,4BAA4B,SAAS,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,YAAY,CAAC,UAAkB;IAC5C,MAAM,QAAQ,GAAgB,EAAE,CAAC;IACjC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAEtD,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC;IAE9D,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;YAC9C,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACrD,MAAM,QAAQ,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;YAE/C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;YACvC,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,CAAC;YAEvE,MAAM,OAAO,GAAc;gBACzB,EAAE,EAAE,SAAS,QAAQ,EAAE;gBACvB,IAAI,EAAE,OAAO;gBACb,OAAO,EAAE,SAAS;gBAClB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,QAAQ,CAAC,WAAW,IAAI,EAAE;gBACvC,YAAY,EAAE,QAAQ,CAAC,eAAe,CAAC,IAAI,EAAE;gBAC7C,QAAQ,EAAE,YAAY;gBACtB,UAAU,EAAE,KAAK;gBACjB,SAAS,EAAE,KAAK;gBAChB,QAAQ,EAAE,EAAE;aACb,CAAC;YAEF,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CAAC,4BAA4B,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,iBAAiB,CAC9B,MAAmB,EACnB,MAAmB,EACnB,UAAkB;IAElB,MAAM,KAAK,GAAgB,EAAE,CAAC;IAC9B,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IAErC,SAAS,OAAO,CAAC,MAAc,EAAE,MAAc,EAAE,IAAsB;QACrE,MAAM,OAAO,GAAG,GAAG,MAAM,KAAK,MAAM,EAAE,CAAC;QACvC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YACrC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,oBAAoB;IACpB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;QACjD,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;QACjD,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,iDAAiD;IACjD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,kCAAkC;QAClC,KAAK,MAAM,YAAY,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YAC3C,MAAM,WAAW,GAAG,YAAY,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;YACtD,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAC7C,IAAI,UAAU,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;YACzC,CAAC;QACH,CAAC;QAED,+BAA+B;QAC/B,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACrC,MAAM,QAAQ,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;YAChD,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACvC,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;IACH,CAAC;IAED,yDAAyD;IACzD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC1E,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;YAE1E,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;gBAE/C,IAAI,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC7D,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;gBACtC,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,6BAA6B;QAC/B,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,WAAmB,EACnB,UAAkB;IAElB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAEjD,kCAAkC;IAClC,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,8BAA8B,OAAO,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,sBAAsB;IACtB,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,UAAU,CAAC,CAAC;IAEhD,8BAA8B;IAC9B,MAAM,SAAS,GAAG,CAAC,GAAG,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC;IAE3C,qBAAqB;IACrB,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IAErE,eAAe;IACf,MAAM,MAAM,GAAc;QACxB,KAAK,EAAE,CAAC,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC;QAChC,KAAK;QACL,QAAQ,EAAE;YACR,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACrC,WAAW,EAAE,OAAO;YACpB,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YACnC,UAAU,EAAE,MAAM,CAAC,MAAM;YACzB,UAAU,EAAE,MAAM,CAAC,MAAM;YACzB,YAAY,EAAE,QAAQ,CAAC,MAAM;YAC7B,SAAS,EAAE,KAAK,CAAC,MAAM;SACxB;KACF,CAAC;IAEF,eAAe;IACf,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC3C,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,MAAM,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAEzE,OAAO,MAAM,CAAC,QAAQ,CAAC;AACzB,CAAC"}
|
package/lib/server.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Server } from 'http';
|
|
2
|
+
export interface ServerOptions {
|
|
3
|
+
port: number;
|
|
4
|
+
projectRoot: string;
|
|
5
|
+
distDir: string;
|
|
6
|
+
dataPath: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Start the Express server for serving the visualizer webapp
|
|
10
|
+
*/
|
|
11
|
+
export declare function startServer(options: ServerOptions): Promise<Server>;
|
|
12
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAE9B,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAkFzE"}
|
package/lib/server.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import * as fs from 'fs/promises';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
/**
|
|
5
|
+
* Start the Express server for serving the visualizer webapp
|
|
6
|
+
*/
|
|
7
|
+
export async function startServer(options) {
|
|
8
|
+
const app = express();
|
|
9
|
+
// CORS headers for development
|
|
10
|
+
app.use((req, res, next) => {
|
|
11
|
+
res.header('Access-Control-Allow-Origin', '*');
|
|
12
|
+
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
13
|
+
res.header('Access-Control-Allow-Headers', 'Content-Type');
|
|
14
|
+
next();
|
|
15
|
+
});
|
|
16
|
+
// API: Serve graph data from user's project
|
|
17
|
+
app.get('/api/graph-data', async (req, res) => {
|
|
18
|
+
try {
|
|
19
|
+
const data = await fs.readFile(options.dataPath, 'utf-8');
|
|
20
|
+
res.json(JSON.parse(data));
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
res.status(404).json({
|
|
24
|
+
error: 'Graph data not found',
|
|
25
|
+
message: 'Run the scanner first to generate graph data',
|
|
26
|
+
path: options.dataPath
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
// API: SSE endpoint for activity stream (optional feature)
|
|
31
|
+
app.get('/api/events', (req, res) => {
|
|
32
|
+
res.writeHead(200, {
|
|
33
|
+
'Content-Type': 'text/event-stream',
|
|
34
|
+
'Cache-Control': 'no-cache',
|
|
35
|
+
'Connection': 'keep-alive'
|
|
36
|
+
});
|
|
37
|
+
// Send connection event
|
|
38
|
+
res.write('event: connected\n');
|
|
39
|
+
res.write('data: {"status":"connected"}\n\n');
|
|
40
|
+
// Watch stream.jsonl if exists (optional enhancement)
|
|
41
|
+
const streamPath = path.join(options.projectRoot, '.claude/stream.jsonl');
|
|
42
|
+
// Keep connection alive with periodic heartbeats
|
|
43
|
+
const heartbeat = setInterval(() => {
|
|
44
|
+
res.write(':heartbeat\n\n');
|
|
45
|
+
}, 30000);
|
|
46
|
+
// Cleanup on client disconnect
|
|
47
|
+
req.on('close', () => {
|
|
48
|
+
clearInterval(heartbeat);
|
|
49
|
+
res.end();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
// API: Health check
|
|
53
|
+
app.get('/api/health', async (req, res) => {
|
|
54
|
+
const dataExists = await fs.access(options.dataPath)
|
|
55
|
+
.then(() => true)
|
|
56
|
+
.catch(() => false);
|
|
57
|
+
res.json({
|
|
58
|
+
status: 'ok',
|
|
59
|
+
dataExists,
|
|
60
|
+
projectRoot: options.projectRoot,
|
|
61
|
+
dataPath: options.dataPath
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
// Serve static webapp files from package dist directory
|
|
65
|
+
app.use(express.static(options.distDir));
|
|
66
|
+
// SPA fallback - serve index.html for all other routes
|
|
67
|
+
app.get('*', (req, res) => {
|
|
68
|
+
res.sendFile(path.join(options.distDir, 'index.html'));
|
|
69
|
+
});
|
|
70
|
+
// Start server
|
|
71
|
+
return new Promise((resolve, reject) => {
|
|
72
|
+
const server = app.listen(options.port, () => {
|
|
73
|
+
resolve(server);
|
|
74
|
+
}).on('error', (err) => {
|
|
75
|
+
reject(new Error(`Failed to start server on port ${options.port}: ${err.message}`));
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/lib/utils.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates if the given directory is a Claude Code project
|
|
3
|
+
* by checking for the existence of .claude directory
|
|
4
|
+
*/
|
|
5
|
+
export declare function validateClaudeProject(projectRoot: string): Promise<boolean>;
|
|
6
|
+
/**
|
|
7
|
+
* Ensures the visualizer directory exists and creates .gitignore if needed
|
|
8
|
+
*/
|
|
9
|
+
export declare function ensureVisualizerDir(vizDir: string): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Finds an available port, starting from the preferred port
|
|
12
|
+
* Tries up to 10 ports after the preferred one if it's in use
|
|
13
|
+
*/
|
|
14
|
+
export declare function findAvailablePort(preferred: number): Promise<number>;
|
|
15
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQjF;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBvE;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAmC1E"}
|
package/lib/utils.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as net from 'net';
|
|
4
|
+
/**
|
|
5
|
+
* Validates if the given directory is a Claude Code project
|
|
6
|
+
* by checking for the existence of .claude directory
|
|
7
|
+
*/
|
|
8
|
+
export async function validateClaudeProject(projectRoot) {
|
|
9
|
+
try {
|
|
10
|
+
const claudeDir = path.join(projectRoot, '.claude');
|
|
11
|
+
const stat = await fs.stat(claudeDir);
|
|
12
|
+
return stat.isDirectory();
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Ensures the visualizer directory exists and creates .gitignore if needed
|
|
20
|
+
*/
|
|
21
|
+
export async function ensureVisualizerDir(vizDir) {
|
|
22
|
+
// Create directory if it doesn't exist
|
|
23
|
+
await fs.mkdir(vizDir, { recursive: true });
|
|
24
|
+
// Create .gitignore to ignore graph-data.json
|
|
25
|
+
const gitignorePath = path.join(vizDir, '.gitignore');
|
|
26
|
+
try {
|
|
27
|
+
await fs.access(gitignorePath);
|
|
28
|
+
// File exists, don't overwrite
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// File doesn't exist, create it
|
|
32
|
+
await fs.writeFile(gitignorePath, '# Visualizer generated data\ngraph-data.json\n', 'utf-8');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Finds an available port, starting from the preferred port
|
|
37
|
+
* Tries up to 10 ports after the preferred one if it's in use
|
|
38
|
+
*/
|
|
39
|
+
export async function findAvailablePort(preferred) {
|
|
40
|
+
const isPortAvailable = (port) => {
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
const server = net.createServer();
|
|
43
|
+
server.once('error', () => {
|
|
44
|
+
resolve(false);
|
|
45
|
+
});
|
|
46
|
+
server.once('listening', () => {
|
|
47
|
+
server.close();
|
|
48
|
+
resolve(true);
|
|
49
|
+
});
|
|
50
|
+
server.listen(port);
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
// Try preferred port first
|
|
54
|
+
if (await isPortAvailable(preferred)) {
|
|
55
|
+
return preferred;
|
|
56
|
+
}
|
|
57
|
+
// Try next 10 ports
|
|
58
|
+
for (let i = 1; i <= 10; i++) {
|
|
59
|
+
const port = preferred + i;
|
|
60
|
+
if (await isPortAvailable(port)) {
|
|
61
|
+
return port;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
throw new Error(`No available port found near ${preferred}. ` +
|
|
65
|
+
`Tried ports ${preferred}-${preferred + 10}.`);
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=utils.js.map
|
package/lib/utils.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,GAAG,MAAM,KAAK,CAAC;AAE3B;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,WAAmB;IAC7D,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAAc;IACtD,uCAAuC;IACvC,MAAM,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5C,8CAA8C;IAC9C,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACtD,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QAC/B,+BAA+B;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,gCAAgC;QAChC,MAAM,EAAE,CAAC,SAAS,CAChB,aAAa,EACb,gDAAgD,EAChD,OAAO,CACR,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,SAAiB;IACvD,MAAM,eAAe,GAAG,CAAC,IAAY,EAAoB,EAAE;QACzD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC;YAElC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;gBACxB,OAAO,CAAC,KAAK,CAAC,CAAC;YACjB,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE;gBAC5B,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,2BAA2B;IAC3B,IAAI,MAAM,eAAe,CAAC,SAAS,CAAC,EAAE,CAAC;QACrC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,oBAAoB;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,SAAS,GAAG,CAAC,CAAC;QAC3B,IAAI,MAAM,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;YAChC,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CACb,gCAAgC,SAAS,IAAI;QAC7C,eAAe,SAAS,IAAI,SAAS,GAAG,EAAE,GAAG,CAC9C,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "viewcc",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Interactive visualization for Claude Code projects - run with npx",
|
|
5
|
+
"main": "./lib/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"viewcc": "./bin/claude-viz.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"files": [
|
|
11
|
+
"bin/",
|
|
12
|
+
"lib/",
|
|
13
|
+
"dist/",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc && npm run build:webapp && npm run copy:dist",
|
|
19
|
+
"build:webapp": "cd .claude/skills/agent-skill-visualizer/webapp && npm run build",
|
|
20
|
+
"copy:dist": "mkdir -p dist && cp -r .claude/skills/agent-skill-visualizer/webapp/dist/* dist/",
|
|
21
|
+
"dev": "tsc --watch",
|
|
22
|
+
"prepublishOnly": "npm run build",
|
|
23
|
+
"test": "npm run build && npm link"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"claude-code",
|
|
27
|
+
"visualization",
|
|
28
|
+
"graph",
|
|
29
|
+
"agents",
|
|
30
|
+
"skills",
|
|
31
|
+
"cli",
|
|
32
|
+
"developer-tools"
|
|
33
|
+
],
|
|
34
|
+
"author": "",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"express": "^4.18.2",
|
|
41
|
+
"open": "^9.1.0",
|
|
42
|
+
"commander": "^11.1.0",
|
|
43
|
+
"chalk": "^5.3.0",
|
|
44
|
+
"glob": "^10.3.10"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/express": "^4.17.21",
|
|
48
|
+
"@types/node": "^20.10.0",
|
|
49
|
+
"typescript": "^5.3.3"
|
|
50
|
+
},
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "https://github.com/YOUR_USERNAME/claude-code-visualizer.git"
|
|
54
|
+
},
|
|
55
|
+
"bugs": {
|
|
56
|
+
"url": "https://github.com/YOUR_USERNAME/claude-code-visualizer/issues"
|
|
57
|
+
},
|
|
58
|
+
"homepage": "https://github.com/YOUR_USERNAME/claude-code-visualizer#readme"
|
|
59
|
+
}
|