@procedures/mark 0.0.0-stage → 0.1.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.
- package/CHANGELOG.md +5 -0
- package/LICENSE +21 -0
- package/README.md +799 -2
- package/dist/.tsbuildinfo +1 -0
- package/dist/args.d.ts +77 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +351 -0
- package/dist/args.js.map +1 -0
- package/dist/ask.d.ts +48 -0
- package/dist/ask.d.ts.map +1 -0
- package/dist/ask.js +91 -0
- package/dist/ask.js.map +1 -0
- package/dist/bin.d.ts +9 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +10 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli.d.ts +61 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +530 -0
- package/dist/cli.js.map +1 -0
- package/dist/client-mode.d.ts +21 -0
- package/dist/client-mode.d.ts.map +1 -0
- package/dist/client-mode.js +75 -0
- package/dist/client-mode.js.map +1 -0
- package/dist/ecosystem.d.ts +66 -0
- package/dist/ecosystem.d.ts.map +1 -0
- package/dist/ecosystem.js +211 -0
- package/dist/ecosystem.js.map +1 -0
- package/dist/failure.d.ts +16 -0
- package/dist/failure.d.ts.map +1 -0
- package/dist/failure.js +47 -0
- package/dist/failure.js.map +1 -0
- package/dist/format.d.ts +30 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +125 -0
- package/dist/format.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/lockfile.d.ts +26 -0
- package/dist/lockfile.d.ts.map +1 -0
- package/dist/lockfile.js +68 -0
- package/dist/lockfile.js.map +1 -0
- package/dist/manifest-build.d.ts +7 -0
- package/dist/manifest-build.d.ts.map +1 -0
- package/dist/manifest-build.js +11 -0
- package/dist/manifest-build.js.map +1 -0
- package/dist/manifest.d.ts +57 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +140 -0
- package/dist/manifest.js.map +1 -0
- package/dist/manifest.json +20885 -0
- package/dist/parse.d.ts +143 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +509 -0
- package/dist/parse.js.map +1 -0
- package/dist/print.d.ts +42 -0
- package/dist/print.d.ts.map +1 -0
- package/dist/print.js +80 -0
- package/dist/print.js.map +1 -0
- package/dist/prompts.d.ts +58 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +124 -0
- package/dist/prompts.js.map +1 -0
- package/dist/repl.d.ts +34 -0
- package/dist/repl.d.ts.map +1 -0
- package/dist/repl.js +101 -0
- package/dist/repl.js.map +1 -0
- package/dist/server-mode.d.ts +37 -0
- package/dist/server-mode.d.ts.map +1 -0
- package/dist/server-mode.js +167 -0
- package/dist/server-mode.js.map +1 -0
- package/package.json +97 -3
- package/src/args.ts +440 -0
- package/src/ask.ts +119 -0
- package/src/bin.ts +11 -0
- package/src/cli.ts +596 -0
- package/src/client-mode.ts +90 -0
- package/src/ecosystem.ts +258 -0
- package/src/failure.ts +47 -0
- package/src/format.ts +177 -0
- package/src/index.ts +31 -0
- package/src/lockfile.ts +85 -0
- package/src/manifest-build.ts +12 -0
- package/src/manifest.ts +162 -0
- package/src/parse.ts +670 -0
- package/src/print.ts +108 -0
- package/src/prompts.ts +181 -0
- package/src/repl.ts +121 -0
- package/src/server-mode.ts +205 -0
package/README.md
CHANGED
|
@@ -1,3 +1,800 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @procedures/mark
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@procedures/mark)
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm install --global @procedures/mark
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
[](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>
|