musubi-sdd 5.0.0 → 5.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. package/README.ja.md +106 -48
  2. package/README.md +110 -32
  3. package/bin/musubi-analyze.js +74 -67
  4. package/bin/musubi-browser.js +27 -26
  5. package/bin/musubi-change.js +48 -47
  6. package/bin/musubi-checkpoint.js +10 -7
  7. package/bin/musubi-convert.js +25 -25
  8. package/bin/musubi-costs.js +27 -10
  9. package/bin/musubi-gui.js +52 -46
  10. package/bin/musubi-init.js +1952 -10
  11. package/bin/musubi-orchestrate.js +327 -239
  12. package/bin/musubi-remember.js +69 -56
  13. package/bin/musubi-resolve.js +53 -45
  14. package/bin/musubi-trace.js +51 -22
  15. package/bin/musubi-validate.js +39 -30
  16. package/bin/musubi-workflow.js +33 -34
  17. package/bin/musubi.js +39 -2
  18. package/package.json +1 -1
  19. package/src/agents/agent-loop.js +94 -95
  20. package/src/agents/agentic/code-generator.js +119 -109
  21. package/src/agents/agentic/code-reviewer.js +105 -108
  22. package/src/agents/agentic/index.js +4 -4
  23. package/src/agents/browser/action-executor.js +13 -13
  24. package/src/agents/browser/ai-comparator.js +11 -10
  25. package/src/agents/browser/context-manager.js +6 -6
  26. package/src/agents/browser/index.js +5 -5
  27. package/src/agents/browser/nl-parser.js +31 -46
  28. package/src/agents/browser/screenshot.js +2 -2
  29. package/src/agents/browser/test-generator.js +6 -4
  30. package/src/agents/function-tool.js +71 -65
  31. package/src/agents/index.js +7 -7
  32. package/src/agents/schema-generator.js +98 -94
  33. package/src/analyzers/ast-extractor.js +164 -145
  34. package/src/analyzers/codegraph-auto-update.js +858 -0
  35. package/src/analyzers/complexity-analyzer.js +536 -0
  36. package/src/analyzers/context-optimizer.js +247 -125
  37. package/src/analyzers/impact-analyzer.js +1 -1
  38. package/src/analyzers/large-project-analyzer.js +766 -0
  39. package/src/analyzers/repository-map.js +83 -80
  40. package/src/analyzers/security-analyzer.js +19 -11
  41. package/src/analyzers/stuck-detector.js +19 -17
  42. package/src/converters/index.js +78 -57
  43. package/src/converters/ir/types.js +12 -12
  44. package/src/converters/parsers/musubi-parser.js +134 -126
  45. package/src/converters/parsers/openapi-parser.js +70 -53
  46. package/src/converters/parsers/speckit-parser.js +239 -175
  47. package/src/converters/writers/musubi-writer.js +123 -118
  48. package/src/converters/writers/speckit-writer.js +124 -113
  49. package/src/generators/rust-migration-generator.js +512 -0
  50. package/src/gui/public/index.html +1365 -1211
  51. package/src/gui/server.js +41 -40
  52. package/src/gui/services/file-watcher.js +23 -8
  53. package/src/gui/services/project-scanner.js +26 -20
  54. package/src/gui/services/replanning-service.js +27 -23
  55. package/src/gui/services/traceability-service.js +8 -8
  56. package/src/gui/services/workflow-service.js +14 -7
  57. package/src/index.js +151 -0
  58. package/src/integrations/cicd.js +90 -104
  59. package/src/integrations/codegraph-mcp.js +643 -0
  60. package/src/integrations/documentation.js +142 -103
  61. package/src/integrations/examples.js +95 -80
  62. package/src/integrations/github-client.js +17 -17
  63. package/src/integrations/index.js +5 -5
  64. package/src/integrations/mcp/index.js +21 -21
  65. package/src/integrations/mcp/mcp-context-provider.js +76 -78
  66. package/src/integrations/mcp/mcp-discovery.js +74 -72
  67. package/src/integrations/mcp/mcp-tool-registry.js +99 -94
  68. package/src/integrations/mcp-connector.js +70 -66
  69. package/src/integrations/platforms.js +50 -49
  70. package/src/integrations/tool-discovery.js +37 -31
  71. package/src/llm-providers/anthropic-provider.js +11 -11
  72. package/src/llm-providers/base-provider.js +16 -18
  73. package/src/llm-providers/copilot-provider.js +22 -19
  74. package/src/llm-providers/index.js +26 -25
  75. package/src/llm-providers/ollama-provider.js +11 -11
  76. package/src/llm-providers/openai-provider.js +12 -12
  77. package/src/managers/agent-memory.js +36 -24
  78. package/src/managers/checkpoint-manager.js +4 -8
  79. package/src/managers/delta-spec.js +19 -19
  80. package/src/managers/index.js +13 -4
  81. package/src/managers/memory-condenser.js +35 -45
  82. package/src/managers/repo-skill-manager.js +57 -31
  83. package/src/managers/skill-loader.js +25 -22
  84. package/src/managers/skill-tools.js +36 -72
  85. package/src/managers/workflow.js +30 -22
  86. package/src/monitoring/cost-tracker.js +53 -44
  87. package/src/monitoring/incident-manager.js +123 -103
  88. package/src/monitoring/index.js +144 -134
  89. package/src/monitoring/observability.js +82 -59
  90. package/src/monitoring/quality-dashboard.js +51 -39
  91. package/src/monitoring/release-manager.js +70 -50
  92. package/src/orchestration/agent-skill-binding.js +39 -47
  93. package/src/orchestration/error-handler.js +65 -107
  94. package/src/orchestration/guardrails/base-guardrail.js +26 -24
  95. package/src/orchestration/guardrails/guardrail-rules.js +50 -64
  96. package/src/orchestration/guardrails/index.js +5 -5
  97. package/src/orchestration/guardrails/input-guardrail.js +58 -45
  98. package/src/orchestration/guardrails/output-guardrail.js +104 -81
  99. package/src/orchestration/guardrails/safety-check.js +79 -79
  100. package/src/orchestration/index.js +38 -55
  101. package/src/orchestration/mcp-tool-adapters.js +96 -99
  102. package/src/orchestration/orchestration-engine.js +21 -21
  103. package/src/orchestration/pattern-registry.js +60 -45
  104. package/src/orchestration/patterns/auto.js +34 -47
  105. package/src/orchestration/patterns/group-chat.js +59 -65
  106. package/src/orchestration/patterns/handoff.js +67 -65
  107. package/src/orchestration/patterns/human-in-loop.js +51 -72
  108. package/src/orchestration/patterns/nested.js +25 -40
  109. package/src/orchestration/patterns/sequential.js +35 -34
  110. package/src/orchestration/patterns/swarm.js +63 -56
  111. package/src/orchestration/patterns/triage.js +150 -109
  112. package/src/orchestration/reasoning/index.js +9 -9
  113. package/src/orchestration/reasoning/planning-engine.js +143 -140
  114. package/src/orchestration/reasoning/reasoning-engine.js +206 -144
  115. package/src/orchestration/reasoning/self-correction.js +121 -128
  116. package/src/orchestration/replanning/adaptive-goal-modifier.js +107 -112
  117. package/src/orchestration/replanning/alternative-generator.js +37 -42
  118. package/src/orchestration/replanning/config.js +63 -59
  119. package/src/orchestration/replanning/goal-progress-tracker.js +98 -100
  120. package/src/orchestration/replanning/index.js +24 -20
  121. package/src/orchestration/replanning/plan-evaluator.js +49 -50
  122. package/src/orchestration/replanning/plan-monitor.js +32 -28
  123. package/src/orchestration/replanning/proactive-path-optimizer.js +175 -178
  124. package/src/orchestration/replanning/replan-history.js +33 -26
  125. package/src/orchestration/replanning/replanning-engine.js +106 -108
  126. package/src/orchestration/skill-executor.js +107 -109
  127. package/src/orchestration/skill-registry.js +85 -89
  128. package/src/orchestration/workflow-examples.js +228 -231
  129. package/src/orchestration/workflow-executor.js +65 -68
  130. package/src/orchestration/workflow-orchestrator.js +72 -73
  131. package/src/phase4-integration.js +47 -40
  132. package/src/phase5-integration.js +89 -30
  133. package/src/reporters/coverage-report.js +82 -30
  134. package/src/reporters/hierarchical-reporter.js +498 -0
  135. package/src/reporters/traceability-matrix-report.js +29 -20
  136. package/src/resolvers/issue-resolver.js +43 -31
  137. package/src/steering/advanced-validation.js +133 -124
  138. package/src/steering/auto-updater.js +60 -73
  139. package/src/steering/index.js +6 -6
  140. package/src/steering/quality-metrics.js +41 -35
  141. package/src/steering/steering-auto-update.js +83 -86
  142. package/src/steering/steering-validator.js +98 -106
  143. package/src/steering/template-constraints.js +53 -54
  144. package/src/templates/agents/claude-code/CLAUDE.md +32 -32
  145. package/src/templates/agents/claude-code/skills/agent-assistant/SKILL.md +13 -5
  146. package/src/templates/agents/claude-code/skills/ai-ml-engineer/mlops-guide.md +23 -23
  147. package/src/templates/agents/claude-code/skills/ai-ml-engineer/model-card-template.md +60 -41
  148. package/src/templates/agents/claude-code/skills/api-designer/api-patterns.md +27 -19
  149. package/src/templates/agents/claude-code/skills/api-designer/openapi-template.md +11 -7
  150. package/src/templates/agents/claude-code/skills/bug-hunter/SKILL.md +4 -3
  151. package/src/templates/agents/claude-code/skills/bug-hunter/root-cause-analysis.md +37 -15
  152. package/src/templates/agents/claude-code/skills/change-impact-analyzer/dependency-graph-patterns.md +36 -42
  153. package/src/templates/agents/claude-code/skills/change-impact-analyzer/impact-analysis-template.md +69 -60
  154. package/src/templates/agents/claude-code/skills/cloud-architect/aws-patterns.md +31 -38
  155. package/src/templates/agents/claude-code/skills/cloud-architect/azure-patterns.md +28 -23
  156. package/src/templates/agents/claude-code/skills/code-reviewer/SKILL.md +61 -0
  157. package/src/templates/agents/claude-code/skills/code-reviewer/best-practices.md +27 -0
  158. package/src/templates/agents/claude-code/skills/code-reviewer/review-checklist.md +29 -10
  159. package/src/templates/agents/claude-code/skills/code-reviewer/review-standards.md +29 -24
  160. package/src/templates/agents/claude-code/skills/constitution-enforcer/SKILL.md +8 -6
  161. package/src/templates/agents/claude-code/skills/constitution-enforcer/constitutional-articles.md +62 -26
  162. package/src/templates/agents/claude-code/skills/constitution-enforcer/phase-minus-one-gates.md +35 -16
  163. package/src/templates/agents/claude-code/skills/database-administrator/backup-recovery.md +27 -17
  164. package/src/templates/agents/claude-code/skills/database-administrator/tuning-guide.md +25 -20
  165. package/src/templates/agents/claude-code/skills/database-schema-designer/schema-patterns.md +39 -22
  166. package/src/templates/agents/claude-code/skills/devops-engineer/ci-cd-templates.md +25 -22
  167. package/src/templates/agents/claude-code/skills/issue-resolver/SKILL.md +24 -21
  168. package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +148 -63
  169. package/src/templates/agents/claude-code/skills/orchestrator/patterns.md +35 -16
  170. package/src/templates/agents/claude-code/skills/orchestrator/selection-matrix.md +69 -64
  171. package/src/templates/agents/claude-code/skills/performance-engineer/optimization-playbook.md +47 -47
  172. package/src/templates/agents/claude-code/skills/performance-optimizer/SKILL.md +69 -0
  173. package/src/templates/agents/claude-code/skills/performance-optimizer/benchmark-template.md +63 -45
  174. package/src/templates/agents/claude-code/skills/performance-optimizer/optimization-patterns.md +33 -35
  175. package/src/templates/agents/claude-code/skills/project-manager/SKILL.md +7 -6
  176. package/src/templates/agents/claude-code/skills/project-manager/agile-ceremonies.md +47 -28
  177. package/src/templates/agents/claude-code/skills/project-manager/project-templates.md +94 -78
  178. package/src/templates/agents/claude-code/skills/quality-assurance/SKILL.md +20 -17
  179. package/src/templates/agents/claude-code/skills/quality-assurance/qa-plan-template.md +63 -49
  180. package/src/templates/agents/claude-code/skills/release-coordinator/SKILL.md +5 -5
  181. package/src/templates/agents/claude-code/skills/release-coordinator/feature-flag-guide.md +30 -26
  182. package/src/templates/agents/claude-code/skills/release-coordinator/release-plan-template.md +67 -35
  183. package/src/templates/agents/claude-code/skills/requirements-analyst/ears-format.md +54 -42
  184. package/src/templates/agents/claude-code/skills/requirements-analyst/validation-rules.md +36 -33
  185. package/src/templates/agents/claude-code/skills/security-auditor/SKILL.md +77 -19
  186. package/src/templates/agents/claude-code/skills/security-auditor/audit-checklists.md +24 -24
  187. package/src/templates/agents/claude-code/skills/security-auditor/owasp-top-10.md +61 -20
  188. package/src/templates/agents/claude-code/skills/security-auditor/vulnerability-patterns.md +43 -11
  189. package/src/templates/agents/claude-code/skills/site-reliability-engineer/SKILL.md +1 -0
  190. package/src/templates/agents/claude-code/skills/site-reliability-engineer/incident-response-template.md +55 -25
  191. package/src/templates/agents/claude-code/skills/site-reliability-engineer/observability-patterns.md +78 -68
  192. package/src/templates/agents/claude-code/skills/site-reliability-engineer/slo-sli-guide.md +73 -53
  193. package/src/templates/agents/claude-code/skills/software-developer/solid-principles.md +83 -37
  194. package/src/templates/agents/claude-code/skills/software-developer/test-first-workflow.md +38 -31
  195. package/src/templates/agents/claude-code/skills/steering/SKILL.md +1 -0
  196. package/src/templates/agents/claude-code/skills/steering/auto-update-rules.md +31 -0
  197. package/src/templates/agents/claude-code/skills/system-architect/adr-template.md +25 -7
  198. package/src/templates/agents/claude-code/skills/system-architect/c4-model-guide.md +74 -61
  199. package/src/templates/agents/claude-code/skills/technical-writer/doc-templates/documentation-templates.md +70 -52
  200. package/src/templates/agents/claude-code/skills/test-engineer/SKILL.md +2 -0
  201. package/src/templates/agents/claude-code/skills/test-engineer/ears-test-mapping.md +75 -71
  202. package/src/templates/agents/claude-code/skills/test-engineer/test-types.md +85 -63
  203. package/src/templates/agents/claude-code/skills/traceability-auditor/coverage-matrix-template.md +39 -36
  204. package/src/templates/agents/claude-code/skills/traceability-auditor/gap-detection-rules.md +22 -17
  205. package/src/templates/agents/claude-code/skills/ui-ux-designer/SKILL.md +1 -0
  206. package/src/templates/agents/claude-code/skills/ui-ux-designer/accessibility-guidelines.md +49 -75
  207. package/src/templates/agents/claude-code/skills/ui-ux-designer/design-system-components.md +71 -59
  208. package/src/templates/agents/codex/AGENTS.md +74 -42
  209. package/src/templates/agents/cursor/AGENTS.md +74 -42
  210. package/src/templates/agents/gemini-cli/GEMINI.md +74 -42
  211. package/src/templates/agents/github-copilot/AGENTS.md +83 -51
  212. package/src/templates/agents/qwen-code/QWEN.md +74 -42
  213. package/src/templates/agents/windsurf/AGENTS.md +74 -42
  214. package/src/templates/architectures/README.md +41 -0
  215. package/src/templates/architectures/clean-architecture/README.md +113 -0
  216. package/src/templates/architectures/event-driven/README.md +162 -0
  217. package/src/templates/architectures/hexagonal/README.md +130 -0
  218. package/src/templates/index.js +6 -1
  219. package/src/templates/locale-manager.js +16 -16
  220. package/src/templates/shared/delta-spec-template.md +20 -13
  221. package/src/templates/shared/github-actions/musubi-issue-resolver.yml +5 -5
  222. package/src/templates/shared/github-actions/musubi-security-check.yml +3 -3
  223. package/src/templates/shared/github-actions/musubi-validate.yml +4 -4
  224. package/src/templates/shared/steering/structure.md +95 -0
  225. package/src/templates/skills/browser-agent.md +21 -16
  226. package/src/templates/skills/web-gui.md +8 -0
  227. package/src/templates/template-constraints.js +50 -53
  228. package/src/validators/advanced-validation.js +30 -36
  229. package/src/validators/constitutional-validator.js +77 -73
  230. package/src/validators/critic-system.js +49 -59
  231. package/src/validators/delta-format.js +59 -55
  232. package/src/validators/traceability-validator.js +7 -11
@@ -1,16 +1,23 @@
1
1
  /**
2
2
  * Context Optimizer
3
- *
3
+ *
4
4
  * Optimizes context for LLM consumption by intelligently selecting
5
5
  * and prioritizing relevant code and documentation.
6
- *
7
- * Part of MUSUBI v4.1.0 - Codebase Intelligence
6
+ *
7
+ * Part of MUSUBI v5.0.0 - Codebase Intelligence
8
+ *
9
+ * @module analyzers/context-optimizer
8
10
  * @version 1.0.0
11
+ *
12
+ * @traceability
13
+ * - Requirement: REQ-P4-003 (Context Optimization)
14
+ * - Design: docs/design/tdd-musubi-v5.0.0.md#2.3
15
+ * - Test: tests/analyzers/context-optimizer.test.js
9
16
  */
10
17
 
11
18
  const { EventEmitter } = require('events');
12
- const { RepositoryMap, createRepositoryMap } = require('./repository-map');
13
- const { ASTExtractor, createASTExtractor } = require('./ast-extractor');
19
+ const { createRepositoryMap } = require('./repository-map');
20
+ const { createASTExtractor } = require('./ast-extractor');
14
21
 
15
22
  /**
16
23
  * @typedef {Object} ContextRequest
@@ -48,7 +55,7 @@ const CHARS_PER_TOKEN = 4; // Approximate
48
55
  const TOKEN_OVERHEAD = {
49
56
  fileHeader: 50,
50
57
  symbolHeader: 20,
51
- separator: 10
58
+ separator: 10,
52
59
  };
53
60
 
54
61
  /**
@@ -60,36 +67,36 @@ const TASK_WEIGHTS = {
60
67
  relatedFiles: 0.8,
61
68
  interfaces: 0.85,
62
69
  tests: 0.3,
63
- docs: 0.5
70
+ docs: 0.5,
64
71
  },
65
72
  debug: {
66
73
  errorLocation: 1.0,
67
74
  callStack: 0.9,
68
75
  relatedFiles: 0.7,
69
76
  tests: 0.6,
70
- docs: 0.4
77
+ docs: 0.4,
71
78
  },
72
79
  review: {
73
80
  changedFiles: 1.0,
74
81
  relatedFiles: 0.7,
75
82
  interfaces: 0.6,
76
83
  tests: 0.8,
77
- docs: 0.5
84
+ docs: 0.5,
78
85
  },
79
86
  explain: {
80
87
  targetFile: 1.0,
81
88
  imports: 0.8,
82
89
  relatedFiles: 0.6,
83
90
  tests: 0.4,
84
- docs: 0.7
91
+ docs: 0.7,
85
92
  },
86
93
  refactor: {
87
94
  targetFile: 1.0,
88
95
  usages: 0.9,
89
96
  interfaces: 0.8,
90
97
  tests: 0.7,
91
- docs: 0.5
92
- }
98
+ docs: 0.5,
99
+ },
93
100
  };
94
101
 
95
102
  /**
@@ -113,44 +120,44 @@ class ContextOptimizer extends EventEmitter {
113
120
  this.maxFiles = options.maxFiles ?? 20;
114
121
  this.useAST = options.useAST ?? true;
115
122
  this.cacheEnabled = options.cache ?? true;
116
-
123
+
117
124
  // Components
118
125
  this.repoMap = null;
119
126
  this.astExtractor = null;
120
-
127
+
121
128
  // Caches
122
129
  this.relevanceCache = new Map();
123
130
  this.astCache = new Map();
124
-
131
+
125
132
  // State
126
133
  this.initialized = false;
127
134
  }
128
-
135
+
129
136
  /**
130
137
  * Initialize optimizer with repository analysis
131
138
  * @returns {Promise<void>}
132
139
  */
133
140
  async initialize() {
134
141
  if (this.initialized) return;
135
-
142
+
136
143
  this.emit('init:start');
137
-
144
+
138
145
  // Create repository map
139
146
  this.repoMap = createRepositoryMap({ rootPath: this.rootPath });
140
147
  await this.repoMap.generate();
141
-
148
+
142
149
  // Create AST extractor
143
150
  if (this.useAST) {
144
151
  this.astExtractor = createASTExtractor();
145
152
  }
146
-
153
+
147
154
  this.initialized = true;
148
- this.emit('init:complete', {
155
+ this.emit('init:complete', {
149
156
  files: this.repoMap.stats.totalFiles,
150
- entryPoints: this.repoMap.entryPoints.length
157
+ entryPoints: this.repoMap.entryPoints.length,
151
158
  });
152
159
  }
153
-
160
+
154
161
  /**
155
162
  * Optimize context for a query
156
163
  * @param {ContextRequest} request - Context request
@@ -160,28 +167,28 @@ class ContextOptimizer extends EventEmitter {
160
167
  if (!this.initialized) {
161
168
  await this.initialize();
162
169
  }
163
-
170
+
164
171
  this.emit('optimize:start', request);
165
-
172
+
166
173
  const maxTokens = request.maxTokens ?? this.maxTokens;
167
174
  const task = request.task || 'implement';
168
175
  const weights = TASK_WEIGHTS[task] || TASK_WEIGHTS.implement;
169
-
176
+
170
177
  // Step 1: Collect candidate files
171
178
  const candidates = await this.collectCandidates(request);
172
-
179
+
173
180
  // Step 2: Score candidates by relevance
174
181
  const scored = await this.scoreRelevance(candidates, request, weights);
175
-
182
+
176
183
  // Step 3: Sort by relevance
177
184
  scored.sort((a, b) => b.relevance - a.relevance);
178
-
185
+
179
186
  // Step 4: Select items within token budget
180
187
  const selected = this.selectWithinBudget(scored, maxTokens);
181
-
188
+
182
189
  // Step 5: Build formatted context
183
190
  const formatted = this.formatContext(selected, request);
184
-
191
+
185
192
  const result = {
186
193
  items: selected,
187
194
  totalTokens: selected.reduce((sum, item) => sum + item.tokens, 0),
@@ -190,14 +197,14 @@ class ContextOptimizer extends EventEmitter {
190
197
  candidateCount: candidates.length,
191
198
  selectedCount: selected.length,
192
199
  tokenBudget: maxTokens,
193
- tokensUsed: selected.reduce((sum, item) => sum + item.tokens, 0)
194
- }
200
+ tokensUsed: selected.reduce((sum, item) => sum + item.tokens, 0),
201
+ },
195
202
  };
196
-
203
+
197
204
  this.emit('optimize:complete', result.stats);
198
205
  return result;
199
206
  }
200
-
207
+
201
208
  /**
202
209
  * Collect candidate files for context
203
210
  * @param {ContextRequest} request - Context request
@@ -206,7 +213,7 @@ class ContextOptimizer extends EventEmitter {
206
213
  */
207
214
  async collectCandidates(request) {
208
215
  const candidates = [];
209
-
216
+
210
217
  // Add focus files with high priority
211
218
  if (request.focusFiles?.length > 0) {
212
219
  for (const pattern of request.focusFiles) {
@@ -218,12 +225,12 @@ class ContextOptimizer extends EventEmitter {
218
225
  content: '',
219
226
  relevance: 1.0,
220
227
  tokens: this.estimateTokens(file.size),
221
- metadata: { source: 'focus', file }
228
+ metadata: { source: 'focus', file },
222
229
  });
223
230
  }
224
231
  }
225
232
  }
226
-
233
+
227
234
  // Add entry points
228
235
  for (const entry of this.repoMap.entryPoints.slice(0, 5)) {
229
236
  const file = this.repoMap.files.find(f => f.path === entry);
@@ -234,11 +241,11 @@ class ContextOptimizer extends EventEmitter {
234
241
  content: '',
235
242
  relevance: 0.8,
236
243
  tokens: this.estimateTokens(file.size),
237
- metadata: { source: 'entryPoint', file }
244
+ metadata: { source: 'entryPoint', file },
238
245
  });
239
246
  }
240
247
  }
241
-
248
+
242
249
  // Add files matching query keywords
243
250
  if (request.query) {
244
251
  const keywords = this.extractKeywords(request.query);
@@ -252,13 +259,13 @@ class ContextOptimizer extends EventEmitter {
252
259
  content: '',
253
260
  relevance: 0.6,
254
261
  tokens: this.estimateTokens(file.size),
255
- metadata: { source: 'keyword', keyword, file }
262
+ metadata: { source: 'keyword', keyword, file },
256
263
  });
257
264
  }
258
265
  }
259
266
  }
260
267
  }
261
-
268
+
262
269
  // Add related files based on imports (if AST enabled)
263
270
  if (this.useAST && candidates.length > 0) {
264
271
  const imports = await this.collectImports(candidates.slice(0, 5));
@@ -270,16 +277,16 @@ class ContextOptimizer extends EventEmitter {
270
277
  content: '',
271
278
  relevance: 0.5,
272
279
  tokens: this.estimateTokens(imp.size),
273
- metadata: { source: 'import', file: imp }
280
+ metadata: { source: 'import', file: imp },
274
281
  });
275
282
  }
276
283
  }
277
284
  }
278
-
285
+
279
286
  // Add test files if requested
280
287
  if (request.includeTests) {
281
- const testFiles = this.repoMap.files.filter(f =>
282
- f.path.includes('test') || f.path.includes('spec')
288
+ const testFiles = this.repoMap.files.filter(
289
+ f => f.path.includes('test') || f.path.includes('spec')
283
290
  );
284
291
  for (const file of testFiles.slice(0, 5)) {
285
292
  if (!candidates.find(c => c.path === file.path)) {
@@ -289,15 +296,15 @@ class ContextOptimizer extends EventEmitter {
289
296
  content: '',
290
297
  relevance: 0.4,
291
298
  tokens: this.estimateTokens(file.size),
292
- metadata: { source: 'test', file }
299
+ metadata: { source: 'test', file },
293
300
  });
294
301
  }
295
302
  }
296
303
  }
297
-
304
+
298
305
  return candidates;
299
306
  }
300
-
307
+
301
308
  /**
302
309
  * Collect imports from candidate files
303
310
  * @param {ContextItem[]} candidates - Candidate items
@@ -307,25 +314,25 @@ class ContextOptimizer extends EventEmitter {
307
314
  async collectImports(candidates) {
308
315
  const imports = [];
309
316
  const path = require('path');
310
-
317
+
311
318
  for (const candidate of candidates) {
312
319
  if (candidate.metadata.file?.language === 'unknown') continue;
313
-
320
+
314
321
  try {
315
322
  const filePath = path.join(this.rootPath, candidate.path);
316
323
  const ast = await this.astExtractor.extractFromFile(filePath);
317
-
324
+
318
325
  for (const imp of ast.imports) {
319
326
  // Resolve relative imports
320
327
  if (imp.source.startsWith('.')) {
321
328
  const dir = path.dirname(candidate.path);
322
329
  let resolved = path.join(dir, imp.source);
323
-
330
+
324
331
  // Try common extensions
325
332
  for (const ext of ['.js', '.ts', '.jsx', '.tsx', '/index.js', '/index.ts']) {
326
333
  const withExt = resolved + ext;
327
- const file = this.repoMap.files.find(f =>
328
- f.path === withExt || f.path === resolved.replace(/\\/g, '/')
334
+ const file = this.repoMap.files.find(
335
+ f => f.path === withExt || f.path === resolved.replace(/\\/g, '/')
329
336
  );
330
337
  if (file) {
331
338
  imports.push(file);
@@ -338,10 +345,10 @@ class ContextOptimizer extends EventEmitter {
338
345
  // Skip files that can't be parsed
339
346
  }
340
347
  }
341
-
348
+
342
349
  return imports;
343
350
  }
344
-
351
+
345
352
  /**
346
353
  * Score relevance of candidates
347
354
  * @param {ContextItem[]} candidates - Candidate items
@@ -353,7 +360,7 @@ class ContextOptimizer extends EventEmitter {
353
360
  async scoreRelevance(candidates, request, weights) {
354
361
  for (const candidate of candidates) {
355
362
  let score = candidate.relevance;
356
-
363
+
357
364
  // Adjust by source
358
365
  switch (candidate.metadata.source) {
359
366
  case 'focus':
@@ -372,20 +379,18 @@ class ContextOptimizer extends EventEmitter {
372
379
  score *= weights.tests;
373
380
  break;
374
381
  }
375
-
382
+
376
383
  // Boost for focus symbols if present
377
384
  if (request.focusSymbols?.length > 0 && this.useAST) {
378
385
  try {
379
386
  const path = require('path');
380
387
  const filePath = path.join(this.rootPath, candidate.path);
381
388
  const ast = await this.getOrExtractAST(filePath);
382
-
383
- const hasSymbol = ast.symbols.some(s =>
384
- request.focusSymbols.some(fs =>
385
- s.name.toLowerCase().includes(fs.toLowerCase())
386
- )
389
+
390
+ const hasSymbol = ast.symbols.some(s =>
391
+ request.focusSymbols.some(fs => s.name.toLowerCase().includes(fs.toLowerCase()))
387
392
  );
388
-
393
+
389
394
  if (hasSymbol) {
390
395
  score *= 1.5;
391
396
  }
@@ -393,23 +398,23 @@ class ContextOptimizer extends EventEmitter {
393
398
  // Skip
394
399
  }
395
400
  }
396
-
401
+
397
402
  // Penalize very large files
398
403
  if (candidate.tokens > 2000) {
399
404
  score *= 0.7;
400
405
  }
401
-
406
+
402
407
  // Boost for exports (more important modules)
403
408
  if (candidate.metadata.file?.exports?.length > 3) {
404
409
  score *= 1.2;
405
410
  }
406
-
411
+
407
412
  candidate.relevance = Math.min(score, 1.0);
408
413
  }
409
-
414
+
410
415
  return candidates;
411
416
  }
412
-
417
+
413
418
  /**
414
419
  * Get or extract AST with caching
415
420
  * @param {string} filePath - File path
@@ -420,16 +425,16 @@ class ContextOptimizer extends EventEmitter {
420
425
  if (this.cacheEnabled && this.astCache.has(filePath)) {
421
426
  return this.astCache.get(filePath);
422
427
  }
423
-
428
+
424
429
  const ast = await this.astExtractor.extractFromFile(filePath);
425
-
430
+
426
431
  if (this.cacheEnabled) {
427
432
  this.astCache.set(filePath, ast);
428
433
  }
429
-
434
+
430
435
  return ast;
431
436
  }
432
-
437
+
433
438
  /**
434
439
  * Select items within token budget
435
440
  * @param {ContextItem[]} scored - Scored items
@@ -440,23 +445,23 @@ class ContextOptimizer extends EventEmitter {
440
445
  selectWithinBudget(scored, maxTokens) {
441
446
  const selected = [];
442
447
  let tokensUsed = 0;
443
-
448
+
444
449
  for (const item of scored) {
445
450
  const itemTokens = item.tokens + TOKEN_OVERHEAD.fileHeader;
446
-
451
+
447
452
  if (tokensUsed + itemTokens <= maxTokens) {
448
453
  selected.push(item);
449
454
  tokensUsed += itemTokens;
450
455
  }
451
-
456
+
452
457
  if (selected.length >= this.maxFiles) {
453
458
  break;
454
459
  }
455
460
  }
456
-
461
+
457
462
  return selected;
458
463
  }
459
-
464
+
460
465
  /**
461
466
  * Format context for LLM consumption
462
467
  * @param {ContextItem[]} items - Selected items
@@ -469,15 +474,15 @@ class ContextOptimizer extends EventEmitter {
469
474
  context += `Task: ${request.task || 'implementation'}\n`;
470
475
  context += `Query: ${request.query || 'N/A'}\n`;
471
476
  context += `Files: ${items.length}\n\n`;
472
-
477
+
473
478
  context += `---\n\n`;
474
-
479
+
475
480
  for (const item of items) {
476
481
  context += `## ${item.path}\n\n`;
477
482
  context += `- Type: ${item.type}\n`;
478
483
  context += `- Relevance: ${(item.relevance * 100).toFixed(0)}%\n`;
479
484
  context += `- Source: ${item.metadata.source}\n`;
480
-
485
+
481
486
  if (item.metadata.file?.exports?.length > 0) {
482
487
  context += `- Exports: ${item.metadata.file.exports.slice(0, 5).join(', ')}`;
483
488
  if (item.metadata.file.exports.length > 5) {
@@ -485,10 +490,10 @@ class ContextOptimizer extends EventEmitter {
485
490
  }
486
491
  context += '\n';
487
492
  }
488
-
493
+
489
494
  context += '\n';
490
495
  }
491
-
496
+
492
497
  // Add repository overview
493
498
  if (this.repoMap) {
494
499
  context += `---\n\n`;
@@ -497,10 +502,10 @@ class ContextOptimizer extends EventEmitter {
497
502
  context += `- Languages: ${Object.keys(this.repoMap.stats.byLanguage).slice(0, 5).join(', ')}\n`;
498
503
  context += `- Entry Points: ${this.repoMap.entryPoints.slice(0, 3).join(', ')}\n`;
499
504
  }
500
-
505
+
501
506
  return context;
502
507
  }
503
-
508
+
504
509
  /**
505
510
  * Extract keywords from query
506
511
  * @param {string} query - User query
@@ -510,36 +515,156 @@ class ContextOptimizer extends EventEmitter {
510
515
  extractKeywords(query) {
511
516
  // Remove common words and extract meaningful terms
512
517
  const stopWords = new Set([
513
- 'the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
514
- 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could',
515
- 'should', 'may', 'might', 'must', 'shall', 'can', 'need', 'dare',
516
- 'ought', 'used', 'to', 'of', 'in', 'for', 'on', 'with', 'at', 'by',
517
- 'from', 'as', 'into', 'through', 'during', 'before', 'after', 'above',
518
- 'below', 'between', 'under', 'again', 'further', 'then', 'once', 'here',
519
- 'there', 'when', 'where', 'why', 'how', 'all', 'each', 'every', 'both',
520
- 'few', 'more', 'most', 'other', 'some', 'such', 'no', 'nor', 'not',
521
- 'only', 'own', 'same', 'so', 'than', 'too', 'very', 'just', 'and',
522
- 'but', 'if', 'or', 'because', 'until', 'while', 'although', 'though',
523
- 'this', 'that', 'these', 'those', 'what', 'which', 'who', 'whom',
524
- 'i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you',
525
- 'your', 'yours', 'yourself', 'yourselves', 'he', 'him', 'his', 'himself',
526
- 'she', 'her', 'hers', 'herself', 'it', 'its', 'itself', 'they', 'them',
527
- 'their', 'theirs', 'themselves', 'create', 'add', 'fix', 'implement',
528
- 'change', 'update', 'modify', 'file', 'code', 'function', 'class'
518
+ 'the',
519
+ 'a',
520
+ 'an',
521
+ 'is',
522
+ 'are',
523
+ 'was',
524
+ 'were',
525
+ 'be',
526
+ 'been',
527
+ 'being',
528
+ 'have',
529
+ 'has',
530
+ 'had',
531
+ 'do',
532
+ 'does',
533
+ 'did',
534
+ 'will',
535
+ 'would',
536
+ 'could',
537
+ 'should',
538
+ 'may',
539
+ 'might',
540
+ 'must',
541
+ 'shall',
542
+ 'can',
543
+ 'need',
544
+ 'dare',
545
+ 'ought',
546
+ 'used',
547
+ 'to',
548
+ 'of',
549
+ 'in',
550
+ 'for',
551
+ 'on',
552
+ 'with',
553
+ 'at',
554
+ 'by',
555
+ 'from',
556
+ 'as',
557
+ 'into',
558
+ 'through',
559
+ 'during',
560
+ 'before',
561
+ 'after',
562
+ 'above',
563
+ 'below',
564
+ 'between',
565
+ 'under',
566
+ 'again',
567
+ 'further',
568
+ 'then',
569
+ 'once',
570
+ 'here',
571
+ 'there',
572
+ 'when',
573
+ 'where',
574
+ 'why',
575
+ 'how',
576
+ 'all',
577
+ 'each',
578
+ 'every',
579
+ 'both',
580
+ 'few',
581
+ 'more',
582
+ 'most',
583
+ 'other',
584
+ 'some',
585
+ 'such',
586
+ 'no',
587
+ 'nor',
588
+ 'not',
589
+ 'only',
590
+ 'own',
591
+ 'same',
592
+ 'so',
593
+ 'than',
594
+ 'too',
595
+ 'very',
596
+ 'just',
597
+ 'and',
598
+ 'but',
599
+ 'if',
600
+ 'or',
601
+ 'because',
602
+ 'until',
603
+ 'while',
604
+ 'although',
605
+ 'though',
606
+ 'this',
607
+ 'that',
608
+ 'these',
609
+ 'those',
610
+ 'what',
611
+ 'which',
612
+ 'who',
613
+ 'whom',
614
+ 'i',
615
+ 'me',
616
+ 'my',
617
+ 'myself',
618
+ 'we',
619
+ 'our',
620
+ 'ours',
621
+ 'ourselves',
622
+ 'you',
623
+ 'your',
624
+ 'yours',
625
+ 'yourself',
626
+ 'yourselves',
627
+ 'he',
628
+ 'him',
629
+ 'his',
630
+ 'himself',
631
+ 'she',
632
+ 'her',
633
+ 'hers',
634
+ 'herself',
635
+ 'it',
636
+ 'its',
637
+ 'itself',
638
+ 'they',
639
+ 'them',
640
+ 'their',
641
+ 'theirs',
642
+ 'themselves',
643
+ 'create',
644
+ 'add',
645
+ 'fix',
646
+ 'implement',
647
+ 'change',
648
+ 'update',
649
+ 'modify',
650
+ 'file',
651
+ 'code',
652
+ 'function',
653
+ 'class',
529
654
  ]);
530
-
655
+
531
656
  const words = query
532
657
  .toLowerCase()
533
658
  .replace(/[^a-z0-9\s-_]/g, ' ')
534
659
  .split(/\s+/)
535
660
  .filter(w => w.length > 2 && !stopWords.has(w));
536
-
661
+
537
662
  // Also extract CamelCase and snake_case identifiers
538
663
  const identifiers = query.match(/[A-Z][a-z]+|[a-z]+_[a-z]+/g) || [];
539
-
664
+
540
665
  return [...new Set([...words, ...identifiers.map(i => i.toLowerCase())])];
541
666
  }
542
-
667
+
543
668
  /**
544
669
  * Estimate tokens from bytes
545
670
  * @param {number} bytes - File size in bytes
@@ -549,7 +674,7 @@ class ContextOptimizer extends EventEmitter {
549
674
  estimateTokens(bytes) {
550
675
  return Math.ceil(bytes / CHARS_PER_TOKEN);
551
676
  }
552
-
677
+
553
678
  /**
554
679
  * Build focused context for specific files
555
680
  * @param {string[]} filePaths - File paths to include
@@ -560,31 +685,29 @@ class ContextOptimizer extends EventEmitter {
560
685
  const fs = require('fs');
561
686
  const path = require('path');
562
687
  const { maxTokens = 4000, includeAST = true } = options;
563
-
688
+
564
689
  let context = '';
565
690
  let tokensUsed = 0;
566
-
691
+
567
692
  for (const filePath of filePaths) {
568
- const absPath = path.isAbsolute(filePath)
569
- ? filePath
570
- : path.join(this.rootPath, filePath);
571
-
693
+ const absPath = path.isAbsolute(filePath) ? filePath : path.join(this.rootPath, filePath);
694
+
572
695
  try {
573
696
  const content = await fs.promises.readFile(absPath, 'utf-8');
574
697
  const tokens = this.estimateTokens(content.length);
575
-
698
+
576
699
  if (tokensUsed + tokens > maxTokens) {
577
700
  // Truncate to fit
578
701
  const remaining = maxTokens - tokensUsed;
579
702
  const chars = remaining * CHARS_PER_TOKEN;
580
703
  context += `\n## ${filePath} (truncated)\n\n\`\`\`\n`;
581
704
  context += content.slice(0, chars);
582
- context += '\n...(truncated)\n\`\`\`\n';
705
+ context += '\n...(truncated)\n```\n';
583
706
  break;
584
707
  }
585
-
708
+
586
709
  context += `\n## ${filePath}\n\n`;
587
-
710
+
588
711
  // Add AST summary if enabled
589
712
  if (includeAST && this.useAST) {
590
713
  try {
@@ -600,18 +723,17 @@ class ContextOptimizer extends EventEmitter {
600
723
  // Skip AST
601
724
  }
602
725
  }
603
-
726
+
604
727
  context += '```\n' + content + '\n```\n';
605
728
  tokensUsed += tokens;
606
-
607
729
  } catch (error) {
608
730
  context += `\n## ${filePath}\n\n*Error reading file: ${error.message}*\n`;
609
731
  }
610
732
  }
611
-
733
+
612
734
  return context;
613
735
  }
614
-
736
+
615
737
  /**
616
738
  * Get optimization statistics
617
739
  * @returns {Object}
@@ -622,10 +744,10 @@ class ContextOptimizer extends EventEmitter {
622
744
  repoFiles: this.repoMap?.stats?.totalFiles || 0,
623
745
  repoEntryPoints: this.repoMap?.entryPoints?.length || 0,
624
746
  astCacheSize: this.astCache.size,
625
- relevanceCacheSize: this.relevanceCache.size
747
+ relevanceCacheSize: this.relevanceCache.size,
626
748
  };
627
749
  }
628
-
750
+
629
751
  /**
630
752
  * Clear all caches
631
753
  */
@@ -633,7 +755,7 @@ class ContextOptimizer extends EventEmitter {
633
755
  this.astCache.clear();
634
756
  this.relevanceCache.clear();
635
757
  }
636
-
758
+
637
759
  /**
638
760
  * Reset optimizer state
639
761
  */
@@ -670,5 +792,5 @@ module.exports = {
670
792
  createContextOptimizer,
671
793
  optimizeContext,
672
794
  TASK_WEIGHTS,
673
- CHARS_PER_TOKEN
795
+ CHARS_PER_TOKEN,
674
796
  };