prjct-cli 0.44.1 → 0.45.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/CHANGELOG.md +39 -0
- package/bin/prjct.ts +14 -0
- package/core/commands/analysis.ts +239 -0
- package/core/commands/command-data.ts +21 -4
- package/core/commands/commands.ts +4 -0
- package/core/commands/register.ts +1 -0
- package/core/context-tools/files-tool.ts +584 -0
- package/core/context-tools/imports-tool.ts +423 -0
- package/core/context-tools/index.ts +458 -0
- package/core/context-tools/recent-tool.ts +313 -0
- package/core/context-tools/signatures-tool.ts +510 -0
- package/core/context-tools/summary-tool.ts +309 -0
- package/core/context-tools/token-counter.ts +279 -0
- package/core/context-tools/types.ts +253 -0
- package/core/index.ts +4 -0
- package/core/schemas/metrics.ts +143 -0
- package/core/services/sync-service.ts +99 -0
- package/core/storage/index.ts +4 -0
- package/core/storage/metrics-storage.ts +315 -0
- package/core/types/index.ts +3 -0
- package/core/types/storage.ts +49 -0
- package/dist/bin/prjct.mjs +5362 -2825
- package/dist/core/infrastructure/command-installer.js +10 -32
- package/dist/core/infrastructure/setup.js +10 -32
- package/package.json +1 -1
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signatures Tool - Extract code signatures without full content
|
|
3
|
+
*
|
|
4
|
+
* Extracts:
|
|
5
|
+
* - Function names + params + return types
|
|
6
|
+
* - Interface/type definitions
|
|
7
|
+
* - Class names + methods
|
|
8
|
+
* - Export lists
|
|
9
|
+
*
|
|
10
|
+
* Achieves ~90% token reduction by returning structure only.
|
|
11
|
+
*
|
|
12
|
+
* Uses regex patterns for broad language support.
|
|
13
|
+
* Falls back to full file if language not supported.
|
|
14
|
+
*
|
|
15
|
+
* @module context-tools/signatures-tool
|
|
16
|
+
* @version 1.0.0
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import fs from 'fs/promises'
|
|
20
|
+
import path from 'path'
|
|
21
|
+
import type { SignaturesToolOutput, CodeSignature, SignatureType } from './types'
|
|
22
|
+
import { measureCompression, noCompression } from './token-counter'
|
|
23
|
+
import { isNotFoundError } from '../types/fs'
|
|
24
|
+
|
|
25
|
+
// =============================================================================
|
|
26
|
+
// Language Support
|
|
27
|
+
// =============================================================================
|
|
28
|
+
|
|
29
|
+
type LanguageId =
|
|
30
|
+
| 'typescript'
|
|
31
|
+
| 'javascript'
|
|
32
|
+
| 'python'
|
|
33
|
+
| 'go'
|
|
34
|
+
| 'rust'
|
|
35
|
+
| 'java'
|
|
36
|
+
| 'csharp'
|
|
37
|
+
| 'php'
|
|
38
|
+
| 'ruby'
|
|
39
|
+
| 'unknown'
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Map file extensions to language identifiers
|
|
43
|
+
*/
|
|
44
|
+
const EXTENSION_TO_LANGUAGE: Record<string, LanguageId> = {
|
|
45
|
+
'.ts': 'typescript',
|
|
46
|
+
'.tsx': 'typescript',
|
|
47
|
+
'.js': 'javascript',
|
|
48
|
+
'.jsx': 'javascript',
|
|
49
|
+
'.mjs': 'javascript',
|
|
50
|
+
'.cjs': 'javascript',
|
|
51
|
+
'.py': 'python',
|
|
52
|
+
'.go': 'go',
|
|
53
|
+
'.rs': 'rust',
|
|
54
|
+
'.java': 'java',
|
|
55
|
+
'.cs': 'csharp',
|
|
56
|
+
'.php': 'php',
|
|
57
|
+
'.rb': 'ruby',
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// =============================================================================
|
|
61
|
+
// Extraction Patterns
|
|
62
|
+
// =============================================================================
|
|
63
|
+
|
|
64
|
+
interface ExtractionPattern {
|
|
65
|
+
type: SignatureType
|
|
66
|
+
pattern: RegExp
|
|
67
|
+
nameIndex: number
|
|
68
|
+
signatureIndex?: number
|
|
69
|
+
exported?: boolean
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* TypeScript/JavaScript extraction patterns
|
|
74
|
+
*/
|
|
75
|
+
const TS_PATTERNS: ExtractionPattern[] = [
|
|
76
|
+
// Exported function declarations
|
|
77
|
+
{
|
|
78
|
+
type: 'function',
|
|
79
|
+
pattern:
|
|
80
|
+
/^export\s+(?:async\s+)?function\s+(\w+)\s*(<[^>]*>)?\s*\(([^)]*)\)\s*(?::\s*([^{;]+))?/gm,
|
|
81
|
+
nameIndex: 1,
|
|
82
|
+
exported: true,
|
|
83
|
+
},
|
|
84
|
+
// Exported const arrow functions
|
|
85
|
+
{
|
|
86
|
+
type: 'function',
|
|
87
|
+
pattern:
|
|
88
|
+
/^export\s+const\s+(\w+)\s*(?::\s*[^=]+)?\s*=\s*(?:async\s+)?\([^)]*\)\s*(?::\s*[^=]+)?\s*=>/gm,
|
|
89
|
+
nameIndex: 1,
|
|
90
|
+
exported: true,
|
|
91
|
+
},
|
|
92
|
+
// Regular function declarations
|
|
93
|
+
{
|
|
94
|
+
type: 'function',
|
|
95
|
+
pattern:
|
|
96
|
+
/^(?:async\s+)?function\s+(\w+)\s*(<[^>]*>)?\s*\(([^)]*)\)\s*(?::\s*([^{;]+))?/gm,
|
|
97
|
+
nameIndex: 1,
|
|
98
|
+
},
|
|
99
|
+
// Const arrow functions
|
|
100
|
+
{
|
|
101
|
+
type: 'function',
|
|
102
|
+
pattern:
|
|
103
|
+
/^const\s+(\w+)\s*(?::\s*[^=]+)?\s*=\s*(?:async\s+)?\([^)]*\)\s*(?::\s*[^=]+)?\s*=>/gm,
|
|
104
|
+
nameIndex: 1,
|
|
105
|
+
},
|
|
106
|
+
// Interface declarations
|
|
107
|
+
{
|
|
108
|
+
type: 'interface',
|
|
109
|
+
pattern: /^export\s+interface\s+(\w+)(?:<[^>]+>)?\s*(?:extends\s+[^{]+)?\s*\{/gm,
|
|
110
|
+
nameIndex: 1,
|
|
111
|
+
exported: true,
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
type: 'interface',
|
|
115
|
+
pattern: /^interface\s+(\w+)(?:<[^>]+>)?\s*(?:extends\s+[^{]+)?\s*\{/gm,
|
|
116
|
+
nameIndex: 1,
|
|
117
|
+
},
|
|
118
|
+
// Type aliases
|
|
119
|
+
{
|
|
120
|
+
type: 'type',
|
|
121
|
+
pattern: /^export\s+type\s+(\w+)(?:<[^>]+>)?\s*=/gm,
|
|
122
|
+
nameIndex: 1,
|
|
123
|
+
exported: true,
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
type: 'type',
|
|
127
|
+
pattern: /^type\s+(\w+)(?:<[^>]+>)?\s*=/gm,
|
|
128
|
+
nameIndex: 1,
|
|
129
|
+
},
|
|
130
|
+
// Class declarations
|
|
131
|
+
{
|
|
132
|
+
type: 'class',
|
|
133
|
+
pattern:
|
|
134
|
+
/^export\s+(?:abstract\s+)?class\s+(\w+)(?:<[^>]+>)?(?:\s+extends\s+[^\{]+)?(?:\s+implements\s+[^\{]+)?\s*\{/gm,
|
|
135
|
+
nameIndex: 1,
|
|
136
|
+
exported: true,
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
type: 'class',
|
|
140
|
+
pattern:
|
|
141
|
+
/^(?:abstract\s+)?class\s+(\w+)(?:<[^>]+>)?(?:\s+extends\s+[^\{]+)?(?:\s+implements\s+[^\{]+)?\s*\{/gm,
|
|
142
|
+
nameIndex: 1,
|
|
143
|
+
},
|
|
144
|
+
// Enum declarations
|
|
145
|
+
{
|
|
146
|
+
type: 'enum',
|
|
147
|
+
pattern: /^export\s+enum\s+(\w+)\s*\{/gm,
|
|
148
|
+
nameIndex: 1,
|
|
149
|
+
exported: true,
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
type: 'enum',
|
|
153
|
+
pattern: /^enum\s+(\w+)\s*\{/gm,
|
|
154
|
+
nameIndex: 1,
|
|
155
|
+
},
|
|
156
|
+
// Exported constants
|
|
157
|
+
{
|
|
158
|
+
type: 'const',
|
|
159
|
+
pattern: /^export\s+const\s+(\w+)\s*(?::\s*([^=]+))?\s*=/gm,
|
|
160
|
+
nameIndex: 1,
|
|
161
|
+
exported: true,
|
|
162
|
+
},
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Python extraction patterns
|
|
167
|
+
*/
|
|
168
|
+
const PYTHON_PATTERNS: ExtractionPattern[] = [
|
|
169
|
+
// Function definitions
|
|
170
|
+
{
|
|
171
|
+
type: 'function',
|
|
172
|
+
pattern: /^def\s+(\w+)\s*\(([^)]*)\)\s*(?:->\s*([^:]+))?\s*:/gm,
|
|
173
|
+
nameIndex: 1,
|
|
174
|
+
},
|
|
175
|
+
// Async function definitions
|
|
176
|
+
{
|
|
177
|
+
type: 'function',
|
|
178
|
+
pattern: /^async\s+def\s+(\w+)\s*\(([^)]*)\)\s*(?:->\s*([^:]+))?\s*:/gm,
|
|
179
|
+
nameIndex: 1,
|
|
180
|
+
},
|
|
181
|
+
// Class definitions
|
|
182
|
+
{
|
|
183
|
+
type: 'class',
|
|
184
|
+
pattern: /^class\s+(\w+)(?:\(([^)]*)\))?\s*:/gm,
|
|
185
|
+
nameIndex: 1,
|
|
186
|
+
},
|
|
187
|
+
]
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Go extraction patterns
|
|
191
|
+
*/
|
|
192
|
+
const GO_PATTERNS: ExtractionPattern[] = [
|
|
193
|
+
// Function declarations
|
|
194
|
+
{
|
|
195
|
+
type: 'function',
|
|
196
|
+
pattern: /^func\s+(\w+)\s*\(([^)]*)\)\s*(?:\(([^)]*)\)|([^\s{]+))?\s*\{/gm,
|
|
197
|
+
nameIndex: 1,
|
|
198
|
+
},
|
|
199
|
+
// Method declarations
|
|
200
|
+
{
|
|
201
|
+
type: 'method',
|
|
202
|
+
pattern: /^func\s+\([^)]+\)\s+(\w+)\s*\(([^)]*)\)\s*(?:\(([^)]*)\)|([^\s{]+))?\s*\{/gm,
|
|
203
|
+
nameIndex: 1,
|
|
204
|
+
},
|
|
205
|
+
// Type definitions
|
|
206
|
+
{
|
|
207
|
+
type: 'type',
|
|
208
|
+
pattern: /^type\s+(\w+)\s+(?:struct|interface)\s*\{/gm,
|
|
209
|
+
nameIndex: 1,
|
|
210
|
+
},
|
|
211
|
+
]
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Rust extraction patterns
|
|
215
|
+
*/
|
|
216
|
+
const RUST_PATTERNS: ExtractionPattern[] = [
|
|
217
|
+
// Public function declarations
|
|
218
|
+
{
|
|
219
|
+
type: 'function',
|
|
220
|
+
pattern: /^pub\s+(?:async\s+)?fn\s+(\w+)(?:<[^>]+>)?\s*\(([^)]*)\)\s*(?:->\s*([^{]+))?\s*\{/gm,
|
|
221
|
+
nameIndex: 1,
|
|
222
|
+
exported: true,
|
|
223
|
+
},
|
|
224
|
+
// Private function declarations
|
|
225
|
+
{
|
|
226
|
+
type: 'function',
|
|
227
|
+
pattern: /^(?:async\s+)?fn\s+(\w+)(?:<[^>]+>)?\s*\(([^)]*)\)\s*(?:->\s*([^{]+))?\s*\{/gm,
|
|
228
|
+
nameIndex: 1,
|
|
229
|
+
},
|
|
230
|
+
// Struct definitions
|
|
231
|
+
{
|
|
232
|
+
type: 'class',
|
|
233
|
+
pattern: /^pub\s+struct\s+(\w+)(?:<[^>]+>)?\s*(?:\{|;)/gm,
|
|
234
|
+
nameIndex: 1,
|
|
235
|
+
exported: true,
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
type: 'class',
|
|
239
|
+
pattern: /^struct\s+(\w+)(?:<[^>]+>)?\s*(?:\{|;)/gm,
|
|
240
|
+
nameIndex: 1,
|
|
241
|
+
},
|
|
242
|
+
// Trait definitions
|
|
243
|
+
{
|
|
244
|
+
type: 'interface',
|
|
245
|
+
pattern: /^pub\s+trait\s+(\w+)(?:<[^>]+>)?\s*(?:\{|:)/gm,
|
|
246
|
+
nameIndex: 1,
|
|
247
|
+
exported: true,
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
type: 'interface',
|
|
251
|
+
pattern: /^trait\s+(\w+)(?:<[^>]+>)?\s*(?:\{|:)/gm,
|
|
252
|
+
nameIndex: 1,
|
|
253
|
+
},
|
|
254
|
+
// Enum definitions
|
|
255
|
+
{
|
|
256
|
+
type: 'enum',
|
|
257
|
+
pattern: /^pub\s+enum\s+(\w+)(?:<[^>]+>)?\s*\{/gm,
|
|
258
|
+
nameIndex: 1,
|
|
259
|
+
exported: true,
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
type: 'enum',
|
|
263
|
+
pattern: /^enum\s+(\w+)(?:<[^>]+>)?\s*\{/gm,
|
|
264
|
+
nameIndex: 1,
|
|
265
|
+
},
|
|
266
|
+
]
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Java extraction patterns
|
|
270
|
+
*/
|
|
271
|
+
const JAVA_PATTERNS: ExtractionPattern[] = [
|
|
272
|
+
// Class declarations
|
|
273
|
+
{
|
|
274
|
+
type: 'class',
|
|
275
|
+
pattern:
|
|
276
|
+
/^(?:public\s+)?(?:abstract\s+)?(?:final\s+)?class\s+(\w+)(?:<[^>]+>)?(?:\s+extends\s+\w+)?(?:\s+implements\s+[^{]+)?\s*\{/gm,
|
|
277
|
+
nameIndex: 1,
|
|
278
|
+
exported: true,
|
|
279
|
+
},
|
|
280
|
+
// Interface declarations
|
|
281
|
+
{
|
|
282
|
+
type: 'interface',
|
|
283
|
+
pattern: /^(?:public\s+)?interface\s+(\w+)(?:<[^>]+>)?(?:\s+extends\s+[^{]+)?\s*\{/gm,
|
|
284
|
+
nameIndex: 1,
|
|
285
|
+
exported: true,
|
|
286
|
+
},
|
|
287
|
+
// Method declarations
|
|
288
|
+
{
|
|
289
|
+
type: 'method',
|
|
290
|
+
pattern:
|
|
291
|
+
/^\s+(?:public|private|protected)?\s*(?:static\s+)?(?:final\s+)?(?:synchronized\s+)?(?:<[^>]+>\s+)?(\w+(?:<[^>]+>)?)\s+(\w+)\s*\([^)]*\)\s*(?:throws\s+[^{]+)?\s*\{/gm,
|
|
292
|
+
nameIndex: 2,
|
|
293
|
+
},
|
|
294
|
+
]
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Language to patterns mapping
|
|
298
|
+
*/
|
|
299
|
+
const LANGUAGE_PATTERNS: Record<LanguageId, ExtractionPattern[]> = {
|
|
300
|
+
typescript: TS_PATTERNS,
|
|
301
|
+
javascript: TS_PATTERNS,
|
|
302
|
+
python: PYTHON_PATTERNS,
|
|
303
|
+
go: GO_PATTERNS,
|
|
304
|
+
rust: RUST_PATTERNS,
|
|
305
|
+
java: JAVA_PATTERNS,
|
|
306
|
+
csharp: JAVA_PATTERNS, // Similar enough for basic extraction
|
|
307
|
+
php: [], // Fallback to full file
|
|
308
|
+
ruby: [], // Fallback to full file
|
|
309
|
+
unknown: [],
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// =============================================================================
|
|
313
|
+
// Main Function
|
|
314
|
+
// =============================================================================
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Extract code signatures from a file
|
|
318
|
+
*
|
|
319
|
+
* @param filePath - Path to the file (absolute or relative to cwd)
|
|
320
|
+
* @param projectPath - Project root path (for resolving relative paths)
|
|
321
|
+
* @returns Extracted signatures with compression metrics
|
|
322
|
+
*/
|
|
323
|
+
export async function extractSignatures(
|
|
324
|
+
filePath: string,
|
|
325
|
+
projectPath: string = process.cwd()
|
|
326
|
+
): Promise<SignaturesToolOutput> {
|
|
327
|
+
// Resolve to absolute path
|
|
328
|
+
const absolutePath = path.isAbsolute(filePath)
|
|
329
|
+
? filePath
|
|
330
|
+
: path.join(projectPath, filePath)
|
|
331
|
+
|
|
332
|
+
// Read file content
|
|
333
|
+
let content: string
|
|
334
|
+
try {
|
|
335
|
+
content = await fs.readFile(absolutePath, 'utf-8')
|
|
336
|
+
} catch (error) {
|
|
337
|
+
if (isNotFoundError(error)) {
|
|
338
|
+
return {
|
|
339
|
+
file: filePath,
|
|
340
|
+
language: 'unknown',
|
|
341
|
+
signatures: [],
|
|
342
|
+
fallback: true,
|
|
343
|
+
fallbackReason: 'File not found',
|
|
344
|
+
metrics: noCompression(''),
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
throw error
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Detect language
|
|
351
|
+
const ext = path.extname(filePath).toLowerCase()
|
|
352
|
+
const language = EXTENSION_TO_LANGUAGE[ext] || 'unknown'
|
|
353
|
+
const patterns = LANGUAGE_PATTERNS[language]
|
|
354
|
+
|
|
355
|
+
// No patterns = fallback to full file
|
|
356
|
+
if (!patterns || patterns.length === 0) {
|
|
357
|
+
return {
|
|
358
|
+
file: filePath,
|
|
359
|
+
language,
|
|
360
|
+
signatures: [],
|
|
361
|
+
fallback: true,
|
|
362
|
+
fallbackReason: `No extraction patterns for ${language}`,
|
|
363
|
+
metrics: noCompression(content),
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Extract signatures
|
|
368
|
+
const signatures = extractFromContent(content, patterns)
|
|
369
|
+
|
|
370
|
+
// Build filtered output (just signatures)
|
|
371
|
+
const filteredContent = signatures
|
|
372
|
+
.map((sig) => {
|
|
373
|
+
const exportPrefix = sig.exported ? 'export ' : ''
|
|
374
|
+
return `${exportPrefix}${sig.type} ${sig.name}: ${sig.signature}`
|
|
375
|
+
})
|
|
376
|
+
.join('\n')
|
|
377
|
+
|
|
378
|
+
return {
|
|
379
|
+
file: filePath,
|
|
380
|
+
language,
|
|
381
|
+
signatures,
|
|
382
|
+
fallback: false,
|
|
383
|
+
metrics: measureCompression(content, filteredContent),
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Extract signatures from multiple files in a directory
|
|
389
|
+
*/
|
|
390
|
+
export async function extractDirectorySignatures(
|
|
391
|
+
dirPath: string,
|
|
392
|
+
projectPath: string = process.cwd(),
|
|
393
|
+
options: { recursive?: boolean } = {}
|
|
394
|
+
): Promise<SignaturesToolOutput[]> {
|
|
395
|
+
const absolutePath = path.isAbsolute(dirPath)
|
|
396
|
+
? dirPath
|
|
397
|
+
: path.join(projectPath, dirPath)
|
|
398
|
+
|
|
399
|
+
const results: SignaturesToolOutput[] = []
|
|
400
|
+
|
|
401
|
+
async function processDir(dir: string): Promise<void> {
|
|
402
|
+
const entries = await fs.readdir(dir, { withFileTypes: true })
|
|
403
|
+
|
|
404
|
+
for (const entry of entries) {
|
|
405
|
+
const fullPath = path.join(dir, entry.name)
|
|
406
|
+
const relativePath = path.relative(projectPath, fullPath)
|
|
407
|
+
|
|
408
|
+
if (entry.isDirectory()) {
|
|
409
|
+
// Skip common ignore patterns
|
|
410
|
+
if (
|
|
411
|
+
entry.name === 'node_modules' ||
|
|
412
|
+
entry.name === '.git' ||
|
|
413
|
+
entry.name.startsWith('.')
|
|
414
|
+
) {
|
|
415
|
+
continue
|
|
416
|
+
}
|
|
417
|
+
if (options.recursive) {
|
|
418
|
+
await processDir(fullPath)
|
|
419
|
+
}
|
|
420
|
+
} else if (entry.isFile()) {
|
|
421
|
+
const ext = path.extname(entry.name).toLowerCase()
|
|
422
|
+
if (EXTENSION_TO_LANGUAGE[ext]) {
|
|
423
|
+
const result = await extractSignatures(relativePath, projectPath)
|
|
424
|
+
results.push(result)
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
await processDir(absolutePath)
|
|
431
|
+
return results
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// =============================================================================
|
|
435
|
+
// Helper Functions
|
|
436
|
+
// =============================================================================
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Extract signatures from content using patterns
|
|
440
|
+
*/
|
|
441
|
+
function extractFromContent(
|
|
442
|
+
content: string,
|
|
443
|
+
patterns: ExtractionPattern[]
|
|
444
|
+
): CodeSignature[] {
|
|
445
|
+
const signatures: CodeSignature[] = []
|
|
446
|
+
const lines = content.split('\n')
|
|
447
|
+
|
|
448
|
+
// Track what we've extracted to avoid duplicates
|
|
449
|
+
const seen = new Set<string>()
|
|
450
|
+
|
|
451
|
+
for (const patternDef of patterns) {
|
|
452
|
+
// Reset lastIndex for global regex
|
|
453
|
+
patternDef.pattern.lastIndex = 0
|
|
454
|
+
|
|
455
|
+
let match
|
|
456
|
+
while ((match = patternDef.pattern.exec(content)) !== null) {
|
|
457
|
+
const name = match[patternDef.nameIndex]
|
|
458
|
+
if (!name) continue
|
|
459
|
+
|
|
460
|
+
// Create a key for deduplication
|
|
461
|
+
const key = `${patternDef.type}:${name}`
|
|
462
|
+
if (seen.has(key)) continue
|
|
463
|
+
seen.add(key)
|
|
464
|
+
|
|
465
|
+
// Get line number
|
|
466
|
+
const matchIndex = match.index
|
|
467
|
+
const lineNumber = content.substring(0, matchIndex).split('\n').length
|
|
468
|
+
|
|
469
|
+
// Extract the full signature line
|
|
470
|
+
const signatureLine = match[0].trim()
|
|
471
|
+
|
|
472
|
+
// Try to extract docstring (line before the signature)
|
|
473
|
+
let docstring: string | undefined
|
|
474
|
+
if (lineNumber > 1) {
|
|
475
|
+
const prevLine = lines[lineNumber - 2]?.trim()
|
|
476
|
+
if (prevLine?.startsWith('/**') || prevLine?.startsWith('///') || prevLine?.startsWith('#')) {
|
|
477
|
+
docstring = prevLine
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
signatures.push({
|
|
482
|
+
type: patternDef.type,
|
|
483
|
+
name,
|
|
484
|
+
signature: cleanSignature(signatureLine),
|
|
485
|
+
exported: patternDef.exported || false,
|
|
486
|
+
line: lineNumber,
|
|
487
|
+
docstring,
|
|
488
|
+
})
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Sort by line number
|
|
493
|
+
return signatures.sort((a, b) => a.line - b.line)
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Clean up a signature line for display
|
|
498
|
+
*/
|
|
499
|
+
function cleanSignature(signature: string): string {
|
|
500
|
+
return signature
|
|
501
|
+
.replace(/\{$/, '') // Remove trailing {
|
|
502
|
+
.replace(/\s+/g, ' ') // Normalize whitespace
|
|
503
|
+
.trim()
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// =============================================================================
|
|
507
|
+
// Exports
|
|
508
|
+
// =============================================================================
|
|
509
|
+
|
|
510
|
+
export default { extractSignatures, extractDirectorySignatures }
|