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.
Files changed (223) hide show
  1. package/.ctxman/llm-profiles.json +1067 -39
  2. package/.ctxman/profiles/backend-team.json +13 -0
  3. package/.ctxman/profiles/default.json +12 -0
  4. package/.ctxman/profiles/devops-team.json +21 -0
  5. package/.ctxman/profiles/frontend-team.json +20 -0
  6. package/.ctxman/templates/bug-fix.json +17 -0
  7. package/.ctxman/templates/code-review.json +9 -0
  8. package/.ctxman/templates/documentation.json +9 -0
  9. package/.ctxman/templates/feature.json +9 -0
  10. package/.ctxman/templates/full-context.json +9 -0
  11. package/.ctxman/templates/refactoring.json +18 -0
  12. package/.ctxman/templates/testing.json +9 -0
  13. package/.ctxman/wizard-profiles/security-audit/profile.json +7 -1
  14. package/LICENSE +21 -0
  15. package/README.md +380 -136
  16. package/bin/cli.js +1880 -529
  17. package/bin/cm-ask.js +18 -13
  18. package/bin/cm-audit.js +124 -0
  19. package/bin/cm-count.js +151 -0
  20. package/bin/cm-gitingest.js +128 -115
  21. package/bin/cm-map.js +242 -0
  22. package/bin/cm-mcp.js +293 -0
  23. package/bin/cm-pack.js +405 -0
  24. package/bin/cm-policy.js +170 -0
  25. package/bin/cm-pr.js +248 -0
  26. package/bin/cm-search.js +178 -0
  27. package/bin/cm-update.js +128 -128
  28. package/bin/cm-why.js +197 -0
  29. package/bin/mcp-server.js +8 -24
  30. package/ctxman.js +237 -183
  31. package/examples/README.md +19 -4
  32. package/examples/github-actions/ctxman-check.yml +49 -0
  33. package/examples/wizard-profiles/security-audit/profile.json +7 -1
  34. package/index.d.ts +1422 -0
  35. package/index.js +55 -31
  36. package/lib/agents/pr-failure-agent.js +48 -45
  37. package/lib/analyzers/ai-suggester.js +451 -0
  38. package/lib/analyzers/dependency-scanner.js +505 -0
  39. package/lib/analyzers/duplicate-detector.js +264 -0
  40. package/lib/analyzers/go-method-analyzer.js +151 -110
  41. package/lib/analyzers/import-tracker.js +431 -0
  42. package/lib/analyzers/method-analyzer.js +1055 -434
  43. package/lib/analyzers/performance-dashboard.js +683 -0
  44. package/lib/analyzers/token-calculator.js +895 -504
  45. package/lib/analyzers/unused-exports-detector.js +336 -0
  46. package/lib/api/mcp/install.js +424 -0
  47. package/lib/api/mcp/prompts.js +281 -281
  48. package/lib/api/mcp/resources.js +300 -241
  49. package/lib/api/mcp/sandbox.js +89 -0
  50. package/lib/api/mcp/server.js +675 -312
  51. package/lib/api/mcp/start.js +125 -0
  52. package/lib/api/mcp/transports/HttpSseTransport.js +60 -52
  53. package/lib/api/rest/server.js +105 -28
  54. package/lib/cache/CacheManager.js +9 -17
  55. package/lib/core/Analyzer.js +16 -10
  56. package/lib/core/ContextBuilder.js +19 -28
  57. package/lib/core/Reporter.js +15 -14
  58. package/lib/core/Scanner.js +81 -12
  59. package/lib/count/index.js +100 -0
  60. package/lib/formatters/format-registry.js +394 -372
  61. package/lib/formatters/gitingest-formatter.js +656 -621
  62. package/lib/formatters/toon-formatter-v1.3.js +637 -632
  63. package/lib/formatters/toon-formatter.js +239 -240
  64. package/lib/integrations/git/BlameTracker.js +4 -4
  65. package/lib/integrations/git/DiffAnalyzer.js +38 -31
  66. package/lib/integrations/git/GitClient.js +71 -53
  67. package/lib/lsp/server.js +311 -0
  68. package/lib/map/graph.js +299 -0
  69. package/lib/map/imports.js +233 -0
  70. package/lib/map/index.js +33 -0
  71. package/lib/map/rank.js +253 -0
  72. package/lib/map/render.js +346 -0
  73. package/lib/map/resolver.js +574 -0
  74. package/lib/pack/budget.js +111 -0
  75. package/lib/pack/code-lexer.js +360 -0
  76. package/lib/pack/compress.js +414 -0
  77. package/lib/pack/index.js +354 -0
  78. package/lib/pack/renderers/common.js +134 -0
  79. package/lib/pack/renderers/index.js +24 -0
  80. package/lib/pack/renderers/json.js +47 -0
  81. package/lib/pack/renderers/markdown.js +54 -0
  82. package/lib/pack/renderers/plain.js +38 -0
  83. package/lib/pack/renderers/xml.js +85 -0
  84. package/lib/pack/select.js +177 -0
  85. package/lib/pack/split.js +182 -0
  86. package/lib/pack/tree.js +35 -0
  87. package/lib/parsers/gitignore-parser.js +113 -107
  88. package/lib/parsers/method-filter-parser.js +50 -33
  89. package/lib/plugins/ExporterPlugin.js +3 -3
  90. package/lib/plugins/LanguagePlugin.js +3 -3
  91. package/lib/plugins/PluginManager.js +7 -13
  92. package/lib/policy/audit.js +294 -0
  93. package/lib/policy/enforce.js +125 -0
  94. package/lib/policy/ignore-files.js +169 -0
  95. package/lib/policy/index.js +52 -0
  96. package/lib/policy/policy.js +489 -0
  97. package/lib/pr/diff-parser.js +267 -0
  98. package/lib/pr/method-mapper.js +148 -0
  99. package/lib/pr/pr-context.js +829 -0
  100. package/lib/pr/pr-git.js +149 -0
  101. package/lib/pr/pr-renderer.js +644 -0
  102. package/lib/pr/related-tests.js +296 -0
  103. package/lib/rag/DataSourcePlugin.js +5 -5
  104. package/lib/rag/EmbeddingProvider.js +19 -8
  105. package/lib/rag/Indexer.js +14 -5
  106. package/lib/rag/VectorStoreFactory.js +3 -3
  107. package/lib/rag/sources/FileDataSource.js +6 -6
  108. package/lib/rag/stores/BaseVectorStore.js +5 -5
  109. package/lib/rag/stores/ChromaDBStore.js +10 -10
  110. package/lib/rag/stores/LanceDBStore.js +29 -19
  111. package/lib/rag/stores/PineconeStore.js +11 -9
  112. package/lib/search/bm25.js +433 -0
  113. package/lib/search/code-units.js +201 -0
  114. package/lib/search/symbols.js +189 -0
  115. package/lib/ui/dashboard.js +120 -89
  116. package/lib/ui/index.js +1 -6
  117. package/lib/ui/init-wizard-ui.js +550 -0
  118. package/lib/ui/progress-bar.js +94 -88
  119. package/lib/ui/select-input.js +40 -32
  120. package/lib/ui/template-selector.js +99 -0
  121. package/lib/ui/wizard.js +374 -329
  122. package/lib/utils/claude-token-counter.js +151 -0
  123. package/lib/utils/cli-output.js +17 -0
  124. package/lib/utils/clipboard-utils.js +49 -50
  125. package/lib/utils/config-utils.js +81 -66
  126. package/lib/utils/context-versioning.js +532 -0
  127. package/lib/utils/error-handler.js +112 -112
  128. package/lib/utils/file-utils.js +153 -69
  129. package/lib/utils/format-converter.js +356 -283
  130. package/lib/utils/git-utils.js +366 -294
  131. package/lib/utils/llm-cost-estimator.js +281 -0
  132. package/lib/utils/llm-detector.js +242 -78
  133. package/lib/utils/logger.js +243 -237
  134. package/lib/utils/messages.js +355 -0
  135. package/lib/utils/multi-repo-manager.js +364 -0
  136. package/lib/utils/profile-manager.js +380 -0
  137. package/lib/utils/safe-read.js +61 -0
  138. package/lib/utils/secret-redactor.js +61 -0
  139. package/lib/utils/snapshot-manager.js +679 -0
  140. package/lib/utils/template-manager.js +378 -0
  141. package/lib/utils/token-utils.js +201 -170
  142. package/lib/utils/tokenizer-adapter.js +421 -377
  143. package/lib/utils/updater.js +539 -506
  144. package/lib/watch/FileWatcher.js +12 -8
  145. package/lib/watch/IncrementalAnalyzer.js +7 -7
  146. package/lib/why/coupling.js +81 -0
  147. package/lib/why/errors.js +23 -0
  148. package/lib/why/git.js +169 -0
  149. package/lib/why/history.js +108 -0
  150. package/lib/why/index.js +720 -0
  151. package/lib/why/ownership.js +79 -0
  152. package/lib/why/paths.js +207 -0
  153. package/lib/why/references.js +259 -0
  154. package/lib/why/render.js +722 -0
  155. package/lib/why/search.js +358 -0
  156. package/lib/why/target.js +389 -0
  157. package/lib/wizards/config-generator.js +255 -0
  158. package/lib/wizards/project-detector.js +571 -0
  159. package/package.json +44 -10
  160. package/.ctxman/logs/ctxman-2026-07-02.log +0 -156
  161. package/.ctxman/self-knowledge/documents.lance/_transactions/0-b3dd1558-f95b-44b9-a154-18e8c330ae7a.txn +0 -0
  162. package/.ctxman/self-knowledge/documents.lance/_transactions/1-44bff6d4-e552-4010-a769-e09ad822698d.txn +0 -0
  163. package/.ctxman/self-knowledge/documents.lance/_transactions/10-37260794-5947-4ecb-b4c8-d732f58f40f1.txn +0 -0
  164. package/.ctxman/self-knowledge/documents.lance/_transactions/11-0d52a883-5518-463c-9b6d-986c3af89dfd.txn +0 -0
  165. package/.ctxman/self-knowledge/documents.lance/_transactions/12-ae354646-52f2-49c9-90fb-2e6ed8932663.txn +0 -0
  166. package/.ctxman/self-knowledge/documents.lance/_transactions/13-5d7d5e57-27da-498b-b734-091b9d38da13.txn +0 -0
  167. package/.ctxman/self-knowledge/documents.lance/_transactions/14-558ce928-810b-4252-a364-fdd1ee6f8a25.txn +0 -0
  168. package/.ctxman/self-knowledge/documents.lance/_transactions/15-27e3a58f-8b64-43b8-a9f4-e3369446968b.txn +0 -0
  169. package/.ctxman/self-knowledge/documents.lance/_transactions/16-a110c390-e5c8-4464-b976-bc7f5195fa15.txn +0 -0
  170. package/.ctxman/self-knowledge/documents.lance/_transactions/17-97513225-53de-4216-b61a-6337bd0cc4ad.txn +0 -0
  171. package/.ctxman/self-knowledge/documents.lance/_transactions/18-5ba450f0-c527-459c-bf4f-6d79b5e4285f.txn +0 -0
  172. package/.ctxman/self-knowledge/documents.lance/_transactions/19-600d3a63-5225-49e9-b8b7-30e037b564ab.txn +0 -0
  173. package/.ctxman/self-knowledge/documents.lance/_transactions/2-48a61b4e-92cf-4342-8376-914c1b99a9f9.txn +0 -0
  174. package/.ctxman/self-knowledge/documents.lance/_transactions/20-121dd912-5410-410e-84ff-82c7a23604a0.txn +0 -0
  175. package/.ctxman/self-knowledge/documents.lance/_transactions/3-bc6844ab-7ea3-4bb3-9ee1-538ce5664a9c.txn +0 -0
  176. package/.ctxman/self-knowledge/documents.lance/_transactions/4-d5e1754f-dbb5-454e-a9be-2a7da8d7bcf9.txn +0 -0
  177. package/.ctxman/self-knowledge/documents.lance/_transactions/5-4fae93df-0568-4b6f-8acf-8bef4bba6047.txn +0 -0
  178. package/.ctxman/self-knowledge/documents.lance/_transactions/6-4b1b8b87-068e-49a3-ba2b-d8dae628b252.txn +0 -0
  179. package/.ctxman/self-knowledge/documents.lance/_transactions/7-63d040fb-d2fa-4c0f-bed0-53411918a7b0.txn +0 -0
  180. package/.ctxman/self-knowledge/documents.lance/_transactions/8-c5628cf6-264c-420f-aab8-bc96a9083591.txn +0 -0
  181. package/.ctxman/self-knowledge/documents.lance/_transactions/9-c94570aa-5e53-4fcb-971f-cecc1d1d5492.txn +0 -0
  182. package/.ctxman/self-knowledge/documents.lance/_versions/1.manifest +0 -0
  183. package/.ctxman/self-knowledge/documents.lance/_versions/10.manifest +0 -0
  184. package/.ctxman/self-knowledge/documents.lance/_versions/11.manifest +0 -0
  185. package/.ctxman/self-knowledge/documents.lance/_versions/12.manifest +0 -0
  186. package/.ctxman/self-knowledge/documents.lance/_versions/13.manifest +0 -0
  187. package/.ctxman/self-knowledge/documents.lance/_versions/14.manifest +0 -0
  188. package/.ctxman/self-knowledge/documents.lance/_versions/15.manifest +0 -0
  189. package/.ctxman/self-knowledge/documents.lance/_versions/16.manifest +0 -0
  190. package/.ctxman/self-knowledge/documents.lance/_versions/17.manifest +0 -0
  191. package/.ctxman/self-knowledge/documents.lance/_versions/18.manifest +0 -0
  192. package/.ctxman/self-knowledge/documents.lance/_versions/19.manifest +0 -0
  193. package/.ctxman/self-knowledge/documents.lance/_versions/2.manifest +0 -0
  194. package/.ctxman/self-knowledge/documents.lance/_versions/20.manifest +0 -0
  195. package/.ctxman/self-knowledge/documents.lance/_versions/21.manifest +0 -0
  196. package/.ctxman/self-knowledge/documents.lance/_versions/3.manifest +0 -0
  197. package/.ctxman/self-knowledge/documents.lance/_versions/4.manifest +0 -0
  198. package/.ctxman/self-knowledge/documents.lance/_versions/5.manifest +0 -0
  199. package/.ctxman/self-knowledge/documents.lance/_versions/6.manifest +0 -0
  200. package/.ctxman/self-knowledge/documents.lance/_versions/7.manifest +0 -0
  201. package/.ctxman/self-knowledge/documents.lance/_versions/8.manifest +0 -0
  202. package/.ctxman/self-knowledge/documents.lance/_versions/9.manifest +0 -0
  203. package/.ctxman/self-knowledge/documents.lance/data/000010010011011101000010de8621405d9d5cb06540dbf02c.lance +0 -0
  204. package/.ctxman/self-knowledge/documents.lance/data/0000101001011100011000003fb62a4aa8978974075c144f1c.lance +0 -0
  205. package/.ctxman/self-knowledge/documents.lance/data/000011010000100111001011c6cc824d628f3bb1f4e04cd5d0.lance +0 -0
  206. package/.ctxman/self-knowledge/documents.lance/data/001000001000011001011100d8f74349ada090b332cd1df669.lance +0 -0
  207. package/.ctxman/self-knowledge/documents.lance/data/01000001111010001000010045ab44467bb1e7e4d8ae06bd58.lance +0 -0
  208. package/.ctxman/self-knowledge/documents.lance/data/01000101101110111110011131ef904241a89e1ffb6ddaf8e3.lance +0 -0
  209. package/.ctxman/self-knowledge/documents.lance/data/010010110000010000011010fbfcc145c6a8a827b28057a07e.lance +0 -0
  210. package/.ctxman/self-knowledge/documents.lance/data/1000011000101010001110003fb4f045c3835982efa1405324.lance +0 -0
  211. package/.ctxman/self-knowledge/documents.lance/data/100111011101110111101011f4396b410fa06af3b028d204e6.lance +0 -0
  212. package/.ctxman/self-knowledge/documents.lance/data/100111100111101000001100596b174e00995d6cc2969cc8dc.lance +0 -0
  213. package/.ctxman/self-knowledge/documents.lance/data/1010001100011000110010102ea16b484d887c7c9725feffd6.lance +0 -0
  214. package/.ctxman/self-knowledge/documents.lance/data/101101100010101111110111c5230944d9b3af7301b6392dda.lance +0 -0
  215. package/.ctxman/self-knowledge/documents.lance/data/1011011100100111111110105892bf41b5a530fedd7cf516f1.lance +0 -0
  216. package/.ctxman/self-knowledge/documents.lance/data/10111110000010001011110164e8774742bade87b483c10f36.lance +0 -0
  217. package/.ctxman/self-knowledge/documents.lance/data/1100000100010110001110103ffaca40a2a42c85067ebaf9ae.lance +0 -0
  218. package/.ctxman/self-knowledge/documents.lance/data/110000010001110110101110664bde4c76a78dfc14b05e83b8.lance +0 -0
  219. package/.ctxman/self-knowledge/documents.lance/data/11001100111000010100110050645c4281b1f35b2022234f7f.lance +0 -0
  220. package/.ctxman/self-knowledge/documents.lance/data/1101111011001100110111017214924a368f5719eab2246619.lance +0 -0
  221. package/.ctxman/self-knowledge/documents.lance/data/1110000111001010001000002150c24dcab653d39d07032e63.lance +0 -0
  222. package/.ctxman/self-knowledge/documents.lance/data/1110100000000100000011014d20534967a18b5c87721659ff.lance +0 -0
  223. package/.ctxman/self-knowledge/documents.lance/data/11110101111010000101011135a0114ef2b35dc6f1297a3381.lance +0 -0
@@ -8,247 +8,257 @@ import fs from 'fs';
8
8
  import path from 'path';
9
9
 
10
10
  class Logger {
11
- constructor(options = {}) {
12
- this.level = options.level || process.env.LOG_LEVEL || 'info';
13
- this.logToFile = options.logToFile !== false;
14
- this.logDir = options.logDir || path.join(process.cwd(), '.ctxman', 'logs');
15
- this.logFile = options.logFile || `ctxman-${this.getDateString()}.log`;
16
- this.silent = options.silent || false;
17
-
18
- // Log levels (higher number = more verbose)
19
- this.levels = {
20
- error: 0,
21
- warn: 1,
22
- info: 2,
23
- debug: 3,
24
- trace: 4
25
- };
26
-
27
- // Colors for terminal output
28
- this.colors = {
29
- error: '\x1b[31m', // Red
30
- warn: '\x1b[33m', // Yellow
31
- info: '\x1b[36m', // Cyan
32
- debug: '\x1b[35m', // Magenta
33
- trace: '\x1b[90m', // Gray
34
- reset: '\x1b[0m'
35
- };
36
-
37
- // Icons for each level
38
- this.icons = {
39
- error: '❌',
40
- warn: '⚠️',
41
- info: 'ℹ️',
42
- debug: '🔍',
43
- trace: '🔬'
44
- };
45
-
46
- this.initializeLogDirectory();
47
- }
48
-
49
- /**
50
- * Initialize log directory
51
- */
52
- initializeLogDirectory() {
53
- if (this.logToFile) {
54
- const fullLogDir = path.isAbsolute(this.logDir) ? this.logDir : path.join(process.cwd(), this.logDir);
55
- if (!fs.existsSync(fullLogDir)) {
56
- fs.mkdirSync(fullLogDir, { recursive: true });
57
- }
11
+ constructor(options = {}) {
12
+ this.level = (options.level || process.env.LOG_LEVEL || 'info').toLowerCase();
13
+ this.logToFile = options.logToFile !== false;
14
+ this.logDir = options.logDir || path.join(process.cwd(), '.ctxman', 'logs');
15
+ this.logFile = options.logFile || `ctxman-${this.getDateString()}.log`;
16
+ this.silent = options.silent || false;
17
+
18
+ // Log levels (higher number = more verbose)
19
+ this.levels = {
20
+ error: 0,
21
+ warn: 1,
22
+ info: 2,
23
+ debug: 3,
24
+ trace: 4,
25
+ };
26
+
27
+ // Colors for terminal output
28
+ this.colors = {
29
+ error: '\x1b[31m', // Red
30
+ warn: '\x1b[33m', // Yellow
31
+ info: '\x1b[36m', // Cyan
32
+ debug: '\x1b[35m', // Magenta
33
+ trace: '\x1b[90m', // Gray
34
+ reset: '\x1b[0m',
35
+ };
36
+
37
+ // Icons for each level
38
+ this.icons = {
39
+ error: '❌',
40
+ warn: '⚠️',
41
+ info: 'ℹ️',
42
+ debug: '🔍',
43
+ trace: '🔬',
44
+ };
45
+
46
+ this.initializeLogDirectory();
47
+ }
48
+
49
+ /**
50
+ * Initialize log directory
51
+ */
52
+ initializeLogDirectory() {
53
+ if (this.logToFile) {
54
+ const fullLogDir = path.isAbsolute(this.logDir)
55
+ ? this.logDir
56
+ : path.join(process.cwd(), this.logDir);
57
+ try {
58
+ if (!fs.existsSync(fullLogDir)) {
59
+ fs.mkdirSync(fullLogDir, { recursive: true });
58
60
  }
61
+ } catch (_error) {
62
+ // Loggers are created at import time, so an unwritable working directory (such as
63
+ // "/" for an MCP server spawned by a desktop app) must not crash the process
64
+ this.logToFile = false;
65
+ }
59
66
  }
60
-
61
- /**
62
- * Get current date string for log file names
63
- */
64
- getDateString() {
65
- const now = new Date();
66
- return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
67
- }
68
-
69
- /**
70
- * Get timestamp for log entries
71
- */
72
- getTimestamp() {
73
- return new Date().toISOString();
67
+ }
68
+
69
+ /**
70
+ * Get current date string for log file names
71
+ */
72
+ getDateString() {
73
+ const now = new Date();
74
+ return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
75
+ }
76
+
77
+ /**
78
+ * Get timestamp for log entries
79
+ */
80
+ getTimestamp() {
81
+ return new Date().toISOString();
82
+ }
83
+
84
+ /**
85
+ * Check if should log based on level
86
+ */
87
+ shouldLog(level) {
88
+ return this.levels[level] <= this.levels[this.level];
89
+ }
90
+
91
+ /**
92
+ * Format log message
93
+ */
94
+ formatMessage(level, message, meta = {}) {
95
+ const timestamp = this.getTimestamp();
96
+ const icon = this.icons[level];
97
+
98
+ // Console format (with colors)
99
+ const consoleMessage = `${this.colors[level]}${icon} [${level.toUpperCase()}]${this.colors.reset} ${message}`;
100
+
101
+ // File format (plain text with metadata)
102
+ const metaString = Object.keys(meta).length > 0 ? ` | ${JSON.stringify(meta)}` : '';
103
+ const fileMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}${metaString}`;
104
+
105
+ return { consoleMessage, fileMessage };
106
+ }
107
+
108
+ /**
109
+ * Write log to file
110
+ */
111
+ writeToFile(message) {
112
+ if (!this.logToFile) return;
113
+
114
+ try {
115
+ const fullLogPath = path.isAbsolute(this.logDir)
116
+ ? path.join(this.logDir, this.logFile)
117
+ : path.join(process.cwd(), this.logDir, this.logFile);
118
+
119
+ fs.appendFileSync(fullLogPath, message + '\n', 'utf8');
120
+ } catch (error) {
121
+ // Fail silently to avoid infinite loop
122
+ console.error('Failed to write to log file:', error.message);
74
123
  }
124
+ }
75
125
 
76
- /**
77
- * Check if should log based on level
78
- */
79
- shouldLog(level) {
80
- return this.levels[level] <= this.levels[this.level];
81
- }
82
-
83
- /**
84
- * Format log message
85
- */
86
- formatMessage(level, message, meta = {}) {
87
- const timestamp = this.getTimestamp();
88
- const icon = this.icons[level];
89
-
90
- // Console format (with colors)
91
- const consoleMessage = `${this.colors[level]}${icon} [${level.toUpperCase()}]${this.colors.reset} ${message}`;
92
-
93
- // File format (plain text with metadata)
94
- const metaString = Object.keys(meta).length > 0 ? ` | ${JSON.stringify(meta)}` : '';
95
- const fileMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}${metaString}`;
96
-
97
- return { consoleMessage, fileMessage };
98
- }
126
+ /**
127
+ * Core logging method
128
+ */
129
+ log(level, message, meta = {}) {
130
+ if (!this.shouldLog(level)) return;
99
131
 
100
- /**
101
- * Write log to file
102
- */
103
- writeToFile(message) {
104
- if (!this.logToFile) return;
105
-
106
- try {
107
- const fullLogPath = path.isAbsolute(this.logDir)
108
- ? path.join(this.logDir, this.logFile)
109
- : path.join(process.cwd(), this.logDir, this.logFile);
110
-
111
- fs.appendFileSync(fullLogPath, message + '\n', 'utf8');
112
- } catch (error) {
113
- // Fail silently to avoid infinite loop
114
- console.error('Failed to write to log file:', error.message);
115
- }
116
- }
117
-
118
- /**
119
- * Core logging method
120
- */
121
- log(level, message, meta = {}) {
122
- if (!this.shouldLog(level)) return;
123
-
124
- const { consoleMessage, fileMessage } = this.formatMessage(level, message, meta);
125
-
126
- // Output to console
127
- if (!this.silent) {
128
- console.log(consoleMessage);
129
- }
130
-
131
- // Output to file
132
- this.writeToFile(fileMessage);
133
- }
134
-
135
- /**
136
- * Convenience methods
137
- */
138
- error(message, meta = {}) {
139
- this.log('error', message, meta);
140
- }
132
+ const { consoleMessage, fileMessage } = this.formatMessage(level, message, meta);
141
133
 
142
- warn(message, meta = {}) {
143
- this.log('warn', message, meta);
134
+ // Output to console
135
+ if (!this.silent) {
136
+ console.log(consoleMessage);
144
137
  }
145
138
 
146
- info(message, meta = {}) {
147
- this.log('info', message, meta);
139
+ // Output to file
140
+ this.writeToFile(fileMessage);
141
+ }
142
+
143
+ /**
144
+ * Convenience methods
145
+ */
146
+ error(message, meta = {}) {
147
+ this.log('error', message, meta);
148
+ }
149
+
150
+ warn(message, meta = {}) {
151
+ this.log('warn', message, meta);
152
+ }
153
+
154
+ info(message, meta = {}) {
155
+ this.log('info', message, meta);
156
+ }
157
+
158
+ debug(message, meta = {}) {
159
+ this.log('debug', message, meta);
160
+ }
161
+
162
+ trace(message, meta = {}) {
163
+ this.log('trace', message, meta);
164
+ }
165
+
166
+ /**
167
+ * Log with custom icon
168
+ */
169
+ custom(icon, level, message, meta = {}) {
170
+ const originalIcon = this.icons[level];
171
+ this.icons[level] = icon;
172
+ this.log(level, message, meta);
173
+ this.icons[level] = originalIcon;
174
+ }
175
+
176
+ /**
177
+ * Group related logs
178
+ */
179
+ group(title) {
180
+ if (!this.silent) {
181
+ console.group(title);
148
182
  }
183
+ this.writeToFile(`\n--- ${title} ---`);
184
+ }
149
185
 
150
- debug(message, meta = {}) {
151
- this.log('debug', message, meta);
186
+ groupEnd() {
187
+ if (!this.silent) {
188
+ console.groupEnd();
152
189
  }
153
-
154
- trace(message, meta = {}) {
155
- this.log('trace', message, meta);
156
- }
157
-
158
- /**
159
- * Log with custom icon
160
- */
161
- custom(icon, level, message, meta = {}) {
162
- const originalIcon = this.icons[level];
163
- this.icons[level] = icon;
164
- this.log(level, message, meta);
165
- this.icons[level] = originalIcon;
166
- }
167
-
168
- /**
169
- * Group related logs
170
- */
171
- group(title) {
172
- if (!this.silent) {
173
- console.group(title);
174
- }
175
- this.writeToFile(`\n--- ${title} ---`);
176
- }
177
-
178
- groupEnd() {
179
- if (!this.silent) {
180
- console.groupEnd();
181
- }
182
- this.writeToFile('--- End Group ---\n');
183
- }
184
-
185
- /**
186
- * Log with timer
187
- */
188
- time(label) {
189
- console.time(label);
190
- this.writeToFile(`[TIMER START] ${label}`);
191
- }
192
-
193
- timeEnd(label) {
194
- console.timeEnd(label);
195
- this.writeToFile(`[TIMER END] ${label}`);
196
- }
197
-
198
- /**
199
- * Clear old log files (keep last N days)
200
- */
201
- clearOldLogs(daysToKeep = 7) {
202
- try {
203
- const fullLogDir = path.isAbsolute(this.logDir) ? this.logDir : path.join(process.cwd(), this.logDir);
204
- if (!fs.existsSync(fullLogDir)) return;
205
-
206
- const now = Date.now();
207
- const maxAge = daysToKeep * 24 * 60 * 60 * 1000;
208
-
209
- const files = fs.readdirSync(fullLogDir);
210
- files.forEach(file => {
211
- const filePath = path.join(fullLogDir, file);
212
- const stats = fs.statSync(filePath);
213
- const age = now - stats.mtime.getTime();
214
-
215
- if (age > maxAge) {
216
- fs.unlinkSync(filePath);
217
- this.debug(`Deleted old log file: ${file}`);
218
- }
219
- });
220
- } catch (error) {
221
- this.error('Failed to clear old logs', { error: error.message });
190
+ this.writeToFile('--- End Group ---\n');
191
+ }
192
+
193
+ /**
194
+ * Log with timer
195
+ */
196
+ time(label) {
197
+ console.time(label);
198
+ this.writeToFile(`[TIMER START] ${label}`);
199
+ }
200
+
201
+ timeEnd(label) {
202
+ console.timeEnd(label);
203
+ this.writeToFile(`[TIMER END] ${label}`);
204
+ }
205
+
206
+ /**
207
+ * Clear old log files (keep last N days)
208
+ */
209
+ clearOldLogs(daysToKeep = 7) {
210
+ try {
211
+ const fullLogDir = path.isAbsolute(this.logDir)
212
+ ? this.logDir
213
+ : path.join(process.cwd(), this.logDir);
214
+ if (!fs.existsSync(fullLogDir)) return;
215
+
216
+ const now = Date.now();
217
+ const maxAge = daysToKeep * 24 * 60 * 60 * 1000;
218
+
219
+ const files = fs.readdirSync(fullLogDir);
220
+ files.forEach((file) => {
221
+ const filePath = path.join(fullLogDir, file);
222
+ const stats = fs.statSync(filePath);
223
+ const age = now - stats.mtime.getTime();
224
+
225
+ if (age > maxAge) {
226
+ fs.unlinkSync(filePath);
227
+ this.debug(`Deleted old log file: ${file}`);
222
228
  }
229
+ });
230
+ } catch (error) {
231
+ this.error('Failed to clear old logs', { error: error.message });
223
232
  }
224
-
225
- /**
226
- * Get log file path
227
- */
228
- getLogFilePath() {
229
- return path.isAbsolute(this.logDir)
230
- ? path.join(this.logDir, this.logFile)
231
- : path.join(process.cwd(), this.logDir, this.logFile);
232
- }
233
-
234
- /**
235
- * Read recent logs
236
- */
237
- getRecentLogs(lines = 100) {
238
- try {
239
- const logPath = this.getLogFilePath();
240
- if (!fs.existsSync(logPath)) {
241
- return [];
242
- }
243
-
244
- const content = fs.readFileSync(logPath, 'utf8');
245
- const allLines = content.split('\n').filter(line => line.trim());
246
- return allLines.slice(-lines);
247
- } catch (error) {
248
- this.error('Failed to read logs', { error: error.message });
249
- return [];
250
- }
233
+ }
234
+
235
+ /**
236
+ * Get log file path
237
+ */
238
+ getLogFilePath() {
239
+ return path.isAbsolute(this.logDir)
240
+ ? path.join(this.logDir, this.logFile)
241
+ : path.join(process.cwd(), this.logDir, this.logFile);
242
+ }
243
+
244
+ /**
245
+ * Read recent logs
246
+ */
247
+ getRecentLogs(lines = 100) {
248
+ try {
249
+ const logPath = this.getLogFilePath();
250
+ if (!fs.existsSync(logPath)) {
251
+ return [];
252
+ }
253
+
254
+ const content = fs.readFileSync(logPath, 'utf8');
255
+ const allLines = content.split('\n').filter((line) => line.trim());
256
+ return allLines.slice(-lines);
257
+ } catch (error) {
258
+ this.error('Failed to read logs', { error: error.message });
259
+ return [];
251
260
  }
261
+ }
252
262
  }
253
263
 
254
264
  // Create singleton instance
@@ -258,21 +268,17 @@ let defaultLogger = null;
258
268
  * Get or create default logger instance
259
269
  */
260
270
  function getLogger(options = {}) {
261
- if (!defaultLogger) {
262
- defaultLogger = new Logger(options);
263
- }
264
- return defaultLogger;
271
+ if (!defaultLogger) {
272
+ defaultLogger = new Logger(options);
273
+ }
274
+ return defaultLogger;
265
275
  }
266
276
 
267
277
  /**
268
278
  * Create a new logger instance
269
279
  */
270
280
  function createLogger(options = {}) {
271
- return new Logger(options);
281
+ return new Logger(options);
272
282
  }
273
283
 
274
- export {
275
- Logger,
276
- getLogger,
277
- createLogger
278
- };
284
+ export { Logger, getLogger, createLogger };