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.
- package/README.ja.md +106 -48
- package/README.md +110 -32
- package/bin/musubi-analyze.js +74 -67
- package/bin/musubi-browser.js +27 -26
- package/bin/musubi-change.js +48 -47
- package/bin/musubi-checkpoint.js +10 -7
- package/bin/musubi-convert.js +25 -25
- package/bin/musubi-costs.js +27 -10
- package/bin/musubi-gui.js +52 -46
- package/bin/musubi-init.js +1952 -10
- package/bin/musubi-orchestrate.js +327 -239
- package/bin/musubi-remember.js +69 -56
- package/bin/musubi-resolve.js +53 -45
- package/bin/musubi-trace.js +51 -22
- package/bin/musubi-validate.js +39 -30
- package/bin/musubi-workflow.js +33 -34
- package/bin/musubi.js +39 -2
- package/package.json +1 -1
- package/src/agents/agent-loop.js +94 -95
- package/src/agents/agentic/code-generator.js +119 -109
- package/src/agents/agentic/code-reviewer.js +105 -108
- package/src/agents/agentic/index.js +4 -4
- package/src/agents/browser/action-executor.js +13 -13
- package/src/agents/browser/ai-comparator.js +11 -10
- package/src/agents/browser/context-manager.js +6 -6
- package/src/agents/browser/index.js +5 -5
- package/src/agents/browser/nl-parser.js +31 -46
- package/src/agents/browser/screenshot.js +2 -2
- package/src/agents/browser/test-generator.js +6 -4
- package/src/agents/function-tool.js +71 -65
- package/src/agents/index.js +7 -7
- package/src/agents/schema-generator.js +98 -94
- package/src/analyzers/ast-extractor.js +158 -146
- package/src/analyzers/codegraph-auto-update.js +858 -0
- package/src/analyzers/complexity-analyzer.js +536 -0
- package/src/analyzers/context-optimizer.js +241 -126
- package/src/analyzers/impact-analyzer.js +1 -1
- package/src/analyzers/large-project-analyzer.js +766 -0
- package/src/analyzers/repository-map.js +77 -81
- package/src/analyzers/security-analyzer.js +19 -11
- package/src/analyzers/stuck-detector.js +19 -17
- package/src/converters/index.js +78 -57
- package/src/converters/ir/types.js +12 -12
- package/src/converters/parsers/musubi-parser.js +134 -126
- package/src/converters/parsers/openapi-parser.js +70 -53
- package/src/converters/parsers/speckit-parser.js +239 -175
- package/src/converters/writers/musubi-writer.js +123 -118
- package/src/converters/writers/speckit-writer.js +124 -113
- package/src/generators/rust-migration-generator.js +512 -0
- package/src/gui/public/index.html +1365 -1211
- package/src/gui/server.js +41 -40
- package/src/gui/services/file-watcher.js +23 -8
- package/src/gui/services/project-scanner.js +26 -20
- package/src/gui/services/replanning-service.js +27 -23
- package/src/gui/services/traceability-service.js +8 -8
- package/src/gui/services/workflow-service.js +14 -7
- package/src/index.js +151 -0
- package/src/integrations/cicd.js +90 -104
- package/src/integrations/codegraph-mcp.js +643 -0
- package/src/integrations/documentation.js +142 -103
- package/src/integrations/examples.js +95 -80
- package/src/integrations/github-client.js +17 -17
- package/src/integrations/index.js +5 -5
- package/src/integrations/mcp/index.js +21 -21
- package/src/integrations/mcp/mcp-context-provider.js +76 -78
- package/src/integrations/mcp/mcp-discovery.js +74 -72
- package/src/integrations/mcp/mcp-tool-registry.js +99 -94
- package/src/integrations/mcp-connector.js +70 -66
- package/src/integrations/platforms.js +50 -49
- package/src/integrations/tool-discovery.js +37 -31
- package/src/llm-providers/anthropic-provider.js +11 -11
- package/src/llm-providers/base-provider.js +16 -18
- package/src/llm-providers/copilot-provider.js +22 -19
- package/src/llm-providers/index.js +26 -25
- package/src/llm-providers/ollama-provider.js +11 -11
- package/src/llm-providers/openai-provider.js +12 -12
- package/src/managers/agent-memory.js +36 -24
- package/src/managers/checkpoint-manager.js +4 -8
- package/src/managers/delta-spec.js +19 -19
- package/src/managers/index.js +13 -4
- package/src/managers/memory-condenser.js +35 -45
- package/src/managers/repo-skill-manager.js +57 -31
- package/src/managers/skill-loader.js +25 -22
- package/src/managers/skill-tools.js +36 -72
- package/src/managers/workflow.js +30 -22
- package/src/monitoring/cost-tracker.js +48 -46
- package/src/monitoring/incident-manager.js +116 -106
- package/src/monitoring/index.js +144 -134
- package/src/monitoring/observability.js +75 -62
- package/src/monitoring/quality-dashboard.js +45 -41
- package/src/monitoring/release-manager.js +63 -53
- package/src/orchestration/agent-skill-binding.js +39 -47
- package/src/orchestration/error-handler.js +65 -107
- package/src/orchestration/guardrails/base-guardrail.js +26 -24
- package/src/orchestration/guardrails/guardrail-rules.js +50 -64
- package/src/orchestration/guardrails/index.js +5 -5
- package/src/orchestration/guardrails/input-guardrail.js +58 -45
- package/src/orchestration/guardrails/output-guardrail.js +104 -81
- package/src/orchestration/guardrails/safety-check.js +79 -79
- package/src/orchestration/index.js +38 -55
- package/src/orchestration/mcp-tool-adapters.js +96 -99
- package/src/orchestration/orchestration-engine.js +21 -21
- package/src/orchestration/pattern-registry.js +60 -45
- package/src/orchestration/patterns/auto.js +34 -47
- package/src/orchestration/patterns/group-chat.js +59 -65
- package/src/orchestration/patterns/handoff.js +67 -65
- package/src/orchestration/patterns/human-in-loop.js +51 -72
- package/src/orchestration/patterns/nested.js +25 -40
- package/src/orchestration/patterns/sequential.js +35 -34
- package/src/orchestration/patterns/swarm.js +63 -56
- package/src/orchestration/patterns/triage.js +150 -109
- package/src/orchestration/reasoning/index.js +9 -9
- package/src/orchestration/reasoning/planning-engine.js +143 -140
- package/src/orchestration/reasoning/reasoning-engine.js +206 -144
- package/src/orchestration/reasoning/self-correction.js +121 -128
- package/src/orchestration/replanning/adaptive-goal-modifier.js +107 -112
- package/src/orchestration/replanning/alternative-generator.js +37 -42
- package/src/orchestration/replanning/config.js +63 -59
- package/src/orchestration/replanning/goal-progress-tracker.js +98 -100
- package/src/orchestration/replanning/index.js +24 -20
- package/src/orchestration/replanning/plan-evaluator.js +49 -50
- package/src/orchestration/replanning/plan-monitor.js +32 -28
- package/src/orchestration/replanning/proactive-path-optimizer.js +175 -178
- package/src/orchestration/replanning/replan-history.js +33 -26
- package/src/orchestration/replanning/replanning-engine.js +106 -108
- package/src/orchestration/skill-executor.js +107 -109
- package/src/orchestration/skill-registry.js +85 -89
- package/src/orchestration/workflow-examples.js +228 -231
- package/src/orchestration/workflow-executor.js +65 -68
- package/src/orchestration/workflow-orchestrator.js +72 -73
- package/src/phase4-integration.js +47 -40
- package/src/phase5-integration.js +89 -30
- package/src/reporters/coverage-report.js +82 -30
- package/src/reporters/hierarchical-reporter.js +498 -0
- package/src/reporters/traceability-matrix-report.js +29 -20
- package/src/resolvers/issue-resolver.js +43 -31
- package/src/steering/advanced-validation.js +133 -124
- package/src/steering/auto-updater.js +60 -73
- package/src/steering/index.js +6 -6
- package/src/steering/quality-metrics.js +41 -35
- package/src/steering/steering-auto-update.js +83 -86
- package/src/steering/steering-validator.js +98 -106
- package/src/steering/template-constraints.js +53 -54
- package/src/templates/agents/claude-code/CLAUDE.md +32 -32
- package/src/templates/agents/claude-code/skills/agent-assistant/SKILL.md +13 -5
- package/src/templates/agents/claude-code/skills/ai-ml-engineer/mlops-guide.md +23 -23
- package/src/templates/agents/claude-code/skills/ai-ml-engineer/model-card-template.md +60 -41
- package/src/templates/agents/claude-code/skills/api-designer/api-patterns.md +27 -19
- package/src/templates/agents/claude-code/skills/api-designer/openapi-template.md +11 -7
- package/src/templates/agents/claude-code/skills/bug-hunter/SKILL.md +4 -3
- package/src/templates/agents/claude-code/skills/bug-hunter/root-cause-analysis.md +37 -15
- package/src/templates/agents/claude-code/skills/change-impact-analyzer/dependency-graph-patterns.md +36 -42
- package/src/templates/agents/claude-code/skills/change-impact-analyzer/impact-analysis-template.md +69 -60
- package/src/templates/agents/claude-code/skills/cloud-architect/aws-patterns.md +31 -38
- package/src/templates/agents/claude-code/skills/cloud-architect/azure-patterns.md +28 -23
- package/src/templates/agents/claude-code/skills/code-reviewer/SKILL.md +61 -0
- package/src/templates/agents/claude-code/skills/code-reviewer/best-practices.md +27 -0
- package/src/templates/agents/claude-code/skills/code-reviewer/review-checklist.md +29 -10
- package/src/templates/agents/claude-code/skills/code-reviewer/review-standards.md +29 -24
- package/src/templates/agents/claude-code/skills/constitution-enforcer/SKILL.md +8 -6
- package/src/templates/agents/claude-code/skills/constitution-enforcer/constitutional-articles.md +62 -26
- package/src/templates/agents/claude-code/skills/constitution-enforcer/phase-minus-one-gates.md +35 -16
- package/src/templates/agents/claude-code/skills/database-administrator/backup-recovery.md +27 -17
- package/src/templates/agents/claude-code/skills/database-administrator/tuning-guide.md +25 -20
- package/src/templates/agents/claude-code/skills/database-schema-designer/schema-patterns.md +39 -22
- package/src/templates/agents/claude-code/skills/devops-engineer/ci-cd-templates.md +25 -22
- package/src/templates/agents/claude-code/skills/issue-resolver/SKILL.md +24 -21
- package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +148 -63
- package/src/templates/agents/claude-code/skills/orchestrator/patterns.md +35 -16
- package/src/templates/agents/claude-code/skills/orchestrator/selection-matrix.md +69 -64
- package/src/templates/agents/claude-code/skills/performance-engineer/optimization-playbook.md +47 -47
- package/src/templates/agents/claude-code/skills/performance-optimizer/SKILL.md +69 -0
- package/src/templates/agents/claude-code/skills/performance-optimizer/benchmark-template.md +63 -45
- package/src/templates/agents/claude-code/skills/performance-optimizer/optimization-patterns.md +33 -35
- package/src/templates/agents/claude-code/skills/project-manager/SKILL.md +7 -6
- package/src/templates/agents/claude-code/skills/project-manager/agile-ceremonies.md +47 -28
- package/src/templates/agents/claude-code/skills/project-manager/project-templates.md +94 -78
- package/src/templates/agents/claude-code/skills/quality-assurance/SKILL.md +20 -17
- package/src/templates/agents/claude-code/skills/quality-assurance/qa-plan-template.md +63 -49
- package/src/templates/agents/claude-code/skills/release-coordinator/SKILL.md +5 -5
- package/src/templates/agents/claude-code/skills/release-coordinator/feature-flag-guide.md +30 -26
- package/src/templates/agents/claude-code/skills/release-coordinator/release-plan-template.md +67 -35
- package/src/templates/agents/claude-code/skills/requirements-analyst/ears-format.md +54 -42
- package/src/templates/agents/claude-code/skills/requirements-analyst/validation-rules.md +36 -33
- package/src/templates/agents/claude-code/skills/security-auditor/SKILL.md +77 -19
- package/src/templates/agents/claude-code/skills/security-auditor/audit-checklists.md +24 -24
- package/src/templates/agents/claude-code/skills/security-auditor/owasp-top-10.md +61 -20
- package/src/templates/agents/claude-code/skills/security-auditor/vulnerability-patterns.md +43 -11
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/SKILL.md +1 -0
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/incident-response-template.md +55 -25
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/observability-patterns.md +78 -68
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/slo-sli-guide.md +73 -53
- package/src/templates/agents/claude-code/skills/software-developer/solid-principles.md +83 -37
- package/src/templates/agents/claude-code/skills/software-developer/test-first-workflow.md +38 -31
- package/src/templates/agents/claude-code/skills/steering/SKILL.md +1 -0
- package/src/templates/agents/claude-code/skills/steering/auto-update-rules.md +31 -0
- package/src/templates/agents/claude-code/skills/system-architect/adr-template.md +25 -7
- package/src/templates/agents/claude-code/skills/system-architect/c4-model-guide.md +74 -61
- package/src/templates/agents/claude-code/skills/technical-writer/doc-templates/documentation-templates.md +70 -52
- package/src/templates/agents/claude-code/skills/test-engineer/SKILL.md +2 -0
- package/src/templates/agents/claude-code/skills/test-engineer/ears-test-mapping.md +75 -71
- package/src/templates/agents/claude-code/skills/test-engineer/test-types.md +85 -63
- package/src/templates/agents/claude-code/skills/traceability-auditor/coverage-matrix-template.md +39 -36
- package/src/templates/agents/claude-code/skills/traceability-auditor/gap-detection-rules.md +22 -17
- package/src/templates/agents/claude-code/skills/ui-ux-designer/SKILL.md +1 -0
- package/src/templates/agents/claude-code/skills/ui-ux-designer/accessibility-guidelines.md +49 -75
- package/src/templates/agents/claude-code/skills/ui-ux-designer/design-system-components.md +71 -59
- package/src/templates/agents/codex/AGENTS.md +74 -42
- package/src/templates/agents/cursor/AGENTS.md +74 -42
- package/src/templates/agents/gemini-cli/GEMINI.md +74 -42
- package/src/templates/agents/github-copilot/AGENTS.md +83 -51
- package/src/templates/agents/qwen-code/QWEN.md +74 -42
- package/src/templates/agents/windsurf/AGENTS.md +74 -42
- package/src/templates/architectures/README.md +41 -0
- package/src/templates/architectures/clean-architecture/README.md +113 -0
- package/src/templates/architectures/event-driven/README.md +162 -0
- package/src/templates/architectures/hexagonal/README.md +130 -0
- package/src/templates/index.js +6 -1
- package/src/templates/locale-manager.js +16 -16
- package/src/templates/shared/delta-spec-template.md +20 -13
- package/src/templates/shared/github-actions/musubi-issue-resolver.yml +5 -5
- package/src/templates/shared/github-actions/musubi-security-check.yml +3 -3
- package/src/templates/shared/github-actions/musubi-validate.yml +4 -4
- package/src/templates/shared/steering/structure.md +95 -0
- package/src/templates/skills/browser-agent.md +21 -16
- package/src/templates/skills/web-gui.md +8 -0
- package/src/templates/template-constraints.js +50 -53
- package/src/validators/advanced-validation.js +30 -36
- package/src/validators/constitutional-validator.js +77 -73
- package/src/validators/critic-system.js +49 -59
- package/src/validators/delta-format.js +59 -55
- package/src/validators/traceability-validator.js +7 -11
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 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
|
-
*
|
|
6
|
+
*
|
|
7
7
|
* Part of MUSUBI v5.0.0 - Codebase Intelligence
|
|
8
|
-
*
|
|
8
|
+
*
|
|
9
9
|
* @module analyzers/repository-map
|
|
10
10
|
* @version 1.0.0
|
|
11
|
-
*
|
|
11
|
+
*
|
|
12
12
|
* @traceability
|
|
13
13
|
* - Requirement: REQ-P4-001 (Repository Map Generation)
|
|
14
14
|
* - Design: docs/design/tdd-musubi-v5.0.0.md#2.1
|
|
@@ -92,7 +92,7 @@ const LANGUAGE_MAP = {
|
|
|
92
92
|
'.gql': 'graphql',
|
|
93
93
|
'.dockerfile': 'dockerfile',
|
|
94
94
|
'.vue': 'vue',
|
|
95
|
-
'.svelte': 'svelte'
|
|
95
|
+
'.svelte': 'svelte',
|
|
96
96
|
};
|
|
97
97
|
|
|
98
98
|
/**
|
|
@@ -111,7 +111,7 @@ const ENTRY_PATTERNS = [
|
|
|
111
111
|
/^pyproject\.toml$/,
|
|
112
112
|
/^Gemfile$/,
|
|
113
113
|
/^pom\.xml$/,
|
|
114
|
-
/^build\.gradle(\.kts)
|
|
114
|
+
/^build\.gradle(\.kts)?$/,
|
|
115
115
|
];
|
|
116
116
|
|
|
117
117
|
/**
|
|
@@ -151,7 +151,7 @@ const DEFAULT_IGNORE_PATTERNS = [
|
|
|
151
151
|
'pnpm-lock.yaml',
|
|
152
152
|
'Cargo.lock',
|
|
153
153
|
'Gemfile.lock',
|
|
154
|
-
'poetry.lock'
|
|
154
|
+
'poetry.lock',
|
|
155
155
|
];
|
|
156
156
|
|
|
157
157
|
/**
|
|
@@ -172,41 +172,38 @@ class RepositoryMap extends EventEmitter {
|
|
|
172
172
|
constructor(options = {}) {
|
|
173
173
|
super();
|
|
174
174
|
this.rootPath = options.rootPath || process.cwd();
|
|
175
|
-
this.ignorePatterns = [
|
|
176
|
-
...DEFAULT_IGNORE_PATTERNS,
|
|
177
|
-
...(options.ignorePatterns || [])
|
|
178
|
-
];
|
|
175
|
+
this.ignorePatterns = [...DEFAULT_IGNORE_PATTERNS, ...(options.ignorePatterns || [])];
|
|
179
176
|
this.maxDepth = options.maxDepth ?? 20;
|
|
180
177
|
this.maxFiles = options.maxFiles ?? 10000;
|
|
181
178
|
this.includeContent = options.includeContent ?? false;
|
|
182
179
|
this.contentMaxSize = options.contentMaxSize ?? 10000;
|
|
183
|
-
|
|
180
|
+
|
|
184
181
|
// Cache
|
|
185
182
|
this.cache = new Map();
|
|
186
183
|
this.lastScanTime = null;
|
|
187
|
-
|
|
184
|
+
|
|
188
185
|
// Statistics
|
|
189
186
|
this.stats = {
|
|
190
187
|
totalFiles: 0,
|
|
191
188
|
totalDirs: 0,
|
|
192
189
|
totalSize: 0,
|
|
193
190
|
byLanguage: {},
|
|
194
|
-
byExtension: {}
|
|
191
|
+
byExtension: {},
|
|
195
192
|
};
|
|
196
|
-
|
|
193
|
+
|
|
197
194
|
// Results
|
|
198
195
|
this.files = [];
|
|
199
196
|
this.structure = {};
|
|
200
197
|
this.entryPoints = [];
|
|
201
198
|
}
|
|
202
|
-
|
|
199
|
+
|
|
203
200
|
/**
|
|
204
201
|
* Generate repository map
|
|
205
202
|
* @returns {Promise<RepositoryMap>}
|
|
206
203
|
*/
|
|
207
204
|
async generate() {
|
|
208
205
|
this.emit('scan:start', { rootPath: this.rootPath });
|
|
209
|
-
|
|
206
|
+
|
|
210
207
|
// Reset state
|
|
211
208
|
this.files = [];
|
|
212
209
|
this.structure = {};
|
|
@@ -216,31 +213,30 @@ class RepositoryMap extends EventEmitter {
|
|
|
216
213
|
totalDirs: 0,
|
|
217
214
|
totalSize: 0,
|
|
218
215
|
byLanguage: {},
|
|
219
|
-
byExtension: {}
|
|
216
|
+
byExtension: {},
|
|
220
217
|
};
|
|
221
|
-
|
|
218
|
+
|
|
222
219
|
try {
|
|
223
220
|
await this.scanDirectory(this.rootPath, '', 0);
|
|
224
221
|
this.lastScanTime = new Date();
|
|
225
|
-
|
|
222
|
+
|
|
226
223
|
const result = {
|
|
227
224
|
root: this.rootPath,
|
|
228
225
|
generatedAt: this.lastScanTime,
|
|
229
226
|
files: this.files,
|
|
230
227
|
stats: this.stats,
|
|
231
228
|
structure: this.structure,
|
|
232
|
-
entryPoints: this.entryPoints
|
|
229
|
+
entryPoints: this.entryPoints,
|
|
233
230
|
};
|
|
234
|
-
|
|
231
|
+
|
|
235
232
|
this.emit('scan:complete', result);
|
|
236
233
|
return result;
|
|
237
|
-
|
|
238
234
|
} catch (error) {
|
|
239
235
|
this.emit('scan:error', error);
|
|
240
236
|
throw error;
|
|
241
237
|
}
|
|
242
238
|
}
|
|
243
|
-
|
|
239
|
+
|
|
244
240
|
/**
|
|
245
241
|
* Scan a directory recursively
|
|
246
242
|
* @param {string} dirPath - Absolute directory path
|
|
@@ -251,7 +247,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
251
247
|
async scanDirectory(dirPath, relativePath, depth) {
|
|
252
248
|
if (depth > this.maxDepth) return;
|
|
253
249
|
if (this.files.length >= this.maxFiles) return;
|
|
254
|
-
|
|
250
|
+
|
|
255
251
|
let entries;
|
|
256
252
|
try {
|
|
257
253
|
entries = await fs.promises.readdir(dirPath, { withFileTypes: true });
|
|
@@ -259,14 +255,14 @@ class RepositoryMap extends EventEmitter {
|
|
|
259
255
|
this.emit('scan:dirError', { path: relativePath, error });
|
|
260
256
|
return;
|
|
261
257
|
}
|
|
262
|
-
|
|
258
|
+
|
|
263
259
|
for (const entry of entries) {
|
|
264
260
|
if (this.files.length >= this.maxFiles) break;
|
|
265
261
|
if (this.shouldIgnore(entry.name)) continue;
|
|
266
|
-
|
|
262
|
+
|
|
267
263
|
const entryRelPath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
|
|
268
264
|
const entryAbsPath = path.join(dirPath, entry.name);
|
|
269
|
-
|
|
265
|
+
|
|
270
266
|
if (entry.isDirectory()) {
|
|
271
267
|
this.stats.totalDirs++;
|
|
272
268
|
this.setStructureNode(entryRelPath, { type: 'directory', children: {} });
|
|
@@ -276,7 +272,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
276
272
|
}
|
|
277
273
|
}
|
|
278
274
|
}
|
|
279
|
-
|
|
275
|
+
|
|
280
276
|
/**
|
|
281
277
|
* Process a single file
|
|
282
278
|
* @param {string} absPath - Absolute file path
|
|
@@ -289,13 +285,13 @@ class RepositoryMap extends EventEmitter {
|
|
|
289
285
|
const ext = path.extname(relPath).toLowerCase();
|
|
290
286
|
const basename = path.basename(relPath);
|
|
291
287
|
const language = LANGUAGE_MAP[ext] || 'unknown';
|
|
292
|
-
|
|
288
|
+
|
|
293
289
|
// Detect entry points
|
|
294
290
|
const isEntry = ENTRY_PATTERNS.some(pattern => pattern.test(basename));
|
|
295
291
|
if (isEntry) {
|
|
296
292
|
this.entryPoints.push(relPath);
|
|
297
293
|
}
|
|
298
|
-
|
|
294
|
+
|
|
299
295
|
// File info
|
|
300
296
|
const fileInfo = {
|
|
301
297
|
path: relPath,
|
|
@@ -305,15 +301,15 @@ class RepositoryMap extends EventEmitter {
|
|
|
305
301
|
mtime: stat.mtime.getTime(),
|
|
306
302
|
language,
|
|
307
303
|
isEntry,
|
|
308
|
-
exports: []
|
|
304
|
+
exports: [],
|
|
309
305
|
};
|
|
310
|
-
|
|
306
|
+
|
|
311
307
|
// Extract exports for JS/TS files
|
|
312
308
|
if (['javascript', 'typescript'].includes(language) && stat.size < 100000) {
|
|
313
309
|
const exports = await this.extractExports(absPath);
|
|
314
310
|
fileInfo.exports = exports;
|
|
315
311
|
}
|
|
316
|
-
|
|
312
|
+
|
|
317
313
|
// Optionally include content
|
|
318
314
|
if (this.includeContent && stat.size <= this.contentMaxSize) {
|
|
319
315
|
try {
|
|
@@ -323,24 +319,23 @@ class RepositoryMap extends EventEmitter {
|
|
|
323
319
|
// Binary or unreadable file
|
|
324
320
|
}
|
|
325
321
|
}
|
|
326
|
-
|
|
322
|
+
|
|
327
323
|
// Update statistics
|
|
328
324
|
this.stats.totalFiles++;
|
|
329
325
|
this.stats.totalSize += stat.size;
|
|
330
326
|
this.stats.byLanguage[language] = (this.stats.byLanguage[language] || 0) + 1;
|
|
331
327
|
this.stats.byExtension[ext] = (this.stats.byExtension[ext] || 0) + 1;
|
|
332
|
-
|
|
328
|
+
|
|
333
329
|
// Add to results
|
|
334
330
|
this.files.push(fileInfo);
|
|
335
331
|
this.setStructureNode(relPath, { type: 'file', language, size: stat.size });
|
|
336
|
-
|
|
332
|
+
|
|
337
333
|
this.emit('file:processed', fileInfo);
|
|
338
|
-
|
|
339
334
|
} catch (error) {
|
|
340
335
|
this.emit('file:error', { path: relPath, error });
|
|
341
336
|
}
|
|
342
337
|
}
|
|
343
|
-
|
|
338
|
+
|
|
344
339
|
/**
|
|
345
340
|
* Extract exported symbols from JS/TS file
|
|
346
341
|
* @param {string} filePath - File path
|
|
@@ -351,39 +346,40 @@ class RepositoryMap extends EventEmitter {
|
|
|
351
346
|
try {
|
|
352
347
|
const content = await fs.promises.readFile(filePath, 'utf-8');
|
|
353
348
|
const exports = [];
|
|
354
|
-
|
|
349
|
+
|
|
355
350
|
// CommonJS exports
|
|
356
351
|
const cjsMatch = content.match(/module\.exports\s*=\s*\{([^}]+)\}/);
|
|
357
352
|
if (cjsMatch) {
|
|
358
353
|
const props = cjsMatch[1].match(/\b([a-zA-Z_$][a-zA-Z0-9_$]*)\b/g);
|
|
359
354
|
if (props) exports.push(...props);
|
|
360
355
|
}
|
|
361
|
-
|
|
356
|
+
|
|
362
357
|
// Named exports
|
|
363
|
-
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
|
+
);
|
|
364
361
|
for (const match of namedExports) {
|
|
365
362
|
exports.push(match[1]);
|
|
366
363
|
}
|
|
367
|
-
|
|
364
|
+
|
|
368
365
|
// Export statements
|
|
369
366
|
const exportStmts = content.matchAll(/export\s*\{\s*([^}]+)\s*\}/g);
|
|
370
367
|
for (const match of exportStmts) {
|
|
371
368
|
const names = match[1].match(/\b([a-zA-Z_$][a-zA-Z0-9_$]*)\b/g);
|
|
372
369
|
if (names) exports.push(...names);
|
|
373
370
|
}
|
|
374
|
-
|
|
371
|
+
|
|
375
372
|
// Default export
|
|
376
373
|
if (/export\s+default/.test(content)) {
|
|
377
374
|
exports.push('default');
|
|
378
375
|
}
|
|
379
|
-
|
|
376
|
+
|
|
380
377
|
return [...new Set(exports)];
|
|
381
|
-
|
|
382
378
|
} catch {
|
|
383
379
|
return [];
|
|
384
380
|
}
|
|
385
381
|
}
|
|
386
|
-
|
|
382
|
+
|
|
387
383
|
/**
|
|
388
384
|
* Set a node in the structure tree
|
|
389
385
|
* @param {string} relPath - Relative path
|
|
@@ -393,14 +389,14 @@ class RepositoryMap extends EventEmitter {
|
|
|
393
389
|
setStructureNode(relPath, value) {
|
|
394
390
|
const parts = relPath.split('/');
|
|
395
391
|
let current = this.structure;
|
|
396
|
-
|
|
392
|
+
|
|
397
393
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
398
394
|
if (!current[parts[i]]) {
|
|
399
395
|
current[parts[i]] = { type: 'directory', children: {} };
|
|
400
396
|
}
|
|
401
397
|
current = current[parts[i]].children || current[parts[i]];
|
|
402
398
|
}
|
|
403
|
-
|
|
399
|
+
|
|
404
400
|
const lastName = parts[parts.length - 1];
|
|
405
401
|
if (value.type === 'directory') {
|
|
406
402
|
if (!current[lastName]) {
|
|
@@ -410,7 +406,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
410
406
|
current[lastName] = value;
|
|
411
407
|
}
|
|
412
408
|
}
|
|
413
|
-
|
|
409
|
+
|
|
414
410
|
/**
|
|
415
411
|
* Check if a path should be ignored
|
|
416
412
|
* @param {string} name - File/directory name
|
|
@@ -426,7 +422,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
426
422
|
return name === pattern;
|
|
427
423
|
});
|
|
428
424
|
}
|
|
429
|
-
|
|
425
|
+
|
|
430
426
|
/**
|
|
431
427
|
* Get files by language
|
|
432
428
|
* @param {string} language - Programming language
|
|
@@ -435,7 +431,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
435
431
|
getFilesByLanguage(language) {
|
|
436
432
|
return this.files.filter(f => f.language === language);
|
|
437
433
|
}
|
|
438
|
-
|
|
434
|
+
|
|
439
435
|
/**
|
|
440
436
|
* Get files by extension
|
|
441
437
|
* @param {string} extension - File extension (with dot)
|
|
@@ -444,7 +440,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
444
440
|
getFilesByExtension(extension) {
|
|
445
441
|
return this.files.filter(f => f.extension === extension);
|
|
446
442
|
}
|
|
447
|
-
|
|
443
|
+
|
|
448
444
|
/**
|
|
449
445
|
* Get files in directory
|
|
450
446
|
* @param {string} dirPath - Relative directory path
|
|
@@ -454,7 +450,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
454
450
|
const prefix = dirPath.endsWith('/') ? dirPath : `${dirPath}/`;
|
|
455
451
|
return this.files.filter(f => f.path.startsWith(prefix));
|
|
456
452
|
}
|
|
457
|
-
|
|
453
|
+
|
|
458
454
|
/**
|
|
459
455
|
* Search files by pattern
|
|
460
456
|
* @param {string|RegExp} pattern - Search pattern
|
|
@@ -464,7 +460,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
464
460
|
const regex = typeof pattern === 'string' ? new RegExp(pattern) : pattern;
|
|
465
461
|
return this.files.filter(f => regex.test(f.path));
|
|
466
462
|
}
|
|
467
|
-
|
|
463
|
+
|
|
468
464
|
/**
|
|
469
465
|
* Generate tree view string
|
|
470
466
|
* @param {Object} [node] - Starting node
|
|
@@ -474,18 +470,18 @@ class RepositoryMap extends EventEmitter {
|
|
|
474
470
|
*/
|
|
475
471
|
toTreeString(node = this.structure, prefix = '', maxDepth = 5) {
|
|
476
472
|
if (maxDepth <= 0) return prefix + '...\n';
|
|
477
|
-
|
|
473
|
+
|
|
478
474
|
let result = '';
|
|
479
475
|
const entries = Object.entries(node);
|
|
480
|
-
|
|
476
|
+
|
|
481
477
|
for (let i = 0; i < entries.length; i++) {
|
|
482
478
|
const [name, info] = entries[i];
|
|
483
479
|
const isLast = i === entries.length - 1;
|
|
484
480
|
const connector = isLast ? '└── ' : '├── ';
|
|
485
481
|
const childPrefix = isLast ? ' ' : '│ ';
|
|
486
|
-
|
|
482
|
+
|
|
487
483
|
result += `${prefix}${connector}${name}`;
|
|
488
|
-
|
|
484
|
+
|
|
489
485
|
if (info.type === 'file') {
|
|
490
486
|
result += ` (${info.language})\n`;
|
|
491
487
|
} else {
|
|
@@ -495,10 +491,10 @@ class RepositoryMap extends EventEmitter {
|
|
|
495
491
|
}
|
|
496
492
|
}
|
|
497
493
|
}
|
|
498
|
-
|
|
494
|
+
|
|
499
495
|
return result;
|
|
500
496
|
}
|
|
501
|
-
|
|
497
|
+
|
|
502
498
|
/**
|
|
503
499
|
* Generate LLM-optimized context
|
|
504
500
|
* @param {Object} options - Context options
|
|
@@ -509,17 +505,17 @@ class RepositoryMap extends EventEmitter {
|
|
|
509
505
|
*/
|
|
510
506
|
toLLMContext(options = {}) {
|
|
511
507
|
const { maxTokens = 4000, focusPaths = [], languages = [] } = options;
|
|
512
|
-
|
|
508
|
+
|
|
513
509
|
let context = `# Repository Map\n\n`;
|
|
514
510
|
context += `**Root**: ${this.rootPath}\n`;
|
|
515
511
|
context += `**Generated**: ${this.lastScanTime?.toISOString() || 'N/A'}\n\n`;
|
|
516
|
-
|
|
512
|
+
|
|
517
513
|
// Statistics
|
|
518
514
|
context += `## Statistics\n\n`;
|
|
519
515
|
context += `- Total Files: ${this.stats.totalFiles}\n`;
|
|
520
516
|
context += `- Total Directories: ${this.stats.totalDirs}\n`;
|
|
521
517
|
context += `- Total Size: ${this.formatBytes(this.stats.totalSize)}\n\n`;
|
|
522
|
-
|
|
518
|
+
|
|
523
519
|
// Language breakdown
|
|
524
520
|
context += `### Languages\n\n`;
|
|
525
521
|
const langEntries = Object.entries(this.stats.byLanguage)
|
|
@@ -529,7 +525,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
529
525
|
context += `- ${lang}: ${count} files\n`;
|
|
530
526
|
}
|
|
531
527
|
context += '\n';
|
|
532
|
-
|
|
528
|
+
|
|
533
529
|
// Entry points
|
|
534
530
|
if (this.entryPoints.length > 0) {
|
|
535
531
|
context += `## Entry Points\n\n`;
|
|
@@ -538,36 +534,36 @@ class RepositoryMap extends EventEmitter {
|
|
|
538
534
|
}
|
|
539
535
|
context += '\n';
|
|
540
536
|
}
|
|
541
|
-
|
|
537
|
+
|
|
542
538
|
// Structure (token-limited)
|
|
543
539
|
context += `## Structure\n\n\`\`\`\n`;
|
|
544
540
|
const treeStr = this.toTreeString(this.structure, '', 4);
|
|
545
541
|
const truncatedTree = this.truncateToTokens(treeStr, Math.floor(maxTokens * 0.6));
|
|
546
542
|
context += truncatedTree;
|
|
547
543
|
context += `\`\`\`\n\n`;
|
|
548
|
-
|
|
544
|
+
|
|
549
545
|
// Key files
|
|
550
546
|
let keyFiles = this.files;
|
|
551
|
-
|
|
547
|
+
|
|
552
548
|
// Filter by focus paths
|
|
553
549
|
if (focusPaths.length > 0) {
|
|
554
|
-
keyFiles = keyFiles.filter(f =>
|
|
550
|
+
keyFiles = keyFiles.filter(f =>
|
|
555
551
|
focusPaths.some(fp => f.path.startsWith(fp) || f.path.includes(fp))
|
|
556
552
|
);
|
|
557
553
|
}
|
|
558
|
-
|
|
554
|
+
|
|
559
555
|
// Filter by languages
|
|
560
556
|
if (languages.length > 0) {
|
|
561
557
|
keyFiles = keyFiles.filter(f => languages.includes(f.language));
|
|
562
558
|
}
|
|
563
|
-
|
|
559
|
+
|
|
564
560
|
// Sort by importance (entry points first, then by size)
|
|
565
561
|
keyFiles.sort((a, b) => {
|
|
566
562
|
if (a.isEntry && !b.isEntry) return -1;
|
|
567
563
|
if (!a.isEntry && b.isEntry) return 1;
|
|
568
564
|
return b.exports.length - a.exports.length;
|
|
569
565
|
});
|
|
570
|
-
|
|
566
|
+
|
|
571
567
|
// Add key files with exports
|
|
572
568
|
context += `## Key Modules\n\n`;
|
|
573
569
|
for (const file of keyFiles.slice(0, 20)) {
|
|
@@ -581,10 +577,10 @@ class RepositoryMap extends EventEmitter {
|
|
|
581
577
|
context += '\n';
|
|
582
578
|
}
|
|
583
579
|
}
|
|
584
|
-
|
|
580
|
+
|
|
585
581
|
return context;
|
|
586
582
|
}
|
|
587
|
-
|
|
583
|
+
|
|
588
584
|
/**
|
|
589
585
|
* Format bytes to human readable
|
|
590
586
|
* @param {number} bytes - Size in bytes
|
|
@@ -598,7 +594,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
598
594
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
599
595
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
600
596
|
}
|
|
601
|
-
|
|
597
|
+
|
|
602
598
|
/**
|
|
603
599
|
* Truncate string to approximate token count
|
|
604
600
|
* @param {string} str - Input string
|
|
@@ -612,7 +608,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
612
608
|
if (str.length <= maxChars) return str;
|
|
613
609
|
return str.slice(0, maxChars) + '\n... (truncated)\n';
|
|
614
610
|
}
|
|
615
|
-
|
|
611
|
+
|
|
616
612
|
/**
|
|
617
613
|
* Export to JSON
|
|
618
614
|
* @returns {Object}
|
|
@@ -624,10 +620,10 @@ class RepositoryMap extends EventEmitter {
|
|
|
624
620
|
stats: this.stats,
|
|
625
621
|
files: this.files,
|
|
626
622
|
entryPoints: this.entryPoints,
|
|
627
|
-
structure: this.structure
|
|
623
|
+
structure: this.structure,
|
|
628
624
|
};
|
|
629
625
|
}
|
|
630
|
-
|
|
626
|
+
|
|
631
627
|
/**
|
|
632
628
|
* Import from JSON
|
|
633
629
|
* @param {Object} data - JSON data
|
|
@@ -640,7 +636,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
640
636
|
this.entryPoints = data.entryPoints;
|
|
641
637
|
this.structure = data.structure;
|
|
642
638
|
}
|
|
643
|
-
|
|
639
|
+
|
|
644
640
|
/**
|
|
645
641
|
* Get cache key for incremental updates
|
|
646
642
|
* @returns {string}
|
|
@@ -648,7 +644,7 @@ class RepositoryMap extends EventEmitter {
|
|
|
648
644
|
getCacheKey() {
|
|
649
645
|
return `repomap:${this.rootPath}`;
|
|
650
646
|
}
|
|
651
|
-
|
|
647
|
+
|
|
652
648
|
/**
|
|
653
649
|
* Check if file has changed since last scan
|
|
654
650
|
* @param {string} filePath - Relative file path
|
|
@@ -688,5 +684,5 @@ module.exports = {
|
|
|
688
684
|
generateRepositoryMap,
|
|
689
685
|
LANGUAGE_MAP,
|
|
690
686
|
ENTRY_PATTERNS,
|
|
691
|
-
DEFAULT_IGNORE_PATTERNS
|
|
687
|
+
DEFAULT_IGNORE_PATTERNS,
|
|
692
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
|
|