@procedures/mark 0.0.0-stage → 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 (91) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +21 -0
  3. package/README.md +799 -2
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/args.d.ts +77 -0
  6. package/dist/args.d.ts.map +1 -0
  7. package/dist/args.js +351 -0
  8. package/dist/args.js.map +1 -0
  9. package/dist/ask.d.ts +48 -0
  10. package/dist/ask.d.ts.map +1 -0
  11. package/dist/ask.js +91 -0
  12. package/dist/ask.js.map +1 -0
  13. package/dist/bin.d.ts +9 -0
  14. package/dist/bin.d.ts.map +1 -0
  15. package/dist/bin.js +10 -0
  16. package/dist/bin.js.map +1 -0
  17. package/dist/cli.d.ts +61 -0
  18. package/dist/cli.d.ts.map +1 -0
  19. package/dist/cli.js +535 -0
  20. package/dist/cli.js.map +1 -0
  21. package/dist/client-mode.d.ts +21 -0
  22. package/dist/client-mode.d.ts.map +1 -0
  23. package/dist/client-mode.js +75 -0
  24. package/dist/client-mode.js.map +1 -0
  25. package/dist/ecosystem.d.ts +67 -0
  26. package/dist/ecosystem.d.ts.map +1 -0
  27. package/dist/ecosystem.js +212 -0
  28. package/dist/ecosystem.js.map +1 -0
  29. package/dist/failure.d.ts +16 -0
  30. package/dist/failure.d.ts.map +1 -0
  31. package/dist/failure.js +47 -0
  32. package/dist/failure.js.map +1 -0
  33. package/dist/format.d.ts +30 -0
  34. package/dist/format.d.ts.map +1 -0
  35. package/dist/format.js +125 -0
  36. package/dist/format.js.map +1 -0
  37. package/dist/index.d.ts +13 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +13 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/lockfile.d.ts +26 -0
  42. package/dist/lockfile.d.ts.map +1 -0
  43. package/dist/lockfile.js +68 -0
  44. package/dist/lockfile.js.map +1 -0
  45. package/dist/manifest-build.d.ts +7 -0
  46. package/dist/manifest-build.d.ts.map +1 -0
  47. package/dist/manifest-build.js +11 -0
  48. package/dist/manifest-build.js.map +1 -0
  49. package/dist/manifest.d.ts +57 -0
  50. package/dist/manifest.d.ts.map +1 -0
  51. package/dist/manifest.js +140 -0
  52. package/dist/manifest.js.map +1 -0
  53. package/dist/manifest.json +20960 -0
  54. package/dist/parse.d.ts +143 -0
  55. package/dist/parse.d.ts.map +1 -0
  56. package/dist/parse.js +509 -0
  57. package/dist/parse.js.map +1 -0
  58. package/dist/print.d.ts +42 -0
  59. package/dist/print.d.ts.map +1 -0
  60. package/dist/print.js +80 -0
  61. package/dist/print.js.map +1 -0
  62. package/dist/prompts.d.ts +58 -0
  63. package/dist/prompts.d.ts.map +1 -0
  64. package/dist/prompts.js +124 -0
  65. package/dist/prompts.js.map +1 -0
  66. package/dist/repl.d.ts +34 -0
  67. package/dist/repl.d.ts.map +1 -0
  68. package/dist/repl.js +101 -0
  69. package/dist/repl.js.map +1 -0
  70. package/dist/server-mode.d.ts +37 -0
  71. package/dist/server-mode.d.ts.map +1 -0
  72. package/dist/server-mode.js +167 -0
  73. package/dist/server-mode.js.map +1 -0
  74. package/package.json +97 -3
  75. package/src/args.ts +440 -0
  76. package/src/ask.ts +119 -0
  77. package/src/bin.ts +11 -0
  78. package/src/cli.ts +600 -0
  79. package/src/client-mode.ts +90 -0
  80. package/src/ecosystem.ts +260 -0
  81. package/src/failure.ts +47 -0
  82. package/src/format.ts +177 -0
  83. package/src/index.ts +31 -0
  84. package/src/lockfile.ts +85 -0
  85. package/src/manifest-build.ts +12 -0
  86. package/src/manifest.ts +162 -0
  87. package/src/parse.ts +670 -0
  88. package/src/print.ts +108 -0
  89. package/src/prompts.ts +181 -0
  90. package/src/repl.ts +121 -0
  91. package/src/server-mode.ts +205 -0
package/README.md CHANGED
@@ -1,3 +1,800 @@
1
- # Temporary Holding Version
1
+ # @procedures/mark
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm](https://img.shields.io/npm/v/@procedures/mark?color=2346c4&label=npm)](https://www.npmjs.com/package/@procedures/mark)
4
+
5
+ ```sh
6
+ npm install --global @procedures/mark
7
+ ```
8
+
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
+
11
+ > Mark CLI - Development workflow automation through dynamic procedure discovery
12
+
13
+ ## Overview
14
+
15
+ `@procedures/mark` is a dynamic, extensible CLI that automatically discovers and executes procedures from ecosystem packages. It provides a unified interface for development workflows, package management, and automation tasks across the `@mark1russell7` ecosystem.
16
+
17
+ ### Key Features
18
+
19
+ - **Dynamic Procedure Discovery**: Automatically loads procedures from ecosystem packages
20
+ - **Self-Documenting**: Generates help text from procedure schemas
21
+ - **Type-Safe**: Leverages Zod schemas for runtime validation
22
+ - **Multiple Output Formats**: text, json, table, streaming with spinners
23
+ - **Workspace-Aware**: Loads each package of its pnpm workspace that declares `client.procedures`
24
+ - **Extensible**: New commands added by installing procedure packages
25
+
26
+ ## Architecture
27
+
28
+ ```mermaid
29
+ graph TB
30
+ subgraph "User Layer"
31
+ CMD[Command Line<br/>mark lib new pkg]
32
+ end
33
+
34
+ subgraph "CLI Layer"
35
+ PARSER[Argument Parser]
36
+ ROUTER[Command Router]
37
+ FORMATTER[Output Formatter]
38
+ end
39
+
40
+ subgraph "Discovery Layer"
41
+ MANIFEST[pnpm workspace<br/>packages/*]
42
+ DISCOVER[Package Discovery]
43
+ LOADER[Dynamic Importer]
44
+ end
45
+
46
+ subgraph "Execution Layer"
47
+ REGISTRY[Procedure Registry]
48
+ TRANSPORT[Local Transport]
49
+ CLIENT[Client]
50
+ end
51
+
52
+ subgraph "Procedure Packages"
53
+ P1[client-lib]
54
+ P2[client-git]
55
+ P3[client-pnpm]
56
+ P4[client-shell]
57
+ end
58
+
59
+ CMD --> PARSER
60
+ PARSER --> ROUTER
61
+ ROUTER --> DISCOVER
62
+ MANIFEST --> DISCOVER
63
+ DISCOVER --> LOADER
64
+ LOADER --> P1
65
+ LOADER --> P2
66
+ LOADER --> P3
67
+ LOADER --> P4
68
+ P1 --> REGISTRY
69
+ P2 --> REGISTRY
70
+ P3 --> REGISTRY
71
+ P4 --> REGISTRY
72
+ ROUTER --> CLIENT
73
+ CLIENT --> TRANSPORT
74
+ TRANSPORT --> REGISTRY
75
+ REGISTRY --> FORMATTER
76
+ FORMATTER --> CMD
77
+
78
+ style CMD fill:#e1f5ff
79
+ style MANIFEST fill:#fff3e0
80
+ style REGISTRY fill:#c8e6c9
81
+ style FORMATTER fill:#f8bbd0
82
+ ```
83
+
84
+ ## Installation
85
+
86
+ `mark` is the package `packages/mark` of the `client` monorepo. Build the workspace, then run the CLI:
87
+
88
+ ```bash
89
+ pnpm install && pnpm build
90
+ node packages/mark/dist/cli.js --help
91
+ ```
92
+
93
+ ### Prerequisites
94
+
95
+ - Node.js >= 22.0.0
96
+ - pnpm (the version of the workspace `packageManager` field)
97
+
98
+ ## Execution Flow
99
+
100
+ ```mermaid
101
+ sequenceDiagram
102
+ participant User
103
+ participant CLI
104
+ participant Discovery
105
+ participant Registry
106
+ participant Procedure
107
+ participant Formatter
108
+
109
+ User->>CLI: mark lib new my-pkg
110
+ CLI->>Discovery: Load ecosystem packages
111
+ Discovery->>Registry: Import & register procedures
112
+ Registry-->>CLI: Procedures ready
113
+ CLI->>CLI: Parse arguments
114
+ CLI->>Registry: Find procedure ["lib", "new"]
115
+ Registry-->>CLI: Return procedure
116
+ CLI->>CLI: Parse input from args + schema
117
+ CLI->>Procedure: Execute with validated input
118
+ Procedure-->>CLI: Return result
119
+ CLI->>Formatter: Format output
120
+ Formatter-->>User: Display result
121
+ ```
122
+
123
+ ## API Reference
124
+
125
+ ### CLI Binary
126
+
127
+ The package provides a `mark` binary for command-line usage.
128
+
129
+ #### Basic Syntax
130
+
131
+ ```bash
132
+ mark [global options] <path...> [positional...] [--options]
133
+ mark serve [--procedures pkgs] [--port N] [--token T | --no-token] [--expose patterns]
134
+ mark mcp [--bundles list] [--tool-timeout ms] [--debug]
135
+ mark --server [--port N] [--host H] [--transport http|websocket|both]
136
+ mark -i
137
+ ```
138
+
139
+ The bin is `dist/bin.js`. `node dist/cli.js` also runs the CLI. Importing `@procedures/mark` runs nothing.
140
+
141
+ #### The hosts
142
+
143
+ `mark` is the one command that a user needs. It starts each host of the ecosystem:
144
+
145
+ | Command | Host | Notes |
146
+ |---|---|---|
147
+ | `mark <command>` | This process | The registry has the module of each workspace package with `client.procedures`. |
148
+ | `mark --server` | The CLI server | The same registry, on `127.0.0.1`, with a token in the lockfile. A later `mark` command in the same folder uses it. |
149
+ | `mark serve` | The procedure server | The options of the `procedure-server` command. A package of `--procedures` resolves from the current folder, then from the folder of `mark`. A token is necessary by default. |
150
+ | `mark mcp` | The dev-tools MCP server, over stdio | The server of Claude Code's MCP configuration. stdout holds only protocol lines, and `mark mcp --help` writes to stderr. |
151
+
152
+ `mark mcp <procedure>` runs an `mcp.*` procedure (for example `mark mcp listTools`), not the server. Each host gets its policy from `hostPolicy()` of `@procedures/client`.
153
+
154
+ #### Global Options
155
+
156
+ A flag before the command path is a global flag. After the path, a flag is the command's flag when the command declares it. Otherwise it is a global flag. For example, `mark docker compose down -v` gives `-v` (volumes) to the command. To show the version, write `mark -v`. `mark lib audit` lists the command flags that hide a global flag.
157
+
158
+ | Option | Short | Description |
159
+ |--------|-------|-------------|
160
+ | `--help` | `-h` | Show help for command or group |
161
+ | `--version` | `-v` | Show CLI version |
162
+ | `--verbose` | `-V` | Enable verbose output during discovery |
163
+ | `--format <format>` | `-f` | Override output format (text, json, table, streaming) |
164
+ | `--local` | | Run the command in this process, not on the CLI server |
165
+ | `--json <json>` | | Execute raw procedure reference |
166
+ | `--server` | | Start the CLI server (with `--port`, `--host`, `--transport`) |
167
+ | `--interactive` | `-i` | Start the REPL |
168
+
169
+ #### Exit Codes and Failures
170
+
171
+ `mark` exits with code 1 when:
172
+
173
+ - the command is unknown, or the command line has a problem (an unknown flag, a missing value, an extra argument);
174
+ - the input is not valid for the procedure;
175
+ - the procedure throws an error;
176
+ - the result reports a failure.
177
+
178
+ A procedure reports a failure in one of three ways:
179
+
180
+ 1. It throws an error. This is the usual way.
181
+ 2. It returns an object with `success: false`. Use this when the result has more to show, for example the problems that `lib audit` found.
182
+ 3. It returns an object with an `exitCode` that is not 0. Use this for a procedure that runs an external command, for example `vitest run`.
183
+
184
+ For a streaming procedure, `mark` examines each item. One failed item makes the command fail.
185
+
186
+ #### Examples
187
+
188
+ ```bash
189
+ # Show all available commands
190
+ mark
191
+
192
+ # Show help for a command group
193
+ mark lib --help
194
+
195
+ # Show help for specific command
196
+ mark lib new --help
197
+
198
+ # Execute command with options
199
+ mark lib new my-package --preset lib
200
+
201
+ # Override output format
202
+ mark git status --format json
203
+
204
+ # A global flag before the command (docker ps has its own --format)
205
+ mark --format json docker ps
206
+
207
+ # Verbose discovery
208
+ mark --verbose lib new my-pkg
209
+ ```
210
+
211
+ ### Programmatic API
212
+
213
+ #### `run(argv: string[]): Promise<void>`
214
+
215
+ Main CLI entry point.
216
+
217
+ ```typescript
218
+ import { run } from "@procedures/mark";
219
+
220
+ // Execute CLI programmatically
221
+ await run(["lib", "new", "my-package"]);
222
+ ```
223
+
224
+ #### `parseFromSchema(params, meta, schema?): Record<string, unknown>`
225
+
226
+ Parse CLI arguments based on procedure metadata. With the schema, each value is converted by the type of its field. `parseCommandLine(argv, procedures)` splits a whole command line: the global flags, the path and the input.
227
+
228
+ ```typescript
229
+ import { parseFromSchema } from "@procedures/mark";
230
+
231
+ const params = {
232
+ array: ["my-package"],
233
+ options: { description: "My pkg" }
234
+ };
235
+
236
+ const meta = {
237
+ args: ["name"],
238
+ shorts: { description: "d" }
239
+ };
240
+
241
+ const input = parseFromSchema(params, meta);
242
+ // { name: "my-package", description: "My pkg" }
243
+ ```
244
+
245
+ #### `generateHelp(path, meta, schema?): string`
246
+
247
+ Generate help text for a procedure.
248
+
249
+ ```typescript
250
+ import { generateHelp } from "@procedures/mark";
251
+
252
+ const help = generateHelp(
253
+ ["lib", "new"],
254
+ {
255
+ description: "Create a new library package",
256
+ args: ["name"],
257
+ shorts: { skipGit: "g" }
258
+ },
259
+ schema
260
+ );
261
+
262
+ console.log(help);
263
+ ```
264
+
265
+ #### `formatOutput(print, result, format): void`
266
+
267
+ Format and print procedure results.
268
+
269
+ ```typescript
270
+ import { formatOutput } from "@procedures/mark";
271
+
272
+ // print: an object with info, error, success, warning, table and spin (see format.ts)
273
+ formatOutput(print, { message: "Success!" }, "text");
274
+ formatOutput(print, { rows: [...] }, "table");
275
+ formatOutput(print, { data: {...} }, "json");
276
+ ```
277
+
278
+ ### CLI Metadata Interface
279
+
280
+ Procedures define CLI behavior via metadata:
281
+
282
+ ```typescript
283
+ interface CLIMeta {
284
+ /** Description for help text */
285
+ description?: string;
286
+
287
+ /** Field names that are positional args (in order) */
288
+ args?: string[];
289
+
290
+ /** Short flag mappings: { fieldName: "f" } */
291
+ shorts?: Record<string, string>;
292
+
293
+ /** Output format hint */
294
+ output?: "text" | "json" | "table" | "streaming";
295
+
296
+ /** Whether to prompt for missing required fields */
297
+ interactive?: boolean;
298
+ }
299
+ ```
300
+
301
+ ### Questions for missing arguments
302
+
303
+ In a terminal, mark asks for each required argument that the command line does not give. The question shows the name, the type and the description of the field. mark does not ask in these cases:
304
+
305
+ - stdin or stdout is not a terminal;
306
+ - the environment variable `CI` is set;
307
+ - the command line has `--no-input` before the command;
308
+ - the procedure sets `interactive: false`.
309
+
310
+ Then a missing argument is an error of the input (exit code 1).
311
+
312
+ ### Output Format Types
313
+
314
+ ```typescript
315
+ type OutputFormat = "text" | "json" | "table" | "streaming";
316
+ ```
317
+
318
+ ## Ecosystem Discovery
319
+
320
+ ### The command manifest
321
+
322
+ The build of mark writes `dist/manifest.json`. It holds the path, the CLI metadata, the input fields and the JSON Schema of each command. mark reads it first, so the help and a call to the CLI server do not load the procedures. The procedures load only when a command runs in this process.
323
+
324
+ - A fingerprint records the build of each package that has procedures (its `dist/.tsbuildinfo`). When a package is built again, added or removed, the manifest is stale. Then mark loads the procedures as before and writes the manifest again.
325
+ - mark depends on each package that has procedures, so `pnpm build` builds mark after them. A test fails when a package is missing from the dependencies of mark, or when the manifest after the build does not match the registry.
326
+
327
+ Measured on Windows with Node 24 (median of 5 runs, the same machine load):
328
+
329
+ | Command | Before | After |
330
+ |---|---:|---:|
331
+ | `mark --help` | 1127 ms | 164 ms |
332
+ | `mark git status` (CLI server) | 1170 ms | 352 ms |
333
+ | `mark git status --local` | 1141 ms | 1027 ms |
334
+
335
+ ### Discovery of the procedures
336
+
337
+ The registry of `mark` is a registry of its own (`loadEcosystemRegistry()`). For each workspace package with `client.procedures`, `mark` imports its `./module` entry, which registers nothing, and puts its procedures in that registry with `createRegistry()`. A package without a `./module` entry still works: `mark` imports its procedures file and takes the procedures that the import adds to the global registry.
338
+
339
+ The CLI uses a multi-step discovery process to find and load procedures:
340
+
341
+ ### Discovery Flow
342
+
343
+ ```mermaid
344
+ graph TB
345
+ START[CLI Starts]
346
+ MANIFEST[Find the workspace root:<br/>pnpm-workspace.yaml]
347
+ PACKAGES[Iterate packages/*]
348
+ PKGJSON[Read package.json]
349
+ CHECK{Has client<br/>.procedures?}
350
+ IMPORT[Dynamic import<br/>procedures file]
351
+ REGISTER[Auto-register to<br/>PROCEDURE_REGISTRY]
352
+ READY[Procedures Ready]
353
+ SKIP[Skip package]
354
+
355
+ START --> MANIFEST
356
+ MANIFEST --> PACKAGES
357
+ PACKAGES --> PKGJSON
358
+ PKGJSON --> CHECK
359
+ CHECK -->|Yes| IMPORT
360
+ CHECK -->|No| SKIP
361
+ IMPORT --> REGISTER
362
+ REGISTER --> READY
363
+ SKIP --> PACKAGES
364
+
365
+ style START fill:#e3f2fd
366
+ style MANIFEST fill:#fff3e0
367
+ style REGISTER fill:#c8e6c9
368
+ style READY fill:#a5d6a7
369
+ ```
370
+
371
+ ### Package Structure
372
+
373
+ For a package to be discovered, it must:
374
+
375
+ 1. Be a folder in `packages/` of the workspace that holds this CLI
376
+ 2. Have a `client.procedures` field in package.json
377
+ 3. Export procedure registrations from that file
378
+
379
+ A package that does not load gives one warning line. A package that is not built yet is reported only with `--verbose`.
380
+
381
+ **Example package.json:**
382
+ ```json
383
+ {
384
+ "name": "@procedures/lib",
385
+ "client": {
386
+ "procedures": "./dist/register.js"
387
+ }
388
+ }
389
+ ```
390
+
391
+ **Example register.js:**
392
+ ```typescript
393
+ import { defineProcedure } from "@procedures/client";
394
+ import { z } from "zod";
395
+
396
+ const libNew = defineProcedure({
397
+ path: ["lib", "new"],
398
+ input: z.object({
399
+ name: z.string(),
400
+ }),
401
+ handler: async (input, ctx) => {
402
+ // Implementation
403
+ return { message: "Created!" };
404
+ },
405
+ metadata: {
406
+ description: "Create a new library",
407
+ args: ["name"],
408
+ },
409
+ });
410
+
411
+ // Auto-registers when imported
412
+ ```
413
+
414
+ ### Discovery Sources
415
+
416
+ | Source | Path | Description |
417
+ |--------|------|-------------|
418
+ | Workspace | `<root>/pnpm-workspace.yaml`, `<root>/packages/*` | The packages that `mark` examines |
419
+ | Package Metadata | `<pkg>/package.json` | Contains `client.procedures` path |
420
+ | Procedure Registry | `PROCEDURE_REGISTRY` | Global registry from `@procedures/client` |
421
+
422
+ ## Argument Parsing
423
+
424
+ The CLI uses a smart parser that detects procedure paths and maps arguments correctly.
425
+
426
+ ### Path Detection
427
+
428
+ ```mermaid
429
+ graph LR
430
+ INPUT[mark lib new pkg<br/>--desc test]
431
+ PATH[Path: lib new]
432
+ ARGS[Positional: pkg]
433
+ OPTS[Options: desc=test]
434
+
435
+ INPUT --> PATH
436
+ INPUT --> ARGS
437
+ INPUT --> OPTS
438
+
439
+ style PATH fill:#e1f5ff
440
+ style ARGS fill:#fff3e0
441
+ style OPTS fill:#f8bbd0
442
+ ```
443
+
444
+ ### Parsing Rules
445
+
446
+ 1. **Path Segments**: Consumed until a procedure match is found.
447
+ 2. **Flags**: A flag is a field of the input schema (`--skip-git` or `--skipGit`), or the short letter of the metadata (`-m`). A flag that the procedure does not declare is a global flag. Otherwise it is an error.
448
+ 3. **Types**: Each value is converted by the type of its field. A string field keeps its text: `mark fs exists 2024` gives the path `"2024"`, not a number. A number field needs a number. An object field, or a field with no type, takes JSON.
449
+ 4. **Boolean Flags**: A boolean flag alone is `true`. It also takes `true` or `false` after it, `--flag=false`, or `--no-flag`.
450
+ 5. **Arrays and Records**: A repeated array flag collects its values (`--tag a --tag b`). A record flag takes `key=value` and can be repeated (`--env A=1 --env B=2`). Both also take JSON.
451
+ 6. **Positional Args**: The fields of `args` take the arguments in order. A last array field takes the rest. An argument that no field takes is an error.
452
+ 7. **Values**: A negative number is a value (`--count -5`). `--` ends the flags: the tokens after it are arguments. Short boolean flags can be grouped (`-it`). A short flag can have its value attached (`-n5`).
453
+
454
+ ### Examples
455
+
456
+ ```bash
457
+ # Simple command
458
+ mark lib new my-package
459
+ # path: ["lib", "new"]
460
+ # input: { name: "my-package" }
461
+
462
+ # With options
463
+ mark lib new my-package --preset lib --dry-run
464
+ # input: { name: "my-package", preset: "lib", dryRun: true }
465
+
466
+ # A boolean flag with a value
467
+ mark git commit --message "message" --amend false
468
+ # input: { message: "message", amend: false }
469
+
470
+ # Short flags (the metadata of git push maps -f to force and -u to setUpstream)
471
+ mark git push -fu
472
+ # input: { force: true, setUpstream: true }
473
+
474
+ # Arguments that look like flags, after --
475
+ mark docker exec box -- ls -la /tmp
476
+ # input: { container: "box", command: ["ls", "-la", "/tmp"] }
477
+ ```
478
+
479
+ ### Metadata-Based Mapping
480
+
481
+ ```typescript
482
+ // Procedure metadata
483
+ metadata: {
484
+ args: ["name", "version"], // Positional mapping
485
+ shorts: { skipGit: "g" }, // Short flag mapping
486
+ }
487
+
488
+ // Command
489
+ // mark lib new my-pkg 1.0.0 -g
490
+
491
+ // Parsed input
492
+ {
493
+ name: "my-pkg",
494
+ version: "1.0.0",
495
+ skipGit: true
496
+ }
497
+ ```
498
+
499
+ ## Output Formatting
500
+
501
+ The CLI supports multiple output formats for different use cases.
502
+
503
+ ### Text Format
504
+
505
+ Default format for human-readable output.
506
+
507
+ ```typescript
508
+ // Result with message
509
+ { message: "Package created successfully!" }
510
+ // Output: Package created successfully!
511
+
512
+ // Result with output field
513
+ { output: "file contents..." }
514
+ // Output: file contents...
515
+
516
+ // Result with error
517
+ { success: false, message: "Failed to create" }
518
+ // Output: [ERROR] Failed to create
519
+ ```
520
+
521
+ ### JSON Format
522
+
523
+ Structured output for programmatic consumption.
524
+
525
+ ```bash
526
+ mark lib scan --format json
527
+ ```
528
+
529
+ ```json
530
+ {
531
+ "rootPath": "/path/to/client",
532
+ "packages": { "@procedures/lib": { "name": "@procedures/lib" } }
533
+ }
534
+ ```
535
+
536
+ ### Table Format
537
+
538
+ Tabular data display.
539
+
540
+ ```bash
541
+ mark procedure list --format table
542
+ ```
543
+
544
+ ```
545
+ path description
546
+ fs read Read file contents
547
+ fs write Write content to file
548
+ ```
549
+
550
+ ### Streaming Format
551
+
552
+ Shows a spinner during execution, then displays the result.
553
+
554
+ ```bash
555
+ mark lib audit --format streaming
556
+ # Running lib audit...
557
+ # lib audit complete
558
+ # Result displayed
559
+ ```
560
+
561
+ ## Integration with the Workspace
562
+
563
+ `mark` loads each package of its pnpm workspace that has a `client.procedures` field in its `package.json`. The field names the built module that registers the procedures.
564
+
565
+ ### Procedure Packages
566
+
567
+ Each client package can export procedures:
568
+
569
+ ```
570
+ packages/client-lib/
571
+ ├── src/
572
+ │ ├── procedures/lib/new.ts
573
+ │ ├── procedures/lib/audit.ts
574
+ │ └── register.ts # Registers all procedures
575
+ ├── dist/
576
+ │ └── register.js # Built procedures
577
+ └── package.json
578
+ └── client.procedures: "./dist/register.js"
579
+ ```
580
+
581
+ ### Transport Layer
582
+
583
+ ```mermaid
584
+ graph LR
585
+ CLI[CLI]
586
+ CLIENT[Client]
587
+ TRANSPORT[LocalTransport]
588
+ REGISTRY[ProcedureRegistry]
589
+ HANDLER[Procedure Handler]
590
+
591
+ CLI --> CLIENT
592
+ CLIENT --> TRANSPORT
593
+ TRANSPORT --> REGISTRY
594
+ REGISTRY --> HANDLER
595
+ HANDLER --> TRANSPORT
596
+ TRANSPORT --> CLIENT
597
+ CLIENT --> CLI
598
+
599
+ style CLI fill:#e1f5ff
600
+ style CLIENT fill:#c8e6c9
601
+ style TRANSPORT fill:#fff3e0
602
+ style HANDLER fill:#f8bbd0
603
+ ```
604
+
605
+ ## Available Commands
606
+
607
+ Commands are discovered dynamically. Common procedure packages include:
608
+
609
+ ### client-lib
610
+
611
+ Package management and scaffolding:
612
+ - `mark lib new <name>` - Create new package
613
+ - `mark lib scan` - List the workspace packages
614
+ - `mark lib audit` - Check the packages against the template
615
+
616
+ ### client-git
617
+
618
+ Git operations:
619
+ - `mark git status` - Show git status
620
+ - `mark git commit` - Create commit
621
+ - `mark git push` - Push to remote
622
+
623
+ ### client-pnpm
624
+
625
+ Package manager operations:
626
+ - `mark pnpm install` - Install dependencies
627
+ - `mark pnpm add <pkg>` - Add package
628
+ - `mark pnpm remove <pkg>` - Remove package
629
+
630
+ ### client-shell
631
+
632
+ Shell command execution:
633
+ - `mark shell run <command>` - Run shell command
634
+ - `mark shell exec <command>` - Execute command
635
+ - `mark shell which <binary>` - Find binary in PATH
636
+
637
+ ### client-procedure
638
+
639
+ Procedure management:
640
+ - `mark procedure new <path>` - Create new procedure
641
+ - `mark procedure list` - List all procedures
642
+
643
+ ## Advanced Usage
644
+
645
+ ### Procedure References
646
+
647
+ Execute procedures via JSON for advanced scripting:
648
+
649
+ ```bash
650
+ # Execute procedure reference
651
+ mark --json '{
652
+ "$proc": ["lib", "new"],
653
+ "input": {
654
+ "name": "my-package",
655
+ "dryRun": true
656
+ }
657
+ }'
658
+ ```
659
+
660
+ ### Chaining Commands
661
+
662
+ Use standard shell pipes. A failed command exits with code 1, so `&&` stops at it:
663
+
664
+ ```bash
665
+ # Get package list and filter
666
+ mark --format json lib scan | jq '.packages | keys'
667
+
668
+ # Create a package, then audit the workspace
669
+ mark lib new my-pkg && mark lib audit
670
+ ```
671
+
672
+ ### Environment Variables
673
+
674
+ ```bash
675
+ # The home folder holds the lockfiles of the CLI servers (~/.mark)
676
+ export HOME=/custom/path
677
+ ```
678
+
679
+ ## Development
680
+
681
+ ### Building
682
+
683
+ ```bash
684
+ pnpm --filter @procedures/mark build
685
+ ```
686
+
687
+ ### Testing
688
+
689
+ ```bash
690
+ pnpm --filter @procedures/mark test # unit tests (src/**/*.test.ts)
691
+ pnpm --filter @procedures/mark test:e2e # end-to-end tests: they run dist/bin.js as a process
692
+ ```
693
+
694
+ The end-to-end tests use a temporary home folder and a temporary workspace. They do not change the repository and do not use a running CLI server.
695
+
696
+ There is no container image of `mark`: the CLI server serves the workspace of the folder where it starts, on loopback, with a token in the lockfile of the user. A container cannot give that. To serve procedures over the network, use the `server` package.
697
+
698
+ ### Project Structure
699
+
700
+ ```
701
+ packages/mark/
702
+ ├── src/
703
+ │ ├── bin.ts # The mark executable
704
+ │ ├── cli.ts # Main CLI implementation (also runnable as dist/cli.js)
705
+ │ ├── args.ts # Command line splitting: global flags, path, procedure flags
706
+ │ ├── parse.ts # Field types, value conversion and help text
707
+ │ ├── failure.ts # Procedure-level failure and the exit code
708
+ │ ├── format.ts # Output formatters
709
+ │ ├── ecosystem.ts # Discovery system
710
+ │ └── index.ts # Public API (importing it runs nothing)
711
+ ├── e2e/ # End-to-end tests
712
+ ├── test/ # Test procedures for the unit tests
713
+ └── package.json
714
+ └── bin: { "mark": "./dist/bin.js" }
715
+ ```
716
+
717
+ ### Key Modules
718
+
719
+ #### cli.ts
720
+
721
+ Main CLI implementation:
722
+ - Command routing
723
+ - Procedure execution
724
+ - Help generation
725
+ - Error handling
726
+
727
+ #### parse.ts
728
+
729
+ Argument parsing and schema introspection:
730
+ - `parseFromSchema()` - Parse args based on metadata
731
+ - `generateHelp()` - Generate help text
732
+ - `extractSchemaFields()` - Introspect Zod schemas
733
+
734
+ #### format.ts
735
+
736
+ Output formatting:
737
+ - `formatOutput()` - Format results based on type
738
+ - `formatText()` - Plain text output
739
+ - `formatTable()` - Tabular output
740
+
741
+ #### ecosystem.ts
742
+
743
+ Procedure discovery:
744
+ - `discoverFromEcosystem()` - Find procedure packages
745
+ - `loadEcosystemProcedures()` - Load and register procedures
746
+ - `listEcosystemProcedurePackages()` - List available packages
747
+
748
+ ## Troubleshooting
749
+
750
+ ### Procedure Not Found
751
+
752
+ ```
753
+ Unknown command: mark foo bar
754
+ ```
755
+
756
+ **Solution:**
757
+ 1. Check that the package is in `packages/` of the workspace
758
+ 2. Verify the package has `client.procedures` in package.json
759
+ 3. Ensure the package is built (`pnpm build` in the workspace root)
760
+ 4. Use `mark --verbose` to see which packages are being loaded
761
+
762
+ ### Import Error
763
+
764
+ ```
765
+ mark: the procedures of @procedures/foo did not load: <message>
766
+ ```
767
+
768
+ **Solution:**
769
+ 1. Check that the procedures file exists at the specified path
770
+ 2. Verify the file is valid JavaScript (not TypeScript)
771
+ 3. Build the workspace: `pnpm build`
772
+
773
+ ### Help Not Showing Schema Fields
774
+
775
+ ```
776
+ # Only shows basic help without field details
777
+ ```
778
+
779
+ **Solution:** Ensure procedures have Zod schemas defined in the `input` field. The CLI introspects schemas to generate detailed help.
780
+
781
+ ## Dependencies
782
+
783
+ The CLI depends on:
784
+
785
+ - `@procedures/client` - Client library and procedure system
786
+ - `@procedures/*` - Various procedure packages
787
+ - `chalk`, `ora`, `cli-table3` - Terminal output
788
+ - `zod` - Schema validation
789
+
790
+ ## License
791
+
792
+ MIT
793
+
794
+ ## Repository
795
+
796
+ https://github.com/mark1russell7/cli
797
+
798
+ ## Author
799
+
800
+ Mark Russell <marktheprogrammer17@gmail.com>