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