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.
- package/README.ja.md +106 -48
- package/README.md +110 -32
- package/bin/musubi-analyze.js +74 -67
- package/bin/musubi-browser.js +27 -26
- package/bin/musubi-change.js +48 -47
- package/bin/musubi-checkpoint.js +10 -7
- package/bin/musubi-convert.js +25 -25
- package/bin/musubi-costs.js +27 -10
- package/bin/musubi-gui.js +52 -46
- package/bin/musubi-init.js +1952 -10
- package/bin/musubi-orchestrate.js +327 -239
- package/bin/musubi-remember.js +69 -56
- package/bin/musubi-resolve.js +53 -45
- package/bin/musubi-trace.js +51 -22
- package/bin/musubi-validate.js +39 -30
- package/bin/musubi-workflow.js +33 -34
- package/bin/musubi.js +39 -2
- package/package.json +1 -1
- package/src/agents/agent-loop.js +94 -95
- package/src/agents/agentic/code-generator.js +119 -109
- package/src/agents/agentic/code-reviewer.js +105 -108
- package/src/agents/agentic/index.js +4 -4
- package/src/agents/browser/action-executor.js +13 -13
- package/src/agents/browser/ai-comparator.js +11 -10
- package/src/agents/browser/context-manager.js +6 -6
- package/src/agents/browser/index.js +5 -5
- package/src/agents/browser/nl-parser.js +31 -46
- package/src/agents/browser/screenshot.js +2 -2
- package/src/agents/browser/test-generator.js +6 -4
- package/src/agents/function-tool.js +71 -65
- package/src/agents/index.js +7 -7
- package/src/agents/schema-generator.js +98 -94
- package/src/analyzers/ast-extractor.js +158 -146
- package/src/analyzers/codegraph-auto-update.js +858 -0
- package/src/analyzers/complexity-analyzer.js +536 -0
- package/src/analyzers/context-optimizer.js +241 -126
- package/src/analyzers/impact-analyzer.js +1 -1
- package/src/analyzers/large-project-analyzer.js +766 -0
- package/src/analyzers/repository-map.js +77 -81
- package/src/analyzers/security-analyzer.js +19 -11
- package/src/analyzers/stuck-detector.js +19 -17
- package/src/converters/index.js +78 -57
- package/src/converters/ir/types.js +12 -12
- package/src/converters/parsers/musubi-parser.js +134 -126
- package/src/converters/parsers/openapi-parser.js +70 -53
- package/src/converters/parsers/speckit-parser.js +239 -175
- package/src/converters/writers/musubi-writer.js +123 -118
- package/src/converters/writers/speckit-writer.js +124 -113
- package/src/generators/rust-migration-generator.js +512 -0
- package/src/gui/public/index.html +1365 -1211
- package/src/gui/server.js +41 -40
- package/src/gui/services/file-watcher.js +23 -8
- package/src/gui/services/project-scanner.js +26 -20
- package/src/gui/services/replanning-service.js +27 -23
- package/src/gui/services/traceability-service.js +8 -8
- package/src/gui/services/workflow-service.js +14 -7
- package/src/index.js +151 -0
- package/src/integrations/cicd.js +90 -104
- package/src/integrations/codegraph-mcp.js +643 -0
- package/src/integrations/documentation.js +142 -103
- package/src/integrations/examples.js +95 -80
- package/src/integrations/github-client.js +17 -17
- package/src/integrations/index.js +5 -5
- package/src/integrations/mcp/index.js +21 -21
- package/src/integrations/mcp/mcp-context-provider.js +76 -78
- package/src/integrations/mcp/mcp-discovery.js +74 -72
- package/src/integrations/mcp/mcp-tool-registry.js +99 -94
- package/src/integrations/mcp-connector.js +70 -66
- package/src/integrations/platforms.js +50 -49
- package/src/integrations/tool-discovery.js +37 -31
- package/src/llm-providers/anthropic-provider.js +11 -11
- package/src/llm-providers/base-provider.js +16 -18
- package/src/llm-providers/copilot-provider.js +22 -19
- package/src/llm-providers/index.js +26 -25
- package/src/llm-providers/ollama-provider.js +11 -11
- package/src/llm-providers/openai-provider.js +12 -12
- package/src/managers/agent-memory.js +36 -24
- package/src/managers/checkpoint-manager.js +4 -8
- package/src/managers/delta-spec.js +19 -19
- package/src/managers/index.js +13 -4
- package/src/managers/memory-condenser.js +35 -45
- package/src/managers/repo-skill-manager.js +57 -31
- package/src/managers/skill-loader.js +25 -22
- package/src/managers/skill-tools.js +36 -72
- package/src/managers/workflow.js +30 -22
- package/src/monitoring/cost-tracker.js +48 -46
- package/src/monitoring/incident-manager.js +116 -106
- package/src/monitoring/index.js +144 -134
- package/src/monitoring/observability.js +75 -62
- package/src/monitoring/quality-dashboard.js +45 -41
- package/src/monitoring/release-manager.js +63 -53
- package/src/orchestration/agent-skill-binding.js +39 -47
- package/src/orchestration/error-handler.js +65 -107
- package/src/orchestration/guardrails/base-guardrail.js +26 -24
- package/src/orchestration/guardrails/guardrail-rules.js +50 -64
- package/src/orchestration/guardrails/index.js +5 -5
- package/src/orchestration/guardrails/input-guardrail.js +58 -45
- package/src/orchestration/guardrails/output-guardrail.js +104 -81
- package/src/orchestration/guardrails/safety-check.js +79 -79
- package/src/orchestration/index.js +38 -55
- package/src/orchestration/mcp-tool-adapters.js +96 -99
- package/src/orchestration/orchestration-engine.js +21 -21
- package/src/orchestration/pattern-registry.js +60 -45
- package/src/orchestration/patterns/auto.js +34 -47
- package/src/orchestration/patterns/group-chat.js +59 -65
- package/src/orchestration/patterns/handoff.js +67 -65
- package/src/orchestration/patterns/human-in-loop.js +51 -72
- package/src/orchestration/patterns/nested.js +25 -40
- package/src/orchestration/patterns/sequential.js +35 -34
- package/src/orchestration/patterns/swarm.js +63 -56
- package/src/orchestration/patterns/triage.js +150 -109
- package/src/orchestration/reasoning/index.js +9 -9
- package/src/orchestration/reasoning/planning-engine.js +143 -140
- package/src/orchestration/reasoning/reasoning-engine.js +206 -144
- package/src/orchestration/reasoning/self-correction.js +121 -128
- package/src/orchestration/replanning/adaptive-goal-modifier.js +107 -112
- package/src/orchestration/replanning/alternative-generator.js +37 -42
- package/src/orchestration/replanning/config.js +63 -59
- package/src/orchestration/replanning/goal-progress-tracker.js +98 -100
- package/src/orchestration/replanning/index.js +24 -20
- package/src/orchestration/replanning/plan-evaluator.js +49 -50
- package/src/orchestration/replanning/plan-monitor.js +32 -28
- package/src/orchestration/replanning/proactive-path-optimizer.js +175 -178
- package/src/orchestration/replanning/replan-history.js +33 -26
- package/src/orchestration/replanning/replanning-engine.js +106 -108
- package/src/orchestration/skill-executor.js +107 -109
- package/src/orchestration/skill-registry.js +85 -89
- package/src/orchestration/workflow-examples.js +228 -231
- package/src/orchestration/workflow-executor.js +65 -68
- package/src/orchestration/workflow-orchestrator.js +72 -73
- package/src/phase4-integration.js +47 -40
- package/src/phase5-integration.js +89 -30
- package/src/reporters/coverage-report.js +82 -30
- package/src/reporters/hierarchical-reporter.js +498 -0
- package/src/reporters/traceability-matrix-report.js +29 -20
- package/src/resolvers/issue-resolver.js +43 -31
- package/src/steering/advanced-validation.js +133 -124
- package/src/steering/auto-updater.js +60 -73
- package/src/steering/index.js +6 -6
- package/src/steering/quality-metrics.js +41 -35
- package/src/steering/steering-auto-update.js +83 -86
- package/src/steering/steering-validator.js +98 -106
- package/src/steering/template-constraints.js +53 -54
- package/src/templates/agents/claude-code/CLAUDE.md +32 -32
- package/src/templates/agents/claude-code/skills/agent-assistant/SKILL.md +13 -5
- package/src/templates/agents/claude-code/skills/ai-ml-engineer/mlops-guide.md +23 -23
- package/src/templates/agents/claude-code/skills/ai-ml-engineer/model-card-template.md +60 -41
- package/src/templates/agents/claude-code/skills/api-designer/api-patterns.md +27 -19
- package/src/templates/agents/claude-code/skills/api-designer/openapi-template.md +11 -7
- package/src/templates/agents/claude-code/skills/bug-hunter/SKILL.md +4 -3
- package/src/templates/agents/claude-code/skills/bug-hunter/root-cause-analysis.md +37 -15
- package/src/templates/agents/claude-code/skills/change-impact-analyzer/dependency-graph-patterns.md +36 -42
- package/src/templates/agents/claude-code/skills/change-impact-analyzer/impact-analysis-template.md +69 -60
- package/src/templates/agents/claude-code/skills/cloud-architect/aws-patterns.md +31 -38
- package/src/templates/agents/claude-code/skills/cloud-architect/azure-patterns.md +28 -23
- package/src/templates/agents/claude-code/skills/code-reviewer/SKILL.md +61 -0
- package/src/templates/agents/claude-code/skills/code-reviewer/best-practices.md +27 -0
- package/src/templates/agents/claude-code/skills/code-reviewer/review-checklist.md +29 -10
- package/src/templates/agents/claude-code/skills/code-reviewer/review-standards.md +29 -24
- package/src/templates/agents/claude-code/skills/constitution-enforcer/SKILL.md +8 -6
- package/src/templates/agents/claude-code/skills/constitution-enforcer/constitutional-articles.md +62 -26
- package/src/templates/agents/claude-code/skills/constitution-enforcer/phase-minus-one-gates.md +35 -16
- package/src/templates/agents/claude-code/skills/database-administrator/backup-recovery.md +27 -17
- package/src/templates/agents/claude-code/skills/database-administrator/tuning-guide.md +25 -20
- package/src/templates/agents/claude-code/skills/database-schema-designer/schema-patterns.md +39 -22
- package/src/templates/agents/claude-code/skills/devops-engineer/ci-cd-templates.md +25 -22
- package/src/templates/agents/claude-code/skills/issue-resolver/SKILL.md +24 -21
- package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +148 -63
- package/src/templates/agents/claude-code/skills/orchestrator/patterns.md +35 -16
- package/src/templates/agents/claude-code/skills/orchestrator/selection-matrix.md +69 -64
- package/src/templates/agents/claude-code/skills/performance-engineer/optimization-playbook.md +47 -47
- package/src/templates/agents/claude-code/skills/performance-optimizer/SKILL.md +69 -0
- package/src/templates/agents/claude-code/skills/performance-optimizer/benchmark-template.md +63 -45
- package/src/templates/agents/claude-code/skills/performance-optimizer/optimization-patterns.md +33 -35
- package/src/templates/agents/claude-code/skills/project-manager/SKILL.md +7 -6
- package/src/templates/agents/claude-code/skills/project-manager/agile-ceremonies.md +47 -28
- package/src/templates/agents/claude-code/skills/project-manager/project-templates.md +94 -78
- package/src/templates/agents/claude-code/skills/quality-assurance/SKILL.md +20 -17
- package/src/templates/agents/claude-code/skills/quality-assurance/qa-plan-template.md +63 -49
- package/src/templates/agents/claude-code/skills/release-coordinator/SKILL.md +5 -5
- package/src/templates/agents/claude-code/skills/release-coordinator/feature-flag-guide.md +30 -26
- package/src/templates/agents/claude-code/skills/release-coordinator/release-plan-template.md +67 -35
- package/src/templates/agents/claude-code/skills/requirements-analyst/ears-format.md +54 -42
- package/src/templates/agents/claude-code/skills/requirements-analyst/validation-rules.md +36 -33
- package/src/templates/agents/claude-code/skills/security-auditor/SKILL.md +77 -19
- package/src/templates/agents/claude-code/skills/security-auditor/audit-checklists.md +24 -24
- package/src/templates/agents/claude-code/skills/security-auditor/owasp-top-10.md +61 -20
- package/src/templates/agents/claude-code/skills/security-auditor/vulnerability-patterns.md +43 -11
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/SKILL.md +1 -0
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/incident-response-template.md +55 -25
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/observability-patterns.md +78 -68
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/slo-sli-guide.md +73 -53
- package/src/templates/agents/claude-code/skills/software-developer/solid-principles.md +83 -37
- package/src/templates/agents/claude-code/skills/software-developer/test-first-workflow.md +38 -31
- package/src/templates/agents/claude-code/skills/steering/SKILL.md +1 -0
- package/src/templates/agents/claude-code/skills/steering/auto-update-rules.md +31 -0
- package/src/templates/agents/claude-code/skills/system-architect/adr-template.md +25 -7
- package/src/templates/agents/claude-code/skills/system-architect/c4-model-guide.md +74 -61
- package/src/templates/agents/claude-code/skills/technical-writer/doc-templates/documentation-templates.md +70 -52
- package/src/templates/agents/claude-code/skills/test-engineer/SKILL.md +2 -0
- package/src/templates/agents/claude-code/skills/test-engineer/ears-test-mapping.md +75 -71
- package/src/templates/agents/claude-code/skills/test-engineer/test-types.md +85 -63
- package/src/templates/agents/claude-code/skills/traceability-auditor/coverage-matrix-template.md +39 -36
- package/src/templates/agents/claude-code/skills/traceability-auditor/gap-detection-rules.md +22 -17
- package/src/templates/agents/claude-code/skills/ui-ux-designer/SKILL.md +1 -0
- package/src/templates/agents/claude-code/skills/ui-ux-designer/accessibility-guidelines.md +49 -75
- package/src/templates/agents/claude-code/skills/ui-ux-designer/design-system-components.md +71 -59
- package/src/templates/agents/codex/AGENTS.md +74 -42
- package/src/templates/agents/cursor/AGENTS.md +74 -42
- package/src/templates/agents/gemini-cli/GEMINI.md +74 -42
- package/src/templates/agents/github-copilot/AGENTS.md +83 -51
- package/src/templates/agents/qwen-code/QWEN.md +74 -42
- package/src/templates/agents/windsurf/AGENTS.md +74 -42
- package/src/templates/architectures/README.md +41 -0
- package/src/templates/architectures/clean-architecture/README.md +113 -0
- package/src/templates/architectures/event-driven/README.md +162 -0
- package/src/templates/architectures/hexagonal/README.md +130 -0
- package/src/templates/index.js +6 -1
- package/src/templates/locale-manager.js +16 -16
- package/src/templates/shared/delta-spec-template.md +20 -13
- package/src/templates/shared/github-actions/musubi-issue-resolver.yml +5 -5
- package/src/templates/shared/github-actions/musubi-security-check.yml +3 -3
- package/src/templates/shared/github-actions/musubi-validate.yml +4 -4
- package/src/templates/shared/steering/structure.md +95 -0
- package/src/templates/skills/browser-agent.md +21 -16
- package/src/templates/skills/web-gui.md +8 -0
- package/src/templates/template-constraints.js +50 -53
- package/src/validators/advanced-validation.js +30 -36
- package/src/validators/constitutional-validator.js +77 -73
- package/src/validators/critic-system.js +49 -59
- package/src/validators/delta-format.js +59 -55
- 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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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_
|
|
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]
|
|
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(
|
|
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]
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
};
|