ctxman 3.0.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.
Files changed (190) hide show
  1. package/.ctxman/custom-profiles.example.json +20 -0
  2. package/.ctxman/llm-profiles.json +131 -0
  3. package/.ctxman/logs/ctxman-2026-07-02.log +156 -0
  4. package/.ctxman/self-knowledge/documents.lance/_transactions/0-b3dd1558-f95b-44b9-a154-18e8c330ae7a.txn +0 -0
  5. package/.ctxman/self-knowledge/documents.lance/_transactions/1-44bff6d4-e552-4010-a769-e09ad822698d.txn +0 -0
  6. package/.ctxman/self-knowledge/documents.lance/_transactions/10-37260794-5947-4ecb-b4c8-d732f58f40f1.txn +0 -0
  7. package/.ctxman/self-knowledge/documents.lance/_transactions/11-0d52a883-5518-463c-9b6d-986c3af89dfd.txn +0 -0
  8. package/.ctxman/self-knowledge/documents.lance/_transactions/12-ae354646-52f2-49c9-90fb-2e6ed8932663.txn +0 -0
  9. package/.ctxman/self-knowledge/documents.lance/_transactions/13-5d7d5e57-27da-498b-b734-091b9d38da13.txn +0 -0
  10. package/.ctxman/self-knowledge/documents.lance/_transactions/14-558ce928-810b-4252-a364-fdd1ee6f8a25.txn +0 -0
  11. package/.ctxman/self-knowledge/documents.lance/_transactions/15-27e3a58f-8b64-43b8-a9f4-e3369446968b.txn +0 -0
  12. package/.ctxman/self-knowledge/documents.lance/_transactions/16-a110c390-e5c8-4464-b976-bc7f5195fa15.txn +0 -0
  13. package/.ctxman/self-knowledge/documents.lance/_transactions/17-97513225-53de-4216-b61a-6337bd0cc4ad.txn +0 -0
  14. package/.ctxman/self-knowledge/documents.lance/_transactions/18-5ba450f0-c527-459c-bf4f-6d79b5e4285f.txn +0 -0
  15. package/.ctxman/self-knowledge/documents.lance/_transactions/19-600d3a63-5225-49e9-b8b7-30e037b564ab.txn +0 -0
  16. package/.ctxman/self-knowledge/documents.lance/_transactions/2-48a61b4e-92cf-4342-8376-914c1b99a9f9.txn +0 -0
  17. package/.ctxman/self-knowledge/documents.lance/_transactions/20-121dd912-5410-410e-84ff-82c7a23604a0.txn +0 -0
  18. package/.ctxman/self-knowledge/documents.lance/_transactions/3-bc6844ab-7ea3-4bb3-9ee1-538ce5664a9c.txn +0 -0
  19. package/.ctxman/self-knowledge/documents.lance/_transactions/4-d5e1754f-dbb5-454e-a9be-2a7da8d7bcf9.txn +0 -0
  20. package/.ctxman/self-knowledge/documents.lance/_transactions/5-4fae93df-0568-4b6f-8acf-8bef4bba6047.txn +0 -0
  21. package/.ctxman/self-knowledge/documents.lance/_transactions/6-4b1b8b87-068e-49a3-ba2b-d8dae628b252.txn +0 -0
  22. package/.ctxman/self-knowledge/documents.lance/_transactions/7-63d040fb-d2fa-4c0f-bed0-53411918a7b0.txn +0 -0
  23. package/.ctxman/self-knowledge/documents.lance/_transactions/8-c5628cf6-264c-420f-aab8-bc96a9083591.txn +0 -0
  24. package/.ctxman/self-knowledge/documents.lance/_transactions/9-c94570aa-5e53-4fcb-971f-cecc1d1d5492.txn +0 -0
  25. package/.ctxman/self-knowledge/documents.lance/_versions/1.manifest +0 -0
  26. package/.ctxman/self-knowledge/documents.lance/_versions/10.manifest +0 -0
  27. package/.ctxman/self-knowledge/documents.lance/_versions/11.manifest +0 -0
  28. package/.ctxman/self-knowledge/documents.lance/_versions/12.manifest +0 -0
  29. package/.ctxman/self-knowledge/documents.lance/_versions/13.manifest +0 -0
  30. package/.ctxman/self-knowledge/documents.lance/_versions/14.manifest +0 -0
  31. package/.ctxman/self-knowledge/documents.lance/_versions/15.manifest +0 -0
  32. package/.ctxman/self-knowledge/documents.lance/_versions/16.manifest +0 -0
  33. package/.ctxman/self-knowledge/documents.lance/_versions/17.manifest +0 -0
  34. package/.ctxman/self-knowledge/documents.lance/_versions/18.manifest +0 -0
  35. package/.ctxman/self-knowledge/documents.lance/_versions/19.manifest +0 -0
  36. package/.ctxman/self-knowledge/documents.lance/_versions/2.manifest +0 -0
  37. package/.ctxman/self-knowledge/documents.lance/_versions/20.manifest +0 -0
  38. package/.ctxman/self-knowledge/documents.lance/_versions/21.manifest +0 -0
  39. package/.ctxman/self-knowledge/documents.lance/_versions/3.manifest +0 -0
  40. package/.ctxman/self-knowledge/documents.lance/_versions/4.manifest +0 -0
  41. package/.ctxman/self-knowledge/documents.lance/_versions/5.manifest +0 -0
  42. package/.ctxman/self-knowledge/documents.lance/_versions/6.manifest +0 -0
  43. package/.ctxman/self-knowledge/documents.lance/_versions/7.manifest +0 -0
  44. package/.ctxman/self-knowledge/documents.lance/_versions/8.manifest +0 -0
  45. package/.ctxman/self-knowledge/documents.lance/_versions/9.manifest +0 -0
  46. package/.ctxman/self-knowledge/documents.lance/data/000010010011011101000010de8621405d9d5cb06540dbf02c.lance +0 -0
  47. package/.ctxman/self-knowledge/documents.lance/data/0000101001011100011000003fb62a4aa8978974075c144f1c.lance +0 -0
  48. package/.ctxman/self-knowledge/documents.lance/data/000011010000100111001011c6cc824d628f3bb1f4e04cd5d0.lance +0 -0
  49. package/.ctxman/self-knowledge/documents.lance/data/001000001000011001011100d8f74349ada090b332cd1df669.lance +0 -0
  50. package/.ctxman/self-knowledge/documents.lance/data/01000001111010001000010045ab44467bb1e7e4d8ae06bd58.lance +0 -0
  51. package/.ctxman/self-knowledge/documents.lance/data/01000101101110111110011131ef904241a89e1ffb6ddaf8e3.lance +0 -0
  52. package/.ctxman/self-knowledge/documents.lance/data/010010110000010000011010fbfcc145c6a8a827b28057a07e.lance +0 -0
  53. package/.ctxman/self-knowledge/documents.lance/data/1000011000101010001110003fb4f045c3835982efa1405324.lance +0 -0
  54. package/.ctxman/self-knowledge/documents.lance/data/100111011101110111101011f4396b410fa06af3b028d204e6.lance +0 -0
  55. package/.ctxman/self-knowledge/documents.lance/data/100111100111101000001100596b174e00995d6cc2969cc8dc.lance +0 -0
  56. package/.ctxman/self-knowledge/documents.lance/data/1010001100011000110010102ea16b484d887c7c9725feffd6.lance +0 -0
  57. package/.ctxman/self-knowledge/documents.lance/data/101101100010101111110111c5230944d9b3af7301b6392dda.lance +0 -0
  58. package/.ctxman/self-knowledge/documents.lance/data/1011011100100111111110105892bf41b5a530fedd7cf516f1.lance +0 -0
  59. package/.ctxman/self-knowledge/documents.lance/data/10111110000010001011110164e8774742bade87b483c10f36.lance +0 -0
  60. package/.ctxman/self-knowledge/documents.lance/data/1100000100010110001110103ffaca40a2a42c85067ebaf9ae.lance +0 -0
  61. package/.ctxman/self-knowledge/documents.lance/data/110000010001110110101110664bde4c76a78dfc14b05e83b8.lance +0 -0
  62. package/.ctxman/self-knowledge/documents.lance/data/11001100111000010100110050645c4281b1f35b2022234f7f.lance +0 -0
  63. package/.ctxman/self-knowledge/documents.lance/data/1101111011001100110111017214924a368f5719eab2246619.lance +0 -0
  64. package/.ctxman/self-knowledge/documents.lance/data/1110000111001010001000002150c24dcab653d39d07032e63.lance +0 -0
  65. package/.ctxman/self-knowledge/documents.lance/data/1110100000000100000011014d20534967a18b5c87721659ff.lance +0 -0
  66. package/.ctxman/self-knowledge/documents.lance/data/11110101111010000101011135a0114ef2b35dc6f1297a3381.lance +0 -0
  67. package/.ctxman/wizard-profiles/code-review/.contextignore +41 -0
  68. package/.ctxman/wizard-profiles/code-review/.contextinclude +30 -0
  69. package/.ctxman/wizard-profiles/code-review/.methodignore +26 -0
  70. package/.ctxman/wizard-profiles/code-review/.methodinclude +47 -0
  71. package/.ctxman/wizard-profiles/code-review/profile.json +26 -0
  72. package/.ctxman/wizard-profiles/documentation/.contextignore +21 -0
  73. package/.ctxman/wizard-profiles/documentation/.contextinclude +24 -0
  74. package/.ctxman/wizard-profiles/documentation/.methodignore +19 -0
  75. package/.ctxman/wizard-profiles/documentation/.methodinclude +44 -0
  76. package/.ctxman/wizard-profiles/documentation/profile.json +27 -0
  77. package/.ctxman/wizard-profiles/full/.contextignore +23 -0
  78. package/.ctxman/wizard-profiles/full/.contextinclude +38 -0
  79. package/.ctxman/wizard-profiles/full/.methodignore +5 -0
  80. package/.ctxman/wizard-profiles/full/.methodinclude +5 -0
  81. package/.ctxman/wizard-profiles/full/profile.json +28 -0
  82. package/.ctxman/wizard-profiles/llm-explain/.contextignore +25 -0
  83. package/.ctxman/wizard-profiles/llm-explain/.contextinclude +33 -0
  84. package/.ctxman/wizard-profiles/llm-explain/.methodignore +19 -0
  85. package/.ctxman/wizard-profiles/llm-explain/.methodinclude +48 -0
  86. package/.ctxman/wizard-profiles/llm-explain/profile.json +27 -0
  87. package/.ctxman/wizard-profiles/minimal/.contextignore +27 -0
  88. package/.ctxman/wizard-profiles/minimal/.contextinclude +14 -0
  89. package/.ctxman/wizard-profiles/minimal/.methodignore +22 -0
  90. package/.ctxman/wizard-profiles/minimal/.methodinclude +18 -0
  91. package/.ctxman/wizard-profiles/minimal/profile.json +26 -0
  92. package/.ctxman/wizard-profiles/security-audit/.contextignore +36 -0
  93. package/.ctxman/wizard-profiles/security-audit/.contextinclude +37 -0
  94. package/.ctxman/wizard-profiles/security-audit/.methodignore +29 -0
  95. package/.ctxman/wizard-profiles/security-audit/.methodinclude +60 -0
  96. package/.ctxman/wizard-profiles/security-audit/profile.json +34 -0
  97. package/README.md +1167 -0
  98. package/bin/cli.js +650 -0
  99. package/bin/cm-ask.js +95 -0
  100. package/bin/cm-gitingest.js +134 -0
  101. package/bin/cm-update.js +175 -0
  102. package/bin/mcp-server.js +31 -0
  103. package/ctxman.js +236 -0
  104. package/examples/README.md +298 -0
  105. package/examples/custom-llm-profiles.example.json +19 -0
  106. package/examples/wizard-profiles/code-review/.contextignore +41 -0
  107. package/examples/wizard-profiles/code-review/.contextinclude +30 -0
  108. package/examples/wizard-profiles/code-review/.methodignore +26 -0
  109. package/examples/wizard-profiles/code-review/.methodinclude +47 -0
  110. package/examples/wizard-profiles/code-review/profile.json +26 -0
  111. package/examples/wizard-profiles/documentation/.contextignore +21 -0
  112. package/examples/wizard-profiles/documentation/.contextinclude +24 -0
  113. package/examples/wizard-profiles/documentation/.methodignore +19 -0
  114. package/examples/wizard-profiles/documentation/.methodinclude +44 -0
  115. package/examples/wizard-profiles/documentation/profile.json +27 -0
  116. package/examples/wizard-profiles/full/.contextignore +23 -0
  117. package/examples/wizard-profiles/full/.contextinclude +38 -0
  118. package/examples/wizard-profiles/full/.methodignore +5 -0
  119. package/examples/wizard-profiles/full/.methodinclude +5 -0
  120. package/examples/wizard-profiles/full/profile.json +28 -0
  121. package/examples/wizard-profiles/llm-explain/.contextignore +25 -0
  122. package/examples/wizard-profiles/llm-explain/.contextinclude +33 -0
  123. package/examples/wizard-profiles/llm-explain/.methodignore +19 -0
  124. package/examples/wizard-profiles/llm-explain/.methodinclude +48 -0
  125. package/examples/wizard-profiles/llm-explain/profile.json +27 -0
  126. package/examples/wizard-profiles/minimal/.contextignore +27 -0
  127. package/examples/wizard-profiles/minimal/.contextinclude +14 -0
  128. package/examples/wizard-profiles/minimal/.methodignore +22 -0
  129. package/examples/wizard-profiles/minimal/.methodinclude +18 -0
  130. package/examples/wizard-profiles/minimal/profile.json +26 -0
  131. package/examples/wizard-profiles/security-audit/.contextignore +36 -0
  132. package/examples/wizard-profiles/security-audit/.contextinclude +37 -0
  133. package/examples/wizard-profiles/security-audit/.methodignore +29 -0
  134. package/examples/wizard-profiles/security-audit/.methodinclude +60 -0
  135. package/examples/wizard-profiles/security-audit/profile.json +34 -0
  136. package/index.js +68 -0
  137. package/lib/agents/pr-failure-agent.js +201 -0
  138. package/lib/analyzers/go-method-analyzer.js +126 -0
  139. package/lib/analyzers/method-analyzer.js +457 -0
  140. package/lib/analyzers/token-calculator.js +577 -0
  141. package/lib/api/mcp/prompts.js +319 -0
  142. package/lib/api/mcp/resources.js +269 -0
  143. package/lib/api/mcp/server.js +339 -0
  144. package/lib/api/mcp/transports/HttpSseTransport.js +63 -0
  145. package/lib/api/rest/server.js +405 -0
  146. package/lib/cache/CacheManager.js +388 -0
  147. package/lib/core/Analyzer.js +180 -0
  148. package/lib/core/ContextBuilder.js +325 -0
  149. package/lib/core/Reporter.js +270 -0
  150. package/lib/core/Scanner.js +129 -0
  151. package/lib/formatters/format-registry.js +419 -0
  152. package/lib/formatters/gitingest-formatter.js +746 -0
  153. package/lib/formatters/toon-formatter-v1.3.js +731 -0
  154. package/lib/formatters/toon-formatter.js +291 -0
  155. package/lib/integrations/git/BlameTracker.js +142 -0
  156. package/lib/integrations/git/DiffAnalyzer.js +260 -0
  157. package/lib/integrations/git/GitClient.js +336 -0
  158. package/lib/parsers/gitignore-parser.js +127 -0
  159. package/lib/parsers/method-filter-parser.js +50 -0
  160. package/lib/plugins/ExporterPlugin.js +102 -0
  161. package/lib/plugins/LanguagePlugin.js +100 -0
  162. package/lib/plugins/PluginManager.js +302 -0
  163. package/lib/rag/DataSourcePlugin.js +31 -0
  164. package/lib/rag/EmbeddingProvider.js +75 -0
  165. package/lib/rag/Indexer.js +83 -0
  166. package/lib/rag/VectorStoreFactory.js +24 -0
  167. package/lib/rag/sources/FileDataSource.js +70 -0
  168. package/lib/rag/stores/BaseVectorStore.js +43 -0
  169. package/lib/rag/stores/ChromaDBStore.js +85 -0
  170. package/lib/rag/stores/LanceDBStore.js +109 -0
  171. package/lib/rag/stores/PineconeStore.js +74 -0
  172. package/lib/ui/dashboard.js +109 -0
  173. package/lib/ui/index.js +15 -0
  174. package/lib/ui/progress-bar.js +110 -0
  175. package/lib/ui/select-input.js +51 -0
  176. package/lib/ui/wizard.js +359 -0
  177. package/lib/utils/clipboard-utils.js +66 -0
  178. package/lib/utils/config-utils.js +94 -0
  179. package/lib/utils/error-handler.js +145 -0
  180. package/lib/utils/file-utils.js +89 -0
  181. package/lib/utils/format-converter.js +325 -0
  182. package/lib/utils/git-utils.js +337 -0
  183. package/lib/utils/llm-detector.js +409 -0
  184. package/lib/utils/logger.js +278 -0
  185. package/lib/utils/token-utils.js +200 -0
  186. package/lib/utils/tokenizer-adapter.js +454 -0
  187. package/lib/utils/updater.js +565 -0
  188. package/lib/watch/FileWatcher.js +208 -0
  189. package/lib/watch/IncrementalAnalyzer.js +206 -0
  190. package/package.json +118 -0
@@ -0,0 +1,359 @@
1
+ import React, { useState, useCallback, useMemo } from 'react';
2
+ import { Box, Text } from 'ink';
3
+ import SelectInput from './select-input.js';
4
+ import { LLMDetector } from '../utils/llm-detector.js';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+
12
+ /**
13
+ * Interactive Configuration Wizard
14
+ * Guides users through context generation setup
15
+ * v2.3.8 - Use case templates with configuration copying
16
+ */
17
+
18
+ /**
19
+ * Discover available wizard profiles from .ctxman/wizard-profiles/
20
+ */
21
+ function discoverProfiles() {
22
+ const profilesDir = path.join(__dirname, '../../.ctxman/wizard-profiles');
23
+ const profiles = [];
24
+
25
+ try {
26
+ if (fs.existsSync(profilesDir)) {
27
+ const dirs = fs.readdirSync(profilesDir, { withFileTypes: true });
28
+
29
+ dirs.forEach(dirent => {
30
+ if (dirent.isDirectory()) {
31
+ const profilePath = path.join(profilesDir, dirent.name);
32
+ const metadataPath = path.join(profilePath, 'profile.json');
33
+
34
+ if (fs.existsSync(metadataPath)) {
35
+ try {
36
+ const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf-8'));
37
+ profiles.push({
38
+ label: `${metadata.icon} ${metadata.name}`,
39
+ value: metadata.id,
40
+ path: profilePath,
41
+ metadata: metadata
42
+ });
43
+ } catch (error) {
44
+ console.error(`Error reading profile metadata ${dirent.name}:`, error);
45
+ }
46
+ }
47
+ }
48
+ });
49
+ }
50
+ } catch (error) {
51
+ console.error('Error discovering profiles:', error);
52
+ }
53
+
54
+ // Add custom option at the end
55
+ profiles.push({
56
+ label: '⚙️ Custom (use existing root config)',
57
+ value: 'custom',
58
+ path: null,
59
+ metadata: null
60
+ });
61
+
62
+ return profiles;
63
+ }
64
+
65
+ /**
66
+ * Copy profile configuration files to project root with profile name suffix
67
+ */
68
+ function copyProfileFiles(profilePath, profileId, projectRoot) {
69
+ const filesToCopy = [
70
+ '.contextinclude',
71
+ '.contextignore',
72
+ '.methodinclude',
73
+ '.methodignore'
74
+ ];
75
+ const copiedFiles = [];
76
+
77
+ filesToCopy.forEach(fileName => {
78
+ const sourcePath = path.join(profilePath, fileName);
79
+ // Copy with profile name suffix: .contextinclude-code-review
80
+ const destFileName = `${fileName}-${profileId}`;
81
+ const destPath = path.join(projectRoot, destFileName);
82
+
83
+ if (fs.existsSync(sourcePath)) {
84
+ try {
85
+ fs.copyFileSync(sourcePath, destPath);
86
+ copiedFiles.push(destFileName);
87
+ } catch (error) {
88
+ console.error(`Error copying ${fileName}:`, error);
89
+ }
90
+ }
91
+ });
92
+
93
+ // Create/update symlinks for active config
94
+ // .contextinclude -> .contextinclude-code-review
95
+ const symlinkFiles = [
96
+ ['.contextinclude', `.contextinclude-${profileId}`],
97
+ ['.contextignore', `.contextignore-${profileId}`],
98
+ ['.methodinclude', `.methodinclude-${profileId}`],
99
+ ['.methodignore', `.methodignore-${profileId}`]
100
+ ];
101
+
102
+ symlinkFiles.forEach(([linkName, targetName]) => {
103
+ const linkPath = path.join(projectRoot, linkName);
104
+ const targetPath = path.join(projectRoot, targetName);
105
+
106
+ try {
107
+ // Remove existing symlink/file
108
+ if (fs.existsSync(linkPath)) {
109
+ fs.unlinkSync(linkPath);
110
+ }
111
+
112
+ // Create copy instead of symlink for better compatibility
113
+ if (fs.existsSync(targetPath)) {
114
+ fs.copyFileSync(targetPath, linkPath);
115
+ }
116
+ } catch (error) {
117
+ console.error(`Error creating link ${linkName}:`, error);
118
+ }
119
+ });
120
+
121
+ return copiedFiles;
122
+ }
123
+
124
+ export default function Wizard({ onComplete }) {
125
+ const [step, setStep] = useState('profile');
126
+ const [answers, setAnswers] = useState({});
127
+
128
+ // Dynamic profile discovery
129
+ const profileItems = useMemo(() => discoverProfiles(), []);
130
+
131
+ // Dynamic LLM model list from JSON config
132
+ const targetModelItems = useMemo(() => {
133
+ const models = LLMDetector.getModelList();
134
+ const items = [
135
+ { label: '✨ Auto-detect from environment', value: 'auto' }
136
+ ];
137
+
138
+ // Add models grouped by vendor
139
+ const anthropicModels = models.filter(m => m.vendor === 'Anthropic');
140
+ const openaiModels = models.filter(m => m.vendor === 'OpenAI');
141
+ const googleModels = models.filter(m => m.vendor === 'Google');
142
+ const otherModels = models.filter(m => !['Anthropic', 'OpenAI', 'Google'].includes(m.vendor));
143
+
144
+ // Anthropic (Claude)
145
+ anthropicModels.forEach(model => {
146
+ const contextK = Math.floor(model.contextWindow / 1000);
147
+ items.push({
148
+ label: `${model.name} (${contextK}k context)`,
149
+ value: model.id
150
+ });
151
+ });
152
+
153
+ // OpenAI (GPT-4)
154
+ openaiModels.forEach(model => {
155
+ const contextK = Math.floor(model.contextWindow / 1000);
156
+ items.push({
157
+ label: `${model.name} (${contextK}k context)`,
158
+ value: model.id
159
+ });
160
+ });
161
+
162
+ // Google (Gemini)
163
+ googleModels.forEach(model => {
164
+ const contextK = model.contextWindow >= 1000000
165
+ ? Math.floor(model.contextWindow / 1000000) + 'M'
166
+ : Math.floor(model.contextWindow / 1000) + 'k';
167
+ items.push({
168
+ label: `${model.name} (${contextK} context)`,
169
+ value: model.id
170
+ });
171
+ });
172
+
173
+ // Others
174
+ otherModels.forEach(model => {
175
+ const contextK = Math.floor(model.contextWindow / 1000);
176
+ items.push({
177
+ label: `${model.name} (${contextK}k context)`,
178
+ value: model.id
179
+ });
180
+ });
181
+
182
+ return items;
183
+ }, []);
184
+
185
+ const outputFormatItems = [
186
+ { label: 'TOON (most efficient, 40-50% reduction)', value: 'toon' },
187
+ { label: 'JSON (standard format)', value: 'json' },
188
+ { label: 'GitIngest (single file)', value: 'gitingest' },
189
+ { label: 'Markdown (documentation)', value: 'markdown' },
190
+ { label: 'YAML (human-readable)', value: 'yaml' },
191
+ { label: 'CSV (spreadsheet)', value: 'csv' }
192
+ ];
193
+
194
+ const handleProfileSelect = useCallback((item) => {
195
+ // Copy profile files if not custom
196
+ if (item.value !== 'custom' && item.path) {
197
+ const projectRoot = process.cwd();
198
+ const copiedFiles = copyProfileFiles(item.path, item.value, projectRoot);
199
+
200
+ setAnswers(prev => ({
201
+ ...prev,
202
+ profile: item.value,
203
+ copiedFiles: copiedFiles,
204
+ profileMetadata: item.metadata
205
+ }));
206
+ } else {
207
+ // Custom - use existing root config files
208
+ setAnswers(prev => ({
209
+ ...prev,
210
+ profile: 'custom',
211
+ copiedFiles: [],
212
+ profileMetadata: null
213
+ }));
214
+ }
215
+
216
+ setStep('target-model');
217
+ }, []);
218
+
219
+ const handleTargetModelSelect = useCallback((item) => {
220
+ let selectedModel = item.value;
221
+
222
+ // Handle auto-detect
223
+ if (selectedModel === 'auto') {
224
+ selectedModel = LLMDetector.detect();
225
+ if (selectedModel === 'unknown') {
226
+ // Fallback to default if detection fails
227
+ selectedModel = 'claude-sonnet-4.5';
228
+ }
229
+ }
230
+
231
+ setAnswers(prev => ({ ...prev, targetModel: selectedModel }));
232
+ setStep('output-format');
233
+ }, []);
234
+
235
+ const handleOutputFormatSelect = useCallback((item) => {
236
+ const finalAnswers = { ...answers, outputFormat: item.value };
237
+ setAnswers(finalAnswers);
238
+ setStep('complete');
239
+
240
+ // Call completion callback
241
+ if (onComplete) {
242
+ onComplete(finalAnswers);
243
+ }
244
+ }, [answers, onComplete]);
245
+
246
+ // Build children array and filter out falsy values
247
+ const children = [
248
+ React.createElement(Box, { key: 'title', marginBottom: 1 },
249
+ React.createElement(Text, { bold: true, color: 'cyan' }, '🧙 Context Generation Wizard')
250
+ )
251
+ ];
252
+
253
+ // Step 1: Profile Selection
254
+ if (step === 'profile') {
255
+ children.push(
256
+ React.createElement(Box, { key: 'profile-step', flexDirection: 'column' },
257
+ React.createElement(Box, { key: 'question-box', marginBottom: 1 },
258
+ React.createElement(Text, null, 'Select analysis profile:')
259
+ ),
260
+ React.createElement(SelectInput, {
261
+ key: 'select-input',
262
+ items: profileItems,
263
+ onSelect: handleProfileSelect
264
+ }),
265
+ React.createElement(Box, { key: 'help-box', marginTop: 1 },
266
+ React.createElement(Text, { dimColor: true }, '[↑↓] Navigate [Enter] Select [Esc] Cancel')
267
+ )
268
+ )
269
+ );
270
+ }
271
+
272
+ // Step 2: Target Model
273
+ if (step === 'target-model') {
274
+ children.push(
275
+ React.createElement(Box, { key: 'target-model-step', flexDirection: 'column' },
276
+ React.createElement(Box, { key: 'question-box-2', marginBottom: 1 },
277
+ React.createElement(Text, null, 'Target LLM?')
278
+ ),
279
+ React.createElement(SelectInput, {
280
+ key: 'select-input-2',
281
+ items: targetModelItems,
282
+ onSelect: handleTargetModelSelect
283
+ }),
284
+ React.createElement(Box, { key: 'help-box-2', marginTop: 1 },
285
+ React.createElement(Text, { dimColor: true }, '[↑↓] Navigate [Enter] Select [Esc] Cancel')
286
+ )
287
+ )
288
+ );
289
+ }
290
+
291
+ // Step 3: Output Format
292
+ if (step === 'output-format') {
293
+ children.push(
294
+ React.createElement(Box, { key: 'output-format-step', flexDirection: 'column' },
295
+ React.createElement(Box, { key: 'question-box-3', marginBottom: 1 },
296
+ React.createElement(Text, null, 'Output format?')
297
+ ),
298
+ React.createElement(SelectInput, {
299
+ key: 'select-input-3',
300
+ items: outputFormatItems,
301
+ onSelect: handleOutputFormatSelect
302
+ }),
303
+ React.createElement(Box, { key: 'help-box-3', marginTop: 1 },
304
+ React.createElement(Text, { dimColor: true }, '[↑↓] Navigate [Enter] Select [Esc] Cancel')
305
+ )
306
+ )
307
+ );
308
+ }
309
+
310
+ // Complete
311
+ if (step === 'complete') {
312
+ const completeBoxChildren = [
313
+ React.createElement(Box, { key: 'complete-title', marginBottom: 1 },
314
+ React.createElement(Text, { bold: true, color: 'green' }, '✓ Configuration Complete!')
315
+ ),
316
+ React.createElement(Box, { key: 'complete-info', flexDirection: 'column', marginBottom: 1 },
317
+ React.createElement(Text, null, `Profile: ${answers.profile}`),
318
+ React.createElement(Text, null, `Target Model: ${answers.targetModel}`),
319
+ React.createElement(Text, null, `Output Format: ${answers.outputFormat}`)
320
+ )
321
+ ];
322
+
323
+ // Show copied files if any
324
+ if (answers.copiedFiles && answers.copiedFiles.length > 0) {
325
+ completeBoxChildren.push(
326
+ React.createElement(Box, { key: 'copied-files', flexDirection: 'column', marginTop: 1 },
327
+ React.createElement(Text, { color: 'cyan' }, 'Profile configuration files:'),
328
+ ...answers.copiedFiles.slice(0, 4).map((file, idx) =>
329
+ React.createElement(Text, { key: `file-${idx}`, color: 'gray' }, ` ✓ ${file}`)
330
+ )
331
+ )
332
+ );
333
+
334
+ completeBoxChildren.push(
335
+ React.createElement(Box, { key: 'active-files', flexDirection: 'column', marginTop: 1 },
336
+ React.createElement(Text, { color: 'green' }, 'Active configuration (symlinked):'),
337
+ React.createElement(Text, { color: 'gray' }, ' → .contextinclude'),
338
+ React.createElement(Text, { color: 'gray' }, ' → .contextignore'),
339
+ React.createElement(Text, { color: 'gray' }, ' → .methodinclude'),
340
+ React.createElement(Text, { color: 'gray' }, ' → .methodignore')
341
+ )
342
+ );
343
+ }
344
+
345
+ children.push(
346
+ React.createElement(Box, { key: 'complete-step', flexDirection: 'column' },
347
+ ...completeBoxChildren
348
+ )
349
+ );
350
+ }
351
+
352
+ return React.createElement(Box, {
353
+ flexDirection: 'column',
354
+ padding: 1,
355
+ borderStyle: 'round',
356
+ borderColor: 'cyan',
357
+ width: 76
358
+ }, ...children);
359
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Cross-platform Clipboard Utilities
3
+ * Handles clipboard operations for macOS, Linux, and Windows
4
+ */
5
+
6
+ import { execSync } from 'child_process';
7
+
8
+ class ClipboardUtils {
9
+ /**
10
+ * Copy text to system clipboard
11
+ * @param {string} text - Text to copy
12
+ * @returns {boolean} Success status
13
+ */
14
+ static copy(text) {
15
+ try {
16
+ if (process.platform === 'darwin') {
17
+ // macOS
18
+ execSync('pbcopy', { input: text, encoding: 'utf8' });
19
+ } else if (process.platform === 'linux') {
20
+ // Linux - try xclip first, then xsel
21
+ try {
22
+ execSync('xclip -selection clipboard', { input: text, encoding: 'utf8' });
23
+ } catch {
24
+ execSync('xsel --clipboard --input', { input: text, encoding: 'utf8' });
25
+ }
26
+ } else if (process.platform === 'win32') {
27
+ // Windows
28
+ execSync('clip', { input: text, encoding: 'utf8' });
29
+ } else {
30
+ throw new Error('Unsupported platform for clipboard');
31
+ }
32
+
33
+ console.log('✅ Context copied to clipboard!');
34
+ console.log(`📋 ${text.length} characters copied`);
35
+ return true;
36
+
37
+ } catch (error) {
38
+ console.error('❌ Failed to copy to clipboard:', error.message);
39
+ return false;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Check if clipboard is available on current platform
45
+ * @returns {boolean}
46
+ */
47
+ static isAvailable() {
48
+ const platform = process.platform;
49
+ return ['darwin', 'linux', 'win32'].includes(platform);
50
+ }
51
+
52
+ /**
53
+ * Get clipboard command for current platform
54
+ * @returns {string} Command name
55
+ */
56
+ static getCommand() {
57
+ const commands = {
58
+ darwin: 'pbcopy',
59
+ linux: 'xclip',
60
+ win32: 'clip'
61
+ };
62
+ return commands[process.platform] || 'unknown';
63
+ }
64
+ }
65
+
66
+ export default ClipboardUtils;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Configuration File Utilities
3
+ * Handles discovery and initialization of configuration files
4
+ */
5
+
6
+ import fs from 'fs';
7
+ import path from 'path';
8
+ import { fileURLToPath } from 'url';
9
+ import { dirname } from 'path';
10
+ import MethodFilterParser from '../parsers/method-filter-parser.js';
11
+ import GitIgnoreParser from '../parsers/gitignore-parser.js';
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = dirname(__filename);
15
+
16
+ class ConfigUtils {
17
+ /**
18
+ * Find configuration file in standard locations
19
+ * @param {string} projectRoot - Project root directory
20
+ * @param {string} filename - Config filename
21
+ * @returns {string|undefined} Config file path or undefined
22
+ */
23
+ static findConfigFile(projectRoot, filename) {
24
+ const locations = [
25
+ path.join(__dirname, '..', '..', filename), // Package root
26
+ path.join(projectRoot, filename) // Project root
27
+ ];
28
+ return locations.find(loc => fs.existsSync(loc));
29
+ }
30
+
31
+ /**
32
+ * Initialize method filter parser
33
+ * @param {string} projectRoot - Project root directory
34
+ * @returns {object|null} MethodFilterParser instance or null
35
+ */
36
+ static initMethodFilter(projectRoot) {
37
+ const paths = {
38
+ methodInclude: this.findConfigFile(projectRoot, '.methodinclude'),
39
+ methodIgnore: this.findConfigFile(projectRoot, '.methodignore')
40
+ };
41
+
42
+ if (!paths.methodInclude && !paths.methodIgnore) {
43
+ return null;
44
+ }
45
+
46
+ return new MethodFilterParser(paths.methodInclude, paths.methodIgnore);
47
+ }
48
+
49
+ /**
50
+ * Detect if method filtering is enabled
51
+ * @param {string} projectRoot - Project root directory
52
+ * @returns {boolean}
53
+ */
54
+ static detectMethodFilters(projectRoot) {
55
+ const methodIncludePath = this.findConfigFile(projectRoot, '.methodinclude');
56
+ const methodIgnorePath = this.findConfigFile(projectRoot, '.methodignore');
57
+ return !!(methodIncludePath || methodIgnorePath);
58
+ }
59
+
60
+ /**
61
+ * Initialize git ignore parser
62
+ * @param {string} projectRoot - Project root directory
63
+ * @returns {object} GitIgnoreParser instance
64
+ */
65
+ static initGitIgnore(projectRoot) {
66
+ const paths = {
67
+ contextIgnore: this.findConfigFile(projectRoot, '.contextignore'),
68
+ contextInclude: this.findConfigFile(projectRoot, '.contextinclude')
69
+ };
70
+
71
+ return new GitIgnoreParser(
72
+ path.join(projectRoot, '.gitignore'),
73
+ paths.contextIgnore,
74
+ paths.contextInclude
75
+ );
76
+ }
77
+
78
+ /**
79
+ * Get all config file paths
80
+ * @param {string} projectRoot - Project root directory
81
+ * @returns {object} Config paths
82
+ */
83
+ static getConfigPaths(projectRoot) {
84
+ return {
85
+ gitignore: path.join(projectRoot, '.gitignore'),
86
+ contextIgnore: this.findConfigFile(projectRoot, '.contextignore'),
87
+ contextInclude: this.findConfigFile(projectRoot, '.contextinclude'),
88
+ methodInclude: this.findConfigFile(projectRoot, '.methodinclude'),
89
+ methodIgnore: this.findConfigFile(projectRoot, '.methodignore')
90
+ };
91
+ }
92
+ }
93
+
94
+ export default ConfigUtils;
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Error Handler Utilities
3
+ * v2.3.4 - Enhanced error handling and user-friendly messages
4
+ */
5
+
6
+ import fs from 'fs';
7
+
8
+ class ErrorHandler {
9
+ constructor(options = {}) {
10
+ this.verbose = options.verbose || false;
11
+ this.logFile = options.logFile || null;
12
+ }
13
+
14
+ /**
15
+ * Handle format errors
16
+ */
17
+ handleFormatError(error, format) {
18
+ const message = `Format error (${format}): ${error.message}`;
19
+
20
+ if (this.verbose) {
21
+ console.error('❌ ' + message);
22
+ console.error(' Stack:', error.stack);
23
+ } else {
24
+ console.error('❌ ' + message);
25
+ console.error(' Use --verbose for detailed error information');
26
+ }
27
+
28
+ this.logError(message, error);
29
+ }
30
+
31
+ /**
32
+ * Handle file errors
33
+ */
34
+ handleFileError(error, filePath) {
35
+ const message = `File error (${filePath}): ${error.message}`;
36
+
37
+ console.error('❌ ' + message);
38
+
39
+ // Provide helpful suggestions
40
+ if (error.code === 'ENOENT') {
41
+ console.error(' Suggestion: Check if the file path is correct');
42
+ } else if (error.code === 'EACCES') {
43
+ console.error(' Suggestion: Check file permissions');
44
+ }
45
+
46
+ this.logError(message, error);
47
+ }
48
+
49
+ /**
50
+ * Handle parse errors
51
+ */
52
+ handleParseError(error, format, content) {
53
+ const message = `Parse error (${format}): ${error.message}`;
54
+
55
+ console.error('❌ ' + message);
56
+
57
+ if (this.verbose && content) {
58
+ console.error(' Content preview:', content.substring(0, 200) + '...');
59
+ }
60
+
61
+ this.logError(message, error);
62
+ }
63
+
64
+ /**
65
+ * Handle validation errors
66
+ */
67
+ handleValidationError(errors, context) {
68
+ console.error(`❌ Validation failed (${context}):`);
69
+
70
+ for (const error of errors) {
71
+ console.error(` • ${error}`);
72
+ }
73
+
74
+ this.logError(`Validation errors in ${context}`, { errors });
75
+ }
76
+
77
+ /**
78
+ * Log error to file (if configured)
79
+ */
80
+ logError(message, error) {
81
+ if (!this.logFile) return;
82
+
83
+ try {
84
+ const timestamp = new Date().toISOString();
85
+ const logEntry = `[${timestamp}] ${message}\n${error.stack || ''}\n\n`;
86
+
87
+ fs.appendFileSync(this.logFile, logEntry, 'utf8');
88
+ } catch (logError) {
89
+ // Silently fail if logging fails
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Wrap async function with error handling
95
+ */
96
+ wrapAsync(fn, context) {
97
+ return async (...args) => {
98
+ try {
99
+ return await fn(...args);
100
+ } catch (error) {
101
+ console.error(`❌ Error in ${context}:`, error.message);
102
+ if (this.verbose) {
103
+ console.error(' Stack:', error.stack);
104
+ }
105
+ this.logError(`Error in ${context}`, error);
106
+ throw error;
107
+ }
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Validate format support
113
+ */
114
+ validateFormat(format, supportedFormats) {
115
+ if (!supportedFormats.includes(format)) {
116
+ const message = `Unsupported format: ${format}`;
117
+ const suggestion = `Supported formats: ${supportedFormats.join(', ')}`;
118
+
119
+ console.error(`❌ ${message}`);
120
+ console.error(` ${suggestion}`);
121
+
122
+ throw new Error(message);
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Create user-friendly error message
128
+ */
129
+ createUserMessage(error, context = '') {
130
+ const prefix = context ? `${context}: ` : '';
131
+
132
+ // Map common errors to user-friendly messages
133
+ const errorMap = {
134
+ 'ENOENT': 'File not found',
135
+ 'EACCES': 'Permission denied',
136
+ 'EISDIR': 'Expected file, got directory',
137
+ 'ENOTDIR': 'Expected directory, got file'
138
+ };
139
+
140
+ const userMessage = errorMap[error.code] || error.message;
141
+ return `${prefix}${userMessage}`;
142
+ }
143
+ }
144
+
145
+ export default ErrorHandler;