musubi-sdd 5.1.0 → 5.6.2

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 (232) hide show
  1. package/README.ja.md +106 -48
  2. package/README.md +110 -32
  3. package/bin/musubi-analyze.js +74 -67
  4. package/bin/musubi-browser.js +27 -26
  5. package/bin/musubi-change.js +48 -47
  6. package/bin/musubi-checkpoint.js +10 -7
  7. package/bin/musubi-convert.js +25 -25
  8. package/bin/musubi-costs.js +27 -10
  9. package/bin/musubi-gui.js +52 -46
  10. package/bin/musubi-init.js +1952 -10
  11. package/bin/musubi-orchestrate.js +327 -239
  12. package/bin/musubi-remember.js +69 -56
  13. package/bin/musubi-resolve.js +53 -45
  14. package/bin/musubi-trace.js +51 -22
  15. package/bin/musubi-validate.js +39 -30
  16. package/bin/musubi-workflow.js +33 -34
  17. package/bin/musubi.js +39 -2
  18. package/package.json +1 -1
  19. package/src/agents/agent-loop.js +94 -95
  20. package/src/agents/agentic/code-generator.js +119 -109
  21. package/src/agents/agentic/code-reviewer.js +105 -108
  22. package/src/agents/agentic/index.js +4 -4
  23. package/src/agents/browser/action-executor.js +13 -13
  24. package/src/agents/browser/ai-comparator.js +11 -10
  25. package/src/agents/browser/context-manager.js +6 -6
  26. package/src/agents/browser/index.js +5 -5
  27. package/src/agents/browser/nl-parser.js +31 -46
  28. package/src/agents/browser/screenshot.js +2 -2
  29. package/src/agents/browser/test-generator.js +6 -4
  30. package/src/agents/function-tool.js +71 -65
  31. package/src/agents/index.js +7 -7
  32. package/src/agents/schema-generator.js +98 -94
  33. package/src/analyzers/ast-extractor.js +158 -146
  34. package/src/analyzers/codegraph-auto-update.js +858 -0
  35. package/src/analyzers/complexity-analyzer.js +536 -0
  36. package/src/analyzers/context-optimizer.js +241 -126
  37. package/src/analyzers/impact-analyzer.js +1 -1
  38. package/src/analyzers/large-project-analyzer.js +766 -0
  39. package/src/analyzers/repository-map.js +77 -81
  40. package/src/analyzers/security-analyzer.js +19 -11
  41. package/src/analyzers/stuck-detector.js +19 -17
  42. package/src/converters/index.js +78 -57
  43. package/src/converters/ir/types.js +12 -12
  44. package/src/converters/parsers/musubi-parser.js +134 -126
  45. package/src/converters/parsers/openapi-parser.js +70 -53
  46. package/src/converters/parsers/speckit-parser.js +239 -175
  47. package/src/converters/writers/musubi-writer.js +123 -118
  48. package/src/converters/writers/speckit-writer.js +124 -113
  49. package/src/generators/rust-migration-generator.js +512 -0
  50. package/src/gui/public/index.html +1365 -1211
  51. package/src/gui/server.js +41 -40
  52. package/src/gui/services/file-watcher.js +23 -8
  53. package/src/gui/services/project-scanner.js +26 -20
  54. package/src/gui/services/replanning-service.js +27 -23
  55. package/src/gui/services/traceability-service.js +8 -8
  56. package/src/gui/services/workflow-service.js +14 -7
  57. package/src/index.js +151 -0
  58. package/src/integrations/cicd.js +90 -104
  59. package/src/integrations/codegraph-mcp.js +643 -0
  60. package/src/integrations/documentation.js +142 -103
  61. package/src/integrations/examples.js +95 -80
  62. package/src/integrations/github-client.js +17 -17
  63. package/src/integrations/index.js +5 -5
  64. package/src/integrations/mcp/index.js +21 -21
  65. package/src/integrations/mcp/mcp-context-provider.js +76 -78
  66. package/src/integrations/mcp/mcp-discovery.js +74 -72
  67. package/src/integrations/mcp/mcp-tool-registry.js +99 -94
  68. package/src/integrations/mcp-connector.js +70 -66
  69. package/src/integrations/platforms.js +50 -49
  70. package/src/integrations/tool-discovery.js +37 -31
  71. package/src/llm-providers/anthropic-provider.js +11 -11
  72. package/src/llm-providers/base-provider.js +16 -18
  73. package/src/llm-providers/copilot-provider.js +22 -19
  74. package/src/llm-providers/index.js +26 -25
  75. package/src/llm-providers/ollama-provider.js +11 -11
  76. package/src/llm-providers/openai-provider.js +12 -12
  77. package/src/managers/agent-memory.js +36 -24
  78. package/src/managers/checkpoint-manager.js +4 -8
  79. package/src/managers/delta-spec.js +19 -19
  80. package/src/managers/index.js +13 -4
  81. package/src/managers/memory-condenser.js +35 -45
  82. package/src/managers/repo-skill-manager.js +57 -31
  83. package/src/managers/skill-loader.js +25 -22
  84. package/src/managers/skill-tools.js +36 -72
  85. package/src/managers/workflow.js +30 -22
  86. package/src/monitoring/cost-tracker.js +48 -46
  87. package/src/monitoring/incident-manager.js +116 -106
  88. package/src/monitoring/index.js +144 -134
  89. package/src/monitoring/observability.js +75 -62
  90. package/src/monitoring/quality-dashboard.js +45 -41
  91. package/src/monitoring/release-manager.js +63 -53
  92. package/src/orchestration/agent-skill-binding.js +39 -47
  93. package/src/orchestration/error-handler.js +65 -107
  94. package/src/orchestration/guardrails/base-guardrail.js +26 -24
  95. package/src/orchestration/guardrails/guardrail-rules.js +50 -64
  96. package/src/orchestration/guardrails/index.js +5 -5
  97. package/src/orchestration/guardrails/input-guardrail.js +58 -45
  98. package/src/orchestration/guardrails/output-guardrail.js +104 -81
  99. package/src/orchestration/guardrails/safety-check.js +79 -79
  100. package/src/orchestration/index.js +38 -55
  101. package/src/orchestration/mcp-tool-adapters.js +96 -99
  102. package/src/orchestration/orchestration-engine.js +21 -21
  103. package/src/orchestration/pattern-registry.js +60 -45
  104. package/src/orchestration/patterns/auto.js +34 -47
  105. package/src/orchestration/patterns/group-chat.js +59 -65
  106. package/src/orchestration/patterns/handoff.js +67 -65
  107. package/src/orchestration/patterns/human-in-loop.js +51 -72
  108. package/src/orchestration/patterns/nested.js +25 -40
  109. package/src/orchestration/patterns/sequential.js +35 -34
  110. package/src/orchestration/patterns/swarm.js +63 -56
  111. package/src/orchestration/patterns/triage.js +150 -109
  112. package/src/orchestration/reasoning/index.js +9 -9
  113. package/src/orchestration/reasoning/planning-engine.js +143 -140
  114. package/src/orchestration/reasoning/reasoning-engine.js +206 -144
  115. package/src/orchestration/reasoning/self-correction.js +121 -128
  116. package/src/orchestration/replanning/adaptive-goal-modifier.js +107 -112
  117. package/src/orchestration/replanning/alternative-generator.js +37 -42
  118. package/src/orchestration/replanning/config.js +63 -59
  119. package/src/orchestration/replanning/goal-progress-tracker.js +98 -100
  120. package/src/orchestration/replanning/index.js +24 -20
  121. package/src/orchestration/replanning/plan-evaluator.js +49 -50
  122. package/src/orchestration/replanning/plan-monitor.js +32 -28
  123. package/src/orchestration/replanning/proactive-path-optimizer.js +175 -178
  124. package/src/orchestration/replanning/replan-history.js +33 -26
  125. package/src/orchestration/replanning/replanning-engine.js +106 -108
  126. package/src/orchestration/skill-executor.js +107 -109
  127. package/src/orchestration/skill-registry.js +85 -89
  128. package/src/orchestration/workflow-examples.js +228 -231
  129. package/src/orchestration/workflow-executor.js +65 -68
  130. package/src/orchestration/workflow-orchestrator.js +72 -73
  131. package/src/phase4-integration.js +47 -40
  132. package/src/phase5-integration.js +89 -30
  133. package/src/reporters/coverage-report.js +82 -30
  134. package/src/reporters/hierarchical-reporter.js +498 -0
  135. package/src/reporters/traceability-matrix-report.js +29 -20
  136. package/src/resolvers/issue-resolver.js +43 -31
  137. package/src/steering/advanced-validation.js +133 -124
  138. package/src/steering/auto-updater.js +60 -73
  139. package/src/steering/index.js +6 -6
  140. package/src/steering/quality-metrics.js +41 -35
  141. package/src/steering/steering-auto-update.js +83 -86
  142. package/src/steering/steering-validator.js +98 -106
  143. package/src/steering/template-constraints.js +53 -54
  144. package/src/templates/agents/claude-code/CLAUDE.md +32 -32
  145. package/src/templates/agents/claude-code/skills/agent-assistant/SKILL.md +13 -5
  146. package/src/templates/agents/claude-code/skills/ai-ml-engineer/mlops-guide.md +23 -23
  147. package/src/templates/agents/claude-code/skills/ai-ml-engineer/model-card-template.md +60 -41
  148. package/src/templates/agents/claude-code/skills/api-designer/api-patterns.md +27 -19
  149. package/src/templates/agents/claude-code/skills/api-designer/openapi-template.md +11 -7
  150. package/src/templates/agents/claude-code/skills/bug-hunter/SKILL.md +4 -3
  151. package/src/templates/agents/claude-code/skills/bug-hunter/root-cause-analysis.md +37 -15
  152. package/src/templates/agents/claude-code/skills/change-impact-analyzer/dependency-graph-patterns.md +36 -42
  153. package/src/templates/agents/claude-code/skills/change-impact-analyzer/impact-analysis-template.md +69 -60
  154. package/src/templates/agents/claude-code/skills/cloud-architect/aws-patterns.md +31 -38
  155. package/src/templates/agents/claude-code/skills/cloud-architect/azure-patterns.md +28 -23
  156. package/src/templates/agents/claude-code/skills/code-reviewer/SKILL.md +61 -0
  157. package/src/templates/agents/claude-code/skills/code-reviewer/best-practices.md +27 -0
  158. package/src/templates/agents/claude-code/skills/code-reviewer/review-checklist.md +29 -10
  159. package/src/templates/agents/claude-code/skills/code-reviewer/review-standards.md +29 -24
  160. package/src/templates/agents/claude-code/skills/constitution-enforcer/SKILL.md +8 -6
  161. package/src/templates/agents/claude-code/skills/constitution-enforcer/constitutional-articles.md +62 -26
  162. package/src/templates/agents/claude-code/skills/constitution-enforcer/phase-minus-one-gates.md +35 -16
  163. package/src/templates/agents/claude-code/skills/database-administrator/backup-recovery.md +27 -17
  164. package/src/templates/agents/claude-code/skills/database-administrator/tuning-guide.md +25 -20
  165. package/src/templates/agents/claude-code/skills/database-schema-designer/schema-patterns.md +39 -22
  166. package/src/templates/agents/claude-code/skills/devops-engineer/ci-cd-templates.md +25 -22
  167. package/src/templates/agents/claude-code/skills/issue-resolver/SKILL.md +24 -21
  168. package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +148 -63
  169. package/src/templates/agents/claude-code/skills/orchestrator/patterns.md +35 -16
  170. package/src/templates/agents/claude-code/skills/orchestrator/selection-matrix.md +69 -64
  171. package/src/templates/agents/claude-code/skills/performance-engineer/optimization-playbook.md +47 -47
  172. package/src/templates/agents/claude-code/skills/performance-optimizer/SKILL.md +69 -0
  173. package/src/templates/agents/claude-code/skills/performance-optimizer/benchmark-template.md +63 -45
  174. package/src/templates/agents/claude-code/skills/performance-optimizer/optimization-patterns.md +33 -35
  175. package/src/templates/agents/claude-code/skills/project-manager/SKILL.md +7 -6
  176. package/src/templates/agents/claude-code/skills/project-manager/agile-ceremonies.md +47 -28
  177. package/src/templates/agents/claude-code/skills/project-manager/project-templates.md +94 -78
  178. package/src/templates/agents/claude-code/skills/quality-assurance/SKILL.md +20 -17
  179. package/src/templates/agents/claude-code/skills/quality-assurance/qa-plan-template.md +63 -49
  180. package/src/templates/agents/claude-code/skills/release-coordinator/SKILL.md +5 -5
  181. package/src/templates/agents/claude-code/skills/release-coordinator/feature-flag-guide.md +30 -26
  182. package/src/templates/agents/claude-code/skills/release-coordinator/release-plan-template.md +67 -35
  183. package/src/templates/agents/claude-code/skills/requirements-analyst/ears-format.md +54 -42
  184. package/src/templates/agents/claude-code/skills/requirements-analyst/validation-rules.md +36 -33
  185. package/src/templates/agents/claude-code/skills/security-auditor/SKILL.md +77 -19
  186. package/src/templates/agents/claude-code/skills/security-auditor/audit-checklists.md +24 -24
  187. package/src/templates/agents/claude-code/skills/security-auditor/owasp-top-10.md +61 -20
  188. package/src/templates/agents/claude-code/skills/security-auditor/vulnerability-patterns.md +43 -11
  189. package/src/templates/agents/claude-code/skills/site-reliability-engineer/SKILL.md +1 -0
  190. package/src/templates/agents/claude-code/skills/site-reliability-engineer/incident-response-template.md +55 -25
  191. package/src/templates/agents/claude-code/skills/site-reliability-engineer/observability-patterns.md +78 -68
  192. package/src/templates/agents/claude-code/skills/site-reliability-engineer/slo-sli-guide.md +73 -53
  193. package/src/templates/agents/claude-code/skills/software-developer/solid-principles.md +83 -37
  194. package/src/templates/agents/claude-code/skills/software-developer/test-first-workflow.md +38 -31
  195. package/src/templates/agents/claude-code/skills/steering/SKILL.md +1 -0
  196. package/src/templates/agents/claude-code/skills/steering/auto-update-rules.md +31 -0
  197. package/src/templates/agents/claude-code/skills/system-architect/adr-template.md +25 -7
  198. package/src/templates/agents/claude-code/skills/system-architect/c4-model-guide.md +74 -61
  199. package/src/templates/agents/claude-code/skills/technical-writer/doc-templates/documentation-templates.md +70 -52
  200. package/src/templates/agents/claude-code/skills/test-engineer/SKILL.md +2 -0
  201. package/src/templates/agents/claude-code/skills/test-engineer/ears-test-mapping.md +75 -71
  202. package/src/templates/agents/claude-code/skills/test-engineer/test-types.md +85 -63
  203. package/src/templates/agents/claude-code/skills/traceability-auditor/coverage-matrix-template.md +39 -36
  204. package/src/templates/agents/claude-code/skills/traceability-auditor/gap-detection-rules.md +22 -17
  205. package/src/templates/agents/claude-code/skills/ui-ux-designer/SKILL.md +1 -0
  206. package/src/templates/agents/claude-code/skills/ui-ux-designer/accessibility-guidelines.md +49 -75
  207. package/src/templates/agents/claude-code/skills/ui-ux-designer/design-system-components.md +71 -59
  208. package/src/templates/agents/codex/AGENTS.md +74 -42
  209. package/src/templates/agents/cursor/AGENTS.md +74 -42
  210. package/src/templates/agents/gemini-cli/GEMINI.md +74 -42
  211. package/src/templates/agents/github-copilot/AGENTS.md +83 -51
  212. package/src/templates/agents/qwen-code/QWEN.md +74 -42
  213. package/src/templates/agents/windsurf/AGENTS.md +74 -42
  214. package/src/templates/architectures/README.md +41 -0
  215. package/src/templates/architectures/clean-architecture/README.md +113 -0
  216. package/src/templates/architectures/event-driven/README.md +162 -0
  217. package/src/templates/architectures/hexagonal/README.md +130 -0
  218. package/src/templates/index.js +6 -1
  219. package/src/templates/locale-manager.js +16 -16
  220. package/src/templates/shared/delta-spec-template.md +20 -13
  221. package/src/templates/shared/github-actions/musubi-issue-resolver.yml +5 -5
  222. package/src/templates/shared/github-actions/musubi-security-check.yml +3 -3
  223. package/src/templates/shared/github-actions/musubi-validate.yml +4 -4
  224. package/src/templates/shared/steering/structure.md +95 -0
  225. package/src/templates/skills/browser-agent.md +21 -16
  226. package/src/templates/skills/web-gui.md +8 -0
  227. package/src/templates/template-constraints.js +50 -53
  228. package/src/validators/advanced-validation.js +30 -36
  229. package/src/validators/constitutional-validator.js +77 -73
  230. package/src/validators/critic-system.js +49 -59
  231. package/src/validators/delta-format.js +59 -55
  232. package/src/validators/traceability-validator.js +7 -11
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * AST Extractor
3
- *
3
+ *
4
4
  * Extracts Abstract Syntax Tree information from source code files.
5
5
  * Provides structured analysis of code structure, symbols, and relationships.
6
- *
6
+ *
7
7
  * Part of MUSUBI v5.0.0 - Codebase Intelligence
8
- *
8
+ *
9
9
  * @module analyzers/ast-extractor
10
10
  * @version 1.0.0
11
- *
11
+ *
12
12
  * @traceability
13
13
  * - Requirement: REQ-P4-002 (AST Extraction and Analysis)
14
14
  * - Design: docs/design/tdd-musubi-v5.0.0.md#2.2
@@ -60,43 +60,53 @@ const { EventEmitter } = require('events');
60
60
  const PATTERNS = {
61
61
  javascript: {
62
62
  // Function declarations (removed ^ to match anywhere in line)
63
- functionDecl: /(?:^|\n)\s*(?:export\s+)?(?:async\s+)?function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(([^)]*)\)/g,
63
+ functionDecl:
64
+ /(?:^|\n)\s*(?:export\s+)?(?:async\s+)?function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(([^)]*)\)/g,
64
65
  // Arrow functions with const/let/var
65
- arrowFunc: /(?:export\s+)?(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*(?:async\s+)?\([^)]*\)\s*=>/g,
66
+ arrowFunc:
67
+ /(?:export\s+)?(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*(?:async\s+)?\([^)]*\)\s*=>/g,
66
68
  // Class declarations
67
- classDecl: /(?:^|\n)\s*(?:export\s+)?class\s+([a-zA-Z_$][a-zA-Z0-9_$]*)(?:\s+extends\s+([a-zA-Z_$][a-zA-Z0-9_$]*))?/g,
69
+ classDecl:
70
+ /(?:^|\n)\s*(?:export\s+)?class\s+([a-zA-Z_$][a-zA-Z0-9_$]*)(?:\s+extends\s+([a-zA-Z_$][a-zA-Z0-9_$]*))?/g,
68
71
  // Class methods
69
- methodDecl: /\n\s+(?:static\s+)?(?:async\s+)?(?:get\s+|set\s+)?([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\([^)]*\)\s*\{/g,
72
+ methodDecl:
73
+ /\n\s+(?:static\s+)?(?:async\s+)?(?:get\s+|set\s+)?([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\([^)]*\)\s*\{/g,
70
74
  // Variable declarations
71
75
  constDecl: /(?:export\s+)?(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=/g,
72
76
  // ES6 imports
73
- importStmt: /import\s+(?:(\{[^}]+\})|([a-zA-Z_$][a-zA-Z0-9_$]*)(?:\s*,\s*(\{[^}]+\}))?|\*\s+as\s+([a-zA-Z_$][a-zA-Z0-9_$]*))\s+from\s+['"](.*?)['"]/g,
77
+ importStmt:
78
+ /import\s+(?:(\{[^}]+\})|([a-zA-Z_$][a-zA-Z0-9_$]*)(?:\s*,\s*(\{[^}]+\}))?|\*\s+as\s+([a-zA-Z_$][a-zA-Z0-9_$]*))\s+from\s+['"](.*?)['"]/g,
74
79
  // CommonJS require
75
- requireStmt: /(?:const|let|var)\s+(?:(\{[^}]+\})|([a-zA-Z_$][a-zA-Z0-9_$]*))\s*=\s*require\s*\(\s*['"](.*?)['"]\s*\)/g,
80
+ requireStmt:
81
+ /(?:const|let|var)\s+(?:(\{[^}]+\})|([a-zA-Z_$][a-zA-Z0-9_$]*))\s*=\s*require\s*\(\s*['"](.*?)['"]\s*\)/g,
76
82
  // Exports - capture function/class/const names after export
77
- exportStmt: /export\s+(?:default\s+)?(?:(const|let|var|function|class|async\s+function)\s+)?([a-zA-Z_$][a-zA-Z0-9_$]*)?/g,
83
+ exportStmt:
84
+ /export\s+(?:default\s+)?(?:(const|let|var|function|class|async\s+function)\s+)?([a-zA-Z_$][a-zA-Z0-9_$]*)?/g,
78
85
  namedExport: /export\s*\{([^}]+)\}/gm,
79
86
  // JSDoc comments
80
87
  jsdoc: /\/\*\*\s*([\s\S]*?)\s*\*\//g,
81
88
  // Single-line comments
82
- comment: /\/\/\s*(.+)$/g
89
+ comment: /\/\/\s*(.+)$/g,
83
90
  },
84
-
91
+
85
92
  typescript: {
86
93
  // Extends JavaScript patterns
87
94
  // Interface declarations
88
- interfaceDecl: /(?:^|\n)\s*(?:export\s+)?interface\s+([a-zA-Z_$][a-zA-Z0-9_$]*)(?:<[^>]+>)?(?:\s+extends\s+([^{]+))?/g,
95
+ interfaceDecl:
96
+ /(?:^|\n)\s*(?:export\s+)?interface\s+([a-zA-Z_$][a-zA-Z0-9_$]*)(?:<[^>]+>)?(?:\s+extends\s+([^{]+))?/g,
89
97
  // Type aliases
90
98
  typeDecl: /(?:^|\n)\s*(?:export\s+)?type\s+([a-zA-Z_$][a-zA-Z0-9_$]*)(?:<[^>]+>)?\s*=/g,
91
99
  // Enum declarations
92
100
  enumDecl: /(?:^|\n)\s*(?:export\s+)?(?:const\s+)?enum\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g,
93
101
  // Function with types
94
- typedFunc: /(?:export\s+)?(?:async\s+)?function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)(?:<[^>]+>)?\s*\(([^)]*)\)\s*:\s*([^{]+)/g
102
+ typedFunc:
103
+ /(?:export\s+)?(?:async\s+)?function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)(?:<[^>]+>)?\s*\(([^)]*)\)\s*:\s*([^{]+)/g,
95
104
  },
96
-
105
+
97
106
  python: {
98
107
  // Function definitions
99
- functionDef: /(?:^|\n)(?:async\s+)?def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(([^)]*)\)(?:\s*->\s*([^\s:]+))?/g,
108
+ functionDef:
109
+ /(?:^|\n)(?:async\s+)?def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(([^)]*)\)(?:\s*->\s*([^\s:]+))?/g,
100
110
  // Class definitions
101
111
  classDef: /(?:^|\n)class\s+([a-zA-Z_][a-zA-Z0-9_]*)(?:\s*\(([^)]*)\))?/g,
102
112
  // Method definitions (inside class)
@@ -109,8 +119,8 @@ const PATTERNS = {
109
119
  // Docstrings
110
120
  docstring: /"""([\s\S]*?)"""|'''([\s\S]*?)'''/g,
111
121
  // Type hints
112
- typeHint: /:\s*([a-zA-Z_][a-zA-Z0-9_\[\],\s]*)/g
113
- }
122
+ typeHint: /:\s*([a-zA-Z_][a-zA-Z0-9_[\],\s]*)/g,
123
+ },
114
124
  };
115
125
 
116
126
  /**
@@ -130,11 +140,11 @@ class ASTExtractor extends EventEmitter {
130
140
  this.supportedLanguages = options.supportedLanguages || ['javascript', 'typescript', 'python'];
131
141
  this.includeDocstrings = options.includeDocstrings ?? true;
132
142
  this.extractComments = options.extractComments ?? false;
133
-
143
+
134
144
  // Results cache
135
145
  this.cache = new Map();
136
146
  }
137
-
147
+
138
148
  /**
139
149
  * Extract AST from file
140
150
  * @param {string} filePath - File path
@@ -143,7 +153,7 @@ class ASTExtractor extends EventEmitter {
143
153
  async extractFromFile(filePath) {
144
154
  const content = await fs.promises.readFile(filePath, 'utf-8');
145
155
  const language = this.detectLanguage(filePath);
146
-
156
+
147
157
  if (!this.supportedLanguages.includes(language)) {
148
158
  return {
149
159
  path: filePath,
@@ -152,13 +162,13 @@ class ASTExtractor extends EventEmitter {
152
162
  imports: [],
153
163
  exports: [],
154
164
  structure: {},
155
- metadata: { supported: false }
165
+ metadata: { supported: false },
156
166
  };
157
167
  }
158
-
168
+
159
169
  return this.extract(content, language, filePath);
160
170
  }
161
-
171
+
162
172
  /**
163
173
  * Extract AST from content
164
174
  * @param {string} content - Source code content
@@ -168,7 +178,7 @@ class ASTExtractor extends EventEmitter {
168
178
  */
169
179
  extract(content, language, filePath = '<source>') {
170
180
  this.emit('extract:start', { filePath, language });
171
-
181
+
172
182
  const lines = content.split('\n');
173
183
  const symbols = [];
174
184
  const imports = [];
@@ -176,9 +186,9 @@ class ASTExtractor extends EventEmitter {
176
186
  const structure = {
177
187
  classes: [],
178
188
  functions: [],
179
- variables: []
189
+ variables: [],
180
190
  };
181
-
191
+
182
192
  try {
183
193
  switch (language) {
184
194
  case 'javascript':
@@ -192,7 +202,7 @@ class ASTExtractor extends EventEmitter {
192
202
  this.extractPython(content, lines, symbols, imports, exports, structure);
193
203
  break;
194
204
  }
195
-
205
+
196
206
  const result = {
197
207
  path: filePath,
198
208
  language,
@@ -203,13 +213,12 @@ class ASTExtractor extends EventEmitter {
203
213
  metadata: {
204
214
  lineCount: lines.length,
205
215
  symbolCount: symbols.length,
206
- extractedAt: new Date().toISOString()
207
- }
216
+ extractedAt: new Date().toISOString(),
217
+ },
208
218
  };
209
-
219
+
210
220
  this.emit('extract:complete', result);
211
221
  return result;
212
-
213
222
  } catch (error) {
214
223
  this.emit('extract:error', { filePath, error });
215
224
  return {
@@ -219,21 +228,21 @@ class ASTExtractor extends EventEmitter {
219
228
  imports: [],
220
229
  exports: [],
221
230
  structure: {},
222
- metadata: { error: error.message }
231
+ metadata: { error: error.message },
223
232
  };
224
233
  }
225
234
  }
226
-
235
+
227
236
  /**
228
237
  * Extract JavaScript/TypeScript patterns
229
238
  * @private
230
239
  */
231
240
  extractJavaScript(content, lines, symbols, imports, exports, structure) {
232
241
  const patterns = PATTERNS.javascript;
233
-
242
+
234
243
  // Extract docstrings for association
235
244
  const docstrings = this.extractDocstrings(content, 'javascript');
236
-
245
+
237
246
  // Functions
238
247
  let match;
239
248
  const funcPattern = new RegExp(patterns.functionDecl.source, 'g');
@@ -243,7 +252,7 @@ class ASTExtractor extends EventEmitter {
243
252
  const isAsync = match[0].includes('async');
244
253
  const params = this.parseParams(match[2]);
245
254
  const doc = this.findNearestDocstring(docstrings, line);
246
-
255
+
247
256
  const symbol = {
248
257
  name: match[1],
249
258
  type: 'function',
@@ -252,17 +261,17 @@ class ASTExtractor extends EventEmitter {
252
261
  params,
253
262
  isExported,
254
263
  isAsync,
255
- docstring: doc
264
+ docstring: doc,
256
265
  };
257
-
266
+
258
267
  symbols.push(symbol);
259
268
  structure.functions.push(symbol.name);
260
-
269
+
261
270
  if (isExported) {
262
271
  exports.push(match[1]);
263
272
  }
264
273
  }
265
-
274
+
266
275
  // Arrow functions
267
276
  const arrowPattern = new RegExp(patterns.arrowFunc.source, 'g');
268
277
  while ((match = arrowPattern.exec(content)) !== null) {
@@ -270,31 +279,31 @@ class ASTExtractor extends EventEmitter {
270
279
  const isExported = match[0].includes('export ');
271
280
  const isAsync = match[0].includes('async');
272
281
  const doc = this.findNearestDocstring(docstrings, line);
273
-
282
+
274
283
  const symbol = {
275
284
  name: match[1],
276
285
  type: 'function',
277
286
  line,
278
287
  isExported,
279
288
  isAsync,
280
- docstring: doc
289
+ docstring: doc,
281
290
  };
282
-
291
+
283
292
  symbols.push(symbol);
284
293
  structure.functions.push(symbol.name);
285
-
294
+
286
295
  if (isExported) {
287
296
  exports.push(match[1]);
288
297
  }
289
298
  }
290
-
299
+
291
300
  // Classes
292
301
  const classPattern = new RegExp(patterns.classDecl.source, 'g');
293
302
  while ((match = classPattern.exec(content)) !== null) {
294
303
  const line = this.getLineNumber(content, match.index);
295
304
  const isExported = match[0].includes('export ');
296
305
  const doc = this.findNearestDocstring(docstrings, line);
297
-
306
+
298
307
  const classSymbol = {
299
308
  name: match[1],
300
309
  type: 'class',
@@ -302,38 +311,38 @@ class ASTExtractor extends EventEmitter {
302
311
  isExported,
303
312
  extends: match[2] || null,
304
313
  docstring: doc,
305
- methods: []
314
+ methods: [],
306
315
  };
307
-
316
+
308
317
  // Extract class methods
309
318
  const classEndIndex = this.findClassEnd(content, match.index);
310
319
  const classContent = content.slice(match.index, classEndIndex);
311
320
  const methodPattern = new RegExp(patterns.methodDecl.source, 'gm');
312
321
  let methodMatch;
313
-
322
+
314
323
  while ((methodMatch = methodPattern.exec(classContent)) !== null) {
315
324
  classSymbol.methods.push(methodMatch[1]);
316
-
325
+
317
326
  symbols.push({
318
327
  name: `${match[1]}.${methodMatch[1]}`,
319
328
  type: 'method',
320
329
  line: line + this.getLineNumber(classContent, methodMatch.index) - 1,
321
- parentClass: match[1]
330
+ parentClass: match[1],
322
331
  });
323
332
  }
324
-
333
+
325
334
  symbols.push(classSymbol);
326
335
  structure.classes.push({
327
336
  name: classSymbol.name,
328
337
  extends: classSymbol.extends,
329
- methods: classSymbol.methods
338
+ methods: classSymbol.methods,
330
339
  });
331
-
340
+
332
341
  if (isExported) {
333
342
  exports.push(match[1]);
334
343
  }
335
344
  }
336
-
345
+
337
346
  // Imports (ES6)
338
347
  const importPattern = new RegExp(patterns.importStmt.source, 'gm');
339
348
  while ((match = importPattern.exec(content)) !== null) {
@@ -342,10 +351,13 @@ class ASTExtractor extends EventEmitter {
342
351
  let names = [];
343
352
  let isDefault = false;
344
353
  let isNamespace = false;
345
-
354
+
346
355
  if (match[1]) {
347
356
  // Named imports { a, b }
348
- names = match[1].replace(/[{}]/g, '').split(',').map(n => n.trim());
357
+ names = match[1]
358
+ .replace(/[{}]/g, '')
359
+ .split(',')
360
+ .map(n => n.trim());
349
361
  }
350
362
  if (match[2]) {
351
363
  // Default import
@@ -354,43 +366,51 @@ class ASTExtractor extends EventEmitter {
354
366
  }
355
367
  if (match[3]) {
356
368
  // Additional named imports after default
357
- names.push(...match[3].replace(/[{}]/g, '').split(',').map(n => n.trim()));
369
+ names.push(
370
+ ...match[3]
371
+ .replace(/[{}]/g, '')
372
+ .split(',')
373
+ .map(n => n.trim())
374
+ );
358
375
  }
359
376
  if (match[4]) {
360
377
  // Namespace import * as X
361
378
  names.push(match[4]);
362
379
  isNamespace = true;
363
380
  }
364
-
381
+
365
382
  imports.push({ source, names, isDefault, isNamespace, line });
366
383
  }
367
-
384
+
368
385
  // CommonJS require
369
386
  const requirePattern = new RegExp(patterns.requireStmt.source, 'gm');
370
387
  while ((match = requirePattern.exec(content)) !== null) {
371
388
  const line = this.getLineNumber(content, match.index);
372
389
  const source = match[3];
373
390
  let names = [];
374
-
391
+
375
392
  if (match[1]) {
376
393
  // Destructured require
377
- names = match[1].replace(/[{}]/g, '').split(',').map(n => n.trim());
394
+ names = match[1]
395
+ .replace(/[{}]/g, '')
396
+ .split(',')
397
+ .map(n => n.trim());
378
398
  }
379
399
  if (match[2]) {
380
400
  // Simple require
381
401
  names.push(match[2]);
382
402
  }
383
-
403
+
384
404
  imports.push({ source, names, isDefault: !!match[2], isNamespace: false, line });
385
405
  }
386
-
406
+
387
407
  // Named exports: export { a, b }
388
408
  const namedExportPattern = new RegExp(patterns.namedExport.source, 'gm');
389
409
  while ((match = namedExportPattern.exec(content)) !== null) {
390
410
  const names = match[1].split(',').map(n => n.trim().split(' as ')[0].trim());
391
411
  exports.push(...names);
392
412
  }
393
-
413
+
394
414
  // Direct exports: export const/let/var/function/class name
395
415
  const exportStmtPattern = new RegExp(patterns.exportStmt.source, 'gm');
396
416
  while ((match = exportStmtPattern.exec(content)) !== null) {
@@ -400,59 +420,59 @@ class ASTExtractor extends EventEmitter {
400
420
  }
401
421
  }
402
422
  }
403
-
423
+
404
424
  /**
405
425
  * Extract TypeScript-specific patterns
406
426
  * @private
407
427
  */
408
- extractTypeScript(content, lines, symbols, structure) {
428
+ extractTypeScript(content, lines, symbols, _structure) {
409
429
  const patterns = PATTERNS.typescript;
410
430
  let match;
411
-
431
+
412
432
  // Interfaces
413
433
  const interfacePattern = new RegExp(patterns.interfaceDecl.source, 'gm');
414
434
  while ((match = interfacePattern.exec(content)) !== null) {
415
435
  const line = this.getLineNumber(content, match.index);
416
436
  const isExported = match[0].includes('export');
417
-
437
+
418
438
  symbols.push({
419
439
  name: match[1],
420
440
  type: 'interface',
421
441
  line,
422
442
  isExported,
423
- extends: match[2] ? match[2].split(',').map(s => s.trim()) : []
443
+ extends: match[2] ? match[2].split(',').map(s => s.trim()) : [],
424
444
  });
425
445
  }
426
-
446
+
427
447
  // Type aliases
428
448
  const typePattern = new RegExp(patterns.typeDecl.source, 'gm');
429
449
  while ((match = typePattern.exec(content)) !== null) {
430
450
  const line = this.getLineNumber(content, match.index);
431
451
  const isExported = match[0].includes('export');
432
-
452
+
433
453
  symbols.push({
434
454
  name: match[1],
435
455
  type: 'type',
436
456
  line,
437
- isExported
457
+ isExported,
438
458
  });
439
459
  }
440
-
460
+
441
461
  // Enums
442
462
  const enumPattern = new RegExp(patterns.enumDecl.source, 'gm');
443
463
  while ((match = enumPattern.exec(content)) !== null) {
444
464
  const line = this.getLineNumber(content, match.index);
445
465
  const isExported = match[0].includes('export');
446
-
466
+
447
467
  symbols.push({
448
468
  name: match[1],
449
469
  type: 'enum',
450
470
  line,
451
- isExported
471
+ isExported,
452
472
  });
453
473
  }
454
474
  }
455
-
475
+
456
476
  /**
457
477
  * Extract Python patterns
458
478
  * @private
@@ -460,7 +480,7 @@ class ASTExtractor extends EventEmitter {
460
480
  extractPython(content, lines, symbols, imports, exports, structure) {
461
481
  const patterns = PATTERNS.python;
462
482
  let match;
463
-
483
+
464
484
  // Collect decorators for association
465
485
  const decorators = [];
466
486
  const decoratorPattern = new RegExp(patterns.decorator.source, 'gm');
@@ -468,10 +488,10 @@ class ASTExtractor extends EventEmitter {
468
488
  const line = this.getLineNumber(content, match.index);
469
489
  decorators.push({ name: match[1], line });
470
490
  }
471
-
491
+
472
492
  // Extract docstrings
473
493
  const docstrings = this.extractDocstrings(content, 'python');
474
-
494
+
475
495
  // Functions
476
496
  const funcPattern = new RegExp(patterns.functionDef.source, 'gm');
477
497
  while ((match = funcPattern.exec(content)) !== null) {
@@ -480,10 +500,8 @@ class ASTExtractor extends EventEmitter {
480
500
  const params = this.parseParams(match[2]);
481
501
  const returnType = match[3] || null;
482
502
  const doc = this.findNearestDocstring(docstrings, line);
483
- const funcDecorators = decorators
484
- .filter(d => d.line === line - 1)
485
- .map(d => d.name);
486
-
503
+ const funcDecorators = decorators.filter(d => d.line === line - 1).map(d => d.name);
504
+
487
505
  const symbol = {
488
506
  name: match[1],
489
507
  type: 'function',
@@ -493,28 +511,26 @@ class ASTExtractor extends EventEmitter {
493
511
  isAsync,
494
512
  docstring: doc,
495
513
  decorators: funcDecorators,
496
- visibility: match[1].startsWith('_') ? 'private' : 'public'
514
+ visibility: match[1].startsWith('_') ? 'private' : 'public',
497
515
  };
498
-
516
+
499
517
  symbols.push(symbol);
500
518
  structure.functions.push(symbol.name);
501
-
519
+
502
520
  // Python uses __all__ for explicit exports, but we mark non-underscore as potentially exported
503
521
  if (!match[1].startsWith('_')) {
504
522
  exports.push(match[1]);
505
523
  }
506
524
  }
507
-
525
+
508
526
  // Classes
509
527
  const classPattern = new RegExp(patterns.classDef.source, 'gm');
510
528
  while ((match = classPattern.exec(content)) !== null) {
511
529
  const line = this.getLineNumber(content, match.index);
512
530
  const doc = this.findNearestDocstring(docstrings, line);
513
531
  const baseClasses = match[2] ? match[2].split(',').map(s => s.trim()) : [];
514
- const classDecorators = decorators
515
- .filter(d => d.line === line - 1)
516
- .map(d => d.name);
517
-
532
+ const classDecorators = decorators.filter(d => d.line === line - 1).map(d => d.name);
533
+
518
534
  const classSymbol = {
519
535
  name: match[1],
520
536
  type: 'class',
@@ -523,40 +539,40 @@ class ASTExtractor extends EventEmitter {
523
539
  docstring: doc,
524
540
  decorators: classDecorators,
525
541
  methods: [],
526
- visibility: match[1].startsWith('_') ? 'private' : 'public'
542
+ visibility: match[1].startsWith('_') ? 'private' : 'public',
527
543
  };
528
-
544
+
529
545
  // Extract class methods
530
546
  const classEndIndex = this.findPythonClassEnd(content, lines, match.index);
531
547
  const classContent = content.slice(match.index, classEndIndex);
532
548
  const methodPattern = new RegExp(patterns.methodDef.source, 'gm');
533
549
  let methodMatch;
534
-
550
+
535
551
  while ((methodMatch = methodPattern.exec(classContent)) !== null) {
536
552
  const methodName = methodMatch[1];
537
553
  classSymbol.methods.push(methodName);
538
-
554
+
539
555
  symbols.push({
540
556
  name: `${match[1]}.${methodName}`,
541
557
  type: 'method',
542
558
  line: line + this.getLineNumber(classContent, methodMatch.index) - 1,
543
559
  parentClass: match[1],
544
- visibility: methodName.startsWith('_') ? 'private' : 'public'
560
+ visibility: methodName.startsWith('_') ? 'private' : 'public',
545
561
  });
546
562
  }
547
-
563
+
548
564
  symbols.push(classSymbol);
549
565
  structure.classes.push({
550
566
  name: classSymbol.name,
551
567
  extends: classSymbol.extends,
552
- methods: classSymbol.methods
568
+ methods: classSymbol.methods,
553
569
  });
554
-
570
+
555
571
  if (!match[1].startsWith('_')) {
556
572
  exports.push(match[1]);
557
573
  }
558
574
  }
559
-
575
+
560
576
  // Imports
561
577
  const importFromPattern = new RegExp(patterns.importFrom.source, 'gm');
562
578
  while ((match = importFromPattern.exec(content)) !== null) {
@@ -566,20 +582,20 @@ class ASTExtractor extends EventEmitter {
566
582
  const parts = n.trim().split(' as ');
567
583
  return parts[0].trim();
568
584
  });
569
-
585
+
570
586
  imports.push({ source, names, isDefault: false, isNamespace: false, line });
571
587
  }
572
-
588
+
573
589
  const importModulePattern = new RegExp(patterns.importModule.source, 'gm');
574
590
  while ((match = importModulePattern.exec(content)) !== null) {
575
591
  const line = this.getLineNumber(content, match.index);
576
592
  const source = match[1];
577
593
  const alias = match[2] || match[1];
578
-
594
+
579
595
  imports.push({ source, names: [alias], isDefault: true, isNamespace: false, line });
580
596
  }
581
597
  }
582
-
598
+
583
599
  /**
584
600
  * Extract docstrings from content
585
601
  * @private
@@ -587,15 +603,13 @@ class ASTExtractor extends EventEmitter {
587
603
  extractDocstrings(content, language) {
588
604
  const docstrings = [];
589
605
  let match;
590
-
606
+
591
607
  if (language === 'javascript' || language === 'typescript') {
592
608
  const pattern = PATTERNS.javascript.jsdoc;
593
609
  const jsdocPattern = new RegExp(pattern.source, 'gm');
594
610
  while ((match = jsdocPattern.exec(content)) !== null) {
595
611
  const line = this.getLineNumber(content, match.index);
596
- const text = match[1]
597
- .replace(/^\s*\*\s?/gm, '')
598
- .trim();
612
+ const text = match[1].replace(/^\s*\*\s?/gm, '').trim();
599
613
  docstrings.push({ line, text });
600
614
  }
601
615
  } else if (language === 'python') {
@@ -607,25 +621,23 @@ class ASTExtractor extends EventEmitter {
607
621
  docstrings.push({ line, text });
608
622
  }
609
623
  }
610
-
624
+
611
625
  return docstrings;
612
626
  }
613
-
627
+
614
628
  /**
615
629
  * Find nearest docstring before a line
616
630
  * @private
617
631
  */
618
632
  findNearestDocstring(docstrings, line) {
619
633
  if (!this.includeDocstrings) return null;
620
-
634
+
621
635
  // Look for docstring within 10 lines before or 2 lines after
622
636
  // JSDoc/docstrings can span multiple lines, so we need a wider range
623
- const candidates = docstrings.filter(d =>
624
- d.line >= line - 10 && d.line <= line + 2
625
- );
626
-
637
+ const candidates = docstrings.filter(d => d.line >= line - 10 && d.line <= line + 2);
638
+
627
639
  if (candidates.length === 0) return null;
628
-
640
+
629
641
  // Return the closest one that comes before the target line
630
642
  const before = candidates.filter(d => d.line <= line);
631
643
  if (before.length > 0) {
@@ -633,12 +645,12 @@ class ASTExtractor extends EventEmitter {
633
645
  before.sort((a, b) => b.line - a.line);
634
646
  return before[0].text;
635
647
  }
636
-
648
+
637
649
  // Fall back to any candidate
638
650
  candidates.sort((a, b) => Math.abs(a.line - line) - Math.abs(b.line - line));
639
651
  return candidates[0].text;
640
652
  }
641
-
653
+
642
654
  /**
643
655
  * Get line number from character index
644
656
  * @private
@@ -646,14 +658,14 @@ class ASTExtractor extends EventEmitter {
646
658
  getLineNumber(content, index) {
647
659
  return content.slice(0, index).split('\n').length;
648
660
  }
649
-
661
+
650
662
  /**
651
663
  * Parse function parameters
652
664
  * @private
653
665
  */
654
666
  parseParams(paramsStr) {
655
667
  if (!paramsStr || !paramsStr.trim()) return [];
656
-
668
+
657
669
  return paramsStr
658
670
  .split(',')
659
671
  .map(p => p.trim())
@@ -664,7 +676,7 @@ class ASTExtractor extends EventEmitter {
664
676
  return name;
665
677
  });
666
678
  }
667
-
679
+
668
680
  /**
669
681
  * Find end of JavaScript class
670
682
  * @private
@@ -672,7 +684,7 @@ class ASTExtractor extends EventEmitter {
672
684
  findClassEnd(content, startIndex) {
673
685
  let depth = 0;
674
686
  let inClass = false;
675
-
687
+
676
688
  for (let i = startIndex; i < content.length; i++) {
677
689
  if (content[i] === '{') {
678
690
  depth++;
@@ -684,10 +696,10 @@ class ASTExtractor extends EventEmitter {
684
696
  }
685
697
  }
686
698
  }
687
-
699
+
688
700
  return content.length;
689
701
  }
690
-
702
+
691
703
  /**
692
704
  * Find end of Python class (by indentation)
693
705
  * @private
@@ -696,11 +708,11 @@ class ASTExtractor extends EventEmitter {
696
708
  const startLine = this.getLineNumber(content, startIndex);
697
709
  const classLine = lines[startLine - 1];
698
710
  const classIndent = classLine.match(/^(\s*)/)[1].length;
699
-
711
+
700
712
  for (let i = startLine; i < lines.length; i++) {
701
713
  const line = lines[i];
702
714
  if (line.trim() === '') continue;
703
-
715
+
704
716
  const indent = line.match(/^(\s*)/)[1].length;
705
717
  if (indent <= classIndent && i > startLine) {
706
718
  // Found line with same or less indentation
@@ -711,10 +723,10 @@ class ASTExtractor extends EventEmitter {
711
723
  return charIndex;
712
724
  }
713
725
  }
714
-
726
+
715
727
  return content.length;
716
728
  }
717
-
729
+
718
730
  /**
719
731
  * Detect language from file path
720
732
  * @param {string} filePath - File path
@@ -729,11 +741,11 @@ class ASTExtractor extends EventEmitter {
729
741
  '.jsx': 'javascript',
730
742
  '.ts': 'typescript',
731
743
  '.tsx': 'typescript',
732
- '.py': 'python'
744
+ '.py': 'python',
733
745
  };
734
746
  return langMap[ext] || 'unknown';
735
747
  }
736
-
748
+
737
749
  /**
738
750
  * Generate symbol summary for LLM context
739
751
  * @param {FileAST} ast - Parsed AST
@@ -744,7 +756,7 @@ class ASTExtractor extends EventEmitter {
744
756
  summary += `Language: ${ast.language}\n`;
745
757
  summary += `Lines: ${ast.metadata.lineCount}\n`;
746
758
  summary += `Symbols: ${ast.metadata.symbolCount}\n\n`;
747
-
759
+
748
760
  // Imports
749
761
  if (ast.imports.length > 0) {
750
762
  summary += `## Dependencies\n\n`;
@@ -753,7 +765,7 @@ class ASTExtractor extends EventEmitter {
753
765
  }
754
766
  summary += '\n';
755
767
  }
756
-
768
+
757
769
  // Classes
758
770
  const classes = ast.symbols.filter(s => s.type === 'class');
759
771
  if (classes.length > 0) {
@@ -764,18 +776,18 @@ class ASTExtractor extends EventEmitter {
764
776
  summary += ` extends ${Array.isArray(cls.extends) ? cls.extends.join(', ') : cls.extends}`;
765
777
  }
766
778
  summary += '\n';
767
-
779
+
768
780
  if (cls.docstring) {
769
781
  summary += `${cls.docstring}\n`;
770
782
  }
771
-
783
+
772
784
  if (cls.methods && cls.methods.length > 0) {
773
785
  summary += `Methods: ${cls.methods.join(', ')}\n`;
774
786
  }
775
787
  summary += '\n';
776
788
  }
777
789
  }
778
-
790
+
779
791
  // Functions
780
792
  const functions = ast.symbols.filter(s => s.type === 'function');
781
793
  if (functions.length > 0) {
@@ -785,23 +797,23 @@ class ASTExtractor extends EventEmitter {
785
797
  if (func.isAsync) summary += ' (async)';
786
798
  if (func.isExported) summary += ' [exported]';
787
799
  summary += '\n';
788
-
800
+
789
801
  if (func.docstring) {
790
802
  summary += ` ${func.docstring.split('\n')[0]}\n`;
791
803
  }
792
804
  }
793
805
  summary += '\n';
794
806
  }
795
-
807
+
796
808
  // Exports
797
809
  if (ast.exports.length > 0) {
798
810
  summary += `## Exports\n\n`;
799
811
  summary += ast.exports.join(', ') + '\n';
800
812
  }
801
-
813
+
802
814
  return summary;
803
815
  }
804
-
816
+
805
817
  /**
806
818
  * Get cache key
807
819
  * @param {string} filePath - File path
@@ -811,7 +823,7 @@ class ASTExtractor extends EventEmitter {
811
823
  getCacheKey(filePath, mtime) {
812
824
  return `ast:${filePath}:${mtime}`;
813
825
  }
814
-
826
+
815
827
  /**
816
828
  * Get from cache
817
829
  * @param {string} filePath - File path
@@ -822,7 +834,7 @@ class ASTExtractor extends EventEmitter {
822
834
  const key = this.getCacheKey(filePath, mtime);
823
835
  return this.cache.get(key) || null;
824
836
  }
825
-
837
+
826
838
  /**
827
839
  * Add to cache
828
840
  * @param {string} filePath - File path
@@ -833,7 +845,7 @@ class ASTExtractor extends EventEmitter {
833
845
  const key = this.getCacheKey(filePath, mtime);
834
846
  this.cache.set(key, ast);
835
847
  }
836
-
848
+
837
849
  /**
838
850
  * Clear cache
839
851
  */
@@ -866,5 +878,5 @@ module.exports = {
866
878
  ASTExtractor,
867
879
  createASTExtractor,
868
880
  extractAST,
869
- PATTERNS
881
+ PATTERNS,
870
882
  };