prjct-cli 0.44.1 → 0.45.3
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/CHANGELOG.md +114 -0
- package/bin/prjct.ts +131 -10
- package/core/__tests__/agentic/memory-system.test.ts +39 -26
- package/core/__tests__/agentic/plan-mode.test.ts +64 -46
- package/core/__tests__/agentic/prompt-builder.test.ts +14 -14
- package/core/__tests__/services/project-index.test.ts +353 -0
- package/core/__tests__/types/fs.test.ts +3 -3
- package/core/__tests__/utils/date-helper.test.ts +10 -10
- package/core/__tests__/utils/output.test.ts +9 -6
- package/core/__tests__/utils/project-commands.test.ts +5 -6
- package/core/agentic/agent-router.ts +9 -10
- package/core/agentic/chain-of-thought.ts +16 -4
- package/core/agentic/command-executor.ts +66 -40
- package/core/agentic/context-builder.ts +8 -5
- package/core/agentic/ground-truth.ts +15 -9
- package/core/agentic/index.ts +145 -152
- package/core/agentic/loop-detector.ts +40 -11
- package/core/agentic/memory-system.ts +98 -35
- package/core/agentic/orchestrator-executor.ts +135 -71
- package/core/agentic/plan-mode.ts +46 -16
- package/core/agentic/prompt-builder.ts +108 -42
- package/core/agentic/services.ts +10 -9
- package/core/agentic/skill-loader.ts +9 -15
- package/core/agentic/smart-context.ts +129 -79
- package/core/agentic/template-executor.ts +13 -12
- package/core/agentic/template-loader.ts +7 -4
- package/core/agentic/tool-registry.ts +16 -13
- package/core/agents/index.ts +1 -1
- package/core/agents/performance.ts +10 -27
- package/core/ai-tools/formatters.ts +8 -6
- package/core/ai-tools/generator.ts +4 -4
- package/core/ai-tools/index.ts +1 -1
- package/core/ai-tools/registry.ts +21 -11
- package/core/bus/bus.ts +23 -16
- package/core/bus/index.ts +2 -2
- package/core/cli/linear.ts +3 -5
- package/core/cli/start.ts +28 -25
- package/core/commands/analysis.ts +287 -29
- package/core/commands/analytics.ts +52 -44
- package/core/commands/base.ts +15 -13
- package/core/commands/cleanup.ts +6 -13
- package/core/commands/command-data.ts +49 -8
- package/core/commands/commands.ts +60 -23
- package/core/commands/context.ts +4 -4
- package/core/commands/design.ts +3 -10
- package/core/commands/index.ts +5 -8
- package/core/commands/maintenance.ts +7 -4
- package/core/commands/planning.ts +179 -56
- package/core/commands/register.ts +14 -9
- package/core/commands/registry.ts +15 -14
- package/core/commands/setup.ts +26 -14
- package/core/commands/shipping.ts +11 -16
- package/core/commands/snapshots.ts +16 -32
- package/core/commands/uninstall.ts +541 -0
- package/core/commands/workflow.ts +24 -28
- package/core/constants/index.ts +10 -22
- package/core/context/generator.ts +82 -33
- package/core/context-tools/files-tool.ts +583 -0
- package/core/context-tools/imports-tool.ts +403 -0
- package/core/context-tools/index.ts +433 -0
- package/core/context-tools/recent-tool.ts +307 -0
- package/core/context-tools/signatures-tool.ts +501 -0
- package/core/context-tools/summary-tool.ts +307 -0
- package/core/context-tools/token-counter.ts +284 -0
- package/core/context-tools/types.ts +253 -0
- package/core/domain/agent-generator.ts +7 -5
- package/core/domain/agent-loader.ts +2 -2
- package/core/domain/analyzer.ts +19 -16
- package/core/domain/architecture-generator.ts +6 -3
- package/core/domain/context-estimator.ts +3 -4
- package/core/domain/snapshot-manager.ts +25 -22
- package/core/domain/task-stack.ts +24 -14
- package/core/errors.ts +1 -1
- package/core/events/events.ts +2 -4
- package/core/events/index.ts +1 -2
- package/core/index.ts +28 -12
- package/core/infrastructure/agent-detector.ts +3 -3
- package/core/infrastructure/ai-provider.ts +23 -20
- package/core/infrastructure/author-detector.ts +16 -10
- package/core/infrastructure/capability-installer.ts +2 -2
- package/core/infrastructure/claude-agent.ts +6 -6
- package/core/infrastructure/command-installer.ts +22 -17
- package/core/infrastructure/config-manager.ts +18 -14
- package/core/infrastructure/editors-config.ts +8 -4
- package/core/infrastructure/path-manager.ts +8 -6
- package/core/infrastructure/permission-manager.ts +20 -17
- package/core/infrastructure/setup.ts +42 -38
- package/core/infrastructure/update-checker.ts +5 -5
- package/core/integrations/issue-tracker/enricher.ts +8 -19
- package/core/integrations/issue-tracker/index.ts +2 -2
- package/core/integrations/issue-tracker/manager.ts +15 -15
- package/core/integrations/issue-tracker/types.ts +5 -22
- package/core/integrations/jira/client.ts +67 -59
- package/core/integrations/jira/index.ts +11 -14
- package/core/integrations/jira/mcp-adapter.ts +5 -10
- package/core/integrations/jira/service.ts +10 -10
- package/core/integrations/linear/client.ts +27 -18
- package/core/integrations/linear/index.ts +9 -12
- package/core/integrations/linear/service.ts +11 -11
- package/core/integrations/linear/sync.ts +8 -8
- package/core/outcomes/analyzer.ts +5 -18
- package/core/outcomes/index.ts +2 -2
- package/core/outcomes/recorder.ts +3 -3
- package/core/plugin/builtin/webhook.ts +19 -15
- package/core/plugin/hooks.ts +29 -21
- package/core/plugin/index.ts +7 -7
- package/core/plugin/loader.ts +19 -19
- package/core/plugin/registry.ts +12 -23
- package/core/schemas/agents.ts +1 -1
- package/core/schemas/analysis.ts +1 -1
- package/core/schemas/enriched-task.ts +62 -49
- package/core/schemas/ideas.ts +13 -13
- package/core/schemas/index.ts +17 -27
- package/core/schemas/issues.ts +40 -25
- package/core/schemas/metrics.ts +143 -0
- package/core/schemas/outcomes.ts +70 -62
- package/core/schemas/permissions.ts +15 -12
- package/core/schemas/prd.ts +27 -14
- package/core/schemas/project.ts +3 -3
- package/core/schemas/roadmap.ts +47 -34
- package/core/schemas/schemas.ts +3 -4
- package/core/schemas/shipped.ts +3 -3
- package/core/schemas/state.ts +43 -29
- package/core/server/index.ts +5 -6
- package/core/server/routes-extended.ts +68 -72
- package/core/server/routes.ts +3 -3
- package/core/server/server.ts +31 -26
- package/core/services/agent-generator.ts +237 -0
- package/core/services/agent-service.ts +2 -2
- package/core/services/breakdown-service.ts +2 -4
- package/core/services/context-generator.ts +299 -0
- package/core/services/context-selector.ts +420 -0
- package/core/services/doctor-service.ts +426 -0
- package/core/services/file-categorizer.ts +448 -0
- package/core/services/file-scorer.ts +270 -0
- package/core/services/git-analyzer.ts +267 -0
- package/core/services/index.ts +27 -10
- package/core/services/memory-service.ts +3 -4
- package/core/services/project-index.ts +911 -0
- package/core/services/project-service.ts +4 -4
- package/core/services/skill-installer.ts +14 -17
- package/core/services/skill-lock.ts +3 -3
- package/core/services/skill-service.ts +12 -6
- package/core/services/stack-detector.ts +245 -0
- package/core/services/sync-service.ts +170 -329
- package/core/services/watch-service.ts +294 -0
- package/core/session/compaction.ts +23 -31
- package/core/session/index.ts +11 -5
- package/core/session/log-migration.ts +3 -3
- package/core/session/metrics.ts +19 -14
- package/core/session/session-log-manager.ts +12 -17
- package/core/session/task-session-manager.ts +25 -25
- package/core/session/utils.ts +1 -1
- package/core/storage/ideas-storage.ts +41 -57
- package/core/storage/index-storage.ts +514 -0
- package/core/storage/index.ts +41 -13
- package/core/storage/metrics-storage.ts +320 -0
- package/core/storage/queue-storage.ts +35 -45
- package/core/storage/shipped-storage.ts +17 -20
- package/core/storage/state-storage.ts +50 -30
- package/core/storage/storage-manager.ts +6 -6
- package/core/storage/storage.ts +18 -15
- package/core/sync/auth-config.ts +3 -3
- package/core/sync/index.ts +13 -19
- package/core/sync/oauth-handler.ts +3 -3
- package/core/sync/sync-client.ts +4 -9
- package/core/sync/sync-manager.ts +12 -14
- package/core/types/commands.ts +42 -7
- package/core/types/index.ts +284 -302
- package/core/types/integrations.ts +3 -3
- package/core/types/storage.ts +49 -0
- package/core/types/utils.ts +3 -3
- package/core/utils/agent-stream.ts +3 -1
- package/core/utils/animations.ts +14 -11
- package/core/utils/branding.ts +7 -7
- package/core/utils/cache.ts +1 -3
- package/core/utils/collection-filters.ts +3 -15
- package/core/utils/date-helper.ts +2 -7
- package/core/utils/file-helper.ts +13 -8
- package/core/utils/jsonl-helper.ts +13 -10
- package/core/utils/keychain.ts +4 -8
- package/core/utils/logger.ts +1 -1
- package/core/utils/next-steps.ts +3 -3
- package/core/utils/output.ts +58 -11
- package/core/utils/project-commands.ts +6 -6
- package/core/utils/project-credentials.ts +5 -12
- package/core/utils/runtime.ts +2 -2
- package/core/utils/session-helper.ts +3 -4
- package/core/utils/version.ts +3 -3
- package/core/wizard/index.ts +13 -0
- package/core/wizard/onboarding.ts +633 -0
- package/core/workflow/state-machine.ts +7 -7
- package/dist/bin/prjct.mjs +18907 -13189
- package/dist/core/infrastructure/command-installer.js +96 -111
- package/dist/core/infrastructure/editors-config.js +6 -6
- package/dist/core/infrastructure/setup.js +256 -257
- package/dist/core/utils/version.js +9 -9
- package/package.json +11 -12
- package/scripts/build.js +3 -3
- package/scripts/postinstall.js +2 -2
- package/templates/mcp-config.json +6 -1
- package/templates/permissions/permissive.jsonc +1 -1
- package/templates/permissions/strict.jsonc +5 -9
- package/templates/global/docs/agents.md +0 -88
- package/templates/global/docs/architecture.md +0 -103
- package/templates/global/docs/commands.md +0 -96
- package/templates/global/docs/validation.md +0 -95
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Imports Tool - Build import/dependency graphs
|
|
3
|
+
*
|
|
4
|
+
* Analyzes:
|
|
5
|
+
* - What a file imports
|
|
6
|
+
* - What files import this file (reverse lookup)
|
|
7
|
+
* - Dependency tree to a given depth
|
|
8
|
+
*
|
|
9
|
+
* @module context-tools/imports-tool
|
|
10
|
+
* @version 1.0.0
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { exec as execCallback } from 'node:child_process'
|
|
14
|
+
import fs from 'node:fs/promises'
|
|
15
|
+
import path from 'node:path'
|
|
16
|
+
import { promisify } from 'node:util'
|
|
17
|
+
import { isNotFoundError } from '../types/fs'
|
|
18
|
+
import type { DependencyNode, ImportedBy, ImportRelation, ImportsToolOutput } from './types'
|
|
19
|
+
|
|
20
|
+
const exec = promisify(execCallback)
|
|
21
|
+
|
|
22
|
+
// =============================================================================
|
|
23
|
+
// Import Patterns by Language
|
|
24
|
+
// =============================================================================
|
|
25
|
+
|
|
26
|
+
interface ImportPattern {
|
|
27
|
+
pattern: RegExp
|
|
28
|
+
sourceIndex: number
|
|
29
|
+
namesIndex?: number
|
|
30
|
+
isDefault?: boolean
|
|
31
|
+
isNamespace?: boolean
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* TypeScript/JavaScript import patterns
|
|
36
|
+
*/
|
|
37
|
+
const TS_IMPORT_PATTERNS: ImportPattern[] = [
|
|
38
|
+
// import { x, y } from 'module'
|
|
39
|
+
{
|
|
40
|
+
pattern: /import\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g,
|
|
41
|
+
sourceIndex: 2,
|
|
42
|
+
namesIndex: 1,
|
|
43
|
+
},
|
|
44
|
+
// import x from 'module'
|
|
45
|
+
{
|
|
46
|
+
pattern: /import\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g,
|
|
47
|
+
sourceIndex: 2,
|
|
48
|
+
namesIndex: 1,
|
|
49
|
+
isDefault: true,
|
|
50
|
+
},
|
|
51
|
+
// import * as x from 'module'
|
|
52
|
+
{
|
|
53
|
+
pattern: /import\s*\*\s*as\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g,
|
|
54
|
+
sourceIndex: 2,
|
|
55
|
+
namesIndex: 1,
|
|
56
|
+
isNamespace: true,
|
|
57
|
+
},
|
|
58
|
+
// import 'module' (side-effect)
|
|
59
|
+
{
|
|
60
|
+
pattern: /import\s*['"]([^'"]+)['"]/g,
|
|
61
|
+
sourceIndex: 1,
|
|
62
|
+
},
|
|
63
|
+
// require('module')
|
|
64
|
+
{
|
|
65
|
+
pattern: /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
66
|
+
sourceIndex: 1,
|
|
67
|
+
},
|
|
68
|
+
// Dynamic import
|
|
69
|
+
{
|
|
70
|
+
pattern: /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
71
|
+
sourceIndex: 1,
|
|
72
|
+
},
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Python import patterns
|
|
77
|
+
*/
|
|
78
|
+
const PYTHON_IMPORT_PATTERNS: ImportPattern[] = [
|
|
79
|
+
// from module import x, y
|
|
80
|
+
{
|
|
81
|
+
pattern: /from\s+([\w.]+)\s+import\s+([^;\n]+)/g,
|
|
82
|
+
sourceIndex: 1,
|
|
83
|
+
namesIndex: 2,
|
|
84
|
+
},
|
|
85
|
+
// import module
|
|
86
|
+
{
|
|
87
|
+
pattern: /^import\s+([\w.]+)(?:\s+as\s+\w+)?$/gm,
|
|
88
|
+
sourceIndex: 1,
|
|
89
|
+
},
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Go import patterns
|
|
94
|
+
*/
|
|
95
|
+
const GO_IMPORT_PATTERNS: ImportPattern[] = [
|
|
96
|
+
// import "module"
|
|
97
|
+
{
|
|
98
|
+
pattern: /import\s*"([^"]+)"/g,
|
|
99
|
+
sourceIndex: 1,
|
|
100
|
+
},
|
|
101
|
+
// import ( "module1" "module2" )
|
|
102
|
+
{
|
|
103
|
+
pattern: /import\s*\([^)]*"([^"]+)"[^)]*\)/g,
|
|
104
|
+
sourceIndex: 1,
|
|
105
|
+
},
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Language to import patterns mapping
|
|
110
|
+
*/
|
|
111
|
+
const IMPORT_PATTERNS: Record<string, ImportPattern[]> = {
|
|
112
|
+
typescript: TS_IMPORT_PATTERNS,
|
|
113
|
+
javascript: TS_IMPORT_PATTERNS,
|
|
114
|
+
python: PYTHON_IMPORT_PATTERNS,
|
|
115
|
+
go: GO_IMPORT_PATTERNS,
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Extension to language mapping
|
|
120
|
+
*/
|
|
121
|
+
const EXT_TO_LANG: Record<string, string> = {
|
|
122
|
+
'.ts': 'typescript',
|
|
123
|
+
'.tsx': 'typescript',
|
|
124
|
+
'.js': 'javascript',
|
|
125
|
+
'.jsx': 'javascript',
|
|
126
|
+
'.mjs': 'javascript',
|
|
127
|
+
'.cjs': 'javascript',
|
|
128
|
+
'.py': 'python',
|
|
129
|
+
'.go': 'go',
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// =============================================================================
|
|
133
|
+
// Main Functions
|
|
134
|
+
// =============================================================================
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Analyze imports for a file
|
|
138
|
+
*
|
|
139
|
+
* @param filePath - Path to the file
|
|
140
|
+
* @param projectPath - Project root path
|
|
141
|
+
* @param options - Analysis options
|
|
142
|
+
* @returns Import analysis with metrics
|
|
143
|
+
*/
|
|
144
|
+
export async function analyzeImports(
|
|
145
|
+
filePath: string,
|
|
146
|
+
projectPath: string = process.cwd(),
|
|
147
|
+
options: {
|
|
148
|
+
reverse?: boolean // Include files that import this
|
|
149
|
+
depth?: number // Dependency tree depth (0 = no tree)
|
|
150
|
+
} = {}
|
|
151
|
+
): Promise<ImportsToolOutput> {
|
|
152
|
+
const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(projectPath, filePath)
|
|
153
|
+
|
|
154
|
+
// Read file content
|
|
155
|
+
let content: string
|
|
156
|
+
try {
|
|
157
|
+
content = await fs.readFile(absolutePath, 'utf-8')
|
|
158
|
+
} catch (error) {
|
|
159
|
+
if (isNotFoundError(error)) {
|
|
160
|
+
return {
|
|
161
|
+
file: filePath,
|
|
162
|
+
imports: [],
|
|
163
|
+
importedBy: [],
|
|
164
|
+
metrics: {
|
|
165
|
+
totalImports: 0,
|
|
166
|
+
externalImports: 0,
|
|
167
|
+
internalImports: 0,
|
|
168
|
+
importedByCount: 0,
|
|
169
|
+
},
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
throw error
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Detect language
|
|
176
|
+
const ext = path.extname(filePath).toLowerCase()
|
|
177
|
+
const language = EXT_TO_LANG[ext] || 'unknown'
|
|
178
|
+
const patterns = IMPORT_PATTERNS[language] || []
|
|
179
|
+
|
|
180
|
+
// Extract imports
|
|
181
|
+
const imports = extractImports(content, patterns, absolutePath, projectPath)
|
|
182
|
+
|
|
183
|
+
// Get reverse imports if requested
|
|
184
|
+
let importedBy: ImportedBy[] = []
|
|
185
|
+
if (options.reverse) {
|
|
186
|
+
importedBy = await findImportedBy(filePath, projectPath)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Build dependency tree if requested
|
|
190
|
+
let dependencyTree: DependencyNode | undefined
|
|
191
|
+
if (options.depth && options.depth > 0) {
|
|
192
|
+
dependencyTree = await buildDependencyTree(filePath, projectPath, options.depth)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Calculate metrics
|
|
196
|
+
const externalImports = imports.filter((i) => i.isExternal).length
|
|
197
|
+
const internalImports = imports.filter((i) => !i.isExternal).length
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
file: filePath,
|
|
201
|
+
imports,
|
|
202
|
+
importedBy,
|
|
203
|
+
dependencyTree,
|
|
204
|
+
metrics: {
|
|
205
|
+
totalImports: imports.length,
|
|
206
|
+
externalImports,
|
|
207
|
+
internalImports,
|
|
208
|
+
importedByCount: importedBy.length,
|
|
209
|
+
},
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// =============================================================================
|
|
214
|
+
// Helper Functions
|
|
215
|
+
// =============================================================================
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Extract imports from file content
|
|
219
|
+
*/
|
|
220
|
+
function extractImports(
|
|
221
|
+
content: string,
|
|
222
|
+
patterns: ImportPattern[],
|
|
223
|
+
absolutePath: string,
|
|
224
|
+
projectPath: string
|
|
225
|
+
): ImportRelation[] {
|
|
226
|
+
const imports: ImportRelation[] = []
|
|
227
|
+
const seen = new Set<string>()
|
|
228
|
+
|
|
229
|
+
for (const patternDef of patterns) {
|
|
230
|
+
patternDef.pattern.lastIndex = 0
|
|
231
|
+
|
|
232
|
+
let match
|
|
233
|
+
while ((match = patternDef.pattern.exec(content)) !== null) {
|
|
234
|
+
const source = match[patternDef.sourceIndex]
|
|
235
|
+
if (!source || seen.has(source)) continue
|
|
236
|
+
seen.add(source)
|
|
237
|
+
|
|
238
|
+
// Parse imported names
|
|
239
|
+
let importedNames: string[] | undefined
|
|
240
|
+
if (patternDef.namesIndex !== undefined) {
|
|
241
|
+
const namesStr = match[patternDef.namesIndex]
|
|
242
|
+
if (namesStr) {
|
|
243
|
+
importedNames = namesStr
|
|
244
|
+
.split(',')
|
|
245
|
+
.map((n) => n.trim().split(' as ')[0].trim())
|
|
246
|
+
.filter(Boolean)
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Determine if external
|
|
251
|
+
const isExternal =
|
|
252
|
+
!source.startsWith('.') && !source.startsWith('/') && !source.startsWith('@/')
|
|
253
|
+
|
|
254
|
+
// Resolve internal imports
|
|
255
|
+
let resolved: string | null = null
|
|
256
|
+
if (!isExternal) {
|
|
257
|
+
resolved = resolveImport(source, absolutePath, projectPath)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
imports.push({
|
|
261
|
+
source,
|
|
262
|
+
resolved,
|
|
263
|
+
isExternal,
|
|
264
|
+
importedNames,
|
|
265
|
+
isDefault: patternDef.isDefault,
|
|
266
|
+
isNamespace: patternDef.isNamespace,
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return imports
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Resolve a relative import to an absolute path
|
|
276
|
+
*/
|
|
277
|
+
function resolveImport(source: string, fromFile: string, projectPath: string): string | null {
|
|
278
|
+
const fileDir = path.dirname(fromFile)
|
|
279
|
+
|
|
280
|
+
// Handle path alias like @/
|
|
281
|
+
if (source.startsWith('@/')) {
|
|
282
|
+
const aliasPath = path.join(projectPath, 'src', source.slice(2))
|
|
283
|
+
return tryResolve(aliasPath, projectPath)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Regular relative import
|
|
287
|
+
const resolved = path.resolve(fileDir, source)
|
|
288
|
+
return tryResolve(resolved, projectPath)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Try to resolve a path, adding extensions if needed
|
|
293
|
+
*/
|
|
294
|
+
function tryResolve(basePath: string, projectPath: string): string | null {
|
|
295
|
+
const extensions = ['', '.ts', '.tsx', '.js', '.jsx', '/index.ts', '/index.js']
|
|
296
|
+
|
|
297
|
+
for (const ext of extensions) {
|
|
298
|
+
const fullPath = basePath + ext
|
|
299
|
+
try {
|
|
300
|
+
// Check synchronously (we're in a hot path)
|
|
301
|
+
const fs = require('node:fs')
|
|
302
|
+
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
|
|
303
|
+
return path.relative(projectPath, fullPath)
|
|
304
|
+
}
|
|
305
|
+
} catch {}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return null
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Find all files that import the target file
|
|
313
|
+
*/
|
|
314
|
+
async function findImportedBy(filePath: string, projectPath: string): Promise<ImportedBy[]> {
|
|
315
|
+
const importedBy: ImportedBy[] = []
|
|
316
|
+
|
|
317
|
+
// Get the base name without extension for matching
|
|
318
|
+
const baseName = path.basename(filePath, path.extname(filePath))
|
|
319
|
+
const _dirName = path.dirname(filePath)
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
// Use ripgrep if available, otherwise grep
|
|
323
|
+
const searchPatterns = [
|
|
324
|
+
`from ['"].*${baseName}['"]`,
|
|
325
|
+
`from ['"]\\./${baseName}['"]`,
|
|
326
|
+
`import\\(['"'].*${baseName}['"]`,
|
|
327
|
+
`require\\(['"'].*${baseName}['"]`,
|
|
328
|
+
]
|
|
329
|
+
|
|
330
|
+
const pattern = searchPatterns.join('|')
|
|
331
|
+
|
|
332
|
+
const { stdout } = await exec(
|
|
333
|
+
`grep -r -l -E '${pattern}' --include='*.ts' --include='*.tsx' --include='*.js' --include='*.jsx' . 2>/dev/null || true`,
|
|
334
|
+
{ cwd: projectPath, maxBuffer: 10 * 1024 * 1024 }
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
const files = stdout
|
|
338
|
+
.trim()
|
|
339
|
+
.split('\n')
|
|
340
|
+
.filter(Boolean)
|
|
341
|
+
.map((f) => f.replace(/^\.\//, ''))
|
|
342
|
+
.filter((f) => f !== filePath) // Exclude self
|
|
343
|
+
|
|
344
|
+
for (const file of files) {
|
|
345
|
+
importedBy.push({ file })
|
|
346
|
+
}
|
|
347
|
+
} catch (_error) {
|
|
348
|
+
// grep not available or error - return empty
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return importedBy
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Build a dependency tree to a given depth
|
|
356
|
+
*/
|
|
357
|
+
async function buildDependencyTree(
|
|
358
|
+
filePath: string,
|
|
359
|
+
projectPath: string,
|
|
360
|
+
maxDepth: number,
|
|
361
|
+
currentDepth: number = 0,
|
|
362
|
+
visited: Set<string> = new Set()
|
|
363
|
+
): Promise<DependencyNode> {
|
|
364
|
+
const node: DependencyNode = {
|
|
365
|
+
file: filePath,
|
|
366
|
+
imports: [],
|
|
367
|
+
depth: currentDepth,
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (currentDepth >= maxDepth || visited.has(filePath)) {
|
|
371
|
+
return node
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
visited.add(filePath)
|
|
375
|
+
|
|
376
|
+
// Get imports for this file
|
|
377
|
+
const analysis = await analyzeImports(filePath, projectPath, {
|
|
378
|
+
reverse: false,
|
|
379
|
+
depth: 0,
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
// Recursively build tree for internal imports
|
|
383
|
+
for (const imp of analysis.imports) {
|
|
384
|
+
if (!imp.isExternal && imp.resolved) {
|
|
385
|
+
const childNode = await buildDependencyTree(
|
|
386
|
+
imp.resolved,
|
|
387
|
+
projectPath,
|
|
388
|
+
maxDepth,
|
|
389
|
+
currentDepth + 1,
|
|
390
|
+
visited
|
|
391
|
+
)
|
|
392
|
+
node.imports.push(childNode)
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return node
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// =============================================================================
|
|
400
|
+
// Exports
|
|
401
|
+
// =============================================================================
|
|
402
|
+
|
|
403
|
+
export default { analyzeImports }
|