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
|
* Repository Map Generator
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Generates a comprehensive map of the repository structure for LLM context.
|
|
5
5
|
* Implements efficient file scanning, caching, and incremental updates.
|
|
6
|
-
*
|
|
7
|
-
* Part of MUSUBI
|
|
6
|
+
*
|
|
7
|
+
* Part of MUSUBI v5.0.0 - Codebase Intelligence
|
|
8
|
+
*
|
|
9
|
+
* @module analyzers/repository-map
|
|
8
10
|
* @version 1.0.0
|
|
11
|
+
*
|
|
12
|
+
* @traceability
|
|
13
|
+
* - Requirement: REQ-P4-001 (Repository Map Generation)
|
|
14
|
+
* - Design: docs/design/tdd-musubi-v5.0.0.md#2.1
|
|
15
|
+
* - Test: tests/analyzers/repository-map.test.js
|
|
9
16
|
*/
|
|
10
17
|
|
|
11
18
|
const fs = require('fs');
|
|
@@ -85,7 +92,7 @@ const LANGUAGE_MAP = {
|
|
|
85
92
|
'.gql': 'graphql',
|
|
86
93
|
'.dockerfile': 'dockerfile',
|
|
87
94
|
'.vue': 'vue',
|
|
88
|
-
'.svelte': 'svelte'
|
|
95
|
+
'.svelte': 'svelte',
|
|
89
96
|
};
|
|
90
97
|
|
|
91
98
|
/**
|
|
@@ -104,7 +111,7 @@ const ENTRY_PATTERNS = [
|
|
|
104
111
|
/^pyproject\.toml$/,
|
|
105
112
|
/^Gemfile$/,
|
|
106
113
|
/^pom\.xml$/,
|
|
107
|
-
/^build\.gradle(\.kts)
|
|
114
|
+
/^build\.gradle(\.kts)?$/,
|
|
108
115
|
];
|
|
109
116
|
|
|
110
117
|
/**
|
|
@@ -144,7 +151,7 @@ const DEFAULT_IGNORE_PATTERNS = [
|
|
|
144
151
|
'pnpm-lock.yaml',
|
|
145
152
|
'Cargo.lock',
|
|
146
153
|
'Gemfile.lock',
|
|
147
|
-
'poetry.lock'
|
|
154
|
+
'poetry.lock',
|
|
148
155
|
];
|
|
149
156
|
|
|
150
157
|
/**
|
|
@@ -165,41 +172,38 @@ class RepositoryMap extends EventEmitter {
|
|
|
165
172
|
constructor(options = {}) {
|
|
166
173
|
super();
|
|
167
174
|
this.rootPath = options.rootPath || process.cwd();
|
|
168
|
-
this.ignorePatterns = [
|
|
169
|
-
...DEFAULT_IGNORE_PATTERNS,
|
|
170
|
-
...(options.ignorePatterns || [])
|
|
171
|
-
];
|
|
175
|
+
this.ignorePatterns = [...DEFAULT_IGNORE_PATTERNS, ...(options.ignorePatterns || [])];
|
|
172
176
|
this.maxDepth = options.maxDepth ?? 20;
|
|
173
177
|
this.maxFiles = options.maxFiles ?? 10000;
|
|
174
178
|
this.includeContent = options.includeContent ?? false;
|
|
175
179
|
this.contentMaxSize = options.contentMaxSize ?? 10000;
|
|
176
|
-
|
|
180
|
+
|
|
177
181
|
// Cache
|
|
178
182
|
this.cache = new Map();
|
|
179
183
|
this.lastScanTime = null;
|
|
180
|
-
|
|
184
|
+
|
|
181
185
|
// Statistics
|
|
182
186
|
this.stats = {
|
|
183
187
|
totalFiles: 0,
|
|
184
188
|
totalDirs: 0,
|
|
185
189
|
totalSize: 0,
|
|
186
190
|
byLanguage: {},
|
|
187
|
-
byExtension: {}
|
|
191
|
+
byExtension: {},
|
|
188
192
|
};
|
|
189
|
-
|
|
193
|
+
|
|
190
194
|
// Results
|
|
191
195
|
this.files = [];
|
|
192
196
|
this.structure = {};
|
|
193
197
|
this.entryPoints = [];
|
|
194
198
|
}
|
|
195
|
-
|
|
199
|
+
|
|
196
200
|
/**
|
|
197
201
|
* Generate repository map
|
|
198
202
|
* @returns {Promise<RepositoryMap>}
|
|
199
203
|
*/
|
|
200
204
|
async generate() {
|
|
201
205
|
this.emit('scan:start', { rootPath: this.rootPath });
|
|
202
|
-
|
|
206
|
+
|
|
203
207
|
// Reset state
|
|
204
208
|
this.files = [];
|
|
205
209
|
this.structure = {};
|
|
@@ -209,31 +213,30 @@ class RepositoryMap extends EventEmitter {
|
|
|
209
213
|
totalDirs: 0,
|
|
210
214
|
totalSize: 0,
|
|
211
215
|
byLanguage: {},
|
|
212
|
-
byExtension: {}
|
|
216
|
+
byExtension: {},
|
|
213
217
|
};
|
|
214
|
-
|
|
218
|
+
|
|
215
219
|
try {
|
|
216
220
|
await this.scanDirectory(this.rootPath, '', 0);
|
|
217
221
|
this.lastScanTime = new Date();
|
|
218
|
-
|
|
222
|
+
|
|
219
223
|
const result = {
|
|
220
224
|
root: this.rootPath,
|
|
221
225
|
generatedAt: this.lastScanTime,
|
|
222
226
|
files: this.files,
|
|
223
227
|
stats: this.stats,
|
|
224
228
|
structure: this.structure,
|
|
225
|
-
entryPoints: this.entryPoints
|
|
229
|
+
entryPoints: this.entryPoints,
|
|
226
230
|
};
|
|
227
|
-
|
|
231
|
+
|
|
228
232
|
this.emit('scan:complete', result);
|
|
229
233
|
return result;
|
|
230
|
-
|
|
231
234
|
} catch (error) {
|
|
232
235
|
this.emit('scan:error', error);
|
|
233
236
|
throw error;
|
|
234
237
|
}
|
|
235
238
|
}
|
|
236
|
-
|
|
239
|
+
|
|
237
240
|
/**
|
|
238
241
|
* Scan a directory recursively
|
|
239
242
|
* @param {string} dirPath - Absolute directory path
|
|
@@ -244,7 +247,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
244
247
|
async scanDirectory(dirPath, relativePath, depth) {
|
|
245
248
|
if (depth > this.maxDepth) return;
|
|
246
249
|
if (this.files.length >= this.maxFiles) return;
|
|
247
|
-
|
|
250
|
+
|
|
248
251
|
let entries;
|
|
249
252
|
try {
|
|
250
253
|
entries = await fs.promises.readdir(dirPath, { withFileTypes: true });
|
|
@@ -252,14 +255,14 @@ class RepositoryMap extends EventEmitter {
|
|
|
252
255
|
this.emit('scan:dirError', { path: relativePath, error });
|
|
253
256
|
return;
|
|
254
257
|
}
|
|
255
|
-
|
|
258
|
+
|
|
256
259
|
for (const entry of entries) {
|
|
257
260
|
if (this.files.length >= this.maxFiles) break;
|
|
258
261
|
if (this.shouldIgnore(entry.name)) continue;
|
|
259
|
-
|
|
262
|
+
|
|
260
263
|
const entryRelPath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
|
|
261
264
|
const entryAbsPath = path.join(dirPath, entry.name);
|
|
262
|
-
|
|
265
|
+
|
|
263
266
|
if (entry.isDirectory()) {
|
|
264
267
|
this.stats.totalDirs++;
|
|
265
268
|
this.setStructureNode(entryRelPath, { type: 'directory', children: {} });
|
|
@@ -269,7 +272,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
269
272
|
}
|
|
270
273
|
}
|
|
271
274
|
}
|
|
272
|
-
|
|
275
|
+
|
|
273
276
|
/**
|
|
274
277
|
* Process a single file
|
|
275
278
|
* @param {string} absPath - Absolute file path
|
|
@@ -282,13 +285,13 @@ class RepositoryMap extends EventEmitter {
|
|
|
282
285
|
const ext = path.extname(relPath).toLowerCase();
|
|
283
286
|
const basename = path.basename(relPath);
|
|
284
287
|
const language = LANGUAGE_MAP[ext] || 'unknown';
|
|
285
|
-
|
|
288
|
+
|
|
286
289
|
// Detect entry points
|
|
287
290
|
const isEntry = ENTRY_PATTERNS.some(pattern => pattern.test(basename));
|
|
288
291
|
if (isEntry) {
|
|
289
292
|
this.entryPoints.push(relPath);
|
|
290
293
|
}
|
|
291
|
-
|
|
294
|
+
|
|
292
295
|
// File info
|
|
293
296
|
const fileInfo = {
|
|
294
297
|
path: relPath,
|
|
@@ -298,15 +301,15 @@ class RepositoryMap extends EventEmitter {
|
|
|
298
301
|
mtime: stat.mtime.getTime(),
|
|
299
302
|
language,
|
|
300
303
|
isEntry,
|
|
301
|
-
exports: []
|
|
304
|
+
exports: [],
|
|
302
305
|
};
|
|
303
|
-
|
|
306
|
+
|
|
304
307
|
// Extract exports for JS/TS files
|
|
305
308
|
if (['javascript', 'typescript'].includes(language) && stat.size < 100000) {
|
|
306
309
|
const exports = await this.extractExports(absPath);
|
|
307
310
|
fileInfo.exports = exports;
|
|
308
311
|
}
|
|
309
|
-
|
|
312
|
+
|
|
310
313
|
// Optionally include content
|
|
311
314
|
if (this.includeContent && stat.size <= this.contentMaxSize) {
|
|
312
315
|
try {
|
|
@@ -316,24 +319,23 @@ class RepositoryMap extends EventEmitter {
|
|
|
316
319
|
// Binary or unreadable file
|
|
317
320
|
}
|
|
318
321
|
}
|
|
319
|
-
|
|
322
|
+
|
|
320
323
|
// Update statistics
|
|
321
324
|
this.stats.totalFiles++;
|
|
322
325
|
this.stats.totalSize += stat.size;
|
|
323
326
|
this.stats.byLanguage[language] = (this.stats.byLanguage[language] || 0) + 1;
|
|
324
327
|
this.stats.byExtension[ext] = (this.stats.byExtension[ext] || 0) + 1;
|
|
325
|
-
|
|
328
|
+
|
|
326
329
|
// Add to results
|
|
327
330
|
this.files.push(fileInfo);
|
|
328
331
|
this.setStructureNode(relPath, { type: 'file', language, size: stat.size });
|
|
329
|
-
|
|
332
|
+
|
|
330
333
|
this.emit('file:processed', fileInfo);
|
|
331
|
-
|
|
332
334
|
} catch (error) {
|
|
333
335
|
this.emit('file:error', { path: relPath, error });
|
|
334
336
|
}
|
|
335
337
|
}
|
|
336
|
-
|
|
338
|
+
|
|
337
339
|
/**
|
|
338
340
|
* Extract exported symbols from JS/TS file
|
|
339
341
|
* @param {string} filePath - File path
|
|
@@ -344,39 +346,40 @@ class RepositoryMap extends EventEmitter {
|
|
|
344
346
|
try {
|
|
345
347
|
const content = await fs.promises.readFile(filePath, 'utf-8');
|
|
346
348
|
const exports = [];
|
|
347
|
-
|
|
349
|
+
|
|
348
350
|
// CommonJS exports
|
|
349
351
|
const cjsMatch = content.match(/module\.exports\s*=\s*\{([^}]+)\}/);
|
|
350
352
|
if (cjsMatch) {
|
|
351
353
|
const props = cjsMatch[1].match(/\b([a-zA-Z_$][a-zA-Z0-9_$]*)\b/g);
|
|
352
354
|
if (props) exports.push(...props);
|
|
353
355
|
}
|
|
354
|
-
|
|
356
|
+
|
|
355
357
|
// Named exports
|
|
356
|
-
const namedExports = content.matchAll(
|
|
358
|
+
const namedExports = content.matchAll(
|
|
359
|
+
/export\s+(?:const|let|var|function|class|async function)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g
|
|
360
|
+
);
|
|
357
361
|
for (const match of namedExports) {
|
|
358
362
|
exports.push(match[1]);
|
|
359
363
|
}
|
|
360
|
-
|
|
364
|
+
|
|
361
365
|
// Export statements
|
|
362
366
|
const exportStmts = content.matchAll(/export\s*\{\s*([^}]+)\s*\}/g);
|
|
363
367
|
for (const match of exportStmts) {
|
|
364
368
|
const names = match[1].match(/\b([a-zA-Z_$][a-zA-Z0-9_$]*)\b/g);
|
|
365
369
|
if (names) exports.push(...names);
|
|
366
370
|
}
|
|
367
|
-
|
|
371
|
+
|
|
368
372
|
// Default export
|
|
369
373
|
if (/export\s+default/.test(content)) {
|
|
370
374
|
exports.push('default');
|
|
371
375
|
}
|
|
372
|
-
|
|
376
|
+
|
|
373
377
|
return [...new Set(exports)];
|
|
374
|
-
|
|
375
378
|
} catch {
|
|
376
379
|
return [];
|
|
377
380
|
}
|
|
378
381
|
}
|
|
379
|
-
|
|
382
|
+
|
|
380
383
|
/**
|
|
381
384
|
* Set a node in the structure tree
|
|
382
385
|
* @param {string} relPath - Relative path
|
|
@@ -386,14 +389,14 @@ class RepositoryMap extends EventEmitter {
|
|
|
386
389
|
setStructureNode(relPath, value) {
|
|
387
390
|
const parts = relPath.split('/');
|
|
388
391
|
let current = this.structure;
|
|
389
|
-
|
|
392
|
+
|
|
390
393
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
391
394
|
if (!current[parts[i]]) {
|
|
392
395
|
current[parts[i]] = { type: 'directory', children: {} };
|
|
393
396
|
}
|
|
394
397
|
current = current[parts[i]].children || current[parts[i]];
|
|
395
398
|
}
|
|
396
|
-
|
|
399
|
+
|
|
397
400
|
const lastName = parts[parts.length - 1];
|
|
398
401
|
if (value.type === 'directory') {
|
|
399
402
|
if (!current[lastName]) {
|
|
@@ -403,7 +406,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
403
406
|
current[lastName] = value;
|
|
404
407
|
}
|
|
405
408
|
}
|
|
406
|
-
|
|
409
|
+
|
|
407
410
|
/**
|
|
408
411
|
* Check if a path should be ignored
|
|
409
412
|
* @param {string} name - File/directory name
|
|
@@ -419,7 +422,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
419
422
|
return name === pattern;
|
|
420
423
|
});
|
|
421
424
|
}
|
|
422
|
-
|
|
425
|
+
|
|
423
426
|
/**
|
|
424
427
|
* Get files by language
|
|
425
428
|
* @param {string} language - Programming language
|
|
@@ -428,7 +431,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
428
431
|
getFilesByLanguage(language) {
|
|
429
432
|
return this.files.filter(f => f.language === language);
|
|
430
433
|
}
|
|
431
|
-
|
|
434
|
+
|
|
432
435
|
/**
|
|
433
436
|
* Get files by extension
|
|
434
437
|
* @param {string} extension - File extension (with dot)
|
|
@@ -437,7 +440,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
437
440
|
getFilesByExtension(extension) {
|
|
438
441
|
return this.files.filter(f => f.extension === extension);
|
|
439
442
|
}
|
|
440
|
-
|
|
443
|
+
|
|
441
444
|
/**
|
|
442
445
|
* Get files in directory
|
|
443
446
|
* @param {string} dirPath - Relative directory path
|
|
@@ -447,7 +450,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
447
450
|
const prefix = dirPath.endsWith('/') ? dirPath : `${dirPath}/`;
|
|
448
451
|
return this.files.filter(f => f.path.startsWith(prefix));
|
|
449
452
|
}
|
|
450
|
-
|
|
453
|
+
|
|
451
454
|
/**
|
|
452
455
|
* Search files by pattern
|
|
453
456
|
* @param {string|RegExp} pattern - Search pattern
|
|
@@ -457,7 +460,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
457
460
|
const regex = typeof pattern === 'string' ? new RegExp(pattern) : pattern;
|
|
458
461
|
return this.files.filter(f => regex.test(f.path));
|
|
459
462
|
}
|
|
460
|
-
|
|
463
|
+
|
|
461
464
|
/**
|
|
462
465
|
* Generate tree view string
|
|
463
466
|
* @param {Object} [node] - Starting node
|
|
@@ -467,18 +470,18 @@ class RepositoryMap extends EventEmitter {
|
|
|
467
470
|
*/
|
|
468
471
|
toTreeString(node = this.structure, prefix = '', maxDepth = 5) {
|
|
469
472
|
if (maxDepth <= 0) return prefix + '...\n';
|
|
470
|
-
|
|
473
|
+
|
|
471
474
|
let result = '';
|
|
472
475
|
const entries = Object.entries(node);
|
|
473
|
-
|
|
476
|
+
|
|
474
477
|
for (let i = 0; i < entries.length; i++) {
|
|
475
478
|
const [name, info] = entries[i];
|
|
476
479
|
const isLast = i === entries.length - 1;
|
|
477
480
|
const connector = isLast ? '└── ' : '├── ';
|
|
478
481
|
const childPrefix = isLast ? ' ' : '│ ';
|
|
479
|
-
|
|
482
|
+
|
|
480
483
|
result += `${prefix}${connector}${name}`;
|
|
481
|
-
|
|
484
|
+
|
|
482
485
|
if (info.type === 'file') {
|
|
483
486
|
result += ` (${info.language})\n`;
|
|
484
487
|
} else {
|
|
@@ -488,10 +491,10 @@ class RepositoryMap extends EventEmitter {
|
|
|
488
491
|
}
|
|
489
492
|
}
|
|
490
493
|
}
|
|
491
|
-
|
|
494
|
+
|
|
492
495
|
return result;
|
|
493
496
|
}
|
|
494
|
-
|
|
497
|
+
|
|
495
498
|
/**
|
|
496
499
|
* Generate LLM-optimized context
|
|
497
500
|
* @param {Object} options - Context options
|
|
@@ -502,17 +505,17 @@ class RepositoryMap extends EventEmitter {
|
|
|
502
505
|
*/
|
|
503
506
|
toLLMContext(options = {}) {
|
|
504
507
|
const { maxTokens = 4000, focusPaths = [], languages = [] } = options;
|
|
505
|
-
|
|
508
|
+
|
|
506
509
|
let context = `# Repository Map\n\n`;
|
|
507
510
|
context += `**Root**: ${this.rootPath}\n`;
|
|
508
511
|
context += `**Generated**: ${this.lastScanTime?.toISOString() || 'N/A'}\n\n`;
|
|
509
|
-
|
|
512
|
+
|
|
510
513
|
// Statistics
|
|
511
514
|
context += `## Statistics\n\n`;
|
|
512
515
|
context += `- Total Files: ${this.stats.totalFiles}\n`;
|
|
513
516
|
context += `- Total Directories: ${this.stats.totalDirs}\n`;
|
|
514
517
|
context += `- Total Size: ${this.formatBytes(this.stats.totalSize)}\n\n`;
|
|
515
|
-
|
|
518
|
+
|
|
516
519
|
// Language breakdown
|
|
517
520
|
context += `### Languages\n\n`;
|
|
518
521
|
const langEntries = Object.entries(this.stats.byLanguage)
|
|
@@ -522,7 +525,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
522
525
|
context += `- ${lang}: ${count} files\n`;
|
|
523
526
|
}
|
|
524
527
|
context += '\n';
|
|
525
|
-
|
|
528
|
+
|
|
526
529
|
// Entry points
|
|
527
530
|
if (this.entryPoints.length > 0) {
|
|
528
531
|
context += `## Entry Points\n\n`;
|
|
@@ -531,36 +534,36 @@ class RepositoryMap extends EventEmitter {
|
|
|
531
534
|
}
|
|
532
535
|
context += '\n';
|
|
533
536
|
}
|
|
534
|
-
|
|
537
|
+
|
|
535
538
|
// Structure (token-limited)
|
|
536
539
|
context += `## Structure\n\n\`\`\`\n`;
|
|
537
540
|
const treeStr = this.toTreeString(this.structure, '', 4);
|
|
538
541
|
const truncatedTree = this.truncateToTokens(treeStr, Math.floor(maxTokens * 0.6));
|
|
539
542
|
context += truncatedTree;
|
|
540
543
|
context += `\`\`\`\n\n`;
|
|
541
|
-
|
|
544
|
+
|
|
542
545
|
// Key files
|
|
543
546
|
let keyFiles = this.files;
|
|
544
|
-
|
|
547
|
+
|
|
545
548
|
// Filter by focus paths
|
|
546
549
|
if (focusPaths.length > 0) {
|
|
547
|
-
keyFiles = keyFiles.filter(f =>
|
|
550
|
+
keyFiles = keyFiles.filter(f =>
|
|
548
551
|
focusPaths.some(fp => f.path.startsWith(fp) || f.path.includes(fp))
|
|
549
552
|
);
|
|
550
553
|
}
|
|
551
|
-
|
|
554
|
+
|
|
552
555
|
// Filter by languages
|
|
553
556
|
if (languages.length > 0) {
|
|
554
557
|
keyFiles = keyFiles.filter(f => languages.includes(f.language));
|
|
555
558
|
}
|
|
556
|
-
|
|
559
|
+
|
|
557
560
|
// Sort by importance (entry points first, then by size)
|
|
558
561
|
keyFiles.sort((a, b) => {
|
|
559
562
|
if (a.isEntry && !b.isEntry) return -1;
|
|
560
563
|
if (!a.isEntry && b.isEntry) return 1;
|
|
561
564
|
return b.exports.length - a.exports.length;
|
|
562
565
|
});
|
|
563
|
-
|
|
566
|
+
|
|
564
567
|
// Add key files with exports
|
|
565
568
|
context += `## Key Modules\n\n`;
|
|
566
569
|
for (const file of keyFiles.slice(0, 20)) {
|
|
@@ -574,10 +577,10 @@ class RepositoryMap extends EventEmitter {
|
|
|
574
577
|
context += '\n';
|
|
575
578
|
}
|
|
576
579
|
}
|
|
577
|
-
|
|
580
|
+
|
|
578
581
|
return context;
|
|
579
582
|
}
|
|
580
|
-
|
|
583
|
+
|
|
581
584
|
/**
|
|
582
585
|
* Format bytes to human readable
|
|
583
586
|
* @param {number} bytes - Size in bytes
|
|
@@ -591,7 +594,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
591
594
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
592
595
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
593
596
|
}
|
|
594
|
-
|
|
597
|
+
|
|
595
598
|
/**
|
|
596
599
|
* Truncate string to approximate token count
|
|
597
600
|
* @param {string} str - Input string
|
|
@@ -605,7 +608,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
605
608
|
if (str.length <= maxChars) return str;
|
|
606
609
|
return str.slice(0, maxChars) + '\n... (truncated)\n';
|
|
607
610
|
}
|
|
608
|
-
|
|
611
|
+
|
|
609
612
|
/**
|
|
610
613
|
* Export to JSON
|
|
611
614
|
* @returns {Object}
|
|
@@ -617,10 +620,10 @@ class RepositoryMap extends EventEmitter {
|
|
|
617
620
|
stats: this.stats,
|
|
618
621
|
files: this.files,
|
|
619
622
|
entryPoints: this.entryPoints,
|
|
620
|
-
structure: this.structure
|
|
623
|
+
structure: this.structure,
|
|
621
624
|
};
|
|
622
625
|
}
|
|
623
|
-
|
|
626
|
+
|
|
624
627
|
/**
|
|
625
628
|
* Import from JSON
|
|
626
629
|
* @param {Object} data - JSON data
|
|
@@ -633,7 +636,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
633
636
|
this.entryPoints = data.entryPoints;
|
|
634
637
|
this.structure = data.structure;
|
|
635
638
|
}
|
|
636
|
-
|
|
639
|
+
|
|
637
640
|
/**
|
|
638
641
|
* Get cache key for incremental updates
|
|
639
642
|
* @returns {string}
|
|
@@ -641,7 +644,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
641
644
|
getCacheKey() {
|
|
642
645
|
return `repomap:${this.rootPath}`;
|
|
643
646
|
}
|
|
644
|
-
|
|
647
|
+
|
|
645
648
|
/**
|
|
646
649
|
* Check if file has changed since last scan
|
|
647
650
|
* @param {string} filePath - Relative file path
|
|
@@ -681,5 +684,5 @@ module.exports = {
|
|
|
681
684
|
generateRepositoryMap,
|
|
682
685
|
LANGUAGE_MAP,
|
|
683
686
|
ENTRY_PATTERNS,
|
|
684
|
-
DEFAULT_IGNORE_PATTERNS
|
|
687
|
+
DEFAULT_IGNORE_PATTERNS,
|
|
685
688
|
};
|
|
@@ -87,7 +87,7 @@ const SECURITY_PATTERNS = {
|
|
|
87
87
|
],
|
|
88
88
|
dangerousCommands: [
|
|
89
89
|
{
|
|
90
|
-
pattern: /rm\s+(-rf?|--recursive)\s+[
|
|
90
|
+
pattern: /rm\s+(-rf?|--recursive)\s+[/~]/g,
|
|
91
91
|
name: 'Recursive Delete (root/home)',
|
|
92
92
|
level: RiskLevel.CRITICAL,
|
|
93
93
|
},
|
|
@@ -316,7 +316,7 @@ class SecurityAnalysisResult {
|
|
|
316
316
|
*/
|
|
317
317
|
exceedsThreshold(threshold) {
|
|
318
318
|
const thresholdSeverity = RISK_SEVERITY[threshold] || 2;
|
|
319
|
-
return this.risks.some(
|
|
319
|
+
return this.risks.some(risk => risk.getSeverity() >= thresholdSeverity);
|
|
320
320
|
}
|
|
321
321
|
|
|
322
322
|
/**
|
|
@@ -324,7 +324,7 @@ class SecurityAnalysisResult {
|
|
|
324
324
|
* @param {string} level
|
|
325
325
|
*/
|
|
326
326
|
getRisksByLevel(level) {
|
|
327
|
-
return this.risks.filter(
|
|
327
|
+
return this.risks.filter(risk => risk.level === level);
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
/**
|
|
@@ -332,14 +332,14 @@ class SecurityAnalysisResult {
|
|
|
332
332
|
* @param {string} category
|
|
333
333
|
*/
|
|
334
334
|
getRisksByCategory(category) {
|
|
335
|
-
return this.risks.filter(
|
|
335
|
+
return this.risks.filter(risk => risk.category === category);
|
|
336
336
|
}
|
|
337
337
|
|
|
338
338
|
/**
|
|
339
339
|
* Check if action should be blocked
|
|
340
340
|
*/
|
|
341
341
|
shouldBlock() {
|
|
342
|
-
return this.risks.some(
|
|
342
|
+
return this.risks.some(risk => risk.level === RiskLevel.CRITICAL);
|
|
343
343
|
}
|
|
344
344
|
|
|
345
345
|
/**
|
|
@@ -375,7 +375,7 @@ class SecurityAnalysisResult {
|
|
|
375
375
|
|
|
376
376
|
toJSON() {
|
|
377
377
|
return {
|
|
378
|
-
risks: this.risks.map(
|
|
378
|
+
risks: this.risks.map(r => r.toJSON()),
|
|
379
379
|
summary: this.getSummary(),
|
|
380
380
|
timestamp: this.timestamp.toISOString(),
|
|
381
381
|
};
|
|
@@ -527,7 +527,7 @@ class SecurityAnalyzer {
|
|
|
527
527
|
* @param {string} command
|
|
528
528
|
*/
|
|
529
529
|
isAllowedCommand(command) {
|
|
530
|
-
return this.options.allowedCommands.some(
|
|
530
|
+
return this.options.allowedCommands.some(allowed => command.includes(allowed));
|
|
531
531
|
}
|
|
532
532
|
|
|
533
533
|
/**
|
|
@@ -535,7 +535,7 @@ class SecurityAnalyzer {
|
|
|
535
535
|
* @param {string} filePath
|
|
536
536
|
*/
|
|
537
537
|
shouldIgnorePath(filePath) {
|
|
538
|
-
return this.options.ignorePaths.some(
|
|
538
|
+
return this.options.ignorePaths.some(pattern => {
|
|
539
539
|
if (pattern.includes('*')) {
|
|
540
540
|
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
|
|
541
541
|
return regex.test(filePath);
|
|
@@ -590,7 +590,7 @@ class SecurityAnalyzer {
|
|
|
590
590
|
if (result.shouldBlock()) {
|
|
591
591
|
return {
|
|
592
592
|
allowed: false,
|
|
593
|
-
reason: `Critical security risk detected: ${result.risks.find(
|
|
593
|
+
reason: `Critical security risk detected: ${result.risks.find(r => r.level === RiskLevel.CRITICAL).name}`,
|
|
594
594
|
result,
|
|
595
595
|
};
|
|
596
596
|
}
|
|
@@ -605,7 +605,10 @@ class SecurityAnalyzer {
|
|
|
605
605
|
|
|
606
606
|
return {
|
|
607
607
|
allowed: true,
|
|
608
|
-
reason:
|
|
608
|
+
reason:
|
|
609
|
+
result.risks.length > 0
|
|
610
|
+
? `${result.risks.length} low-level risks detected but within threshold`
|
|
611
|
+
: 'No security risks detected',
|
|
609
612
|
result,
|
|
610
613
|
};
|
|
611
614
|
}
|
|
@@ -632,7 +635,12 @@ class SecurityAnalyzer {
|
|
|
632
635
|
* @returns {string}
|
|
633
636
|
*/
|
|
634
637
|
generateReport(result) {
|
|
635
|
-
const lines = [
|
|
638
|
+
const lines = [
|
|
639
|
+
'# Security Analysis Report',
|
|
640
|
+
'',
|
|
641
|
+
`Generated: ${result.timestamp.toISOString()}`,
|
|
642
|
+
'',
|
|
643
|
+
];
|
|
636
644
|
|
|
637
645
|
const summary = result.getSummary();
|
|
638
646
|
|