@davidorex/pi-context-cli 0.30.0 → 0.32.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 +46 -0
- package/README.md +138 -8
- package/dist/bin.js +0 -0
- package/dist/cli.d.ts +118 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +584 -30
- package/dist/cli.js.map +1 -1
- package/dist/pi-bound.d.ts +119 -0
- package/dist/pi-bound.d.ts.map +1 -0
- package/dist/pi-bound.js +257 -0
- package/dist/pi-bound.js.map +1 -0
- package/dist/render.d.ts +57 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +95 -0
- package/dist/render.js.map +1 -0
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,52 @@ All notable changes to this package are documented here. Format follows [Keep a
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.32.0] - 2026-07-05
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- `npm run promote:cli` (monorepo root) installs the operator `pi-context` binary as a publish-free LOCAL PACKED COPY of the working tree: it builds the tree, `npm pack`s the `@davidorex` workspace set, and co-installs that packed set into the global npm prefix in one `npm i -g --force`, so the operator resolves the current working-tree code (not the registry release) and a subsequent dev build (`rm -rf dist && tsc`) no longer affects the operator command. The target prefix is overridable via `--prefix <dir>` or the `PROMOTE_PREFIX` env var to promote into a throwaway directory without touching the real global binary.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- `scripts/promote-cli.mjs` no longer refuses an inherited `npm_config_prefix` in its real-global arm. `npm run promote:cli` (the documented invocation) itself exports `npm_config_prefix=<true global>` into the script env, so the prior refuse-on-inherited block (`resolveTargetPrefix()` exiting non-zero whenever any `npm_config_prefix` was inherited) refused the tool's own normal happy path against the real global, while providing no protection the `cleanNpmEnv()` scrub does not — npm's own legitimate injection is indistinguishable at the env-var level from a hostile `npm --prefix x run` override. Real-global non-poisonability is now carried entirely by the `cleanNpmEnv()` scrub (every `npm_config_*` stripped) applied to BOTH the `npm prefix -g` probe and the `npm i -g --force` install, so the resolved and installed prefix reflect the true global regardless of any inherited value; `assertSafeTargetPrefix()`'s repo-containment validation remains the structural precondition before any destructive op.
|
|
14
|
+
|
|
15
|
+
## [0.31.0] - 2026-06-13
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- The CLI auto-supplied params (`writer`, `arrayKey` — filled after parse so a caller need not pass them) are now described by one internal source that drives all of: their exemption from the missing-required check, their bracketed-optional rendering in the per-op synopsis, and their `autoSupplied` annotation in the `--help` Flags block. Observable behavior is unchanged — these params remain optional to pass and auto-filled.
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- `pi-context context-install [--update]` reflects the new `context-install` op, listed under the top-level help's Substrate lifecycle group (after `context-accept-all`). It materializes the schemas + starter blocks declared in `config.json`'s `installed_schemas` / `installed_blocks` from the package samples catalog and records the install baseline (`config.installed_from`). Default skip-if-exists; `--update` re-syncs existing installed schemas (migration-aware) and replaces empty blocks with the catalog starter, while populated block data is preserved. `authGated` (confirm or `--yes`/`--force`). The install ceremony was previously reachable only as the `/context install` slash command.
|
|
22
|
+
- `pi-context read-catalog-schema --kind <canonical_id>` reflects the new read-only op, listed under the top-level help's Read & query group. It fetches and prints the verbatim bundled catalog `*.schema.json` body for a named block kind (the raw JSON Schema — `properties` / `definitions` / `$id`, not the `read-samples-catalog` projection), so the body is diffable locally against the installed `<substrate>/schemas/<name>.schema.json` without hunting through `node_modules`. Under `--json` the raw schema text rides the op-result envelope as a string; on the default text surface it prints the bytes as-is. Package-intrinsic (reads the extension's bundled catalog, independent of any substrate); an unknown kind is a non-zero exit.
|
|
23
|
+
- `pi-context context-check-status` reflects the new read-only drift-report op, listed under the top-level help's Read & query group. It reports each installed schema's installed-vs-catalog drift state and, for each schema behind the catalog (`catalog-ahead` / `both-diverged`), which schema is behind and by what version gap — the baseline → catalog version pair under `version_delta` (a declared version bump) or a content-only basis when the catalog body moved with the version string unchanged. Under `--json` the structured `CheckStatusReport` (`perAsset` + `summary`) rides in the op-result envelope; writes nothing.
|
|
24
|
+
- `pi-context update`'s text surface now reports, on a schema blocked by items failing the catalog schema, that git-style failure markers were written INTO the block file(s) — the blocked report header and trailing guidance tell the operator to open each block file, fix the items between the `<<<<<<< BLOCKED …` / `>>>>>>> target: …` markers, then run `pi-context resolve-blocked --schemaName <name> --yes`. `pi-context resolve-blocked` strips the markers before re-validating the corrected block against the pinned target.
|
|
25
|
+
- `pi-context resolve-blocked --schemaName <name>` — the CLI now reflects the new `resolve-blocked` op, the commit step that resolves a schema `update` blocked. After `update` blocks a catalog-ahead resync (and persists a pending-blocked record), the calling agent fixes the block's failing items (or widens the local schema) and runs `resolve-blocked` to re-validate the corrected block against the pinned target and, on pass, write the target schema + advance the merge base so a subsequent `update` converges (in-sync) instead of re-blocking; on fail it reports the remaining per-item failures and writes nothing. Listed under the top-level help's Schema & config group; `authGated` (confirm or `--yes`/`--force`). The blocked-resync report `update` prints on the text surface now ends with the matching guidance line (fix the named items → `resolve-blocked --schemaName <name> --yes`).
|
|
26
|
+
- `pi-context update` now surfaces the per-schema blocked-resync diagnostic on its default text surface: when a catalog-ahead resync is refused, the CLI renders a readable report below the op's own output — a `blocked: <name> (<from> -> <to>)` header per blocked schema, then either a `no migration chain reaches <to> from <from>` line or, for a validation failure, one line per failing item naming the item id (or its `instancePath` when no id resolved), the field, and the constraint. Under `--json` the structured `blockedDetail` already rides in the op-result envelope — the report is not double-emitted there. An `update` with no blocked schemas, or any other op, is unaffected.
|
|
27
|
+
- `pi-context validate-block-items --block <name>` reflects the new read-only diagnostic op: it validates a block's items against the catalog schema version (forward-migrating in memory when the block lags the catalog version) and returns `{ block, from?, to?, valid, failures[] }` — each failure naming the item id, field, and constraint — without writing. An unknown block or a missing installed block file is a non-zero exit. Listed under the top-level help's Read & query group.
|
|
28
|
+
- `pi-context context-lens-view --lensId <id>` projects a config-declared lens (`config.lenses[]`) as a binned item-view. Without `--bin` it returns a bin→count summary (the count of items in each declared bin plus `uncategorized` and `total`, always under the read cap); with `--bin <name>` it returns that bin's items paged by `--offset`/`--limit` (`{ items, total, hasMore }`). Serves target, composition, and hand-curated lenses. An unknown lens id or an undeclared bin is a non-zero exit.
|
|
29
|
+
- Per-op `pi-context <op> --help` now renders a structured page: a one-line summary, a `SYNOPSIS` line (`pi-context <op>` with required flags first and optional flags bracketed — auto-injected `writer`/`arrayKey` shown bracketed-optional), the per-field `Flags` block (type tag, enum choices, required/optional, description; JSON-valued flags shown as `<json | @file>`), an `EXAMPLES` section of copy-pasteable invocations for that op, a `RELATED` section listing the sibling commands in the same help group, and a footer pointing to `pi-context --help` and the machine-readable form. **`pi-context <op> --help --format json`** (or `--help --json`) emits that same help as a machine-readable JSON model (`{ name, synopsis, summary, flags[], examples[], related[] }`).
|
|
30
|
+
- The top-level `pi-context --help` (and bare `pi-context`) is now grouped and scannable: the surfaced ops are partitioned into seven command classes — Read & query, Block writes, Relations, Schema & config, Substrate lifecycle, Workflow, and a Process modes section (`pi-bound`) — rendered read-before-mutate, alphabetically within each class, each row showing the op's short reflection summary truncated to a uniform width. The classification is derived from the reflected op set, so a newly surfaced op appears under its class automatically. A new **`pi-context --version`** (alias `-v`) prints the package version and exits.
|
|
31
|
+
- An ergonomic-affordance layer adds three CLI-side conveniences, leaving every in-pi op byte-unchanged: (1) **`--show-schema`** previews a block-mutation op's contract — array key, required-field set, every field with its type (and enum values when declared), and the id pattern — and exits before any write (read through the installed schema; exits `3` when the block's schema is absent). (2) **`append-block-item --dry-run`** (alias `--dryRun`) validates the prospective whole file (`{...existing, <arrayKey>: [...items, newItem]}` against the block schema — exactly what a real append validates) and writes nothing, replicating the op's `--autoId` allocation and reporting the id that would be allocated (`[dry-run] PASS — would append <id>`); it never invokes the append op, so the op stays frozen. The flag is a global flag (matched on both kebab and camel forms) only for an op that declares no `dryRun` param; ops with their own `dryRun` param keep their existing semantics. (3) **Granular exit codes** distinguish error classes: `0` success, `1` runtime error / declined authorization, `2` usage error, `3` schema-absent, `4` id-allocation failure, `5` schema validation. A validation failure that previously exited `1` now exits `5`.
|
|
32
|
+
- An output + error-shaping layer adds three CLI-side affordances at the emit/catch boundary, leaving every in-pi op byte-unchanged: (1) **`--format text|json|table`** selects the render — `text` (default) reproduces each op's prior human output, `json` is the existing `{ ok, op, output }` envelope (`--json` is now its exact alias), and `table` projects a renderable row array (a read whose body is a collection, or a data op returning an array) as a compact markdown table (`id`-first, ≤4 columns, cells one-lined + 80-char-capped); a result that is not a complete tabular collection (prose, a non-array value, or an over-cap read) falls back to `text` so a degenerate table never replaces the real output; an unknown `--format` value is a usage error (exit 2). (2) **Field-named validation guidance** — a schema-validation failure now surfaces which field and what constraint (e.g. `` `/gaps/0`: missing required field `description` ``, `expected string`, `must be one of …`, `unexpected property …`) on both the text and `--json` surfaces, instead of the raw validator phrasing. (3) **Whole-subtree addressed reads** — `read-schema --schemaName <name> --path <dotted.path>` and `read-config --registry <name> [--id <id>]` return the complete node at that address (all children included), 50KB-capped, instead of a paged slice of one of the node's arrays. The whole-subtree behavior is now sourced from the in-pi op itself (the op passes `whole: true` on the addressed read); the earlier CLI-side recompute that produced the same envelope is retired.
|
|
33
|
+
- A pre-call input layer adds four additive flag affordances over the schema-derived surface (the in-pi op schemas + handlers are unchanged — the ops still receive + require their declared params; the CLI normalizes/supplies them, and every schema-exact form keeps working): (1) **kebab-case flags** resolve to their camelCase op-schema keys (`--dry-run` → `--dryRun`); (2) **`--id`** aliases an op's single id-shaped param (`itemId`/`parentId`/`taskId`/`unitId`/…), rejecting as ambiguous when an op declares two (e.g. `complete-task`, `rename-canonical-id`); (3) **`--arrayKey` is derived** from `config.block_kinds[].array_key` for the seven block-mutation ops, so a caller passes only `--block <name>` (an explicit `--arrayKey` overrides); (4) three shorthands — **`--writer kind:id`** (`human:you@example.com` → `{"kind":"human","user":"you@example.com"}`, `agent:`→`agent_id`, `monitor:`→`monitor_name`, `workflow:`→`workflow_step_id`), **`--where field:op:value`** for the filter predicate (split on the first two colons; the value may contain colons), and **CSV `--op in`** (a comma-separated `--value a,b,c` becomes `["a","b","c"]`, argv-order-independent).
|
|
34
|
+
- `pi-context resolve-conflict --schemaName <name> [--schema <reconciled>]` — the CLI now reflects the new `resolve-conflict` op, the commit step that completes the caller-as-reconciler conflict path. After `update` surfaces a conflict and the calling agent reconciles it, `resolve-conflict` writes the reconciled body AND advances the merge base for that schema to the catalog, so a subsequent `update` converges (the schema reads as `locally-modified` and its merge takes the reconciled body) instead of re-reporting the same conflict — which a bare `write-schema` (not advancing the base) would do. Omit `--schema` to treat the current on-disk body as already reconciled and only advance the base. `authGated` (confirm or `--yes`/`--force`).
|
|
35
|
+
- `pi-context update` surfaces the irreconcilable 3-way-merge conflicts it produces (`UpdateResult.conflicts`) to the **calling agent** rather than spawning a subordinate session. Under `--json` the structured conflict set (per schema, each `{ path, base, ours, theirs }`) rides in the op-result envelope; on the default text surface the CLI additionally renders a readable per-schema conflict report ending with a reconcile-guidance line (reconcile the conflicting paths into a schema → `resolve-conflict --schemaName <name> --schema <reconciled>`). Because the CLI is an agent-invoked surface, the caller — already an interactive, credentialed agent holding the conflict set and the operator's authority — reconciles and commits via `resolve-conflict`; the CLI spawns nothing. The `update` op's own output is unchanged and still printed first; an `update` with no conflicts, or any other op, is unaffected.
|
|
36
|
+
- `pi-context pi-bound [--grant <id>]... [...pi flags]` — a CLI process mode that launches an interactive `pi` coding-agent session whose tool surface is constrained to the canonical bounded set: the static tools derived from the installed packages' generated `SKILL.md` files, plus pi's built-in read-only tools (`read`/`ls`/`grep`/`find`), plus the active substrate's declared bounded composites (`config.tool_operations[]`). Repeated `--grant <canonical_id>` scopes the composite surface to a subset (default: all declared); every other token passes through to `pi`. Runs from the target dir; re-derives the full allowlist and runs `pi install -l` on every launch, including `--continue`/`-c` resumes. Replaces the `scripts/launch-constrained-pi.sh` entrypoint.
|
|
37
|
+
- `pi-context update [--dryRun]` — a reflected op that brings the installed substrate model toward the current catalog: it consults the per-schema drift classification, resyncs `in-sync`/`catalog-ahead` schemas, and **refuses to overwrite** a `locally-modified`/`both-diverged` schema, reporting it instead (a user's schema edits are never silently clobbered). `--dryRun` previews the per-schema action plan and writes nothing.
|
|
38
|
+
|
|
39
|
+
### Removed
|
|
40
|
+
- The CLI-side addressed-read override (the post-`op.run` block that re-addressed the node off a fresh source load and re-wrapped it with `whole: true` for `read-schema --path` / `read-config --registry [--id]`) is retired. The in-pi op now returns the whole addressed subtree itself, so both surfaces — the op and the CLI — produce the whole-subtree envelope from the single op source; the CLI no longer recomputes it. The now-unused `addressInto` / `structureForRead` imports were dropped.
|
|
41
|
+
- `resolveConflicts` and the `resolve-conflicts` module (the subordinate-spawn conflict mergetool) are removed; `pi-context update` surfaces the conflict set to the calling agent instead.
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
- `scripts/promote-cli.mjs` (the `npm run promote:cli` driver) no longer trusts a poisonable global-prefix resolution and no longer runs a destructive op before validating its target. Two classes are closed: (B) `resolveTargetPrefix()` resolved the real global via `npm prefix -g`, which honors an inherited `npm_config_prefix`, so a poisoned prefix was classified as the real global; the real-global arm now runs the `npm prefix -g` probe with all `npm_config_*` env scrubbed so it reflects the true global regardless of any inherited override. (An interim hardening pass had additionally REFUSED an inherited `npm_config_prefix`; that refusal was subsequently removed — see [Unreleased] — because `npm run` injects the prefix itself, so refusing it broke the documented invocation while the scrub already provides the non-poisonability.) (A) the destructive `npm rm -g @davidorex/pi-context-cli` ran before any precondition check; a new `assertSafeTargetPrefix()` now runs immediately after the target is resolved and BEFORE the build/pack and the `npm rm -g` / `npm i -g`, refusing (exit non-zero) any target that is not absolute, is the repo root, or is under the repo root (path-separator-guarded), and additionally — for the real-global arm only — any target that is not an existing directory containing `lib/node_modules`. Override targets (`--prefix`/`PROMOTE_PREFIX`, including a not-yet-created throwaway dir npm will create) pass the three structural checks without an existence requirement. The post-install realpath verification block is retained unchanged as defense-in-depth. A subsequent hardening pass closes four further classes: (1) the separate `npm rm -g @davidorex/pi-context-cli` is DROPPED — it ran without `--prefix` and under inherited env, so it could act on a target diverging from the validated prefix; `npm i -g --force` over an existing npm-link cleanly replaces it, so the removal was redundant and was the only unpinned destructive op. (2) the single `npm i -g --prefix <prefix> … --force` now runs under a `cleanNpmEnv()`-scrubbed environment (every `npm_config_*` removed) so no ambient override can redirect the install; the explicit `--prefix` still takes precedence. (3) an interim case-insensitive inherited-prefix refusal (scanning for any env key whose lowercase is `npm_config_prefix`, since npm honors `NPM_CONFIG_PREFIX` and other case variants) was added and then removed — see [Unreleased]; the scrub (`cleanNpmEnv()`, itself case-insensitive via `/^npm_config_/i`) carries the non-poisonability without refusing the tool's own injected prefix. (4) `assertSafeTargetPrefix()` now compares the target's REALPATH (nearest-existing-ancestor-resolved, so a symlink-into-repo is caught even before the target dir exists) against the realpath of the repo root, refusing equality or containment before any destructive op; the prior check compared the literal prefix string, so a symlink whose realpath was inside the repo passed and was caught only by the post-install guard after the write. Dropping the separate remove also closes the remove-then-install window in which a failed install left the operator uninstalled. A third hardening pass closes three further classes: (5) a TOCTOU gap — `assertSafeTargetPrefix()` computed the target's nearest-existing-ancestor realpath, validated it, then discarded it; the install and `binPath` consumed the literal unresolved `prefix`, so a symlink component repointed in the build/pack window could redirect the install to a target that was never validated. The validator now RETURNS that resolved realpath (and runs the real-global existence checks — `statSync` / `lib/node_modules`-exists — against the resolved realpath rather than the literal `prefix`), the call site captures it as `safePrefix`, and every post-validation consumer (the `npm i -g --prefix`, `binPath = join(safePrefix, "bin", "pi-context")`, and the target/Done location logs) uses `safePrefix`, so the path validated is exactly the path installed into — no second resolution downstream. (6) the `--prefix` arm matched only the space form (`argv.indexOf("--prefix")`), so the glued `--prefix=<dir>` form fell through to the real-global arm and could install into the true global; the arm now finds the first token that is exactly `--prefix` (value = next argv token) or starts with `--prefix=` (value = the suffix), preserving the empty/missing-value error, `resolve(val)`, the `source: "--prefix"` classification, and first-occurrence-wins for repeats. (7) the post-install verify guard compared `realpathSync(binPath)` against the literal `REPO_ROOT` via a bare `startsWith` (no path-separator boundary), an asymmetry with validation's `REPO_ROOT_REAL + sep` form; the guard now tests `target === REPO_ROOT_REAL || target.startsWith(REPO_ROOT_REAL + sep)`, mirroring the validation arm so a symlinked repo root is caught and a sibling dir sharing the repo-root prefix string does not falsely match. A fourth hardening pass closes a residual leaf-segment TOCTOU within (5): `nearestExistingRealpath()` resolves the nearest EXISTING ancestor and re-appends the not-yet-created leaf segments LITERAL (unresolved), so the returned `safePrefix` for an override target still carried a literal leaf — a leaf later materialized as a symlink-into-repo between validation and the `npm i -g` could redirect the install to a target the containment checks never saw. `assertSafeTargetPrefix()` now, after the containment + existence checks pass, MATERIALIZES the validated target (`mkdirSync(resolved, { recursive: true })` — idempotent no-op for the pre-existing real-global root; creates the intended throwaway for override arms), FULLY realpath-resolves it (`realpathSync` on the now-wholly-existing path, leaving no literal leaf and no symlink to follow), re-runs the repo-root equality/containment refusals on that fully-resolved path (defense-in-depth on the materialized path), and returns it. Every downstream consumer already uses the returned value, so the path validated is now exactly the path installed into across the whole prefix, not just its existing ancestor.
|
|
45
|
+
- The `read-catalog-schema` `verbatimText` text-surface test and the two trailing-newline comments (the test's doc-comment and the `cli.ts` emit-branch comment) are corrected to the verified fact that the bundled catalog `*.schema.json` files end with their own trailing newline (last bytes `}\n`). The prior text encoded a false premise — that the files end with `}` and carry no trailing newline. The verbatimText emit reproduces the file's own bytes byte-exact (its single trailing newline included) and does not append a second one; the defect it addresses was the CLI appending an extra newline (doubling `}\n` to `}\n\n`), which a `read-catalog-schema --kind <k> | diff <installed> -` surfaced as a phantom trailing-empty-line. The byte-exact behavior of `read-catalog-schema` is unchanged.
|
|
46
|
+
- Per-op `--help` now reconciles its `Flags` block with its `SYNOPSIS` for CLI-auto-supplied params (`writer`, `arrayKey`). The `Flags` block keeps the schema-truth `(required)` tag but appends the param's provenance — `--arrayKey <string> (required; auto-derived from --block)` and `--writer <json | @file> (required; auto-injected)` — so a reader no longer sees a bare `(required)` for a param the synopsis brackets as optional and the CLI fills automatically. The `--help --format json` model carries the same provenance on each such flag (`autoSupplied`). A genuinely-required param the caller must pass keeps a plain `(required)` with no provenance marker.
|
|
47
|
+
- `--dryRun`/`--dry-run` on an op that does not support a dry run now errors as an unknown flag (exit 2) instead of being silently swallowed and the real operation running. The global `--dry-run` capture is now scoped to `append-block-item` — the only op whose dry run the CLI performs client-side; for every other op that declares no `dryRun` param (`update-block-item`, `remove-block-item`, and the other no-`dryRun` mutation ops) the flag is rejected rather than dropped, so a caller's dry-run intent can no longer turn into a real write. Ops that declare their own `dryRun` param (`upsert-block-item`, `update`, the relation ops, …) are unaffected — the flag still reaches their own preview semantics.
|
|
48
|
+
- A schema-absent failure surfaced through id allocation now classifies to exit `3` (schema-absent) instead of `4` (id-allocation failure). When an `--autoId` append targets a block whose schema file is missing, the id allocator's "schema not found" throw was matching the id-allocation exit-code pattern first; the catch classifier now tests schema-not-found ahead of id-allocation, so the exit code names the true cause. Genuine id-allocation failures (no id pattern, or an unparseable pattern) still exit `4`.
|
|
49
|
+
- The `--id` alias no longer binds to a boolean flag whose name happens to end in `Id`. The alias identifies an op's id-param(s) by name (`id` or `…Id`); it now additionally requires the matched param to be string-typed, so `append-block-item`'s `autoId` (a boolean auto-allocation flag, not an identity selector) is excluded. Previously `autoId` was that op's sole `…Id` match, so `--id` silently resolved to it — `append-block-item --id SOME-123` swallowed the value into `autoId` and `--id true` silently enabled auto-allocation on a write. With the guard, `append-block-item` declares zero id-params, so `--id` correctly falls through to the unknown-flag error; `--autoId` as the boolean presence flag is unaffected.
|
|
50
|
+
- `pi-context update` no longer leaves an irreconcilable schema conflict unresolvable from the CLI. The prior route dispatched a subordinate `pi-bound` mergetool via `pi -p`, which runs headless (`hasUI=false`) so the spawned agent's `write-schema` was auth-gate-refused and its reconciliation was never applied — the spawned agent could only print a proposal. The conflict set is now returned to the calling agent — the interactive, credentialed actor that invoked the CLI — which reconciles it and commits via `resolve-conflict` (which writes the reconciled body AND advances the merge base to the catalog, so a subsequent `update` converges instead of re-reporting the conflict).
|
|
51
|
+
- `pi-bound` pre-launch setup-step failures now abort before the `pi` launch with an attributed `pi-context pi-bound: …` stderr line and a deterministic exit code, restoring the `set -e` parity of the `scripts/launch-constrained-pi.sh` entrypoint it ports: a non-resolvable package returns 1 (was a raw `Cannot find module` throw surfaced as a generic error); a non-zero `pi install -l` exit propagates the install's own code and aborts (was discarded, proceeding to a broken launch); a `pi install`/launch spawn failure (pi un-runnable) returns 1 (was a bare reject). The success path still returns the launch's exit code unchanged.
|
|
52
|
+
|
|
7
53
|
## [0.30.0] - 2026-06-04
|
|
8
54
|
|
|
9
55
|
### Added
|
package/README.md
CHANGED
|
@@ -6,17 +6,29 @@ The command set is **auto-tracking**: every operation in pi-context's op-registr
|
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
|
+
The published package installs globally:
|
|
10
|
+
|
|
9
11
|
```bash
|
|
10
12
|
npm i -g @davidorex/pi-context-cli
|
|
11
13
|
```
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
For the operator `pi-context` binary in this monorepo, install a publish-free packed copy of the working tree from the repo root:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm run promote:cli
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`promote:cli` (`scripts/promote-cli.mjs`) builds the working tree, packs the `@davidorex` workspace set into tarballs, and installs the whole set into the global npm prefix as a real copy (one `npm i -g <tarball...>`). The installed binary resolves its `@davidorex/*` dependencies from the co-installed packed siblings — current working-tree code, not the registry release — and because it is a copy, a subsequent repo `npm run build` cannot touch it. It uses no `npm link`. Pass `--prefix <dir>` (or `PROMOTE_PREFIX=<dir>`) to install into a throwaway prefix instead of the real global.
|
|
22
|
+
|
|
23
|
+
Either path provides a `pi-context` binary.
|
|
14
24
|
|
|
15
25
|
## Usage
|
|
16
26
|
|
|
17
27
|
```bash
|
|
18
|
-
pi-context --help #
|
|
19
|
-
pi-context
|
|
28
|
+
pi-context --help # grouped op index (by command class) + global flags
|
|
29
|
+
pi-context --version # print the package version (alias -v)
|
|
30
|
+
pi-context <op> --help # per-op help: synopsis + flags + examples + related commands
|
|
31
|
+
pi-context <op> --help --format json # the same help as a machine-readable JSON model
|
|
20
32
|
pi-context <op> [flags] # run an op
|
|
21
33
|
```
|
|
22
34
|
|
|
@@ -31,15 +43,128 @@ Example:
|
|
|
31
43
|
pi-context read-block --block tasks
|
|
32
44
|
pi-context append-block-item --block issues --arrayKey issues --item @new-issue.json --autoId
|
|
33
45
|
pi-context read-block-page --block framework-gaps --offset 0 --limit 50
|
|
46
|
+
pi-context update --dryRun
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Input affordances
|
|
50
|
+
|
|
51
|
+
On top of the schema-derived flags, the CLI accepts a set of input conveniences. Each is additive — the schema-exact form (camelCase flags, explicit `--arrayKey`, a JSON `--writer`, separate `--field`/`--op`/`--value`) keeps working unchanged.
|
|
52
|
+
|
|
53
|
+
- **kebab-case flags** are accepted alongside the camelCase op-schema keys. `--dry-run` resolves to `--dryRun`; any conventional kebab form resolves when its camelCase key exists. An unrecognized flag (kebab or otherwise) is still rejected.
|
|
54
|
+
- **`--id` aliases the op's single id-param.** When an op declares exactly one id-shaped parameter (`itemId` / `parentId` / `taskId` / `unitId` / …), `--id <value>` resolves to it. An op that already declares a literal `id` parameter takes `--id` directly. An op with two id-params (e.g. `complete-task`, `rename-canonical-id`) rejects `--id` as ambiguous — name the explicit flag.
|
|
55
|
+
- **`--arrayKey` is derived from config** for the block-mutation ops (`append-block-item`, `update-block-item`, `upsert-block-item`, `remove-block-item`, and the nested variants). Pass only `--block <name>`; the array key is read from that block's `config.block_kinds[].array_key`. An explicit `--arrayKey` overrides the derivation.
|
|
56
|
+
- **`--writer kind:id` shorthand.** `--writer human:you@example.com` expands to `{"kind":"human","user":"you@example.com"}`; `--writer agent:claude` to `{"kind":"agent","agent_id":"claude"}` (`monitor:`→`monitor_name`, `workflow:`→`workflow_step_id`). The first colon delimits the kind; the identifier may itself contain colons.
|
|
57
|
+
- **`--where field:op:value` shorthand** for the filter predicate: `--where status:eq:done` sets `--field status --op eq --value done`. Split on the first two colons only, so the value may contain colons.
|
|
58
|
+
- **CSV `--op in`.** When the comparison operator is `in`, a comma-separated `--value a,b,c` is split into the array `["a","b","c"]`. Order-independent — `--op in --value a,b,c` and `--value a,b,c --op in` are equivalent.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pi-context append-block-item --block framework-gaps --item @gap.json # arrayKey derived from config
|
|
62
|
+
pi-context find-references --id TASK-1 # --id → itemId
|
|
63
|
+
pi-context append-block-item --block tasks --item @t.json --writer human:you@example.com
|
|
64
|
+
pi-context filter-block-items --block tasks --where status:eq:done # field/op/value from one token
|
|
65
|
+
pi-context filter-block-items --block tasks --field tag --op in --value a,b,c
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Contract preview + dry-run
|
|
69
|
+
|
|
70
|
+
For the block-mutation ops the CLI offers two pre-write affordances:
|
|
71
|
+
|
|
72
|
+
- **`--show-schema`** previews a block op's contract and exits before any write — the array key, the required-field set, every field with its type (and enum values when declared), and the id pattern. Pass it with the op and `--block <name>`; no `--item` is needed.
|
|
73
|
+
- **`append-block-item --dry-run`** (or `--dryRun`) validates the prospective whole file — `{...existing, <arrayKey>: [...items, newItem]}` against the block schema, exactly what a real append validates — and writes nothing. With `--autoId` it reports the id that would be allocated. The output is `[dry-run] PASS` (or `[dry-run] PASS — would append <id>`); a schema-invalid item surfaces the field-named validation error.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pi-context append-block-item --block framework-gaps --show-schema # contract preview, no write
|
|
77
|
+
pi-context append-block-item --block tasks --item @t.json --dry-run # validate the prospective file, no write
|
|
78
|
+
pi-context append-block-item --block tasks --item @t.json --dry-run --autoId # also reports the allocated id
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Output rendering
|
|
82
|
+
|
|
83
|
+
`--format text|json|table` selects how an op's result is rendered. `--json` is the exact alias of `--format json`.
|
|
84
|
+
|
|
85
|
+
- **`text`** (default) — each op's human render: prose, a JSON.stringify'd value, or a read body with its paging footer.
|
|
86
|
+
- **`json`** — the `{ ok, op, output }` envelope (`{ ok: false, op, error }` on failure). `output` is the un-stringified value (single-parse), read-capped at 50KB.
|
|
87
|
+
- **`table`** — a compact markdown table of a renderable row array (a read whose body is a collection, or a data op that returns an array). The projection is best-effort terse: `id` first when present, then up to three more fields (≤4 columns), cells one-lined and capped at 80 chars. A result that is not a complete tabular collection (prose, a non-array value, or an over-cap read) renders as `text` instead — a degenerate table is never substituted for the real output.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pi-context read-block --block tasks --format table # markdown table of the tasks
|
|
91
|
+
pi-context filter-block-items --block tasks --where status:eq:open --format table
|
|
92
|
+
pi-context read-block --block tasks --json # ≡ --format json
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Schema-validation failures surface **field-named guidance** — which field and what constraint (e.g. `` `/gaps/0`: missing required field `description` ``) — rather than the raw validator phrasing, on both the text and `--json` surfaces.
|
|
96
|
+
|
|
97
|
+
Addressed reads return the **whole addressed subtree** (50KB-capped): `read-schema --schemaName <name> --path <dotted.path>` and `read-config --registry <name> [--id <id>]` return the complete node at that address — including all of its children — not a paged slice of one of its arrays.
|
|
98
|
+
|
|
99
|
+
## `pi-context pi-bound` — constrained pi session
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
pi-context pi-bound [--grant <id>]... [...pi-args]
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`pi-bound` is a CLI **process mode** (not a substrate op): it launches a `pi` coding-agent session restricted to the composed pi-extension tool surface. On every launch it:
|
|
106
|
+
|
|
107
|
+
1. runs `pi install -l <@davidorex/pi-project-workflows root>` to register the extensions into the target dir's `.pi/`
|
|
108
|
+
2. derives the static tool allowlist from the installed packages' generated `skills/*/SKILL.md` (`@davidorex/pi-context` + `@davidorex/pi-project-workflows`)
|
|
109
|
+
3. always adds the built-in read-only tools `read`, `ls`, `grep`, `find`
|
|
110
|
+
4. appends the bounded composites declared in the active substrate's `config.tool_operations[]`
|
|
111
|
+
5. launches `pi --tools <union> ...pi-args`
|
|
112
|
+
|
|
113
|
+
It runs from the process cwd and reads that dir's `.pi-context.json` for composites (warns, non-fatally, if absent).
|
|
114
|
+
|
|
115
|
+
**Flags:**
|
|
116
|
+
|
|
117
|
+
- `--grant <id>` (repeatable) — scope the bounded composites to only the named ids. Default: all declared composites.
|
|
118
|
+
- any other token — passed through verbatim to `pi` (e.g. `--continue` / `-c` to resume a session).
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
pi-context pi-bound # launch with the full composed tool surface
|
|
122
|
+
pi-context pi-bound --grant grep-paths # restrict composites to a single named op
|
|
123
|
+
pi-context pi-bound -c # pass -c through to pi to resume
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
This process mode replaces the former `scripts/launch-constrained-pi.sh` launch script.
|
|
127
|
+
|
|
128
|
+
## `pi-context update` — drift-aware model update + conflict surfacing
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
pi-context update [--dryRun]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`update` brings the installed schema model current with the packaged catalog. Per installed schema it consults the drift classification and routes by state: an `in-sync` schema is a no-op; a `catalog-ahead` schema re-syncs through the migration-aware path; a `locally-modified` / `both-diverged` schema is reconciled by a deterministic 3-way merge of base (the as-installed schema body in the object store, keyed by the recorded baseline `content_hash`) × ours (the installed schema) × theirs (the catalog schema). Disjoint edits auto-merge so both the user's and the catalog's changes survive (`required` / `enum` / array-valued `type` nodes merge as sets).
|
|
135
|
+
|
|
136
|
+
A schema whose per-path edits cannot be reconciled is left unmodified, and the conflict is SURFACED to the calling agent — `update` does not spawn a subordinate resolver. The conflict set is returned in the op output (the `conflicts` array, printed under `--json`) alongside a readable per-schema report on the default text surface. The report ends with a guidance line; the calling agent reconciles each conflicting schema and commits the reconciliation with `pi-context resolve-conflict`:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
pi-context read-schema --schemaName <name> # inspect the current installed body
|
|
140
|
+
# resolve the conflicting paths into a reconciled draft-07 schema, then commit it:
|
|
141
|
+
pi-context resolve-conflict --schemaName <name> --schema '<reconciled-json>'
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`resolve-conflict` writes the reconciled body AND advances the merge base for that schema to the catalog, so the next `update` sees the schema as `locally-modified` and its deterministic merge takes the reconciled body (base === theirs → ours) — converging with zero conflicts and preserving the resolution. A bare `write-schema` does not advance the base, so `update` would re-report the same conflict on every run. Omit `--schema` to treat the current on-disk body as already reconciled and only advance the base.
|
|
145
|
+
|
|
146
|
+
`update` also additively propagates catalog-new config-registry entries (`relation_types` / `invariants` / `block_kinds` / `lenses`) absent from the substrate config, preserving every user-authored entry and any locally-diverged body of an existing entry (additive-only — present entries are never overwritten; the added ids are reported under `registryAdditions`).
|
|
147
|
+
|
|
148
|
+
A `catalog-ahead` schema whose resync is refused (`blocked`) carries its diagnostic under `blockedDetail` (one entry per blocked schema): the refusal reason — `no-migration-chain` (no shipped chain reaches the catalog version) vs `validation-failed` (the forward-migrated items fail the catalog schema) — the installed→catalog version pair, and for a validation failure the per-item failures naming the failing item id, field, and constraint. Under `--json` the `blockedDetail` array rides in the op-result envelope; on the default text surface the CLI renders a readable per-schema report below the op output (`blocked: <name> (<from> -> <to>)` then the no-chain line or the per-item failure lines), ending with a guidance line. A live blocked `update` also persists a pending-blocked record pinning the target catalog schema, and — for a `validation-failed` block — writes git-style failure markers INTO the block file at the offending items (full-line `<<<<<<< BLOCKED …` / `>>>>>>> target: …` sentinels), pinning the pre-marker bytes (the schema and `migrations.json` stay byte-unchanged). The resolution loop is: open the block file, fix the items between the markers (or widen the local schema) → `pi-context resolve-blocked --schemaName <name>`, which strips the markers, re-validates the corrected block against the pinned target and, on pass, writes the target schema + advances the merge base so a subsequent `update` converges (in-sync) instead of re-blocking. The standalone `pi-context validate-block-items --block <name>` runs the same per-item check on demand (read-only, returns `{ block, from?, to?, valid, failures[] }`).
|
|
149
|
+
|
|
150
|
+
`--dryRun` predicts the precise per-schema outcome (resync / migrate / block / merge / conflict) by running the forward-migration and re-validation in memory, alongside the per-blocked-schema diagnostic detail and the config-registry entries that would be added, and writes nothing.
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
pi-context update --dryRun # predict the precise per-schema outcome (in-memory migrate + re-validate) + config-registry additions
|
|
154
|
+
pi-context update # apply: resync + auto-merge; conflicts → surfaced (op output + report) for the caller to reconcile + commit via resolve-conflict
|
|
34
155
|
```
|
|
35
156
|
|
|
36
157
|
## Global flags
|
|
37
158
|
|
|
38
159
|
- `--cwd <dir>` — substrate root (default: current working directory; relative paths resolve against it)
|
|
39
|
-
- `--json` — emit a `{ ok, op, output }` envelope on success (`{ ok: false, op, error }` on failure) instead of raw output
|
|
160
|
+
- `--json` — emit a `{ ok, op, output }` envelope on success (`{ ok: false, op, error }` on failure) instead of raw output (≡ `--format json`)
|
|
161
|
+
- `--format <text|json|table>` — select the output render (default `text`, or `json` with `--json`); see [Output rendering](#output-rendering)
|
|
40
162
|
- `--yes`, `--force` — pre-authorize an auth-gated op in a non-interactive context
|
|
41
163
|
- `--writer <json>` — override the auto-resolved writer identity
|
|
42
|
-
- `--
|
|
164
|
+
- `--show-schema` — preview a block op's contract (array key / required fields / field types / id pattern) and exit; see [Contract preview + dry-run](#contract-preview--dry-run)
|
|
165
|
+
- `--dry-run`, `--dryRun` — for `append-block-item`, validate the prospective file and write nothing; see [Contract preview + dry-run](#contract-preview--dry-run)
|
|
166
|
+
- `--version`, `-v` — print the package version and exit
|
|
167
|
+
- `--help`, `-h` — grouped top-level help (ops by command class + a Process modes section), or per-op help after an op name
|
|
43
168
|
|
|
44
169
|
## Writer identity
|
|
45
170
|
|
|
@@ -59,6 +184,11 @@ Ops marked `authGated` (writes that mutate config / schemas / migrations) requir
|
|
|
59
184
|
|
|
60
185
|
## Exit codes
|
|
61
186
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
187
|
+
| Code | Meaning |
|
|
188
|
+
| --- | --- |
|
|
189
|
+
| `0` | success |
|
|
190
|
+
| `1` | op/runtime error, or declined authorization |
|
|
191
|
+
| `2` | usage error (unknown op, unknown flag, missing required field) |
|
|
192
|
+
| `3` | schema absent (the block's schema is not installed) |
|
|
193
|
+
| `4` | id-allocation failure (`--autoId` could not allocate the next id) |
|
|
194
|
+
| `5` | schema validation failure |
|
package/dist/bin.js
CHANGED
|
File without changes
|
package/dist/cli.d.ts
CHANGED
|
@@ -6,6 +6,16 @@ import { type OpDefinition } from "@davidorex/pi-context/ops";
|
|
|
6
6
|
* is excluded here by the partition, never by name.
|
|
7
7
|
*/
|
|
8
8
|
export declare const useOps: OpDefinition[];
|
|
9
|
+
/**
|
|
10
|
+
* The pi-context-cli package version, read ONCE at module load from the shipped
|
|
11
|
+
* package.json. Resolved RELATIVE to this module's URL (mirrors pi-bound.ts's
|
|
12
|
+
* createRequire(import.meta.url) pattern) so it works from the BUILT bin: from
|
|
13
|
+
* `dist/cli.js`, `../package.json` is the package root (npm always ships
|
|
14
|
+
* package.json in a published tarball). A plain `import pkg from "../package.json"`
|
|
15
|
+
* is deliberately avoided — tsconfig.build's rootDir:"./src" / include:["src/**"]
|
|
16
|
+
* would make that compile path a hazard. `--version`/`-v` prints this value.
|
|
17
|
+
*/
|
|
18
|
+
export declare const PKG_VERSION: string;
|
|
9
19
|
/** Field-type tag rendered in help and used to pick a coercion strategy. */
|
|
10
20
|
export type FieldType = "string" | "number" | "boolean" | "json";
|
|
11
21
|
interface FieldSchema {
|
|
@@ -59,6 +69,17 @@ export interface ParsedArgs {
|
|
|
59
69
|
json: boolean;
|
|
60
70
|
yes: boolean;
|
|
61
71
|
help: boolean;
|
|
72
|
+
/** --show-schema (FGAP-022): print the block contract and exit before any write. */
|
|
73
|
+
showSchema?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* --dryRun / --dry-run (FGAP-024): for append-block-item, validate the prospective
|
|
76
|
+
* whole file and write nothing. Parsed as a global flag — never injected into
|
|
77
|
+
* `params` — because the frozen append op declares no `dryRun` param and would
|
|
78
|
+
* reject it as an unknown flag.
|
|
79
|
+
*/
|
|
80
|
+
dryRun?: boolean;
|
|
81
|
+
/** Selected output render (FGAP-021). Unset → resolved from `json` at emit time. */
|
|
82
|
+
format?: "text" | "json" | "table";
|
|
62
83
|
explicitWriter?: unknown;
|
|
63
84
|
params: Record<string, unknown>;
|
|
64
85
|
}
|
|
@@ -77,6 +98,18 @@ export declare function parseOpArgs(op: OpDefinition, argv: string[], cwdBase?:
|
|
|
77
98
|
* identity (falling back to "operator"). Mutates and returns `params`.
|
|
78
99
|
*/
|
|
79
100
|
export declare function injectWriter(op: OpDefinition, params: Record<string, unknown>, identity: string | null): Record<string, unknown>;
|
|
101
|
+
/**
|
|
102
|
+
* Schema- + config-driven arrayKey injection (FGAP-019), mirroring injectWriter.
|
|
103
|
+
* The 7 block-mutation ops still DECLARE `arrayKey` required (their in-pi schema +
|
|
104
|
+
* handler are byte-unchanged and still receive + require it) — the CLI supplies it
|
|
105
|
+
* pre-call so a caller passes only `--block`. When the op declares `arrayKey`, none
|
|
106
|
+
* was passed, and a string `block` is present, derive the array_key from the
|
|
107
|
+
* config block_kinds entry whose canonical_id matches `block` (canonical_id ≠
|
|
108
|
+
* array_key in real data, e.g. framework-gaps → gaps, so the derivation is genuinely
|
|
109
|
+
* needed). Best-effort: no substrate, no config, or an unknown block leaves arrayKey
|
|
110
|
+
* unset — the op then throws its own missing-param error. Mutates and returns params.
|
|
111
|
+
*/
|
|
112
|
+
export declare function injectArrayKey(op: OpDefinition, params: Record<string, unknown>, cwd: string): Record<string, unknown>;
|
|
80
113
|
/**
|
|
81
114
|
* Build the DispatchContext threaded into `op.run` as its 3rd arg so every CLI
|
|
82
115
|
* write op stamps attestation (created_by / created_at) on schemas that declare
|
|
@@ -112,9 +145,92 @@ export declare function authDecision(op: OpDefinition, opts: {
|
|
|
112
145
|
yes: boolean;
|
|
113
146
|
interactive: boolean;
|
|
114
147
|
}): AuthDecision;
|
|
115
|
-
/**
|
|
148
|
+
/**
|
|
149
|
+
* One flag descriptor in the machine-readable help model (CHANGE 3 / TASK-042).
|
|
150
|
+
* `type` is the enum-join (`eq|neq|in|matches`) for string-enum fields, else the
|
|
151
|
+
* coarse FieldType tag. `required` reflects the op's declared schema `required` set
|
|
152
|
+
* verbatim (writer/arrayKey ARE marked required here — schema-truth). `autoSupplied`
|
|
153
|
+
* carries the AUTO_SUPPLIED provenance phrase when the CLI fills the param after
|
|
154
|
+
* parse (writer / arrayKey), else omitted — the Flags block + json help render it as
|
|
155
|
+
* `(required; <autoSupplied>)` so a schema-required-but-CLI-supplied param is not
|
|
156
|
+
* mistaken for one the caller must pass.
|
|
157
|
+
*/
|
|
158
|
+
export interface HelpFlag {
|
|
159
|
+
name: string;
|
|
160
|
+
type: string;
|
|
161
|
+
required: boolean;
|
|
162
|
+
description?: string;
|
|
163
|
+
autoSupplied?: string;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Structured per-op help model — the single source both the text template
|
|
167
|
+
* (deriveHelp) and the `--help --format json` machine help render from.
|
|
168
|
+
*
|
|
169
|
+
* `synopsis` treats `writer` and `arrayKey` as OPTIONAL even when the schema lists
|
|
170
|
+
* them required, because both are auto-injected after parse (writer from the
|
|
171
|
+
* resolved operator identity; arrayKey from config.block_kinds[].array_key) — the
|
|
172
|
+
* same exemption parseOpArgs applies to its required-field check. So a flag is
|
|
173
|
+
* synopsis-required iff it is schema-required AND not writer/arrayKey.
|
|
174
|
+
*
|
|
175
|
+
* `related` is the sibling use-ops sharing this op's top-level help group
|
|
176
|
+
* (groupForOp — the single grouping source the top-level help uses), name-sorted,
|
|
177
|
+
* self excluded. pi-bound modes are not useOps, so they never appear.
|
|
178
|
+
*/
|
|
179
|
+
export interface HelpModel {
|
|
180
|
+
name: string;
|
|
181
|
+
synopsis: string;
|
|
182
|
+
summary: string;
|
|
183
|
+
flags: HelpFlag[];
|
|
184
|
+
examples: string[];
|
|
185
|
+
related: string[];
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Single source for the CLI's auto-supplied params: a field declared `required`
|
|
189
|
+
* by the op schema that the CLI fills after parse, so the caller never passes it.
|
|
190
|
+
* Maps the param name to its provenance phrase. This map is the ONE source for
|
|
191
|
+
* both the synopsis exemption (bracketed-optional, `isSynopsisRequired`) and the
|
|
192
|
+
* per-flag `autoSupplied` annotation surfaced in the Flags block + json help —
|
|
193
|
+
* reconciling the Flags `(required)` schema-truth with the optional synopsis so
|
|
194
|
+
* neither surface contradicts the other (TASK-042 iterate-to-zero finding):
|
|
195
|
+
* - writer: injectWriter fills it from the resolved operator identity
|
|
196
|
+
* - arrayKey: injectArrayKey derives it from config.block_kinds[].array_key
|
|
197
|
+
*/
|
|
198
|
+
export declare const AUTO_SUPPLIED: Record<string, string>;
|
|
199
|
+
/**
|
|
200
|
+
* Build the structured per-op help model from the op's typebox parameters +
|
|
201
|
+
* the registry `examples` + the help-group siblings. Pure — no I/O.
|
|
202
|
+
*/
|
|
203
|
+
export declare function buildHelpModel(op: OpDefinition): HelpModel;
|
|
204
|
+
/**
|
|
205
|
+
* Best-of-breed per-op help text (TASK-042): `<name> — <description>` → SYNOPSIS →
|
|
206
|
+
* Flags (per-field, enum joins + required/optional + desc; json fields show
|
|
207
|
+
* `<json | @file>`) → EXAMPLES → RELATED (omitted when empty) → footer →
|
|
208
|
+
* the Global flags trailer. Plain text.
|
|
209
|
+
*/
|
|
116
210
|
export declare function deriveHelp(op: OpDefinition): string;
|
|
117
|
-
/**
|
|
211
|
+
/**
|
|
212
|
+
* Classify a use-op into its top-level help group LABEL (first-match-wins over the
|
|
213
|
+
* ordered HELP_GROUPS). Throws when a use-op matches no group — a new op that slips
|
|
214
|
+
* past every rule fails loudly here rather than silently vanishing from the help.
|
|
215
|
+
* The drift-guard test asserts every `useOps` name maps to exactly one group.
|
|
216
|
+
*/
|
|
217
|
+
export declare function groupForOp(name: string): string;
|
|
218
|
+
/**
|
|
219
|
+
* One-liner for an op row: prefer `promptSnippet` (the terse reflection summary),
|
|
220
|
+
* fall back to `description`. Collapse to a single line, then truncate uniformly to
|
|
221
|
+
* ~HELP_ONELINER_WIDTH at the last word boundary, appending an ellipsis when cut.
|
|
222
|
+
* Applied to EVERY row identically — no per-op special-casing, and the ops-registry
|
|
223
|
+
* text itself is never edited.
|
|
224
|
+
*/
|
|
225
|
+
export declare function helpOneLiner(op: OpDefinition): string;
|
|
226
|
+
/**
|
|
227
|
+
* Grouped, scannable top-level help. Sections appear in HELP_GROUPS order (only
|
|
228
|
+
* groups with ≥1 op), ops sorted alphabetically within a group, each row a name
|
|
229
|
+
* padded to the group's own max-name width followed by the truncated one-liner.
|
|
230
|
+
* A static Process modes section surfaces `pi-bound` (a process mode, not a
|
|
231
|
+
* substrate op). The Global flags block is retained verbatim plus `--version`.
|
|
232
|
+
* Plain text only.
|
|
233
|
+
*/
|
|
118
234
|
export declare function deriveTopHelp(): string;
|
|
119
235
|
export declare function main(argv: string[]): Promise<number>;
|
|
120
236
|
export {};
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|