mcp-adr-analysis-server 2.0.21 → 2.1.0
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.md +94 -839
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/config/ai-config.d.ts.map +1 -1
- package/dist/src/config/ai-config.js +9 -9
- package/dist/src/config/ai-config.js.map +1 -1
- package/dist/src/index.d.ts +15 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1594 -837
- package/dist/src/index.js.map +1 -1
- package/dist/src/prompts/analysis-prompts.d.ts +4 -1
- package/dist/src/prompts/analysis-prompts.d.ts.map +1 -1
- package/dist/src/prompts/analysis-prompts.js +61 -30
- package/dist/src/prompts/analysis-prompts.js.map +1 -1
- package/dist/src/prompts/index.d.ts +17 -1
- package/dist/src/prompts/index.d.ts.map +1 -1
- package/dist/src/prompts/index.js +325 -1
- package/dist/src/prompts/index.js.map +1 -1
- package/dist/src/prompts/rule-generation-prompts.d.ts +5 -1
- package/dist/src/prompts/rule-generation-prompts.d.ts.map +1 -1
- package/dist/src/prompts/rule-generation-prompts.js +5 -1
- package/dist/src/prompts/rule-generation-prompts.js.map +1 -1
- package/dist/src/prompts/security-prompts.d.ts +6 -1
- package/dist/src/prompts/security-prompts.d.ts.map +1 -1
- package/dist/src/prompts/security-prompts.js +6 -1
- package/dist/src/prompts/security-prompts.js.map +1 -1
- package/dist/src/prompts/todo-prompts.d.ts +49 -0
- package/dist/src/prompts/todo-prompts.d.ts.map +1 -0
- package/dist/src/prompts/todo-prompts.js +824 -0
- package/dist/src/prompts/todo-prompts.js.map +1 -0
- package/dist/src/resources/index.d.ts.map +1 -1
- package/dist/src/resources/index.js +9 -7
- package/dist/src/resources/index.js.map +1 -1
- package/dist/src/tools/adr-bootstrap-validation-tool.d.ts +47 -0
- package/dist/src/tools/adr-bootstrap-validation-tool.d.ts.map +1 -0
- package/dist/src/tools/adr-bootstrap-validation-tool.js +979 -0
- package/dist/src/tools/adr-bootstrap-validation-tool.js.map +1 -0
- package/dist/src/tools/adr-suggestion-tool.d.ts +1 -0
- package/dist/src/tools/adr-suggestion-tool.d.ts.map +1 -1
- package/dist/src/tools/adr-suggestion-tool.js +330 -32
- package/dist/src/tools/adr-suggestion-tool.js.map +1 -1
- package/dist/src/tools/content-masking-tool.d.ts +5 -0
- package/dist/src/tools/content-masking-tool.d.ts.map +1 -1
- package/dist/src/tools/content-masking-tool.js +631 -28
- package/dist/src/tools/content-masking-tool.js.map +1 -1
- package/dist/src/tools/deployment-readiness-tool.d.ts +8 -5
- package/dist/src/tools/deployment-readiness-tool.d.ts.map +1 -1
- package/dist/src/tools/deployment-readiness-tool.js +1017 -93
- package/dist/src/tools/deployment-readiness-tool.js.map +1 -1
- package/dist/src/tools/environment-analysis-tool.d.ts +4 -0
- package/dist/src/tools/environment-analysis-tool.d.ts.map +1 -1
- package/dist/src/tools/environment-analysis-tool.js +568 -24
- package/dist/src/tools/environment-analysis-tool.js.map +1 -1
- package/dist/src/tools/interactive-adr-planning-tool.d.ts.map +1 -1
- package/dist/src/tools/interactive-adr-planning-tool.js +270 -108
- package/dist/src/tools/interactive-adr-planning-tool.js.map +1 -1
- package/dist/src/tools/mcp-planning-tool.d.ts.map +1 -1
- package/dist/src/tools/mcp-planning-tool.js +316 -183
- package/dist/src/tools/mcp-planning-tool.js.map +1 -1
- package/dist/src/tools/memory-loading-tool.d.ts +60 -0
- package/dist/src/tools/memory-loading-tool.d.ts.map +1 -0
- package/dist/src/tools/memory-loading-tool.js +457 -0
- package/dist/src/tools/memory-loading-tool.js.map +1 -0
- package/dist/src/tools/research-integration-tool.d.ts +4 -1
- package/dist/src/tools/research-integration-tool.d.ts.map +1 -1
- package/dist/src/tools/research-integration-tool.js +5 -1
- package/dist/src/tools/research-integration-tool.js.map +1 -1
- package/dist/src/tools/review-existing-adrs-tool.d.ts +19 -0
- package/dist/src/tools/review-existing-adrs-tool.d.ts.map +1 -0
- package/dist/src/tools/review-existing-adrs-tool.js +1204 -0
- package/dist/src/tools/review-existing-adrs-tool.js.map +1 -0
- package/dist/src/tools/rule-generation-tool.d.ts +2 -0
- package/dist/src/tools/rule-generation-tool.d.ts.map +1 -1
- package/dist/src/tools/rule-generation-tool.js +274 -33
- package/dist/src/tools/rule-generation-tool.js.map +1 -1
- package/dist/src/tools/smart-git-push-tool-v2.d.ts +4 -0
- package/dist/src/tools/smart-git-push-tool-v2.d.ts.map +1 -1
- package/dist/src/tools/smart-git-push-tool-v2.js +423 -229
- package/dist/src/tools/smart-git-push-tool-v2.js.map +1 -1
- package/dist/src/tools/smart-git-push-tool.d.ts +3 -0
- package/dist/src/tools/smart-git-push-tool.d.ts.map +1 -1
- package/dist/src/tools/smart-git-push-tool.js +241 -235
- package/dist/src/tools/smart-git-push-tool.js.map +1 -1
- package/dist/src/tools/tool-chain-orchestrator.d.ts +2 -2
- package/dist/src/tools/tool-chain-orchestrator.d.ts.map +1 -1
- package/dist/src/tools/tool-chain-orchestrator.js +152 -93
- package/dist/src/tools/tool-chain-orchestrator.js.map +1 -1
- package/dist/src/tools/troubleshoot-guided-workflow-tool.d.ts +22 -13
- package/dist/src/tools/troubleshoot-guided-workflow-tool.d.ts.map +1 -1
- package/dist/src/tools/troubleshoot-guided-workflow-tool.js +660 -28
- package/dist/src/tools/troubleshoot-guided-workflow-tool.js.map +1 -1
- package/dist/src/types/ape-framework.d.ts +93 -0
- package/dist/src/types/ape-framework.d.ts.map +1 -1
- package/dist/src/types/ape-framework.js +10 -10
- package/dist/src/types/ape-framework.js.map +1 -1
- package/dist/src/types/enhanced-errors.d.ts +173 -0
- package/dist/src/types/enhanced-errors.d.ts.map +1 -0
- package/dist/src/types/enhanced-errors.js +668 -0
- package/dist/src/types/enhanced-errors.js.map +1 -0
- package/dist/src/types/index.d.ts +129 -3
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/index.js +15 -0
- package/dist/src/types/index.js.map +1 -1
- package/dist/src/types/knowledge-generation.d.ts +78 -0
- package/dist/src/types/knowledge-generation.d.ts.map +1 -1
- package/dist/src/types/knowledge-generation.js +10 -6
- package/dist/src/types/knowledge-generation.js.map +1 -1
- package/dist/src/types/knowledge-graph-schemas.d.ts +92 -0
- package/dist/src/types/knowledge-graph-schemas.d.ts.map +1 -1
- package/dist/src/types/knowledge-graph-schemas.js +45 -17
- package/dist/src/types/knowledge-graph-schemas.js.map +1 -1
- package/dist/src/types/memory-entities.d.ts +7209 -0
- package/dist/src/types/memory-entities.d.ts.map +1 -0
- package/dist/src/types/memory-entities.js +395 -0
- package/dist/src/types/memory-entities.js.map +1 -0
- package/dist/src/types/reflexion-framework.d.ts +44 -0
- package/dist/src/types/reflexion-framework.d.ts.map +1 -1
- package/dist/src/types/reflexion-framework.js +7 -7
- package/dist/src/types/reflexion-framework.js.map +1 -1
- package/dist/src/types/todo-json-schemas.d.ts +7 -653
- package/dist/src/types/todo-json-schemas.d.ts.map +1 -1
- package/dist/src/types/todo-json-schemas.js +6 -110
- package/dist/src/types/todo-json-schemas.js.map +1 -1
- package/dist/src/types/tool-arguments.d.ts +224 -0
- package/dist/src/types/tool-arguments.d.ts.map +1 -0
- package/dist/src/types/tool-arguments.js +6 -0
- package/dist/src/types/tool-arguments.js.map +1 -0
- package/dist/src/utils/actual-file-operations.d.ts.map +1 -1
- package/dist/src/utils/actual-file-operations.js +25 -9
- package/dist/src/utils/actual-file-operations.js.map +1 -1
- package/dist/src/utils/adr-discovery.d.ts +33 -1
- package/dist/src/utils/adr-discovery.d.ts.map +1 -1
- package/dist/src/utils/adr-discovery.js +14 -9
- package/dist/src/utils/adr-discovery.js.map +1 -1
- package/dist/src/utils/adr-suggestions.d.ts +72 -0
- package/dist/src/utils/adr-suggestions.d.ts.map +1 -1
- package/dist/src/utils/adr-suggestions.js +6 -0
- package/dist/src/utils/adr-suggestions.js.map +1 -1
- package/dist/src/utils/cache.d.ts.map +1 -1
- package/dist/src/utils/cache.js +22 -19
- package/dist/src/utils/cache.js.map +1 -1
- package/dist/src/utils/chain-of-thought-template.d.ts +13 -1
- package/dist/src/utils/chain-of-thought-template.d.ts.map +1 -1
- package/dist/src/utils/chain-of-thought-template.js +31 -22
- package/dist/src/utils/chain-of-thought-template.js.map +1 -1
- package/dist/src/utils/config.d.ts.map +1 -1
- package/dist/src/utils/config.js +1 -0
- package/dist/src/utils/config.js.map +1 -1
- package/dist/src/utils/directory-compat.js +1 -1
- package/dist/src/utils/directory-compat.js.map +1 -1
- package/dist/src/utils/enhanced-logging.d.ts +195 -0
- package/dist/src/utils/enhanced-logging.d.ts.map +1 -0
- package/dist/src/utils/enhanced-logging.js +449 -0
- package/dist/src/utils/enhanced-logging.js.map +1 -0
- package/dist/src/utils/file-system.d.ts +182 -34
- package/dist/src/utils/file-system.d.ts.map +1 -1
- package/dist/src/utils/file-system.js +648 -774
- package/dist/src/utils/file-system.js.map +1 -1
- package/dist/src/utils/gitleaks-detector.d.ts +50 -0
- package/dist/src/utils/gitleaks-detector.d.ts.map +1 -0
- package/dist/src/utils/gitleaks-detector.js +334 -0
- package/dist/src/utils/gitleaks-detector.js.map +1 -0
- package/dist/src/utils/interactive-approval.d.ts +1 -1
- package/dist/src/utils/interactive-approval.d.ts.map +1 -1
- package/dist/src/utils/interactive-approval.js +22 -17
- package/dist/src/utils/interactive-approval.js.map +1 -1
- package/dist/src/utils/json-safe.d.ts.map +1 -1
- package/dist/src/utils/json-safe.js +9 -6
- package/dist/src/utils/json-safe.js.map +1 -1
- package/dist/src/utils/knowledge-graph-manager.d.ts +37 -1
- package/dist/src/utils/knowledge-graph-manager.d.ts.map +1 -1
- package/dist/src/utils/knowledge-graph-manager.js +315 -70
- package/dist/src/utils/knowledge-graph-manager.js.map +1 -1
- package/dist/src/utils/location-filter.d.ts +35 -10
- package/dist/src/utils/location-filter.d.ts.map +1 -1
- package/dist/src/utils/location-filter.js +360 -94
- package/dist/src/utils/location-filter.js.map +1 -1
- package/dist/src/utils/mcp-response-validator.d.ts.map +1 -1
- package/dist/src/utils/mcp-response-validator.js +34 -19
- package/dist/src/utils/mcp-response-validator.js.map +1 -1
- package/dist/src/utils/memory-entity-manager.d.ts +134 -0
- package/dist/src/utils/memory-entity-manager.d.ts.map +1 -0
- package/dist/src/utils/memory-entity-manager.js +851 -0
- package/dist/src/utils/memory-entity-manager.js.map +1 -0
- package/dist/src/utils/memory-health-scoring.d.ts +125 -0
- package/dist/src/utils/memory-health-scoring.d.ts.map +1 -0
- package/dist/src/utils/memory-health-scoring.js +277 -0
- package/dist/src/utils/memory-health-scoring.js.map +1 -0
- package/dist/src/utils/memory-migration-manager.d.ts +93 -0
- package/dist/src/utils/memory-migration-manager.d.ts.map +1 -0
- package/dist/src/utils/memory-migration-manager.js +842 -0
- package/dist/src/utils/memory-migration-manager.js.map +1 -0
- package/dist/src/utils/memory-relationship-mapper.d.ts +87 -0
- package/dist/src/utils/memory-relationship-mapper.d.ts.map +1 -0
- package/dist/src/utils/memory-relationship-mapper.js +482 -0
- package/dist/src/utils/memory-relationship-mapper.js.map +1 -0
- package/dist/src/utils/memory-rollback-manager.d.ts +105 -0
- package/dist/src/utils/memory-rollback-manager.d.ts.map +1 -0
- package/dist/src/utils/memory-rollback-manager.js +575 -0
- package/dist/src/utils/memory-rollback-manager.js.map +1 -0
- package/dist/src/utils/memory-transformation.d.ts +59 -0
- package/dist/src/utils/memory-transformation.d.ts.map +1 -0
- package/dist/src/utils/memory-transformation.js +742 -0
- package/dist/src/utils/memory-transformation.js.map +1 -0
- package/dist/src/utils/output-masking.d.ts +12 -0
- package/dist/src/utils/output-masking.d.ts.map +1 -1
- package/dist/src/utils/output-masking.js +5 -0
- package/dist/src/utils/output-masking.js.map +1 -1
- package/dist/src/utils/prompt-execution.d.ts.map +1 -1
- package/dist/src/utils/prompt-execution.js +72 -51
- package/dist/src/utils/prompt-execution.js.map +1 -1
- package/dist/src/utils/release-readiness-detector.d.ts.map +1 -1
- package/dist/src/utils/release-readiness-detector.js +43 -42
- package/dist/src/utils/release-readiness-detector.js.map +1 -1
- package/dist/src/utils/research-integration.d.ts.map +1 -1
- package/dist/src/utils/research-integration.js +73 -44
- package/dist/src/utils/research-integration.js.map +1 -1
- package/dist/src/utils/research-questions.d.ts.map +1 -1
- package/dist/src/utils/research-questions.js +40 -14
- package/dist/src/utils/research-questions.js.map +1 -1
- package/dist/src/utils/ripgrep-wrapper.d.ts +84 -0
- package/dist/src/utils/ripgrep-wrapper.d.ts.map +1 -0
- package/dist/src/utils/ripgrep-wrapper.js +255 -0
- package/dist/src/utils/ripgrep-wrapper.js.map +1 -0
- package/dist/src/utils/rule-generation.d.ts.map +1 -1
- package/dist/src/utils/rule-generation.js +26 -15
- package/dist/src/utils/rule-generation.js.map +1 -1
- package/dist/src/utils/todo-file-watcher.d.ts +11 -23
- package/dist/src/utils/todo-file-watcher.d.ts.map +1 -1
- package/dist/src/utils/todo-file-watcher.js +18 -156
- package/dist/src/utils/todo-file-watcher.js.map +1 -1
- package/dist/src/utils/tree-sitter-analyzer.d.ts +185 -0
- package/dist/src/utils/tree-sitter-analyzer.d.ts.map +1 -0
- package/dist/src/utils/tree-sitter-analyzer.js +885 -0
- package/dist/src/utils/tree-sitter-analyzer.js.map +1 -0
- package/package.json +51 -10
- package/dist/src/tools/smart-score-tool.d.ts +0 -173
- package/dist/src/tools/smart-score-tool.d.ts.map +0 -1
- package/dist/src/tools/smart-score-tool.js +0 -681
- package/dist/src/tools/smart-score-tool.js.map +0 -1
- package/dist/src/tools/todo-management-tool-v2.d.ts +0 -368
- package/dist/src/tools/todo-management-tool-v2.d.ts.map +0 -1
- package/dist/src/tools/todo-management-tool-v2.js +0 -922
- package/dist/src/tools/todo-management-tool-v2.js.map +0 -1
- package/dist/src/utils/enhanced-sensitive-detector.d.ts +0 -61
- package/dist/src/utils/enhanced-sensitive-detector.d.ts.map +0 -1
- package/dist/src/utils/enhanced-sensitive-detector.js +0 -437
- package/dist/src/utils/enhanced-sensitive-detector.js.map +0 -1
- package/dist/src/utils/project-health-scoring.d.ts +0 -177
- package/dist/src/utils/project-health-scoring.d.ts.map +0 -1
- package/dist/src/utils/project-health-scoring.js +0 -370
- package/dist/src/utils/project-health-scoring.js.map +0 -1
- package/dist/src/utils/todo-json-manager.d.ts +0 -71
- package/dist/src/utils/todo-json-manager.d.ts.map +0 -1
- package/dist/src/utils/todo-json-manager.js +0 -396
- package/dist/src/utils/todo-json-manager.js.map +0 -1
- package/dist/src/utils/todo-markdown-converter.d.ts +0 -16
- package/dist/src/utils/todo-markdown-converter.d.ts.map +0 -1
- package/dist/src/utils/todo-markdown-converter.js +0 -362
- package/dist/src/utils/todo-markdown-converter.js.map +0 -1
- package/dist/src/utils/todo-migration.d.ts +0 -47
- package/dist/src/utils/todo-migration.d.ts.map +0 -1
- package/dist/src/utils/todo-migration.js +0 -261
- package/dist/src/utils/todo-migration.js.map +0 -1
|
@@ -1,838 +1,712 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* File system utilities for MCP ADR Analysis Server
|
|
3
|
-
*
|
|
4
|
-
* Enhanced with
|
|
3
|
+
* Uses fast-glob for efficient file discovery with .gitignore support
|
|
4
|
+
* Enhanced with Smart Code Linking capabilities
|
|
5
5
|
*/
|
|
6
|
-
import
|
|
6
|
+
import fg from 'fast-glob';
|
|
7
|
+
import * as fs from 'fs/promises';
|
|
8
|
+
import * as path from 'path';
|
|
7
9
|
import { FileSystemError } from '../types/index.js';
|
|
8
|
-
import { enhancePromptWithCoT, COT_PATTERNS } from './chain-of-thought-template.js';
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
+
* Find files using fast-glob with .gitignore support
|
|
12
|
+
*
|
|
13
|
+
* @param projectPath - Path to the project to search
|
|
14
|
+
* @param patterns - Glob patterns to match files
|
|
15
|
+
* @param options - Options for file discovery
|
|
16
|
+
* @returns Promise resolving to file discovery results
|
|
11
17
|
*/
|
|
12
|
-
export async function
|
|
18
|
+
export async function findFiles(projectPath, patterns, options = {}) {
|
|
13
19
|
try {
|
|
14
|
-
console.error(`[DEBUG] Starting prompt generation for project path: ${projectPath}`);
|
|
15
|
-
// Pure prompt-driven approach - delegate all path resolution and validation to AI
|
|
16
20
|
const safeProjectPath = projectPath || '.';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
21
|
+
// Ensure patterns are properly formatted
|
|
22
|
+
const normalizedPatterns = patterns.map(pattern => {
|
|
23
|
+
// If pattern doesn't start with !, make it relative to the project path
|
|
24
|
+
if (!pattern.startsWith('!') && !path.isAbsolute(pattern)) {
|
|
25
|
+
return pattern;
|
|
26
|
+
}
|
|
27
|
+
return pattern;
|
|
28
|
+
});
|
|
29
|
+
// Use fast-glob to find files
|
|
30
|
+
const entries = await fg(normalizedPatterns, {
|
|
31
|
+
cwd: safeProjectPath,
|
|
32
|
+
dot: true,
|
|
33
|
+
absolute: false,
|
|
34
|
+
stats: true,
|
|
35
|
+
followSymbolicLinks: false,
|
|
36
|
+
onlyFiles: true,
|
|
37
|
+
// Exclude common non-source directories by default
|
|
38
|
+
// fast-glob automatically respects .gitignore when these patterns are used
|
|
39
|
+
ignore: [
|
|
40
|
+
'**/node_modules/**',
|
|
41
|
+
'**/.git/**',
|
|
42
|
+
'**/dist/**',
|
|
43
|
+
'**/build/**',
|
|
44
|
+
'**/coverage/**',
|
|
45
|
+
'**/.mcp-adr-cache/**',
|
|
46
|
+
'**/*.log',
|
|
47
|
+
'**/.DS_Store',
|
|
48
|
+
'**/Thumbs.db',
|
|
49
|
+
],
|
|
50
|
+
});
|
|
51
|
+
// Apply limit if specified
|
|
52
|
+
const limitedEntries = options.limit ? entries.slice(0, options.limit) : entries;
|
|
53
|
+
// Convert glob results to FileInfo format
|
|
54
|
+
const files = await Promise.all(limitedEntries.map(async (entry) => {
|
|
55
|
+
const filePath = typeof entry === 'string' ? entry : entry.path;
|
|
56
|
+
const fullPath = path.join(safeProjectPath, filePath);
|
|
57
|
+
let stats;
|
|
58
|
+
if (typeof entry === 'object' && entry.stats) {
|
|
59
|
+
stats = entry.stats;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
stats = await fs.stat(fullPath);
|
|
63
|
+
}
|
|
64
|
+
const fileInfo = {
|
|
65
|
+
path: filePath,
|
|
66
|
+
name: path.basename(filePath),
|
|
67
|
+
extension: path.extname(filePath),
|
|
68
|
+
size: stats.size,
|
|
69
|
+
directory: path.dirname(filePath),
|
|
70
|
+
};
|
|
71
|
+
// Include content if requested
|
|
72
|
+
if (options.includeContent) {
|
|
73
|
+
try {
|
|
74
|
+
fileInfo.content = await fs.readFile(fullPath, 'utf-8');
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.warn(`Could not read content of ${fullPath}:`, error);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return fileInfo;
|
|
81
|
+
}));
|
|
82
|
+
return {
|
|
83
|
+
files,
|
|
84
|
+
totalFiles: files.length,
|
|
85
|
+
searchPatterns: patterns,
|
|
86
|
+
searchPath: safeProjectPath,
|
|
87
|
+
};
|
|
82
88
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
{
|
|
86
|
-
"path": "relative/path/to/file",
|
|
87
|
-
"name": "filename.ext",
|
|
88
|
-
"extension": ".ext",
|
|
89
|
-
"directory": "relative/path/to",
|
|
90
|
-
"category": "source|config|documentation|test"
|
|
89
|
+
catch (error) {
|
|
90
|
+
throw new FileSystemError(`Failed to find files: ${error instanceof Error ? error.message : String(error)}`, { projectPath, patterns });
|
|
91
91
|
}
|
|
92
|
-
],
|
|
93
|
-
"directories": ["dir1", "dir2/subdir"],
|
|
94
|
-
"summary": "Brief project description and key findings"
|
|
95
92
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Analyze project structure to detect technologies and patterns
|
|
95
|
+
* Uses direct file analysis instead of AI prompts
|
|
96
|
+
*
|
|
97
|
+
* @param projectPath - Path to the project to analyze
|
|
98
|
+
* @returns Promise resolving to project analysis results
|
|
99
|
+
*/
|
|
100
|
+
export async function analyzeProjectStructure(projectPath) {
|
|
101
|
+
try {
|
|
102
|
+
const safeProjectPath = path.resolve(projectPath || '.');
|
|
103
|
+
// Define patterns for different file types
|
|
104
|
+
const filePatterns = [
|
|
105
|
+
'**/*.{js,jsx,ts,tsx,py,java,cs,go,rs,rb,php,swift,kt,scala,c,cpp,h,hpp}',
|
|
106
|
+
'**/package.json',
|
|
107
|
+
'**/requirements.txt',
|
|
108
|
+
'**/Gemfile',
|
|
109
|
+
'**/Cargo.toml',
|
|
110
|
+
'**/go.mod',
|
|
111
|
+
'**/pom.xml',
|
|
112
|
+
'**/*.{yml,yaml}',
|
|
113
|
+
'**/Dockerfile',
|
|
114
|
+
'**/*.tf',
|
|
115
|
+
'**/.env*',
|
|
116
|
+
'**/README*',
|
|
117
|
+
];
|
|
118
|
+
// Find all relevant files
|
|
119
|
+
const { files } = await findFiles(safeProjectPath, filePatterns, {
|
|
120
|
+
includeContent: false,
|
|
121
|
+
limit: 1000, // Limit for performance
|
|
122
|
+
});
|
|
123
|
+
// Analyze technologies based on files found
|
|
124
|
+
const technologies = await detectTechnologies(safeProjectPath, files);
|
|
125
|
+
// Detect architectural patterns
|
|
126
|
+
const patterns = detectArchitecturalPatterns(files);
|
|
127
|
+
// Count directories
|
|
128
|
+
const directories = [...new Set(files.map(f => f.directory))];
|
|
129
|
+
// Generate summary
|
|
130
|
+
const summary = generateProjectSummary(files, technologies, patterns);
|
|
123
131
|
return {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
132
|
+
rootPath: safeProjectPath,
|
|
133
|
+
files,
|
|
134
|
+
technologies,
|
|
135
|
+
patterns,
|
|
136
|
+
summary,
|
|
137
|
+
totalFiles: files.length,
|
|
138
|
+
totalDirectories: directories.length,
|
|
131
139
|
};
|
|
132
140
|
}
|
|
133
141
|
catch (error) {
|
|
134
|
-
|
|
135
|
-
if (error instanceof FileSystemError) {
|
|
136
|
-
throw error;
|
|
137
|
-
}
|
|
138
|
-
throw new FileSystemError(`Failed to generate project analysis prompt: ${error instanceof Error ? error.message : String(error)}`, { projectPath, absoluteProjectPath: 'resolved-by-ai-agent' });
|
|
142
|
+
throw new FileSystemError(`Failed to analyze project structure: ${error instanceof Error ? error.message : String(error)}`, { projectPath });
|
|
139
143
|
}
|
|
140
144
|
}
|
|
141
145
|
/**
|
|
142
|
-
*
|
|
146
|
+
* Read file content safely
|
|
147
|
+
*
|
|
148
|
+
* @param filePath - Path to the file to read
|
|
149
|
+
* @returns Promise resolving to file content and metadata
|
|
143
150
|
*/
|
|
144
151
|
export async function readFileContent(filePath) {
|
|
145
152
|
try {
|
|
146
|
-
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
## File Reading Requirements
|
|
163
|
-
|
|
164
|
-
1. **Safe File Access**: Read the file content using appropriate file system tools
|
|
165
|
-
2. **Encoding Handling**: Read the file as UTF-8 text content
|
|
166
|
-
3. **Error Handling**: Provide clear error messages for:
|
|
167
|
-
- File not found
|
|
168
|
-
- Permission denied
|
|
169
|
-
- Invalid file path
|
|
170
|
-
- Binary files or unsupported formats
|
|
171
|
-
|
|
172
|
-
## Expected Output Format
|
|
173
|
-
|
|
174
|
-
\`\`\`json
|
|
175
|
-
{
|
|
176
|
-
"success": true,
|
|
177
|
-
"filePath": "${safeFilePath}",
|
|
178
|
-
"content": "file content as string",
|
|
179
|
-
"metadata": {
|
|
180
|
-
"size": <file_size_in_bytes>,
|
|
181
|
-
"encoding": "utf-8",
|
|
182
|
-
"lastModified": "ISO_timestamp"
|
|
183
|
-
},
|
|
184
|
-
"security": {
|
|
185
|
-
"pathValidated": true,
|
|
186
|
-
"accessAuthorized": true,
|
|
187
|
-
"systemDirectoryCheck": "passed"
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
\`\`\`
|
|
191
|
-
|
|
192
|
-
## Error Response Format (if file cannot be read)
|
|
193
|
-
|
|
194
|
-
\`\`\`json
|
|
195
|
-
{
|
|
196
|
-
"success": false,
|
|
197
|
-
"filePath": "${safeFilePath}",
|
|
198
|
-
"error": "detailed error message",
|
|
199
|
-
"errorType": "FILE_NOT_FOUND|PERMISSION_DENIED|INVALID_PATH|SECURITY_VIOLATION",
|
|
200
|
-
"security": {
|
|
201
|
-
"pathValidated": boolean,
|
|
202
|
-
"accessAuthorized": boolean,
|
|
203
|
-
"systemDirectoryCheck": "passed|failed",
|
|
204
|
-
"rejectionReason": "specific reason if access denied"
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
\`\`\`
|
|
208
|
-
`;
|
|
209
|
-
const instructions = `
|
|
210
|
-
## Implementation Steps
|
|
211
|
-
|
|
212
|
-
1. **Security Validation**:
|
|
213
|
-
- Validate the file path: ${safeFilePath}
|
|
214
|
-
- Ensure it's not a system directory or sensitive location
|
|
215
|
-
- Check for path traversal attempts (../, .\\, etc.)
|
|
216
|
-
|
|
217
|
-
2. **File Access**:
|
|
218
|
-
- Check if the file exists and is readable
|
|
219
|
-
- Verify file permissions allow reading
|
|
220
|
-
- Handle symbolic links safely
|
|
221
|
-
|
|
222
|
-
3. **Content Reading**:
|
|
223
|
-
- Read file content as UTF-8 text
|
|
224
|
-
- Handle large files appropriately
|
|
225
|
-
- Detect and handle binary files
|
|
226
|
-
|
|
227
|
-
4. **Response Generation**:
|
|
228
|
-
- Format response according to the specified JSON structure
|
|
229
|
-
- Include comprehensive metadata and security validation results
|
|
230
|
-
- Provide detailed error information if reading fails
|
|
231
|
-
|
|
232
|
-
## Safety Checks
|
|
233
|
-
|
|
234
|
-
- ✅ Path security validation delegated to AI agent
|
|
235
|
-
- ✅ System directory protection enabled
|
|
236
|
-
- ✅ File access permission validation required
|
|
237
|
-
- ✅ Comprehensive error handling specified
|
|
238
|
-
|
|
239
|
-
## Context Information
|
|
240
|
-
|
|
241
|
-
- **File Path**: ${safeFilePath}
|
|
242
|
-
- **Operation**: Safe file content reading
|
|
243
|
-
- **Security Level**: High (with path validation and access control)
|
|
244
|
-
`;
|
|
153
|
+
const resolvedPath = path.resolve(filePath);
|
|
154
|
+
// Check if file exists
|
|
155
|
+
try {
|
|
156
|
+
await fs.access(resolvedPath);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return {
|
|
160
|
+
success: false,
|
|
161
|
+
filePath,
|
|
162
|
+
error: 'File not found',
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
// Read file content
|
|
166
|
+
const content = await fs.readFile(resolvedPath, 'utf-8');
|
|
167
|
+
const stats = await fs.stat(resolvedPath);
|
|
245
168
|
return {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
169
|
+
success: true,
|
|
170
|
+
filePath,
|
|
171
|
+
content,
|
|
172
|
+
metadata: {
|
|
173
|
+
size: stats.size,
|
|
174
|
+
encoding: 'utf-8',
|
|
175
|
+
lastModified: stats.mtime.toISOString(),
|
|
176
|
+
},
|
|
254
177
|
};
|
|
255
178
|
}
|
|
256
179
|
catch (error) {
|
|
257
|
-
|
|
180
|
+
return {
|
|
181
|
+
success: false,
|
|
182
|
+
filePath,
|
|
183
|
+
error: error instanceof Error ? error.message : String(error),
|
|
184
|
+
};
|
|
258
185
|
}
|
|
259
186
|
}
|
|
260
187
|
/**
|
|
261
|
-
*
|
|
188
|
+
* Check if a file exists
|
|
189
|
+
*
|
|
190
|
+
* @param filePath - Path to check
|
|
191
|
+
* @returns Promise resolving to existence check result
|
|
262
192
|
*/
|
|
263
|
-
export async function
|
|
193
|
+
export async function fileExists(filePath) {
|
|
264
194
|
try {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
195
|
+
const resolvedPath = path.resolve(filePath);
|
|
196
|
+
try {
|
|
197
|
+
const stats = await fs.stat(resolvedPath);
|
|
198
|
+
let type = 'file';
|
|
199
|
+
if (stats.isDirectory())
|
|
200
|
+
type = 'directory';
|
|
201
|
+
else if (stats.isSymbolicLink())
|
|
202
|
+
type = 'symlink';
|
|
203
|
+
return {
|
|
204
|
+
success: true,
|
|
205
|
+
filePath,
|
|
206
|
+
exists: true,
|
|
207
|
+
metadata: {
|
|
208
|
+
type,
|
|
209
|
+
accessible: true,
|
|
210
|
+
lastChecked: new Date().toISOString(),
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
return {
|
|
216
|
+
success: true,
|
|
217
|
+
filePath,
|
|
218
|
+
exists: false,
|
|
219
|
+
metadata: {
|
|
220
|
+
type: null,
|
|
221
|
+
accessible: false,
|
|
222
|
+
lastChecked: new Date().toISOString(),
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
throw new FileSystemError(`Failed to check file existence: ${error instanceof Error ? error.message : String(error)}`, { filePath });
|
|
296
229
|
}
|
|
297
|
-
],
|
|
298
|
-
"totalFiles": <number>,
|
|
299
|
-
"searchPatterns": ${JSON.stringify(patterns)},
|
|
300
|
-
"searchPath": "<resolved-absolute-path>"
|
|
301
230
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
231
|
+
/**
|
|
232
|
+
* Ensure a directory exists, creating it if necessary
|
|
233
|
+
*
|
|
234
|
+
* @param dirPath - Path to the directory
|
|
235
|
+
* @returns Promise resolving when directory is ensured
|
|
236
|
+
*/
|
|
237
|
+
export async function ensureDirectory(dirPath) {
|
|
238
|
+
try {
|
|
239
|
+
const resolvedPath = path.resolve(dirPath);
|
|
240
|
+
try {
|
|
241
|
+
await fs.access(resolvedPath);
|
|
242
|
+
return {
|
|
243
|
+
success: true,
|
|
244
|
+
dirPath,
|
|
245
|
+
created: false,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
// Directory doesn't exist, create it
|
|
250
|
+
await fs.mkdir(resolvedPath, { recursive: true });
|
|
251
|
+
return {
|
|
252
|
+
success: true,
|
|
253
|
+
dirPath,
|
|
254
|
+
created: true,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
327
257
|
}
|
|
328
258
|
catch (error) {
|
|
329
|
-
|
|
259
|
+
return {
|
|
260
|
+
success: false,
|
|
261
|
+
dirPath,
|
|
262
|
+
created: false,
|
|
263
|
+
error: error instanceof Error ? error.message : String(error),
|
|
264
|
+
};
|
|
330
265
|
}
|
|
331
266
|
}
|
|
332
267
|
/**
|
|
333
|
-
*
|
|
268
|
+
* Write content to a file, creating directories as needed
|
|
269
|
+
*
|
|
270
|
+
* @param filePath - Path to the file
|
|
271
|
+
* @param content - Content to write
|
|
272
|
+
* @returns Promise resolving when file is written
|
|
334
273
|
*/
|
|
335
|
-
export async function
|
|
274
|
+
export async function writeFile(filePath, content) {
|
|
336
275
|
try {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
Please check if the file exists: **${safeFilePath}**
|
|
344
|
-
|
|
345
|
-
## Path Security Validation
|
|
346
|
-
|
|
347
|
-
The AI agent must:
|
|
348
|
-
1. **Validate file path security**: Ensure the path is not a system directory (/, /usr, /bin, /etc, /var, /tmp, etc.)
|
|
349
|
-
2. **Resolve relative paths safely**: Handle relative paths from the current working directory
|
|
350
|
-
3. **Prevent unauthorized access**: Reject attempts to check sensitive system files or directories outside the project scope
|
|
351
|
-
4. **Validate path format**: Check for valid file path format and reject malicious patterns
|
|
352
|
-
|
|
353
|
-
## File Existence Check Requirements
|
|
354
|
-
|
|
355
|
-
1. **Safe Path Resolution**: Resolve the file path using appropriate file system tools
|
|
356
|
-
2. **Access Validation**: Check if the file exists and is accessible
|
|
357
|
-
3. **Security Compliance**: Ensure the check doesn't expose sensitive information
|
|
358
|
-
4. **Error Handling**: Handle various scenarios:
|
|
359
|
-
- File exists and is accessible
|
|
360
|
-
- File does not exist
|
|
361
|
-
- Path is invalid or malformed
|
|
362
|
-
- Permission denied
|
|
363
|
-
- Path points to a directory instead of a file
|
|
364
|
-
|
|
365
|
-
## Expected Output Format
|
|
366
|
-
|
|
367
|
-
\`\`\`json
|
|
368
|
-
{
|
|
369
|
-
"success": true,
|
|
370
|
-
"filePath": "${safeFilePath}",
|
|
371
|
-
"exists": true,
|
|
372
|
-
"metadata": {
|
|
373
|
-
"type": "file|directory|symlink",
|
|
374
|
-
"accessible": true,
|
|
375
|
-
"lastChecked": "ISO_timestamp"
|
|
376
|
-
},
|
|
377
|
-
"security": {
|
|
378
|
-
"pathValidated": true,
|
|
379
|
-
"accessAuthorized": true,
|
|
380
|
-
"systemDirectoryCheck": "passed"
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
\`\`\`
|
|
384
|
-
|
|
385
|
-
## Response Format for Non-Existent File
|
|
386
|
-
|
|
387
|
-
\`\`\`json
|
|
388
|
-
{
|
|
389
|
-
"success": true,
|
|
390
|
-
"filePath": "${safeFilePath}",
|
|
391
|
-
"exists": false,
|
|
392
|
-
"metadata": {
|
|
393
|
-
"type": null,
|
|
394
|
-
"accessible": false,
|
|
395
|
-
"lastChecked": "ISO_timestamp"
|
|
396
|
-
},
|
|
397
|
-
"security": {
|
|
398
|
-
"pathValidated": true,
|
|
399
|
-
"accessAuthorized": true,
|
|
400
|
-
"systemDirectoryCheck": "passed"
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
\`\`\`
|
|
404
|
-
|
|
405
|
-
## Error Response Format (for security violations or invalid paths)
|
|
406
|
-
|
|
407
|
-
\`\`\`json
|
|
408
|
-
{
|
|
409
|
-
"success": false,
|
|
410
|
-
"filePath": "${safeFilePath}",
|
|
411
|
-
"exists": false,
|
|
412
|
-
"error": "detailed error message",
|
|
413
|
-
"errorType": "INVALID_PATH|SECURITY_VIOLATION|PERMISSION_DENIED|MALFORMED_PATH",
|
|
414
|
-
"security": {
|
|
415
|
-
"pathValidated": boolean,
|
|
416
|
-
"accessAuthorized": boolean,
|
|
417
|
-
"systemDirectoryCheck": "passed|failed",
|
|
418
|
-
"rejectionReason": "specific reason if access denied"
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
\`\`\`
|
|
422
|
-
`;
|
|
423
|
-
const instructions = `
|
|
424
|
-
## Implementation Steps
|
|
425
|
-
|
|
426
|
-
1. **Security Validation**:
|
|
427
|
-
- Validate the file path: ${safeFilePath}
|
|
428
|
-
- Ensure it's not a system directory or sensitive location
|
|
429
|
-
- Check for path traversal attempts (../, .\\, etc.)
|
|
430
|
-
- Validate path format and reject malicious patterns
|
|
431
|
-
|
|
432
|
-
2. **Path Resolution**:
|
|
433
|
-
- Resolve relative paths from the current working directory
|
|
434
|
-
- Handle symbolic links appropriately
|
|
435
|
-
- Normalize path separators for the operating system
|
|
436
|
-
|
|
437
|
-
3. **Existence Check**:
|
|
438
|
-
- Check if the file exists at the resolved path
|
|
439
|
-
- Determine if it's a file, directory, or symbolic link
|
|
440
|
-
- Verify accessibility without reading content
|
|
441
|
-
|
|
442
|
-
4. **Response Generation**:
|
|
443
|
-
- Format response according to the specified JSON structure
|
|
444
|
-
- Include comprehensive metadata and security validation results
|
|
445
|
-
- Provide clear boolean result in the "exists" field
|
|
446
|
-
- Include detailed error information if check fails
|
|
447
|
-
|
|
448
|
-
## Safety Checks
|
|
449
|
-
|
|
450
|
-
- ✅ Path security validation delegated to AI agent
|
|
451
|
-
- ✅ System directory protection enabled
|
|
452
|
-
- ✅ File access permission validation required
|
|
453
|
-
- ✅ Comprehensive error handling specified
|
|
454
|
-
- ✅ Boolean result format clearly defined
|
|
455
|
-
|
|
456
|
-
## Context Information
|
|
457
|
-
|
|
458
|
-
- **File Path**: ${safeFilePath}
|
|
459
|
-
- **Operation**: Safe file existence check
|
|
460
|
-
- **Security Level**: High (with path validation and access control)
|
|
461
|
-
- **Expected Result**: Boolean existence status with metadata
|
|
462
|
-
`;
|
|
276
|
+
const resolvedPath = path.resolve(filePath);
|
|
277
|
+
const dir = path.dirname(resolvedPath);
|
|
278
|
+
// Ensure directory exists
|
|
279
|
+
const dirResult = await ensureDirectory(dir);
|
|
280
|
+
// Write file
|
|
281
|
+
await fs.writeFile(resolvedPath, content, 'utf-8');
|
|
463
282
|
return {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
resultType: 'boolean'
|
|
472
|
-
}
|
|
283
|
+
success: true,
|
|
284
|
+
filePath,
|
|
285
|
+
written: true,
|
|
286
|
+
metadata: {
|
|
287
|
+
size: Buffer.byteLength(content, 'utf-8'),
|
|
288
|
+
directoryCreated: dirResult.created,
|
|
289
|
+
},
|
|
473
290
|
};
|
|
474
291
|
}
|
|
475
292
|
catch (error) {
|
|
476
|
-
|
|
293
|
+
return {
|
|
294
|
+
success: false,
|
|
295
|
+
filePath,
|
|
296
|
+
written: false,
|
|
297
|
+
error: error instanceof Error ? error.message : String(error),
|
|
298
|
+
};
|
|
477
299
|
}
|
|
478
300
|
}
|
|
479
301
|
/**
|
|
480
|
-
*
|
|
302
|
+
* Find related code files for an ADR using Smart Code Linking
|
|
303
|
+
* Uses AI for keyword extraction and ripgrep for efficient searching
|
|
304
|
+
*
|
|
305
|
+
* @param adrPath - Path to the ADR file
|
|
306
|
+
* @param adrContent - Content of the ADR
|
|
307
|
+
* @param projectPath - Root path of the project
|
|
308
|
+
* @param options - Additional options for search customization
|
|
309
|
+
* @returns Promise resolving to related code files
|
|
481
310
|
*/
|
|
482
|
-
export async function
|
|
311
|
+
export async function findRelatedCode(adrPath, adrContent, projectPath, options = {}) {
|
|
312
|
+
const { useAI = true, useRipgrep = true, maxFiles = 50, includeContent = false } = options;
|
|
483
313
|
try {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
2. **Resolve relative paths safely**: Handle relative paths from the current working directory
|
|
497
|
-
3. **Prevent unauthorized access**: Reject attempts to create directories in sensitive system locations
|
|
498
|
-
4. **Validate path format**: Check for valid directory path format and reject malicious patterns
|
|
499
|
-
|
|
500
|
-
## Directory Creation Requirements
|
|
501
|
-
|
|
502
|
-
1. **Safe Path Resolution**: Resolve the directory path using appropriate file system tools
|
|
503
|
-
2. **Recursive Creation**: Create parent directories if they don't exist (recursive: true)
|
|
504
|
-
3. **Permission Handling**: Ensure appropriate permissions for directory creation
|
|
505
|
-
4. **Error Handling**: Handle various scenarios:
|
|
506
|
-
- Directory already exists (success)
|
|
507
|
-
- Parent directories don't exist (create them)
|
|
508
|
-
- Permission denied
|
|
509
|
-
- Invalid path or malformed directory name
|
|
510
|
-
- Disk space issues
|
|
511
|
-
|
|
512
|
-
## Action Confirmation Required
|
|
513
|
-
|
|
514
|
-
Before creating any directories, confirm the action with the user:
|
|
515
|
-
|
|
516
|
-
**Action**: Create directory structure
|
|
517
|
-
**Target**: ${safeDirPath}
|
|
518
|
-
**Type**: Directory creation (recursive)
|
|
519
|
-
**Impact**: Creates directory and any missing parent directories
|
|
520
|
-
|
|
521
|
-
## Expected Output Format
|
|
522
|
-
|
|
523
|
-
\`\`\`json
|
|
524
|
-
{
|
|
525
|
-
"success": true,
|
|
526
|
-
"dirPath": "${safeDirPath}",
|
|
527
|
-
"created": true,
|
|
528
|
-
"metadata": {
|
|
529
|
-
"type": "directory",
|
|
530
|
-
"recursive": true,
|
|
531
|
-
"permissions": "755",
|
|
532
|
-
"createdAt": "ISO_timestamp"
|
|
533
|
-
},
|
|
534
|
-
"security": {
|
|
535
|
-
"pathValidated": true,
|
|
536
|
-
"accessAuthorized": true,
|
|
537
|
-
"systemDirectoryCheck": "passed"
|
|
538
|
-
},
|
|
539
|
-
"confirmation": {
|
|
540
|
-
"actionConfirmed": true,
|
|
541
|
-
"userApproved": true
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
\`\`\`
|
|
545
|
-
|
|
546
|
-
## Response Format for Existing Directory
|
|
547
|
-
|
|
548
|
-
\`\`\`json
|
|
549
|
-
{
|
|
550
|
-
"success": true,
|
|
551
|
-
"dirPath": "${safeDirPath}",
|
|
552
|
-
"created": false,
|
|
553
|
-
"metadata": {
|
|
554
|
-
"type": "directory",
|
|
555
|
-
"alreadyExists": true,
|
|
556
|
-
"permissions": "existing_permissions",
|
|
557
|
-
"lastModified": "ISO_timestamp"
|
|
558
|
-
},
|
|
559
|
-
"security": {
|
|
560
|
-
"pathValidated": true,
|
|
561
|
-
"accessAuthorized": true,
|
|
562
|
-
"systemDirectoryCheck": "passed"
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
\`\`\`
|
|
566
|
-
|
|
567
|
-
## Error Response Format
|
|
568
|
-
|
|
569
|
-
\`\`\`json
|
|
570
|
-
{
|
|
571
|
-
"success": false,
|
|
572
|
-
"dirPath": "${safeDirPath}",
|
|
573
|
-
"created": false,
|
|
574
|
-
"error": "detailed error message",
|
|
575
|
-
"errorType": "PERMISSION_DENIED|INVALID_PATH|SECURITY_VIOLATION|DISK_FULL",
|
|
576
|
-
"security": {
|
|
577
|
-
"pathValidated": boolean,
|
|
578
|
-
"accessAuthorized": boolean,
|
|
579
|
-
"systemDirectoryCheck": "passed|failed",
|
|
580
|
-
"rejectionReason": "specific reason if access denied"
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
\`\`\`
|
|
584
|
-
`;
|
|
585
|
-
const instructions = `
|
|
586
|
-
## Implementation Steps
|
|
587
|
-
|
|
588
|
-
1. **Security Validation**:
|
|
589
|
-
- Validate the directory path: ${safeDirPath}
|
|
590
|
-
- Ensure it's not a system directory or sensitive location
|
|
591
|
-
- Check for path traversal attempts (../, .\\, etc.)
|
|
592
|
-
- Validate path format and reject malicious patterns
|
|
593
|
-
|
|
594
|
-
2. **Action Confirmation**:
|
|
595
|
-
- Present the directory creation action to the user for confirmation
|
|
596
|
-
- Include details about what directories will be created
|
|
597
|
-
- Wait for user approval before proceeding
|
|
598
|
-
|
|
599
|
-
3. **Directory Creation**:
|
|
600
|
-
- Check if the directory already exists
|
|
601
|
-
- Create parent directories recursively if needed
|
|
602
|
-
- Set appropriate permissions (755 or system default)
|
|
603
|
-
- Handle symbolic links appropriately
|
|
604
|
-
|
|
605
|
-
4. **Response Generation**:
|
|
606
|
-
- Format response according to the specified JSON structure
|
|
607
|
-
- Include comprehensive metadata and security validation results
|
|
608
|
-
- Provide detailed error information if creation fails
|
|
609
|
-
- Include confirmation status
|
|
610
|
-
|
|
611
|
-
## Safety Checks
|
|
612
|
-
|
|
613
|
-
- ✅ Path security validation delegated to AI agent
|
|
614
|
-
- ✅ System directory protection enabled
|
|
615
|
-
- ✅ Action confirmation required before directory creation
|
|
616
|
-
- ✅ Recursive directory creation with permission handling
|
|
617
|
-
- ✅ Comprehensive error handling specified
|
|
618
|
-
|
|
619
|
-
## Context Information
|
|
620
|
-
|
|
621
|
-
- **Directory Path**: ${safeDirPath}
|
|
622
|
-
- **Operation**: Safe directory creation (recursive)
|
|
623
|
-
- **Security Level**: High (with path validation and access control)
|
|
624
|
-
- **Confirmation Required**: Yes (action confirmation pattern)
|
|
625
|
-
`;
|
|
626
|
-
return {
|
|
627
|
-
prompt,
|
|
628
|
-
instructions,
|
|
629
|
-
context: {
|
|
630
|
-
dirPath: safeDirPath,
|
|
631
|
-
operation: 'directory_creation',
|
|
632
|
-
securityLevel: 'high',
|
|
633
|
-
expectedFormat: 'json',
|
|
634
|
-
confirmationRequired: true,
|
|
635
|
-
recursive: true
|
|
314
|
+
let keywords = [];
|
|
315
|
+
let confidence = 0.5;
|
|
316
|
+
// Step 1: Extract keywords using AI if available
|
|
317
|
+
if (useAI) {
|
|
318
|
+
try {
|
|
319
|
+
keywords = await extractKeywordsWithAI(adrContent);
|
|
320
|
+
confidence = 0.9; // Higher confidence with AI
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
console.warn('AI keyword extraction failed, falling back to regex:', error);
|
|
324
|
+
keywords = extractKeywordsFromContent(adrContent);
|
|
325
|
+
confidence = 0.6;
|
|
636
326
|
}
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
keywords = extractKeywordsFromContent(adrContent);
|
|
330
|
+
confidence = 0.6;
|
|
331
|
+
}
|
|
332
|
+
// Step 2: Search for files using ripgrep or fast-glob
|
|
333
|
+
let relatedFiles = [];
|
|
334
|
+
if (useRipgrep) {
|
|
335
|
+
try {
|
|
336
|
+
const ripgrepModule = await import('./ripgrep-wrapper.js');
|
|
337
|
+
const { searchMultiplePatterns, isRipgrepAvailable } = ripgrepModule;
|
|
338
|
+
if (await isRipgrepAvailable()) {
|
|
339
|
+
// Use ripgrep for efficient searching
|
|
340
|
+
const searchResults = await searchMultiplePatterns(keywords.slice(0, 10), // Limit keywords for performance
|
|
341
|
+
projectPath, {
|
|
342
|
+
fileType: 'ts,js,py,java,go,rs,cs',
|
|
343
|
+
maxMatches: 5,
|
|
344
|
+
caseInsensitive: true,
|
|
345
|
+
});
|
|
346
|
+
// Collect unique files from all pattern matches
|
|
347
|
+
const uniqueFiles = new Set();
|
|
348
|
+
searchResults.forEach(files => {
|
|
349
|
+
files.forEach(file => uniqueFiles.add(file));
|
|
350
|
+
});
|
|
351
|
+
// Convert file paths to FileInfo objects
|
|
352
|
+
const filePromises = Array.from(uniqueFiles)
|
|
353
|
+
.slice(0, maxFiles)
|
|
354
|
+
.map(async (filePath) => {
|
|
355
|
+
const fullPath = path.resolve(projectPath, filePath);
|
|
356
|
+
const stats = await fs.stat(fullPath);
|
|
357
|
+
const fileInfo = {
|
|
358
|
+
path: filePath,
|
|
359
|
+
name: path.basename(filePath),
|
|
360
|
+
extension: path.extname(filePath),
|
|
361
|
+
size: stats.size,
|
|
362
|
+
directory: path.dirname(filePath),
|
|
363
|
+
};
|
|
364
|
+
if (includeContent) {
|
|
365
|
+
try {
|
|
366
|
+
fileInfo.content = await fs.readFile(fullPath, 'utf-8');
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
369
|
+
// Ignore read errors
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return fileInfo;
|
|
373
|
+
});
|
|
374
|
+
relatedFiles = await Promise.all(filePromises);
|
|
375
|
+
confidence = Math.min(0.95, confidence + 0.1); // Boost confidence with ripgrep
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
throw new Error('Ripgrep not available');
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
catch (error) {
|
|
382
|
+
console.warn('Ripgrep search failed, falling back to fast-glob:', error);
|
|
383
|
+
// Fall through to fast-glob search
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
// Fallback to fast-glob if ripgrep didn't work or wasn't used
|
|
387
|
+
if (relatedFiles.length === 0) {
|
|
388
|
+
// Build search patterns based on keywords
|
|
389
|
+
const searchPatterns = keywords.slice(0, 10).map(keyword => `**/*${keyword}*`);
|
|
390
|
+
// Find files matching the patterns
|
|
391
|
+
const { files } = await findFiles(projectPath, searchPatterns, {
|
|
392
|
+
includeContent,
|
|
393
|
+
limit: maxFiles,
|
|
394
|
+
});
|
|
395
|
+
// Filter to source code files only
|
|
396
|
+
const sourceExtensions = ['.ts', '.js', '.tsx', '.jsx', '.py', '.java', '.cs', '.go', '.rs'];
|
|
397
|
+
relatedFiles = files.filter(file => sourceExtensions.includes(file.extension));
|
|
398
|
+
}
|
|
399
|
+
// Step 3: Score and rank files by relevance
|
|
400
|
+
const scoredFiles = relatedFiles.map(file => {
|
|
401
|
+
let score = 0;
|
|
402
|
+
const fileName = file.name.toLowerCase();
|
|
403
|
+
const filePath = file.path.toLowerCase();
|
|
404
|
+
// Score based on keyword matches in file name/path
|
|
405
|
+
keywords.forEach(keyword => {
|
|
406
|
+
const lowerKeyword = keyword.toLowerCase();
|
|
407
|
+
if (fileName.includes(lowerKeyword))
|
|
408
|
+
score += 3;
|
|
409
|
+
if (filePath.includes(lowerKeyword))
|
|
410
|
+
score += 1;
|
|
411
|
+
});
|
|
412
|
+
return { file, score };
|
|
413
|
+
});
|
|
414
|
+
// Sort by score and take top results
|
|
415
|
+
scoredFiles.sort((a, b) => b.score - a.score);
|
|
416
|
+
const topFiles = scoredFiles.slice(0, maxFiles).map(item => item.file);
|
|
417
|
+
// Adjust confidence based on results
|
|
418
|
+
if (topFiles.length === 0) {
|
|
419
|
+
confidence = 0.2;
|
|
420
|
+
}
|
|
421
|
+
else if (topFiles.length < 5) {
|
|
422
|
+
confidence = Math.min(confidence, 0.5);
|
|
423
|
+
}
|
|
424
|
+
return {
|
|
425
|
+
adrPath,
|
|
426
|
+
relatedFiles: topFiles,
|
|
427
|
+
keywords,
|
|
428
|
+
searchPatterns: keywords.map(k => `**/*${k}*`),
|
|
429
|
+
confidence,
|
|
637
430
|
};
|
|
638
431
|
}
|
|
639
432
|
catch (error) {
|
|
640
|
-
throw new FileSystemError(`Failed to
|
|
433
|
+
throw new FileSystemError(`Failed to find related code: ${error instanceof Error ? error.message : String(error)}`, { adrPath });
|
|
641
434
|
}
|
|
642
435
|
}
|
|
643
436
|
/**
|
|
644
|
-
*
|
|
437
|
+
* Extract keywords from ADR content using AI
|
|
645
438
|
*/
|
|
646
|
-
|
|
439
|
+
async function extractKeywordsWithAI(content) {
|
|
647
440
|
try {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
const
|
|
651
|
-
const
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
1. **Directory Creation**: Ensure parent directory exists (create if needed)
|
|
677
|
-
2. **Safe File Writing**: Write content using appropriate file system tools
|
|
678
|
-
3. **Encoding Handling**: Write the file as UTF-8 text content
|
|
679
|
-
4. **Atomic Operations**: Use atomic write operations when possible
|
|
680
|
-
5. **Permission Handling**: Set appropriate file permissions
|
|
681
|
-
6. **Backup Consideration**: Consider backing up existing files if they exist
|
|
682
|
-
|
|
683
|
-
## Action Confirmation Required
|
|
684
|
-
|
|
685
|
-
Before writing any files, confirm the action with the user:
|
|
686
|
-
|
|
687
|
-
**Action**: Write file content
|
|
688
|
-
**Target**: ${safeFilePath}
|
|
689
|
-
**Content Size**: ${safeContent.length} characters
|
|
690
|
-
**Content Preview**: ${contentPreview}
|
|
691
|
-
**Type**: File creation/modification
|
|
692
|
-
**Impact**: Creates file and any missing parent directories
|
|
693
|
-
|
|
694
|
-
## Expected Output Format
|
|
695
|
-
|
|
696
|
-
\`\`\`json
|
|
697
|
-
{
|
|
698
|
-
"success": true,
|
|
699
|
-
"filePath": "${safeFilePath}",
|
|
700
|
-
"written": true,
|
|
701
|
-
"metadata": {
|
|
702
|
-
"type": "file",
|
|
703
|
-
"size": ${safeContent.length},
|
|
704
|
-
"encoding": "utf-8",
|
|
705
|
-
"permissions": "644",
|
|
706
|
-
"createdAt": "ISO_timestamp",
|
|
707
|
-
"directoryCreated": boolean
|
|
708
|
-
},
|
|
709
|
-
"security": {
|
|
710
|
-
"pathValidated": true,
|
|
711
|
-
"contentValidated": true,
|
|
712
|
-
"accessAuthorized": true,
|
|
713
|
-
"systemDirectoryCheck": "passed"
|
|
714
|
-
},
|
|
715
|
-
"confirmation": {
|
|
716
|
-
"actionConfirmed": true,
|
|
717
|
-
"userApproved": true
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
\`\`\`
|
|
721
|
-
|
|
722
|
-
## Response Format for File Update
|
|
723
|
-
|
|
724
|
-
\`\`\`json
|
|
725
|
-
{
|
|
726
|
-
"success": true,
|
|
727
|
-
"filePath": "${safeFilePath}",
|
|
728
|
-
"written": true,
|
|
729
|
-
"metadata": {
|
|
730
|
-
"type": "file",
|
|
731
|
-
"size": ${safeContent.length},
|
|
732
|
-
"encoding": "utf-8",
|
|
733
|
-
"permissions": "644",
|
|
734
|
-
"updatedAt": "ISO_timestamp",
|
|
735
|
-
"previousSize": <previous_file_size>,
|
|
736
|
-
"directoryCreated": false
|
|
737
|
-
},
|
|
738
|
-
"security": {
|
|
739
|
-
"pathValidated": true,
|
|
740
|
-
"contentValidated": true,
|
|
741
|
-
"accessAuthorized": true,
|
|
742
|
-
"systemDirectoryCheck": "passed"
|
|
743
|
-
},
|
|
744
|
-
"confirmation": {
|
|
745
|
-
"actionConfirmed": true,
|
|
746
|
-
"userApproved": true
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
\`\`\`
|
|
750
|
-
|
|
751
|
-
## Error Response Format
|
|
752
|
-
|
|
753
|
-
\`\`\`json
|
|
754
|
-
{
|
|
755
|
-
"success": false,
|
|
756
|
-
"filePath": "${safeFilePath}",
|
|
757
|
-
"written": false,
|
|
758
|
-
"error": "detailed error message",
|
|
759
|
-
"errorType": "PERMISSION_DENIED|INVALID_PATH|SECURITY_VIOLATION|CONTENT_INVALID|DISK_FULL",
|
|
760
|
-
"security": {
|
|
761
|
-
"pathValidated": boolean,
|
|
762
|
-
"contentValidated": boolean,
|
|
763
|
-
"accessAuthorized": boolean,
|
|
764
|
-
"systemDirectoryCheck": "passed|failed",
|
|
765
|
-
"rejectionReason": "specific reason if access denied"
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
\`\`\`
|
|
769
|
-
`;
|
|
770
|
-
const instructions = `
|
|
771
|
-
## Implementation Steps
|
|
772
|
-
|
|
773
|
-
1. **Security Validation**:
|
|
774
|
-
- Validate the file path: ${safeFilePath}
|
|
775
|
-
- Ensure it's not a system directory or sensitive location
|
|
776
|
-
- Check for path traversal attempts (../, .\\, etc.)
|
|
777
|
-
- Validate content for security issues
|
|
778
|
-
|
|
779
|
-
2. **Action Confirmation**:
|
|
780
|
-
- Present the file writing action to the user for confirmation
|
|
781
|
-
- Include details about the file path and content preview
|
|
782
|
-
- Show content size and any directories that will be created
|
|
783
|
-
- Wait for user approval before proceeding
|
|
784
|
-
|
|
785
|
-
3. **Directory Preparation**:
|
|
786
|
-
- Extract directory path from file path: ${dirname(safeFilePath)}
|
|
787
|
-
- Ensure parent directory exists (create recursively if needed)
|
|
788
|
-
- Validate directory permissions
|
|
789
|
-
|
|
790
|
-
4. **File Writing**:
|
|
791
|
-
- Check if file already exists (for backup consideration)
|
|
792
|
-
- Write content to file using UTF-8 encoding
|
|
793
|
-
- Set appropriate file permissions (644 or system default)
|
|
794
|
-
- Use atomic write operations when possible
|
|
795
|
-
|
|
796
|
-
5. **Response Generation**:
|
|
797
|
-
- Format response according to the specified JSON structure
|
|
798
|
-
- Include comprehensive metadata and security validation results
|
|
799
|
-
- Provide detailed error information if writing fails
|
|
800
|
-
- Include confirmation status and directory creation details
|
|
801
|
-
|
|
802
|
-
## Safety Checks
|
|
803
|
-
|
|
804
|
-
- ✅ Path security validation delegated to AI agent
|
|
805
|
-
- ✅ Content security validation required
|
|
806
|
-
- ✅ System directory protection enabled
|
|
807
|
-
- ✅ Action confirmation required before file writing
|
|
808
|
-
- ✅ Directory creation with permission handling
|
|
809
|
-
- ✅ Comprehensive error handling specified
|
|
810
|
-
|
|
811
|
-
## Context Information
|
|
812
|
-
|
|
813
|
-
- **File Path**: ${safeFilePath}
|
|
814
|
-
- **Content Size**: ${safeContent.length} characters
|
|
815
|
-
- **Operation**: Safe file writing with directory creation
|
|
816
|
-
- **Security Level**: High (with path and content validation)
|
|
817
|
-
- **Confirmation Required**: Yes (action confirmation pattern)
|
|
818
|
-
`;
|
|
819
|
-
return {
|
|
820
|
-
prompt,
|
|
821
|
-
instructions,
|
|
822
|
-
context: {
|
|
823
|
-
filePath: safeFilePath,
|
|
824
|
-
contentSize: safeContent.length,
|
|
825
|
-
operation: 'file_write',
|
|
826
|
-
securityLevel: 'high',
|
|
827
|
-
expectedFormat: 'json',
|
|
828
|
-
confirmationRequired: true,
|
|
829
|
-
directoryCreation: true
|
|
441
|
+
// Dynamic import to avoid circular dependencies
|
|
442
|
+
const aiModule = await import('./ai-executor.js');
|
|
443
|
+
const { AIExecutor } = aiModule;
|
|
444
|
+
const executor = new AIExecutor();
|
|
445
|
+
if (!executor.isAvailable()) {
|
|
446
|
+
throw new Error('AI executor not available');
|
|
447
|
+
}
|
|
448
|
+
const prompt = `Extract the most important technical keywords, class names, function names, and architectural terms from this ADR content. Return only a JSON array of strings with the top 20 most relevant keywords for code searching.
|
|
449
|
+
|
|
450
|
+
ADR Content:
|
|
451
|
+
${content.substring(0, 3000)} // Limit content length
|
|
452
|
+
|
|
453
|
+
Example output format:
|
|
454
|
+
["UserService", "authentication", "JWT", "PostgreSQL", "REST_API", "validate_token", "user_repository"]
|
|
455
|
+
|
|
456
|
+
Return ONLY the JSON array, no other text.`;
|
|
457
|
+
const result = await executor.executePrompt(prompt, {
|
|
458
|
+
temperature: 0.3,
|
|
459
|
+
maxTokens: 500,
|
|
460
|
+
});
|
|
461
|
+
// Parse the AI response
|
|
462
|
+
const cleanedResponse = result.content.trim();
|
|
463
|
+
const jsonMatch = cleanedResponse.match(/\[.*\]/s);
|
|
464
|
+
if (jsonMatch) {
|
|
465
|
+
const keywords = JSON.parse(jsonMatch[0]);
|
|
466
|
+
if (Array.isArray(keywords)) {
|
|
467
|
+
return keywords.filter(k => typeof k === 'string').slice(0, 20);
|
|
830
468
|
}
|
|
831
|
-
}
|
|
469
|
+
}
|
|
470
|
+
throw new Error('Invalid AI response format');
|
|
832
471
|
}
|
|
833
472
|
catch (error) {
|
|
834
|
-
|
|
473
|
+
console.error('AI keyword extraction failed:', error);
|
|
474
|
+
throw error;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
// Helper functions
|
|
478
|
+
/**
|
|
479
|
+
* Detect technologies from project files
|
|
480
|
+
*/
|
|
481
|
+
async function detectTechnologies(projectPath, files) {
|
|
482
|
+
const technologies = new Set();
|
|
483
|
+
// Check for package.json
|
|
484
|
+
const packageJsonFile = files.find(f => f.name === 'package.json');
|
|
485
|
+
if (packageJsonFile) {
|
|
486
|
+
technologies.add('Node.js');
|
|
487
|
+
try {
|
|
488
|
+
const content = await fs.readFile(path.join(projectPath, packageJsonFile.path), 'utf-8');
|
|
489
|
+
const packageJson = JSON.parse(content);
|
|
490
|
+
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
491
|
+
if (deps.react)
|
|
492
|
+
technologies.add('React');
|
|
493
|
+
if (deps.vue)
|
|
494
|
+
technologies.add('Vue');
|
|
495
|
+
if (deps.angular)
|
|
496
|
+
technologies.add('Angular');
|
|
497
|
+
if (deps.express)
|
|
498
|
+
technologies.add('Express');
|
|
499
|
+
if (deps.fastify)
|
|
500
|
+
technologies.add('Fastify');
|
|
501
|
+
if (deps['@aws-sdk'] || deps['aws-sdk'])
|
|
502
|
+
technologies.add('AWS SDK');
|
|
503
|
+
if (deps['@google-cloud'])
|
|
504
|
+
technologies.add('GCP SDK');
|
|
505
|
+
if (deps['@azure'])
|
|
506
|
+
technologies.add('Azure SDK');
|
|
507
|
+
}
|
|
508
|
+
catch {
|
|
509
|
+
// Ignore parsing errors
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
// Check for other technology indicators
|
|
513
|
+
if (files.some(f => f.name === 'requirements.txt'))
|
|
514
|
+
technologies.add('Python');
|
|
515
|
+
if (files.some(f => f.name === 'Gemfile'))
|
|
516
|
+
technologies.add('Ruby');
|
|
517
|
+
if (files.some(f => f.name === 'pom.xml'))
|
|
518
|
+
technologies.add('Maven/Java');
|
|
519
|
+
if (files.some(f => f.name === 'Cargo.toml'))
|
|
520
|
+
technologies.add('Rust');
|
|
521
|
+
if (files.some(f => f.name === 'go.mod'))
|
|
522
|
+
technologies.add('Go');
|
|
523
|
+
if (files.some(f => f.name === 'Dockerfile'))
|
|
524
|
+
technologies.add('Docker');
|
|
525
|
+
if (files.some(f => f.name === 'docker-compose.yml'))
|
|
526
|
+
technologies.add('Docker Compose');
|
|
527
|
+
if (files.some(f => f.extension === '.tf'))
|
|
528
|
+
technologies.add('Terraform');
|
|
529
|
+
// Check for Kubernetes files
|
|
530
|
+
const k8sFiles = files.filter(f => f.extension === '.yaml' || f.extension === '.yml');
|
|
531
|
+
if (k8sFiles.length > 0) {
|
|
532
|
+
for (const file of k8sFiles.slice(0, 5)) {
|
|
533
|
+
// Check first 5 YAML files
|
|
534
|
+
try {
|
|
535
|
+
const content = await fs.readFile(path.join(projectPath, file.path), 'utf-8');
|
|
536
|
+
if (content.includes('apiVersion:') && content.includes('kind:')) {
|
|
537
|
+
technologies.add('Kubernetes');
|
|
538
|
+
break;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
catch {
|
|
542
|
+
// Ignore read errors
|
|
543
|
+
}
|
|
544
|
+
}
|
|
835
545
|
}
|
|
546
|
+
return Array.from(technologies);
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Detect architectural patterns from file structure
|
|
550
|
+
*/
|
|
551
|
+
function detectArchitecturalPatterns(files) {
|
|
552
|
+
const patterns = new Set();
|
|
553
|
+
const fileNames = files.map(f => f.name.toLowerCase());
|
|
554
|
+
const filePaths = files.map(f => f.path.toLowerCase());
|
|
555
|
+
// Common architectural patterns
|
|
556
|
+
if (fileNames.some(name => name.includes('controller')))
|
|
557
|
+
patterns.add('MVC');
|
|
558
|
+
if (fileNames.some(name => name.includes('service')))
|
|
559
|
+
patterns.add('Service Layer');
|
|
560
|
+
if (fileNames.some(name => name.includes('repository')))
|
|
561
|
+
patterns.add('Repository Pattern');
|
|
562
|
+
if (fileNames.some(name => name.includes('factory')))
|
|
563
|
+
patterns.add('Factory Pattern');
|
|
564
|
+
// Microservices patterns
|
|
565
|
+
if (filePaths.some(path => path.includes('microservice')))
|
|
566
|
+
patterns.add('Microservices');
|
|
567
|
+
// Container patterns
|
|
568
|
+
if (fileNames.some(name => name === 'dockerfile'))
|
|
569
|
+
patterns.add('Containerization');
|
|
570
|
+
// Infrastructure as Code
|
|
571
|
+
if (files.some(f => f.extension === '.tf'))
|
|
572
|
+
patterns.add('Infrastructure as Code');
|
|
573
|
+
// CI/CD patterns
|
|
574
|
+
if (filePaths.some(path => path.includes('.github/workflows')))
|
|
575
|
+
patterns.add('CI/CD Pipeline');
|
|
576
|
+
return Array.from(patterns);
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Generate a summary of the project analysis
|
|
580
|
+
*/
|
|
581
|
+
function generateProjectSummary(files, technologies, patterns) {
|
|
582
|
+
const extensions = [...new Set(files.map(f => f.extension))].filter(ext => ext);
|
|
583
|
+
const summary = `Project with ${files.length} files. ` +
|
|
584
|
+
`Technologies: ${technologies.join(', ') || 'Not detected'}. ` +
|
|
585
|
+
`Patterns: ${patterns.join(', ') || 'Not detected'}. ` +
|
|
586
|
+
`Main file types: ${extensions.slice(0, 5).join(', ')}.`;
|
|
587
|
+
return summary;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Simple keyword extraction from content
|
|
591
|
+
* This will be enhanced with LLM integration later
|
|
592
|
+
*/
|
|
593
|
+
function extractKeywordsFromContent(content) {
|
|
594
|
+
const keywords = [];
|
|
595
|
+
// Extract technology names
|
|
596
|
+
const techPatterns = [
|
|
597
|
+
/\b(React|Vue|Angular|Node\.js|Express|FastAPI|Django|Spring|Rails)\b/gi,
|
|
598
|
+
/\b(PostgreSQL|MySQL|MongoDB|Redis|Elasticsearch)\b/gi,
|
|
599
|
+
/\b(Docker|Kubernetes|AWS|Azure|GCP)\b/gi,
|
|
600
|
+
];
|
|
601
|
+
techPatterns.forEach(pattern => {
|
|
602
|
+
const matches = content.match(pattern);
|
|
603
|
+
if (matches) {
|
|
604
|
+
keywords.push(...matches.map(m => m.toLowerCase()));
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
// Extract potential class/function names (CamelCase or snake_case)
|
|
608
|
+
const codePatterns = /\b[A-Z][a-zA-Z]+(?:[A-Z][a-z]+)*\b|\b[a-z]+_[a-z_]+\b/g;
|
|
609
|
+
const codeMatches = content.match(codePatterns);
|
|
610
|
+
if (codeMatches) {
|
|
611
|
+
keywords.push(...codeMatches.slice(0, 10)); // Limit to 10 code patterns
|
|
612
|
+
}
|
|
613
|
+
return [...new Set(keywords)]; // Remove duplicates
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Generate backward-compatible prompt response for project analysis
|
|
617
|
+
* This wraps the new analyzeProjectStructure to provide the old interface
|
|
618
|
+
* @deprecated Use analyzeProjectStructure directly
|
|
619
|
+
*/
|
|
620
|
+
export async function analyzeProjectStructureCompat(projectPath) {
|
|
621
|
+
const analysis = await analyzeProjectStructure(projectPath);
|
|
622
|
+
// Generate a descriptive prompt from the actual analysis
|
|
623
|
+
const prompt = `Project Analysis Results:
|
|
624
|
+
Root Path: ${analysis.rootPath}
|
|
625
|
+
Total Files: ${analysis.totalFiles}
|
|
626
|
+
Total Directories: ${analysis.totalDirectories}
|
|
627
|
+
Technologies: ${analysis.technologies.join(', ')}
|
|
628
|
+
Patterns: ${analysis.patterns.join(', ')}
|
|
629
|
+
Summary: ${analysis.summary}`;
|
|
630
|
+
const instructions = `This analysis was performed using fast-glob file discovery.
|
|
631
|
+
Technologies were detected by examining configuration files and dependencies.
|
|
632
|
+
Architectural patterns were identified from file structure and naming conventions.`;
|
|
633
|
+
return {
|
|
634
|
+
prompt,
|
|
635
|
+
instructions,
|
|
636
|
+
context: {
|
|
637
|
+
projectPath: analysis.rootPath,
|
|
638
|
+
absoluteProjectPath: analysis.rootPath,
|
|
639
|
+
filePatterns: ['**/*'],
|
|
640
|
+
},
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Backward-compatible file exists wrapper
|
|
645
|
+
* @deprecated Use fileExists directly
|
|
646
|
+
*/
|
|
647
|
+
export async function fileExistsCompat(filePath) {
|
|
648
|
+
const result = await fileExists(filePath);
|
|
649
|
+
const prompt = `File existence check: ${filePath}
|
|
650
|
+
Result: ${result.exists ? 'File exists' : 'File does not exist'}
|
|
651
|
+
Type: ${result.metadata?.type || 'unknown'}`;
|
|
652
|
+
const instructions = 'File existence was checked using Node.js fs.stat';
|
|
653
|
+
return {
|
|
654
|
+
...result,
|
|
655
|
+
prompt,
|
|
656
|
+
instructions,
|
|
657
|
+
context: { filePath },
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Backward-compatible ensure directory wrapper
|
|
662
|
+
* @deprecated Use ensureDirectory directly
|
|
663
|
+
*/
|
|
664
|
+
export async function ensureDirectoryCompat(dirPath) {
|
|
665
|
+
const result = await ensureDirectory(dirPath);
|
|
666
|
+
const prompt = `Directory operation: ${dirPath}
|
|
667
|
+
Result: ${result.created ? 'Directory created' : 'Directory already exists'}
|
|
668
|
+
Success: ${result.success}`;
|
|
669
|
+
const instructions = 'Directory was ensured using Node.js fs.mkdir with recursive option';
|
|
670
|
+
return {
|
|
671
|
+
...result,
|
|
672
|
+
prompt,
|
|
673
|
+
instructions,
|
|
674
|
+
context: { dirPath },
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Backward-compatible write file wrapper
|
|
679
|
+
* @deprecated Use writeFile directly
|
|
680
|
+
*/
|
|
681
|
+
export async function writeFileCompat(filePath, content) {
|
|
682
|
+
const result = await writeFile(filePath, content);
|
|
683
|
+
const prompt = `File write operation: ${filePath}
|
|
684
|
+
Result: ${result.written ? 'File written successfully' : 'File write failed'}
|
|
685
|
+
Size: ${result.metadata?.size || 0} bytes`;
|
|
686
|
+
const instructions = 'File was written using Node.js fs.writeFile';
|
|
687
|
+
return {
|
|
688
|
+
...result,
|
|
689
|
+
prompt,
|
|
690
|
+
instructions,
|
|
691
|
+
context: { filePath, contentSize: content.length },
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Backward-compatible read file wrapper
|
|
696
|
+
* @deprecated Use readFileContent directly
|
|
697
|
+
*/
|
|
698
|
+
export async function readFileContentCompat(filePath) {
|
|
699
|
+
const result = await readFileContent(filePath);
|
|
700
|
+
const prompt = `File read operation: ${filePath}
|
|
701
|
+
Result: ${result.success ? 'File read successfully' : 'File read failed'}
|
|
702
|
+
Size: ${result.metadata?.size || 0} bytes`;
|
|
703
|
+
const instructions = 'File was read using Node.js fs.readFile';
|
|
704
|
+
return {
|
|
705
|
+
...result,
|
|
706
|
+
prompt,
|
|
707
|
+
instructions,
|
|
708
|
+
context: { filePath },
|
|
709
|
+
};
|
|
836
710
|
}
|
|
837
711
|
/**
|
|
838
712
|
* Get file extension patterns for different file types
|