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.
- 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 +164 -145
- package/src/analyzers/codegraph-auto-update.js +858 -0
- package/src/analyzers/complexity-analyzer.js +536 -0
- package/src/analyzers/context-optimizer.js +247 -125
- package/src/analyzers/impact-analyzer.js +1 -1
- package/src/analyzers/large-project-analyzer.js +766 -0
- package/src/analyzers/repository-map.js +83 -80
- 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 +53 -44
- package/src/monitoring/incident-manager.js +123 -103
- package/src/monitoring/index.js +144 -134
- package/src/monitoring/observability.js +82 -59
- package/src/monitoring/quality-dashboard.js +51 -39
- package/src/monitoring/release-manager.js +70 -50
- 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,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
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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_
|
|
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]
|
|
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(
|
|
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]
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
};
|