@su-record/vibe 2.3.2 → 2.4.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.
Files changed (118) hide show
  1. package/.claude/settings.json +35 -35
  2. package/.claude/settings.local.json +30 -24
  3. package/.claude/vibe/constitution.md +184 -184
  4. package/.claude/vibe/rules/core/communication-guide.md +104 -104
  5. package/.claude/vibe/rules/core/development-philosophy.md +52 -52
  6. package/.claude/vibe/rules/core/quick-start.md +120 -120
  7. package/.claude/vibe/rules/languages/dart-flutter.md +509 -509
  8. package/.claude/vibe/rules/languages/go.md +396 -396
  9. package/.claude/vibe/rules/languages/java-spring.md +586 -586
  10. package/.claude/vibe/rules/languages/kotlin-android.md +491 -491
  11. package/.claude/vibe/rules/languages/python-django.md +371 -371
  12. package/.claude/vibe/rules/languages/python-fastapi.md +386 -386
  13. package/.claude/vibe/rules/languages/rust.md +425 -425
  14. package/.claude/vibe/rules/languages/swift-ios.md +516 -516
  15. package/.claude/vibe/rules/languages/typescript-nextjs.md +441 -441
  16. package/.claude/vibe/rules/languages/typescript-node.md +375 -375
  17. package/.claude/vibe/rules/languages/typescript-nuxt.md +521 -521
  18. package/.claude/vibe/rules/languages/typescript-react-native.md +446 -446
  19. package/.claude/vibe/rules/languages/typescript-react.md +525 -525
  20. package/.claude/vibe/rules/languages/typescript-vue.md +353 -353
  21. package/.claude/vibe/rules/quality/bdd-contract-testing.md +388 -388
  22. package/.claude/vibe/rules/quality/checklist.md +276 -276
  23. package/.claude/vibe/rules/quality/testing-strategy.md +437 -437
  24. package/.claude/vibe/rules/standards/anti-patterns.md +369 -369
  25. package/.claude/vibe/rules/standards/code-structure.md +291 -291
  26. package/.claude/vibe/rules/standards/complexity-metrics.md +312 -312
  27. package/.claude/vibe/rules/standards/naming-conventions.md +198 -198
  28. package/.claude/vibe/setup.sh +31 -31
  29. package/.claude/vibe/templates/constitution-template.md +184 -184
  30. package/.claude/vibe/templates/contract-backend-template.md +517 -517
  31. package/.claude/vibe/templates/contract-frontend-template.md +594 -594
  32. package/.claude/vibe/templates/feature-template.md +96 -96
  33. package/.claude/vibe/templates/spec-template.md +199 -199
  34. package/CLAUDE.md +345 -345
  35. package/LICENSE +21 -21
  36. package/README.md +817 -744
  37. package/agents/compounder.md +261 -261
  38. package/agents/diagrammer.md +178 -178
  39. package/agents/e2e-tester.md +266 -266
  40. package/agents/explorer.md +48 -48
  41. package/agents/implementer.md +53 -53
  42. package/agents/research/best-practices-agent.md +139 -139
  43. package/agents/research/codebase-patterns-agent.md +147 -147
  44. package/agents/research/framework-docs-agent.md +181 -181
  45. package/agents/research/security-advisory-agent.md +167 -167
  46. package/agents/review/architecture-reviewer.md +107 -107
  47. package/agents/review/complexity-reviewer.md +116 -116
  48. package/agents/review/data-integrity-reviewer.md +88 -88
  49. package/agents/review/git-history-reviewer.md +103 -103
  50. package/agents/review/performance-reviewer.md +86 -86
  51. package/agents/review/python-reviewer.md +152 -152
  52. package/agents/review/rails-reviewer.md +139 -139
  53. package/agents/review/react-reviewer.md +144 -144
  54. package/agents/review/security-reviewer.md +80 -80
  55. package/agents/review/simplicity-reviewer.md +140 -140
  56. package/agents/review/test-coverage-reviewer.md +116 -116
  57. package/agents/review/typescript-reviewer.md +127 -127
  58. package/agents/searcher.md +54 -54
  59. package/agents/simplifier.md +119 -119
  60. package/agents/tester.md +49 -49
  61. package/agents/ui-previewer.md +137 -137
  62. package/commands/vibe.analyze.md +260 -245
  63. package/commands/vibe.reason.md +223 -223
  64. package/commands/vibe.review.md +213 -200
  65. package/commands/vibe.run.md +842 -838
  66. package/commands/vibe.spec.md +405 -419
  67. package/commands/vibe.utils.md +101 -101
  68. package/commands/vibe.verify.md +282 -282
  69. package/dist/cli/index.d.ts.map +1 -1
  70. package/dist/cli/index.js +422 -385
  71. package/dist/cli/index.js.map +1 -1
  72. package/dist/lib/MemoryManager.js +92 -92
  73. package/dist/lib/PythonParser.js +108 -108
  74. package/dist/lib/gemini-mcp.js +15 -15
  75. package/dist/lib/gemini-oauth.js +35 -35
  76. package/dist/lib/gpt-mcp.js +17 -17
  77. package/dist/lib/gpt-oauth.js +44 -44
  78. package/dist/orchestrator/agentDiscovery.d.ts +18 -0
  79. package/dist/orchestrator/agentDiscovery.d.ts.map +1 -0
  80. package/dist/orchestrator/agentDiscovery.js +174 -0
  81. package/dist/orchestrator/agentDiscovery.js.map +1 -0
  82. package/dist/orchestrator/backgroundAgent.d.ts +31 -0
  83. package/dist/orchestrator/backgroundAgent.d.ts.map +1 -0
  84. package/dist/orchestrator/backgroundAgent.js +325 -0
  85. package/dist/orchestrator/backgroundAgent.js.map +1 -0
  86. package/dist/orchestrator/index.d.ts +58 -0
  87. package/dist/orchestrator/index.d.ts.map +1 -0
  88. package/dist/orchestrator/index.js +115 -0
  89. package/dist/orchestrator/index.js.map +1 -0
  90. package/dist/orchestrator/orchestrator.d.ts +82 -0
  91. package/dist/orchestrator/orchestrator.d.ts.map +1 -0
  92. package/dist/orchestrator/orchestrator.js +257 -0
  93. package/dist/orchestrator/orchestrator.js.map +1 -0
  94. package/dist/orchestrator/parallelResearch.d.ts +19 -0
  95. package/dist/orchestrator/parallelResearch.d.ts.map +1 -0
  96. package/dist/orchestrator/parallelResearch.js +214 -0
  97. package/dist/orchestrator/parallelResearch.js.map +1 -0
  98. package/dist/orchestrator/types.d.ts +109 -0
  99. package/dist/orchestrator/types.d.ts.map +1 -0
  100. package/dist/orchestrator/types.js +5 -0
  101. package/dist/orchestrator/types.js.map +1 -0
  102. package/dist/tools/analytics/getUsageAnalytics.js +12 -12
  103. package/dist/tools/memory/createMemoryTimeline.js +10 -10
  104. package/dist/tools/memory/getMemoryGraph.js +12 -12
  105. package/dist/tools/memory/getSessionContext.js +9 -9
  106. package/dist/tools/memory/linkMemories.js +14 -14
  107. package/dist/tools/memory/listMemories.js +4 -4
  108. package/dist/tools/memory/recallMemory.js +4 -4
  109. package/dist/tools/memory/saveMemory.js +4 -4
  110. package/dist/tools/memory/searchMemoriesAdvanced.js +22 -22
  111. package/dist/tools/planning/generatePrd.js +46 -46
  112. package/dist/tools/prompt/enhancePromptGemini.js +160 -160
  113. package/dist/tools/reasoning/applyReasoningFramework.js +56 -56
  114. package/dist/tools/semantic/analyzeDependencyGraph.js +12 -12
  115. package/hooks/hooks.json +121 -121
  116. package/package.json +75 -73
  117. package/skills/git-worktree.md +178 -178
  118. package/skills/priority-todos.md +236 -236
@@ -102,37 +102,37 @@ export class MemoryManager {
102
102
  }
103
103
  initializeDatabase() {
104
104
  // Create memories table
105
- this.db.exec(`
106
- CREATE TABLE IF NOT EXISTS memories (
107
- key TEXT PRIMARY KEY,
108
- value TEXT NOT NULL,
109
- category TEXT NOT NULL DEFAULT 'general',
110
- timestamp TEXT NOT NULL,
111
- lastAccessed TEXT NOT NULL,
112
- priority INTEGER DEFAULT 0
113
- );
114
-
115
- CREATE INDEX IF NOT EXISTS idx_category ON memories(category);
116
- CREATE INDEX IF NOT EXISTS idx_timestamp ON memories(timestamp);
117
- CREATE INDEX IF NOT EXISTS idx_priority ON memories(priority);
118
- CREATE INDEX IF NOT EXISTS idx_lastAccessed ON memories(lastAccessed);
105
+ this.db.exec(`
106
+ CREATE TABLE IF NOT EXISTS memories (
107
+ key TEXT PRIMARY KEY,
108
+ value TEXT NOT NULL,
109
+ category TEXT NOT NULL DEFAULT 'general',
110
+ timestamp TEXT NOT NULL,
111
+ lastAccessed TEXT NOT NULL,
112
+ priority INTEGER DEFAULT 0
113
+ );
114
+
115
+ CREATE INDEX IF NOT EXISTS idx_category ON memories(category);
116
+ CREATE INDEX IF NOT EXISTS idx_timestamp ON memories(timestamp);
117
+ CREATE INDEX IF NOT EXISTS idx_priority ON memories(priority);
118
+ CREATE INDEX IF NOT EXISTS idx_lastAccessed ON memories(lastAccessed);
119
119
  `);
120
120
  // v2.0: Create memory_relations table for Knowledge Graph
121
- this.db.exec(`
122
- CREATE TABLE IF NOT EXISTS memory_relations (
123
- id INTEGER PRIMARY KEY AUTOINCREMENT,
124
- sourceKey TEXT NOT NULL,
125
- targetKey TEXT NOT NULL,
126
- relationType TEXT NOT NULL,
127
- strength REAL DEFAULT 1.0,
128
- metadata TEXT,
129
- timestamp TEXT NOT NULL,
130
- UNIQUE(sourceKey, targetKey, relationType)
131
- );
132
-
133
- CREATE INDEX IF NOT EXISTS idx_rel_source ON memory_relations(sourceKey);
134
- CREATE INDEX IF NOT EXISTS idx_rel_target ON memory_relations(targetKey);
135
- CREATE INDEX IF NOT EXISTS idx_rel_type ON memory_relations(relationType);
121
+ this.db.exec(`
122
+ CREATE TABLE IF NOT EXISTS memory_relations (
123
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
124
+ sourceKey TEXT NOT NULL,
125
+ targetKey TEXT NOT NULL,
126
+ relationType TEXT NOT NULL,
127
+ strength REAL DEFAULT 1.0,
128
+ metadata TEXT,
129
+ timestamp TEXT NOT NULL,
130
+ UNIQUE(sourceKey, targetKey, relationType)
131
+ );
132
+
133
+ CREATE INDEX IF NOT EXISTS idx_rel_source ON memory_relations(sourceKey);
134
+ CREATE INDEX IF NOT EXISTS idx_rel_target ON memory_relations(targetKey);
135
+ CREATE INDEX IF NOT EXISTS idx_rel_type ON memory_relations(relationType);
136
136
  `);
137
137
  // Enable WAL mode for better concurrency
138
138
  this.db.pragma('journal_mode = WAL');
@@ -142,10 +142,10 @@ export class MemoryManager {
142
142
  initializePreparedStatements() {
143
143
  // Pre-compile recall statement
144
144
  try {
145
- this.recallStmt = this.db.prepare(`
146
- UPDATE memories SET lastAccessed = ?
147
- WHERE key = ?
148
- RETURNING *
145
+ this.recallStmt = this.db.prepare(`
146
+ UPDATE memories SET lastAccessed = ?
147
+ WHERE key = ?
148
+ RETURNING *
149
149
  `);
150
150
  }
151
151
  catch (error) {
@@ -155,9 +155,9 @@ export class MemoryManager {
155
155
  this.recallUpdateStmt = this.db.prepare(`UPDATE memories SET lastAccessed = ? WHERE key = ?`);
156
156
  }
157
157
  // Pre-compile save statement
158
- this.saveStmt = this.db.prepare(`
159
- INSERT OR REPLACE INTO memories (key, value, category, timestamp, lastAccessed, priority)
160
- VALUES (?, ?, ?, ?, ?, ?)
158
+ this.saveStmt = this.db.prepare(`
159
+ INSERT OR REPLACE INTO memories (key, value, category, timestamp, lastAccessed, priority)
160
+ VALUES (?, ?, ?, ?, ?, ?)
161
161
  `);
162
162
  }
163
163
  /**
@@ -193,9 +193,9 @@ export class MemoryManager {
193
193
  * Import memories into SQLite database
194
194
  */
195
195
  importMemories(memories) {
196
- const insert = this.db.prepare(`
197
- INSERT OR REPLACE INTO memories (key, value, category, timestamp, lastAccessed, priority)
198
- VALUES (?, ?, ?, ?, ?, ?)
196
+ const insert = this.db.prepare(`
197
+ INSERT OR REPLACE INTO memories (key, value, category, timestamp, lastAccessed, priority)
198
+ VALUES (?, ?, ?, ?, ?, ?)
199
199
  `);
200
200
  const insertMany = this.db.transaction((items) => {
201
201
  for (const item of items) {
@@ -230,9 +230,9 @@ export class MemoryManager {
230
230
  }
231
231
  else {
232
232
  // Fallback if prepared statement not available
233
- const stmt = this.db.prepare(`
234
- INSERT OR REPLACE INTO memories (key, value, category, timestamp, lastAccessed, priority)
235
- VALUES (?, ?, ?, ?, ?, ?)
233
+ const stmt = this.db.prepare(`
234
+ INSERT OR REPLACE INTO memories (key, value, category, timestamp, lastAccessed, priority)
235
+ VALUES (?, ?, ?, ?, ?, ?)
236
236
  `);
237
237
  stmt.run(key, value, category, timestamp, timestamp, priority);
238
238
  }
@@ -267,8 +267,8 @@ export class MemoryManager {
267
267
  delete(key) {
268
268
  // Also delete related relations
269
269
  this.db.prepare(`DELETE FROM memory_relations WHERE sourceKey = ? OR targetKey = ?`).run(key, key);
270
- const stmt = this.db.prepare(`
271
- DELETE FROM memories WHERE key = ?
270
+ const stmt = this.db.prepare(`
271
+ DELETE FROM memories WHERE key = ?
272
272
  `);
273
273
  const result = stmt.run(key);
274
274
  return result.changes > 0;
@@ -281,10 +281,10 @@ export class MemoryManager {
281
281
  */
282
282
  update(key, value) {
283
283
  const timestamp = new Date().toISOString();
284
- const stmt = this.db.prepare(`
285
- UPDATE memories
286
- SET value = ?, timestamp = ?, lastAccessed = ?
287
- WHERE key = ?
284
+ const stmt = this.db.prepare(`
285
+ UPDATE memories
286
+ SET value = ?, timestamp = ?, lastAccessed = ?
287
+ WHERE key = ?
288
288
  `);
289
289
  const result = stmt.run(value, timestamp, timestamp, key);
290
290
  return result.changes > 0;
@@ -297,16 +297,16 @@ export class MemoryManager {
297
297
  list(category) {
298
298
  let stmt;
299
299
  if (category) {
300
- stmt = this.db.prepare(`
301
- SELECT * FROM memories WHERE category = ?
302
- ORDER BY priority DESC, timestamp DESC
300
+ stmt = this.db.prepare(`
301
+ SELECT * FROM memories WHERE category = ?
302
+ ORDER BY priority DESC, timestamp DESC
303
303
  `);
304
304
  return stmt.all(category);
305
305
  }
306
306
  else {
307
- stmt = this.db.prepare(`
308
- SELECT * FROM memories
309
- ORDER BY priority DESC, timestamp DESC
307
+ stmt = this.db.prepare(`
308
+ SELECT * FROM memories
309
+ ORDER BY priority DESC, timestamp DESC
310
310
  `);
311
311
  return stmt.all();
312
312
  }
@@ -317,10 +317,10 @@ export class MemoryManager {
317
317
  * @returns Array of matching memory items
318
318
  */
319
319
  search(query) {
320
- const stmt = this.db.prepare(`
321
- SELECT * FROM memories
322
- WHERE key LIKE ? OR value LIKE ?
323
- ORDER BY priority DESC, timestamp DESC
320
+ const stmt = this.db.prepare(`
321
+ SELECT * FROM memories
322
+ WHERE key LIKE ? OR value LIKE ?
323
+ ORDER BY priority DESC, timestamp DESC
324
324
  `);
325
325
  const pattern = `%${query}%`;
326
326
  return stmt.all(pattern, pattern);
@@ -331,10 +331,10 @@ export class MemoryManager {
331
331
  * @returns Array of memory items with specified priority
332
332
  */
333
333
  getByPriority(priority) {
334
- const stmt = this.db.prepare(`
335
- SELECT * FROM memories
336
- WHERE priority = ?
337
- ORDER BY timestamp DESC
334
+ const stmt = this.db.prepare(`
335
+ SELECT * FROM memories
336
+ WHERE priority = ?
337
+ ORDER BY timestamp DESC
338
338
  `);
339
339
  return stmt.all(priority);
340
340
  }
@@ -345,8 +345,8 @@ export class MemoryManager {
345
345
  * @returns True if updated successfully
346
346
  */
347
347
  setPriority(key, priority) {
348
- const stmt = this.db.prepare(`
349
- UPDATE memories SET priority = ? WHERE key = ?
348
+ const stmt = this.db.prepare(`
349
+ UPDATE memories SET priority = ? WHERE key = ?
350
350
  `);
351
351
  const result = stmt.run(priority, key);
352
352
  return result.changes > 0;
@@ -357,10 +357,10 @@ export class MemoryManager {
357
357
  */
358
358
  getStats() {
359
359
  // Single query with ROLLUP or combined approach
360
- const categories = this.db.prepare(`
361
- SELECT category, COUNT(*) as count
362
- FROM memories
363
- GROUP BY category
360
+ const categories = this.db.prepare(`
361
+ SELECT category, COUNT(*) as count
362
+ FROM memories
363
+ GROUP BY category
364
364
  `).all();
365
365
  const byCategory = {};
366
366
  let total = 0;
@@ -385,10 +385,10 @@ export class MemoryManager {
385
385
  const timestamp = new Date().toISOString();
386
386
  const metadataJson = metadata ? JSON.stringify(metadata) : null;
387
387
  try {
388
- const stmt = this.db.prepare(`
389
- INSERT OR REPLACE INTO memory_relations
390
- (sourceKey, targetKey, relationType, strength, metadata, timestamp)
391
- VALUES (?, ?, ?, ?, ?, ?)
388
+ const stmt = this.db.prepare(`
389
+ INSERT OR REPLACE INTO memory_relations
390
+ (sourceKey, targetKey, relationType, strength, metadata, timestamp)
391
+ VALUES (?, ?, ?, ?, ?, ?)
392
392
  `);
393
393
  stmt.run(sourceKey, targetKey, relationType, strength, metadataJson, timestamp);
394
394
  return true;
@@ -644,9 +644,9 @@ export class MemoryManager {
644
644
  }
645
645
  }
646
646
  searchKeyword(query, limit, category) {
647
- let sql = `
648
- SELECT * FROM memories
649
- WHERE (key LIKE ? OR value LIKE ?)
647
+ let sql = `
648
+ SELECT * FROM memories
649
+ WHERE (key LIKE ? OR value LIKE ?)
650
650
  `;
651
651
  const params = [`%${query}%`, `%${query}%`];
652
652
  if (category) {
@@ -658,32 +658,32 @@ export class MemoryManager {
658
658
  return this.db.prepare(sql).all(...params);
659
659
  }
660
660
  searchTemporal(query, limit) {
661
- const sql = `
662
- SELECT * FROM memories
663
- WHERE key LIKE ? OR value LIKE ?
664
- ORDER BY timestamp DESC
665
- LIMIT ?
661
+ const sql = `
662
+ SELECT * FROM memories
663
+ WHERE key LIKE ? OR value LIKE ?
664
+ ORDER BY timestamp DESC
665
+ LIMIT ?
666
666
  `;
667
667
  return this.db.prepare(sql).all(`%${query}%`, `%${query}%`, limit);
668
668
  }
669
669
  searchByPriority(query, limit) {
670
- const sql = `
671
- SELECT * FROM memories
672
- WHERE key LIKE ? OR value LIKE ?
673
- ORDER BY priority DESC, lastAccessed DESC
674
- LIMIT ?
670
+ const sql = `
671
+ SELECT * FROM memories
672
+ WHERE key LIKE ? OR value LIKE ?
673
+ ORDER BY priority DESC, lastAccessed DESC
674
+ LIMIT ?
675
675
  `;
676
676
  return this.db.prepare(sql).all(`%${query}%`, `%${query}%`, limit);
677
677
  }
678
678
  searchContextAware(query, limit, category) {
679
679
  // Combined strategy: keyword + priority + recency
680
- let sql = `
681
- SELECT *,
682
- (CASE WHEN key LIKE ? THEN 3 ELSE 0 END +
683
- CASE WHEN value LIKE ? THEN 2 ELSE 0 END +
684
- priority * 0.5) as relevance_score
685
- FROM memories
686
- WHERE key LIKE ? OR value LIKE ?
680
+ let sql = `
681
+ SELECT *,
682
+ (CASE WHEN key LIKE ? THEN 3 ELSE 0 END +
683
+ CASE WHEN value LIKE ? THEN 2 ELSE 0 END +
684
+ priority * 0.5) as relevance_score
685
+ FROM memories
686
+ WHERE key LIKE ? OR value LIKE ?
687
687
  `;
688
688
  const params = [`%${query}%`, `%${query}%`, `%${query}%`, `%${query}%`];
689
689
  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;