icore 1.0.14 → 1.0.16

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 +17 -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} +79 -6
  5. package/dist/{commands.js → command/mechanics.js} +60 -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 -16
  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 +51 -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 -3
  48. package/readme.md +281 -290
  49. package/dist/cli.d.ts +0 -14
  50. package/dist/cli.js +0 -31
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,20 +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
- - [`resolveCommand(registry, positionals)`](#resolvecommandregistry-positionals)
36
- - [`resolveCommandFromArgs(registry, args)`](#resolvecommandfromargsregistry-args)
37
- - [`prepareCommandFromArgs(registry, args, options?)`](#preparecommandfromargsregistry-args-options)
38
- - [`runCommandFromRegistry(registry, args, context, options?)`](#runcommandfromregistryregistry-args-context-options)
39
- - [`mergeOptionsSchema(...schemas)`](#mergeoptionsschemaschemas)
40
- - [`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)
41
61
  - [How It Works](#how-it-works)
42
62
  - [Example](#example)
43
63
  - [Option Schemas](#option-schemas)
@@ -46,374 +66,340 @@ npm install icore
46
66
  - [Error Messages](#error-messages)
47
67
  - [Project Boundary](#project-boundary)
48
68
 
49
- ## API
69
+ ## API Reference
50
70
 
51
- ### `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.
52
74
 
53
- Parses raw CLI arguments into positionals and raw option values.
75
+ ### `createTerminalApp()`
54
76
 
55
- ```ts
56
- import { parseArgv } from 'icore';
57
-
58
- const argv = parseArgv([
59
- 'hello',
60
- '--name',
61
- 'Alice',
62
- '--uppercase'
63
- ]);
64
- ```
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.
65
80
 
66
- Result:
81
+ The checked TypeScript contract lives in [`src/terminal/app.ts`](src/terminal/app.ts).
67
82
 
68
- ```ts
69
- {
70
- positionals: ['hello'],
71
- options: {
72
- name: 'Alice',
73
- uppercase: true
74
- }
75
- }
76
- ```
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.
77
95
 
78
- When an option schema is provided, boolean options are parsed as flags without
79
- 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:
80
105
 
81
106
  ```ts
82
- const argv = parseArgv([
83
- 'hello',
84
- '--uppercase',
85
- 'Alice'
86
- ], {
87
- uppercase: {
88
- type: 'boolean'
89
- }
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
90
116
  });
91
117
  ```
92
118
 
93
- 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.
94
123
 
95
- ```ts
96
- {
97
- positionals: ['hello', 'Alice'],
98
- options: {
99
- uppercase: true
100
- }
101
- }
102
- ```
124
+ ### `createCommand()`
103
125
 
104
- ### `parseOptions(schema, values)`
126
+ `createCommand()` returns the command mechanics entrypoint used to build
127
+ `commands` for `createTerminalApp()`.
105
128
 
106
- Validates raw option values using an option schema and returns typed options.
129
+ Simplified shape:
107
130
 
108
131
  ```ts
109
- import { parseOptions } from 'icore';
132
+ const command = createCommand();
110
133
 
111
- const options = parseOptions({
112
- name: {
113
- type: 'string',
114
- default: 'world'
115
- },
116
- uppercase: {
117
- type: 'boolean'
118
- }
119
- } as const, {
120
- name: 'Alice',
121
- uppercase: true
122
- });
134
+ command.define(commandDefinition);
135
+ command.registry(commandDefinitions);
136
+ command.run(commandDefinition, args, context);
123
137
  ```
124
138
 
125
- Result:
139
+ Build `commands` before creating the terminal app:
126
140
 
127
- ```ts
128
- {
129
- name: 'Alice',
130
- uppercase: true
131
- }
132
- ```
141
+ - `createCommand()` creates the command mechanics entrypoint;
142
+ - `command.define(...)` declares one command;
143
+ - `command.registry([...])` returns the `commands` object required by
144
+ `createTerminalApp()`.
133
145
 
134
- ### `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.
135
149
 
136
- Validates raw option values and returns parsed options together with
137
- user-provided metadata.
150
+ #### `command.define(command)`
138
151
 
139
- ```ts
140
- 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.
141
154
 
142
- const result = parseOptionsDetailed({
143
- name: {
144
- type: 'string',
145
- default: 'world'
146
- },
147
- uppercase: {
148
- type: 'boolean'
149
- }
150
- } as const, {
151
- uppercase: true
152
- });
153
- ```
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)`
154
162
 
155
- 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:
156
175
 
157
176
  ```ts
158
- {
159
- options: {
160
- name: 'world',
161
- uppercase: true
162
- },
163
- provided: {
164
- name: false,
165
- uppercase: true
166
- }
167
- }
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);
168
186
  ```
169
187
 
170
- `provided` is useful when a default value and an omitted option have different
171
- 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.
172
192
 
173
- ### `defineCommand(command)`
193
+ #### `commands.prepare(args, options?)`
174
194
 
175
- 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(...)`.
176
197
 
177
- ```ts
178
- import { defineCommand } from 'icore';
198
+ #### `commands.run(prepared, context)`
179
199
 
180
- const command = defineCommand({
181
- path: ['hello'],
182
- options: {
183
- name: {
184
- type: 'string',
185
- default: 'world'
186
- },
187
- uppercase: {
188
- type: 'boolean'
189
- }
190
- },
191
- async handle({ options }) {
192
- 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.
193
202
 
194
- return options.uppercase ? message.toUpperCase() : message;
195
- }
196
- });
197
- ```
203
+ #### `commands.runFromArgs(args, context, options?)`
198
204
 
199
- ### `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.
200
207
 
201
- Defines a command registry while preserving literal command path types.
208
+ ### `createPresentation()`
202
209
 
203
- ```ts
204
- import { defineCommandRegistry } from 'icore';
210
+ `createPresentation()` creates the presentation object used by
211
+ `createTerminalApp()` to render terminal-ready results.
205
212
 
206
- const registry = defineCommandRegistry([
207
- helloCommand,
208
- helloFormalCommand
209
- ] as const);
210
- ```
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.
211
221
 
212
- The registry exposes ordered commands and derived command names:
222
+ Simplified shape:
213
223
 
214
224
  ```ts
215
- 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);
216
234
  ```
217
235
 
218
- Result:
236
+ The presentation object owns generic JSON, CSV, and table rendering mechanics.
237
+ Application code still maps domain objects to presentation-ready values.
219
238
 
220
- ```ts
221
- [
222
- 'hello',
223
- 'hello formal'
224
- ]
225
- ```
239
+ #### `presentation.empty()`
226
240
 
227
- Duplicate command paths are rejected.
241
+ Creates a presentation result with no terminal output.
228
242
 
229
- ### `isCommandName(registry, value)`
243
+ #### `presentation.text(value)`
230
244
 
231
- 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.
232
246
 
233
- ```ts
234
- if (isCommandName(registry, value)) {
235
- // value is narrowed to the registry command name union.
236
- }
237
- ```
247
+ #### `presentation.record(value)`
238
248
 
239
- ### `resolveCommand(registry, positionals)`
249
+ Creates a generic one-record view. The renderer can print it as table, CSV, or
250
+ JSON depending on the selected format.
240
251
 
241
- Resolves a command from already parsed positional arguments.
252
+ #### `presentation.records(values)`
242
253
 
243
- ```ts
244
- import { resolveCommand } from 'icore';
254
+ Creates a generic multi-record view. Application code still decides which fields
255
+ belong in each record.
245
256
 
246
- const resolved = resolveCommand(registry, [
247
- 'hello',
248
- 'formal',
249
- 'Alice'
250
- ]);
251
- ```
257
+ #### `presentation.table(rows)`
252
258
 
253
- Result:
259
+ Creates an explicit table view from prepared text rows.
254
260
 
255
- ```ts
256
- {
257
- name: 'hello formal',
258
- path: ['hello', 'formal'],
259
- command: helloFormalCommand,
260
- positionals: ['Alice']
261
- }
262
- ```
261
+ #### `presentation.csv(rows)`
263
262
 
264
- When several command paths match, the most specific command wins. For example,
265
- `hello formal` is preferred over `hello`.
263
+ Creates an explicit CSV view from scalar rows.
266
264
 
267
- ### `resolveCommandFromArgs(registry, args)`
265
+ #### `presentation.render(result, format?)`
268
266
 
269
- Resolves a command from raw CLI arguments. Each candidate command is parsed with
270
- its own option schema, so boolean flags do not accidentally consume command path
271
- segments.
267
+ Renders a presentation result to terminal text. The default format is `table`.
272
268
 
273
- ```ts
274
- const resolved = resolveCommandFromArgs(registry, [
275
- 'hello',
276
- '--name',
277
- 'Alice',
278
- '--uppercase'
279
- ]);
280
- ```
269
+ #### `presentation.renderers.*`
270
+
271
+ Exposes lower-level JSON, table, and CSV renderers for custom presentation
272
+ composition. Prefer `presentation.render(...)` for regular terminal commands.
273
+
274
+ ### `createOutput()`
281
275
 
282
- `resolveCommandFromArgs` keeps legacy option-anywhere command resolution. Use
283
- `prepareCommandFromArgs(..., { strict: true })` when command path diagnostics
284
- should reject options before the command path.
276
+ `createOutput()` creates the output object used by `createTerminalApp()` to
277
+ write rendered text.
285
278
 
286
- ### `prepareCommandFromArgs(registry, args, options?)`
279
+ Source of truth:
287
280
 
288
- Resolves and validates a command without requiring runtime context or calling
289
- the command handler.
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.
286
+
287
+ Simplified shape:
290
288
 
291
289
  ```ts
292
- const prepared = await prepareCommandFromArgs(
293
- registry,
294
- ['hello', '--name', 'Alice', '--uppercase'],
295
- {
296
- strict: true
297
- }
298
- );
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);
299
296
  ```
300
297
 
301
- A command `prepare` hook can return a typed payload. The payload is available
302
- on the prepared command before runtime context is created, then passed to the
303
- 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.
304
300
 
305
- ```ts
306
- const helloCommand = defineCommand({
307
- path: ['hello'],
308
- options: {
309
- name: {
310
- type: 'string',
311
- required: true
312
- }
313
- },
314
- prepare({ options }) {
315
- return {
316
- normalizedName: options.name.trim().toLowerCase()
317
- };
318
- },
319
- handle({ payload, context }) {
320
- return context.greeter.greet(payload.normalizedName);
321
- }
322
- });
301
+ #### `output.write(chunk)`
323
302
 
324
- const prepared = await prepareCommandFromArgs(registry, [
325
- 'hello',
326
- '--name',
327
- ' Alice '
328
- ]);
303
+ Writes regular terminal output through stdout.
329
304
 
330
- prepared.payload.normalizedName;
331
- ```
305
+ #### `output.error(chunk)`
332
306
 
333
- Use payload for derived CLI data. Runtime resources such as databases, sockets,
334
- or SDK clients should still be created outside `icore` and passed as `context`.
307
+ Writes diagnostic terminal output through stderr.
335
308
 
336
- With `strict: true`, the command path must appear before options:
309
+ #### `output.stdout.write(chunk)`
337
310
 
338
- ```console
339
- $ node cli.js --unknown hello
340
- Unexpected argument '--unknown'
341
- ```
311
+ Writes directly to the stdout channel. Use this only when a specific channel
312
+ must be passed around.
342
313
 
343
- Without strict mode, legacy candidate-schema resolution is preserved for
344
- backward compatibility.
314
+ #### `output.stderr.write(chunk)`
345
315
 
346
- ### `runCommandFromRegistry(registry, args, context, options?)`
316
+ Writes directly to the stderr channel. Use this only when a specific channel
317
+ must be passed around.
347
318
 
348
- Resolves a command from a registry and runs its handler.
319
+ ### Lower-Level Mechanics
349
320
 
350
- ```ts
351
- const output = await runCommandFromRegistry(
352
- registry,
353
- ['hello', '--name', 'Alice', '--uppercase'],
354
- context,
355
- {
356
- strict: true
357
- }
358
- );
359
- ```
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.
360
323
 
361
- This is **registry-level mechanics only**. Application-specific setup, side
362
- 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.
363
326
 
364
- ### `mergeOptionsSchema(...schemas)`
327
+ Source of truth:
365
328
 
366
- Merges multiple option schemas while preserving literal option definition types.
367
- 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.
368
333
 
369
- ```ts
370
- import { mergeOptionsSchema } from 'icore';
334
+ #### Parsing And Resolution
371
335
 
372
- const nameOptions = {
373
- name: {
374
- type: 'string',
375
- default: 'world'
376
- }
377
- } as const;
336
+ This group turns raw arguments or positionals into a selected command.
378
337
 
379
- const greetingOptions = {
380
- uppercase: {
381
- type: 'boolean'
382
- }
383
- } 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.
384
344
 
385
- const options = mergeOptionsSchema(nameOptions, greetingOptions);
386
- ```
345
+ Use these methods when command selection is needed without validation or
346
+ execution.
387
347
 
388
- ### `runCommand(command, args, context, options?)`
348
+ #### Preparation
389
349
 
390
- Parses arguments, validates options, checks command positionals, and runs the
391
- handler.
350
+ This group validates command input without runtime context.
392
351
 
393
- ```ts
394
- const output = await runCommand(
395
- command,
396
- ['hello', '--name', 'Alice', '--uppercase'],
397
- context,
398
- {
399
- strict: true
400
- }
401
- );
402
- ```
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.
403
364
 
404
- **By default**, extra positionals are rejected. A command can opt in to extra
405
- 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.
406
370
 
407
- With `strict: true`, direct command execution also requires the command path to
408
- 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.
409
392
 
410
393
  ## How It Works
411
394
 
412
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])
413
396
 
414
-
415
397
  ## Example
416
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
+
417
403
  ```ts
418
404
  import { defineCommand, runCommand } from 'icore';
419
405
 
@@ -713,4 +699,9 @@ Good responsibilities for `icore`:
713
699
  - option schema evaluation;
714
700
  - command path checking;
715
701
  - common argument errors;
716
- - 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`.