apcore-cli 0.6.0 → 0.8.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/dist/index.d.ts CHANGED
@@ -1,5 +1,174 @@
1
1
  import { Command } from 'commander';
2
2
 
3
+ /**
4
+ * Determines which modules are exposed as CLI commands.
5
+ *
6
+ * Filtering modes:
7
+ * - `all`: every discovered module becomes a CLI command (default).
8
+ * - `include`: only modules matching at least one include pattern are exposed.
9
+ * - `exclude`: all modules are exposed except those matching any exclude pattern.
10
+ */
11
+ declare class ExposureFilter {
12
+ static readonly VALID_MODES: readonly ["all", "include", "exclude", "none"];
13
+ readonly _mode: string;
14
+ private readonly _compiledInclude;
15
+ private readonly _compiledExclude;
16
+ constructor(mode?: string, include?: string[], exclude?: string[]);
17
+ /** Return true if the module should be exposed as a CLI command. */
18
+ isExposed(moduleId: string): boolean;
19
+ /** Partition moduleIds into [exposed, hidden] lists. */
20
+ filterModules(moduleIds: string[]): [string[], string[]];
21
+ /**
22
+ * Create an ExposureFilter from a parsed config dict.
23
+ *
24
+ * Expected: `{ expose: { mode: "include", include: ["admin.*"] } }`
25
+ */
26
+ static fromConfig(config: Record<string, unknown>): ExposureFilter;
27
+ }
28
+
29
+ /**
30
+ * Built-in Command Group (FE-13).
31
+ *
32
+ * Encapsulates visibility resolution and subcommand filtering for the
33
+ * reserved `apcli` group. Instantiated once by createCli() and attached
34
+ * to the root command.
35
+ *
36
+ * Shape mirrors `src/exposure.ts` ExposureFilter: private constructor,
37
+ * named static factories, and a small set of predicate methods.
38
+ * See the feature spec §4.2–4.7 for authoritative semantics.
39
+ */
40
+ /**
41
+ * Thrown for invalid built-in apcli group configuration (e.g. invalid
42
+ * `builtinGroupName` regex match). Cross-SDK parity with Rust's
43
+ * `ApcliGroupError` (D1-info-1, 2026-05-08).
44
+ *
45
+ * Extends `Error` so existing `catch (e)` blocks continue to work via
46
+ * `instanceof Error`; callers that want to distinguish apcli config
47
+ * errors from generic errors can switch on `instanceof ApcliGroupError`.
48
+ */
49
+ declare class ApcliGroupError extends Error {
50
+ constructor(message: string);
51
+ }
52
+ /**
53
+ * Resolved visibility mode.
54
+ *
55
+ * `"auto"` is an internal sentinel — it is never returned from
56
+ * {@link ApcliGroup.resolveVisibility} and is rejected when supplied via
57
+ * user config (CliConfig or apcore.yaml).
58
+ */
59
+ type ApcliMode = "auto" | "all" | "none" | "include" | "exclude";
60
+ /**
61
+ * User-facing apcli config shape.
62
+ *
63
+ * Boolean shorthand maps to `{mode: "all"}` / `{mode: "none"}`.
64
+ * Object form rejects `"auto"` per spec §4.2 (internal sentinel only).
65
+ */
66
+ type ApcliConfig = boolean | {
67
+ mode?: Exclude<ApcliMode, "auto">;
68
+ include?: string[];
69
+ exclude?: string[];
70
+ disableEnv?: boolean;
71
+ };
72
+ /**
73
+ * Set of group names reserved by apcore-cli when no rename is configured.
74
+ * Default mirrors {@link DEFAULT_BUILTIN_GROUP_NAME}; when `builtinGroupName`
75
+ * is overridden the live reserved set is `new Set([apcliGroup.name])` and
76
+ * is applied per-instance during the cli.ts collision check.
77
+ */
78
+ declare const RESERVED_GROUP_NAMES: ReadonlySet<string>;
79
+ /**
80
+ * Visibility configuration for the built-in `apcli` command group.
81
+ *
82
+ * Instantiated via {@link ApcliGroup.fromCliConfig} (Tier 1) or
83
+ * {@link ApcliGroup.fromYaml} (Tier 3). The constructor is private to
84
+ * preserve the Tier-1-vs-Tier-3 flag distinction.
85
+ */
86
+ declare class ApcliGroup {
87
+ private readonly _mode;
88
+ private readonly _include;
89
+ private readonly _exclude;
90
+ private readonly _disableEnv;
91
+ private readonly _registryInjected;
92
+ private readonly _fromCliConfig;
93
+ private readonly _name;
94
+ private constructor();
95
+ /**
96
+ * Resolved name for the built-in command group (default `"apcli"`).
97
+ * Overridable via createCli's `builtinGroupName` option for downstream
98
+ * branded CLIs that want a custom namespace. Cross-SDK parity with
99
+ * Python `ApcliGroup.name` (2026-05-08).
100
+ */
101
+ get name(): string;
102
+ /**
103
+ * Tier 1 constructor — config came from `createCli({ apcli })`.
104
+ *
105
+ * A non-auto mode from this tier wins over env var and yaml.
106
+ */
107
+ static fromCliConfig(config: ApcliConfig | undefined, opts: {
108
+ registryInjected: boolean;
109
+ name?: string;
110
+ }): ApcliGroup;
111
+ /**
112
+ * Tier 3 constructor — config came from `apcore.yaml`.
113
+ *
114
+ * Env var (Tier 2) may override the yaml-supplied mode.
115
+ */
116
+ static fromYaml(config: unknown, opts: {
117
+ registryInjected: boolean;
118
+ name?: string;
119
+ }): ApcliGroup;
120
+ /**
121
+ * Non-panicking Tier 3 factory (A-001 parity with Rust's `try_from_yaml`).
122
+ * Returns `[instance, null]` on success or `[null, errorMessage]` on invalid input.
123
+ * Use this in programmatic contexts where throwing/exiting is unwanted.
124
+ */
125
+ static tryFromYaml(config: unknown, opts: {
126
+ registryInjected: boolean;
127
+ name?: string;
128
+ }): [ApcliGroup, null] | [null, string];
129
+ private static _build;
130
+ /**
131
+ * Normalize an include/exclude list. Non-array → warn and return [].
132
+ *
133
+ * Unknown but well-formed entries emit a WARNING (spec §7 error table,
134
+ * T-APCLI-25) but are retained in the returned list for forward-compat —
135
+ * if apcore-cli later adds a subcommand named `foo`, existing configs
136
+ * continue to work without a config change. At runtime, unknown names
137
+ * simply never match any registered subcommand.
138
+ */
139
+ private static _normalizeList;
140
+ /**
141
+ * Resolve effective visibility mode after applying tier precedence.
142
+ *
143
+ * Returns one of `"all" | "none" | "include" | "exclude"` — never `"auto"`.
144
+ *
145
+ * Tier order (spec §4.4):
146
+ * 1. CliConfig non-auto wins outright.
147
+ * 2. `APCORE_CLI_APCLI` env var (unless sealed by disableEnv).
148
+ * 3. yaml non-auto.
149
+ * 4. Auto-detect from registryInjected.
150
+ */
151
+ resolveVisibility(): "all" | "none" | "include" | "exclude";
152
+ /**
153
+ * True iff `subcommand` passes the include/exclude filter.
154
+ *
155
+ * Callers MUST first check {@link resolveVisibility} — this method throws
156
+ * under modes `"all"` or `"none"` (caller bug per spec §4.6).
157
+ */
158
+ isSubcommandIncluded(subcommand: string): boolean;
159
+ /** True iff the `apcli` group itself should appear in root `--help`. */
160
+ isGroupVisible(): boolean;
161
+ /**
162
+ * Parse APCORE_CLI_APCLI. Case-insensitive.
163
+ *
164
+ * - `show` / `1` / `true` → `"all"`
165
+ * - `hide` / `0` / `false` → `"none"`
166
+ * - Empty / unset → `null`
167
+ * - Anything else → warn and return `null`
168
+ */
169
+ private _parseEnv;
170
+ }
171
+
3
172
  /**
4
173
  * LazyModuleGroup — Dynamic command loading from Registry.
5
174
  *
@@ -14,7 +183,21 @@ interface Registry {
14
183
  listModules(): ModuleDescriptor[];
15
184
  getModule(moduleId: string): ModuleDescriptor | null;
16
185
  }
17
- /** Placeholder for apcore-js Executor. */
186
+ /** Strategy info returned by Executor.describePipeline(). */
187
+ interface StrategyInfo {
188
+ name: string;
189
+ stepCount: number;
190
+ stepNames: string[];
191
+ description: string;
192
+ }
193
+ /** A step in the executor strategy (shape parity with apcore-js Step). */
194
+ interface StrategyStep {
195
+ name: string;
196
+ pure?: boolean;
197
+ removable: boolean;
198
+ timeoutMs?: number;
199
+ }
200
+ /** Placeholder for apcore-js Executor. Shape-compatible with apcore-js >= 0.19.0. */
18
201
  interface Executor {
19
202
  execute(moduleId: string, input: Record<string, unknown>): Promise<unknown>;
20
203
  /** Validate inputs without executing. Returns a PreflightResult. */
@@ -27,33 +210,45 @@ interface Executor {
27
210
  stream?(moduleId: string, input: Record<string, unknown>): AsyncIterable<unknown>;
28
211
  /** Call a module (synchronous-style, used by system commands). */
29
212
  call?(moduleId: string, input: Record<string, unknown>): Promise<unknown>;
213
+ /**
214
+ * Describe the executor's currently-set strategy. Returns StrategyInfo
215
+ * (apcore-js >= 0.18.0). Takes no arguments — to introspect a different
216
+ * strategy, use `Executor.listStrategies()` (static) via `executor.constructor`.
217
+ */
218
+ describePipeline?(): StrategyInfo;
219
+ /** The current execution strategy object, exposing step metadata. */
220
+ currentStrategy?: {
221
+ readonly steps: readonly StrategyStep[];
222
+ };
30
223
  }
31
224
  /** Result of a preflight validation check. */
32
225
  interface PreflightCheck {
33
- check: string;
34
- passed: boolean;
35
- error?: unknown;
36
- warnings?: string[];
226
+ readonly check: string;
227
+ readonly passed: boolean;
228
+ readonly error?: unknown;
229
+ readonly warnings?: string[];
37
230
  }
38
- /** Result of executor.validate(). */
231
+ /** Result of executor.validate() — parity with apcore-js PreflightResult. */
39
232
  interface PreflightResult {
40
- valid: boolean;
41
- requires_approval: boolean;
42
- checks: PreflightCheck[];
233
+ readonly valid: boolean;
234
+ readonly requiresApproval: boolean;
235
+ readonly checks: readonly PreflightCheck[];
236
+ readonly errors?: ReadonlyArray<Record<string, unknown>>;
43
237
  }
44
- /** A single step in a pipeline trace. */
238
+ /** A single step in a pipeline trace — parity with apcore-js StepTrace. */
45
239
  interface PipelineTraceStep {
46
- name: string;
47
- duration_ms: number;
48
- skipped: boolean;
49
- skip_reason?: string;
240
+ readonly name: string;
241
+ readonly durationMs: number;
242
+ readonly skipped: boolean;
243
+ readonly skipReason?: string | null;
50
244
  }
51
- /** Pipeline execution trace returned by callWithTrace(). */
245
+ /** Pipeline execution trace returned by callWithTrace() — parity with apcore-js PipelineTrace. */
52
246
  interface PipelineTrace {
53
- strategy_name: string;
54
- total_duration_ms: number;
55
- success: boolean;
56
- steps: PipelineTraceStep[];
247
+ readonly moduleId?: string;
248
+ readonly strategyName: string;
249
+ readonly totalDurationMs: number;
250
+ readonly success: boolean;
251
+ readonly steps: readonly PipelineTraceStep[];
57
252
  }
58
253
  /** Placeholder for apcore-js ModuleDescriptor. */
59
254
  interface ModuleDescriptor {
@@ -67,8 +262,6 @@ interface ModuleDescriptor {
67
262
  annotations?: Record<string, unknown>;
68
263
  metadata?: Record<string, unknown>;
69
264
  }
70
- /** Built-in command names that cannot be overridden by modules. */
71
- declare const BUILTIN_COMMANDS: string[];
72
265
  /**
73
266
  * Dynamically loads apcore modules as Commander subcommands from Registry.
74
267
  */
@@ -124,6 +317,16 @@ declare class GroupedModuleGroup extends LazyModuleGroup {
124
317
  /** Cached LazyGroup instances */
125
318
  private groupCache;
126
319
  private groupMapBuilt;
320
+ /** Exposure filter (FE-12) — controls which modules appear as CLI commands */
321
+ exposureFilter: ExposureFilter;
322
+ /** Effective group depth (CLAUDE.md v0.6.0): constructor arg > APCORE_CLI_GROUP_DEPTH env > 1. */
323
+ readonly groupDepth: number;
324
+ constructor(registry: Registry, executor: Executor, helpTextMaxLength?: number, exposureFilter?: ExposureFilter, groupDepth?: number);
325
+ /**
326
+ * Resolve group depth from constructor arg > APCORE_CLI_GROUP_DEPTH env > default 1.
327
+ * Invalid env values (non-integer, non-positive) fall through to the default.
328
+ */
329
+ static resolveGroupDepth(explicit: number | undefined): number;
127
330
  /**
128
331
  * Determine (groupName | null, commandName) for a module from its display overlay.
129
332
  *
@@ -135,10 +338,18 @@ declare class GroupedModuleGroup extends LazyModuleGroup {
135
338
  static resolveGroup(moduleId: string, descriptor: ModuleDescriptor, groupDepth?: number): [string | null, string];
136
339
  /**
137
340
  * Build the group map from registry modules.
341
+ *
342
+ * FE-13: hard-fails with exit 2 when a module resolves to the reserved
343
+ * `apcli` namespace in any of three ways — explicit `display.cli.group`,
344
+ * auto-grouped dotted prefix, or top-level alias/id. See spec §4.10.
138
345
  */
139
346
  buildGroupMap(): void;
140
347
  /**
141
- * List all available command names: builtins + group names + top-level module names.
348
+ * List all available command names: group names + top-level module names.
349
+ *
350
+ * FE-13: the built-in subcommand list is no longer folded in here — those
351
+ * commands live under the `apcli` prefix and are registered directly by
352
+ * `createCli`.
142
353
  */
143
354
  listCommands(): string[];
144
355
  /**
@@ -154,20 +365,18 @@ declare class GroupedModuleGroup extends LazyModuleGroup {
154
365
  }
155
366
 
156
367
  /**
157
- * CLI entry point createCli / main equivalents.
368
+ * Validate that a module ID conforms to the expected format.
158
369
  *
159
- * Protocol spec: CLI bootstrapping & command registration
160
- * Implements: F1 (dry-run), F3 (enhanced errors), F4 (trace),
161
- * F5 (approval handler), F6 (stream), F8 (strategy), F9 (output formats),
162
- * F11 (extra commands)
370
+ * Pattern: `[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*` max 192 chars.
371
+ *
372
+ * On invalid input, writes a human-readable error to stderr and calls
373
+ * `process.exit(EXIT_CODES.INVALID_CLI_INPUT)`. Cross-SDK parity with Python
374
+ * `apcore_cli.validate.validate_module_id` and Rust `validate_module_id`.
163
375
  */
376
+ declare function validateModuleId(moduleId: string): void;
164
377
 
165
- /** Whether --verbose was passed (controls help detail level). */
166
- declare let verboseHelp: boolean;
167
378
  /** Set the verbose help flag. When false, built-in options are hidden from help. */
168
379
  declare function setVerboseHelp(verbose: boolean): void;
169
- /** Base URL for online documentation. Null means no docs link shown. */
170
- declare let docsUrl: string | null;
171
380
  /**
172
381
  * Set the base URL for online documentation links shown in help and man pages.
173
382
  * Pass null to disable. Command-level help appends `/commands/{name}` automatically.
@@ -197,24 +406,87 @@ interface OptionConfig {
197
406
  parseArg?: (value: string) => unknown;
198
407
  }
199
408
  /**
200
- * Emit structured JSON error to stderr for AI agents.
201
- */
202
- declare function emitErrorJson(e: unknown, exitCode: number): void;
203
- /**
204
- * Emit human-readable error to stderr with guidance fields.
409
+ * APCore unified client facade (apcore-js >= 0.18.0).
410
+ * Exposes registry and executor as top-level properties.
205
411
  */
206
- declare function emitErrorTty(e: unknown, exitCode: number): void;
412
+ interface APCore {
413
+ registry: Registry;
414
+ executor: Executor;
415
+ }
207
416
  /** Options for createCli. */
208
417
  interface CreateCliOptions {
209
418
  extensionsDir?: string;
210
419
  progName?: string;
211
420
  verbose?: boolean;
421
+ /**
422
+ * APCore unified client instance (apcore-js >= 0.18.0).
423
+ * Mutually exclusive with registry/executor — providing app alongside
424
+ * either of those will throw.
425
+ */
426
+ app?: APCore;
212
427
  /** Pre-populated Registry instance. Skips filesystem discovery when provided. */
213
428
  registry?: Registry;
214
429
  /** Pre-built Executor instance. Used alongside registry. */
215
430
  executor?: Executor;
216
431
  /** Extra commands to register after built-in commands (FE-11 F11). */
217
432
  extraCommands?: Command[];
433
+ /** Exposure filter config or instance (FE-12). */
434
+ expose?: Record<string, unknown> | ExposureFilter;
435
+ /** Path to convention-based commands directory (apcore-toolkit ConventionScanner). */
436
+ commandsDir?: string;
437
+ /** Path to binding.yaml for display overlay (apcore-toolkit DisplayResolver). */
438
+ bindingPath?: string;
439
+ /**
440
+ * Optional allowlist of module-path prefixes forwarded to
441
+ * apcore-toolkit's `RegistryWriter.write` when registering convention-scanned
442
+ * or binding-loaded modules. When set, the writer rejects any `target:`
443
+ * path outside the listed prefixes — mitigates arbitrary-code-execution via
444
+ * forged binding YAML (e.g. `target: "os:system"`).
445
+ *
446
+ * Mirrors the Python SDK's `allowed_prefixes` kwarg
447
+ * (apcore-cli-python/src/apcore_cli/factory.py).
448
+ */
449
+ allowedPrefixes?: string[];
450
+ /**
451
+ * Built-in apcli group configuration (FE-13).
452
+ *
453
+ * Accepts:
454
+ * - `true` / `false` (shorthand for `{mode: "all"}` / `{mode: "none"}`)
455
+ * - A config object (see {@link ApcliConfig})
456
+ * - A pre-built {@link ApcliGroup} instance (Tier 1 override)
457
+ *
458
+ * When absent, Tier 3 (apcore.yaml `apcli:` block) is consulted, falling
459
+ * back to auto-detect: standalone → visible, embedded → hidden.
460
+ */
461
+ apcli?: ApcliConfig | ApcliGroup;
462
+ /**
463
+ * Override the name of the built-in command group (default `"apcli"`).
464
+ * Downstream branded CLIs that want their built-ins under a custom
465
+ * namespace (e.g. `mycorp-cli admin health`) pass a different value
466
+ * here. Must match `/^[a-z][a-z0-9_-]*$/` — non-empty, lowercase,
467
+ * alphanumeric + `_` / `-`. Invalid values cause exit code 2.
468
+ *
469
+ * Note: env var `APCORE_CLI_APCLI` and config keys `apcli.*` remain
470
+ * stable regardless of this rename — they are apcore-cli-internal
471
+ * toggles, not user-facing. Mirrors Python
472
+ * `create_cli(builtin_group_name=)`. Cross-SDK parity: 2026-05-08.
473
+ */
474
+ builtinGroupName?: string;
475
+ /**
476
+ * Host application version printed by `-V, --version`.
477
+ *
478
+ * When omitted, the `--version` flag is NOT registered — embedded CLIs
479
+ * that do not opt in will not surface the SDK's own version. The
480
+ * standalone `apcore-cli` binary entry point passes its package version
481
+ * explicitly (see `main()`).
482
+ */
483
+ version?: string;
484
+ /**
485
+ * Top-level CLI description shown at the head of `--help`. Defaults to
486
+ * `${progName} CLI` when omitted, so embedded CLIs do not leak the
487
+ * "apcore" framework name into their own help output.
488
+ */
489
+ description?: string;
218
490
  }
219
491
  /**
220
492
  * Build and return the top-level Commander program.
@@ -224,13 +496,16 @@ interface CreateCliOptions {
224
496
  * @param verbose Show verbose help output
225
497
  */
226
498
  declare function createCli(extensionsDirOrOpts?: string | CreateCliOptions, progName?: string, verbose?: boolean): Command;
227
- /**
228
- * Optionally apply apcore-toolkit features (DisplayResolver, RegistryWriter).
229
- *
230
- * Uses dynamic import so the dependency remains optional — if apcore-toolkit
231
- * is not installed, a warning is printed and the CLI continues without it.
232
- */
233
- declare function applyToolkitIntegration(commandsDir?: string, bindingPath?: string): Promise<void>;
499
+ /** Options bag for {@link applyToolkitIntegration}. */
500
+ interface ApplyToolkitIntegrationOptions {
501
+ /**
502
+ * Allowlist of module-path prefixes forwarded to apcore-toolkit's
503
+ * `RegistryWriter.write` when registering scanned/loaded modules. Mirrors
504
+ * the Python factory's `allowed_prefixes` kwarg.
505
+ */
506
+ allowedPrefixes?: string[];
507
+ }
508
+ declare function applyToolkitIntegration(commandsDir?: string, bindingPath?: string, options?: ApplyToolkitIntegrationOptions): Promise<void>;
234
509
  /**
235
510
  * Parse argv and run the CLI. Handles top-level error catching and exit codes.
236
511
  */
@@ -242,11 +517,7 @@ declare function main(progName?: string): void;
242
517
  * --approval-timeout, --approval-token, --fields, and enhanced --format choices.
243
518
  */
244
519
  declare function buildModuleCommand(moduleDef: ModuleDescriptor, executor: Executor, helpTextMaxLength?: number, cmdName?: string, verbose?: boolean): Command;
245
- /**
246
- * Validate that a module ID conforms to the expected format.
247
- * Pattern: [a-z][a-z0-9_]*(.[a-z][a-z0-9_])* — max 128 chars.
248
- */
249
- declare function validateModuleId(moduleId: string): void;
520
+
250
521
  /**
251
522
  * Collect module input from stdin and/or CLI keyword arguments.
252
523
  */
@@ -282,40 +553,30 @@ declare class CliApprovalHandler {
282
553
  /**
283
554
  * Check if module requires approval and handle accordingly.
284
555
  * Returns normally if approved (or approval not required).
285
- * Calls process.exit(46) if denied/timed out/non-TTY.
556
+ * Throws ApprovalDeniedError or ApprovalTimeoutError on denial/timeout so the
557
+ * caller (buildModuleCommand action) can run audit flush and tear-down before
558
+ * the process exits via the shared error path.
286
559
  */
287
560
  declare function checkApproval(moduleDef: ModuleDescriptor, autoApprove: boolean, timeout?: number): Promise<void>;
288
561
 
289
562
  /**
290
- * Display overlay helpers shared resolution logic for CLI surfaces.
291
- */
292
-
293
- /**
294
- * Extract resolved display overlay from a ModuleDescriptor's metadata.
295
- */
296
- declare function getDisplay(descriptor: ModuleDescriptor): Record<string, unknown>;
297
- /**
298
- * Return [displayName, description, tags] resolved from the display overlay.
563
+ * ConfigResolver 4-tier config resolution (CLI flag > env > file > default).
299
564
  *
300
- * Falls back to scanner-provided values when no overlay is present.
301
- */
302
- declare function getCliDisplayFields(descriptor: ModuleDescriptor): [string, string, string[]];
303
-
304
- /**
305
- * Init command — scaffold new apcore modules (Phase 1).
306
- */
307
-
308
- /**
309
- * Register the init command group on the CLI program.
565
+ * Protocol spec: Configuration resolution
310
566
  */
311
- declare function registerInitCommand(cli: Command): void;
312
-
313
567
  /**
314
- * ConfigResolver 4-tier config resolution (CLI flag > env > file > default).
568
+ * Default configuration values.
315
569
  *
316
- * Protocol spec: Configuration resolution
570
+ * Audit D9 (config cleanup, v0.6.x): the entries `sandbox.enabled`,
571
+ * `cli.auto_approve`, `cli.stdin_buffer_limit`, and the eight `apcore-cli.*`
572
+ * namespace aliases were removed because no production code path reads
573
+ * them via `resolve()`. Sandbox is configured via the `--sandbox` CLI flag,
574
+ * auto-approve via `--yes`, the stdin buffer is hard-coded, and namespace
575
+ * aliases are registered separately by `apcore-js`'s Config Bus when
576
+ * `registerConfigNamespace()` runs at `createCli` startup. The cross-key
577
+ * file-lookup mechanism (`NAMESPACE_TO_LEGACY` / `LEGACY_TO_NAMESPACE`)
578
+ * still works regardless — it does not depend on these DEFAULTS entries.
317
579
  */
318
- /** Default configuration values. */
319
580
  declare const DEFAULTS: Record<string, unknown>;
320
581
  /**
321
582
  * Register the apcore-cli Config Bus namespace (apcore >= 0.15.0).
@@ -334,6 +595,13 @@ declare class ConfigResolver {
334
595
  private readonly configPath;
335
596
  private fileCache;
336
597
  private fileCacheLoaded;
598
+ /**
599
+ * Raw parsed yaml root (pre-flatten). Populated alongside `fileCache`
600
+ * on load. Used by `resolveObject()` to walk nested paths without
601
+ * invoking `flattenDict` — see FE-13 spec §4.8 M1 note.
602
+ * `null` when no config file is present or parsing fails.
603
+ */
604
+ private _rawConfig;
337
605
  constructor(cliFlags?: Record<string, unknown>, configPath?: string);
338
606
  /**
339
607
  * Resolve a single configuration key across all four tiers.
@@ -343,6 +611,26 @@ declare class ConfigResolver {
343
611
  * Load a value from the config file using a dot-separated key path.
344
612
  */
345
613
  private resolveFromFile;
614
+ /**
615
+ * Resolve a configuration key to its raw nested value (FE-13).
616
+ *
617
+ * Unlike `resolve()`, this method does NOT flatten the yaml tree — it
618
+ * walks the dot-separated path directly against the parsed yaml root.
619
+ * This lets callers retrieve non-leaf structures (booleans, arrays,
620
+ * objects) such as the `apcli` visibility config, which is naturally
621
+ * shaped as a nested object in apcore.yaml.
622
+ *
623
+ * Semantics:
624
+ * - Returns `null` when no config file is loaded or when the path is
625
+ * not present / descends into a non-object node (including arrays).
626
+ * - Returns the raw value (boolean / array / object / scalar) when the
627
+ * full path resolves to a leaf or intermediate node.
628
+ *
629
+ * Intentionally DOES NOT consult DEFAULTS, env vars, or CLI flags — it is
630
+ * strictly a yaml-tree accessor. Scalar `resolve()` semantics are
631
+ * unaffected.
632
+ */
633
+ resolveObject(key: string): unknown;
346
634
  /**
347
635
  * Load and flatten a YAML config file.
348
636
  */
@@ -354,42 +642,48 @@ declare class ConfigResolver {
354
642
  }
355
643
 
356
644
  /**
357
- * Discovery commands list, describe, validate (FE-04, FE-11).
358
- *
359
- * Protocol spec: Module discovery & introspection
645
+ * Register the `list` subcommand on the given group (FE-13).
360
646
  */
361
-
647
+ declare function registerListCommand(apcliGroup: Command, registry: Registry, exposureFilter?: ExposureFilter): void;
362
648
  /**
363
- * Register list and describe commands on the CLI group.
649
+ * Register the `describe` subcommand on the given group (FE-13).
364
650
  */
365
- declare function registerDiscoveryCommands(cli: Command, registry: Registry): void;
651
+ declare function registerDescribeCommand(apcliGroup: Command, registry: Registry): void;
652
+ /**
653
+ * Register the `exec` subcommand on the given group (FE-13).
654
+ *
655
+ * Generic dispatch: `apcli exec <module-id> [--format fmt] [--input json]`.
656
+ * Unlike the per-module commands built by `buildModuleCommand`, this command
657
+ * does not derive options from the module's input schema — inputs are passed
658
+ * as a JSON object via `--input`. This mirrors the apcli-flavoured generic
659
+ * dispatch contract in the builtin-group feature spec.
660
+ */
661
+ declare function registerExecCommand(apcliGroup: Command, registry: Registry, executor: Executor): void;
366
662
  /**
367
663
  * Register the standalone validate command.
368
664
  */
369
665
  declare function registerValidateCommand(cli: Command, registry: Registry, executor: Executor): void;
370
666
 
371
667
  /**
372
- * TTY-adaptive output formatting (table/json/csv/yaml/jsonl).
668
+ * TTY-adaptive output formatting (table/json/csv/yaml/jsonl/markdown/skill).
373
669
  *
374
- * Protocol spec: Output formatting (FE-09 enhanced)
670
+ * Protocol spec: Output formatting (FE-08 / FE-09 enhanced)
375
671
  */
376
672
 
377
673
  /**
378
674
  * Resolve output format with TTY-adaptive default.
379
675
  */
380
676
  declare function resolveFormat(explicitFormat?: string): string;
381
- /**
382
- * Truncate text to maxLength, appending '...' if needed.
383
- */
384
- declare function truncate(text: string, maxLength?: number): string;
385
677
  /**
386
678
  * Format and print a list of modules.
387
679
  */
388
- declare function formatModuleList(modules: ModuleDescriptor[], format: string, filterTags?: string[], showDeps?: boolean): void;
680
+ declare function formatModuleList(modules: ModuleDescriptor[], format: string, filterTags?: string[], showDeps?: boolean, exposureFilter?: {
681
+ isExposed(moduleId: string): boolean;
682
+ }): Promise<void>;
389
683
  /**
390
684
  * Format and print full module metadata.
391
685
  */
392
- declare function formatModuleDetail(moduleDef: ModuleDescriptor, format: string): void;
686
+ declare function formatModuleDetail(moduleDef: ModuleDescriptor, format: string): Promise<void>;
393
687
  /**
394
688
  * Format and print module execution result.
395
689
  *
@@ -397,14 +691,6 @@ declare function formatModuleDetail(moduleDef: ModuleDescriptor, format: string)
397
691
  * The `fields` option allows dot-path field selection on dict results.
398
692
  */
399
693
  declare function formatExecResult(result: unknown, format?: string, fields?: string): void;
400
- /**
401
- * Format and print a PreflightResult to stdout.
402
- */
403
- declare function formatPreflightResult(result: PreflightResult, format?: string): void;
404
- /**
405
- * Return the exit code for the first failed check in a PreflightResult.
406
- */
407
- declare function firstFailedExitCode(result: PreflightResult): number;
408
694
 
409
695
  /**
410
696
  * JSON Schema $ref resolver.
@@ -423,17 +709,6 @@ declare function resolveRefs(schema: Record<string, unknown>, maxDepth?: number,
423
709
  * Protocol spec: Schema-driven argument parsing
424
710
  */
425
711
 
426
- /** Sentinel type marker for boolean flags. */
427
- declare const BOOLEAN_FLAG: unique symbol;
428
- type TypeResult = "string" | "int" | "float" | typeof BOOLEAN_FLAG | "file";
429
- /**
430
- * Map JSON Schema type to a type identifier.
431
- */
432
- declare function mapType(propName: string, propSchema: Record<string, unknown>): TypeResult;
433
- /**
434
- * Extract help text from schema property, preferring x-llm-description.
435
- */
436
- declare function extractHelp(propSchema: Record<string, unknown>, maxLength?: number): string | undefined;
437
712
  /**
438
713
  * Convert a JSON Schema `properties` object into an array of
439
714
  * Commander option configurations.
@@ -446,11 +721,6 @@ declare function schemaToCliOptions(schema: Record<string, unknown>, maxHelpLeng
446
721
  * Protocol spec: Shell integration
447
722
  */
448
723
 
449
- /**
450
- * Build a complete roff man page for the entire program.
451
- * Covers all registered commands including downstream business commands.
452
- */
453
- declare function buildProgramManPage(program: Command, progName: string, version: string, description?: string, docsUrl?: string): string;
454
724
  /**
455
725
  * Configure --help --man support on a Commander program.
456
726
  * When --man is passed with --help, outputs a complete roff man page
@@ -461,21 +731,53 @@ declare function buildProgramManPage(program: Command, progName: string, version
461
731
  */
462
732
  declare function configureManHelp(program: Command, progName: string, version: string, description?: string, docsUrl?: string): void;
463
733
  /**
464
- * Register completion and man commands.
734
+ * Register the `completion` subcommand on `host` (typically the apcli group
735
+ * per spec §4.1, or the root program during the transition period).
736
+ *
737
+ * The completion-script generator enumerates the actually-registered set of
738
+ * subcommands from the root program's Commander tree at generation time
739
+ * (spec §4.13).
465
740
  */
466
- declare function registerShellCommands(cli: Command, progName?: string): void;
741
+ declare function registerCompletionCommand(host: Command): void;
467
742
 
468
743
  /**
469
744
  * System management commands — health, usage, enable, disable, reload, config (FE-11 F2).
470
745
  *
471
- * Each delegates to system.* modules via executor.
472
- * No-op if system modules are unavailable (graceful probe).
746
+ * Each registrar delegates to the corresponding system.* module via the
747
+ * executor. The six registrars are pure attach-only operations and are
748
+ * dispatched by `createCli()`'s FE-13 apcli group integration.
473
749
  */
474
750
 
475
751
  /**
476
- * Register system management commands. No-op if system modules are not available.
752
+ * Attach the `health` subcommand to the passed apcliGroup.
753
+ */
754
+ declare function registerHealthCommand(apcliGroup: Command, executor: Executor): void;
755
+ /**
756
+ * Attach the `usage` subcommand to the passed apcliGroup.
757
+ */
758
+ declare function registerUsageCommand(apcliGroup: Command, executor: Executor): void;
759
+ /**
760
+ * Attach the `enable` subcommand to the passed apcliGroup.
761
+ */
762
+ declare function registerEnableCommand(apcliGroup: Command, executor: Executor): void;
763
+ /**
764
+ * Attach the `disable` subcommand to the passed apcliGroup.
765
+ */
766
+ declare function registerDisableCommand(apcliGroup: Command, executor: Executor): void;
767
+ /**
768
+ * Attach the `reload` subcommand to the passed apcliGroup.
769
+ */
770
+ declare function registerReloadCommand(apcliGroup: Command, executor: Executor): void;
771
+ /**
772
+ * Attach the `config` subcommand (with `get` and `set` children) to the
773
+ * passed apcliGroup.
774
+ *
775
+ * Signature note: config reads/writes go through `executor.call()` to
776
+ * `system.config.get` / `system.control.update_config`, so this registrar
777
+ * takes an {@link Executor} (not a Registry). The FE-13 dispatcher table
778
+ * entry for `config` sets `requiresExecutor: true` accordingly.
477
779
  */
478
- declare function registerSystemCommands(cli: Command, executor: Executor): Promise<void>;
780
+ declare function registerConfigCommand(apcliGroup: Command, executor: Executor): void;
479
781
 
480
782
  /**
481
783
  * Pipeline strategy commands — describe-pipeline (FE-11 F8).
@@ -486,6 +788,15 @@ declare function registerSystemCommands(cli: Command, executor: Executor): Promi
486
788
  */
487
789
  declare function registerPipelineCommand(cli: Command, executor: Executor): void;
488
790
 
791
+ /**
792
+ * Init command — scaffold new apcore modules (Phase 1).
793
+ */
794
+
795
+ /**
796
+ * Register the init command group on the CLI program.
797
+ */
798
+ declare function registerInitCommand(cli: Command): void;
799
+
489
800
  /**
490
801
  * Error classes and exit code mapping for apcore-cli.
491
802
  *
@@ -527,6 +838,8 @@ declare const EXIT_CODES: {
527
838
  readonly MODULE_NOT_FOUND: 44;
528
839
  readonly MODULE_LOAD_ERROR: 44;
529
840
  readonly MODULE_DISABLED: 44;
841
+ readonly DEPENDENCY_NOT_FOUND: 44;
842
+ readonly DEPENDENCY_VERSION_MISMATCH: 44;
530
843
  readonly SCHEMA_VALIDATION_ERROR: 45;
531
844
  readonly APPROVAL_DENIED: 46;
532
845
  readonly APPROVAL_TIMEOUT: 46;
@@ -562,10 +875,6 @@ declare const LEVELS: {
562
875
  type LogLevel = keyof typeof LEVELS;
563
876
  declare function setLogLevel(level: string): void;
564
877
  declare function getLogLevel(): LogLevel;
565
- declare function debug(message: string): void;
566
- declare function info(message: string): void;
567
- declare function warn(message: string): void;
568
- declare function error(message: string): void;
569
878
 
570
879
  /**
571
880
  * AuditLogger — JSONL audit trail.
@@ -584,6 +893,7 @@ declare function getAuditLogger(): AuditLogger | null;
584
893
  declare class AuditLogger {
585
894
  static readonly DEFAULT_PATH: string;
586
895
  private readonly logPath;
896
+ private writeFailureWarned;
587
897
  constructor(path?: string);
588
898
  private ensureDirectory;
589
899
  logExecution(moduleId: string, inputData: Record<string, unknown>, status: ExecutionStatus, exitCode: number, durationMs: number): void;
@@ -591,19 +901,31 @@ declare class AuditLogger {
591
901
  private getUser;
592
902
  }
593
903
 
594
- /**
595
- * ConfigEncryptor — Keyring + AES-256-GCM fallback.
596
- *
597
- * Protocol spec: Security — config encryption
598
- */
599
904
  /**
600
905
  * Encrypts and decrypts configuration values. Prefers OS keyring for key
601
- * storage, falling back to AES-256-GCM with a derived key.
906
+ * storage, falling back to AES-256-GCM with a key derived from
907
+ * APCORE_CLI_CONFIG_PASSPHRASE when set, or from hostname+username
908
+ * (obfuscation-only) as a last resort.
909
+ *
910
+ * Wire format v2: enc:v2:<base64(salt(16)+nonce(12)+tag(16)+ciphertext)>
911
+ * Legacy format: enc:<base64(nonce(12)+tag(16)+ciphertext)> (read-only)
602
912
  */
603
913
  declare class ConfigEncryptor {
604
914
  static readonly SERVICE_NAME = "apcore-cli";
915
+ private static weakFallbackWarned;
605
916
  /**
606
917
  * Encrypt and store a configuration value.
918
+ *
919
+ * Cross-SDK contract (D10-003, 2026-04-26): when the OS keyring is
920
+ * detected as available but `setPassword` then throws (locked keyring,
921
+ * transient backend failure, permission revoked, etc.), the error is
922
+ * propagated wrapped in a `ConfigDecryptionError`. Previously TS
923
+ * caught the exception and silently fell through to AES file encryption
924
+ * — a quiet downgrade that surprised users who expected a hard failure.
925
+ * Python lets the keyring exception propagate raw; Rust returns
926
+ * `ConfigDecryptionError::KeyringError`. The fall-through to AES is
927
+ * still reached when `getKeytar()` returns `null` (keyring
928
+ * genuinely unavailable on this platform / install).
607
929
  */
608
930
  store(key: string, value: string): Promise<string>;
609
931
  /**
@@ -613,6 +935,8 @@ declare class ConfigEncryptor {
613
935
  private deriveKey;
614
936
  private aesEncrypt;
615
937
  private aesDecrypt;
938
+ /** Decrypt legacy v1-format values: nonce(12)+tag(16)+ct, static salt. */
939
+ private aesDecryptV1;
616
940
  }
617
941
 
618
942
  /**
@@ -635,6 +959,15 @@ declare class AuthProvider {
635
959
  getApiKey(): Promise<string | null>;
636
960
  /**
637
961
  * Add authentication headers to an outgoing request.
962
+ *
963
+ * Cross-SDK contract (D10-002, 2026-04-26): the input `headers` object
964
+ * is mutated **in place** and the same reference is returned. Callers
965
+ * that share the headers reference (the documented pattern in
966
+ * apcore-cli/docs/features/security.md §AuthProvider) can read
967
+ * `headers.Authorization` after the call without re-binding the
968
+ * return value. Python and Rust both mutate-and-return; TS previously
969
+ * spread into a new object, which silently broke shared-reference
970
+ * callers.
638
971
  */
639
972
  authenticateRequest(headers: Record<string, string>): Promise<Record<string, string>>;
640
973
  /**
@@ -646,23 +979,52 @@ declare class AuthProvider {
646
979
  /**
647
980
  * Sandbox — Subprocess isolation for module execution.
648
981
  *
649
- * Protocol spec: Security — sandboxed execution
982
+ * Protocol spec: Security — sandboxed execution (tech-design §8.6.4).
983
+ *
984
+ * Uses a re-exec model: spawns `node <this-binary> --internal-sandbox-runner
985
+ * <module_id>` with a stripped environment and isolated HOME/TMPDIR.
986
+ * The child reads JSON from stdin, runs the module via a fresh
987
+ * Registry+Executor, and writes JSON to stdout.
650
988
  */
651
989
 
652
990
  /**
653
991
  * Executes modules in an isolated subprocess to limit the blast radius
654
992
  * of untrusted or third-party modules.
655
993
  *
656
- * When disabled, delegates directly to the Executor.
994
+ * When disabled (the default), delegates directly to the Executor.
995
+ * When enabled, spawns a restricted child process via re-exec with
996
+ * `--internal-sandbox-runner <module_id>`.
657
997
  */
658
998
  declare class Sandbox {
999
+ /** Default post-capture stdout+stderr byte budget for sandboxed children. */
1000
+ static readonly DEFAULT_MAX_OUTPUT_BYTES: number;
659
1001
  private readonly enabled;
660
- constructor(enabled?: boolean);
1002
+ private readonly timeoutSeconds;
1003
+ private extensionsRoot;
1004
+ private maxOutputBytes;
1005
+ constructor(enabled?: boolean, timeoutSeconds?: number);
1006
+ /**
1007
+ * Set the extensions root that is forwarded to the sandboxed runner via
1008
+ * `APCORE_EXTENSIONS_ROOT`. The path is resolved to absolute when injected
1009
+ * so the child (whose cwd is the fresh sandbox tempdir) can locate modules.
1010
+ *
1011
+ * Builder-style — returns `this` so call sites can chain. Mirrors Python's
1012
+ * `Sandbox.with_extensions_root` (D1-004 cross-SDK parity).
1013
+ */
1014
+ withExtensionsRoot(extensionsRoot: string | null): this;
1015
+ /**
1016
+ * Cap the post-capture stdout+stderr byte budget for the sandboxed
1017
+ * subprocess. Default: 64 MiB (`Sandbox.DEFAULT_MAX_OUTPUT_BYTES`).
1018
+ *
1019
+ * Builder-style — returns `this`. Mirrors Python's
1020
+ * `Sandbox.with_max_output_bytes` (D1-004 cross-SDK parity).
1021
+ */
1022
+ withMaxOutputBytes(maxOutputBytes: number): this;
661
1023
  /**
662
1024
  * Execute a module, optionally inside a sandboxed subprocess.
663
1025
  */
664
1026
  execute(moduleId: string, inputData: Record<string, unknown>, executor: Executor): Promise<unknown>;
665
- private sandboxedExecute;
1027
+ private _sandboxedExecute;
666
1028
  }
667
1029
 
668
- export { ApprovalDeniedError, ApprovalTimeoutError, AuditLogger, AuthProvider, AuthenticationError, BUILTIN_COMMANDS, CliApprovalHandler, ConfigDecryptionError, ConfigEncryptor, ConfigResolver, type CreateCliOptions, DEFAULTS, EXIT_CODES, type Executor, type ExitCode, GroupedModuleGroup, LazyGroup, LazyModuleGroup, type ModuleDescriptor, ModuleExecutionError, ModuleNotFoundError, type OptionConfig, type PipelineTrace, type PipelineTraceStep, type PreflightCheck, type PreflightResult, type Registry, Sandbox, SchemaValidationError, applyToolkitIntegration, buildModuleCommand, buildProgramManPage, checkApproval, collectInput, configureManHelp, createCli, debug, docsUrl, emitErrorJson, emitErrorTty, error, exitCodeForError, extractHelp, firstFailedExitCode, formatExecResult, formatModuleDetail, formatModuleList, formatPreflightResult, getAuditLogger, getCliDisplayFields, getDisplay, getLogLevel, info, main, mapType, reconvertEnumValues, registerConfigNamespace, registerDiscoveryCommands, registerInitCommand, registerPipelineCommand, registerShellCommands, registerSystemCommands, registerValidateCommand, resolveFormat, resolveRefs, schemaToCliOptions, setAuditLogger, setDocsUrl, setLogLevel, setVerboseHelp, truncate, validateModuleId, verboseHelp, warn };
1030
+ export { type APCore, type ApcliConfig, ApcliGroup, ApcliGroupError, type ApcliMode, type ApplyToolkitIntegrationOptions, ApprovalDeniedError, ApprovalTimeoutError, AuditLogger, AuthProvider, AuthenticationError, CliApprovalHandler, ConfigDecryptionError, ConfigEncryptor, ConfigResolver, type CreateCliOptions, DEFAULTS, EXIT_CODES, type Executor, type ExitCode, ExposureFilter, GroupedModuleGroup, LazyGroup, LazyModuleGroup, type ModuleDescriptor, ModuleExecutionError, ModuleNotFoundError, type OptionConfig, type PipelineTrace, type PipelineTraceStep, type PreflightCheck, type PreflightResult, RESERVED_GROUP_NAMES, type Registry, Sandbox, SchemaValidationError, type StrategyInfo, type StrategyStep, applyToolkitIntegration, buildModuleCommand, checkApproval, collectInput, configureManHelp, createCli, exitCodeForError, formatExecResult, formatModuleDetail, formatModuleList, getAuditLogger, getLogLevel, main, reconvertEnumValues, registerCompletionCommand, registerConfigCommand, registerConfigNamespace, registerDescribeCommand, registerDisableCommand, registerEnableCommand, registerExecCommand, registerHealthCommand, registerInitCommand, registerListCommand, registerPipelineCommand, registerReloadCommand, registerUsageCommand, registerValidateCommand, resolveFormat, resolveRefs, schemaToCliOptions, setAuditLogger, setDocsUrl, setLogLevel, setVerboseHelp, validateModuleId };