@su-record/vibe 2.2.2 → 2.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/.claude/settings.json +152 -152
  2. package/.claude/vibe/constitution.md +184 -184
  3. package/.claude/vibe/rules/core/communication-guide.md +104 -104
  4. package/.claude/vibe/rules/core/development-philosophy.md +52 -52
  5. package/.claude/vibe/rules/core/quick-start.md +120 -120
  6. package/.claude/vibe/rules/quality/bdd-contract-testing.md +388 -388
  7. package/.claude/vibe/rules/quality/checklist.md +276 -276
  8. package/.claude/vibe/rules/quality/testing-strategy.md +437 -437
  9. package/.claude/vibe/rules/standards/anti-patterns.md +369 -369
  10. package/.claude/vibe/rules/standards/code-structure.md +291 -291
  11. package/.claude/vibe/rules/standards/complexity-metrics.md +312 -312
  12. package/.claude/vibe/rules/standards/naming-conventions.md +198 -198
  13. package/.claude/vibe/setup.sh +31 -31
  14. package/CLAUDE.md +323 -323
  15. package/LICENSE +21 -21
  16. package/README.md +724 -721
  17. package/agents/explorer.md +48 -0
  18. package/agents/implementer.md +53 -0
  19. package/agents/research/best-practices-agent.md +139 -0
  20. package/agents/research/codebase-patterns-agent.md +147 -0
  21. package/agents/research/framework-docs-agent.md +181 -0
  22. package/agents/research/security-advisory-agent.md +167 -0
  23. package/agents/review/architecture-reviewer.md +107 -0
  24. package/agents/review/complexity-reviewer.md +116 -0
  25. package/agents/review/data-integrity-reviewer.md +88 -0
  26. package/agents/review/git-history-reviewer.md +103 -0
  27. package/agents/review/performance-reviewer.md +86 -0
  28. package/agents/review/python-reviewer.md +152 -0
  29. package/agents/review/rails-reviewer.md +139 -0
  30. package/agents/review/react-reviewer.md +144 -0
  31. package/agents/review/security-reviewer.md +80 -0
  32. package/agents/review/simplicity-reviewer.md +140 -0
  33. package/agents/review/test-coverage-reviewer.md +116 -0
  34. package/agents/review/typescript-reviewer.md +127 -0
  35. package/agents/searcher.md +54 -0
  36. package/agents/simplifier.md +119 -0
  37. package/agents/tester.md +49 -0
  38. package/commands/vibe.analyze.md +239 -0
  39. package/commands/vibe.compound.md +261 -0
  40. package/commands/vibe.continue.md +88 -0
  41. package/commands/vibe.diagram.md +178 -0
  42. package/commands/vibe.e2e.md +266 -0
  43. package/commands/vibe.reason.md +306 -0
  44. package/commands/vibe.review.md +324 -0
  45. package/commands/vibe.run.md +836 -0
  46. package/commands/vibe.setup.md +97 -0
  47. package/commands/vibe.spec.md +383 -0
  48. package/commands/vibe.ui.md +137 -0
  49. package/commands/vibe.verify.md +238 -0
  50. package/dist/cli/index.js +389 -389
  51. package/dist/cli/index.js.map +1 -1
  52. package/dist/lib/MemoryManager.js +92 -92
  53. package/dist/lib/PythonParser.js +108 -108
  54. package/dist/lib/gemini-mcp.js +15 -15
  55. package/dist/lib/gemini-oauth.js +35 -35
  56. package/dist/lib/gpt-mcp.js +17 -17
  57. package/dist/lib/gpt-oauth.js +44 -44
  58. package/dist/tools/analytics/getUsageAnalytics.js +12 -12
  59. package/dist/tools/memory/createMemoryTimeline.js +10 -10
  60. package/dist/tools/memory/getMemoryGraph.js +12 -12
  61. package/dist/tools/memory/getSessionContext.js +9 -9
  62. package/dist/tools/memory/linkMemories.js +14 -14
  63. package/dist/tools/memory/listMemories.js +4 -4
  64. package/dist/tools/memory/recallMemory.js +4 -4
  65. package/dist/tools/memory/saveMemory.js +4 -4
  66. package/dist/tools/memory/searchMemoriesAdvanced.js +22 -22
  67. package/dist/tools/planning/generatePrd.js +46 -46
  68. package/dist/tools/prompt/enhancePromptGemini.js +160 -160
  69. package/dist/tools/reasoning/applyReasoningFramework.js +56 -56
  70. package/dist/tools/semantic/analyzeDependencyGraph.js +12 -12
  71. package/package.json +69 -66
  72. package/skills/git-worktree.md +178 -0
  73. package/skills/priority-todos.md +236 -0
  74. package/templates/constitution-template.md +184 -184
  75. package/templates/contract-backend-template.md +517 -517
  76. package/templates/contract-frontend-template.md +594 -594
  77. package/templates/feature-template.md +96 -96
  78. package/templates/hooks-template.json +103 -103
  79. package/templates/spec-template.md +199 -199
  80. package/.claude/vibe/rules/tools/mcp-hi-ai-guide.md +0 -665
  81. package/.claude/vibe/rules/tools/mcp-workflow.md +0 -51
@@ -97,37 +97,37 @@ export class MemoryManager {
97
97
  }
98
98
  initializeDatabase() {
99
99
  // Create memories table
100
- this.db.exec(`
101
- CREATE TABLE IF NOT EXISTS memories (
102
- key TEXT PRIMARY KEY,
103
- value TEXT NOT NULL,
104
- category TEXT NOT NULL DEFAULT 'general',
105
- timestamp TEXT NOT NULL,
106
- lastAccessed TEXT NOT NULL,
107
- priority INTEGER DEFAULT 0
108
- );
109
-
110
- CREATE INDEX IF NOT EXISTS idx_category ON memories(category);
111
- CREATE INDEX IF NOT EXISTS idx_timestamp ON memories(timestamp);
112
- CREATE INDEX IF NOT EXISTS idx_priority ON memories(priority);
113
- CREATE INDEX IF NOT EXISTS idx_lastAccessed ON memories(lastAccessed);
100
+ this.db.exec(`
101
+ CREATE TABLE IF NOT EXISTS memories (
102
+ key TEXT PRIMARY KEY,
103
+ value TEXT NOT NULL,
104
+ category TEXT NOT NULL DEFAULT 'general',
105
+ timestamp TEXT NOT NULL,
106
+ lastAccessed TEXT NOT NULL,
107
+ priority INTEGER DEFAULT 0
108
+ );
109
+
110
+ CREATE INDEX IF NOT EXISTS idx_category ON memories(category);
111
+ CREATE INDEX IF NOT EXISTS idx_timestamp ON memories(timestamp);
112
+ CREATE INDEX IF NOT EXISTS idx_priority ON memories(priority);
113
+ CREATE INDEX IF NOT EXISTS idx_lastAccessed ON memories(lastAccessed);
114
114
  `);
115
115
  // v2.0: Create memory_relations table for Knowledge Graph
116
- this.db.exec(`
117
- CREATE TABLE IF NOT EXISTS memory_relations (
118
- id INTEGER PRIMARY KEY AUTOINCREMENT,
119
- sourceKey TEXT NOT NULL,
120
- targetKey TEXT NOT NULL,
121
- relationType TEXT NOT NULL,
122
- strength REAL DEFAULT 1.0,
123
- metadata TEXT,
124
- timestamp TEXT NOT NULL,
125
- UNIQUE(sourceKey, targetKey, relationType)
126
- );
127
-
128
- CREATE INDEX IF NOT EXISTS idx_rel_source ON memory_relations(sourceKey);
129
- CREATE INDEX IF NOT EXISTS idx_rel_target ON memory_relations(targetKey);
130
- CREATE INDEX IF NOT EXISTS idx_rel_type ON memory_relations(relationType);
116
+ this.db.exec(`
117
+ CREATE TABLE IF NOT EXISTS memory_relations (
118
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
119
+ sourceKey TEXT NOT NULL,
120
+ targetKey TEXT NOT NULL,
121
+ relationType TEXT NOT NULL,
122
+ strength REAL DEFAULT 1.0,
123
+ metadata TEXT,
124
+ timestamp TEXT NOT NULL,
125
+ UNIQUE(sourceKey, targetKey, relationType)
126
+ );
127
+
128
+ CREATE INDEX IF NOT EXISTS idx_rel_source ON memory_relations(sourceKey);
129
+ CREATE INDEX IF NOT EXISTS idx_rel_target ON memory_relations(targetKey);
130
+ CREATE INDEX IF NOT EXISTS idx_rel_type ON memory_relations(relationType);
131
131
  `);
132
132
  // Enable WAL mode for better concurrency
133
133
  this.db.pragma('journal_mode = WAL');
@@ -137,10 +137,10 @@ export class MemoryManager {
137
137
  initializePreparedStatements() {
138
138
  // Pre-compile recall statement
139
139
  try {
140
- this.recallStmt = this.db.prepare(`
141
- UPDATE memories SET lastAccessed = ?
142
- WHERE key = ?
143
- RETURNING *
140
+ this.recallStmt = this.db.prepare(`
141
+ UPDATE memories SET lastAccessed = ?
142
+ WHERE key = ?
143
+ RETURNING *
144
144
  `);
145
145
  }
146
146
  catch (error) {
@@ -150,9 +150,9 @@ export class MemoryManager {
150
150
  this.recallUpdateStmt = this.db.prepare(`UPDATE memories SET lastAccessed = ? WHERE key = ?`);
151
151
  }
152
152
  // Pre-compile save statement
153
- this.saveStmt = this.db.prepare(`
154
- INSERT OR REPLACE INTO memories (key, value, category, timestamp, lastAccessed, priority)
155
- VALUES (?, ?, ?, ?, ?, ?)
153
+ this.saveStmt = this.db.prepare(`
154
+ INSERT OR REPLACE INTO memories (key, value, category, timestamp, lastAccessed, priority)
155
+ VALUES (?, ?, ?, ?, ?, ?)
156
156
  `);
157
157
  }
158
158
  /**
@@ -188,9 +188,9 @@ export class MemoryManager {
188
188
  * Import memories into SQLite database
189
189
  */
190
190
  importMemories(memories) {
191
- const insert = this.db.prepare(`
192
- INSERT OR REPLACE INTO memories (key, value, category, timestamp, lastAccessed, priority)
193
- VALUES (?, ?, ?, ?, ?, ?)
191
+ const insert = this.db.prepare(`
192
+ INSERT OR REPLACE INTO memories (key, value, category, timestamp, lastAccessed, priority)
193
+ VALUES (?, ?, ?, ?, ?, ?)
194
194
  `);
195
195
  const insertMany = this.db.transaction((items) => {
196
196
  for (const item of items) {
@@ -225,9 +225,9 @@ export class MemoryManager {
225
225
  }
226
226
  else {
227
227
  // Fallback if prepared statement not available
228
- const stmt = this.db.prepare(`
229
- INSERT OR REPLACE INTO memories (key, value, category, timestamp, lastAccessed, priority)
230
- VALUES (?, ?, ?, ?, ?, ?)
228
+ const stmt = this.db.prepare(`
229
+ INSERT OR REPLACE INTO memories (key, value, category, timestamp, lastAccessed, priority)
230
+ VALUES (?, ?, ?, ?, ?, ?)
231
231
  `);
232
232
  stmt.run(key, value, category, timestamp, timestamp, priority);
233
233
  }
@@ -262,8 +262,8 @@ export class MemoryManager {
262
262
  delete(key) {
263
263
  // Also delete related relations
264
264
  this.db.prepare(`DELETE FROM memory_relations WHERE sourceKey = ? OR targetKey = ?`).run(key, key);
265
- const stmt = this.db.prepare(`
266
- DELETE FROM memories WHERE key = ?
265
+ const stmt = this.db.prepare(`
266
+ DELETE FROM memories WHERE key = ?
267
267
  `);
268
268
  const result = stmt.run(key);
269
269
  return result.changes > 0;
@@ -276,10 +276,10 @@ export class MemoryManager {
276
276
  */
277
277
  update(key, value) {
278
278
  const timestamp = new Date().toISOString();
279
- const stmt = this.db.prepare(`
280
- UPDATE memories
281
- SET value = ?, timestamp = ?, lastAccessed = ?
282
- WHERE key = ?
279
+ const stmt = this.db.prepare(`
280
+ UPDATE memories
281
+ SET value = ?, timestamp = ?, lastAccessed = ?
282
+ WHERE key = ?
283
283
  `);
284
284
  const result = stmt.run(value, timestamp, timestamp, key);
285
285
  return result.changes > 0;
@@ -292,16 +292,16 @@ export class MemoryManager {
292
292
  list(category) {
293
293
  let stmt;
294
294
  if (category) {
295
- stmt = this.db.prepare(`
296
- SELECT * FROM memories WHERE category = ?
297
- ORDER BY priority DESC, timestamp DESC
295
+ stmt = this.db.prepare(`
296
+ SELECT * FROM memories WHERE category = ?
297
+ ORDER BY priority DESC, timestamp DESC
298
298
  `);
299
299
  return stmt.all(category);
300
300
  }
301
301
  else {
302
- stmt = this.db.prepare(`
303
- SELECT * FROM memories
304
- ORDER BY priority DESC, timestamp DESC
302
+ stmt = this.db.prepare(`
303
+ SELECT * FROM memories
304
+ ORDER BY priority DESC, timestamp DESC
305
305
  `);
306
306
  return stmt.all();
307
307
  }
@@ -312,10 +312,10 @@ export class MemoryManager {
312
312
  * @returns Array of matching memory items
313
313
  */
314
314
  search(query) {
315
- const stmt = this.db.prepare(`
316
- SELECT * FROM memories
317
- WHERE key LIKE ? OR value LIKE ?
318
- ORDER BY priority DESC, timestamp DESC
315
+ const stmt = this.db.prepare(`
316
+ SELECT * FROM memories
317
+ WHERE key LIKE ? OR value LIKE ?
318
+ ORDER BY priority DESC, timestamp DESC
319
319
  `);
320
320
  const pattern = `%${query}%`;
321
321
  return stmt.all(pattern, pattern);
@@ -326,10 +326,10 @@ export class MemoryManager {
326
326
  * @returns Array of memory items with specified priority
327
327
  */
328
328
  getByPriority(priority) {
329
- const stmt = this.db.prepare(`
330
- SELECT * FROM memories
331
- WHERE priority = ?
332
- ORDER BY timestamp DESC
329
+ const stmt = this.db.prepare(`
330
+ SELECT * FROM memories
331
+ WHERE priority = ?
332
+ ORDER BY timestamp DESC
333
333
  `);
334
334
  return stmt.all(priority);
335
335
  }
@@ -340,8 +340,8 @@ export class MemoryManager {
340
340
  * @returns True if updated successfully
341
341
  */
342
342
  setPriority(key, priority) {
343
- const stmt = this.db.prepare(`
344
- UPDATE memories SET priority = ? WHERE key = ?
343
+ const stmt = this.db.prepare(`
344
+ UPDATE memories SET priority = ? WHERE key = ?
345
345
  `);
346
346
  const result = stmt.run(priority, key);
347
347
  return result.changes > 0;
@@ -352,10 +352,10 @@ export class MemoryManager {
352
352
  */
353
353
  getStats() {
354
354
  // Single query with ROLLUP or combined approach
355
- const categories = this.db.prepare(`
356
- SELECT category, COUNT(*) as count
357
- FROM memories
358
- GROUP BY category
355
+ const categories = this.db.prepare(`
356
+ SELECT category, COUNT(*) as count
357
+ FROM memories
358
+ GROUP BY category
359
359
  `).all();
360
360
  const byCategory = {};
361
361
  let total = 0;
@@ -380,10 +380,10 @@ export class MemoryManager {
380
380
  const timestamp = new Date().toISOString();
381
381
  const metadataJson = metadata ? JSON.stringify(metadata) : null;
382
382
  try {
383
- const stmt = this.db.prepare(`
384
- INSERT OR REPLACE INTO memory_relations
385
- (sourceKey, targetKey, relationType, strength, metadata, timestamp)
386
- VALUES (?, ?, ?, ?, ?, ?)
383
+ const stmt = this.db.prepare(`
384
+ INSERT OR REPLACE INTO memory_relations
385
+ (sourceKey, targetKey, relationType, strength, metadata, timestamp)
386
+ VALUES (?, ?, ?, ?, ?, ?)
387
387
  `);
388
388
  stmt.run(sourceKey, targetKey, relationType, strength, metadataJson, timestamp);
389
389
  return true;
@@ -639,9 +639,9 @@ export class MemoryManager {
639
639
  }
640
640
  }
641
641
  searchKeyword(query, limit, category) {
642
- let sql = `
643
- SELECT * FROM memories
644
- WHERE (key LIKE ? OR value LIKE ?)
642
+ let sql = `
643
+ SELECT * FROM memories
644
+ WHERE (key LIKE ? OR value LIKE ?)
645
645
  `;
646
646
  const params = [`%${query}%`, `%${query}%`];
647
647
  if (category) {
@@ -653,32 +653,32 @@ export class MemoryManager {
653
653
  return this.db.prepare(sql).all(...params);
654
654
  }
655
655
  searchTemporal(query, limit) {
656
- const sql = `
657
- SELECT * FROM memories
658
- WHERE key LIKE ? OR value LIKE ?
659
- ORDER BY timestamp DESC
660
- LIMIT ?
656
+ const sql = `
657
+ SELECT * FROM memories
658
+ WHERE key LIKE ? OR value LIKE ?
659
+ ORDER BY timestamp DESC
660
+ LIMIT ?
661
661
  `;
662
662
  return this.db.prepare(sql).all(`%${query}%`, `%${query}%`, limit);
663
663
  }
664
664
  searchByPriority(query, limit) {
665
- const sql = `
666
- SELECT * FROM memories
667
- WHERE key LIKE ? OR value LIKE ?
668
- ORDER BY priority DESC, lastAccessed DESC
669
- LIMIT ?
665
+ const sql = `
666
+ SELECT * FROM memories
667
+ WHERE key LIKE ? OR value LIKE ?
668
+ ORDER BY priority DESC, lastAccessed DESC
669
+ LIMIT ?
670
670
  `;
671
671
  return this.db.prepare(sql).all(`%${query}%`, `%${query}%`, limit);
672
672
  }
673
673
  searchContextAware(query, limit, category) {
674
674
  // Combined strategy: keyword + priority + recency
675
- let sql = `
676
- SELECT *,
677
- (CASE WHEN key LIKE ? THEN 3 ELSE 0 END +
678
- CASE WHEN value LIKE ? THEN 2 ELSE 0 END +
679
- priority * 0.5) as relevance_score
680
- FROM memories
681
- WHERE key LIKE ? OR value LIKE ?
675
+ let sql = `
676
+ SELECT *,
677
+ (CASE WHEN key LIKE ? THEN 3 ELSE 0 END +
678
+ CASE WHEN value LIKE ? THEN 2 ELSE 0 END +
679
+ priority * 0.5) as relevance_score
680
+ FROM memories
681
+ WHERE key LIKE ? OR value LIKE ?
682
682
  `;
683
683
  const params = [`%${query}%`, `%${query}%`, `%${query}%`, `%${query}%`];
684
684
  if (category) {
@@ -35,114 +35,114 @@ function getPythonCommand() {
35
35
  const PYTHON_CMD = getPythonCommand();
36
36
  export class PythonParser {
37
37
  static cleanupRegistered = false;
38
- static pythonScript = `
39
- import ast
40
- import sys
41
- import json
42
-
43
- def analyze_code(code):
44
- try:
45
- tree = ast.parse(code)
46
- symbols = []
47
-
48
- for node in ast.walk(tree):
49
- if isinstance(node, ast.FunctionDef):
50
- symbols.append({
51
- 'name': node.name,
52
- 'kind': 'function',
53
- 'line': node.lineno,
54
- 'column': node.col_offset,
55
- 'endLine': node.end_lineno,
56
- 'docstring': ast.get_docstring(node)
57
- })
58
- elif isinstance(node, ast.ClassDef):
59
- symbols.append({
60
- 'name': node.name,
61
- 'kind': 'class',
62
- 'line': node.lineno,
63
- 'column': node.col_offset,
64
- 'endLine': node.end_lineno,
65
- 'docstring': ast.get_docstring(node)
66
- })
67
- elif isinstance(node, ast.Assign):
68
- for target in node.targets:
69
- if isinstance(target, ast.Name):
70
- symbols.append({
71
- 'name': target.id,
72
- 'kind': 'variable',
73
- 'line': node.lineno,
74
- 'column': node.col_offset
75
- })
76
- elif isinstance(node, ast.Import) or isinstance(node, ast.ImportFrom):
77
- for alias in node.names:
78
- symbols.append({
79
- 'name': alias.name,
80
- 'kind': 'import',
81
- 'line': node.lineno,
82
- 'column': node.col_offset
83
- })
84
-
85
- return {'success': True, 'symbols': symbols}
86
- except SyntaxError as e:
87
- return {'success': False, 'error': str(e)}
88
- except Exception as e:
89
- return {'success': False, 'error': str(e)}
90
-
91
- def calculate_complexity(code):
92
- try:
93
- tree = ast.parse(code)
94
-
95
- def cyclomatic_complexity(node):
96
- complexity = 1
97
- for child in ast.walk(node):
98
- if isinstance(child, (ast.If, ast.For, ast.While, ast.And, ast.Or, ast.ExceptHandler)):
99
- complexity += 1
100
- elif isinstance(child, ast.BoolOp):
101
- complexity += len(child.values) - 1
102
- return complexity
103
-
104
- functions = []
105
- classes = []
106
- total_complexity = 1
107
-
108
- for node in ast.walk(tree):
109
- if isinstance(node, ast.FunctionDef):
110
- func_complexity = cyclomatic_complexity(node)
111
- functions.append({
112
- 'name': node.name,
113
- 'complexity': func_complexity,
114
- 'line': node.lineno
115
- })
116
- total_complexity += func_complexity
117
- elif isinstance(node, ast.ClassDef):
118
- method_count = sum(1 for n in node.body if isinstance(n, ast.FunctionDef))
119
- classes.append({
120
- 'name': node.name,
121
- 'methods': method_count,
122
- 'line': node.lineno
123
- })
124
-
125
- return {
126
- 'success': True,
127
- 'cyclomaticComplexity': total_complexity,
128
- 'functions': functions,
129
- 'classes': classes
130
- }
131
- except Exception as e:
132
- return {'success': False, 'error': str(e)}
133
-
134
- if __name__ == '__main__':
135
- code = sys.stdin.read()
136
- action = sys.argv[1] if len(sys.argv) > 1 else 'symbols'
137
-
138
- if action == 'symbols':
139
- result = analyze_code(code)
140
- elif action == 'complexity':
141
- result = calculate_complexity(code)
142
- else:
143
- result = {'success': False, 'error': 'Unknown action'}
144
-
145
- print(json.dumps(result))
38
+ static pythonScript = `
39
+ import ast
40
+ import sys
41
+ import json
42
+
43
+ def analyze_code(code):
44
+ try:
45
+ tree = ast.parse(code)
46
+ symbols = []
47
+
48
+ for node in ast.walk(tree):
49
+ if isinstance(node, ast.FunctionDef):
50
+ symbols.append({
51
+ 'name': node.name,
52
+ 'kind': 'function',
53
+ 'line': node.lineno,
54
+ 'column': node.col_offset,
55
+ 'endLine': node.end_lineno,
56
+ 'docstring': ast.get_docstring(node)
57
+ })
58
+ elif isinstance(node, ast.ClassDef):
59
+ symbols.append({
60
+ 'name': node.name,
61
+ 'kind': 'class',
62
+ 'line': node.lineno,
63
+ 'column': node.col_offset,
64
+ 'endLine': node.end_lineno,
65
+ 'docstring': ast.get_docstring(node)
66
+ })
67
+ elif isinstance(node, ast.Assign):
68
+ for target in node.targets:
69
+ if isinstance(target, ast.Name):
70
+ symbols.append({
71
+ 'name': target.id,
72
+ 'kind': 'variable',
73
+ 'line': node.lineno,
74
+ 'column': node.col_offset
75
+ })
76
+ elif isinstance(node, ast.Import) or isinstance(node, ast.ImportFrom):
77
+ for alias in node.names:
78
+ symbols.append({
79
+ 'name': alias.name,
80
+ 'kind': 'import',
81
+ 'line': node.lineno,
82
+ 'column': node.col_offset
83
+ })
84
+
85
+ return {'success': True, 'symbols': symbols}
86
+ except SyntaxError as e:
87
+ return {'success': False, 'error': str(e)}
88
+ except Exception as e:
89
+ return {'success': False, 'error': str(e)}
90
+
91
+ def calculate_complexity(code):
92
+ try:
93
+ tree = ast.parse(code)
94
+
95
+ def cyclomatic_complexity(node):
96
+ complexity = 1
97
+ for child in ast.walk(node):
98
+ if isinstance(child, (ast.If, ast.For, ast.While, ast.And, ast.Or, ast.ExceptHandler)):
99
+ complexity += 1
100
+ elif isinstance(child, ast.BoolOp):
101
+ complexity += len(child.values) - 1
102
+ return complexity
103
+
104
+ functions = []
105
+ classes = []
106
+ total_complexity = 1
107
+
108
+ for node in ast.walk(tree):
109
+ if isinstance(node, ast.FunctionDef):
110
+ func_complexity = cyclomatic_complexity(node)
111
+ functions.append({
112
+ 'name': node.name,
113
+ 'complexity': func_complexity,
114
+ 'line': node.lineno
115
+ })
116
+ total_complexity += func_complexity
117
+ elif isinstance(node, ast.ClassDef):
118
+ method_count = sum(1 for n in node.body if isinstance(n, ast.FunctionDef))
119
+ classes.append({
120
+ 'name': node.name,
121
+ 'methods': method_count,
122
+ 'line': node.lineno
123
+ })
124
+
125
+ return {
126
+ 'success': True,
127
+ 'cyclomaticComplexity': total_complexity,
128
+ 'functions': functions,
129
+ 'classes': classes
130
+ }
131
+ except Exception as e:
132
+ return {'success': False, 'error': str(e)}
133
+
134
+ if __name__ == '__main__':
135
+ code = sys.stdin.read()
136
+ action = sys.argv[1] if len(sys.argv) > 1 else 'symbols'
137
+
138
+ if action == 'symbols':
139
+ result = analyze_code(code)
140
+ elif action == 'complexity':
141
+ result = calculate_complexity(code)
142
+ else:
143
+ result = {'success': False, 'error': 'Unknown action'}
144
+
145
+ print(json.dumps(result))
146
146
  `;
147
147
  // Singleton Python script path to avoid recreating it
148
148
  static scriptPath = null;
@@ -161,12 +161,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
161
161
  }
162
162
  case 'gemini_analyze_code': {
163
163
  const { code, language, focus } = args;
164
- const sysPrompt = `You are an expert code reviewer. Analyze the given ${language || 'code'} and provide detailed feedback focusing on: ${focus || 'all aspects including performance, security, readability, and best practices'}.
165
-
166
- Respond in Korean and provide:
167
- 1. 코드 요약
168
- 2. 주요 이슈 (있다면)
169
- 3. 개선 제안
164
+ const sysPrompt = `You are an expert code reviewer. Analyze the given ${language || 'code'} and provide detailed feedback focusing on: ${focus || 'all aspects including performance, security, readability, and best practices'}.
165
+
166
+ Respond in Korean and provide:
167
+ 1. 코드 요약
168
+ 2. 주요 이슈 (있다면)
169
+ 3. 개선 제안
170
170
  4. 전체 품질 점수 (1-10)`;
171
171
  const result = await geminiApi.chat({
172
172
  model: 'gemini-2.5-flash',
@@ -186,15 +186,15 @@ Respond in Korean and provide:
186
186
  }
187
187
  case 'gemini_review_ui': {
188
188
  const { description, context } = args;
189
- const sysPrompt = `You are a UI/UX expert. Review the given UI description and provide detailed feedback.
190
-
191
- ${context ? `Project context: ${context}` : ''}
192
-
193
- Respond in Korean and provide:
194
- 1. UI 구조 평가
195
- 2. UX 개선점
196
- 3. 접근성 체크
197
- 4. 모범 사례 비교
189
+ const sysPrompt = `You are a UI/UX expert. Review the given UI description and provide detailed feedback.
190
+
191
+ ${context ? `Project context: ${context}` : ''}
192
+
193
+ Respond in Korean and provide:
194
+ 1. UI 구조 평가
195
+ 2. UX 개선점
196
+ 3. 접근성 체크
197
+ 4. 모범 사례 비교
198
198
  5. 구체적인 개선 제안`;
199
199
  const result = await geminiApi.chat({
200
200
  model: 'gemini-2.5-flash',
@@ -212,15 +212,15 @@ export function startOAuthFlow() {
212
212
  const error = url.searchParams.get('error');
213
213
  if (error) {
214
214
  res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
215
- res.end(`
216
- <html>
217
- <head><title>인증 실패</title></head>
218
- <body style="font-family: sans-serif; text-align: center; padding-top: 50px;">
219
- <h1>인증 실패</h1>
220
- <p>오류: ${error}</p>
221
- <p>이 창을 닫고 다시 시도해주세요.</p>
222
- </body>
223
- </html>
215
+ res.end(`
216
+ <html>
217
+ <head><title>인증 실패</title></head>
218
+ <body style="font-family: sans-serif; text-align: center; padding-top: 50px;">
219
+ <h1>인증 실패</h1>
220
+ <p>오류: ${error}</p>
221
+ <p>이 창을 닫고 다시 시도해주세요.</p>
222
+ </body>
223
+ </html>
224
224
  `);
225
225
  if (!isResolved) {
226
226
  isResolved = true;
@@ -231,14 +231,14 @@ export function startOAuthFlow() {
231
231
  }
232
232
  if (!code || !state) {
233
233
  res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
234
- res.end(`
235
- <html>
236
- <head><title>인증 실패</title></head>
237
- <body style="font-family: sans-serif; text-align: center; padding-top: 50px;">
238
- <h1>인증 실패</h1>
239
- <p>필수 파라미터가 없습니다.</p>
240
- </body>
241
- </html>
234
+ res.end(`
235
+ <html>
236
+ <head><title>인증 실패</title></head>
237
+ <body style="font-family: sans-serif; text-align: center; padding-top: 50px;">
238
+ <h1>인증 실패</h1>
239
+ <p>필수 파라미터가 없습니다.</p>
240
+ </body>
241
+ </html>
242
242
  `);
243
243
  if (!isResolved) {
244
244
  isResolved = true;
@@ -250,16 +250,16 @@ export function startOAuthFlow() {
250
250
  try {
251
251
  const tokens = await exchangeCodeForTokens(code, state);
252
252
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
253
- res.end(`
254
- <html>
255
- <head><title>인증 성공</title></head>
256
- <body style="font-family: sans-serif; text-align: center; padding-top: 50px;">
257
- <h1>인증 성공!</h1>
258
- <p>${tokens.email}로 로그인되었습니다.</p>
259
- <p>이 창을 닫아도 됩니다.</p>
260
- <script>setTimeout(() => window.close(), 2000);</script>
261
- </body>
262
- </html>
253
+ res.end(`
254
+ <html>
255
+ <head><title>인증 성공</title></head>
256
+ <body style="font-family: sans-serif; text-align: center; padding-top: 50px;">
257
+ <h1>인증 성공!</h1>
258
+ <p>${tokens.email}로 로그인되었습니다.</p>
259
+ <p>이 창을 닫아도 됩니다.</p>
260
+ <script>setTimeout(() => window.close(), 2000);</script>
261
+ </body>
262
+ </html>
263
263
  `);
264
264
  if (!isResolved) {
265
265
  isResolved = true;
@@ -269,14 +269,14 @@ export function startOAuthFlow() {
269
269
  }
270
270
  catch (err) {
271
271
  res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' });
272
- res.end(`
273
- <html>
274
- <head><title>인증 실패</title></head>
275
- <body style="font-family: sans-serif; text-align: center; padding-top: 50px;">
276
- <h1>인증 실패</h1>
277
- <p>${err.message}</p>
278
- </body>
279
- </html>
272
+ res.end(`
273
+ <html>
274
+ <head><title>인증 실패</title></head>
275
+ <body style="font-family: sans-serif; text-align: center; padding-top: 50px;">
276
+ <h1>인증 실패</h1>
277
+ <p>${err.message}</p>
278
+ </body>
279
+ </html>
280
280
  `);
281
281
  if (!isResolved) {
282
282
  isResolved = true;