procedure-cli 1.0.2 → 1.0.4

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.
@@ -0,0 +1,375 @@
1
+ # Technical Specifications
2
+
3
+ Technical stack, dependencies, configuration, and architecture for building CLI applications with React + Ink.js.
4
+
5
+ Reference source: [Procedure CLI](https://github.com/ducban/procedure-cli.git)
6
+
7
+ ---
8
+
9
+ ## Dev Stack Summary
10
+
11
+ | Layer | Technology | Version |
12
+ |-------|-----------|---------|
13
+ | **Runtime** | Node.js | 22+ (ES2022 target) |
14
+ | **Language** | TypeScript | ^5.8 (strict mode) |
15
+ | **UI Framework** | React + Ink.js | React ^19.1 + Ink ^6.0 |
16
+ | **UI Components** | @inkjs/ui | ^2.0 |
17
+ | **Database** | SQLite (better-sqlite3) | ^11.8 |
18
+ | **ID Generation** | nanoid | ^5.1 |
19
+ | **Module System** | ESM (`"type": "module"`) | Node16 resolution |
20
+ | **Build** | tsc (no bundler) | Native ES modules |
21
+ | **Dev Server** | tsx (watch mode) | ^4.19 |
22
+ | **Linting** | ESLint + @typescript-eslint | ^8.x |
23
+ | **Testing** | Node.js built-in test runner | `node --test` |
24
+
25
+ ---
26
+
27
+ ## Core Dependencies
28
+
29
+ ### Production
30
+
31
+ | Package | Version | Purpose |
32
+ |---------|---------|---------|
33
+ | `react` | ^19.1.0 | Component model for UI rendering |
34
+ | `ink` | ^6.0.0 | Terminal UI rendering engine — renders React components as terminal output |
35
+ | `@inkjs/ui` | ^2.0.0 | Pre-built Ink components (TextInput, Spinner, etc.) |
36
+ | `ink-spinner` | ^5.0.0 | Loading spinner animations |
37
+ | `better-sqlite3` | ^11.8.1 | Synchronous SQLite database driver (no async overhead) |
38
+ | `nanoid` | ^5.1.5 | Compact, URL-safe unique ID generation |
39
+
40
+ ### Development
41
+
42
+ | Package | Version | Purpose |
43
+ |---------|---------|---------|
44
+ | `typescript` | ^5.8.3 | TypeScript compiler |
45
+ | `tsx` | ^4.19.4 | TypeScript execution with hot-reload for dev |
46
+ | `@types/node` | ^22.15.0 | Node.js type definitions |
47
+ | `@types/react` | ^19.1.2 | React type definitions |
48
+ | `@types/better-sqlite3` | ^7.6.13 | better-sqlite3 type definitions |
49
+ | `eslint` | ^8.57.1 | JavaScript/TypeScript linter |
50
+ | `@typescript-eslint/parser` | ^8.56.1 | TypeScript parser for ESLint |
51
+
52
+ ---
53
+
54
+ ## TypeScript Configuration
55
+
56
+ ```json
57
+ {
58
+ "compilerOptions": {
59
+ "target": "ES2022",
60
+ "module": "Node16",
61
+ "moduleResolution": "Node16",
62
+ "strict": true,
63
+ "esModuleInterop": true,
64
+ "skipLibCheck": true,
65
+ "outDir": "dist",
66
+ "rootDir": "src",
67
+ "jsx": "react-jsx",
68
+ "declaration": true,
69
+ "sourceMap": true
70
+ },
71
+ "include": ["src"],
72
+ "exclude": ["node_modules", "dist"]
73
+ }
74
+ ```
75
+
76
+ **Key settings:**
77
+ - `"jsx": "react-jsx"` — uses the default React JSX runtime (no manual `import React` needed, though explicitly imported for clarity)
78
+ - `"module": "Node16"` — ESM with `.js` extensions in imports
79
+ - `"strict": true` — full strict mode (noImplicitAny, strictNullChecks, etc.)
80
+ - `"target": "ES2022"` — modern JS features (top-level await, Array.at, etc.)
81
+
82
+ ---
83
+
84
+ ## Package.json Template
85
+
86
+ ```json
87
+ {
88
+ "name": "your-cli-app",
89
+ "version": "0.1.0",
90
+ "description": "Your CLI app description",
91
+ "type": "module",
92
+ "files": ["dist", "config"],
93
+ "bin": {
94
+ "your-cli": "./dist/cli.js"
95
+ },
96
+ "scripts": {
97
+ "dev": "tsx src/cli.tsx",
98
+ "dev:watch": "tsx watch src/cli.tsx",
99
+ "build": "tsc",
100
+ "typecheck": "tsc --noEmit",
101
+ "test": "node --import tsx --test \"src/**/*.test.ts\"",
102
+ "lint": "eslint src/ --ext .ts,.tsx",
103
+ "start": "tsx src/cli.tsx"
104
+ },
105
+ "dependencies": {
106
+ "@inkjs/ui": "^2.0.0",
107
+ "better-sqlite3": "^11.8.1",
108
+ "ink": "^6.0.0",
109
+ "ink-spinner": "^5.0.0",
110
+ "nanoid": "^5.1.5",
111
+ "react": "^19.1.0"
112
+ },
113
+ "devDependencies": {
114
+ "@types/better-sqlite3": "^7.6.13",
115
+ "@types/node": "^22.15.0",
116
+ "@types/react": "^19.1.2",
117
+ "@typescript-eslint/parser": "^8.56.1",
118
+ "eslint": "^8.57.1",
119
+ "tsx": "^4.19.4",
120
+ "typescript": "^5.8.3"
121
+ }
122
+ }
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Build System
128
+
129
+ | Command | Purpose |
130
+ |---------|---------|
131
+ | `npm run dev` | Run in development mode (tsx, single run) |
132
+ | `npm run dev:watch` | Run with hot-reload (tsx watch) |
133
+ | `npm run build` | Compile TypeScript → `dist/` (tsc) |
134
+ | `npm run typecheck` | Type-check without emitting files (fast) |
135
+ | `npm run test` | Run tests with Node.js built-in test runner |
136
+ | `npm run lint` | Lint all `.ts` and `.tsx` files |
137
+ | `npm run start` | Run the app via tsx |
138
+
139
+ **No bundler required** — uses native ES modules with Node.js. TypeScript compiles to `.js` files in `dist/`, imports use `.js` extensions throughout.
140
+
141
+ ---
142
+
143
+ ## Project Structure
144
+
145
+ ```
146
+ project-root/
147
+ ├── src/
148
+ │ ├── cli.tsx # Entry point — render(<App />) via Ink
149
+ │ ├── app.tsx # Main app shell — menu-driven screen router
150
+ │ ├── components/
151
+ │ │ ├── banner.tsx # ASCII art header with brand identity
152
+ │ │ ├── timeline.tsx # Vertical wizard with step indicators
153
+ │ │ ├── step-indicator.tsx # Animated step status (◆/◇/○)
154
+ │ │ ├── gutter-line.tsx # │ prefix component for content lines
155
+ │ │ └── guttered-select.tsx # Select + MultiSelect with gutter integration
156
+ │ ├── screens/
157
+ │ │ ├── [feature].tsx # Feature screens (one file per screen)
158
+ │ │ └── steps/
159
+ │ │ └── [step].tsx # Wizard step components (self-contained)
160
+ │ └── lib/
161
+ │ ├── theme.ts # Color tokens — exports `C` object
162
+ │ ├── themes/ # Theme definitions (future: multiple themes)
163
+ │ │ ├── index.ts # Theme registry and loader
164
+ │ │ ├── catppuccin-mocha.ts
165
+ │ │ ├── catppuccin-latte.ts
166
+ │ │ ├── tokyonight-dark.ts
167
+ │ │ └── tokyonight-light.ts
168
+ │ ├── types.ts # All TypeScript types and interfaces
169
+ │ ├── config.ts # JSON config loader with validation
170
+ │ ├── db.ts # SQLite CRUD (better-sqlite3, nanoid IDs)
171
+ │ ├── csv.ts # CSV export utilities
172
+ │ ├── validation.ts # Input validation helpers
173
+ │ └── input/
174
+ │ └── normalize-keys.ts # ANSI input normalization for deterministic key handling
175
+ ├── config/ # JSON configuration files
176
+ ├── data/ # SQLite database, backups, exports
177
+ ├── docs/
178
+ │ ├── B3AWESOME-CLI-APP-UI-UX-GUIDELINE.md # UI/UX design system
179
+ │ ├── TECH-SPECS.md # This file
180
+ │ ├── PRD.md # Product requirements
181
+ │ └── USER-STORIES.md # User stories with acceptance criteria
182
+ └── dist/ # Compiled output (git-ignored)
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Styling System
188
+
189
+ ### Color Token Architecture
190
+
191
+ All colors are accessed through semantic tokens in `src/lib/theme.ts`:
192
+
193
+ ```typescript
194
+ export const C = {
195
+ mauve: '#...', // brand, active focus, cursor highlight
196
+ green: '#...', // success, completed steps, confirmed values
197
+ yellow: '#...', // in-progress input highlight
198
+ sapphire: '#...', // headings, section headers
199
+ teal: '#...', // secondary accent
200
+ red: '#...', // errors, destructive warnings
201
+ peach: '#...', // warnings
202
+ overlay1: '#...', // gutter lines, dim text, hints, instructions
203
+ overlay0: '#...', // pending steps, very dim elements
204
+ inputing: '#...', // currently typing value
205
+ inputed: '#...', // confirmed/completed value
206
+ label: '#...', // general text labels, content
207
+ } as const;
208
+ ```
209
+
210
+ **Components import `C` — never hardcode hex values.** This enables theme switching.
211
+
212
+ ### Available Themes
213
+
214
+ | Theme | Type | Token Source |
215
+ |-------|------|-------------|
216
+ | Catppuccin Mocha | Dark | [catppuccin.com](https://catppuccin.com) |
217
+ | Catppuccin Latte | Light | [catppuccin.com](https://catppuccin.com) |
218
+ | Tokyo Night | Dark | [github.com/enkia/tokyo-night-vscode-theme](https://github.com/enkia/tokyo-night-vscode-theme) |
219
+ | Tokyo Night Light | Light | Same source |
220
+
221
+ See `B3AWESOME-CLI-APP-UI-UX-GUIDELINE.md` §2 for complete hex values for all themes.
222
+
223
+ ---
224
+
225
+ ## UI Framework: React + Ink.js
226
+
227
+ ### How It Works
228
+
229
+ Ink.js uses React's component model to render terminal output:
230
+
231
+ ```tsx
232
+ // cli.tsx — Entry point
233
+ import { render } from 'ink';
234
+ import App from './app.js';
235
+
236
+ render(<App />);
237
+ ```
238
+
239
+ - React components render to **terminal characters** instead of DOM elements
240
+ - `<Box>` = flex container (like `<div>` with flexbox)
241
+ - `<Text>` = styled text (like `<span>` with color/bold)
242
+ - All Ink components support `color`, `bold`, `dimColor`, etc.
243
+ - Layout uses `flexDirection="column"` (vertical) or `flexDirection="row"` (horizontal)
244
+
245
+ ### Key Ink.js APIs
246
+
247
+ | API | Purpose |
248
+ |-----|---------|
249
+ | `<Box>` | Flex container — `flexDirection`, `padding`, `margin`, `gap` |
250
+ | `<Text>` | Styled text — `color`, `bold`, `dimColor`, `wrap` |
251
+ | `useInput(handler)` | Keyboard input listener — receives `(input, key)` |
252
+ | `useApp()` | App lifecycle — `exit()` to quit |
253
+ | `render(<Component />)` | Mount React tree to terminal |
254
+
255
+ ### Key @inkjs/ui APIs
256
+
257
+ | Component | Purpose |
258
+ |-----------|---------|
259
+ | `<TextInput>` | Text input field — `placeholder`, `defaultValue`, `onSubmit` |
260
+ | `<Spinner>` | Loading spinner animation |
261
+ | `<Select>` | Selection list (we use custom GutteredSelect instead) |
262
+
263
+ ---
264
+
265
+ ## Database Pattern
266
+
267
+ ### SQLite with better-sqlite3
268
+
269
+ ```typescript
270
+ import Database from 'better-sqlite3';
271
+
272
+ const db = new Database('data/assessments.db');
273
+ db.pragma('journal_mode = WAL'); // Write-ahead logging
274
+ db.pragma('foreign_keys = ON'); // Enforce FK constraints
275
+ ```
276
+
277
+ **Key patterns:**
278
+ - **Synchronous API** — no async/await needed, simpler code
279
+ - **nanoid for IDs** — `nanoid()` generates compact unique IDs
280
+ - **Prepared statements** — `db.prepare(sql).run(params)` for all queries
281
+ - **Transactions** — `db.transaction(() => { ... })()` for multi-step operations
282
+ - **Migrations** — detect columns via `PRAGMA table_info(tablename)`, alter as needed
283
+ - **Result pattern** — all DB functions return `{ ok: true, data } | { ok: false, error: string }`
284
+
285
+ ### Migration Pattern
286
+
287
+ ```typescript
288
+ function migrateIfNeeded() {
289
+ const columns = db.pragma('table_info(tablename)') as { name: string }[];
290
+ const hasNewColumn = columns.some((c) => c.name === 'new_column');
291
+ if (!hasNewColumn) {
292
+ db.exec('ALTER TABLE tablename ADD COLUMN new_column TEXT');
293
+ }
294
+ }
295
+ ```
296
+
297
+ ---
298
+
299
+ ## Input Handling
300
+
301
+ ### ANSI Input Normalization
302
+
303
+ Terminal input can arrive as batched ANSI sequences in a single stdin chunk. The normalizer handles:
304
+
305
+ ```typescript
306
+ // CSI arrow sequences: \x1b[A (up), \x1b[B (down)
307
+ // SS3 arrow sequences: \x1bOA (up), \x1bOB (down)
308
+ // Enter/Return: \r, \n, \r\n
309
+ ```
310
+
311
+ This prevents the "ghost selection" bug where rapid arrow key presses get misinterpreted.
312
+
313
+ ### Keyboard Shortcut Reference
314
+
315
+ | Key | Action |
316
+ |-----|--------|
317
+ | `↑` / `↓` | Move selection in lists |
318
+ | `Enter` | Confirm/submit |
319
+ | `Space` | Toggle in multi-select |
320
+ | `Esc` | Return to main menu |
321
+ | `Shift+Tab` | Go back (wizard steps, form fields) |
322
+
323
+ ---
324
+
325
+ ## Code Style
326
+
327
+ | Rule | Convention |
328
+ |------|-----------|
329
+ | **Variables** | camelCase |
330
+ | **Types/Interfaces** | PascalCase |
331
+ | **File names** | kebab-case (`gutter-line.tsx`, `employee-step.tsx`) |
332
+ | **Import order** | stdlib → external → internal |
333
+ | **Error handling** | Return `Result<T>` objects, never throw |
334
+ | **JSX runtime** | `react-jsx` (default React runtime) |
335
+ | **Module imports** | Always use `.js` extension (ESM) |
336
+ | **Strict mode** | TypeScript strict: true |
337
+
338
+ ### Result Pattern
339
+
340
+ ```typescript
341
+ type Result<T> = { ok: true; data: T } | { ok: false; error: string };
342
+ ```
343
+
344
+ All functions that can fail return this pattern. No try/catch at call sites — check `.ok` instead.
345
+
346
+ ---
347
+
348
+ ## Testing
349
+
350
+ Uses Node.js built-in test runner:
351
+
352
+ ```bash
353
+ # Run all tests
354
+ node --import tsx --test "src/**/*.test.ts"
355
+
356
+ # Run specific test
357
+ node --import tsx --test -t "test name" "src/**/*.test.ts"
358
+ ```
359
+
360
+ Test files are co-located with source: `src/lib/validation.test.ts` alongside `src/lib/validation.ts`.
361
+
362
+ ---
363
+
364
+ ## Verification Checklist
365
+
366
+ Before every commit or PR:
367
+
368
+ ```bash
369
+ npm run typecheck # TypeScript type-checking (fast)
370
+ npm run build # Full compilation
371
+ npm run lint # ESLint
372
+ npm run test # Test suite
373
+ ```
374
+
375
+ All four must pass clean.