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/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":"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"}
@@ -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
@@ -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
+ }