logicstamp-context 0.1.1 → 0.2.1

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