musubi-sdd 5.0.0 → 5.6.1

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 +164 -145
  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 +247 -125
  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 +83 -80
  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 +53 -44
  87. package/src/monitoring/incident-manager.js +123 -103
  88. package/src/monitoring/index.js +144 -134
  89. package/src/monitoring/observability.js +82 -59
  90. package/src/monitoring/quality-dashboard.js +51 -39
  91. package/src/monitoring/release-manager.js +70 -50
  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,11 +1,18 @@
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
- *
7
- * Part of MUSUBI v4.1.0 - Codebase Intelligence
6
+ *
7
+ * Part of MUSUBI v5.0.0 - Codebase Intelligence
8
+ *
9
+ * @module analyzers/ast-extractor
8
10
  * @version 1.0.0
11
+ *
12
+ * @traceability
13
+ * - Requirement: REQ-P4-002 (AST Extraction and Analysis)
14
+ * - Design: docs/design/tdd-musubi-v5.0.0.md#2.2
15
+ * - Test: tests/analyzers/ast-extractor.test.js
9
16
  */
10
17
 
11
18
  const fs = require('fs');
@@ -53,43 +60,53 @@ const { EventEmitter } = require('events');
53
60
  const PATTERNS = {
54
61
  javascript: {
55
62
  // Function declarations (removed ^ to match anywhere in line)
56
- 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,
57
65
  // Arrow functions with const/let/var
58
- 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,
59
68
  // Class declarations
60
- 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,
61
71
  // Class methods
62
- 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,
63
74
  // Variable declarations
64
75
  constDecl: /(?:export\s+)?(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=/g,
65
76
  // ES6 imports
66
- 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,
67
79
  // CommonJS require
68
- 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,
69
82
  // Exports - capture function/class/const names after export
70
- 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,
71
85
  namedExport: /export\s*\{([^}]+)\}/gm,
72
86
  // JSDoc comments
73
87
  jsdoc: /\/\*\*\s*([\s\S]*?)\s*\*\//g,
74
88
  // Single-line comments
75
- comment: /\/\/\s*(.+)$/g
89
+ comment: /\/\/\s*(.+)$/g,
76
90
  },
77
-
91
+
78
92
  typescript: {
79
93
  // Extends JavaScript patterns
80
94
  // Interface declarations
81
- 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,
82
97
  // Type aliases
83
98
  typeDecl: /(?:^|\n)\s*(?:export\s+)?type\s+([a-zA-Z_$][a-zA-Z0-9_$]*)(?:<[^>]+>)?\s*=/g,
84
99
  // Enum declarations
85
100
  enumDecl: /(?:^|\n)\s*(?:export\s+)?(?:const\s+)?enum\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g,
86
101
  // Function with types
87
- 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,
88
104
  },
89
-
105
+
90
106
  python: {
91
107
  // Function definitions
92
- 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,
93
110
  // Class definitions
94
111
  classDef: /(?:^|\n)class\s+([a-zA-Z_][a-zA-Z0-9_]*)(?:\s*\(([^)]*)\))?/g,
95
112
  // Method definitions (inside class)
@@ -102,8 +119,8 @@ const PATTERNS = {
102
119
  // Docstrings
103
120
  docstring: /"""([\s\S]*?)"""|'''([\s\S]*?)'''/g,
104
121
  // Type hints
105
- typeHint: /:\s*([a-zA-Z_][a-zA-Z0-9_\[\],\s]*)/g
106
- }
122
+ typeHint: /:\s*([a-zA-Z_][a-zA-Z0-9_[\],\s]*)/g,
123
+ },
107
124
  };
108
125
 
109
126
  /**
@@ -123,11 +140,11 @@ class ASTExtractor extends EventEmitter {
123
140
  this.supportedLanguages = options.supportedLanguages || ['javascript', 'typescript', 'python'];
124
141
  this.includeDocstrings = options.includeDocstrings ?? true;
125
142
  this.extractComments = options.extractComments ?? false;
126
-
143
+
127
144
  // Results cache
128
145
  this.cache = new Map();
129
146
  }
130
-
147
+
131
148
  /**
132
149
  * Extract AST from file
133
150
  * @param {string} filePath - File path
@@ -136,7 +153,7 @@ class ASTExtractor extends EventEmitter {
136
153
  async extractFromFile(filePath) {
137
154
  const content = await fs.promises.readFile(filePath, 'utf-8');
138
155
  const language = this.detectLanguage(filePath);
139
-
156
+
140
157
  if (!this.supportedLanguages.includes(language)) {
141
158
  return {
142
159
  path: filePath,
@@ -145,13 +162,13 @@ class ASTExtractor extends EventEmitter {
145
162
  imports: [],
146
163
  exports: [],
147
164
  structure: {},
148
- metadata: { supported: false }
165
+ metadata: { supported: false },
149
166
  };
150
167
  }
151
-
168
+
152
169
  return this.extract(content, language, filePath);
153
170
  }
154
-
171
+
155
172
  /**
156
173
  * Extract AST from content
157
174
  * @param {string} content - Source code content
@@ -161,7 +178,7 @@ class ASTExtractor extends EventEmitter {
161
178
  */
162
179
  extract(content, language, filePath = '<source>') {
163
180
  this.emit('extract:start', { filePath, language });
164
-
181
+
165
182
  const lines = content.split('\n');
166
183
  const symbols = [];
167
184
  const imports = [];
@@ -169,9 +186,9 @@ class ASTExtractor extends EventEmitter {
169
186
  const structure = {
170
187
  classes: [],
171
188
  functions: [],
172
- variables: []
189
+ variables: [],
173
190
  };
174
-
191
+
175
192
  try {
176
193
  switch (language) {
177
194
  case 'javascript':
@@ -185,7 +202,7 @@ class ASTExtractor extends EventEmitter {
185
202
  this.extractPython(content, lines, symbols, imports, exports, structure);
186
203
  break;
187
204
  }
188
-
205
+
189
206
  const result = {
190
207
  path: filePath,
191
208
  language,
@@ -196,13 +213,12 @@ class ASTExtractor extends EventEmitter {
196
213
  metadata: {
197
214
  lineCount: lines.length,
198
215
  symbolCount: symbols.length,
199
- extractedAt: new Date().toISOString()
200
- }
216
+ extractedAt: new Date().toISOString(),
217
+ },
201
218
  };
202
-
219
+
203
220
  this.emit('extract:complete', result);
204
221
  return result;
205
-
206
222
  } catch (error) {
207
223
  this.emit('extract:error', { filePath, error });
208
224
  return {
@@ -212,21 +228,21 @@ class ASTExtractor extends EventEmitter {
212
228
  imports: [],
213
229
  exports: [],
214
230
  structure: {},
215
- metadata: { error: error.message }
231
+ metadata: { error: error.message },
216
232
  };
217
233
  }
218
234
  }
219
-
235
+
220
236
  /**
221
237
  * Extract JavaScript/TypeScript patterns
222
238
  * @private
223
239
  */
224
240
  extractJavaScript(content, lines, symbols, imports, exports, structure) {
225
241
  const patterns = PATTERNS.javascript;
226
-
242
+
227
243
  // Extract docstrings for association
228
244
  const docstrings = this.extractDocstrings(content, 'javascript');
229
-
245
+
230
246
  // Functions
231
247
  let match;
232
248
  const funcPattern = new RegExp(patterns.functionDecl.source, 'g');
@@ -236,7 +252,7 @@ class ASTExtractor extends EventEmitter {
236
252
  const isAsync = match[0].includes('async');
237
253
  const params = this.parseParams(match[2]);
238
254
  const doc = this.findNearestDocstring(docstrings, line);
239
-
255
+
240
256
  const symbol = {
241
257
  name: match[1],
242
258
  type: 'function',
@@ -245,17 +261,17 @@ class ASTExtractor extends EventEmitter {
245
261
  params,
246
262
  isExported,
247
263
  isAsync,
248
- docstring: doc
264
+ docstring: doc,
249
265
  };
250
-
266
+
251
267
  symbols.push(symbol);
252
268
  structure.functions.push(symbol.name);
253
-
269
+
254
270
  if (isExported) {
255
271
  exports.push(match[1]);
256
272
  }
257
273
  }
258
-
274
+
259
275
  // Arrow functions
260
276
  const arrowPattern = new RegExp(patterns.arrowFunc.source, 'g');
261
277
  while ((match = arrowPattern.exec(content)) !== null) {
@@ -263,31 +279,31 @@ class ASTExtractor extends EventEmitter {
263
279
  const isExported = match[0].includes('export ');
264
280
  const isAsync = match[0].includes('async');
265
281
  const doc = this.findNearestDocstring(docstrings, line);
266
-
282
+
267
283
  const symbol = {
268
284
  name: match[1],
269
285
  type: 'function',
270
286
  line,
271
287
  isExported,
272
288
  isAsync,
273
- docstring: doc
289
+ docstring: doc,
274
290
  };
275
-
291
+
276
292
  symbols.push(symbol);
277
293
  structure.functions.push(symbol.name);
278
-
294
+
279
295
  if (isExported) {
280
296
  exports.push(match[1]);
281
297
  }
282
298
  }
283
-
299
+
284
300
  // Classes
285
301
  const classPattern = new RegExp(patterns.classDecl.source, 'g');
286
302
  while ((match = classPattern.exec(content)) !== null) {
287
303
  const line = this.getLineNumber(content, match.index);
288
304
  const isExported = match[0].includes('export ');
289
305
  const doc = this.findNearestDocstring(docstrings, line);
290
-
306
+
291
307
  const classSymbol = {
292
308
  name: match[1],
293
309
  type: 'class',
@@ -295,38 +311,38 @@ class ASTExtractor extends EventEmitter {
295
311
  isExported,
296
312
  extends: match[2] || null,
297
313
  docstring: doc,
298
- methods: []
314
+ methods: [],
299
315
  };
300
-
316
+
301
317
  // Extract class methods
302
318
  const classEndIndex = this.findClassEnd(content, match.index);
303
319
  const classContent = content.slice(match.index, classEndIndex);
304
320
  const methodPattern = new RegExp(patterns.methodDecl.source, 'gm');
305
321
  let methodMatch;
306
-
322
+
307
323
  while ((methodMatch = methodPattern.exec(classContent)) !== null) {
308
324
  classSymbol.methods.push(methodMatch[1]);
309
-
325
+
310
326
  symbols.push({
311
327
  name: `${match[1]}.${methodMatch[1]}`,
312
328
  type: 'method',
313
329
  line: line + this.getLineNumber(classContent, methodMatch.index) - 1,
314
- parentClass: match[1]
330
+ parentClass: match[1],
315
331
  });
316
332
  }
317
-
333
+
318
334
  symbols.push(classSymbol);
319
335
  structure.classes.push({
320
336
  name: classSymbol.name,
321
337
  extends: classSymbol.extends,
322
- methods: classSymbol.methods
338
+ methods: classSymbol.methods,
323
339
  });
324
-
340
+
325
341
  if (isExported) {
326
342
  exports.push(match[1]);
327
343
  }
328
344
  }
329
-
345
+
330
346
  // Imports (ES6)
331
347
  const importPattern = new RegExp(patterns.importStmt.source, 'gm');
332
348
  while ((match = importPattern.exec(content)) !== null) {
@@ -335,10 +351,13 @@ class ASTExtractor extends EventEmitter {
335
351
  let names = [];
336
352
  let isDefault = false;
337
353
  let isNamespace = false;
338
-
354
+
339
355
  if (match[1]) {
340
356
  // Named imports { a, b }
341
- names = match[1].replace(/[{}]/g, '').split(',').map(n => n.trim());
357
+ names = match[1]
358
+ .replace(/[{}]/g, '')
359
+ .split(',')
360
+ .map(n => n.trim());
342
361
  }
343
362
  if (match[2]) {
344
363
  // Default import
@@ -347,43 +366,51 @@ class ASTExtractor extends EventEmitter {
347
366
  }
348
367
  if (match[3]) {
349
368
  // Additional named imports after default
350
- 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
+ );
351
375
  }
352
376
  if (match[4]) {
353
377
  // Namespace import * as X
354
378
  names.push(match[4]);
355
379
  isNamespace = true;
356
380
  }
357
-
381
+
358
382
  imports.push({ source, names, isDefault, isNamespace, line });
359
383
  }
360
-
384
+
361
385
  // CommonJS require
362
386
  const requirePattern = new RegExp(patterns.requireStmt.source, 'gm');
363
387
  while ((match = requirePattern.exec(content)) !== null) {
364
388
  const line = this.getLineNumber(content, match.index);
365
389
  const source = match[3];
366
390
  let names = [];
367
-
391
+
368
392
  if (match[1]) {
369
393
  // Destructured require
370
- names = match[1].replace(/[{}]/g, '').split(',').map(n => n.trim());
394
+ names = match[1]
395
+ .replace(/[{}]/g, '')
396
+ .split(',')
397
+ .map(n => n.trim());
371
398
  }
372
399
  if (match[2]) {
373
400
  // Simple require
374
401
  names.push(match[2]);
375
402
  }
376
-
403
+
377
404
  imports.push({ source, names, isDefault: !!match[2], isNamespace: false, line });
378
405
  }
379
-
406
+
380
407
  // Named exports: export { a, b }
381
408
  const namedExportPattern = new RegExp(patterns.namedExport.source, 'gm');
382
409
  while ((match = namedExportPattern.exec(content)) !== null) {
383
410
  const names = match[1].split(',').map(n => n.trim().split(' as ')[0].trim());
384
411
  exports.push(...names);
385
412
  }
386
-
413
+
387
414
  // Direct exports: export const/let/var/function/class name
388
415
  const exportStmtPattern = new RegExp(patterns.exportStmt.source, 'gm');
389
416
  while ((match = exportStmtPattern.exec(content)) !== null) {
@@ -393,59 +420,59 @@ class ASTExtractor extends EventEmitter {
393
420
  }
394
421
  }
395
422
  }
396
-
423
+
397
424
  /**
398
425
  * Extract TypeScript-specific patterns
399
426
  * @private
400
427
  */
401
- extractTypeScript(content, lines, symbols, structure) {
428
+ extractTypeScript(content, lines, symbols, _structure) {
402
429
  const patterns = PATTERNS.typescript;
403
430
  let match;
404
-
431
+
405
432
  // Interfaces
406
433
  const interfacePattern = new RegExp(patterns.interfaceDecl.source, 'gm');
407
434
  while ((match = interfacePattern.exec(content)) !== null) {
408
435
  const line = this.getLineNumber(content, match.index);
409
436
  const isExported = match[0].includes('export');
410
-
437
+
411
438
  symbols.push({
412
439
  name: match[1],
413
440
  type: 'interface',
414
441
  line,
415
442
  isExported,
416
- extends: match[2] ? match[2].split(',').map(s => s.trim()) : []
443
+ extends: match[2] ? match[2].split(',').map(s => s.trim()) : [],
417
444
  });
418
445
  }
419
-
446
+
420
447
  // Type aliases
421
448
  const typePattern = new RegExp(patterns.typeDecl.source, 'gm');
422
449
  while ((match = typePattern.exec(content)) !== null) {
423
450
  const line = this.getLineNumber(content, match.index);
424
451
  const isExported = match[0].includes('export');
425
-
452
+
426
453
  symbols.push({
427
454
  name: match[1],
428
455
  type: 'type',
429
456
  line,
430
- isExported
457
+ isExported,
431
458
  });
432
459
  }
433
-
460
+
434
461
  // Enums
435
462
  const enumPattern = new RegExp(patterns.enumDecl.source, 'gm');
436
463
  while ((match = enumPattern.exec(content)) !== null) {
437
464
  const line = this.getLineNumber(content, match.index);
438
465
  const isExported = match[0].includes('export');
439
-
466
+
440
467
  symbols.push({
441
468
  name: match[1],
442
469
  type: 'enum',
443
470
  line,
444
- isExported
471
+ isExported,
445
472
  });
446
473
  }
447
474
  }
448
-
475
+
449
476
  /**
450
477
  * Extract Python patterns
451
478
  * @private
@@ -453,7 +480,7 @@ class ASTExtractor extends EventEmitter {
453
480
  extractPython(content, lines, symbols, imports, exports, structure) {
454
481
  const patterns = PATTERNS.python;
455
482
  let match;
456
-
483
+
457
484
  // Collect decorators for association
458
485
  const decorators = [];
459
486
  const decoratorPattern = new RegExp(patterns.decorator.source, 'gm');
@@ -461,10 +488,10 @@ class ASTExtractor extends EventEmitter {
461
488
  const line = this.getLineNumber(content, match.index);
462
489
  decorators.push({ name: match[1], line });
463
490
  }
464
-
491
+
465
492
  // Extract docstrings
466
493
  const docstrings = this.extractDocstrings(content, 'python');
467
-
494
+
468
495
  // Functions
469
496
  const funcPattern = new RegExp(patterns.functionDef.source, 'gm');
470
497
  while ((match = funcPattern.exec(content)) !== null) {
@@ -473,10 +500,8 @@ class ASTExtractor extends EventEmitter {
473
500
  const params = this.parseParams(match[2]);
474
501
  const returnType = match[3] || null;
475
502
  const doc = this.findNearestDocstring(docstrings, line);
476
- const funcDecorators = decorators
477
- .filter(d => d.line === line - 1)
478
- .map(d => d.name);
479
-
503
+ const funcDecorators = decorators.filter(d => d.line === line - 1).map(d => d.name);
504
+
480
505
  const symbol = {
481
506
  name: match[1],
482
507
  type: 'function',
@@ -486,28 +511,26 @@ class ASTExtractor extends EventEmitter {
486
511
  isAsync,
487
512
  docstring: doc,
488
513
  decorators: funcDecorators,
489
- visibility: match[1].startsWith('_') ? 'private' : 'public'
514
+ visibility: match[1].startsWith('_') ? 'private' : 'public',
490
515
  };
491
-
516
+
492
517
  symbols.push(symbol);
493
518
  structure.functions.push(symbol.name);
494
-
519
+
495
520
  // Python uses __all__ for explicit exports, but we mark non-underscore as potentially exported
496
521
  if (!match[1].startsWith('_')) {
497
522
  exports.push(match[1]);
498
523
  }
499
524
  }
500
-
525
+
501
526
  // Classes
502
527
  const classPattern = new RegExp(patterns.classDef.source, 'gm');
503
528
  while ((match = classPattern.exec(content)) !== null) {
504
529
  const line = this.getLineNumber(content, match.index);
505
530
  const doc = this.findNearestDocstring(docstrings, line);
506
531
  const baseClasses = match[2] ? match[2].split(',').map(s => s.trim()) : [];
507
- const classDecorators = decorators
508
- .filter(d => d.line === line - 1)
509
- .map(d => d.name);
510
-
532
+ const classDecorators = decorators.filter(d => d.line === line - 1).map(d => d.name);
533
+
511
534
  const classSymbol = {
512
535
  name: match[1],
513
536
  type: 'class',
@@ -516,40 +539,40 @@ class ASTExtractor extends EventEmitter {
516
539
  docstring: doc,
517
540
  decorators: classDecorators,
518
541
  methods: [],
519
- visibility: match[1].startsWith('_') ? 'private' : 'public'
542
+ visibility: match[1].startsWith('_') ? 'private' : 'public',
520
543
  };
521
-
544
+
522
545
  // Extract class methods
523
546
  const classEndIndex = this.findPythonClassEnd(content, lines, match.index);
524
547
  const classContent = content.slice(match.index, classEndIndex);
525
548
  const methodPattern = new RegExp(patterns.methodDef.source, 'gm');
526
549
  let methodMatch;
527
-
550
+
528
551
  while ((methodMatch = methodPattern.exec(classContent)) !== null) {
529
552
  const methodName = methodMatch[1];
530
553
  classSymbol.methods.push(methodName);
531
-
554
+
532
555
  symbols.push({
533
556
  name: `${match[1]}.${methodName}`,
534
557
  type: 'method',
535
558
  line: line + this.getLineNumber(classContent, methodMatch.index) - 1,
536
559
  parentClass: match[1],
537
- visibility: methodName.startsWith('_') ? 'private' : 'public'
560
+ visibility: methodName.startsWith('_') ? 'private' : 'public',
538
561
  });
539
562
  }
540
-
563
+
541
564
  symbols.push(classSymbol);
542
565
  structure.classes.push({
543
566
  name: classSymbol.name,
544
567
  extends: classSymbol.extends,
545
- methods: classSymbol.methods
568
+ methods: classSymbol.methods,
546
569
  });
547
-
570
+
548
571
  if (!match[1].startsWith('_')) {
549
572
  exports.push(match[1]);
550
573
  }
551
574
  }
552
-
575
+
553
576
  // Imports
554
577
  const importFromPattern = new RegExp(patterns.importFrom.source, 'gm');
555
578
  while ((match = importFromPattern.exec(content)) !== null) {
@@ -559,20 +582,20 @@ class ASTExtractor extends EventEmitter {
559
582
  const parts = n.trim().split(' as ');
560
583
  return parts[0].trim();
561
584
  });
562
-
585
+
563
586
  imports.push({ source, names, isDefault: false, isNamespace: false, line });
564
587
  }
565
-
588
+
566
589
  const importModulePattern = new RegExp(patterns.importModule.source, 'gm');
567
590
  while ((match = importModulePattern.exec(content)) !== null) {
568
591
  const line = this.getLineNumber(content, match.index);
569
592
  const source = match[1];
570
593
  const alias = match[2] || match[1];
571
-
594
+
572
595
  imports.push({ source, names: [alias], isDefault: true, isNamespace: false, line });
573
596
  }
574
597
  }
575
-
598
+
576
599
  /**
577
600
  * Extract docstrings from content
578
601
  * @private
@@ -580,15 +603,13 @@ class ASTExtractor extends EventEmitter {
580
603
  extractDocstrings(content, language) {
581
604
  const docstrings = [];
582
605
  let match;
583
-
606
+
584
607
  if (language === 'javascript' || language === 'typescript') {
585
608
  const pattern = PATTERNS.javascript.jsdoc;
586
609
  const jsdocPattern = new RegExp(pattern.source, 'gm');
587
610
  while ((match = jsdocPattern.exec(content)) !== null) {
588
611
  const line = this.getLineNumber(content, match.index);
589
- const text = match[1]
590
- .replace(/^\s*\*\s?/gm, '')
591
- .trim();
612
+ const text = match[1].replace(/^\s*\*\s?/gm, '').trim();
592
613
  docstrings.push({ line, text });
593
614
  }
594
615
  } else if (language === 'python') {
@@ -600,25 +621,23 @@ class ASTExtractor extends EventEmitter {
600
621
  docstrings.push({ line, text });
601
622
  }
602
623
  }
603
-
624
+
604
625
  return docstrings;
605
626
  }
606
-
627
+
607
628
  /**
608
629
  * Find nearest docstring before a line
609
630
  * @private
610
631
  */
611
632
  findNearestDocstring(docstrings, line) {
612
633
  if (!this.includeDocstrings) return null;
613
-
634
+
614
635
  // Look for docstring within 10 lines before or 2 lines after
615
636
  // JSDoc/docstrings can span multiple lines, so we need a wider range
616
- const candidates = docstrings.filter(d =>
617
- d.line >= line - 10 && d.line <= line + 2
618
- );
619
-
637
+ const candidates = docstrings.filter(d => d.line >= line - 10 && d.line <= line + 2);
638
+
620
639
  if (candidates.length === 0) return null;
621
-
640
+
622
641
  // Return the closest one that comes before the target line
623
642
  const before = candidates.filter(d => d.line <= line);
624
643
  if (before.length > 0) {
@@ -626,12 +645,12 @@ class ASTExtractor extends EventEmitter {
626
645
  before.sort((a, b) => b.line - a.line);
627
646
  return before[0].text;
628
647
  }
629
-
648
+
630
649
  // Fall back to any candidate
631
650
  candidates.sort((a, b) => Math.abs(a.line - line) - Math.abs(b.line - line));
632
651
  return candidates[0].text;
633
652
  }
634
-
653
+
635
654
  /**
636
655
  * Get line number from character index
637
656
  * @private
@@ -639,14 +658,14 @@ class ASTExtractor extends EventEmitter {
639
658
  getLineNumber(content, index) {
640
659
  return content.slice(0, index).split('\n').length;
641
660
  }
642
-
661
+
643
662
  /**
644
663
  * Parse function parameters
645
664
  * @private
646
665
  */
647
666
  parseParams(paramsStr) {
648
667
  if (!paramsStr || !paramsStr.trim()) return [];
649
-
668
+
650
669
  return paramsStr
651
670
  .split(',')
652
671
  .map(p => p.trim())
@@ -657,7 +676,7 @@ class ASTExtractor extends EventEmitter {
657
676
  return name;
658
677
  });
659
678
  }
660
-
679
+
661
680
  /**
662
681
  * Find end of JavaScript class
663
682
  * @private
@@ -665,7 +684,7 @@ class ASTExtractor extends EventEmitter {
665
684
  findClassEnd(content, startIndex) {
666
685
  let depth = 0;
667
686
  let inClass = false;
668
-
687
+
669
688
  for (let i = startIndex; i < content.length; i++) {
670
689
  if (content[i] === '{') {
671
690
  depth++;
@@ -677,10 +696,10 @@ class ASTExtractor extends EventEmitter {
677
696
  }
678
697
  }
679
698
  }
680
-
699
+
681
700
  return content.length;
682
701
  }
683
-
702
+
684
703
  /**
685
704
  * Find end of Python class (by indentation)
686
705
  * @private
@@ -689,11 +708,11 @@ class ASTExtractor extends EventEmitter {
689
708
  const startLine = this.getLineNumber(content, startIndex);
690
709
  const classLine = lines[startLine - 1];
691
710
  const classIndent = classLine.match(/^(\s*)/)[1].length;
692
-
711
+
693
712
  for (let i = startLine; i < lines.length; i++) {
694
713
  const line = lines[i];
695
714
  if (line.trim() === '') continue;
696
-
715
+
697
716
  const indent = line.match(/^(\s*)/)[1].length;
698
717
  if (indent <= classIndent && i > startLine) {
699
718
  // Found line with same or less indentation
@@ -704,10 +723,10 @@ class ASTExtractor extends EventEmitter {
704
723
  return charIndex;
705
724
  }
706
725
  }
707
-
726
+
708
727
  return content.length;
709
728
  }
710
-
729
+
711
730
  /**
712
731
  * Detect language from file path
713
732
  * @param {string} filePath - File path
@@ -722,11 +741,11 @@ class ASTExtractor extends EventEmitter {
722
741
  '.jsx': 'javascript',
723
742
  '.ts': 'typescript',
724
743
  '.tsx': 'typescript',
725
- '.py': 'python'
744
+ '.py': 'python',
726
745
  };
727
746
  return langMap[ext] || 'unknown';
728
747
  }
729
-
748
+
730
749
  /**
731
750
  * Generate symbol summary for LLM context
732
751
  * @param {FileAST} ast - Parsed AST
@@ -737,7 +756,7 @@ class ASTExtractor extends EventEmitter {
737
756
  summary += `Language: ${ast.language}\n`;
738
757
  summary += `Lines: ${ast.metadata.lineCount}\n`;
739
758
  summary += `Symbols: ${ast.metadata.symbolCount}\n\n`;
740
-
759
+
741
760
  // Imports
742
761
  if (ast.imports.length > 0) {
743
762
  summary += `## Dependencies\n\n`;
@@ -746,7 +765,7 @@ class ASTExtractor extends EventEmitter {
746
765
  }
747
766
  summary += '\n';
748
767
  }
749
-
768
+
750
769
  // Classes
751
770
  const classes = ast.symbols.filter(s => s.type === 'class');
752
771
  if (classes.length > 0) {
@@ -757,18 +776,18 @@ class ASTExtractor extends EventEmitter {
757
776
  summary += ` extends ${Array.isArray(cls.extends) ? cls.extends.join(', ') : cls.extends}`;
758
777
  }
759
778
  summary += '\n';
760
-
779
+
761
780
  if (cls.docstring) {
762
781
  summary += `${cls.docstring}\n`;
763
782
  }
764
-
783
+
765
784
  if (cls.methods && cls.methods.length > 0) {
766
785
  summary += `Methods: ${cls.methods.join(', ')}\n`;
767
786
  }
768
787
  summary += '\n';
769
788
  }
770
789
  }
771
-
790
+
772
791
  // Functions
773
792
  const functions = ast.symbols.filter(s => s.type === 'function');
774
793
  if (functions.length > 0) {
@@ -778,23 +797,23 @@ class ASTExtractor extends EventEmitter {
778
797
  if (func.isAsync) summary += ' (async)';
779
798
  if (func.isExported) summary += ' [exported]';
780
799
  summary += '\n';
781
-
800
+
782
801
  if (func.docstring) {
783
802
  summary += ` ${func.docstring.split('\n')[0]}\n`;
784
803
  }
785
804
  }
786
805
  summary += '\n';
787
806
  }
788
-
807
+
789
808
  // Exports
790
809
  if (ast.exports.length > 0) {
791
810
  summary += `## Exports\n\n`;
792
811
  summary += ast.exports.join(', ') + '\n';
793
812
  }
794
-
813
+
795
814
  return summary;
796
815
  }
797
-
816
+
798
817
  /**
799
818
  * Get cache key
800
819
  * @param {string} filePath - File path
@@ -804,7 +823,7 @@ class ASTExtractor extends EventEmitter {
804
823
  getCacheKey(filePath, mtime) {
805
824
  return `ast:${filePath}:${mtime}`;
806
825
  }
807
-
826
+
808
827
  /**
809
828
  * Get from cache
810
829
  * @param {string} filePath - File path
@@ -815,7 +834,7 @@ class ASTExtractor extends EventEmitter {
815
834
  const key = this.getCacheKey(filePath, mtime);
816
835
  return this.cache.get(key) || null;
817
836
  }
818
-
837
+
819
838
  /**
820
839
  * Add to cache
821
840
  * @param {string} filePath - File path
@@ -826,7 +845,7 @@ class ASTExtractor extends EventEmitter {
826
845
  const key = this.getCacheKey(filePath, mtime);
827
846
  this.cache.set(key, ast);
828
847
  }
829
-
848
+
830
849
  /**
831
850
  * Clear cache
832
851
  */
@@ -859,5 +878,5 @@ module.exports = {
859
878
  ASTExtractor,
860
879
  createASTExtractor,
861
880
  extractAST,
862
- PATTERNS
881
+ PATTERNS,
863
882
  };