ctxman 3.0.0 ā 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.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 +381 -136
- package/bin/cli.js +1880 -529
- package/bin/cm-ask.js +18 -67
- package/bin/cm-audit.js +121 -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 +687 -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 +772 -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 +116 -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 +312 -0
- package/lib/policy/enforce.js +125 -0
- package/lib/policy/ignore-files.js +169 -0
- package/lib/policy/index.js +53 -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/ask.js +122 -0
- package/lib/rag/sources/FileDataSource.js +12 -9
- 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 +246 -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 +257 -0
- package/lib/wizards/project-detector.js +572 -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
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template Manager
|
|
3
|
+
* Manages context templates for different development tasks
|
|
4
|
+
* FEAT-009: Context Templates
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { dirname } from 'path';
|
|
11
|
+
import { DEFAULT_TARGET_MODEL } from './llm-detector.js';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Built-in templates defined in code for reliability
|
|
18
|
+
*/
|
|
19
|
+
const BUILTIN_TEMPLATES = {
|
|
20
|
+
'bug-fix': {
|
|
21
|
+
name: 'Bug Fix Context',
|
|
22
|
+
description: 'Optimized for debugging and fixing bugs',
|
|
23
|
+
include: ['src/**/*.js', 'lib/**/*.js', '!src/**/*.test.js', '!lib/**/*.test.js'],
|
|
24
|
+
exclude: [
|
|
25
|
+
'docs/**',
|
|
26
|
+
'*.md',
|
|
27
|
+
'*.json',
|
|
28
|
+
'examples/**',
|
|
29
|
+
'test/**',
|
|
30
|
+
'**/*.test.js',
|
|
31
|
+
'**/*.spec.js',
|
|
32
|
+
],
|
|
33
|
+
methodInclude: ['*Handler', '*Service', '*Controller', '*Manager'],
|
|
34
|
+
methodExclude: ['*test*', 'console*', 'debug*', 'log*'],
|
|
35
|
+
targetModel: DEFAULT_TARGET_MODEL,
|
|
36
|
+
},
|
|
37
|
+
feature: {
|
|
38
|
+
name: 'Feature Development Context',
|
|
39
|
+
description: 'Optimized for implementing new features',
|
|
40
|
+
include: ['src/**/*.js', 'lib/**/*.js', '!src/**/*.test.js'],
|
|
41
|
+
exclude: ['docs/**', '*.md', 'examples/**'],
|
|
42
|
+
methodInclude: ['*Handler', '*Service', '*Controller', '*Manager', '*Factory', '*Builder'],
|
|
43
|
+
methodExclude: ['*test*', 'console*'],
|
|
44
|
+
targetModel: DEFAULT_TARGET_MODEL,
|
|
45
|
+
},
|
|
46
|
+
'code-review': {
|
|
47
|
+
name: 'Code Review Context',
|
|
48
|
+
description: 'Optimized for reviewing code changes',
|
|
49
|
+
include: ['src/**/*.js', 'lib/**/*.js', 'bin/**/*.js'],
|
|
50
|
+
exclude: ['docs/**', '*.md', '*.json', 'node_modules/**'],
|
|
51
|
+
methodInclude: ['*Handler', '*Service', '*Controller', '*Manager', '*Parser', '*Analyzer'],
|
|
52
|
+
methodExclude: ['*test*'],
|
|
53
|
+
targetModel: DEFAULT_TARGET_MODEL,
|
|
54
|
+
},
|
|
55
|
+
documentation: {
|
|
56
|
+
name: 'Documentation Context',
|
|
57
|
+
description: 'Optimized for writing documentation',
|
|
58
|
+
include: ['src/**/*.js', 'lib/**/*.js', 'index.js'],
|
|
59
|
+
exclude: ['**/*.test.js', '**/*.spec.js', 'test/**', 'examples/**', 'node_modules/**'],
|
|
60
|
+
methodInclude: ['*'],
|
|
61
|
+
methodExclude: ['_*', '*test*', '*internal*'],
|
|
62
|
+
targetModel: DEFAULT_TARGET_MODEL,
|
|
63
|
+
},
|
|
64
|
+
refactoring: {
|
|
65
|
+
name: 'Refactoring Context',
|
|
66
|
+
description: 'Optimized for code refactoring and restructuring',
|
|
67
|
+
include: ['src/**/*.js', 'lib/**/*.js', 'bin/**/*.js', 'test/**/*.js'],
|
|
68
|
+
exclude: ['docs/**', '*.md', 'node_modules/**'],
|
|
69
|
+
methodInclude: [
|
|
70
|
+
'*Handler',
|
|
71
|
+
'*Service',
|
|
72
|
+
'*Controller',
|
|
73
|
+
'*Manager',
|
|
74
|
+
'*Parser',
|
|
75
|
+
'*Analyzer',
|
|
76
|
+
'*Utils',
|
|
77
|
+
'*Helper',
|
|
78
|
+
],
|
|
79
|
+
methodExclude: ['*test*'],
|
|
80
|
+
targetModel: DEFAULT_TARGET_MODEL,
|
|
81
|
+
},
|
|
82
|
+
testing: {
|
|
83
|
+
name: 'Testing Context',
|
|
84
|
+
description: 'Optimized for writing tests',
|
|
85
|
+
include: ['src/**/*.js', 'lib/**/*.js', 'test/**/*.js'],
|
|
86
|
+
exclude: ['docs/**', '*.md', 'examples/**', 'node_modules/**'],
|
|
87
|
+
methodInclude: ['*'],
|
|
88
|
+
methodExclude: ['_*', 'console*'],
|
|
89
|
+
targetModel: DEFAULT_TARGET_MODEL,
|
|
90
|
+
},
|
|
91
|
+
'full-context': {
|
|
92
|
+
name: 'Full Context',
|
|
93
|
+
description: 'Complete project context for comprehensive analysis',
|
|
94
|
+
include: ['**/*'],
|
|
95
|
+
exclude: ['node_modules/**', '.git/**', 'coverage/**', 'dist/**', 'build/**'],
|
|
96
|
+
methodInclude: ['*'],
|
|
97
|
+
methodExclude: [],
|
|
98
|
+
targetModel: DEFAULT_TARGET_MODEL,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
class TemplateManager {
|
|
103
|
+
/**
|
|
104
|
+
* Create a TemplateManager instance
|
|
105
|
+
* @param {string} projectRoot - Project root directory
|
|
106
|
+
*/
|
|
107
|
+
constructor(projectRoot = process.cwd()) {
|
|
108
|
+
this.projectRoot = projectRoot;
|
|
109
|
+
this.customTemplatesPath = path.join(projectRoot, '.ctxman', 'templates');
|
|
110
|
+
this.builtinTemplatesPath = path.join(__dirname, '..', '..', '.ctxman', 'templates');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* List all available templates (built-in + custom)
|
|
115
|
+
* @returns {Array} Array of template objects
|
|
116
|
+
*/
|
|
117
|
+
list() {
|
|
118
|
+
const templates = [];
|
|
119
|
+
|
|
120
|
+
// Add built-in templates
|
|
121
|
+
for (const [id, template] of Object.entries(BUILTIN_TEMPLATES)) {
|
|
122
|
+
templates.push({
|
|
123
|
+
id,
|
|
124
|
+
...template,
|
|
125
|
+
source: 'builtin',
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Add custom templates from project .ctxman/templates/
|
|
130
|
+
const customTemplates = this.loadCustomTemplates();
|
|
131
|
+
templates.push(...customTemplates);
|
|
132
|
+
|
|
133
|
+
return templates;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get a specific template by ID
|
|
138
|
+
* @param {string} templateId - Template identifier
|
|
139
|
+
* @returns {object|null} Template object or null if not found
|
|
140
|
+
*/
|
|
141
|
+
get(templateId) {
|
|
142
|
+
// Check built-in first
|
|
143
|
+
if (BUILTIN_TEMPLATES[templateId]) {
|
|
144
|
+
return {
|
|
145
|
+
id: templateId,
|
|
146
|
+
...BUILTIN_TEMPLATES[templateId],
|
|
147
|
+
source: 'builtin',
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Check custom templates
|
|
152
|
+
const customTemplates = this.loadCustomTemplates();
|
|
153
|
+
const custom = customTemplates.find((t) => t.id === templateId);
|
|
154
|
+
if (custom) {
|
|
155
|
+
return custom;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Load custom templates from .ctxman/templates/
|
|
163
|
+
* @returns {Array} Array of custom template objects
|
|
164
|
+
*/
|
|
165
|
+
loadCustomTemplates() {
|
|
166
|
+
const templates = [];
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
if (!fs.existsSync(this.customTemplatesPath)) {
|
|
170
|
+
return templates;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const files = fs.readdirSync(this.customTemplatesPath);
|
|
174
|
+
|
|
175
|
+
for (const file of files) {
|
|
176
|
+
if (!file.endsWith('.json')) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const filePath = path.join(this.customTemplatesPath, file);
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
184
|
+
const template = JSON.parse(content);
|
|
185
|
+
const id = path.basename(file, '.json');
|
|
186
|
+
|
|
187
|
+
// Skip if already defined as built-in (built-ins take precedence)
|
|
188
|
+
if (BUILTIN_TEMPLATES[id]) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
templates.push({
|
|
193
|
+
id,
|
|
194
|
+
...template,
|
|
195
|
+
source: 'custom',
|
|
196
|
+
});
|
|
197
|
+
} catch (parseError) {
|
|
198
|
+
console.error(`Warning: Failed to parse template ${file}: ${parseError.message}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.error(`Warning: Failed to load custom templates: ${error.message}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return templates;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Apply a template and get configuration options
|
|
210
|
+
* @param {string} templateId - Template identifier
|
|
211
|
+
* @param {object} options - Additional options to merge
|
|
212
|
+
* @returns {object} Merged configuration options
|
|
213
|
+
*/
|
|
214
|
+
apply(templateId, options = {}) {
|
|
215
|
+
const template = this.get(templateId);
|
|
216
|
+
|
|
217
|
+
if (!template) {
|
|
218
|
+
throw new Error(`Template '${templateId}' not found`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Build configuration from template
|
|
222
|
+
const config = {
|
|
223
|
+
templateId,
|
|
224
|
+
templateName: template.name,
|
|
225
|
+
templateDescription: template.description,
|
|
226
|
+
templateSource: template.source,
|
|
227
|
+
// Convert include/exclude patterns to ignore patterns for TokenAnalyzer
|
|
228
|
+
ignorePatterns: this.buildIgnorePatterns(template),
|
|
229
|
+
includePatterns: template.include || [],
|
|
230
|
+
methodInclude: template.methodInclude || [],
|
|
231
|
+
methodExclude: template.methodExclude || [],
|
|
232
|
+
targetModel: template.targetModel || options.targetModel,
|
|
233
|
+
...options,
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
return config;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Build ignore patterns from template exclude list
|
|
241
|
+
* @param {object} template - Template object
|
|
242
|
+
* @returns {Array} Array of ignore patterns
|
|
243
|
+
*/
|
|
244
|
+
buildIgnorePatterns(template) {
|
|
245
|
+
const patterns = [];
|
|
246
|
+
|
|
247
|
+
if (template.exclude) {
|
|
248
|
+
patterns.push(...template.exclude);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return patterns;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Create a custom template
|
|
256
|
+
* @param {string} id - Template identifier
|
|
257
|
+
* @param {object} template - Template configuration
|
|
258
|
+
* @returns {string} Path to created template file
|
|
259
|
+
*/
|
|
260
|
+
createCustom(id, template) {
|
|
261
|
+
// Ensure .ctxman/templates directory exists
|
|
262
|
+
if (!fs.existsSync(this.customTemplatesPath)) {
|
|
263
|
+
fs.mkdirSync(this.customTemplatesPath, { recursive: true });
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const filePath = path.join(this.customTemplatesPath, `${id}.json`);
|
|
267
|
+
|
|
268
|
+
// Validate template structure
|
|
269
|
+
this.validateTemplate(template);
|
|
270
|
+
|
|
271
|
+
// Write template file
|
|
272
|
+
try {
|
|
273
|
+
// 'wx' fails when the file exists, without a separate check that could go stale
|
|
274
|
+
fs.writeFileSync(filePath, JSON.stringify(template, null, 2), { flag: 'wx' });
|
|
275
|
+
} catch (error) {
|
|
276
|
+
if (error.code === 'EEXIST') throw new Error(`Template '${id}' already exists`);
|
|
277
|
+
throw error;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return filePath;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Validate template structure
|
|
285
|
+
* @param {object} template - Template to validate
|
|
286
|
+
* @throws {Error} If template is invalid
|
|
287
|
+
*/
|
|
288
|
+
validateTemplate(template) {
|
|
289
|
+
if (!template.name || typeof template.name !== 'string') {
|
|
290
|
+
throw new Error('Template must have a "name" string property');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (!template.description || typeof template.description !== 'string') {
|
|
294
|
+
throw new Error('Template must have a "description" string property');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (template.include && !Array.isArray(template.include)) {
|
|
298
|
+
throw new Error('Template "include" must be an array');
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (template.exclude && !Array.isArray(template.exclude)) {
|
|
302
|
+
throw new Error('Template "exclude" must be an array');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (template.methodInclude && !Array.isArray(template.methodInclude)) {
|
|
306
|
+
throw new Error('Template "methodInclude" must be an array');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (template.methodExclude && !Array.isArray(template.methodExclude)) {
|
|
310
|
+
throw new Error('Template "methodExclude" must be an array');
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Delete a custom template
|
|
316
|
+
* @param {string} id - Template identifier
|
|
317
|
+
* @returns {boolean} True if deleted, false if not found
|
|
318
|
+
*/
|
|
319
|
+
deleteCustom(id) {
|
|
320
|
+
// Prevent deletion of built-in templates
|
|
321
|
+
if (BUILTIN_TEMPLATES[id]) {
|
|
322
|
+
throw new Error('Cannot delete built-in templates');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const filePath = path.join(this.customTemplatesPath, `${id}.json`);
|
|
326
|
+
|
|
327
|
+
if (!fs.existsSync(filePath)) {
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
fs.unlinkSync(filePath);
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Format templates for display
|
|
337
|
+
* @returns {string} Formatted template list
|
|
338
|
+
*/
|
|
339
|
+
formatList() {
|
|
340
|
+
const templates = this.list();
|
|
341
|
+
const lines = [];
|
|
342
|
+
|
|
343
|
+
lines.push('\nš Available Context Templates:\n');
|
|
344
|
+
lines.push('ā'.repeat(70));
|
|
345
|
+
|
|
346
|
+
// Group by source
|
|
347
|
+
const builtin = templates.filter((t) => t.source === 'builtin');
|
|
348
|
+
const custom = templates.filter((t) => t.source === 'custom');
|
|
349
|
+
|
|
350
|
+
if (builtin.length > 0) {
|
|
351
|
+
lines.push('\nBuilt-in Templates:');
|
|
352
|
+
builtin.forEach((t) => {
|
|
353
|
+
lines.push(` ${t.id.padEnd(15)} ${t.description}`);
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (custom.length > 0) {
|
|
358
|
+
lines.push('\nCustom Templates:');
|
|
359
|
+
custom.forEach((t) => {
|
|
360
|
+
lines.push(` ${t.id.padEnd(15)} ${t.description}`);
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
lines.push('\n' + 'ā'.repeat(70));
|
|
365
|
+
lines.push('\nUsage:');
|
|
366
|
+
lines.push(' ctxman --template <template-id>');
|
|
367
|
+
lines.push(' ctxman -t bug-fix');
|
|
368
|
+
lines.push(' ctxman -t feature --cli');
|
|
369
|
+
lines.push('\nCustom Templates:');
|
|
370
|
+
lines.push(' Add templates to .ctxman/templates/<name>.json');
|
|
371
|
+
lines.push('');
|
|
372
|
+
|
|
373
|
+
return lines.join('\n');
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export default TemplateManager;
|
|
378
|
+
export { BUILTIN_TEMPLATES };
|