@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.
- package/LICENSE +21 -0
- package/README.md +561 -0
- package/bin/cli.mjs +3 -0
- package/dist/arg-registry-YaWTVu_x.d.ts +1025 -0
- package/dist/augment.d.ts +15 -0
- package/dist/augment.js +1 -0
- package/dist/cli-main-Dn88vIyn.js +84 -0
- package/dist/cli-run-eibUcgys.js +7 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +16 -0
- package/dist/command-k-4yAz4J.js +42 -0
- package/dist/compile-cache-Ct41pWGL.js +100 -0
- package/dist/compile-cache.d.ts +78 -0
- package/dist/compile-cache.js +3 -0
- package/dist/completion-gtWX3mwP.js +5608 -0
- package/dist/completion.d.ts +242 -0
- package/dist/completion.js +4 -0
- package/dist/docs.d.ts +770 -0
- package/dist/docs.js +3044 -0
- package/dist/field-meta-DMy5BcRr.js +146 -0
- package/dist/index-CvhsecfS.d.ts +455 -0
- package/dist/index.d.ts +799 -0
- package/dist/index.js +17 -0
- package/dist/log-collector-CoUkLVJB.js +114 -0
- package/dist/logger-i_bb-Jhc.js +133 -0
- package/dist/prompt-CEIZ-7H1.js +171 -0
- package/dist/prompt-clack.d.ts +16 -0
- package/dist/prompt-clack.js +32 -0
- package/dist/prompt-inquirer.d.ts +16 -0
- package/dist/prompt-inquirer.js +47 -0
- package/dist/prompt.d.ts +106 -0
- package/dist/prompt.js +4 -0
- package/dist/register-Bk0K83W2.js +439 -0
- package/dist/runner-D72I7wvK.js +2956 -0
- package/dist/runner-FvUwOHyE.js +3 -0
- package/dist/schema-extractor-DMSozq40.js +250 -0
- package/dist/skill.d.ts +608 -0
- package/dist/skill.js +1832 -0
- package/dist/src-KzC0g5CS.js +191 -0
- package/dist/subcommand-router-Cskpofdk.js +134 -0
- package/package.json +103 -0
package/dist/skill.d.ts
ADDED
|
@@ -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 };
|