ctxman 3.0.0 → 3.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/.ctxman/llm-profiles.json +1067 -39
- package/.ctxman/profiles/backend-team.json +13 -0
- package/.ctxman/profiles/default.json +12 -0
- package/.ctxman/profiles/devops-team.json +21 -0
- package/.ctxman/profiles/frontend-team.json +20 -0
- package/.ctxman/templates/bug-fix.json +17 -0
- package/.ctxman/templates/code-review.json +9 -0
- package/.ctxman/templates/documentation.json +9 -0
- package/.ctxman/templates/feature.json +9 -0
- package/.ctxman/templates/full-context.json +9 -0
- package/.ctxman/templates/refactoring.json +18 -0
- package/.ctxman/templates/testing.json +9 -0
- package/.ctxman/wizard-profiles/security-audit/profile.json +7 -1
- package/LICENSE +21 -0
- package/README.md +380 -136
- package/bin/cli.js +1880 -529
- package/bin/cm-ask.js +18 -13
- package/bin/cm-audit.js +124 -0
- package/bin/cm-count.js +151 -0
- package/bin/cm-gitingest.js +128 -115
- package/bin/cm-map.js +242 -0
- package/bin/cm-mcp.js +293 -0
- package/bin/cm-pack.js +405 -0
- package/bin/cm-policy.js +170 -0
- package/bin/cm-pr.js +248 -0
- package/bin/cm-search.js +178 -0
- package/bin/cm-update.js +128 -128
- package/bin/cm-why.js +197 -0
- package/bin/mcp-server.js +8 -24
- package/ctxman.js +237 -183
- package/examples/README.md +19 -4
- package/examples/github-actions/ctxman-check.yml +49 -0
- package/examples/wizard-profiles/security-audit/profile.json +7 -1
- package/index.d.ts +1422 -0
- package/index.js +55 -31
- package/lib/agents/pr-failure-agent.js +48 -45
- package/lib/analyzers/ai-suggester.js +451 -0
- package/lib/analyzers/dependency-scanner.js +505 -0
- package/lib/analyzers/duplicate-detector.js +264 -0
- package/lib/analyzers/go-method-analyzer.js +151 -110
- package/lib/analyzers/import-tracker.js +431 -0
- package/lib/analyzers/method-analyzer.js +1055 -434
- package/lib/analyzers/performance-dashboard.js +683 -0
- package/lib/analyzers/token-calculator.js +895 -504
- package/lib/analyzers/unused-exports-detector.js +336 -0
- package/lib/api/mcp/install.js +424 -0
- package/lib/api/mcp/prompts.js +281 -281
- package/lib/api/mcp/resources.js +300 -241
- package/lib/api/mcp/sandbox.js +89 -0
- package/lib/api/mcp/server.js +675 -312
- package/lib/api/mcp/start.js +125 -0
- package/lib/api/mcp/transports/HttpSseTransport.js +60 -52
- package/lib/api/rest/server.js +105 -28
- package/lib/cache/CacheManager.js +9 -17
- package/lib/core/Analyzer.js +16 -10
- package/lib/core/ContextBuilder.js +19 -28
- package/lib/core/Reporter.js +15 -14
- package/lib/core/Scanner.js +81 -12
- package/lib/count/index.js +100 -0
- package/lib/formatters/format-registry.js +394 -372
- package/lib/formatters/gitingest-formatter.js +656 -621
- package/lib/formatters/toon-formatter-v1.3.js +637 -632
- package/lib/formatters/toon-formatter.js +239 -240
- package/lib/integrations/git/BlameTracker.js +4 -4
- package/lib/integrations/git/DiffAnalyzer.js +38 -31
- package/lib/integrations/git/GitClient.js +71 -53
- package/lib/lsp/server.js +311 -0
- package/lib/map/graph.js +299 -0
- package/lib/map/imports.js +233 -0
- package/lib/map/index.js +33 -0
- package/lib/map/rank.js +253 -0
- package/lib/map/render.js +346 -0
- package/lib/map/resolver.js +574 -0
- package/lib/pack/budget.js +111 -0
- package/lib/pack/code-lexer.js +360 -0
- package/lib/pack/compress.js +414 -0
- package/lib/pack/index.js +354 -0
- package/lib/pack/renderers/common.js +134 -0
- package/lib/pack/renderers/index.js +24 -0
- package/lib/pack/renderers/json.js +47 -0
- package/lib/pack/renderers/markdown.js +54 -0
- package/lib/pack/renderers/plain.js +38 -0
- package/lib/pack/renderers/xml.js +85 -0
- package/lib/pack/select.js +177 -0
- package/lib/pack/split.js +182 -0
- package/lib/pack/tree.js +35 -0
- package/lib/parsers/gitignore-parser.js +113 -107
- package/lib/parsers/method-filter-parser.js +50 -33
- package/lib/plugins/ExporterPlugin.js +3 -3
- package/lib/plugins/LanguagePlugin.js +3 -3
- package/lib/plugins/PluginManager.js +7 -13
- package/lib/policy/audit.js +294 -0
- package/lib/policy/enforce.js +125 -0
- package/lib/policy/ignore-files.js +169 -0
- package/lib/policy/index.js +52 -0
- package/lib/policy/policy.js +489 -0
- package/lib/pr/diff-parser.js +267 -0
- package/lib/pr/method-mapper.js +148 -0
- package/lib/pr/pr-context.js +829 -0
- package/lib/pr/pr-git.js +149 -0
- package/lib/pr/pr-renderer.js +644 -0
- package/lib/pr/related-tests.js +296 -0
- package/lib/rag/DataSourcePlugin.js +5 -5
- package/lib/rag/EmbeddingProvider.js +19 -8
- package/lib/rag/Indexer.js +14 -5
- package/lib/rag/VectorStoreFactory.js +3 -3
- package/lib/rag/sources/FileDataSource.js +6 -6
- package/lib/rag/stores/BaseVectorStore.js +5 -5
- package/lib/rag/stores/ChromaDBStore.js +10 -10
- package/lib/rag/stores/LanceDBStore.js +29 -19
- package/lib/rag/stores/PineconeStore.js +11 -9
- package/lib/search/bm25.js +433 -0
- package/lib/search/code-units.js +201 -0
- package/lib/search/symbols.js +189 -0
- package/lib/ui/dashboard.js +120 -89
- package/lib/ui/index.js +1 -6
- package/lib/ui/init-wizard-ui.js +550 -0
- package/lib/ui/progress-bar.js +94 -88
- package/lib/ui/select-input.js +40 -32
- package/lib/ui/template-selector.js +99 -0
- package/lib/ui/wizard.js +374 -329
- package/lib/utils/claude-token-counter.js +151 -0
- package/lib/utils/cli-output.js +17 -0
- package/lib/utils/clipboard-utils.js +49 -50
- package/lib/utils/config-utils.js +81 -66
- package/lib/utils/context-versioning.js +532 -0
- package/lib/utils/error-handler.js +112 -112
- package/lib/utils/file-utils.js +153 -69
- package/lib/utils/format-converter.js +356 -283
- package/lib/utils/git-utils.js +366 -294
- package/lib/utils/llm-cost-estimator.js +281 -0
- package/lib/utils/llm-detector.js +242 -78
- package/lib/utils/logger.js +243 -237
- package/lib/utils/messages.js +355 -0
- package/lib/utils/multi-repo-manager.js +364 -0
- package/lib/utils/profile-manager.js +380 -0
- package/lib/utils/safe-read.js +61 -0
- package/lib/utils/secret-redactor.js +61 -0
- package/lib/utils/snapshot-manager.js +679 -0
- package/lib/utils/template-manager.js +378 -0
- package/lib/utils/token-utils.js +201 -170
- package/lib/utils/tokenizer-adapter.js +421 -377
- package/lib/utils/updater.js +539 -506
- package/lib/watch/FileWatcher.js +12 -8
- package/lib/watch/IncrementalAnalyzer.js +7 -7
- package/lib/why/coupling.js +81 -0
- package/lib/why/errors.js +23 -0
- package/lib/why/git.js +169 -0
- package/lib/why/history.js +108 -0
- package/lib/why/index.js +720 -0
- package/lib/why/ownership.js +79 -0
- package/lib/why/paths.js +207 -0
- package/lib/why/references.js +259 -0
- package/lib/why/render.js +722 -0
- package/lib/why/search.js +358 -0
- package/lib/why/target.js +389 -0
- package/lib/wizards/config-generator.js +255 -0
- package/lib/wizards/project-detector.js +571 -0
- package/package.json +44 -10
- package/.ctxman/logs/ctxman-2026-07-02.log +0 -156
- package/.ctxman/self-knowledge/documents.lance/_transactions/0-b3dd1558-f95b-44b9-a154-18e8c330ae7a.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/1-44bff6d4-e552-4010-a769-e09ad822698d.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/10-37260794-5947-4ecb-b4c8-d732f58f40f1.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/11-0d52a883-5518-463c-9b6d-986c3af89dfd.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/12-ae354646-52f2-49c9-90fb-2e6ed8932663.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/13-5d7d5e57-27da-498b-b734-091b9d38da13.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/14-558ce928-810b-4252-a364-fdd1ee6f8a25.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/15-27e3a58f-8b64-43b8-a9f4-e3369446968b.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/16-a110c390-e5c8-4464-b976-bc7f5195fa15.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/17-97513225-53de-4216-b61a-6337bd0cc4ad.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/18-5ba450f0-c527-459c-bf4f-6d79b5e4285f.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/19-600d3a63-5225-49e9-b8b7-30e037b564ab.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/2-48a61b4e-92cf-4342-8376-914c1b99a9f9.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/20-121dd912-5410-410e-84ff-82c7a23604a0.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/3-bc6844ab-7ea3-4bb3-9ee1-538ce5664a9c.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/4-d5e1754f-dbb5-454e-a9be-2a7da8d7bcf9.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/5-4fae93df-0568-4b6f-8acf-8bef4bba6047.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/6-4b1b8b87-068e-49a3-ba2b-d8dae628b252.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/7-63d040fb-d2fa-4c0f-bed0-53411918a7b0.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/8-c5628cf6-264c-420f-aab8-bc96a9083591.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/9-c94570aa-5e53-4fcb-971f-cecc1d1d5492.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/1.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/10.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/11.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/12.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/13.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/14.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/15.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/16.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/17.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/18.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/19.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/2.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/20.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/21.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/3.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/4.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/5.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/6.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/7.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/8.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/9.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/000010010011011101000010de8621405d9d5cb06540dbf02c.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/0000101001011100011000003fb62a4aa8978974075c144f1c.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/000011010000100111001011c6cc824d628f3bb1f4e04cd5d0.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/001000001000011001011100d8f74349ada090b332cd1df669.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/01000001111010001000010045ab44467bb1e7e4d8ae06bd58.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/01000101101110111110011131ef904241a89e1ffb6ddaf8e3.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/010010110000010000011010fbfcc145c6a8a827b28057a07e.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1000011000101010001110003fb4f045c3835982efa1405324.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/100111011101110111101011f4396b410fa06af3b028d204e6.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/100111100111101000001100596b174e00995d6cc2969cc8dc.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1010001100011000110010102ea16b484d887c7c9725feffd6.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/101101100010101111110111c5230944d9b3af7301b6392dda.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1011011100100111111110105892bf41b5a530fedd7cf516f1.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/10111110000010001011110164e8774742bade87b483c10f36.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1100000100010110001110103ffaca40a2a42c85067ebaf9ae.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/110000010001110110101110664bde4c76a78dfc14b05e83b8.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/11001100111000010100110050645c4281b1f35b2022234f7f.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1101111011001100110111017214924a368f5719eab2246619.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1110000111001010001000002150c24dcab653d39d07032e63.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1110100000000100000011014d20534967a18b5c87721659ff.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/11110101111010000101011135a0114ef2b35dc6f1297a3381.lance +0 -0
|
@@ -6,122 +6,128 @@
|
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
|
|
8
8
|
class GitIgnoreParser {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
constructor(gitignorePath, contextIgnorePath, contextIncludePath) {
|
|
10
|
+
this.patterns = [];
|
|
11
|
+
this.contextPatterns = [];
|
|
12
|
+
this.hasIncludeFile = false;
|
|
13
|
+
this._lastIgnoreReason = null;
|
|
14
|
+
|
|
15
|
+
this.loadPatterns(gitignorePath, contextIgnorePath, contextIncludePath);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
loadPatterns(gitignorePath, contextIgnorePath, contextIncludePath) {
|
|
19
|
+
// Load .gitignore
|
|
20
|
+
if (gitignorePath && fs.existsSync(gitignorePath)) {
|
|
21
|
+
this.patterns = this.parsePatternFile(gitignorePath);
|
|
16
22
|
}
|
|
17
23
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
this.contextPatterns = this.parsePatternFile(contextIncludePath);
|
|
27
|
-
this.hasIncludeFile = true;
|
|
28
|
-
console.log(`📅 Context include rules loaded: ${this.contextPatterns.length} patterns`);
|
|
29
|
-
} else if (contextIgnorePath && fs.existsSync(contextIgnorePath)) {
|
|
30
|
-
this.contextPatterns = this.parsePatternFile(contextIgnorePath);
|
|
31
|
-
console.log(`📋 Context ignore rules loaded: ${this.contextPatterns.length} patterns`);
|
|
32
|
-
}
|
|
24
|
+
// Load context patterns (include takes priority)
|
|
25
|
+
if (contextIncludePath && fs.existsSync(contextIncludePath)) {
|
|
26
|
+
this.contextPatterns = this.parsePatternFile(contextIncludePath);
|
|
27
|
+
this.hasIncludeFile = true;
|
|
28
|
+
console.log(`📅 Context include rules loaded: ${this.contextPatterns.length} patterns`);
|
|
29
|
+
} else if (contextIgnorePath && fs.existsSync(contextIgnorePath)) {
|
|
30
|
+
this.contextPatterns = this.parsePatternFile(contextIgnorePath);
|
|
31
|
+
console.log(`📋 Context ignore rules loaded: ${this.contextPatterns.length} patterns`);
|
|
33
32
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// File doesn't exist or can't be read
|
|
51
|
-
return [];
|
|
52
|
-
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
parsePatternFile(filePath) {
|
|
36
|
+
try {
|
|
37
|
+
return fs
|
|
38
|
+
.readFileSync(filePath, 'utf8')
|
|
39
|
+
.split('\n')
|
|
40
|
+
.map((line) => line.trim())
|
|
41
|
+
.filter((line) => line && !line.startsWith('#'))
|
|
42
|
+
.map((pattern) => this.convertToRegex(pattern));
|
|
43
|
+
} catch (error) {
|
|
44
|
+
// A directory, or a file that doesn't exist or can't be read
|
|
45
|
+
if (error.code === 'EISDIR') {
|
|
46
|
+
console.warn(`Warning: ${filePath} is a directory, expected a file`);
|
|
47
|
+
}
|
|
48
|
+
return [];
|
|
53
49
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
convertToRegex(pattern) {
|
|
53
|
+
const isDirectory = pattern.endsWith('/');
|
|
54
|
+
const isNegation = pattern.startsWith('!');
|
|
55
|
+
const original = pattern;
|
|
56
|
+
|
|
57
|
+
pattern = pattern.replace(/^[!/]/, '').replace(/\/$/, '');
|
|
58
|
+
|
|
59
|
+
// Placeholders keep the `*` inside `.*` from being rewritten by the single-star rule
|
|
60
|
+
let regexPattern = pattern
|
|
61
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
62
|
+
.replace(/\*\*\//g, '\u0001') // `**/`: zero or more directories
|
|
63
|
+
.replace(/\*\*/g, '\u0002') // any other `**`: anything, across directories
|
|
64
|
+
.replace(/\*/g, '[^/]*')
|
|
65
|
+
.replace(/\?/g, '[^/]')
|
|
66
|
+
.replace(/\u0001/g, '(?:.*/)?')
|
|
67
|
+
.replace(/\u0002/g, '.*');
|
|
68
|
+
|
|
69
|
+
regexPattern = original.startsWith('/') ? `^${regexPattern}` : `(^|/)${regexPattern}`;
|
|
70
|
+
|
|
71
|
+
regexPattern += isDirectory ? '(/.*)?$' : '$';
|
|
72
|
+
|
|
73
|
+
return { regex: new RegExp(regexPattern), isNegation, original, isDirectory };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
isIgnored(filePath, relativePath) {
|
|
77
|
+
let ignored = this.testPatterns(this.patterns, relativePath, 'gitignore');
|
|
78
|
+
|
|
79
|
+
if (this.hasIncludeFile) {
|
|
80
|
+
if (ignored) return true; // Keep gitignore exclusions
|
|
81
|
+
|
|
82
|
+
if (this.isDirectory(filePath)) {
|
|
83
|
+
const shouldTraverse = this.contextPatterns.some(
|
|
84
|
+
(p) =>
|
|
85
|
+
p.original.startsWith(relativePath) || p.original.includes('**') || relativePath === ''
|
|
86
|
+
);
|
|
87
|
+
ignored = !shouldTraverse;
|
|
88
|
+
} else {
|
|
89
|
+
const included = this.testPatternsWithNegation(this.contextPatterns, relativePath);
|
|
90
|
+
ignored = !included;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (ignored) this._lastIgnoreReason = 'context-include';
|
|
94
|
+
} else if (!ignored) {
|
|
95
|
+
ignored = this.testPatterns(this.contextPatterns, relativePath, 'context');
|
|
75
96
|
}
|
|
76
97
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (this.hasIncludeFile) {
|
|
81
|
-
if (ignored) return true; // Keep gitignore exclusions
|
|
82
|
-
|
|
83
|
-
const stat = fs.statSync(filePath);
|
|
84
|
-
if (stat.isDirectory()) {
|
|
85
|
-
const shouldTraverse = this.contextPatterns.some(p =>
|
|
86
|
-
p.original.startsWith(relativePath) ||
|
|
87
|
-
p.original.includes('**') ||
|
|
88
|
-
relativePath === ''
|
|
89
|
-
);
|
|
90
|
-
ignored = !shouldTraverse;
|
|
91
|
-
} else {
|
|
92
|
-
const included = this.testPatternsWithNegation(this.contextPatterns, relativePath);
|
|
93
|
-
ignored = !included;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (ignored) this._lastIgnoreReason = 'context-include';
|
|
97
|
-
} else if (!ignored) {
|
|
98
|
-
ignored = this.testPatterns(this.contextPatterns, relativePath, 'context');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return ignored;
|
|
102
|
-
}
|
|
98
|
+
return ignored;
|
|
99
|
+
}
|
|
103
100
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return ignored;
|
|
101
|
+
isDirectory(filePath) {
|
|
102
|
+
try {
|
|
103
|
+
return fs.statSync(filePath).isDirectory();
|
|
104
|
+
} catch (_error) {
|
|
105
|
+
// Deleted (e.g. a watch-mode "rename" event) or unreadable: treat it as a file
|
|
106
|
+
return false;
|
|
113
107
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
testPatterns(patterns, relativePath, reason) {
|
|
111
|
+
let ignored = false;
|
|
112
|
+
for (const pattern of patterns) {
|
|
113
|
+
if (pattern.regex.test(relativePath)) {
|
|
114
|
+
ignored = !pattern.isNegation;
|
|
115
|
+
if (ignored) this._lastIgnoreReason = reason;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return ignored;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
testPatternsWithNegation(patterns, relativePath) {
|
|
122
|
+
let included = false;
|
|
123
|
+
for (const pattern of patterns) {
|
|
124
|
+
if (pattern.regex.test(relativePath)) {
|
|
125
|
+
included = pattern.isNegation ? false : true;
|
|
126
|
+
if (pattern.isNegation) break;
|
|
127
|
+
}
|
|
124
128
|
}
|
|
129
|
+
return included;
|
|
130
|
+
}
|
|
125
131
|
}
|
|
126
132
|
|
|
127
133
|
export default GitIgnoreParser;
|
|
@@ -6,45 +6,62 @@
|
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
|
|
8
8
|
class MethodFilterParser {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (methodIgnorePath && fs.existsSync(methodIgnorePath)) {
|
|
21
|
-
this.ignorePatterns = this.parseMethodFile(methodIgnorePath);
|
|
22
|
-
console.log(`🚫 Method ignore rules loaded: ${this.ignorePatterns.length} patterns`);
|
|
23
|
-
}
|
|
9
|
+
constructor(methodIncludePath, methodIgnorePath) {
|
|
10
|
+
this.includePatterns = [];
|
|
11
|
+
this.ignorePatterns = [];
|
|
12
|
+
this.hasIncludeFile = false;
|
|
13
|
+
|
|
14
|
+
if (methodIncludePath && fs.existsSync(methodIncludePath)) {
|
|
15
|
+
this.includePatterns = this.parseMethodFile(methodIncludePath);
|
|
16
|
+
this.hasIncludeFile = true;
|
|
17
|
+
console.log(`🔧 Method include rules loaded: ${this.includePatterns.length} patterns`);
|
|
24
18
|
}
|
|
25
19
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
.map(line => line.trim())
|
|
30
|
-
.filter(line => line && !line.startsWith('#'))
|
|
31
|
-
.map(pattern => ({
|
|
32
|
-
pattern: pattern,
|
|
33
|
-
regex: new RegExp(pattern.replace(/\*/g, '.*'), 'i')
|
|
34
|
-
}));
|
|
20
|
+
if (methodIgnorePath && fs.existsSync(methodIgnorePath)) {
|
|
21
|
+
this.ignorePatterns = this.parseMethodFile(methodIgnorePath);
|
|
22
|
+
console.log(`🚫 Method ignore rules loaded: ${this.ignorePatterns.length} patterns`);
|
|
35
23
|
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
parseMethodFile(filePath) {
|
|
27
|
+
return (
|
|
28
|
+
fs
|
|
29
|
+
.readFileSync(filePath, 'utf8')
|
|
30
|
+
.split('\n')
|
|
31
|
+
// Trailing comments (`*Handler # all handlers`) as in the documented examples
|
|
32
|
+
.map((line) => line.replace(/\s+#.*$/, '').trim())
|
|
33
|
+
.filter((line) => line && !line.startsWith('#'))
|
|
34
|
+
.map((line) => {
|
|
35
|
+
const negated = line.startsWith('!');
|
|
36
|
+
const pattern = negated ? line.slice(1) : line;
|
|
37
|
+
return {
|
|
38
|
+
pattern: line,
|
|
39
|
+
negated,
|
|
40
|
+
// Anchored so a plain name is an exact match and `*Handler` / `handle*` are suffix /
|
|
41
|
+
// prefix matches, as documented. `*` is the wildcard; other regex characters are
|
|
42
|
+
// literal (Ruby's `valid?`, `$emit`). `.` stays a regex dot: it still matches the
|
|
43
|
+
// `file.method` separator, and patterns such as `get.*Data` rely on it.
|
|
44
|
+
regex: new RegExp(
|
|
45
|
+
`^${pattern.replace(/[+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*')}$`,
|
|
46
|
+
'i'
|
|
47
|
+
),
|
|
48
|
+
};
|
|
49
|
+
})
|
|
50
|
+
);
|
|
51
|
+
}
|
|
36
52
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return this.includePatterns.some(p =>
|
|
40
|
-
p.regex.test(methodName) || p.regex.test(`${fileName}.${methodName}`)
|
|
41
|
-
);
|
|
42
|
-
}
|
|
53
|
+
shouldIncludeMethod(methodName, fileName) {
|
|
54
|
+
const matches = (p) => p.regex.test(methodName) || p.regex.test(`${fileName}.${methodName}`);
|
|
43
55
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
56
|
+
// The last matching pattern decides, so a later `!pattern` overrides a broader one
|
|
57
|
+
if (this.hasIncludeFile) {
|
|
58
|
+
const match = this.includePatterns.findLast(matches);
|
|
59
|
+
return Boolean(match) && !match.negated;
|
|
47
60
|
}
|
|
61
|
+
|
|
62
|
+
const match = this.ignorePatterns.findLast(matches);
|
|
63
|
+
return !match || match.negated;
|
|
64
|
+
}
|
|
48
65
|
}
|
|
49
66
|
|
|
50
67
|
export default MethodFilterParser;
|
|
@@ -24,7 +24,7 @@ export class ExporterPlugin {
|
|
|
24
24
|
extension: this.extension,
|
|
25
25
|
mimeType: this.mimeType,
|
|
26
26
|
supportsChunking: this.supportsChunking(),
|
|
27
|
-
supportsCompression: this.supportsCompression()
|
|
27
|
+
supportsCompression: this.supportsCompression(),
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -50,7 +50,7 @@ export class ExporterPlugin {
|
|
|
50
50
|
* @param {object} options
|
|
51
51
|
* @returns {string}
|
|
52
52
|
*/
|
|
53
|
-
encode(context,
|
|
53
|
+
encode(context, _options = {}) {
|
|
54
54
|
throw new Error('encode() must be implemented by subclass');
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -59,7 +59,7 @@ export class ExporterPlugin {
|
|
|
59
59
|
* @param {string} content
|
|
60
60
|
* @returns {Context}
|
|
61
61
|
*/
|
|
62
|
-
decode(
|
|
62
|
+
decode(_content) {
|
|
63
63
|
throw new Error('decode() not supported for this format');
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -23,7 +23,7 @@ export class LanguagePlugin {
|
|
|
23
23
|
extensions: this.extensions,
|
|
24
24
|
supportsAST: this.supportsAST(),
|
|
25
25
|
supportsMethodExtraction: this.supportsMethodExtraction(),
|
|
26
|
-
supportsFrameworkDetection: this.supportsFrameworkDetection()
|
|
26
|
+
supportsFrameworkDetection: this.supportsFrameworkDetection(),
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -57,7 +57,7 @@ export class LanguagePlugin {
|
|
|
57
57
|
* @param {string} filePath - File path
|
|
58
58
|
* @returns {Array<Method>}
|
|
59
59
|
*/
|
|
60
|
-
extractMethods(
|
|
60
|
+
extractMethods(_content, _filePath) {
|
|
61
61
|
throw new Error('extractMethods() must be implemented by subclass');
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -66,7 +66,7 @@ export class LanguagePlugin {
|
|
|
66
66
|
* @param {string} content - File content
|
|
67
67
|
* @returns {string|null} Framework name or null
|
|
68
68
|
*/
|
|
69
|
-
detectFramework(
|
|
69
|
+
detectFramework(_content) {
|
|
70
70
|
return null;
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -21,14 +21,10 @@ export class PluginManager extends EventEmitter {
|
|
|
21
21
|
super();
|
|
22
22
|
|
|
23
23
|
this.options = {
|
|
24
|
-
pluginPaths: [
|
|
25
|
-
'./lib/languages',
|
|
26
|
-
'./lib/exporters',
|
|
27
|
-
'./plugins'
|
|
28
|
-
],
|
|
24
|
+
pluginPaths: ['./lib/languages', './lib/exporters', './plugins'],
|
|
29
25
|
autoLoad: true,
|
|
30
26
|
lazy: true, // Lazy load plugins
|
|
31
|
-
...options
|
|
27
|
+
...options,
|
|
32
28
|
};
|
|
33
29
|
|
|
34
30
|
this.plugins = new Map();
|
|
@@ -36,7 +32,7 @@ export class PluginManager extends EventEmitter {
|
|
|
36
32
|
this.stats = {
|
|
37
33
|
registered: 0,
|
|
38
34
|
loaded: 0,
|
|
39
|
-
errors: 0
|
|
35
|
+
errors: 0,
|
|
40
36
|
};
|
|
41
37
|
}
|
|
42
38
|
|
|
@@ -103,12 +99,11 @@ export class PluginManager extends EventEmitter {
|
|
|
103
99
|
name: pluginName,
|
|
104
100
|
path: pluginPath,
|
|
105
101
|
loaded: false,
|
|
106
|
-
instance: null
|
|
102
|
+
instance: null,
|
|
107
103
|
});
|
|
108
104
|
|
|
109
105
|
this.stats.registered++;
|
|
110
106
|
logger.debug(`Registered plugin: ${pluginName}`);
|
|
111
|
-
|
|
112
107
|
} catch (error) {
|
|
113
108
|
logger.error(`Failed to register plugin ${pluginPath}: ${error.message}`);
|
|
114
109
|
this.stats.errors++;
|
|
@@ -158,7 +153,6 @@ export class PluginManager extends EventEmitter {
|
|
|
158
153
|
this.emit('plugin:loaded', { name: pluginName, elapsed });
|
|
159
154
|
|
|
160
155
|
return instance;
|
|
161
|
-
|
|
162
156
|
} catch (error) {
|
|
163
157
|
logger.error(`Failed to load plugin ${pluginName}: ${error.message}`);
|
|
164
158
|
this.stats.errors++;
|
|
@@ -173,7 +167,7 @@ export class PluginManager extends EventEmitter {
|
|
|
173
167
|
* @returns {Promise<Array<Plugin>>}
|
|
174
168
|
*/
|
|
175
169
|
async loadMultiple(pluginNames) {
|
|
176
|
-
const promises = pluginNames.map(name => this.load(name));
|
|
170
|
+
const promises = pluginNames.map((name) => this.load(name));
|
|
177
171
|
return Promise.all(promises);
|
|
178
172
|
}
|
|
179
173
|
|
|
@@ -266,7 +260,7 @@ export class PluginManager extends EventEmitter {
|
|
|
266
260
|
name: plugin.name,
|
|
267
261
|
loaded: plugin.loaded,
|
|
268
262
|
path: plugin.path,
|
|
269
|
-
hasInstance: plugin.instance !== null
|
|
263
|
+
hasInstance: plugin.instance !== null,
|
|
270
264
|
};
|
|
271
265
|
}
|
|
272
266
|
|
|
@@ -288,7 +282,7 @@ export class PluginManager extends EventEmitter {
|
|
|
288
282
|
name,
|
|
289
283
|
path: null,
|
|
290
284
|
loaded: true,
|
|
291
|
-
instance
|
|
285
|
+
instance,
|
|
292
286
|
});
|
|
293
287
|
|
|
294
288
|
this.stats.registered++;
|