@politty/zod 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +561 -0
  3. package/bin/cli.mjs +3 -0
  4. package/dist/arg-registry-YaWTVu_x.d.ts +1025 -0
  5. package/dist/augment.d.ts +15 -0
  6. package/dist/augment.js +1 -0
  7. package/dist/cli-main-Dn88vIyn.js +84 -0
  8. package/dist/cli-run-eibUcgys.js +7 -0
  9. package/dist/cli.d.ts +1 -0
  10. package/dist/cli.js +16 -0
  11. package/dist/command-k-4yAz4J.js +42 -0
  12. package/dist/compile-cache-Ct41pWGL.js +100 -0
  13. package/dist/compile-cache.d.ts +78 -0
  14. package/dist/compile-cache.js +3 -0
  15. package/dist/completion-gtWX3mwP.js +5608 -0
  16. package/dist/completion.d.ts +242 -0
  17. package/dist/completion.js +4 -0
  18. package/dist/docs.d.ts +770 -0
  19. package/dist/docs.js +3044 -0
  20. package/dist/field-meta-DMy5BcRr.js +146 -0
  21. package/dist/index-CvhsecfS.d.ts +455 -0
  22. package/dist/index.d.ts +799 -0
  23. package/dist/index.js +17 -0
  24. package/dist/log-collector-CoUkLVJB.js +114 -0
  25. package/dist/logger-i_bb-Jhc.js +133 -0
  26. package/dist/prompt-CEIZ-7H1.js +171 -0
  27. package/dist/prompt-clack.d.ts +16 -0
  28. package/dist/prompt-clack.js +32 -0
  29. package/dist/prompt-inquirer.d.ts +16 -0
  30. package/dist/prompt-inquirer.js +47 -0
  31. package/dist/prompt.d.ts +106 -0
  32. package/dist/prompt.js +4 -0
  33. package/dist/register-Bk0K83W2.js +439 -0
  34. package/dist/runner-D72I7wvK.js +2956 -0
  35. package/dist/runner-FvUwOHyE.js +3 -0
  36. package/dist/schema-extractor-DMSozq40.js +250 -0
  37. package/dist/skill.d.ts +608 -0
  38. package/dist/skill.js +1832 -0
  39. package/dist/src-KzC0g5CS.js +191 -0
  40. package/dist/subcommand-router-Cskpofdk.js +134 -0
  41. package/package.json +103 -0
@@ -0,0 +1,608 @@
1
+ import { Z as UnknownKeysMode, b as ArgsSchema, v as AnyCommand } from "./arg-registry-YaWTVu_x.js";
2
+ import { z } from "zod";
3
+ //#region ../core/src/skill/types.d.ts
4
+ /**
5
+ * SKILL.md frontmatter metadata, validated against the Agent Skills
6
+ * specification (https://agentskills.io/specification). Unknown fields are
7
+ * preserved (passthrough) so spec extensions and vendor keys round-trip
8
+ * intact.
9
+ *
10
+ * Provenance for politty-managed installs is recorded under
11
+ * `metadata["politty-cli"]` as `"{packageName}:{cliName}"`.
12
+ */
13
+ interface SkillFrontmatter {
14
+ /** Skill identifier. Lowercase alphanumerics + hyphens, 1..64 chars. */
15
+ name: string;
16
+ /** Human-readable description (1..1024 chars). */
17
+ description: string;
18
+ /** SPDX license identifier or free-form string. */
19
+ license?: string;
20
+ /** Runtime / tool compatibility string (<=500 chars). */
21
+ compatibility?: string;
22
+ /** Metadata map (spec: string keys, string values). */
23
+ metadata?: Record<string, string>;
24
+ /** Experimental spec field. */
25
+ "allowed-tools"?: string;
26
+ /** Unknown fields are preserved verbatim. */
27
+ [key: string]: unknown;
28
+ }
29
+ /**
30
+ * A skill discovered from a source directory (npm package).
31
+ */
32
+ interface DiscoveredSkill {
33
+ /** Parsed frontmatter metadata */
34
+ frontmatter: SkillFrontmatter;
35
+ /** Path to the directory containing SKILL.md */
36
+ sourcePath: string;
37
+ /** Raw SKILL.md content (frontmatter + body) */
38
+ rawContent: string;
39
+ }
40
+ /**
41
+ * All kinds of scan failure, as a runtime tuple so callers can exhaustively
42
+ * iterate (e.g. for message tables). Derived {@link ScanErrorReason} stays
43
+ * the single source of truth for the type-level enum.
44
+ */
45
+ declare const SCAN_ERROR_REASONS: readonly ["parse-failed", "name-mismatch", "read-failed", "missing-source"];
46
+ /** Kind of problem encountered by {@link scanSourceDir}. */
47
+ type ScanErrorReason = (typeof SCAN_ERROR_REASONS)[number];
48
+ /**
49
+ * A non-fatal problem encountered while scanning a source directory.
50
+ *
51
+ * Scan errors (invalid frontmatter, name/parent-dir mismatch, unreadable
52
+ * files) are collected rather than thrown so that a single malformed skill
53
+ * does not hide the rest from CLI commands.
54
+ */
55
+ interface ScanError {
56
+ /** Directory that produced the error. */
57
+ path: string;
58
+ /** Kind of problem encountered. */
59
+ reason: ScanErrorReason;
60
+ /** Human-readable detail, suitable for logging. */
61
+ message: string;
62
+ /**
63
+ * Parsed frontmatter `name`, when the scan got far enough to read it.
64
+ * Currently populated only for `name-mismatch` — both the directory
65
+ * basename and this frontmatter name correspond to plausible existing
66
+ * install slot names (depending on which side the user just renamed),
67
+ * so `sync`'s orphan-retention guard needs both to avoid reaping an
68
+ * installed slot belonging to a source skill that failed this scan.
69
+ */
70
+ skillName?: string;
71
+ }
72
+ /**
73
+ * Result of scanning a source directory for SKILL.md files.
74
+ */
75
+ interface ScanResult {
76
+ /** Valid, spec-compliant skills. */
77
+ skills: DiscoveredSkill[];
78
+ /** Directories that looked like skills but failed validation. */
79
+ errors: ScanError[];
80
+ }
81
+ /**
82
+ * How an install materializes skill files under `.agents/skills/<name>`
83
+ * and each `SYMLINK_TARGETS` entry.
84
+ *
85
+ * - `"symlink"` (default): symlink the source into place. Source updates
86
+ * propagate live. Throws with guidance to retry with `"copy"` when
87
+ * `symlinkSync` fails (e.g. Windows without Developer Mode, filesystems
88
+ * that do not support symlinks).
89
+ * - `"copy"`: recursive copy. Source updates require re-running `skills
90
+ * sync`. Works on any filesystem, trades liveness for portability.
91
+ */
92
+ type InstallMode = "symlink" | "copy";
93
+ /** Options for {@link installSkill}. */
94
+ interface InstallSkillOptions {
95
+ /** Install materialization strategy. Default: `"symlink"`. */
96
+ mode?: InstallMode;
97
+ }
98
+ /** Options for {@link uninstallSkill}. */
99
+ interface UninstallSkillOptions {
100
+ /**
101
+ * If set, `uninstallSkill` also removes a real directory at the install
102
+ * path when its SKILL.md's `metadata["politty-cli"]` matches this stamp
103
+ * (a copy-mode install this CLI owns). Without this option, only
104
+ * symlinks are removed — real directories are assumed to be legacy or
105
+ * manual installs and left untouched.
106
+ */
107
+ expectedOwnership?: string;
108
+ }
109
+ /**
110
+ * Per-flag overrides for the built-in skill subcommand options.
111
+ *
112
+ * Pass through {@link SkillCommandOptions.flags} to resolve collisions with
113
+ * CLI-level global flags. Setting `alias` to `false` disables the short
114
+ * alias entirely; passing a string renames it.
115
+ */
116
+ interface SkillFlagOverrides {
117
+ /**
118
+ * `--exclude` flag on `skills sync`. The default short alias is `-x`.
119
+ */
120
+ exclude?: {
121
+ alias?: string | false;
122
+ };
123
+ /**
124
+ * `--verbose` flag shared by `skills add` and `skills sync`. The default
125
+ * short alias is `-v`.
126
+ *
127
+ * This is independent of {@link SkillCommandOptions.globalArgs}'s
128
+ * field-name auto-detection (which drops the flag entirely when the
129
+ * host's `globalArgs` already defines a `verbose` field). `alias` instead
130
+ * resolves a *short-alias-only* collision — e.g. the host's `globalArgs`
131
+ * uses `-v` for an unrelated flag (not named `verbose`), so auto-detection
132
+ * doesn't apply, but the single-character alias still collides.
133
+ */
134
+ verbose?: {
135
+ alias?: string | false;
136
+ };
137
+ }
138
+ /**
139
+ * Options for `withSkillCommand`.
140
+ */
141
+ interface SkillCommandOptions {
142
+ /**
143
+ * Source directory containing SKILL.md files.
144
+ *
145
+ * Each subdirectory whose name matches its `SKILL.md` frontmatter `name`
146
+ * is treated as a skill. Symlinks within the source tree are followed.
147
+ *
148
+ * @example
149
+ * ```typescript
150
+ * // Resolves to ../skills relative to the current file.
151
+ * // Works from both src/ and dist/ if at the same depth.
152
+ * const sourceDir = resolve(dirname(fileURLToPath(import.meta.url)), "../skills");
153
+ * ```
154
+ */
155
+ sourceDir: string;
156
+ /**
157
+ * npm package name that owns this CLI's bundled skills.
158
+ *
159
+ * Each source `SKILL.md` must pre-declare
160
+ * `metadata["politty-cli"]: "{package}:{cliName}"`. The `skills add` and
161
+ * `skills sync` subcommands verify this stamp before installing — two
162
+ * tools managing skills in the same project cannot accidentally clobber
163
+ * each other. `installSkill` itself does not compare ownership;
164
+ * programmatic callers that bypass `withSkillCommand` are responsible
165
+ * for matching the stamp against their own `{package}:{cliName}` up
166
+ * front. (In `mode: "copy"`, `installSkill` additionally requires
167
+ * *some* `politty-cli` stamp on the source and throws otherwise — the
168
+ * caller-side ownership check naturally satisfies that precondition.)
169
+ */
170
+ package: string;
171
+ /**
172
+ * Default install mode for the `skills add` and `skills sync` commands.
173
+ * Defaults to `"symlink"` — install fails with a clear error on
174
+ * filesystems without symlink support (e.g. Windows without Developer
175
+ * Mode). Set to `"copy"` to always copy. See {@link InstallMode}.
176
+ */
177
+ mode?: InstallMode;
178
+ /**
179
+ * Project root directory used by every `skills` subcommand for resolving
180
+ * `.agents/skills/...` install paths.
181
+ *
182
+ * Default: walk up from `process.cwd()` and use the first ancestor that
183
+ * contains `.git/` or `package.json`; fall back to `process.cwd()` when
184
+ * neither is found. This avoids creating `<sub>/.agents/skills/...` when
185
+ * the CLI is invoked from a subdirectory of the project.
186
+ *
187
+ * Pass an explicit absolute (or cwd-relative) path to override — for
188
+ * example, the directory of a CLI-specific config file.
189
+ */
190
+ cwd?: string;
191
+ /**
192
+ * The host CLI's global args schema — the same value passed to
193
+ * `runMain`/`runCommand`'s `globalArgs` option.
194
+ *
195
+ * When provided, `skills add`/`skills sync`'s `--verbose` and `skills
196
+ * list`'s `--json` are automatically omitted from their own schema if
197
+ * this schema already defines a same-named *non-positional boolean*
198
+ * field — no manual configuration needed, and the host's global flag of
199
+ * the same name takes over instead.
200
+ *
201
+ * A same-named field of another type (e.g. a string verbosity level)
202
+ * doesn't trigger this — the local boolean flag stays declared. However,
203
+ * politty itself independently rejects that combination: when
204
+ * `globalArgs` and a command's own schema declare a same-named field
205
+ * with different definitions, `runMain`/`runCommand` throws
206
+ * `FieldTypeConflictError` at parse time. {@link SkillFlagOverrides} can't
207
+ * help here — it only renames the short alias (`-v`/`-x`), not the long
208
+ * `--verbose`/`--json` field name itself, so the conflicting name stays
209
+ * either way. A host whose `globalArgs` uses a non-boolean `verbose`/
210
+ * `json` field must rename or remove that global field (or make it a
211
+ * plain boolean) instead.
212
+ *
213
+ * (Historical note: this omission used to also be required to work around
214
+ * a politty core bug where a global flag typed *before* the subcommand
215
+ * resolved correctly as a global value, but a same-named local field's
216
+ * own default would then overwrite it during the merge. That bug is
217
+ * fixed in politty core independently of this option — a same-named
218
+ * local and global field with *identical* definitions now resolves
219
+ * correctly regardless of position. This option still omits the local
220
+ * flag by default for simplicity: hosts don't need to duplicate the
221
+ * flag's definition on both schemas to get the expected behavior.)
222
+ *
223
+ * @example
224
+ * ```ts
225
+ * const globalArgs = z.object({
226
+ * verbose: arg(z.boolean().default(false), { alias: "v" }),
227
+ * });
228
+ *
229
+ * const cli = withSkillCommand(baseCommand, {
230
+ * sourceDir, package: "@my-agent/skills",
231
+ * globalArgs, // skills add/sync automatically drop their own --verbose
232
+ * });
233
+ *
234
+ * runMain(cli, { globalArgs }); // same schema, passed to the real entry point
235
+ * ```
236
+ */
237
+ globalArgs?: ArgsSchema;
238
+ /**
239
+ * Unknown-keys handling for the `add`/`sync`/`remove`/`list` arg schemas
240
+ * (applied uniformly to all four).
241
+ *
242
+ * - `"strip"` (default) — matches politty's own `z.object()` default:
243
+ * an unrecognized flag prints a warning and its value is dropped from
244
+ * the parsed args.
245
+ * - `"strict"` — an unrecognized flag is a hard error, matching a host
246
+ * CLI that uses `z.strictObject()`/`.strict()` throughout.
247
+ * - `"passthrough"` — matches `z.object().passthrough()`: no warning,
248
+ * and unlike `"strip"`, the unrecognized flag's value is *kept* on the
249
+ * parsed args object under its raw CLI name (e.g. `args["some-flag"]`)
250
+ * rather than dropped.
251
+ *
252
+ * This only governs flags the parser cannot attribute to *either* this
253
+ * subcommand's own schema or the host's `globalArgs` schema — a value
254
+ * that legitimately arrives via `globalArgs` (see
255
+ * {@link SkillCommandOptions.globalArgs}) is validated separately against
256
+ * that schema and merged in afterward, so it is never rejected here.
257
+ */
258
+ unknownKeys?: UnknownKeysMode;
259
+ /**
260
+ * Customize built-in subcommand flags. Use to resolve collisions with
261
+ * the CLI's global flags or to opt out of short aliases.
262
+ *
263
+ * @example
264
+ * ```ts
265
+ * // CLI already uses -x globally; rename the exclude alias.
266
+ * withSkillCommand(cmd, {
267
+ * sourceDir, package: "@my-agent/skills",
268
+ * flags: { exclude: { alias: "X" } },
269
+ * });
270
+ *
271
+ * // Disable the short alias entirely.
272
+ * withSkillCommand(cmd, {
273
+ * sourceDir, package: "@my-agent/skills",
274
+ * flags: { exclude: { alias: false } },
275
+ * });
276
+ * ```
277
+ */
278
+ flags?: SkillFlagOverrides;
279
+ /**
280
+ * Append a one-line skills usage hint to the wrapped command's
281
+ * `description` so `--help` advertises the skills subcommand.
282
+ *
283
+ * - `undefined` (default) — append a default hint mentioning the
284
+ * available subcommands.
285
+ * - `string` — append this exact string instead.
286
+ * - `false` — leave the description untouched.
287
+ */
288
+ descriptionAppend?: string | false;
289
+ /**
290
+ * Override the primary (dispatched) name and aliases of `skills add` and
291
+ * `skills remove`. In each array, the *first* element becomes the
292
+ * subcommand's primary name; every element after it becomes an alias.
293
+ *
294
+ * Default: `add: ["add", "install"]`, `remove: ["remove", "uninstall"]`.
295
+ *
296
+ * Useful when a host CLI wants to rename these subcommands outright, drop
297
+ * the `install`/`uninstall` backward-compatibility aliases, add further
298
+ * aliases, or some combination of all three.
299
+ *
300
+ * @example
301
+ * ```ts
302
+ * withSkillCommand(cmd, {
303
+ * sourceDir, package: "@my-agent/skills",
304
+ * commandMap: {
305
+ * add: ["setup", "add", "install"], // renamed to "setup"; "add"/"install" still work
306
+ * remove: ["remove"], // no aliases at all
307
+ * },
308
+ * });
309
+ * ```
310
+ */
311
+ commandMap?: {
312
+ /** Primary name + aliases for `skills add`. Default: `["add", "install"]`. */
313
+ add?: string[];
314
+ /** Primary name + aliases for `skills remove`. Default: `["remove", "uninstall"]`. */
315
+ remove?: string[];
316
+ };
317
+ /**
318
+ * Override the description text for the `skills` command and/or its
319
+ * built-in subcommands. Any key left unset keeps politty's default
320
+ * description for that command.
321
+ *
322
+ * Keys refer to the subcommand's *canonical role*, not its dispatched
323
+ * name — e.g. `descriptions.add` still applies after `commandMap.add`
324
+ * renames the subcommand to something else.
325
+ *
326
+ * @example
327
+ * ```ts
328
+ * withSkillCommand(cmd, {
329
+ * sourceDir, package: "@my-agent/skills",
330
+ * commandMap: { add: ["setup", "add", "install"] },
331
+ * descriptions: {
332
+ * skills: "Manage My Agent skills",
333
+ * add: "Install My Agent skills", // still keyed by "add", not "setup"
334
+ * },
335
+ * });
336
+ * ```
337
+ */
338
+ descriptions?: {
339
+ /** Description for the top-level `skills` command. Default: `"Manage agent skills"`. */
340
+ skills?: string;
341
+ /** Description for `skills sync`. Default: `"Remove and reinstall all skills from source"`. */
342
+ sync?: string;
343
+ /** Description for `skills add`. Default: `"Install skills from source"`. */
344
+ add?: string;
345
+ /** Description for `skills remove`. Default: `"Remove installed skills"`. */
346
+ remove?: string;
347
+ /** Description for `skills list`. Default: `"List available skills from source"`. */
348
+ list?: string;
349
+ };
350
+ }
351
+ /**
352
+ * Result of wrapping `T` with {@link withSkillCommand} — `T` with a
353
+ * `skills` entry added to (and required on) `subCommands`, so consumers can
354
+ * access `command.subCommands.skills` without an `as AnyCommand` cast.
355
+ */
356
+ type WithSkillCommand<T extends AnyCommand> = T & {
357
+ subCommands: NonNullable<T["subCommands"]> & {
358
+ skills: AnyCommand;
359
+ };
360
+ };
361
+ //#endregion
362
+ //#region ../core/src/skill/frontmatter.d.ts
363
+ /**
364
+ * Result of parsing a SKILL.md file.
365
+ */
366
+ interface ParsedSkillMd {
367
+ /** Parsed and validated frontmatter */
368
+ frontmatter: SkillFrontmatter;
369
+ /** Markdown body (content after frontmatter) */
370
+ body: string;
371
+ /** Full raw content */
372
+ rawContent: string;
373
+ }
374
+ /**
375
+ * Parse YAML frontmatter from a SKILL.md string.
376
+ *
377
+ * `parseError` is set when the frontmatter fence was present but the YAML
378
+ * inside failed to parse, so the scanner can distinguish "invalid YAML"
379
+ * from "missing required field" in its diagnostics. A non-object root
380
+ * (e.g. a top-level YAML list) also returns empty `data` without
381
+ * `parseError` — schema validation surfaces that case clearly
382
+ * enough on its own.
383
+ *
384
+ * @example
385
+ * ```typescript
386
+ * const result = parseFrontmatter(`---
387
+ * name: commit
388
+ * description: Git commit message generation
389
+ * ---
390
+ * # Instructions...`);
391
+ *
392
+ * result.data.name; // "commit"
393
+ * ```
394
+ */
395
+ declare function parseFrontmatter(content: string): {
396
+ data: Record<string, unknown>;
397
+ body: string;
398
+ parseError?: string;
399
+ };
400
+ /**
401
+ * Parse and validate a SKILL.md content string.
402
+ *
403
+ * @returns Parsed skill metadata and body, or `null` if the frontmatter is
404
+ * missing or fails schema validation.
405
+ */
406
+ declare function parseSkillMd(content: string): ParsedSkillMd | null;
407
+ //#endregion
408
+ //#region ../core/src/skill/installer.d.ts
409
+ /**
410
+ * Key used to read provenance off an installed skill. The SKILL.md's
411
+ * `metadata["politty-cli"]` must equal `"{packageName}:{cliName}"` for the
412
+ * owning CLI to manage it. This stamp is authored by the skill package,
413
+ * not rewritten at install time.
414
+ */
415
+ declare const OWNERSHIP_METADATA_KEY = "politty-cli";
416
+ /**
417
+ * Install a skill to the project's agent skill directories.
418
+ *
419
+ * Canonical `.agents/skills/<name>` and each `SYMLINK_TARGETS` entry are
420
+ * populated according to `options.mode`:
421
+ *
422
+ * - `"symlink"` (default): symlink to the source (or to the canonical dir
423
+ * for the agent-specific slots). Source updates propagate live. Throws
424
+ * with guidance to retry with `"copy"` on filesystems without symlink
425
+ * support (e.g. Windows without Developer Mode).
426
+ * - `"copy"`: recursive copy. Works anywhere, but source updates require
427
+ * re-running install.
428
+ *
429
+ * **Symlink target convention.** Symlinks are written with relative
430
+ * targets so an install survives when the project tree is copied or
431
+ * mounted at a different absolute path. The two endpoints are resolved
432
+ * asymmetrically:
433
+ *
434
+ * - The install root (`.agents/skills/`, each `SYMLINK_TARGETS` parent)
435
+ * is passed through `realpathSync` so a symlinked checkout doesn't
436
+ * bake a stale parent path into the relative target.
437
+ * - The source path is resolved by {@link resolveSourcePreservingPackageHop},
438
+ * which walks root→leaf dereferencing every ancestor symlink (a symlinked
439
+ * checkout, macOS `/tmp` → `/private/tmp`, etc.) like `realpathSync` would
440
+ * — EXCEPT a `node_modules/<pkg>` (or `node_modules/@scope/<pkg>`)
441
+ * symlink, which is preserved. Following the package-manager hop would
442
+ * bake pnpm's volatile `node_modules/.pnpm/<pkg>@<version>_<hash>/...`
443
+ * into the link target and a subsequent `pnpm update` would leave every
444
+ * install dangling. Keeping the hop preserves the stable
445
+ * `node_modules/<pkg>` symlink that pnpm keeps repointing, while the
446
+ * project-root portion still matches the install root's realpath style
447
+ * so the install survives copying or remounting the project tree.
448
+ *
449
+ * The overlap guard and copy-mode payload still use the *fully*
450
+ * `realpathSync`-resolved source: the guard must catch a source-side
451
+ * symlink whose target is nested inside the install root, and the
452
+ * copy-mode payload reads through every symlink so a copy install doesn't
453
+ * leave dangling references back into `node_modules`.
454
+ *
455
+ * No absolute-path symlinks are produced by this function.
456
+ *
457
+ * **Atomicity.** This call is *not* transactional across multi-step
458
+ * installs. The canonical slot is cleared then written, and each
459
+ * `SYMLINK_TARGETS` slot is then cleared and written one at a time. A
460
+ * crash mid-install can leave the canonical slot updated and one or
461
+ * more agent-specific slots stale; re-running `installSkill` (or the
462
+ * `skills sync` subcommand, which iterates over multiple skills) is
463
+ * idempotent and converges back to the intended state. Within a single
464
+ * slot's copy-mode write, the staging-and-rename in {@link atomicCopyDir}
465
+ * guarantees the destination is either absent or fully populated — a
466
+ * mid-copy failure never leaves a stamp-less partial directory that the
467
+ * next install's `clearInstallSlot` would refuse to replace. Multi-skill
468
+ * orchestration in {@link createSkillSyncCommand} is fail-fast — the
469
+ * first failed skill aborts the loop without rolling back already-
470
+ * installed siblings, again because re-running converges.
471
+ *
472
+ * The ownership stamp (`metadata["politty-cli"]`) is authored by the skill
473
+ * package; the installer does not modify SKILL.md.
474
+ */
475
+ declare function installSkill(skill: DiscoveredSkill, cwd?: string, options?: InstallSkillOptions): void;
476
+ /**
477
+ * Uninstall a skill from the project's agent skill directories.
478
+ *
479
+ * Each slot is unlinked only when its ownership can be proven:
480
+ * - Agent-specific symlink slots (`.claude/skills/<name>` etc.) — a live
481
+ * symlink is unlinked only when it routes to our canonical slot, so a
482
+ * foreign tool's symlink at the same shared path is left untouched.
483
+ * - The canonical slot (`.agents/skills/<name>`) — a live symlink is
484
+ * unlinked only when its routed-to SKILL.md carries
485
+ * `options.expectedOwnership`, so another politty-based CLI's live
486
+ * install in the same shared namespace is left untouched.
487
+ * - Real directories at any slot are removed only when the directory's
488
+ * SKILL.md carries `options.expectedOwnership`. Unstamped or foreign
489
+ * real directories are left alone so legacy/manual installs are not
490
+ * silently recursively deleted.
491
+ *
492
+ * `skills remove` / `skills sync` always pass `expectedOwnership`. Direct
493
+ * programmatic callers that omit it get the legacy permissive behaviour
494
+ * on symlinks (unconditional unlink) but the conservative behaviour on
495
+ * real directories (no-op). Broken (dangling) canonical symlinks are
496
+ * outside this function's purview — they have no SKILL.md to read, so
497
+ * `cleanupBrokenSlot` handles them with a routing check instead.
498
+ */
499
+ declare function uninstallSkill(name: string, cwd?: string, options?: UninstallSkillOptions): void;
500
+ /**
501
+ * Report whether a skill is currently installed, independent of its
502
+ * ownership stamp. Returns `true` when `.agents/skills/<name>/SKILL.md`
503
+ * resolves to a readable file (through a valid symlink or directly, or
504
+ * via a copy-mode install); returns `false` when the path is absent or
505
+ * the canonical symlink is broken (source package uninstalled).
506
+ *
507
+ * Callers use this to distinguish the two cases where
508
+ * {@link readInstalledOwnership} returns `null` — "not installed" (safe
509
+ * to install fresh) vs. "installed but unstamped" (legacy or manual
510
+ * install that should not be silently clobbered).
511
+ */
512
+ declare function hasInstalledSkill(name: string, cwd?: string): boolean;
513
+ /**
514
+ * Read the ownership stamp off an installed skill's SKILL.md, if any.
515
+ *
516
+ * For symlink-mode installs `.agents/skills/<name>` points at the source,
517
+ * so this reads the package-authored stamp. For copy-mode installs the
518
+ * stamp was captured at install time into the local copy.
519
+ *
520
+ * @returns `metadata["politty-cli"]` as `"{packageName}:{cliName}"`, or
521
+ * `null` if the skill is not installed *or* the stamp is absent/malformed.
522
+ * Use {@link hasInstalledSkill} to distinguish the two cases.
523
+ */
524
+ declare function readInstalledOwnership(name: string, cwd?: string): string | null;
525
+ //#endregion
526
+ //#region ../core/src/skill/scanner.d.ts
527
+ /**
528
+ * Scan a source directory for SKILL.md files.
529
+ *
530
+ * Each immediate subdirectory is a candidate skill; its `SKILL.md` is
531
+ * parsed and validated against the Agent Skills specification, and the
532
+ * frontmatter `name` must match the subdirectory name (spec requirement).
533
+ *
534
+ * If `sourceDir` itself contains a `SKILL.md`, it is treated as a
535
+ * single-skill source. The parent-directory-name match is not enforced in
536
+ * that case because the caller chose an arbitrary path.
537
+ *
538
+ * Symlinks within the source tree are followed (symlinked skill dirs and
539
+ * symlinked SKILL.md files are both accepted). npm packages already
540
+ * execute arbitrary JS on install, so additional symlink-based isolation
541
+ * here would not raise the trust boundary in any realistic threat model.
542
+ *
543
+ * @example
544
+ * ```
545
+ * sourceDir: "node_modules/@my-agent/skills/skills"
546
+ *
547
+ * node_modules/@my-agent/skills/skills/
548
+ * ├── commit/
549
+ * │ └── SKILL.md
550
+ * └── review-pr/
551
+ * └── SKILL.md
552
+ * ```
553
+ */
554
+ declare function scanSourceDir(sourceDir: string): ScanResult;
555
+ //#endregion
556
+ //#region ../core/src/skill/index.d.ts
557
+ /**
558
+ * Wrap a command with a `skills` subcommand for managing SKILL.md-based skills.
559
+ *
560
+ * Adds `skills sync`, `skills add`, `skills remove`, and `skills list`.
561
+ * The install materialization is controlled by `options.mode`
562
+ * (see {@link SkillCommandOptions}):
563
+ *
564
+ * - `"symlink"` (default) — symlink the source into place. Source updates
565
+ * propagate live. Install errors out with guidance to retry with `"copy"`
566
+ * when `symlinkSync` fails (e.g. Windows without Developer Mode).
567
+ * - `"copy"` — recursive copy. Source updates require re-running sync.
568
+ *
569
+ * Under both modes the canonical slot is `.agents/skills/<name>` and each
570
+ * agent-specific directory (e.g. `.claude/skills/<name>`) is populated
571
+ * from that canonical slot. politty never writes to `SKILL.md`. The
572
+ * ownership stamp `metadata["politty-cli"] = "{package}:{cliName}"` must
573
+ * be authored by the skill package itself; `add` and `sync` verify it
574
+ * before installing and `remove` / `sync` consult it before deleting, so
575
+ * this CLI never clobbers skills another tool installed.
576
+ *
577
+ * @throws if `command.subCommands.skills` already exists — silently
578
+ * overwriting it would hide a configuration bug.
579
+ */
580
+ declare function withSkillCommand<T extends AnyCommand>(command: T, options: SkillCommandOptions): WithSkillCommand<T>;
581
+ //#endregion
582
+ //#region src/skill-frontmatter-schema.d.ts
583
+ /**
584
+ * Zod schema for SKILL.md frontmatter.
585
+ *
586
+ * @deprecated Kept as a public export of `politty/skill` for backwards
587
+ * compatibility. politty itself no longer validates frontmatter through
588
+ * this schema — see `validateSkillFrontmatter` in `frontmatter.ts`, which
589
+ * implements the same Agent Skills specification rules without a runtime
590
+ * zod dependency. Keep the two in sync when the spec changes.
591
+ *
592
+ * Strictly validates the fields defined in the Agent Skills specification
593
+ * (https://agentskills.io/specification). Unknown fields are preserved via
594
+ * `.passthrough()` so spec extensions and vendor keys round-trip intact.
595
+ *
596
+ * Provenance / ownership for politty-managed installs is recorded under
597
+ * `metadata["politty-cli"]` as `"{packageName}:{cliName}"`.
598
+ */
599
+ declare const skillFrontmatterSchema: z.ZodObject<{
600
+ name: z.ZodString;
601
+ description: z.ZodString;
602
+ license: z.ZodOptional<z.ZodString>;
603
+ compatibility: z.ZodOptional<z.ZodString>;
604
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
605
+ "allowed-tools": z.ZodOptional<z.ZodString>;
606
+ }, z.core.$loose>;
607
+ //#endregion
608
+ export { type DiscoveredSkill, type InstallMode, type InstallSkillOptions, OWNERSHIP_METADATA_KEY, type ParsedSkillMd, SCAN_ERROR_REASONS, type ScanError, type ScanErrorReason, type ScanResult, type SkillCommandOptions, type SkillFlagOverrides, type SkillFrontmatter, type UninstallSkillOptions, type WithSkillCommand, hasInstalledSkill, installSkill, parseFrontmatter, parseSkillMd, readInstalledOwnership, scanSourceDir, skillFrontmatterSchema, uninstallSkill, withSkillCommand };