logicstamp-context 0.1.0 → 0.2.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 (176) hide show
  1. package/LICENSE +21 -21
  2. package/LLM_CONTEXT.md +4 -3
  3. package/README.md +140 -21
  4. package/dist/cli/commands/compare.d.ts.map +1 -1
  5. package/dist/cli/commands/compare.js +17 -8
  6. package/dist/cli/commands/compare.js.map +1 -1
  7. package/dist/cli/commands/context/bundleFormatter.d.ts +20 -0
  8. package/dist/cli/commands/context/bundleFormatter.d.ts.map +1 -0
  9. package/dist/cli/commands/context/bundleFormatter.js +55 -0
  10. package/dist/cli/commands/context/bundleFormatter.js.map +1 -0
  11. package/dist/cli/commands/context/configManager.d.ts +23 -0
  12. package/dist/cli/commands/context/configManager.d.ts.map +1 -0
  13. package/dist/cli/commands/context/configManager.js +69 -0
  14. package/dist/cli/commands/context/configManager.js.map +1 -0
  15. package/dist/cli/commands/context/contractBuilder.d.ts +18 -0
  16. package/dist/cli/commands/context/contractBuilder.d.ts.map +1 -0
  17. package/dist/cli/commands/context/contractBuilder.js +72 -0
  18. package/dist/cli/commands/context/contractBuilder.js.map +1 -0
  19. package/dist/cli/commands/context/fileWriter.d.ts +38 -0
  20. package/dist/cli/commands/context/fileWriter.d.ts.map +1 -0
  21. package/dist/cli/commands/context/fileWriter.js +165 -0
  22. package/dist/cli/commands/context/fileWriter.js.map +1 -0
  23. package/dist/cli/commands/context/statsCalculator.d.ts +44 -0
  24. package/dist/cli/commands/context/statsCalculator.d.ts.map +1 -0
  25. package/dist/cli/commands/context/statsCalculator.js +150 -0
  26. package/dist/cli/commands/context/statsCalculator.js.map +1 -0
  27. package/dist/cli/commands/context/tokenEstimator.d.ts +76 -0
  28. package/dist/cli/commands/context/tokenEstimator.d.ts.map +1 -0
  29. package/dist/cli/commands/context/tokenEstimator.js +258 -0
  30. package/dist/cli/commands/context/tokenEstimator.js.map +1 -0
  31. package/dist/cli/commands/context.d.ts +2 -0
  32. package/dist/cli/commands/context.d.ts.map +1 -1
  33. package/dist/cli/commands/context.js +75 -405
  34. package/dist/cli/commands/context.js.map +1 -1
  35. package/dist/cli/commands/init.d.ts.map +1 -1
  36. package/dist/cli/commands/init.js +113 -24
  37. package/dist/cli/commands/init.js.map +1 -1
  38. package/dist/cli/commands/style.d.ts +11 -0
  39. package/dist/cli/commands/style.d.ts.map +1 -0
  40. package/dist/cli/commands/style.js +15 -0
  41. package/dist/cli/commands/style.js.map +1 -0
  42. package/dist/cli/handlers/cleanHandler.d.ts +5 -0
  43. package/dist/cli/handlers/cleanHandler.d.ts.map +1 -0
  44. package/dist/cli/handlers/cleanHandler.js +28 -0
  45. package/dist/cli/handlers/cleanHandler.js.map +1 -0
  46. package/dist/cli/handlers/compareHandler.d.ts +5 -0
  47. package/dist/cli/handlers/compareHandler.d.ts.map +1 -0
  48. package/dist/cli/handlers/compareHandler.js +332 -0
  49. package/dist/cli/handlers/compareHandler.js.map +1 -0
  50. package/dist/cli/handlers/contextHandler.d.ts +5 -0
  51. package/dist/cli/handlers/contextHandler.d.ts.map +1 -0
  52. package/dist/cli/handlers/contextHandler.js +24 -0
  53. package/dist/cli/handlers/contextHandler.js.map +1 -0
  54. package/dist/cli/handlers/initHandler.d.ts +6 -0
  55. package/dist/cli/handlers/initHandler.d.ts.map +1 -0
  56. package/dist/cli/handlers/initHandler.js +30 -0
  57. package/dist/cli/handlers/initHandler.js.map +1 -0
  58. package/dist/cli/handlers/styleHandler.d.ts +5 -0
  59. package/dist/cli/handlers/styleHandler.d.ts.map +1 -0
  60. package/dist/cli/handlers/styleHandler.js +30 -0
  61. package/dist/cli/handlers/styleHandler.js.map +1 -0
  62. package/dist/cli/handlers/validateHandler.d.ts +5 -0
  63. package/dist/cli/handlers/validateHandler.d.ts.map +1 -0
  64. package/dist/cli/handlers/validateHandler.js +23 -0
  65. package/dist/cli/handlers/validateHandler.js.map +1 -0
  66. package/dist/cli/index.js +5 -0
  67. package/dist/cli/index.js.map +1 -1
  68. package/dist/cli/parser/argumentParser.d.ts +44 -0
  69. package/dist/cli/parser/argumentParser.d.ts.map +1 -0
  70. package/dist/cli/parser/argumentParser.js +194 -0
  71. package/dist/cli/parser/argumentParser.js.map +1 -0
  72. package/dist/cli/parser/helpText.d.ts +11 -0
  73. package/dist/cli/parser/helpText.d.ts.map +1 -0
  74. package/dist/cli/parser/helpText.js +373 -0
  75. package/dist/cli/parser/helpText.js.map +1 -0
  76. package/dist/cli/stamp.js +14 -897
  77. package/dist/cli/stamp.js.map +1 -1
  78. package/dist/core/astParser/detectors.d.ts +24 -0
  79. package/dist/core/astParser/detectors.d.ts.map +1 -0
  80. package/dist/core/astParser/detectors.js +102 -0
  81. package/dist/core/astParser/detectors.js.map +1 -0
  82. package/dist/core/astParser/extractors/componentExtractor.d.ts +13 -0
  83. package/dist/core/astParser/extractors/componentExtractor.d.ts.map +1 -0
  84. package/dist/core/astParser/extractors/componentExtractor.js +41 -0
  85. package/dist/core/astParser/extractors/componentExtractor.js.map +1 -0
  86. package/dist/core/astParser/extractors/eventExtractor.d.ts +14 -0
  87. package/dist/core/astParser/extractors/eventExtractor.d.ts.map +1 -0
  88. package/dist/core/astParser/extractors/eventExtractor.js +49 -0
  89. package/dist/core/astParser/extractors/eventExtractor.js.map +1 -0
  90. package/dist/core/astParser/extractors/propExtractor.d.ts +14 -0
  91. package/dist/core/astParser/extractors/propExtractor.d.ts.map +1 -0
  92. package/dist/core/astParser/extractors/propExtractor.js +73 -0
  93. package/dist/core/astParser/extractors/propExtractor.js.map +1 -0
  94. package/dist/core/astParser/extractors/stateExtractor.d.ts +13 -0
  95. package/dist/core/astParser/extractors/stateExtractor.d.ts.map +1 -0
  96. package/dist/core/astParser/extractors/stateExtractor.js +66 -0
  97. package/dist/core/astParser/extractors/stateExtractor.js.map +1 -0
  98. package/dist/core/astParser.d.ts.map +1 -1
  99. package/dist/core/astParser.js +5 -307
  100. package/dist/core/astParser.js.map +1 -1
  101. package/dist/core/contractBuilder.d.ts +1 -0
  102. package/dist/core/contractBuilder.d.ts.map +1 -1
  103. package/dist/core/contractBuilder.js +1 -0
  104. package/dist/core/contractBuilder.js.map +1 -1
  105. package/dist/core/pack/builder.d.ts +35 -0
  106. package/dist/core/pack/builder.d.ts.map +1 -0
  107. package/dist/core/pack/builder.js +76 -0
  108. package/dist/core/pack/builder.js.map +1 -0
  109. package/dist/core/pack/collector.d.ts +20 -0
  110. package/dist/core/pack/collector.d.ts.map +1 -0
  111. package/dist/core/pack/collector.js +71 -0
  112. package/dist/core/pack/collector.js.map +1 -0
  113. package/dist/core/pack/loader.d.ts +23 -0
  114. package/dist/core/pack/loader.d.ts.map +1 -0
  115. package/dist/core/pack/loader.js +66 -0
  116. package/dist/core/pack/loader.js.map +1 -0
  117. package/dist/core/pack/resolver.d.ts +21 -0
  118. package/dist/core/pack/resolver.d.ts.map +1 -0
  119. package/dist/core/pack/resolver.js +79 -0
  120. package/dist/core/pack/resolver.js.map +1 -0
  121. package/dist/core/pack.d.ts +17 -27
  122. package/dist/core/pack.d.ts.map +1 -1
  123. package/dist/core/pack.js +18 -225
  124. package/dist/core/pack.js.map +1 -1
  125. package/dist/core/styleExtractor/index.d.ts +11 -0
  126. package/dist/core/styleExtractor/index.d.ts.map +1 -0
  127. package/dist/core/styleExtractor/index.js +11 -0
  128. package/dist/core/styleExtractor/index.js.map +1 -0
  129. package/dist/core/styleExtractor/layout.d.ts +14 -0
  130. package/dist/core/styleExtractor/layout.d.ts.map +1 -0
  131. package/dist/core/styleExtractor/layout.js +86 -0
  132. package/dist/core/styleExtractor/layout.js.map +1 -0
  133. package/dist/core/styleExtractor/motion.d.ts +20 -0
  134. package/dist/core/styleExtractor/motion.d.ts.map +1 -0
  135. package/dist/core/styleExtractor/motion.js +74 -0
  136. package/dist/core/styleExtractor/motion.js.map +1 -0
  137. package/dist/core/styleExtractor/scss.d.ts +30 -0
  138. package/dist/core/styleExtractor/scss.d.ts.map +1 -0
  139. package/dist/core/styleExtractor/scss.js +80 -0
  140. package/dist/core/styleExtractor/scss.js.map +1 -0
  141. package/dist/core/styleExtractor/styleExtractor.d.ts +11 -0
  142. package/dist/core/styleExtractor/styleExtractor.d.ts.map +1 -0
  143. package/dist/core/styleExtractor/styleExtractor.js +115 -0
  144. package/dist/core/styleExtractor/styleExtractor.js.map +1 -0
  145. package/dist/core/styleExtractor/styled.d.ts +13 -0
  146. package/dist/core/styleExtractor/styled.d.ts.map +1 -0
  147. package/dist/core/styleExtractor/styled.js +31 -0
  148. package/dist/core/styleExtractor/styled.js.map +1 -0
  149. package/dist/core/styleExtractor/tailwind.d.ts +16 -0
  150. package/dist/core/styleExtractor/tailwind.d.ts.map +1 -0
  151. package/dist/core/styleExtractor/tailwind.js +85 -0
  152. package/dist/core/styleExtractor/tailwind.js.map +1 -0
  153. package/dist/core/styleExtractor.d.ts +8 -0
  154. package/dist/core/styleExtractor.d.ts.map +1 -0
  155. package/dist/core/styleExtractor.js +8 -0
  156. package/dist/core/styleExtractor.js.map +1 -0
  157. package/dist/index.d.ts +4 -2
  158. package/dist/index.d.ts.map +1 -1
  159. package/dist/index.js.map +1 -1
  160. package/dist/types/UIFContract.d.ts +71 -0
  161. package/dist/types/UIFContract.d.ts.map +1 -1
  162. package/dist/types/UIFContract.js.map +1 -1
  163. package/dist/utils/gitignore.d.ts +6 -5
  164. package/dist/utils/gitignore.d.ts.map +1 -1
  165. package/dist/utils/gitignore.js +11 -50
  166. package/dist/utils/gitignore.js.map +1 -1
  167. package/dist/utils/llmContext.d.ts +4 -6
  168. package/dist/utils/llmContext.d.ts.map +1 -1
  169. package/dist/utils/llmContext.js +8 -59
  170. package/dist/utils/llmContext.js.map +1 -1
  171. package/dist/utils/tokens.d.ts +18 -10
  172. package/dist/utils/tokens.d.ts.map +1 -1
  173. package/dist/utils/tokens.js +112 -10
  174. package/dist/utils/tokens.js.map +1 -1
  175. package/package.json +3 -1
  176. package/schema/logicstamp.context.schema.json +288 -0
package/dist/cli/stamp.js CHANGED
@@ -3,22 +3,17 @@
3
3
  * Stamp CLI - Main entry point for LogicStamp Context tools
4
4
  * Routes to context operations: generate, validate, compare
5
5
  */
6
- import { contextCommand } from './commands/context.js';
7
- import { compareCommand, multiFileCompare, displayMultiFileCompareResult, cleanOrphanedFiles, } from './commands/compare.js';
8
- import { validateCommand } from './commands/validate.js';
9
- import { init } from './commands/init.js';
10
- import { cleanCommand } from './commands/clean.js';
11
6
  import { readFile } from 'node:fs/promises';
12
7
  import { join, dirname } from 'node:path';
13
8
  import { fileURLToPath } from 'node:url';
14
- function printFoxIcon() {
15
- console.log(`
16
- /\\_/\\
17
- ( o.o )
18
- > ^ <
19
- 🦊 Meet the Logic Fox
20
- `);
21
- }
9
+ import { handleInit } from './handlers/initHandler.js';
10
+ import { handleValidate } from './handlers/validateHandler.js';
11
+ import { handleCompare } from './handlers/compareHandler.js';
12
+ import { handleClean } from './handlers/cleanHandler.js';
13
+ import { handleStyle } from './handlers/styleHandler.js';
14
+ import { handleGenerate } from './handlers/contextHandler.js';
15
+ import { getMainHelp } from './parser/helpText.js';
16
+ import { printFoxIcon } from './handlers/initHandler.js';
22
17
  async function main() {
23
18
  const args = process.argv.slice(2);
24
19
  // Check for version
@@ -41,7 +36,7 @@ async function main() {
41
36
  // Check for help - only if no args or first arg is help
42
37
  if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
43
38
  printFoxIcon();
44
- printMainHelp();
39
+ console.log(getMainHelp());
45
40
  process.exit(0);
46
41
  }
47
42
  // First argument should be the subcommand
@@ -71,891 +66,13 @@ async function main() {
71
66
  await handleClean(contextArgs.slice(1));
72
67
  return;
73
68
  }
74
- // Default: generate context
75
- await handleGenerate(contextArgs);
76
- }
77
- async function handleInit(args) {
78
- printFoxIcon();
79
- if (args.includes('--help') || args.includes('-h')) {
80
- printInitHelp();
81
- process.exit(0);
82
- }
83
- const options = {};
84
- // Parse command line arguments
85
- for (let i = 0; i < args.length; i++) {
86
- const arg = args[i];
87
- if (arg.startsWith('--')) {
88
- const key = arg.replace(/^--/, '');
89
- switch (key) {
90
- case 'skip-gitignore':
91
- options.skipGitignore = true;
92
- break;
93
- case 'quiet':
94
- // Ignore --quiet for init (quiet mode not supported)
95
- break;
96
- default:
97
- console.error(`❌ Unknown option: ${arg}`);
98
- process.exit(1);
99
- }
100
- }
101
- else if (arg !== '-q') {
102
- // First non-option argument is the target directory (ignore -q)
103
- if (!options.targetDir) {
104
- options.targetDir = arg;
105
- }
106
- }
107
- }
108
- try {
109
- await init(options);
110
- }
111
- catch (error) {
112
- console.error('❌ Initialization failed:', error.message);
113
- process.exit(1);
114
- }
115
- }
116
- async function handleValidate(args) {
117
- if (args.includes('--help') || args.includes('-h')) {
118
- printFoxIcon();
119
- printValidateHelp();
120
- process.exit(0);
121
- }
122
- const quiet = args.includes('--quiet') || args.includes('-q');
123
- const filePath = args.filter(arg => arg !== '--quiet' && arg !== '-q')[0];
124
- try {
125
- await validateCommand(filePath, quiet);
126
- }
127
- catch (error) {
128
- console.error('❌ Validation failed:', error.message);
129
- process.exit(1);
130
- }
131
- }
132
- async function handleClean(args) {
133
- if (args.includes('--help') || args.includes('-h')) {
134
- printFoxIcon();
135
- printCleanHelp();
136
- process.exit(0);
137
- }
138
- const options = {
139
- all: args.includes('--all'),
140
- yes: args.includes('--yes'),
141
- quiet: args.includes('--quiet') || args.includes('-q'),
142
- };
143
- // First non-option argument is the target directory
144
- for (const arg of args) {
145
- if (!arg.startsWith('--') && !options.projectRoot) {
146
- options.projectRoot = arg;
147
- break;
148
- }
149
- }
150
- try {
151
- await cleanCommand(options);
152
- }
153
- catch (error) {
154
- console.error('❌ Clean failed:', error.message);
155
- process.exit(1);
156
- }
157
- }
158
- /**
159
- * Prompt user for Y/N input (only in TTY mode)
160
- */
161
- async function promptYesNo(question) {
162
- const { createInterface } = await import('node:readline');
163
- const readline = createInterface({
164
- input: process.stdin,
165
- output: process.stdout,
166
- });
167
- return new Promise((resolve) => {
168
- readline.question(question, (answer) => {
169
- readline.close();
170
- resolve(answer.toLowerCase() === 'y');
171
- });
172
- });
173
- }
174
- /**
175
- * Check if running in interactive TTY
176
- */
177
- function isTTY() {
178
- return process.stdout.isTTY === true && process.stdin.isTTY === true;
179
- }
180
- /**
181
- * Wrap a function call to suppress or filter console output
182
- */
183
- async function withSuppressedOutput(fn, quiet) {
184
- if (quiet) {
185
- // In quiet mode, suppress all output
186
- const originalLog = console.log;
187
- const originalError = console.error;
188
- const originalWarn = console.warn;
189
- console.log = () => { };
190
- console.error = () => { };
191
- console.warn = () => { };
192
- try {
193
- return await fn();
194
- }
195
- finally {
196
- console.log = originalLog;
197
- console.error = originalError;
198
- console.warn = originalWarn;
199
- }
200
- }
201
- else {
202
- // In regular mode, filter verbose output but keep essential progress
203
- const originalLog = console.log;
204
- const originalError = console.error;
205
- const originalWarn = console.warn;
206
- const keepPatterns = [
207
- /^🔍 Scanning/,
208
- /^🔨 Analyzing/,
209
- /^📊 Building/,
210
- /^📦 Generating/,
211
- /^🔍 Validating/,
212
- /^✅ Validation/,
213
- /^📝 Writing/,
214
- /^❌/,
215
- ];
216
- const skipPatterns = [
217
- /^ Found \d+ files$/,
218
- /^ Analyzed \d+ components$/,
219
- /^📋 Using profile:/,
220
- /^ ✓ .* \(.* bundles\)$/,
221
- /^📊 Summary:/,
222
- /^ Total components:/,
223
- /^ Root components:/,
224
- /^ Leaf components:/,
225
- /^ Bundles generated:/,
226
- /^ Total nodes in context:/,
227
- /^ Total edges:/,
228
- /^ Missing dependencies:/,
229
- /^📏 Token Estimates/,
230
- /^ GPT-4o-mini:/,
231
- /^ Claude:/,
232
- /^📊 Mode Comparison:/,
233
- /^ none:/,
234
- /^ header:/,
235
- /^ full:/,
236
- /^⏱ Completed in/,
237
- /^✅ \d+ context files written successfully$/,
238
- /^ Writing context files for \d+ folders\.\.\.$/,
239
- /^📝 Writing main context index\.\.\.$/,
240
- /^ ✓ .*context_main\.json/,
241
- ];
242
- console.log = (...args) => {
243
- const message = args.join(' ');
244
- // Keep essential progress, skip verbose details
245
- const shouldKeep = keepPatterns.some(pattern => pattern.test(message));
246
- const shouldSkip = skipPatterns.some(pattern => pattern.test(message));
247
- if (shouldKeep && !shouldSkip) {
248
- originalLog(...args);
249
- }
250
- };
251
- console.error = originalError;
252
- console.warn = originalWarn;
253
- try {
254
- return await fn();
255
- }
256
- finally {
257
- console.log = originalLog;
258
- console.error = originalError;
259
- console.warn = originalWarn;
260
- }
261
- }
262
- }
263
- async function handleCompare(args) {
264
- if (args[0] === '--help' || args[0] === '-h') {
265
- printFoxIcon();
266
- printCompareHelp();
267
- process.exit(0);
268
- }
269
- const stats = args.includes('--stats');
270
- const approve = args.includes('--approve');
271
- const cleanOrphaned = args.includes('--clean-orphaned');
272
- const quiet = args.includes('--quiet') || args.includes('-q');
273
- // Filter out flag arguments to get positional args (including -q)
274
- const positionalArgs = args.filter(arg => !arg.startsWith('--') && arg !== '-q');
275
- // Auto-mode: no files specified - use multi-file comparison with context_main.json
276
- if (positionalArgs.length === 0) {
277
- const { tmpdir } = await import('node:os');
278
- const { join, dirname } = await import('node:path');
279
- const { unlink, copyFile, rm, mkdir } = await import('node:fs/promises');
280
- const { existsSync } = await import('node:fs');
281
- // Check if context_main.json exists
282
- if (!existsSync('context_main.json')) {
283
- console.error('❌ context_main.json not found. Run "stamp context" first to generate context files.');
284
- process.exit(1);
285
- }
286
- if (!quiet) {
287
- console.log('Auto-compare mode');
288
- }
289
- // Create temp directory for new context generation
290
- const tempDir = join(tmpdir(), `context-compare-${Date.now()}`);
291
- await mkdir(tempDir, { recursive: true });
292
- if (!quiet) {
293
- console.log('🔄 Generating fresh context...');
294
- }
295
- // Generate fresh context to temp directory
296
- const contextOptions = {
297
- depth: 1,
298
- includeCode: 'header',
299
- format: 'json',
300
- out: tempDir,
301
- hashLock: false,
302
- strict: false,
303
- allowMissing: true,
304
- maxNodes: 100,
305
- profile: 'llm-chat',
306
- predictBehavior: false,
307
- dryRun: false,
308
- stats: false,
309
- strictMissing: false,
310
- compareModes: false,
311
- };
312
- try {
313
- await withSuppressedOutput(() => contextCommand(contextOptions), quiet);
314
- // Multi-file compare using context_main.json indices
315
- if (!quiet) {
316
- console.log('🔍 Comparing all context files...\n');
317
- }
318
- const multiCompareOptions = {
319
- oldIndexFile: 'context_main.json',
320
- newIndexFile: join(tempDir, 'context_main.json'),
321
- stats,
322
- approve,
323
- autoCleanOrphaned: cleanOrphaned,
324
- quiet,
325
- };
326
- const result = await multiFileCompare(multiCompareOptions);
327
- displayMultiFileCompareResult(result, stats, quiet);
328
- // Handle drift approval
329
- if (result.status === 'DRIFT') {
330
- let shouldUpdate = false;
331
- if (approve) {
332
- // --approve flag: non-interactive, deterministic
333
- shouldUpdate = true;
334
- console.log('🔄 --approve flag set, updating all context files...');
335
- }
336
- else if (isTTY()) {
337
- // Interactive prompt (local dev convenience)
338
- shouldUpdate = await promptYesNo('Update all context files? (y/N) ');
339
- }
340
- if (shouldUpdate) {
341
- // Copy all new context files to current directory
342
- const { readFile } = await import('node:fs/promises');
343
- const newIndexContent = await readFile(join(tempDir, 'context_main.json'), 'utf8');
344
- const newIndex = JSON.parse(newIndexContent);
345
- let copiedFiles = 0;
346
- for (const folder of newIndex.folders) {
347
- const srcPath = join(tempDir, folder.contextFile);
348
- const destPath = folder.contextFile;
349
- // Create parent directory if needed
350
- await mkdir(dirname(destPath), { recursive: true });
351
- await copyFile(srcPath, destPath);
352
- copiedFiles++;
353
- if (!quiet) {
354
- console.log(` ✓ Updated ${destPath}`);
355
- }
356
- }
357
- // Copy context_main.json
358
- await copyFile(join(tempDir, 'context_main.json'), 'context_main.json');
359
- if (!quiet) {
360
- console.log(` ✓ Updated context_main.json`);
361
- }
362
- // Clean up orphaned files if requested
363
- if (cleanOrphaned && result.orphanedFiles && result.orphanedFiles.length > 0) {
364
- if (!quiet) {
365
- console.log('\n🗑️ Cleaning up orphaned files...');
366
- }
367
- const deletedCount = await cleanOrphanedFiles(result.orphanedFiles, '.', quiet);
368
- if (!quiet) {
369
- console.log(` ✓ Deleted ${deletedCount} orphaned file(s)`);
370
- }
371
- }
372
- if (!quiet) {
373
- console.log(`\n✅ ${copiedFiles + 1} context files updated successfully`);
374
- }
375
- // Clean up temp directory
376
- await rm(tempDir, { recursive: true, force: true });
377
- process.exit(0); // Success: drift approved and updated
378
- }
379
- else {
380
- // Clean up temp directory
381
- await rm(tempDir, { recursive: true, force: true });
382
- if (isTTY() && !approve) {
383
- console.log('❌ Update declined\n');
384
- }
385
- process.exit(1); // Drift detected but not approved
386
- }
387
- }
388
- else {
389
- // No drift - clean up and exit success
390
- await rm(tempDir, { recursive: true, force: true });
391
- process.exit(0);
392
- }
393
- }
394
- catch (error) {
395
- // Try to clean up temp directory even on error
396
- try {
397
- await rm(tempDir, { recursive: true, force: true });
398
- }
399
- catch { }
400
- console.error('❌ Compare failed:', error.message);
401
- process.exit(1);
402
- }
69
+ // Check for 'style' subcommand (stamp context style)
70
+ if (contextArgs[0] === 'style') {
71
+ await handleStyle(contextArgs.slice(1));
403
72
  return;
404
73
  }
405
- // Manual mode: explicit files provided
406
- if (positionalArgs.length < 2) {
407
- printFoxIcon();
408
- printCompareHelp();
409
- process.exit(1);
410
- }
411
- const oldFile = positionalArgs[0];
412
- const newFile = positionalArgs[1];
413
- // Detect if we're comparing context_main.json files (multi-file mode)
414
- const isMultiFileMode = oldFile.endsWith('context_main.json') || oldFile.endsWith('context_main.json');
415
- if (isMultiFileMode) {
416
- // Multi-file comparison mode
417
- const multiCompareOptions = {
418
- oldIndexFile: oldFile,
419
- newIndexFile: newFile,
420
- stats,
421
- approve,
422
- autoCleanOrphaned: cleanOrphaned,
423
- quiet,
424
- };
425
- try {
426
- const result = await multiFileCompare(multiCompareOptions);
427
- displayMultiFileCompareResult(result, stats, quiet);
428
- // Handle drift approval in manual mode
429
- if (result.status === 'DRIFT') {
430
- let shouldUpdate = false;
431
- if (approve) {
432
- // --approve flag: non-interactive, deterministic
433
- shouldUpdate = true;
434
- if (!quiet) {
435
- console.log('🔄 --approve flag set, updating all context files...');
436
- }
437
- }
438
- else if (isTTY()) {
439
- // Interactive prompt (local dev convenience)
440
- shouldUpdate = await promptYesNo('Update all context files? (y/N) ');
441
- }
442
- if (shouldUpdate) {
443
- // Copy all new context files
444
- const { readFile, copyFile, mkdir } = await import('node:fs/promises');
445
- const { dirname } = await import('node:path');
446
- const newIndexContent = await readFile(newFile, 'utf8');
447
- const newIndex = JSON.parse(newIndexContent);
448
- const baseDir = dirname(oldFile);
449
- let copiedFiles = 0;
450
- for (const folder of newIndex.folders) {
451
- const { join } = await import('node:path');
452
- const srcPath = join(dirname(newFile), folder.contextFile);
453
- const destPath = join(baseDir, folder.contextFile);
454
- // Create parent directory if needed
455
- await mkdir(dirname(destPath), { recursive: true });
456
- await copyFile(srcPath, destPath);
457
- copiedFiles++;
458
- if (!quiet) {
459
- console.log(` ✓ Updated ${folder.contextFile}`);
460
- }
461
- }
462
- // Copy context_main.json
463
- await copyFile(newFile, oldFile);
464
- if (!quiet) {
465
- console.log(` ✓ Updated ${oldFile}`);
466
- }
467
- // Clean up orphaned files if requested
468
- if (cleanOrphaned && result.orphanedFiles && result.orphanedFiles.length > 0) {
469
- if (!quiet) {
470
- console.log('\n🗑️ Cleaning up orphaned files...');
471
- }
472
- const deletedCount = await cleanOrphanedFiles(result.orphanedFiles, baseDir, quiet);
473
- if (!quiet) {
474
- console.log(` ✓ Deleted ${deletedCount} orphaned file(s)`);
475
- }
476
- }
477
- if (!quiet) {
478
- console.log(`\n✅ ${copiedFiles + 1} context files updated successfully`);
479
- }
480
- process.exit(0); // Success: drift approved and updated
481
- }
482
- else {
483
- if (isTTY() && !approve) {
484
- console.log('❌ Update declined\n');
485
- }
486
- process.exit(1); // Drift detected but not approved
487
- }
488
- }
489
- else {
490
- // No drift
491
- process.exit(0);
492
- }
493
- }
494
- catch (error) {
495
- console.error('❌ Compare failed:', error.message);
496
- process.exit(1);
497
- }
498
- }
499
- else {
500
- // Single-file comparison mode (backward compatible)
501
- const compareOptions = {
502
- oldFile,
503
- newFile,
504
- stats,
505
- approve,
506
- quiet,
507
- };
508
- try {
509
- const result = await compareCommand(compareOptions);
510
- // Handle drift approval in manual mode
511
- if (result.status === 'DRIFT') {
512
- let shouldUpdate = false;
513
- if (approve) {
514
- // --approve flag: non-interactive, deterministic
515
- shouldUpdate = true;
516
- if (!quiet) {
517
- console.log(`🔄 --approve flag set, updating ${oldFile}...`);
518
- }
519
- }
520
- else if (isTTY()) {
521
- // Interactive prompt (local dev convenience)
522
- shouldUpdate = await promptYesNo(`Update ${oldFile} with ${newFile}? (y/N) `);
523
- }
524
- if (shouldUpdate) {
525
- const { copyFile } = await import('node:fs/promises');
526
- await copyFile(newFile, oldFile);
527
- if (!quiet) {
528
- console.log(`✅ ${oldFile} updated successfully\n`);
529
- }
530
- process.exit(0); // Success: drift approved and updated
531
- }
532
- else {
533
- if (isTTY() && !approve) {
534
- console.log('❌ Update declined\n');
535
- }
536
- process.exit(1); // Drift detected but not approved
537
- }
538
- }
539
- else {
540
- // No drift
541
- process.exit(0);
542
- }
543
- }
544
- catch (error) {
545
- console.error('❌ Compare failed:', error.message);
546
- process.exit(1);
547
- }
548
- }
549
- }
550
- async function handleGenerate(args) {
551
- if (args.includes('--help') || args.includes('-h')) {
552
- printFoxIcon();
553
- printGenerateHelp();
554
- process.exit(0);
555
- }
556
- const options = {
557
- depth: 1,
558
- includeCode: 'header',
559
- format: 'json',
560
- out: 'context.json',
561
- hashLock: false,
562
- strict: false,
563
- allowMissing: true,
564
- maxNodes: 100,
565
- profile: 'llm-chat',
566
- predictBehavior: false,
567
- dryRun: false,
568
- stats: false,
569
- strictMissing: false,
570
- compareModes: false,
571
- skipGitignore: false,
572
- quiet: false,
573
- };
574
- // Parse command line arguments
575
- for (let i = 0; i < args.length; i++) {
576
- const arg = args[i];
577
- if (arg.startsWith('--') || arg.startsWith('-')) {
578
- const key = arg.replace(/^--?/, '');
579
- const value = args[i + 1];
580
- switch (key) {
581
- case 'depth':
582
- case 'd':
583
- options.depth = parseInt(value, 10);
584
- i++;
585
- break;
586
- case 'include-code':
587
- case 'c':
588
- options.includeCode = value;
589
- i++;
590
- break;
591
- case 'format':
592
- case 'f':
593
- options.format = value;
594
- i++;
595
- break;
596
- case 'out':
597
- case 'o':
598
- options.out = value;
599
- i++;
600
- break;
601
- case 'max-nodes':
602
- case 'm':
603
- options.maxNodes = parseInt(value, 10);
604
- i++;
605
- break;
606
- case 'profile':
607
- options.profile = value;
608
- i++;
609
- break;
610
- case 'strict':
611
- case 's':
612
- options.strict = true;
613
- break;
614
- case 'predict-behavior':
615
- options.predictBehavior = true;
616
- break;
617
- case 'dry-run':
618
- options.dryRun = true;
619
- break;
620
- case 'stats':
621
- options.stats = true;
622
- break;
623
- case 'strict-missing':
624
- options.strictMissing = true;
625
- break;
626
- case 'compare-modes':
627
- options.compareModes = true;
628
- break;
629
- case 'skip-gitignore':
630
- options.skipGitignore = true;
631
- break;
632
- case 'quiet':
633
- case 'q':
634
- options.quiet = true;
635
- break;
636
- default:
637
- console.error(`❌ Unknown option: ${arg}`);
638
- process.exit(1);
639
- }
640
- }
641
- else {
642
- // First non-option argument is the entry path
643
- if (!options.entry) {
644
- options.entry = arg;
645
- }
646
- }
647
- }
648
- try {
649
- await contextCommand(options);
650
- }
651
- catch (error) {
652
- console.error('❌ Context generation failed:', error.message);
653
- console.error(error.stack);
654
- process.exit(1);
655
- }
656
- }
657
- function printMainHelp() {
658
- console.log(`
659
- ╭─────────────────────────────────────────────────╮
660
- │ Stamp - LogicStamp Context CLI │
661
- │ AI-ready context generation for React/TS │
662
- ╰─────────────────────────────────────────────────╯
663
-
664
- USAGE:
665
- stamp init [path] Initialize LogicStamp in a project
666
- stamp context [path] [options] Generate context
667
- stamp context validate [file] Validate context file
668
- stamp context compare [options] Detect drift (auto-generates fresh context)
669
- stamp context clean [path] [options] Remove all generated context artifacts
670
-
671
- OPTIONS:
672
- -v, --version Show version number
673
- -h, --help Show this help
674
-
675
- EXAMPLES:
676
- stamp init
677
- Set up LogicStamp in current directory (creates/updates .gitignore)
678
-
679
- stamp context
680
- Generate context.json for current directory
681
-
682
- stamp context validate
683
- Validate context.json in current directory
684
-
685
- stamp context compare
686
- Auto-detect drift by comparing with fresh context
687
-
688
- stamp context clean
689
- Show what would be removed (dry run)
690
-
691
- stamp context clean --all --yes
692
- Actually delete all context artifacts
693
-
694
- For detailed help on a specific command, run:
695
- stamp init --help
696
- stamp context --help
697
- stamp context validate --help
698
- stamp context compare --help
699
- stamp context clean --help
700
- `);
701
- }
702
- function printGenerateHelp() {
703
- console.log(`
704
- ╭─────────────────────────────────────────────────╮
705
- │ Stamp Context - Generate AI Context │
706
- │ Scan and analyze React/TS codebase │
707
- ╰─────────────────────────────────────────────────╯
708
-
709
- USAGE:
710
- stamp context [path] [options]
711
-
712
- ARGUMENTS:
713
- [path] Directory to scan (default: current)
714
-
715
- OPTIONS:
716
- --depth, -d <n> Dependency depth (default: 1)
717
- --include-code, -c <mode> Code inclusion: none|header|full (default: header)
718
- --format, -f <format> Output format: json|pretty|ndjson (default: json)
719
- --out, -o <file> Output file (default: context.json)
720
- --max-nodes, -m <n> Max nodes per bundle (default: 100)
721
- --profile <profile> Preset profile: llm-safe|llm-chat|ci-strict
722
- --strict, -s Fail on missing dependencies
723
- --strict-missing Exit with error if any missing dependencies
724
- --predict-behavior Include behavior predictions
725
- --dry-run Skip writing output
726
- --stats Emit JSON stats
727
- --compare-modes Show detailed mode comparison table
728
- --skip-gitignore Skip .gitignore setup (never prompt or modify)
729
- --quiet, -q Suppress verbose output (show only errors)
730
- -h, --help Show this help
731
-
732
- EXAMPLES:
733
- stamp context
734
- Generate context for current directory
735
-
736
- stamp context ./src --depth 2
737
- Deep scan of src directory
738
-
739
- stamp context --include-code none --out api.json
740
- Generate API documentation only
741
-
742
- stamp context --compare-modes
743
- Show token cost comparison across modes
744
-
745
- stamp context --quiet
746
- Suppress verbose output (show only errors)
747
- `);
748
- }
749
- function printValidateHelp() {
750
- console.log(`
751
- ╭─────────────────────────────────────────────────╮
752
- │ Stamp Context Validate - Bundle Validator │
753
- │ Validate context.json structure and schema │
754
- ╰─────────────────────────────────────────────────╯
755
-
756
- USAGE:
757
- stamp context validate [file]
758
-
759
- ARGUMENTS:
760
- [file] Path to context.json (default: context.json)
761
-
762
- OPTIONS:
763
- --quiet Show only errors (suppress summaries and valid folders)
764
- -h, --help Show this help
765
-
766
- EXAMPLES:
767
- stamp context validate
768
- Validate context.json in current directory
769
-
770
- stamp context validate docs/api-context.json
771
- Validate a specific context file
772
-
773
- stamp context validate --quiet
774
- Show only validation errors
775
-
776
- NOTES:
777
- • Validates bundle structure and schema compliance
778
- • Checks for required fields and hash formats
779
- • Exits with code 0 on success, 1 on failure
780
- `);
781
- }
782
- function printCompareHelp() {
783
- console.log(`
784
- ╭─────────────────────────────────────────────────╮
785
- │ Stamp Context Compare - Drift Detection │
786
- │ Compare context files for changes │
787
- ╰─────────────────────────────────────────────────╯
788
-
789
- USAGE:
790
- stamp context compare [options] Auto-compare all context files
791
- stamp context compare <old.json> <new.json> Compare two specific files
792
- stamp context compare <old_main.json> <new_main.json> Compare multi-file indices
793
-
794
- ARGUMENTS:
795
- <old.json> Path to old context file or context_main.json
796
- <new.json> Path to new context file or context_main.json
797
-
798
- OPTIONS:
799
- --approve Auto-approve updates (non-interactive, CI-safe)
800
- --clean-orphaned Auto-delete orphaned files with --approve
801
- --quiet Show only diffs (suppress summaries, PASS folders, and token analysis)
802
- -h, --help Show this help
803
-
804
- COMPARISON MODES:
805
- Auto-Mode (Multi-File):
806
- Compares ALL context files using context_main.json as index
807
- → Detects ADDED, ORPHANED, DRIFT, and PASS status per folder
808
- → Shows three-tier output: folder summary, component summary, details
809
-
810
- Single-File Mode:
811
- Compares two individual context.json files
812
- → Detects added/removed/changed components
813
-
814
- Multi-File Manual Mode:
815
- Auto-detects when comparing context_main.json files
816
- → Compares all referenced context files
817
-
818
- EXAMPLES:
819
- stamp context compare
820
- Auto-mode: generate fresh context, compare ALL files
821
- → Shows folder-level and component-level changes
822
- → Interactive: prompts Y/N to update if drift detected
823
- → CI: exits with code 1 if drift detected (no prompt)
824
-
825
- stamp context compare --approve
826
- Auto-approve and update ALL context files if drift (like jest -u)
827
-
828
- stamp context compare --approve --clean-orphaned
829
- Auto-approve updates and delete orphaned context files
830
-
831
- stamp context compare --stats
832
- Show per-folder token count deltas
833
-
834
- stamp context compare --quiet
835
- Show only diffs (suppress summaries, PASS folders, and status headers)
836
-
837
- stamp context compare old.json new.json
838
- Compare two specific context files
839
-
840
- stamp context compare old/context_main.json new/context_main.json
841
- Compare all context files between two directories
842
-
843
- stamp context compare || exit 1
844
- CI validation: fail build if drift detected
845
-
846
- EXIT CODES:
847
- 0 PASS - No drift OR drift approved and updated
848
- 1 DRIFT - Changes detected but not approved
849
-
850
- BEHAVIOR:
851
- • --approve: Non-interactive, deterministic, updates immediately if drift
852
- • Interactive (TTY): Prompts "Update all context files? (y/N)" if drift
853
- • CI (non-TTY): Never prompts, exits 1 if drift detected
854
- • --clean-orphaned: Requires --approve, deletes orphaned files automatically
855
- • --quiet: Shows only diffs - suppresses status headers (PASS), summaries, PASS folders, and token analysis
856
-
857
- DRIFT INDICATORS:
858
- ➕ ADDED FILE New folder with context file
859
- 🗑️ ORPHANED FILE Folder removed (context file still exists)
860
- ⚠️ DRIFT Folder has component changes
861
- ✅ PASS Folder unchanged
862
-
863
- NOTES:
864
- This matches Jest snapshot workflow:
865
- jest → prompts to update snapshots locally
866
- jest -u → updates snapshots without prompt
867
- CI → fails if snapshots don't match
868
- `);
869
- }
870
- function printCleanHelp() {
871
- console.log(`
872
- ╭─────────────────────────────────────────────────╮
873
- │ Stamp Context Clean - Remove Artifacts │
874
- │ Delete all generated context files │
875
- ╰─────────────────────────────────────────────────╯
876
-
877
- USAGE:
878
- stamp context clean [path] [options]
879
-
880
- ARGUMENTS:
881
- [path] Directory to clean (default: current)
882
-
883
- OPTIONS:
884
- --all Include all context files
885
- --yes Confirm deletion (required with --all)
886
- --quiet, -q Suppress verbose output (show only errors)
887
- -h, --help Show this help
888
-
889
- BEHAVIOR:
890
- • Default (dry run): Shows what would be removed
891
- • --all --yes: Actually deletes the files
892
- • Automatically includes .logicstamp/ directory if it exists
893
- • --quiet: Shows only ✓ on success, errors otherwise
894
-
895
- FILES REMOVED:
896
- • context_main.json Main index file
897
- • **/context.json All folder context files
898
- • .logicstamp/ Cache directory (if present)
899
-
900
- EXAMPLES:
901
- stamp context clean
902
- Show what would be removed (dry run)
903
-
904
- stamp context clean --all --yes
905
- Actually delete all context artifacts (includes .logicstamp/ if present)
906
-
907
- stamp context clean --all --yes --quiet
908
- Delete files silently (show only ✓)
909
-
910
- stamp context clean ./src --all --yes
911
- Clean context files in specific directory
912
-
913
- NOTES:
914
- • Safe by default - requires --all --yes to actually delete
915
- • Ignores node_modules, dist, build, .next directories
916
- • Exits with code 0 on success
917
- `);
918
- }
919
- function printInitHelp() {
920
- console.log(`
921
- ╭─────────────────────────────────────────────────╮
922
- │ Stamp Init - Initialize LogicStamp │
923
- │ Set up LogicStamp in your project │
924
- ╰─────────────────────────────────────────────────╯
925
-
926
- USAGE:
927
- stamp init [path] [options]
928
-
929
- ARGUMENTS:
930
- [path] Target directory (default: current)
931
-
932
- OPTIONS:
933
- --skip-gitignore Skip .gitignore setup
934
- -h, --help Show this help
935
-
936
- EXAMPLES:
937
- stamp init
938
- Set up LogicStamp in current directory
939
-
940
- stamp init ./my-project
941
- Set up LogicStamp in a specific directory
942
-
943
- stamp init --skip-gitignore
944
- Initialize without modifying .gitignore
945
-
946
- WHAT IT DOES:
947
- • Creates or updates .gitignore with LogicStamp patterns:
948
- - context.json
949
- - context_*.json
950
- - *.uif.json
951
- - logicstamp.manifest.json
952
- - .logicstamp/
953
-
954
- NOTES:
955
- • Safe to run multiple times (idempotent)
956
- • Won't duplicate patterns if they already exist
957
- • Creates .gitignore if it doesn't exist
958
- `);
74
+ // Default: generate context
75
+ await handleGenerate(contextArgs);
959
76
  }
960
77
  main();
961
78
  //# sourceMappingURL=stamp.js.map