icore 1.0.15 → 1.0.17

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 (50) hide show
  1. package/CHANGELOG.md +220 -0
  2. package/dist/{argv.d.ts → argv/parser.d.ts} +3 -1
  3. package/dist/{argv.js → argv/parser.js} +5 -5
  4. package/dist/{commands.d.ts → command/mechanics.d.ts} +73 -6
  5. package/dist/{commands.js → command/mechanics.js} +53 -15
  6. package/dist/{errors.d.ts → errors/icore-error.d.ts} +2 -0
  7. package/dist/{errors.js → errors/icore-error.js} +2 -0
  8. package/dist/index.d.ts +28 -1
  9. package/dist/index.js +61 -17
  10. package/dist/options/parser.d.ts +43 -0
  11. package/dist/{options.js → options/parser.js} +27 -17
  12. package/dist/{options.d.ts → options/schema.d.ts} +16 -23
  13. package/dist/options/schema.js +23 -0
  14. package/dist/output/facade.d.ts +28 -0
  15. package/dist/output/facade.js +32 -0
  16. package/dist/output/node-writer.d.ts +17 -0
  17. package/dist/output/node-writer.js +25 -0
  18. package/dist/output/text-writer.d.ts +29 -0
  19. package/dist/output/text-writer.js +49 -0
  20. package/dist/presentation/facade.d.ts +39 -0
  21. package/dist/presentation/facade.js +47 -0
  22. package/dist/presentation/format-options.d.ts +31 -0
  23. package/dist/presentation/format-options.js +37 -0
  24. package/dist/presentation/renderers/csv.d.ts +18 -0
  25. package/dist/presentation/renderers/csv.js +35 -0
  26. package/dist/presentation/renderers/json.d.ts +13 -0
  27. package/dist/presentation/renderers/json.js +18 -0
  28. package/dist/presentation/renderers/table.d.ts +14 -0
  29. package/dist/presentation/renderers/table.js +29 -0
  30. package/dist/presentation/result-renderer.d.ts +25 -0
  31. package/dist/presentation/result-renderer.js +184 -0
  32. package/dist/presentation/view.d.ts +58 -0
  33. package/dist/presentation/view.js +56 -0
  34. package/dist/terminal/app.d.ts +70 -0
  35. package/dist/terminal/app.js +100 -0
  36. package/examples/cli-argument-syntax.md +218 -0
  37. package/examples/command-resolution.md +174 -0
  38. package/examples/custom-command-flow.md +109 -0
  39. package/examples/option-schemas.md +206 -0
  40. package/examples/output-writers.md +128 -0
  41. package/examples/practical-cli-patterns.md +385 -0
  42. package/examples/presentation-output.md +66 -0
  43. package/examples/presentation-primitives.md +190 -0
  44. package/examples/readme.md +48 -0
  45. package/examples/terminal-app.md +118 -0
  46. package/examples/two-phase-primitives.md +282 -0
  47. package/package.json +9 -4
  48. package/readme.md +278 -299
  49. package/dist/cli.d.ts +0 -14
  50. package/dist/cli.js +0 -32
package/readme.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![types](https://img.shields.io/npm/types/icore.svg)](https://www.npmjs.com/package/icore)
6
6
  [![license](https://img.shields.io/npm/l/icore.svg)](LICENSE)
7
7
 
8
- Small dependency-free command line interface mechanics for [Node.js®](https://nodejs.org) applications.
8
+ Small dependency-free command line interface and terminal presentation mechanics for [Node.js®](https://nodejs.org) applications.
9
9
 
10
10
  Supports a practical GNU-style option syntax:
11
11
 
@@ -24,21 +24,40 @@ npm install icore
24
24
 
25
25
  ## Table of Contents
26
26
 
27
- - [Installation](#installation)
28
- - [API](#api)
29
- - [`parseArgv(args, schema?)`](#parseargvargs-schema)
30
- - [`parseOptions(schema, values)`](#parseoptionsschema-values)
31
- - [`parseOptionsDetailed(schema, values)`](#parseoptionsdetailedschema-values)
32
- - [`defineCommand(command)`](#definecommandcommand)
33
- - [`defineCommandRegistry(commands)`](#definecommandregistrycommands)
34
- - [`isCommandName(registry, value)`](#iscommandnameregistry-value)
35
- - [`isPreparedCommandName(prepared, name)`](#ispreparedcommandnameprepared-name)
36
- - [`resolveCommand(registry, positionals)`](#resolvecommandregistry-positionals)
37
- - [`resolveCommandFromArgs(registry, args)`](#resolvecommandfromargsregistry-args)
38
- - [`prepareCommandFromArgs(registry, args, options?)`](#preparecommandfromargsregistry-args-options)
39
- - [`runCommandFromRegistry(registry, args, context, options?)`](#runcommandfromregistryregistry-args-context-options)
40
- - [`mergeOptionsSchema(...schemas)`](#mergeoptionsschemaschemas)
41
- - [`runCommand(command, args, context, options?)`](#runcommandcommand-args-context-options)
27
+ - [API Reference](#api-reference)
28
+ - [`createTerminalApp()`](#createterminalapp)
29
+ - [`createCommand()`](#createcommand)
30
+ - [`command.define(command)`](#commanddefinecommand)
31
+ - [`command.registry(commands)`](#commandregistrycommands)
32
+ - [`command.run(command, args, context)`](#commandruncommand-args-context)
33
+ - [`createCommands(commands)`](#createcommandscommands)
34
+ - [`commands.prepare(args, options?)`](#commandsprepareargs-options)
35
+ - [`commands.run(prepared, context)`](#commandsrunprepared-context)
36
+ - [`commands.runFromArgs(args, context, options?)`](#commandsrunfromargsargs-context-options)
37
+ - [`createPresentation()`](#createpresentation)
38
+ - [`presentation.empty()`](#presentationempty)
39
+ - [`presentation.text(value)`](#presentationtextvalue)
40
+ - [`presentation.record(value)`](#presentationrecordvalue)
41
+ - [`presentation.records(values)`](#presentationrecordsvalues)
42
+ - [`presentation.table(rows)`](#presentationtablerows)
43
+ - [`presentation.csv(rows)`](#presentationcsvrows)
44
+ - [`presentation.render(result, format?)`](#presentationrenderresult-format)
45
+ - [`presentation.renderers.*`](#presentationrenderers)
46
+ - [`createOutput()`](#createoutput)
47
+ - [`output.write(chunk)`](#outputwritechunk)
48
+ - [`output.error(chunk)`](#outputerrorchunk)
49
+ - [`output.stdout.write(chunk)`](#outputstdoutwritechunk)
50
+ - [`output.stderr.write(chunk)`](#outputstderrwritechunk)
51
+ - [Lower-Level Mechanics](#lower-level-mechanics)
52
+ - [`parseArgv(args, schema?)`](#parseargv)
53
+ - [`parseOptionsDetailed(schema, values)`](#parseoptionsdetailed)
54
+ - [`resolveCommand(registry, positionals)`](#resolvecommand)
55
+ - [`resolveCommandFromArgs(registry, args)`](#resolvecommandfromargs)
56
+ - [`prepareCommandFromArgs(registry, args, options?)`](#preparecommandfromargs)
57
+ - [`runPreparedCommand(prepared, context)`](#runpreparedcommand)
58
+ - [`runCommandFromRegistry(registry, args, context, options?)`](#runcommandfromregistry)
59
+ - [`runCommand(command, args, context, options?)`](#runcommand)
60
+ - [Internal Source Layout](#internal-source-layout)
42
61
  - [How It Works](#how-it-works)
43
62
  - [Example](#example)
44
63
  - [Option Schemas](#option-schemas)
@@ -47,385 +66,340 @@ npm install icore
47
66
  - [Error Messages](#error-messages)
48
67
  - [Project Boundary](#project-boundary)
49
68
 
50
- ## API
69
+ ## API Reference
51
70
 
52
- ### `parseArgv(args, schema?)`
71
+ Start at `createTerminalApp()` for regular terminal applications, then move to
72
+ the construction methods only when the app needs explicit command,
73
+ presentation, or output wiring.
53
74
 
54
- Parses raw CLI arguments into positionals and raw option values.
75
+ ### `createTerminalApp()`
55
76
 
56
- ```ts
57
- import { parseArgv } from 'icore';
58
-
59
- const argv = parseArgv([
60
- 'hello',
61
- '--name',
62
- 'Alice',
63
- '--uppercase'
64
- ]);
65
- ```
77
+ `createTerminalApp()` is the top-level terminal application composition point.
78
+ It wires command execution, presentation rendering, and stdout/stderr delivery
79
+ without taking ownership of application behavior.
66
80
 
67
- Result:
81
+ The checked TypeScript contract lives in [`src/terminal/app.ts`](src/terminal/app.ts).
68
82
 
69
- ```ts
70
- {
71
- positionals: ['hello'],
72
- options: {
73
- name: 'Alice',
74
- uppercase: true
75
- }
76
- }
77
- ```
83
+ The method returns an application object with `prepare(args)` and
84
+ `run(args, context)`.
85
+
86
+ Construction inputs:
87
+
88
+ - `commands` is required; build it with [`createCommand()`](#createcommand) and
89
+ [`command.registry(commands)`](#commandregistrycommands), or directly with
90
+ [`createCommands(commands)`](#createcommandscommands);
91
+ - `presentation` is optional; omit it to use [`createPresentation()`](#createpresentation);
92
+ - `output` is optional; omit it to use [`createOutput()`](#createoutput);
93
+ - `resolveFormat` is optional and customizes how a prepared command selects a
94
+ presentation format.
78
95
 
79
- When an option schema is provided, boolean options are parsed as flags without
80
- consuming the following positional argument:
96
+ Returned app methods:
97
+
98
+ - `app.prepare(args, options?)` delegates to
99
+ [`commands.prepare(args, options?)`](#commandsprepareargs-options);
100
+ - `app.run(args, context, options?)` prepares the command, delegates command
101
+ execution to [`commands.run(prepared, context)`](#commandsrunprepared-context),
102
+ then renders and writes terminal output.
103
+
104
+ Minimal shape:
81
105
 
82
106
  ```ts
83
- const argv = parseArgv([
84
- 'hello',
85
- '--uppercase',
86
- 'Alice'
87
- ], {
88
- uppercase: {
89
- type: 'boolean'
90
- }
107
+ const app = createTerminalApp({
108
+ commands,
109
+ presentation,
110
+ output
111
+ });
112
+
113
+ const args = process.argv.slice(2);
114
+ const exitCode = await app.run(args, context, {
115
+ strict: true
91
116
  });
92
117
  ```
93
118
 
94
- Result:
119
+ Command handlers keep ownership of application work. The terminal app only
120
+ accepts terminal-ready results: text, streaming text, presentation results, or
121
+ no output. Application DTO mapping, config loading, network clients, and domain
122
+ behavior stay in the consuming application.
95
123
 
96
- ```ts
97
- {
98
- positionals: ['hello', 'Alice'],
99
- options: {
100
- uppercase: true
101
- }
102
- }
103
- ```
124
+ ### `createCommand()`
104
125
 
105
- ### `parseOptions(schema, values)`
126
+ `createCommand()` returns the command mechanics entrypoint used to build
127
+ `commands` for `createTerminalApp()`.
106
128
 
107
- Validates raw option values using an option schema and returns typed options.
129
+ Simplified shape:
108
130
 
109
131
  ```ts
110
- import { parseOptions } from 'icore';
132
+ const command = createCommand();
111
133
 
112
- const options = parseOptions({
113
- name: {
114
- type: 'string',
115
- default: 'world'
116
- },
117
- uppercase: {
118
- type: 'boolean'
119
- }
120
- } as const, {
121
- name: 'Alice',
122
- uppercase: true
123
- });
134
+ command.define(commandDefinition);
135
+ command.registry(commandDefinitions);
136
+ command.run(commandDefinition, args, context);
124
137
  ```
125
138
 
126
- Result:
139
+ Build `commands` before creating the terminal app:
127
140
 
128
- ```ts
129
- {
130
- name: 'Alice',
131
- uppercase: true
132
- }
133
- ```
141
+ - `createCommand()` creates the command mechanics entrypoint;
142
+ - `command.define(...)` declares one command;
143
+ - `command.registry([...])` returns the `commands` object required by
144
+ `createTerminalApp()`.
134
145
 
135
- ### `parseOptionsDetailed(schema, values)`
146
+ The command definition keeps the application-specific parts: command path,
147
+ option schema, and handler behavior. The terminal app only needs the resulting
148
+ registry object.
136
149
 
137
- Validates raw option values and returns parsed options together with
138
- user-provided metadata.
150
+ #### `command.define(command)`
139
151
 
140
- ```ts
141
- import { parseOptionsDetailed } from 'icore';
152
+ Declares one command and preserves its literal path and option schema types.
153
+ Use it when defining commands inline before adding them to a registry.
142
154
 
143
- const result = parseOptionsDetailed({
144
- name: {
145
- type: 'string',
146
- default: 'world'
147
- },
148
- uppercase: {
149
- type: 'boolean'
150
- }
151
- } as const, {
152
- uppercase: true
153
- });
154
- ```
155
+ #### `command.registry(commands)`
156
+
157
+ Builds the `commands` object required by `createTerminalApp()`. It keeps the
158
+ registered command definitions, derived command names, and command flow methods
159
+ together.
160
+
161
+ #### `command.run(command, args, context)`
155
162
 
156
- Result:
163
+ Runs a single command without a registry. Use it for focused command execution,
164
+ small tests, or custom flows where command path resolution is not needed.
165
+
166
+ #### `createCommands(commands)`
167
+
168
+ Use `createCommands(commands)` when the application already has command
169
+ definitions and does not need the `createCommand()` object form.
170
+
171
+ The returned object is the same `commands` contract consumed by
172
+ `createTerminalApp()`: it can resolve, prepare, and run registered commands.
173
+
174
+ Simplified shape:
157
175
 
158
176
  ```ts
159
- {
160
- options: {
161
- name: 'world',
162
- uppercase: true
163
- },
164
- provided: {
165
- name: false,
166
- uppercase: true
167
- }
168
- }
177
+ const commands = createCommands(commandDefinitions);
178
+
179
+ commands.names;
180
+ commands.registry;
181
+ commands.resolve(positionals);
182
+ commands.resolveFromArgs(args);
183
+ commands.prepare(args, options);
184
+ commands.run(prepared, context);
185
+ commands.runFromArgs(args, context, options);
169
186
  ```
170
187
 
171
- `provided` is useful when a default value and an omitted option have different
172
- application-level meaning.
188
+ Use `commands.prepare(...)` and `commands.run(...)` when application code needs
189
+ the same two-phase flow used by `createTerminalApp()`. Use
190
+ `commands.runFromArgs(...)` for custom terminal boundaries that still want the
191
+ registry-level command flow.
173
192
 
174
- ### `defineCommand(command)`
193
+ #### `commands.prepare(args, options?)`
175
194
 
176
- Defines a command while preserving its option schema types.
195
+ Resolves and validates a registered command without runtime context. This is
196
+ the same preparation step used by `app.prepare(...)` and `app.run(...)`.
177
197
 
178
- ```ts
179
- import { defineCommand } from 'icore';
198
+ #### `commands.run(prepared, context)`
180
199
 
181
- const command = defineCommand({
182
- path: ['hello'],
183
- options: {
184
- name: {
185
- type: 'string',
186
- default: 'world'
187
- },
188
- uppercase: {
189
- type: 'boolean'
190
- }
191
- },
192
- async handle({ options }) {
193
- const message = `Hello, ${options.name}!`;
200
+ Runs an already prepared command with application context. Use it after
201
+ `commands.prepare(...)` when the application needs a custom two-phase flow.
194
202
 
195
- return options.uppercase ? message.toUpperCase() : message;
196
- }
197
- });
198
- ```
203
+ #### `commands.runFromArgs(args, context, options?)`
199
204
 
200
- ### `defineCommandRegistry(commands)`
205
+ Resolves, prepares, and runs a registered command without the terminal
206
+ application boundary. Use it when a custom boundary owns presentation or output.
201
207
 
202
- Defines a command registry while preserving literal command path types.
208
+ ### `createPresentation()`
203
209
 
204
- ```ts
205
- import { defineCommandRegistry } from 'icore';
210
+ `createPresentation()` creates the presentation object used by
211
+ `createTerminalApp()` to render terminal-ready results.
206
212
 
207
- const registry = defineCommandRegistry([
208
- helloCommand,
209
- helloFormalCommand
210
- ] as const);
211
- ```
213
+ Source of truth:
214
+
215
+ - [src/presentation/facade.ts](src/presentation/facade.ts) for the presentation
216
+ object;
217
+ - [src/presentation/view.ts](src/presentation/view.ts) for presentation view
218
+ factories;
219
+ - [src/presentation/result-renderer.ts](src/presentation/result-renderer.ts)
220
+ for rendering presentation results.
212
221
 
213
- The registry exposes ordered commands and derived command names:
222
+ Simplified shape:
214
223
 
215
224
  ```ts
216
- registry.commandNames;
225
+ const presentation = createPresentation();
226
+
227
+ presentation.empty();
228
+ presentation.text(value);
229
+ presentation.record(value);
230
+ presentation.records(values);
231
+ presentation.table(rows);
232
+ presentation.csv(rows);
233
+ presentation.render(result, format);
217
234
  ```
218
235
 
219
- Result:
236
+ The presentation object owns generic JSON, CSV, and table rendering mechanics.
237
+ Application code still maps domain objects to presentation-ready values.
220
238
 
221
- ```ts
222
- [
223
- 'hello',
224
- 'hello formal'
225
- ]
226
- ```
239
+ #### `presentation.empty()`
227
240
 
228
- Duplicate command paths are rejected.
241
+ Creates a presentation result with no terminal output.
229
242
 
230
- ### `isCommandName(registry, value)`
243
+ #### `presentation.text(value)`
231
244
 
232
- Checks whether an unknown value is a command name registered in the registry.
245
+ Wraps ready terminal text. Use it when the command already owns the final text.
233
246
 
234
- ```ts
235
- if (isCommandName(registry, value)) {
236
- // value is narrowed to the registry command name union.
237
- }
238
- ```
247
+ #### `presentation.record(value)`
239
248
 
240
- ### `isPreparedCommandName(prepared, name)`
249
+ Creates a generic one-record view. The renderer can print it as table, CSV, or
250
+ JSON depending on the selected format.
241
251
 
242
- Checks whether a prepared command has the given command name and narrows the
243
- prepared command union.
252
+ #### `presentation.records(values)`
244
253
 
245
- ```ts
246
- if (isPreparedCommandName(prepared, 'hello')) {
247
- // prepared.payload is narrowed to the `hello` command payload.
248
- }
249
- ```
254
+ Creates a generic multi-record view. Application code still decides which fields
255
+ belong in each record.
250
256
 
251
- ### `resolveCommand(registry, positionals)`
257
+ #### `presentation.table(rows)`
252
258
 
253
- Resolves a command from already parsed positional arguments.
259
+ Creates an explicit table view from prepared text rows.
254
260
 
255
- ```ts
256
- import { resolveCommand } from 'icore';
261
+ #### `presentation.csv(rows)`
257
262
 
258
- const resolved = resolveCommand(registry, [
259
- 'hello',
260
- 'formal',
261
- 'Alice'
262
- ]);
263
- ```
263
+ Creates an explicit CSV view from scalar rows.
264
264
 
265
- Result:
265
+ #### `presentation.render(result, format?)`
266
266
 
267
- ```ts
268
- {
269
- name: 'hello formal',
270
- path: ['hello', 'formal'],
271
- command: helloFormalCommand,
272
- positionals: ['Alice']
273
- }
274
- ```
267
+ Renders a presentation result to terminal text. The default format is `table`.
275
268
 
276
- When several command paths match, the most specific command wins. For example,
277
- `hello formal` is preferred over `hello`.
269
+ #### `presentation.renderers.*`
278
270
 
279
- ### `resolveCommandFromArgs(registry, args)`
271
+ Exposes lower-level JSON, table, and CSV renderers for custom presentation
272
+ composition. Prefer `presentation.render(...)` for regular terminal commands.
280
273
 
281
- Resolves a command from raw CLI arguments. Each candidate command is parsed with
282
- its own option schema, so boolean flags do not accidentally consume command path
283
- segments.
274
+ ### `createOutput()`
284
275
 
285
- ```ts
286
- const resolved = resolveCommandFromArgs(registry, [
287
- 'hello',
288
- '--name',
289
- 'Alice',
290
- '--uppercase'
291
- ]);
292
- ```
276
+ `createOutput()` creates the output object used by `createTerminalApp()` to
277
+ write rendered text.
293
278
 
294
- `resolveCommandFromArgs` keeps legacy option-anywhere command resolution. Use
295
- `prepareCommandFromArgs(..., { strict: true })` when command path diagnostics
296
- should reject options before the command path.
279
+ Source of truth:
297
280
 
298
- ### `prepareCommandFromArgs(registry, args, options?)`
281
+ - [src/output/facade.ts](src/output/facade.ts) for the output object;
282
+ - [src/output/node-writer.ts](src/output/node-writer.ts) for stdout and stderr
283
+ writers;
284
+ - [src/output/text-writer.ts](src/output/text-writer.ts) for backpressure-aware
285
+ text writing.
299
286
 
300
- Resolves and validates a command without requiring runtime context or calling
301
- the command handler.
287
+ Simplified shape:
302
288
 
303
289
  ```ts
304
- const prepared = await prepareCommandFromArgs(
305
- registry,
306
- ['hello', '--name', 'Alice', '--uppercase'],
307
- {
308
- strict: true
309
- }
310
- );
290
+ const output = createOutput();
291
+
292
+ await output.write(chunk);
293
+ await output.error(chunk);
294
+ await output.stdout.write(chunk);
295
+ await output.stderr.write(chunk);
311
296
  ```
312
297
 
313
- A command `prepare` hook can return a typed payload. The payload is available
314
- on the prepared command before runtime context is created, then passed to the
315
- handler:
298
+ The default output writes regular text to `stdout` and diagnostic text to
299
+ `stderr`. Pass custom sinks when tests or applications need controlled output.
316
300
 
317
- ```ts
318
- const helloCommand = defineCommand({
319
- path: ['hello'],
320
- options: {
321
- name: {
322
- type: 'string',
323
- required: true
324
- }
325
- },
326
- prepare({ options }) {
327
- return {
328
- normalizedName: options.name.trim().toLowerCase()
329
- };
330
- },
331
- handle({ payload, context }) {
332
- return context.greeter.greet(payload.normalizedName);
333
- }
334
- });
301
+ #### `output.write(chunk)`
335
302
 
336
- const prepared = await prepareCommandFromArgs(registry, [
337
- 'hello',
338
- '--name',
339
- ' Alice '
340
- ]);
303
+ Writes regular terminal output through stdout.
341
304
 
342
- prepared.payload.normalizedName;
343
- ```
305
+ #### `output.error(chunk)`
344
306
 
345
- Use payload for derived CLI data. Runtime resources such as databases, sockets,
346
- or SDK clients should still be created outside `icore` and passed as `context`.
307
+ Writes diagnostic terminal output through stderr.
347
308
 
348
- With `strict: true`, the command path must appear before options:
309
+ #### `output.stdout.write(chunk)`
349
310
 
350
- ```console
351
- $ node cli.js --unknown hello
352
- Unexpected argument '--unknown'
353
- ```
311
+ Writes directly to the stdout channel. Use this only when a specific channel
312
+ must be passed around.
354
313
 
355
- Without strict mode, legacy candidate-schema resolution is preserved for
356
- backward compatibility.
314
+ #### `output.stderr.write(chunk)`
357
315
 
358
- ### `runCommandFromRegistry(registry, args, context, options?)`
316
+ Writes directly to the stderr channel. Use this only when a specific channel
317
+ must be passed around.
359
318
 
360
- Resolves a command from a registry and runs its handler.
319
+ ### Lower-Level Mechanics
361
320
 
362
- ```ts
363
- const output = await runCommandFromRegistry(
364
- registry,
365
- ['hello', '--name', 'Alice', '--uppercase'],
366
- context,
367
- {
368
- strict: true
369
- }
370
- );
371
- ```
321
+ Lower-level mechanics sit behind the construction methods. They are kept out of
322
+ the main navigation to keep the application construction path first.
372
323
 
373
- This is **registry-level mechanics only**. Application-specific setup, side
374
- effects, and output formatting still belong outside `icore`.
324
+ For application code, prefer `createTerminalApp()` first. Drop to this level
325
+ only when the terminal application flow or construction methods are too coarse.
375
326
 
376
- ### `mergeOptionsSchema(...schemas)`
327
+ Source of truth:
377
328
 
378
- Merges multiple option schemas while preserving literal option definition types.
379
- Later schemas override earlier schemas with the same option name.
329
+ - [src/argv/parser.ts](src/argv/parser.ts) for raw argv parsing;
330
+ - [src/options/parser.ts](src/options/parser.ts) for option value validation;
331
+ - [src/command/mechanics.ts](src/command/mechanics.ts) for command resolution,
332
+ preparation, and execution.
380
333
 
381
- ```ts
382
- import { mergeOptionsSchema } from 'icore';
334
+ #### Parsing And Resolution
383
335
 
384
- const nameOptions = {
385
- name: {
386
- type: 'string',
387
- default: 'world'
388
- }
389
- } as const;
336
+ This group turns raw arguments or positionals into a selected command.
390
337
 
391
- const greetingOptions = {
392
- uppercase: {
393
- type: 'boolean'
394
- }
395
- } as const;
338
+ - <a id="parseargv"></a>`parseArgv(args, schema?)` parses raw CLI arguments into positionals and raw
339
+ option values;
340
+ - <a id="resolvecommand"></a>`resolveCommand(registry, positionals)` resolves from already parsed
341
+ positionals;
342
+ - <a id="resolvecommandfromargs"></a>`resolveCommandFromArgs(registry, args)` resolves from raw CLI arguments by
343
+ using registered command schemas.
396
344
 
397
- const options = mergeOptionsSchema(nameOptions, greetingOptions);
398
- ```
345
+ Use these methods when command selection is needed without validation or
346
+ execution.
399
347
 
400
- ### `runCommand(command, args, context, options?)`
348
+ #### Preparation
401
349
 
402
- Parses arguments, validates options, checks command positionals, and runs the
403
- handler.
350
+ This group validates command input without runtime context.
404
351
 
405
- ```ts
406
- const output = await runCommand(
407
- command,
408
- ['hello', '--name', 'Alice', '--uppercase'],
409
- context,
410
- {
411
- strict: true
412
- }
413
- );
414
- ```
352
+ - <a id="parseoptionsdetailed"></a>`parseOptionsDetailed(schema, values)` validates raw option values and keeps
353
+ option presence metadata;
354
+ - <a id="preparecommandfromargs"></a>`prepareCommandFromArgs(registry, args, options?)` resolves and validates a
355
+ registered command;
356
+ - `options.strict` rejects options before the command path.
357
+
358
+ Use preparation when runtime resources should be created only after command
359
+ selection is known.
360
+
361
+ #### Execution
362
+
363
+ This group calls command handlers after command input is prepared.
415
364
 
416
- **By default**, extra positionals are rejected. A command can opt in to extra
417
- positionals with `allowExtraPositionals: true`.
365
+ - <a id="runpreparedcommand"></a>`runPreparedCommand(prepared, context)` runs an already prepared command;
366
+ - <a id="runcommandfromregistry"></a>`runCommandFromRegistry(registry, args, context, options?)` prepares and runs
367
+ a command from a registry;
368
+ - <a id="runcommand"></a>`runCommand(command, args, context, options?)` runs a single command without a
369
+ registry.
418
370
 
419
- With `strict: true`, direct command execution also requires the command path to
420
- appear before options.
371
+ Use execution primitives for custom boundaries. Regular CLI applications should
372
+ usually stay at `createTerminalApp()`.
373
+
374
+ ## Internal Source Layout
375
+
376
+ The public package entrypoint remains `src/index.ts`. Internal source files are
377
+ grouped by CLI framework responsibility:
378
+
379
+ ```text
380
+ src/
381
+ argv/ raw argv token parsing
382
+ options/ option schemas and option value validation
383
+ command/ command resolution and execution mechanics
384
+ presentation/ terminal view models and JSON/CSV/table renderers
385
+ output/ stdout/stderr writer boundaries
386
+ terminal/ command + presentation + output app composition
387
+ errors/ machine-readable icore errors
388
+ ```
389
+
390
+ Consumers should continue importing from `icore`; deep imports are an internal
391
+ source layout detail.
421
392
 
422
393
  ## How It Works
423
394
 
424
395
  ![yuml diagram](http://yuml.me/diagram/scruffy;dir:LR;/class/[*argv*%20{bg:gray}|External;hello%20--name%20Alice%20--uppercase]->[*matches*%20{bg:lavender}|System;parse,%20resolve,%20validate,%20infer]->[*typed%20result*%20{bg:honeydew}|Container;command=hello;%20name=Alice;%20uppercase=true]->[*your%20app*%20{bg:cornsilk}|System;business%20logic%20and%20output])
425
396
 
426
-
427
397
  ## Example
428
398
 
399
+ More examples live in [examples/readme.md](examples/readme.md), including
400
+ option schemas, CLI argument syntax, practical CLI patterns, and lower-level
401
+ primitives.
402
+
429
403
  ```ts
430
404
  import { defineCommand, runCommand } from 'icore';
431
405
 
@@ -725,4 +699,9 @@ Good responsibilities for `icore`:
725
699
  - option schema evaluation;
726
700
  - command path checking;
727
701
  - common argument errors;
728
- - typed command handler input.
702
+ - typed command handler input;
703
+ - generic JSON, CSV, and text table rendering;
704
+ - stdout/stderr text writer mechanics.
705
+
706
+ Application-specific report mapping, scalar formatting, API calls, config
707
+ loading, and domain behavior stay outside `icore`.