@levu/snap 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 (138) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +33 -2
  3. package/dist/cli/cli-runner.d.ts +37 -0
  4. package/dist/cli/cli-runner.js +161 -0
  5. package/dist/cli-entry.js +6 -64
  6. package/dist/core/contracts/action-contract.d.ts +1 -3
  7. package/dist/core/contracts/tui-contract.d.ts +47 -0
  8. package/dist/core/contracts/tui-contract.js +1 -0
  9. package/dist/core/registry/action-registry.js +3 -1
  10. package/dist/dx/args/env.d.ts +6 -0
  11. package/dist/dx/args/env.js +15 -0
  12. package/dist/dx/args/index.d.ts +4 -0
  13. package/dist/dx/args/index.js +3 -0
  14. package/dist/dx/args/readers.d.ts +5 -0
  15. package/dist/dx/args/readers.js +36 -0
  16. package/dist/dx/args/types.d.ts +5 -0
  17. package/dist/dx/args/types.js +1 -0
  18. package/dist/dx/help/builder.d.ts +10 -0
  19. package/dist/dx/help/builder.js +11 -0
  20. package/dist/dx/help/index.d.ts +4 -0
  21. package/dist/dx/help/index.js +2 -0
  22. package/dist/dx/help/schema.d.ts +14 -0
  23. package/dist/dx/help/schema.js +33 -0
  24. package/dist/dx/runtime/action-result.d.ts +12 -0
  25. package/dist/dx/runtime/action-result.js +35 -0
  26. package/dist/dx/runtime/flow.d.ts +9 -0
  27. package/dist/dx/runtime/flow.js +19 -0
  28. package/dist/dx/runtime/index.d.ts +4 -0
  29. package/dist/dx/runtime/index.js +2 -0
  30. package/dist/dx/terminal/index.d.ts +3 -0
  31. package/dist/dx/terminal/index.js +3 -0
  32. package/dist/dx/terminal/intro-outro.d.ts +4 -0
  33. package/dist/dx/terminal/intro-outro.js +44 -0
  34. package/dist/dx/terminal/output.d.ts +19 -0
  35. package/dist/dx/terminal/output.js +59 -0
  36. package/dist/dx/tui/components.d.ts +6 -0
  37. package/dist/dx/tui/components.js +40 -0
  38. package/dist/dx/tui/flow.d.ts +2 -0
  39. package/dist/dx/tui/flow.js +14 -0
  40. package/dist/dx/tui/index.d.ts +16 -0
  41. package/dist/dx/tui/index.js +15 -0
  42. package/dist/dx/tui/no-result.d.ts +13 -0
  43. package/dist/dx/tui/no-result.js +18 -0
  44. package/dist/help/help-renderer.js +5 -1
  45. package/dist/help/hierarchy-resolver.js +1 -1
  46. package/dist/index.d.ts +16 -0
  47. package/dist/index.js +11 -0
  48. package/dist/runtime/dispatch.d.ts +2 -1
  49. package/dist/runtime/engine.d.ts +2 -1
  50. package/dist/runtime/engine.js +22 -1
  51. package/dist/runtime/mode-resolver.d.ts +3 -2
  52. package/dist/runtime/runtime-context.d.ts +8 -1
  53. package/dist/tui/component-adapters/autocomplete.d.ts +15 -0
  54. package/dist/tui/component-adapters/autocomplete.js +34 -0
  55. package/dist/tui/component-adapters/cancel.d.ts +6 -0
  56. package/dist/tui/component-adapters/cancel.js +20 -0
  57. package/dist/tui/component-adapters/confirm.d.ts +2 -0
  58. package/dist/tui/component-adapters/confirm.js +13 -1
  59. package/dist/tui/component-adapters/multiselect.d.ts +4 -0
  60. package/dist/tui/component-adapters/multiselect.js +23 -3
  61. package/dist/tui/component-adapters/note.d.ts +7 -0
  62. package/dist/tui/component-adapters/note.js +23 -0
  63. package/dist/tui/component-adapters/password.d.ts +7 -0
  64. package/dist/tui/component-adapters/password.js +24 -0
  65. package/dist/tui/component-adapters/progress.d.ts +7 -0
  66. package/dist/tui/component-adapters/progress.js +44 -0
  67. package/dist/tui/component-adapters/readline-utils.d.ts +1 -0
  68. package/dist/tui/component-adapters/readline-utils.js +2 -0
  69. package/dist/tui/component-adapters/select.d.ts +2 -0
  70. package/dist/tui/component-adapters/select.js +25 -3
  71. package/dist/tui/component-adapters/spinner.d.ts +10 -0
  72. package/dist/tui/component-adapters/spinner.js +48 -0
  73. package/dist/tui/component-adapters/tasks.d.ts +9 -0
  74. package/dist/tui/component-adapters/tasks.js +31 -0
  75. package/dist/tui/component-adapters/text.d.ts +2 -0
  76. package/dist/tui/component-adapters/text.js +21 -4
  77. package/dist/tui/custom/custom-prompt.d.ts +16 -0
  78. package/dist/tui/custom/custom-prompt.js +72 -0
  79. package/dist/tui/custom/index.d.ts +2 -0
  80. package/dist/tui/custom/index.js +1 -0
  81. package/dist/tui/prompt-toolkit.d.ts +15 -0
  82. package/dist/tui/prompt-toolkit.js +17 -0
  83. package/docs/component-reference.md +474 -0
  84. package/docs/getting-started.md +242 -0
  85. package/docs/integration-examples.md +677 -0
  86. package/docs/module-authoring-guide.md +105 -1
  87. package/docs/snap-args.md +323 -0
  88. package/docs/snap-help.md +372 -0
  89. package/docs/snap-runtime.md +394 -0
  90. package/docs/snap-terminal.md +410 -0
  91. package/docs/snap-tui.md +529 -0
  92. package/package.json +15 -2
  93. package/.github/workflows/ci.yml +0 -26
  94. package/plans/260209-1547-hub-dual-runtime-framework/phase-01-foundation-and-contracts.md +0 -71
  95. package/plans/260209-1547-hub-dual-runtime-framework/phase-02-runtime-and-state-machine.md +0 -76
  96. package/plans/260209-1547-hub-dual-runtime-framework/phase-03-tui-components-and-policies.md +0 -71
  97. package/plans/260209-1547-hub-dual-runtime-framework/phase-04-help-system-and-ai-readability.md +0 -69
  98. package/plans/260209-1547-hub-dual-runtime-framework/phase-05-testing-and-quality-gates.md +0 -79
  99. package/plans/260209-1547-hub-dual-runtime-framework/phase-06-sample-modules-and-adoption.md +0 -75
  100. package/plans/260209-1547-hub-dual-runtime-framework/plan.md +0 -105
  101. package/plans/260209-1547-hub-dual-runtime-framework/reports/planner-report.md +0 -27
  102. package/plans/260209-1547-hub-dual-runtime-framework/research/researcher-01-report.md +0 -166
  103. package/plans/260209-1547-hub-dual-runtime-framework/research/researcher-02-report.md +0 -87
  104. package/plans/260209-1547-hub-dual-runtime-framework/scout/scout-01-report.md +0 -24
  105. package/src/cli/help-command.ts +0 -1
  106. package/src/cli-entry.ts +0 -83
  107. package/src/core/contracts/action-contract.ts +0 -30
  108. package/src/core/contracts/help-contract.ts +0 -20
  109. package/src/core/contracts/module-contract.ts +0 -7
  110. package/src/core/errors/framework-errors.ts +0 -26
  111. package/src/core/registry/action-registry.ts +0 -94
  112. package/src/help/help-command.ts +0 -32
  113. package/src/help/help-model.ts +0 -10
  114. package/src/help/help-renderer.ts +0 -21
  115. package/src/help/hierarchy-resolver.ts +0 -54
  116. package/src/index.ts +0 -10
  117. package/src/modules/sample-content/module.ts +0 -66
  118. package/src/modules/sample-system/module.ts +0 -74
  119. package/src/runtime/dispatch.ts +0 -64
  120. package/src/runtime/engine.ts +0 -59
  121. package/src/runtime/mode-resolver.ts +0 -18
  122. package/src/runtime/resume-store.ts +0 -53
  123. package/src/runtime/runtime-context.ts +0 -10
  124. package/src/runtime/state-machine.ts +0 -77
  125. package/src/tui/accessibility-footer.ts +0 -11
  126. package/src/tui/component-adapters/confirm.ts +0 -8
  127. package/src/tui/component-adapters/group.ts +0 -12
  128. package/src/tui/component-adapters/multiselect.ts +0 -22
  129. package/src/tui/component-adapters/select.ts +0 -18
  130. package/src/tui/component-adapters/text.ts +0 -13
  131. package/src/tui/interrupt-handlers.ts +0 -15
  132. package/tests/e2e/cli-smoke.e2e.test.ts +0 -19
  133. package/tests/integration/runtime-dispatch.integration.test.ts +0 -23
  134. package/tests/transcript/help.transcript.test.ts +0 -20
  135. package/tests/unit/state-machine.test.ts +0 -22
  136. package/tsconfig.build.json +0 -9
  137. package/tsconfig.json +0 -17
  138. package/vitest.config.ts +0 -8
@@ -0,0 +1,410 @@
1
+ # SnapTerminal - Terminal Output Helpers
2
+
3
+ `SnapTerminal` provides a simple, testable interface for writing to the terminal.
4
+
5
+ ## Import
6
+
7
+ ```typescript
8
+ import * as SnapTerminal from 'snap-framework';
9
+ ```
10
+
11
+ ## API Reference
12
+
13
+ ### `createTerminalOutput(stdout?, stderr?)`
14
+
15
+ Creates a terminal output interface with write methods.
16
+
17
+ ```typescript
18
+ const terminal = SnapTerminal.createTerminalOutput();
19
+ // Or with custom streams:
20
+ const customTerminal = SnapTerminal.createTerminalOutput(customStdout, customStderr);
21
+ ```
22
+
23
+ **Default:** Uses `process.stdout` and `process.stderr`
24
+
25
+ ### TerminalOutput Interface
26
+
27
+ ```typescript
28
+ interface TerminalOutput {
29
+ line(message: string): void;
30
+ lines(messages: readonly string[]): void;
31
+ error(message: string): void;
32
+ }
33
+ ```
34
+
35
+ #### `line(message)`
36
+
37
+ Writes a single line to stdout.
38
+
39
+ ```typescript
40
+ terminal.line('Hello, world!');
41
+ // Output: Hello, world!
42
+ ```
43
+
44
+ #### `lines(messages)`
45
+
46
+ Writes multiple lines to stdout.
47
+
48
+ ```typescript
49
+ terminal.lines([
50
+ 'Line 1',
51
+ 'Line 2',
52
+ 'Line 3'
53
+ ]);
54
+ // Output:
55
+ // Line 1
56
+ // Line 2
57
+ // Line 3
58
+ ```
59
+
60
+ #### `error(message)`
61
+
62
+ Writes an error message to stderr.
63
+
64
+ ```typescript
65
+ terminal.error('Something went wrong!');
66
+ // Output (stderr): Something went wrong!
67
+ ```
68
+
69
+ ## Usage in Actions
70
+
71
+ The `RuntimeContext` includes a `terminal` property:
72
+
73
+ ```typescript
74
+ run: async (context) => {
75
+ // Access terminal via context
76
+ context.terminal.line('Starting operation...');
77
+ context.terminal.lines(['Progress:', ' - Step 1 complete', ' - Step 2 complete']);
78
+
79
+ // Errors
80
+ context.terminal.error('Operation failed!');
81
+
82
+ return { ok: true, mode: context.mode, exitCode: ExitCode.SUCCESS, data: {} };
83
+ }
84
+ ```
85
+
86
+ ## Complete Examples
87
+
88
+ ### Basic Output
89
+
90
+ ```typescript
91
+ import type { ModuleContract } from 'snap-framework';
92
+ import { ExitCode } from 'snap-framework';
93
+
94
+ const echoModule: ModuleContract = {
95
+ moduleId: 'echo',
96
+ description: 'Echo messages',
97
+ actions: [
98
+ {
99
+ actionId: 'say',
100
+ description: 'Echo a message',
101
+ tui: { steps: ['collect-message'] },
102
+ commandline: { requiredArgs: ['message'] },
103
+ help: {
104
+ summary: 'Echo a message to the terminal.',
105
+ args: [{ name: 'message', required: true, description: 'Message to echo' }]
106
+ },
107
+ run: async (context) => {
108
+ const message = String(context.args.message ?? '');
109
+
110
+ // Output the message
111
+ context.terminal.line(message);
112
+
113
+ return {
114
+ ok: true,
115
+ mode: context.mode,
116
+ exitCode: ExitCode.SUCCESS,
117
+ data: { echoed: message }
118
+ };
119
+ }
120
+ }
121
+ ]
122
+ };
123
+ ```
124
+
125
+ ### Progress Updates
126
+
127
+ ```typescript
128
+ const deployModule: ModuleContract = {
129
+ moduleId: 'deploy',
130
+ description: 'Deploy with progress',
131
+ actions: [
132
+ {
133
+ actionId: 'start',
134
+ description: 'Start deployment',
135
+ tui: { steps: ['collect-input'] },
136
+ commandline: { requiredArgs: ['environment'] },
137
+ help: {
138
+ summary: 'Deploy to environment with progress updates.',
139
+ args: [{ name: 'environment', required: true, description: 'Target env' }]
140
+ },
141
+ run: async (context) => {
142
+ const environment = String(context.args.environment ?? '');
143
+
144
+ context.terminal.line(`Deploying to ${environment}...`);
145
+ context.terminal.line('');
146
+
147
+ // Simulate deployment steps
148
+ const steps = [
149
+ 'Building application...',
150
+ 'Running tests...',
151
+ 'Uploading assets...',
152
+ 'Running migrations...',
153
+ 'Starting services...'
154
+ ];
155
+
156
+ for (const step of steps) {
157
+ context.terminal.line(` ✓ ${step}`);
158
+ // Simulate work
159
+ await new Promise(resolve => setTimeout(resolve, 500));
160
+ }
161
+
162
+ context.terminal.line('');
163
+ context.terminal.line('Deployment complete!');
164
+
165
+ return {
166
+ ok: true,
167
+ mode: context.mode,
168
+ exitCode: ExitCode.SUCCESS,
169
+ data: { environment, status: 'deployed' }
170
+ };
171
+ }
172
+ }
173
+ ]
174
+ };
175
+ ```
176
+
177
+ ### Error Reporting
178
+
179
+ ```typescript
180
+ const validationModule: ModuleContract = {
181
+ moduleId: 'validate',
182
+ description: 'Configuration validation',
183
+ actions: [
184
+ {
185
+ actionId: 'check',
186
+ description: 'Validate configuration',
187
+ tui: { steps: ['collect-config'] },
188
+ commandline: { requiredArgs: ['config'] },
189
+ help: {
190
+ summary: 'Validate configuration file.',
191
+ args: [{ name: 'config', required: true, description: 'Config file path' }]
192
+ },
193
+ run: async (context) => {
194
+ const configPath = String(context.args.config ?? '');
195
+
196
+ context.terminal.line(`Validating ${configPath}...`);
197
+
198
+ try {
199
+ const config = await loadConfig(configPath);
200
+ const errors = validateConfig(config);
201
+
202
+ if (errors.length > 0) {
203
+ context.terminal.error('Configuration validation failed:');
204
+ context.terminal.lines(errors.map(e => ` ✗ ${e}`));
205
+
206
+ return {
207
+ ok: false,
208
+ mode: context.mode,
209
+ exitCode: ExitCode.VALIDATION_ERROR,
210
+ errorMessage: errors.join('; ')
211
+ };
212
+ }
213
+
214
+ context.terminal.line('Configuration is valid!');
215
+ context.terminal.lines([
216
+ ` Environment: ${config.environment}`,
217
+ ` Region: ${config.region}`,
218
+ ` Log level: ${config.logLevel}`
219
+ ]);
220
+
221
+ return {
222
+ ok: true,
223
+ mode: context.mode,
224
+ exitCode: ExitCode.SUCCESS,
225
+ data: { valid: true, config }
226
+ };
227
+ } catch (error) {
228
+ context.terminal.error(`Failed to load config: ${error}`);
229
+
230
+ return {
231
+ ok: false,
232
+ mode: context.mode,
233
+ exitCode: ExitCode.RUNTIME_ERROR,
234
+ errorMessage: 'Failed to load configuration'
235
+ };
236
+ }
237
+ }
238
+ }
239
+ ]
240
+ };
241
+ ```
242
+
243
+ ### Table Output
244
+
245
+ ```typescript
246
+ const listModule: ModuleContract = {
247
+ moduleId: 'list',
248
+ description: 'List resources',
249
+ actions: [
250
+ {
251
+ actionId: 'users',
252
+ description: 'List all users',
253
+ tui: { steps: [] },
254
+ commandline: { requiredArgs: [] },
255
+ help: {
256
+ summary: 'List all users in the system.'
257
+ },
258
+ run: async (context) => {
259
+ const users = await fetchUsers();
260
+
261
+ context.terminal.line('Users:');
262
+ context.terminal.line('');
263
+
264
+ if (users.length === 0) {
265
+ context.terminal.line(' No users found.');
266
+ } else {
267
+ // Simple table formatting
268
+ const maxNameLength = Math.max(...users.map(u => u.name.length));
269
+ const maxEmailLength = Math.max(...users.map(u => u.email.length));
270
+
271
+ context.terminal.line(
272
+ ` ${'Name'.padEnd(maxNameLength)} ${'Email'.padEnd(maxEmailLength)} Role`
273
+ );
274
+ context.terminal.line(
275
+ ` ${'─'.repeat(maxNameLength)} ${'─'.repeat(maxEmailLength)} ──────`
276
+ );
277
+
278
+ for (const user of users) {
279
+ context.terminal.line(
280
+ ` ${user.name.padEnd(maxNameLength)} ${user.email.padEnd(maxEmailLength)} ${user.role}`
281
+ );
282
+ }
283
+
284
+ context.terminal.line('');
285
+ context.terminal.line(` Total: ${users.length} user(s)`);
286
+ }
287
+
288
+ return {
289
+ ok: true,
290
+ mode: context.mode,
291
+ exitCode: ExitCode.SUCCESS,
292
+ data: { count: users.length, users }
293
+ };
294
+ }
295
+ }
296
+ ]
297
+ };
298
+ ```
299
+
300
+ ## Testing with Terminal Output
301
+
302
+ `createTerminalOutput` accepts custom Writable streams for testing:
303
+
304
+ ```typescript
305
+ import { PassThrough } from 'node:stream';
306
+ import * as SnapTerminal from 'snap-framework';
307
+
308
+ describe('my action', () => {
309
+ it('should output messages', async () => {
310
+ // Create in-memory streams
311
+ const stdout = new PassThrough();
312
+ const stderr = new PassThrough();
313
+ const outputChunks: string[] = [];
314
+
315
+ stdout.on('data', (chunk) => outputChunks.push(chunk.toString()));
316
+
317
+ const terminal = SnapTerminal.createTerminalOutput(stdout, stderr);
318
+
319
+ // Use in your action or test
320
+ terminal.line('Hello, world!');
321
+ terminal.error('Error!');
322
+
323
+ // Verify output
324
+ expect(outputChunks).toContain('Hello, world!\n');
325
+ });
326
+ });
327
+ ```
328
+
329
+ ## Mocking for Unit Tests
330
+
331
+ Simple mock for tests:
332
+
333
+ ```typescript
334
+ const mockTerminal = {
335
+ line: vi.fn(),
336
+ lines: vi.fn(),
337
+ error: vi.fn()
338
+ };
339
+
340
+ // Use in test
341
+ const context = {
342
+ // ... other properties
343
+ terminal: mockTerminal
344
+ };
345
+
346
+ await action.run(context);
347
+
348
+ expect(mockTerminal.line).toHaveBeenCalledWith('Starting...');
349
+ expect(mockTerminal.error).not.toHaveBeenCalled();
350
+ ```
351
+
352
+ ## Best Practices
353
+
354
+ 1. **Use line() for single messages** - Most common case
355
+ 2. **Use lines() for bulk output** - More efficient than multiple line() calls
356
+ 3. **Use error() for errors only** - Goes to stderr, not stdout
357
+ 4. **Add blank lines for readability** - `context.terminal.line('')` for spacing
358
+ 5. **Keep output structured** - Use consistent formatting
359
+ 6. **Consider non-TTY environments** - Avoid complex ANSI codes without detection
360
+ 7. **Test output separately** - Use custom streams in tests
361
+
362
+ ## Output Patterns
363
+
364
+ ### Status Messages
365
+
366
+ ```typescript
367
+ context.terminal.line('✓ Operation completed');
368
+ context.terminal.line('✗ Operation failed');
369
+ context.terminal.line('→ Processing...');
370
+ ```
371
+
372
+ ### Section Headers
373
+
374
+ ```typescript
375
+ context.terminal.line('');
376
+ context.terminal.line('=== Deployment Summary ===');
377
+ context.terminal.line('');
378
+ ```
379
+
380
+ ### Key-Value Pairs
381
+
382
+ ```typescript
383
+ context.terminal.line(`Environment: ${env}`);
384
+ context.terminal.line(`Region: ${region}`);
385
+ context.terminal.line(`Status: ${status}`);
386
+ ```
387
+
388
+ ### Lists
389
+
390
+ ```typescript
391
+ context.terminal.line('Changes:');
392
+ context.terminal.lines([
393
+ ' - Added user authentication',
394
+ ' - Updated dependencies',
395
+ ' - Fixed login bug'
396
+ ]);
397
+ ```
398
+
399
+ ## Integration with Context
400
+
401
+ The `RuntimeContext` always has a `terminal` property:
402
+
403
+ ```typescript
404
+ interface RuntimeContext {
405
+ terminal: TerminalOutput;
406
+ // ... other properties
407
+ }
408
+ ```
409
+
410
+ No need to create your own in actions - just use `context.terminal`.