@invokable/skills 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Invokable
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # @invokable/skills
2
+
3
+ Turns an [invokable](https://github.com/beinvokable/invokable) tool schema into
4
+ agent instructions, and installs them wherever the user's agent will look.
5
+
6
+ ```js
7
+ import { initCommand } from '@invokable/skills';
8
+
9
+ export default defineTool({
10
+ name: 'demo-tool',
11
+ commands: { init: initCommand(), /* … */ },
12
+ });
13
+ ```
14
+
15
+ ```console
16
+ $ demo-tool init
17
+ created: .claude/skills/demo-tool/SKILL.md
18
+ created: .codex/skills/demo-tool/SKILL.md
19
+
20
+ ```
21
+
22
+ ## What it writes
23
+
24
+ **One portable `SKILL.md`**, byte-identical, into every skills directory:
25
+ `.claude/skills/`, `.codex/skills/`, `.cursor/skills/`, `.gemini/skills/` and
26
+ `.agents/skills/`. Plus `references/commands.md`, `references/errors.md`, and
27
+ `references/checkpoints.md` for tools with approval gates.
28
+
29
+ **A short section** in the flat instruction files other agents read: `AGENTS.md`
30
+ (Codex, Copilot, Cursor, Gemini, Jules, Aider, Zed, Windsurf, Devin, …),
31
+ `.github/copilot-instructions.md`, and `.cursor/rules/<tool>.mdc` for Cursor
32
+ versions predating skills. Only the region between
33
+ `<!-- invokable:begin <tool> -->` and `<!-- invokable:end <tool> -->` is touched.
34
+
35
+ `CLAUDE.md` gets `@AGENTS.md` rather than a copy of the section: Claude Code
36
+ reads `CLAUDE.md` and not `AGENTS.md`, and duplicating would load the same text
37
+ twice into every session.
38
+
39
+ ## Spec compliance
40
+
41
+ Only the six frontmatter fields of the [Agent Skills spec](https://agentskills.io)
42
+ are emitted — `name`, `description`, `license`, `compatibility`, `metadata`,
43
+ `allowed-tools`. Claude Code accepts more, but a skill carrying a non-spec field
44
+ fails to upload to claude.ai or the Skills API with a hard error, so emitting one
45
+ would trade portability for nothing.
46
+
47
+ `name` and `description` are validated against the API's constraints
48
+ (`^[a-z0-9-]{1,64}$`, no reserved words; ≤ 1024 characters, no XML tags) and
49
+ reported as `issues` rather than written out broken.
50
+
51
+ ## Keeping edits
52
+
53
+ Anything inside `<!-- invokable:custom -->` … `<!-- /invokable:custom -->` is
54
+ carried across regeneration. Blocks that no longer have a placeholder are
55
+ appended rather than dropped — losing someone's edits silently is worse than an
56
+ odd layout. `--force` overwrites them.
57
+
58
+ ## In CI
59
+
60
+ ```yaml
61
+ - run: npx demo-tool init --check
62
+ ```
63
+
64
+ Exits **30** when a schema change was not regenerated. A distinct code, so it
65
+ does not look like a crash.
66
+
67
+ ## Options
68
+
69
+ | Option | Effect |
70
+ |---|---|
71
+ | `--dir <path>` | Project root. Defaults to the working directory. |
72
+ | `--targets <ids>` | Comma-separated subset. Default: all. |
73
+ | `--check` | Report what is stale; write nothing; exit 30 if any. |
74
+ | `--force` | Overwrite hand-edited custom blocks. |
75
+
76
+ Target ids: `claude-code`, `codex`, `cursor`, `gemini`, `agents-skills`,
77
+ `agents-md`, `claude-md`, `copilot`, `cursor-rules`.
@@ -0,0 +1,11 @@
1
+ export { renderSkill, renderSkillMd, renderCommandsReference, renderErrorsReference, renderCheckpointsReference, buildDescription, } from './render.js';
2
+ export type { RenderOptions, RenderedSkill, RenderedFile } from './render.js';
3
+ export { installSkills } from './install.js';
4
+ export type { InstallOptions, InstallResult, InstalledFile, FileAction } from './install.js';
5
+ export { TARGETS, DEFAULT_TARGET_IDS, targetById, renderSection, renderMdc } from './targets.js';
6
+ export type { Target, TargetKind } from './targets.js';
7
+ export { extractCustomBlocks, restoreCustomBlocks, upsertSection, beginMarker, endMarker, } from './markers.js';
8
+ export { SPEC_FIELDS, NAME_PATTERN, DESCRIPTION_MAX, validateSkillName, validateDescription, } from './spec.js';
9
+ export type { SpecField, ValidationIssue } from './spec.js';
10
+ export { initCommand } from './init-command.js';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,aAAa,EACb,uBAAuB,EACvB,qBAAqB,EACrB,0BAA0B,EAC1B,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE9E,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE7F,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACjG,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEvD,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,EACb,WAAW,EACX,SAAS,GACV,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export { renderSkill, renderSkillMd, renderCommandsReference, renderErrorsReference, renderCheckpointsReference, buildDescription, } from './render.js';
2
+ export { installSkills } from './install.js';
3
+ export { TARGETS, DEFAULT_TARGET_IDS, targetById, renderSection, renderMdc } from './targets.js';
4
+ export { extractCustomBlocks, restoreCustomBlocks, upsertSection, beginMarker, endMarker, } from './markers.js';
5
+ export { SPEC_FIELDS, NAME_PATTERN, DESCRIPTION_MAX, validateSkillName, validateDescription, } from './spec.js';
6
+ export { initCommand } from './init-command.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,aAAa,EACb,uBAAuB,EACvB,qBAAqB,EACrB,0BAA0B,EAC1B,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAG7C,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGjG,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,EACb,WAAW,EACX,SAAS,GACV,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The `init` built-in from spec 5.3, provided here rather than in core so that
3
+ * the runtime does not depend on the generator. A tool opts in:
4
+ *
5
+ * commands: { init: initCommand(), … }
6
+ */
7
+ export declare function initCommand(): import("@invokable/core").CommandSpec<{
8
+ readonly dir: {
9
+ readonly type: "string";
10
+ readonly description: "Project root. Defaults to the working directory.";
11
+ };
12
+ readonly targets: {
13
+ readonly type: "string";
14
+ readonly description: `Comma-separated targets. Default: all (${string}).`;
15
+ };
16
+ readonly check: {
17
+ readonly type: "boolean";
18
+ readonly description: "Report what is out of date without writing.";
19
+ };
20
+ readonly force: {
21
+ readonly type: "boolean";
22
+ readonly description: "Overwrite hand-edited custom blocks.";
23
+ };
24
+ }>;
25
+ //# sourceMappingURL=init-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-command.d.ts","sourceRoot":"","sources":["../src/init-command.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,wBAAgB,WAAW;;;;;;;;;;;;;;;;;GAqD1B"}
@@ -0,0 +1,61 @@
1
+ import { InvokableError, buildManifest, command } from '@invokable/core';
2
+ import { installSkills } from './install.js';
3
+ import { DEFAULT_TARGET_IDS } from './targets.js';
4
+ /**
5
+ * The `init` built-in from spec 5.3, provided here rather than in core so that
6
+ * the runtime does not depend on the generator. A tool opts in:
7
+ *
8
+ * commands: { init: initCommand(), … }
9
+ */
10
+ export function initCommand() {
11
+ return command({
12
+ description: 'Install agent instructions for this tool into the current project.',
13
+ options: {
14
+ dir: { type: 'string', description: 'Project root. Defaults to the working directory.' },
15
+ targets: {
16
+ type: 'string',
17
+ description: `Comma-separated targets. Default: all (${DEFAULT_TARGET_IDS.join(', ')}).`,
18
+ },
19
+ check: { type: 'boolean', description: 'Report what is out of date without writing.' },
20
+ force: { type: 'boolean', description: 'Overwrite hand-edited custom blocks.' },
21
+ },
22
+ exitCodes: { 30: 'Generated agent instructions are out of date (--check).' },
23
+ // The per-file notes below are the human output; dumping the result object
24
+ // underneath them would just repeat it at ten times the length.
25
+ formatHuman: () => null,
26
+ run: ({ opts, ctx }) => {
27
+ const result = installSkills({
28
+ manifest: buildManifest(ctx.tool),
29
+ ...(opts.dir !== undefined ? { root: opts.dir } : {}),
30
+ ...(opts.targets !== undefined
31
+ ? { targets: opts.targets.split(',').map((s) => s.trim()).filter(Boolean) }
32
+ : {}),
33
+ check: opts.check,
34
+ force: opts.force,
35
+ });
36
+ for (const issue of result.issues) {
37
+ ctx.io.warn(`warning: ${issue.field}: ${issue.message}`);
38
+ }
39
+ const changed = result.files.filter((f) => f.action !== 'unchanged');
40
+ const verb = (action) => opts.check ? (action === 'created' ? 'would create' : 'would update') : action;
41
+ for (const file of changed) {
42
+ ctx.io.note(`${verb(file.action)}: ${file.path}`);
43
+ }
44
+ if (!changed.length)
45
+ ctx.io.note('Agent instructions are up to date.');
46
+ if (opts.check && result.outOfDate) {
47
+ // A tool-defined code so CI can fail on stale generated files without
48
+ // conflating it with a real error.
49
+ throw new InvokableError({
50
+ code: 'skills_out_of_date',
51
+ message: `${changed.length} generated file(s) are out of date.`,
52
+ remediation: `${ctx.tool.name} init`,
53
+ exitCode: 30,
54
+ retryable: false,
55
+ });
56
+ }
57
+ return result;
58
+ },
59
+ });
60
+ }
61
+ //# sourceMappingURL=init-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-command.js","sourceRoot":"","sources":["../src/init-command.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { type RenderOptions } from './render.js';
2
+ import type { ValidationIssue } from './spec.js';
3
+ export type FileAction = 'created' | 'updated' | 'unchanged';
4
+ export interface InstalledFile {
5
+ path: string;
6
+ action: FileAction;
7
+ /** Custom blocks carried across from the previous version. */
8
+ preservedBlocks?: number;
9
+ }
10
+ export interface InstallResult {
11
+ tool: string;
12
+ files: InstalledFile[];
13
+ targets: string[];
14
+ issues: ValidationIssue[];
15
+ /** True when `check` was set and something is out of date. */
16
+ outOfDate: boolean;
17
+ }
18
+ export interface InstallOptions extends RenderOptions {
19
+ /** Project root. Defaults to the current working directory. */
20
+ root?: string;
21
+ /** Target ids; defaults to all of them. */
22
+ targets?: readonly string[];
23
+ /** Report what would change without writing. */
24
+ check?: boolean;
25
+ /** Overwrite hand-edited custom blocks instead of preserving them. */
26
+ force?: boolean;
27
+ }
28
+ /**
29
+ * Generates the skill and installs it into every requested target.
30
+ *
31
+ * The same SKILL.md bytes go to every `skill` target: that is what the Agent
32
+ * Skills standard buys, and duplicating rather than symlinking keeps it working
33
+ * on Windows and inside archives that do not preserve links.
34
+ */
35
+ export declare function installSkills(options: InstallOptions): InstallResult;
36
+ //# sourceMappingURL=install.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAIA,OAAO,EAAiC,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAShF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,CAAC;AAE7D,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;IACnB,8DAA8D;IAC9D,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,8DAA8D;IAC9D,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,gDAAgD;IAChD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,sEAAsE;IACtE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAoBD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,aAAa,CAmFpE"}
@@ -0,0 +1,90 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join, relative } from 'node:path';
3
+ import { extractCustomBlocks, restoreCustomBlocks, upsertSection } from './markers.js';
4
+ import { buildDescription, renderSkill } from './render.js';
5
+ import { DEFAULT_TARGET_IDS, renderClaudeMdPointer, renderMdc, renderSection, targetById, } from './targets.js';
6
+ function writeIfChanged(absolute, content, opts) {
7
+ const path = relative(opts.root, absolute) || absolute;
8
+ const exists = existsSync(absolute);
9
+ const current = exists ? readFileSync(absolute, 'utf8') : null;
10
+ if (current === content)
11
+ return { path, action: 'unchanged' };
12
+ if (!opts.check) {
13
+ mkdirSync(dirname(absolute), { recursive: true });
14
+ writeFileSync(absolute, content, 'utf8');
15
+ }
16
+ return { path, action: exists ? 'updated' : 'created' };
17
+ }
18
+ /**
19
+ * Generates the skill and installs it into every requested target.
20
+ *
21
+ * The same SKILL.md bytes go to every `skill` target: that is what the Agent
22
+ * Skills standard buys, and duplicating rather than symlinking keeps it working
23
+ * on Windows and inside archives that do not preserve links.
24
+ */
25
+ export function installSkills(options) {
26
+ const root = options.root ?? process.cwd();
27
+ const check = options.check ?? false;
28
+ const ids = options.targets ?? DEFAULT_TARGET_IDS;
29
+ const targets = [];
30
+ for (const id of ids) {
31
+ const target = targetById(id);
32
+ if (!target) {
33
+ throw new Error(`Unknown skills target "${id}". Known targets: ${DEFAULT_TARGET_IDS.join(', ')}.`);
34
+ }
35
+ targets.push(target);
36
+ }
37
+ const rendered = renderSkill(options);
38
+ const manifest = options.manifest;
39
+ const description = buildDescription(options);
40
+ const files = [];
41
+ // The canonical skill location, referenced by every section target so an
42
+ // agent reading AGENTS.md knows where the detail lives.
43
+ const canonical = targets.find((t) => t.kind === 'skill')?.path(manifest.name) ??
44
+ `.claude/skills/${manifest.name}`;
45
+ for (const target of targets) {
46
+ if (target.kind === 'skill') {
47
+ const dir = join(root, target.path(manifest.name));
48
+ for (const file of rendered.files) {
49
+ const absolute = join(dir, file.path);
50
+ let content = file.content;
51
+ let preserved = 0;
52
+ if (!options.force && existsSync(absolute)) {
53
+ const blocks = extractCustomBlocks(readFileSync(absolute, 'utf8'));
54
+ if (blocks.length) {
55
+ content = restoreCustomBlocks(content, blocks);
56
+ preserved = blocks.length;
57
+ }
58
+ }
59
+ const result = writeIfChanged(absolute, content, { check, root });
60
+ files.push(preserved ? { ...result, preservedBlocks: preserved } : result);
61
+ }
62
+ continue;
63
+ }
64
+ const absolute = join(root, target.path(manifest.name));
65
+ if (target.kind === 'mdc') {
66
+ files.push(writeIfChanged(absolute, renderMdc(manifest, description, canonical), { check, root }));
67
+ continue;
68
+ }
69
+ // kind === 'section': touch only our marked region of someone else's file.
70
+ const existing = existsSync(absolute) ? readFileSync(absolute, 'utf8') : '';
71
+ // Claude Code does not read AGENTS.md, so CLAUDE.md has to carry the
72
+ // content somehow. When AGENTS.md is also a target it already has it, and
73
+ // an import is enough — repeating the section would load the same text
74
+ // twice into every Claude Code session.
75
+ const writesAgentsMd = ids.includes('agents-md');
76
+ const section = target.id === 'claude-md' && writesAgentsMd
77
+ ? renderClaudeMdPointer()
78
+ : renderSection(manifest, canonical);
79
+ const next = upsertSection(existing, manifest.name, section);
80
+ files.push(writeIfChanged(absolute, next, { check, root }));
81
+ }
82
+ return {
83
+ tool: manifest.name,
84
+ files,
85
+ targets: targets.map((t) => t.id),
86
+ issues: rendered.issues,
87
+ outOfDate: files.some((f) => f.action !== 'unchanged'),
88
+ };
89
+ }
90
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Generated files are regenerated whenever the tool schema changes, so anything
3
+ * a developer writes by hand has to survive that. Two marker pairs make it:
4
+ *
5
+ * <!-- invokable:custom --> … <!-- /invokable:custom -->
6
+ * Inside a generated file. Content is carried across regeneration.
7
+ *
8
+ * <!-- invokable:begin <tool> --> … <!-- invokable:end <tool> -->
9
+ * Around our section of a shared file such as AGENTS.md, so we can update
10
+ * just that region and never touch the rest of someone's file.
11
+ */
12
+ export declare function beginMarker(tool: string): string;
13
+ export declare function endMarker(tool: string): string;
14
+ /** Extracts custom blocks in document order. */
15
+ export declare function extractCustomBlocks(content: string): string[];
16
+ /**
17
+ * Re-inserts previously extracted blocks into freshly generated content,
18
+ * matching them to placeholders in order. Extra blocks are appended rather than
19
+ * dropped: losing a developer's edits silently is worse than an odd layout.
20
+ */
21
+ export declare function restoreCustomBlocks(generated: string, blocks: string[]): string;
22
+ export declare function emptyCustomBlock(hint: string): string;
23
+ /**
24
+ * Replaces our marked region of a shared file, or appends it when absent.
25
+ * Content outside the markers is returned untouched.
26
+ */
27
+ export declare function upsertSection(existing: string, tool: string, section: string): string;
28
+ //# sourceMappingURL=markers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markers.d.ts","sourceRoot":"","sources":["../src/markers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AACD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,gDAAgD;AAChD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAY7D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CA0B/E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAarF"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Generated files are regenerated whenever the tool schema changes, so anything
3
+ * a developer writes by hand has to survive that. Two marker pairs make it:
4
+ *
5
+ * <!-- invokable:custom --> … <!-- /invokable:custom -->
6
+ * Inside a generated file. Content is carried across regeneration.
7
+ *
8
+ * <!-- invokable:begin <tool> --> … <!-- invokable:end <tool> -->
9
+ * Around our section of a shared file such as AGENTS.md, so we can update
10
+ * just that region and never touch the rest of someone's file.
11
+ */
12
+ const CUSTOM_OPEN = '<!-- invokable:custom -->';
13
+ const CUSTOM_CLOSE = '<!-- /invokable:custom -->';
14
+ export function beginMarker(tool) {
15
+ return `<!-- invokable:begin ${tool} -->`;
16
+ }
17
+ export function endMarker(tool) {
18
+ return `<!-- invokable:end ${tool} -->`;
19
+ }
20
+ /** Extracts custom blocks in document order. */
21
+ export function extractCustomBlocks(content) {
22
+ const blocks = [];
23
+ let index = 0;
24
+ for (;;) {
25
+ const open = content.indexOf(CUSTOM_OPEN, index);
26
+ if (open === -1)
27
+ break;
28
+ const close = content.indexOf(CUSTOM_CLOSE, open);
29
+ if (close === -1)
30
+ break;
31
+ blocks.push(content.slice(open + CUSTOM_OPEN.length, close).trim());
32
+ index = close + CUSTOM_CLOSE.length;
33
+ }
34
+ return blocks;
35
+ }
36
+ /**
37
+ * Re-inserts previously extracted blocks into freshly generated content,
38
+ * matching them to placeholders in order. Extra blocks are appended rather than
39
+ * dropped: losing a developer's edits silently is worse than an odd layout.
40
+ */
41
+ export function restoreCustomBlocks(generated, blocks) {
42
+ if (blocks.length === 0)
43
+ return generated;
44
+ let result = generated;
45
+ let used = 0;
46
+ for (;;) {
47
+ const open = result.indexOf(CUSTOM_OPEN, used === 0 ? 0 : result.indexOf(CUSTOM_CLOSE) + 1);
48
+ if (open === -1 || used >= blocks.length)
49
+ break;
50
+ const close = result.indexOf(CUSTOM_CLOSE, open);
51
+ if (close === -1)
52
+ break;
53
+ const body = blocks[used] ?? '';
54
+ const replacement = `${CUSTOM_OPEN}\n${body ? body + '\n' : ''}${CUSTOM_CLOSE}`;
55
+ result = result.slice(0, open) + replacement + result.slice(close + CUSTOM_CLOSE.length);
56
+ used += 1;
57
+ }
58
+ if (used < blocks.length) {
59
+ const leftover = blocks.slice(used).filter(Boolean);
60
+ if (leftover.length) {
61
+ result +=
62
+ `\n\n${CUSTOM_OPEN}\n` +
63
+ leftover.join('\n\n') +
64
+ `\n${CUSTOM_CLOSE}\n`;
65
+ }
66
+ }
67
+ return result;
68
+ }
69
+ export function emptyCustomBlock(hint) {
70
+ return `${CUSTOM_OPEN}\n<!-- ${hint} -->\n${CUSTOM_CLOSE}`;
71
+ }
72
+ /**
73
+ * Replaces our marked region of a shared file, or appends it when absent.
74
+ * Content outside the markers is returned untouched.
75
+ */
76
+ export function upsertSection(existing, tool, section) {
77
+ const begin = beginMarker(tool);
78
+ const end = endMarker(tool);
79
+ const block = `${begin}\n${section.trim()}\n${end}`;
80
+ const start = existing.indexOf(begin);
81
+ const stop = existing.indexOf(end);
82
+ if (start !== -1 && stop !== -1 && stop > start) {
83
+ return existing.slice(0, start) + block + existing.slice(stop + end.length);
84
+ }
85
+ const base = existing.trimEnd();
86
+ return base ? `${base}\n\n${block}\n` : `${block}\n`;
87
+ }
88
+ //# sourceMappingURL=markers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markers.js","sourceRoot":"","sources":["../src/markers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { type ToolManifest } from '@invokable/core';
2
+ import { type ValidationIssue } from './spec.js';
3
+ export interface RenderOptions {
4
+ manifest: ToolManifest;
5
+ /** Overrides the generated `description` frontmatter. */
6
+ description?: string;
7
+ license?: string;
8
+ /** Extra trigger phrases folded into the description. */
9
+ triggers?: readonly string[];
10
+ /** Defaults to `Bash, Read`. */
11
+ allowedTools?: readonly string[];
12
+ }
13
+ export interface RenderedFile {
14
+ /** Path relative to the skill directory. */
15
+ path: string;
16
+ content: string;
17
+ }
18
+ export interface RenderedSkill {
19
+ name: string;
20
+ files: RenderedFile[];
21
+ issues: ValidationIssue[];
22
+ }
23
+ /**
24
+ * Builds the `description`, which is the single most important line: it is what
25
+ * every agent reads to decide whether to load the skill at all. Leads with the
26
+ * capability, then the trigger phrases, then the auth precondition.
27
+ */
28
+ export declare function buildDescription(opts: RenderOptions): string;
29
+ /** The main SKILL.md, using only the six spec-portable frontmatter fields. */
30
+ export declare function renderSkillMd(opts: RenderOptions): {
31
+ content: string;
32
+ issues: ValidationIssue[];
33
+ };
34
+ export declare function renderCommandsReference(manifest: ToolManifest): string;
35
+ export declare function renderErrorsReference(manifest: ToolManifest): string;
36
+ export declare function renderCheckpointsReference(manifest: ToolManifest): string;
37
+ /** Renders the complete portable skill bundle. */
38
+ export declare function renderSkill(opts: RenderOptions): RenderedSkill;
39
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyC,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE3F,OAAO,EAA2D,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AAE1G,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,YAAY,CAAC;IACvB,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,gCAAgC;IAChC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAwBD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAyB5D;AAuCD,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,IAAI,EAAE,aAAa,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,eAAe,EAAE,CAAA;CAAE,CAoGjG;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CA2CtE;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CA4CpE;AAED,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CA0DzE;AAED,kDAAkD;AAClD,wBAAgB,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa,CAc9D"}
package/dist/render.js ADDED
@@ -0,0 +1,319 @@
1
+ import { EXIT, EXIT_DESCRIPTION } from '@invokable/core';
2
+ import { emptyCustomBlock } from './markers.js';
3
+ import { DESCRIPTION_MAX, validateDescription, validateSkillName } from './spec.js';
4
+ function yamlString(value) {
5
+ // Quote whenever YAML would otherwise misread the value.
6
+ if (/^[\w][\w .,'()/-]*$/.test(value) && !/:\s/.test(value))
7
+ return value;
8
+ return JSON.stringify(value);
9
+ }
10
+ function table(headers, rows) {
11
+ const lines = [`| ${headers.join(' | ')} |`, `|${headers.map(() => '---').join('|')}|`];
12
+ for (const row of rows)
13
+ lines.push(`| ${row.join(' | ')} |`);
14
+ return lines.join('\n');
15
+ }
16
+ function commandSignature(tool, cmd) {
17
+ const parts = [tool, cmd.name];
18
+ for (const p of cmd.positionals)
19
+ parts.push(`<${p}>`);
20
+ for (const opt of cmd.options) {
21
+ if (!opt.required)
22
+ continue;
23
+ parts.push(opt.type === 'boolean' ? `--${opt.name}` : `--${opt.name} <${opt.type}>`);
24
+ }
25
+ return parts.join(' ');
26
+ }
27
+ /**
28
+ * Builds the `description`, which is the single most important line: it is what
29
+ * every agent reads to decide whether to load the skill at all. Leads with the
30
+ * capability, then the trigger phrases, then the auth precondition.
31
+ */
32
+ export function buildDescription(opts) {
33
+ if (opts.description)
34
+ return opts.description;
35
+ const { manifest } = opts;
36
+ const verbs = manifest.commands
37
+ .filter((c) => !['login', 'logout', 'whoami', 'doctor', 'init', 'update'].includes(c.name))
38
+ .map((c) => c.name)
39
+ .slice(0, 8);
40
+ const base = manifest.description ??
41
+ `Run ${manifest.name} commands from the CLI.`;
42
+ const triggerText = opts.triggers?.length
43
+ ? opts.triggers.join(', ')
44
+ : verbs.join(', ');
45
+ const parts = [
46
+ base.replace(/\s+$/, ''),
47
+ triggerText ? `Use when the user asks to: ${triggerText}.` : '',
48
+ `Always pass --json and read the exit code. Requires \`${manifest.name} login\` first.`,
49
+ ].filter(Boolean);
50
+ const joined = parts.join(' ');
51
+ return joined.length > DESCRIPTION_MAX ? joined.slice(0, DESCRIPTION_MAX - 1) + '…' : joined;
52
+ }
53
+ function renderFrontmatter(opts, description) {
54
+ const { manifest } = opts;
55
+ const allowed = (opts.allowedTools ?? ['Bash', 'Read']).join(', ');
56
+ const lines = [
57
+ '---',
58
+ `name: ${manifest.name}`,
59
+ `description: ${yamlString(description)}`,
60
+ `allowed-tools: ${allowed}`,
61
+ ];
62
+ if (opts.license)
63
+ lines.push(`license: ${yamlString(opts.license)}`);
64
+ lines.push('metadata:', ` tool-version: ${yamlString(manifest.version)}`, ' generated-by: "@invokable/skills"');
65
+ lines.push('---');
66
+ return lines.join('\n');
67
+ }
68
+ function renderCommandsSection(manifest) {
69
+ const rows = manifest.commands.map((c) => [
70
+ `\`${commandSignature(manifest.name, c)}\``,
71
+ c.description.replace(/\|/g, '\\|') + (c.spends ? ' **(spends money)**' : ''),
72
+ ]);
73
+ return table(['Command', 'What it does'], rows);
74
+ }
75
+ function renderExitCodesSection(manifest) {
76
+ const rows = manifest.exitCodes.map((e) => [
77
+ String(e.code),
78
+ `\`${e.name}\``,
79
+ e.description.replace(/\|/g, '\\|'),
80
+ ]);
81
+ return table(['Exit', 'Name', 'What to do'], rows);
82
+ }
83
+ /** The main SKILL.md, using only the six spec-portable frontmatter fields. */
84
+ export function renderSkillMd(opts) {
85
+ const { manifest } = opts;
86
+ const description = buildDescription(opts);
87
+ const issues = [...validateSkillName(manifest.name), ...validateDescription(description)];
88
+ const spending = manifest.commands.filter((c) => c.spends);
89
+ const body = `
90
+ # ${manifest.name}
91
+
92
+ ${manifest.description ?? `The \`${manifest.name}\` command-line tool.`}
93
+
94
+ Every command returns **one JSON document on stdout** when given \`--json\`, and a
95
+ **semantic exit code**. Read both. Progress and warnings go to stderr and are not
96
+ part of the result.
97
+
98
+ ## Check auth before the first command
99
+
100
+ \`\`\`bash
101
+ ${manifest.name} doctor --json
102
+ \`\`\`
103
+
104
+ Read \`.data.auth.ok\`:
105
+
106
+ - \`true\` — proceed.
107
+ - \`false\` — tell the user to run \`${manifest.name} login\` themselves. **Do not run it
108
+ for them**: it opens a browser and waits for a human to approve a code. Running it
109
+ will hang.
110
+
111
+ If \`.data.api.reachable\` is \`false\`, the network or the service is down. Say so;
112
+ do not retry in a loop.
113
+
114
+ ## Reading a result
115
+
116
+ \`\`\`bash
117
+ ${manifest.name} <command> --json
118
+ \`\`\`
119
+
120
+ | stdout \`status\` | Meaning | What to do |
121
+ |---|---|---|
122
+ | \`ok\` | Succeeded | Use \`.data\`. |
123
+ | \`error\` | Failed | Read \`.message\`. If \`.remediation\` is present it is the exact next command. Honour \`.retryable\`: when \`false\`, do not retry. |
124
+ | \`checkpoint\` | Waiting for approval | Show \`.display\` to the user verbatim and stop. See below. |
125
+
126
+ ## Commands
127
+
128
+ ${renderCommandsSection(manifest)}
129
+
130
+ ## Exit codes
131
+
132
+ ${renderExitCodesSection(manifest)}
133
+
134
+ ${spending.length
135
+ ? `## Approval gates
136
+
137
+ ${spending.map((c) => `\`${manifest.name} ${c.name}\``).join(', ')} can spend the user's money.
138
+ ${spending.length === 1 ? 'It stops' : 'They stop'} before doing so and ${spending.length === 1 ? 'exits' : 'exit'} **10** with \`"status": "checkpoint"\`.
139
+
140
+ When that happens:
141
+
142
+ 1. Print \`.display\` to the user **exactly as given**. It is a pre-rendered panel
143
+ showing the plan and the cost. Do not summarise, shorten or paraphrase it —
144
+ the user is deciding whether to pay, and they must see the real numbers.
145
+ 2. Ask whether to proceed.
146
+ 3. Only if they agree, run \`.next.approve\` verbatim. It is a complete command.
147
+ 4. If they decline, run \`.next.reject\` when present, and otherwise stop.
148
+
149
+ Exit **12** (\`checkpoint_stale\`) means the approval no longer matches reality —
150
+ it was already used, it expired, or the plan changed. Re-run the original command
151
+ without \`--approve\` to get a fresh plan, and ask the user again.
152
+
153
+ See \`references/checkpoints.md\`.
154
+ `
155
+ : ''}
156
+ ## Never
157
+
158
+ - **Never pass \`--yes\`.** It approves spending without asking the user. The gate
159
+ exists because the cost is theirs, not yours.
160
+ - **Never pass \`--token\` on the command line.** It is visible to every process on
161
+ the machine via \`ps\`. Authentication comes from \`${manifest.name} login\`.
162
+ - **Never retry on exit 7** (\`rate_limited\`). Retrying makes it worse. Report it.
163
+ - **Never retry on exit 4** (\`insufficient_spend\`) or **20** (\`declined\`). Neither
164
+ will succeed on a second attempt, and 20 means the user said no.
165
+ - **Never parse stderr** for results. It carries progress text whose wording changes.
166
+ - **Never invent a command.** Run \`${manifest.name} --help --json\` for the exact
167
+ schema of every command and option.
168
+
169
+ ${emptyCustomBlock('Add project-specific guidance here. It survives regeneration.')}
170
+
171
+ ## Reference
172
+
173
+ - \`references/commands.md\` — every option of every command
174
+ - \`references/errors.md\` — every exit code and the correct response
175
+ ${spending.length ? '- `references/checkpoints.md` — the approval flow in detail\n' : ''}`.trimStart();
176
+ return { content: `${renderFrontmatter(opts, description)}\n\n${body}`, issues };
177
+ }
178
+ export function renderCommandsReference(manifest) {
179
+ const sections = manifest.commands.map((cmd) => {
180
+ const lines = [`## \`${manifest.name} ${cmd.name}\``, '', cmd.description, ''];
181
+ if (cmd.spends) {
182
+ lines.push('**Spends money.** Stops at an approval gate and exits 10.', '');
183
+ }
184
+ lines.push('```bash', commandSignature(manifest.name, cmd) + ' --json', '```', '');
185
+ if (cmd.positionals.length) {
186
+ lines.push(table(['Positional', 'Required'], cmd.positionals.map((p) => [`\`<${p}>\``, 'yes'])), '');
187
+ }
188
+ if (cmd.options.length) {
189
+ lines.push(table(['Option', 'Type', 'Required', 'Values', 'Description'], cmd.options.map((o) => [
190
+ `\`--${o.name}\`` + (o.short ? ` / \`-${o.short}\`` : ''),
191
+ o.type,
192
+ o.required ? 'yes' : 'no',
193
+ o.choices ? o.choices.map((c) => `\`${c}\``).join(', ') : o.default !== undefined ? `default \`${String(o.default)}\`` : '—',
194
+ (o.description ?? '').replace(/\|/g, '\\|'),
195
+ ])), '');
196
+ }
197
+ else if (!cmd.positionals.length) {
198
+ lines.push('Takes no options.', '');
199
+ }
200
+ return lines.join('\n');
201
+ });
202
+ return `# ${manifest.name} — command reference
203
+
204
+ Generated from the tool schema; do not edit by hand.
205
+
206
+ ${sections.join('\n')}`;
207
+ }
208
+ export function renderErrorsReference(manifest) {
209
+ const custom = manifest.commands.flatMap((c) => Object.entries(c.exitCodes ?? {}).map(([code, desc]) => [c.name, code, desc]));
210
+ return `# ${manifest.name} — errors and exit codes
211
+
212
+ Every failure is one JSON document on stdout plus an exit code:
213
+
214
+ \`\`\`json
215
+ {"status":"error","code":"not_found","message":"…","remediation":"…","retryable":false}
216
+ \`\`\`
217
+
218
+ - \`remediation\`, when present, is the **exact command to run next**. Prefer it over
219
+ guessing.
220
+ - \`retryable: false\` means a retry cannot succeed. Do not loop.
221
+
222
+ ## Reserved exit codes
223
+
224
+ ${table(['Exit', 'Name', 'Retry?', 'What to do'], manifest.exitCodes.map((e) => [
225
+ String(e.code),
226
+ `\`${e.name}\``,
227
+ ['timeout', 'network'].includes(e.name) ? 'once' : 'no',
228
+ e.description.replace(/\|/g, '\\|'),
229
+ ]))}
230
+
231
+ ${custom.length
232
+ ? `## Tool-specific exit codes
233
+
234
+ ${table(['Command', 'Exit', 'Meaning'], custom.map(([cmd, code, desc]) => [`\`${cmd}\``, code, String(desc)]))}
235
+ `
236
+ : ''}
237
+ ## Codes that mean "stop", not "try again"
238
+
239
+ - **${EXIT.rate_limited} (\`rate_limited\`)** — ${EXIT_DESCRIPTION['rate_limited']}
240
+ - **${EXIT.insufficient_spend} (\`insufficient_spend\`)** — the user is out of balance. Only they can fix it.
241
+ - **${EXIT.declined} (\`declined\`)** — the user said no. Do not ask again in the same turn.
242
+ - **${EXIT.auth} (\`auth\`)** — run the \`remediation\`, which is \`${manifest.name} login\`, by telling the **user** to run it.
243
+ `;
244
+ }
245
+ export function renderCheckpointsReference(manifest) {
246
+ const spending = manifest.commands.filter((c) => c.spends);
247
+ const example = spending[0]?.name ?? 'deploy';
248
+ return `# ${manifest.name} — approval gates
249
+
250
+ Commands that spend money stop first and ask. This is not an error.
251
+
252
+ ## What you receive
253
+
254
+ \`\`\`json
255
+ {
256
+ "status": "checkpoint",
257
+ "schema": "invokable.checkpoint/v1",
258
+ "gate": "deploy_review",
259
+ "fingerprint": "GCI3HOREK4LY34J7",
260
+ "display": "┌───────────…┐\\n│ …the panel… │\\n└───────────┘",
261
+ "question": "Deploy this plan to production?",
262
+ "explain": "Approving starts the deploy and bills 1 credit per minute.",
263
+ "spend": { "estimated": 12, "balance": 100 },
264
+ "next": {
265
+ "approve": "${manifest.name} ${example} --env prod --json --approve deploy_review@GCI3HOREK4LY34J7",
266
+ "reject": "${manifest.name} ${example} --env prod --dry-run"
267
+ }
268
+ }
269
+ \`\`\`
270
+
271
+ Exit code: **10**.
272
+
273
+ ## What to do
274
+
275
+ 1. **Print \`display\` verbatim.** It is already formatted for a human and contains
276
+ the real cost. Paraphrasing it is how a user ends up agreeing to a number they
277
+ never saw.
278
+ 2. **Ask the user.** Quote \`question\`, and \`explain\` if present.
279
+ 3. **On yes:** run \`next.approve\` exactly as given. It is the original command with
280
+ the approval appended, so it needs no editing.
281
+ 4. **On no:** run \`next.reject\` if present; otherwise stop and say nothing was done.
282
+
283
+ ## Why you cannot skip this
284
+
285
+ The fingerprint is issued by the server, not computed locally. It is bound to the
286
+ gate, the target, and the exact plan you were shown; it expires; and it is consumed
287
+ once, by the action it authorises.
288
+
289
+ So an approval cannot be reused, and one issued against a plan that has since
290
+ changed is rejected with exit **12** (\`checkpoint_stale\`). On 12: re-run the
291
+ original command **without** \`--approve\`, show the user the new plan, and ask again.
292
+
293
+ ## \`--yes\`
294
+
295
+ \`--yes\` skips the question. **Do not pass it.** The server still records an
296
+ approval, so the audit trail will show that the spend was approved without a human
297
+ being asked — which is exactly the thing the user will object to afterwards.
298
+
299
+ If the user explicitly instructs you to run without prompting, prefer
300
+ \`--max-spend <n>\`, which refuses to auto-approve anything above the cap.
301
+ `;
302
+ }
303
+ /** Renders the complete portable skill bundle. */
304
+ export function renderSkill(opts) {
305
+ const { content, issues } = renderSkillMd(opts);
306
+ const files = [
307
+ { path: 'SKILL.md', content },
308
+ { path: 'references/commands.md', content: renderCommandsReference(opts.manifest) },
309
+ { path: 'references/errors.md', content: renderErrorsReference(opts.manifest) },
310
+ ];
311
+ if (opts.manifest.commands.some((c) => c.spends)) {
312
+ files.push({
313
+ path: 'references/checkpoints.md',
314
+ content: renderCheckpointsReference(opts.manifest),
315
+ });
316
+ }
317
+ return { name: opts.manifest.name, files, issues };
318
+ }
319
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAoC,MAAM,iBAAiB,CAAC;AAC3F,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,iBAAiB,EAAwB,MAAM,WAAW,CAAC;AAyB1G,SAAS,UAAU,CAAC,KAAa;IAC/B,yDAAyD;IACzD,IAAI,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1E,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,KAAK,CAAC,OAAiB,EAAE,IAAgB;IAChD,MAAM,KAAK,GAAG,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACxF,KAAK,MAAM,GAAG,IAAI,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7D,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY,EAAE,GAAqC;IAC3E,MAAM,KAAK,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,WAAW;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtD,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QAC9B,IAAI,CAAC,GAAG,CAAC,QAAQ;YAAE,SAAS;QAC5B,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;IACvF,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAmB;IAClD,IAAI,IAAI,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC,WAAW,CAAC;IAE9C,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IAC1B,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ;SAC5B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;SAC1F,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;SAClB,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAEf,MAAM,IAAI,GACR,QAAQ,CAAC,WAAW;QACpB,OAAO,QAAQ,CAAC,IAAI,yBAAyB,CAAC;IAEhD,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,EAAE,MAAM;QACvC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;QAC1B,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAErB,MAAM,KAAK,GAAG;QACZ,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QACxB,WAAW,CAAC,CAAC,CAAC,8BAA8B,WAAW,GAAG,CAAC,CAAC,CAAC,EAAE;QAC/D,yDAAyD,QAAQ,CAAC,IAAI,iBAAiB;KACxF,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAElB,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/B,OAAO,MAAM,CAAC,MAAM,GAAG,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;AAC/F,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAmB,EAAE,WAAmB;IACjE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IAC1B,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEnE,MAAM,KAAK,GAAG;QACZ,KAAK;QACL,SAAS,QAAQ,CAAC,IAAI,EAAE;QACxB,gBAAgB,UAAU,CAAC,WAAW,CAAC,EAAE;QACzC,kBAAkB,OAAO,EAAE;KAC5B,CAAC;IACF,IAAI,IAAI,CAAC,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,YAAY,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACrE,KAAK,CAAC,IAAI,CACR,WAAW,EACX,mBAAmB,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,EACjD,qCAAqC,CACtC,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,qBAAqB,CAAC,QAAsB;IACnD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACxC,KAAK,gBAAgB,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI;QAC3C,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9E,CAAC,CAAC;IACH,OAAO,KAAK,CAAC,CAAC,SAAS,EAAE,cAAc,CAAC,EAAE,IAAI,CAAC,CAAC;AAClD,CAAC;AAED,SAAS,sBAAsB,CAAC,QAAsB;IACpD,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QACd,KAAK,CAAC,CAAC,IAAI,IAAI;QACf,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;KACpC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,IAAI,CAAC,CAAC;AACrD,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,aAAa,CAAC,IAAmB;IAC/C,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IAC1B,MAAM,WAAW,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,CAAC,GAAG,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC,CAAC;IAE1F,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAE3D,MAAM,IAAI,GAAG;IACX,QAAQ,CAAC,IAAI;;EAEf,QAAQ,CAAC,WAAW,IAAI,SAAS,QAAQ,CAAC,IAAI,uBAAuB;;;;;;;;;EASrE,QAAQ,CAAC,IAAI;;;;;;uCAMwB,QAAQ,CAAC,IAAI;;;;;;;;;;EAUlD,QAAQ,CAAC,IAAI;;;;;;;;;;;EAWb,qBAAqB,CAAC,QAAQ,CAAC;;;;EAI/B,sBAAsB,CAAC,QAAQ,CAAC;;EAGhC,QAAQ,CAAC,MAAM;QACb,CAAC,CAAC;;EAEJ,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,QAAQ,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;EAEhE,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WACvC,wBAAwB,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM;;;;;;;;;;;;;;;;CAgB/D;QACG,CAAC,CAAC,EACN;;;;;;wDAMwD,QAAQ,CAAC,IAAI;;;;;sCAK/B,QAAQ,CAAC,IAAI;;;EAGjD,gBAAgB,CAAC,+DAA+D,CAAC;;;;;;EAMjF,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,+DAA+D,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,EAAE,CAAC;IAErG,OAAO,EAAE,OAAO,EAAE,GAAG,iBAAiB,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC;AACnF,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,QAAsB;IAC5D,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QAC7C,MAAM,KAAK,GAAG,CAAC,QAAQ,QAAQ,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAC/E,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YACf,KAAK,CAAC,IAAI,CAAC,2DAA2D,EAAE,EAAE,CAAC,CAAC;QAC9E,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,GAAG,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAEnF,IAAI,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CACR,KAAK,CACH,CAAC,YAAY,EAAE,UAAU,CAAC,EAC1B,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAClD,EACD,EAAE,CACH,CAAC;QACJ,CAAC;QAED,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CACR,KAAK,CACH,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,aAAa,CAAC,EACvD,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;gBACrB,OAAO,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;gBACzD,CAAC,CAAC,IAAI;gBACN,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;gBACzB,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG;gBAC5H,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;aAC5C,CAAC,CACH,EACD,EAAE,CACH,CAAC;QACJ,CAAC;aAAM,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;YACnC,KAAK,CAAC,IAAI,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,OAAO,KAAK,QAAQ,CAAC,IAAI;;;;EAIzB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,QAAsB;IAC1D,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAC7C,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAC,CACvF,CAAC;IAEF,OAAO,KAAK,QAAQ,CAAC,IAAI;;;;;;;;;;;;;;EAczB,KAAK,CACL,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,CAAC,EACxC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QAC5B,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QACd,KAAK,CAAC,CAAC,IAAI,IAAI;QACf,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;QACvD,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;KACpC,CAAC,CACH;;EAGC,MAAM,CAAC,MAAM;QACX,CAAC,CAAC;;EAEJ,KAAK,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;CAC7G;QACG,CAAC,CAAC,EACN;;;MAGM,IAAI,CAAC,YAAY,2BAA2B,gBAAgB,CAAC,cAA0B,CAAC;MACxF,IAAI,CAAC,kBAAkB;MACvB,IAAI,CAAC,QAAQ;MACb,IAAI,CAAC,IAAI,uDAAuD,QAAQ,CAAC,IAAI;CAClF,CAAC;AACF,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,QAAsB;IAC/D,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,QAAQ,CAAC;IAE9C,OAAO,KAAK,QAAQ,CAAC,IAAI;;;;;;;;;;;;;;;;;kBAiBT,QAAQ,CAAC,IAAI,IAAI,OAAO;iBACzB,QAAQ,CAAC,IAAI,IAAI,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmCxC,CAAC;AACF,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,WAAW,CAAC,IAAmB;IAC7C,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,KAAK,GAAmB;QAC5B,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE;QAC7B,EAAE,IAAI,EAAE,wBAAwB,EAAE,OAAO,EAAE,uBAAuB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;QACnF,EAAE,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;KAChF,CAAC;IACF,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACjD,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,2BAA2B;YACjC,OAAO,EAAE,0BAA0B,CAAC,IAAI,CAAC,QAAQ,CAAC;SACnD,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AACrD,CAAC"}
package/dist/spec.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Constraints from the Agent Skills specification (agentskills.io), as enforced
3
+ * by the Claude Skills API and by `package_skill.py` in anthropics/skills.
4
+ *
5
+ * Only six frontmatter fields are portable. Claude Code accepts many more, but
6
+ * a skill carrying any of them fails to upload to claude.ai or the Skills API
7
+ * with a hard error rather than being ignored — so a generator that wants one
8
+ * file to work everywhere must emit only these.
9
+ */
10
+ export declare const SPEC_FIELDS: readonly ["name", "description", "license", "compatibility", "metadata", "allowed-tools"];
11
+ export type SpecField = (typeof SPEC_FIELDS)[number];
12
+ export declare const NAME_PATTERN: RegExp;
13
+ export declare const NAME_MAX = 64;
14
+ export declare const DESCRIPTION_MAX = 1024;
15
+ export declare const COMPATIBILITY_MAX = 500;
16
+ export interface ValidationIssue {
17
+ field: string;
18
+ message: string;
19
+ }
20
+ export declare function validateSkillName(name: string): ValidationIssue[];
21
+ export declare function validateDescription(description: string): ValidationIssue[];
22
+ //# sourceMappingURL=spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../src/spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,2FAOd,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,eAAO,MAAM,YAAY,QAAsB,CAAC;AAChD,eAAO,MAAM,QAAQ,KAAK,CAAC;AAC3B,eAAO,MAAM,eAAe,OAAO,CAAC;AACpC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAKrC,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,EAAE,CAmBjE;AAED,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,EAAE,CAmB1E"}
package/dist/spec.js ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Constraints from the Agent Skills specification (agentskills.io), as enforced
3
+ * by the Claude Skills API and by `package_skill.py` in anthropics/skills.
4
+ *
5
+ * Only six frontmatter fields are portable. Claude Code accepts many more, but
6
+ * a skill carrying any of them fails to upload to claude.ai or the Skills API
7
+ * with a hard error rather than being ignored — so a generator that wants one
8
+ * file to work everywhere must emit only these.
9
+ */
10
+ export const SPEC_FIELDS = [
11
+ 'name',
12
+ 'description',
13
+ 'license',
14
+ 'compatibility',
15
+ 'metadata',
16
+ 'allowed-tools',
17
+ ];
18
+ export const NAME_PATTERN = /^[a-z0-9-]{1,64}$/;
19
+ export const NAME_MAX = 64;
20
+ export const DESCRIPTION_MAX = 1024;
21
+ export const COMPATIBILITY_MAX = 500;
22
+ /** Reserved by the spec; a skill named with these is rejected on upload. */
23
+ const RESERVED_WORDS = ['anthropic', 'claude'];
24
+ export function validateSkillName(name) {
25
+ const issues = [];
26
+ if (!NAME_PATTERN.test(name)) {
27
+ issues.push({
28
+ field: 'name',
29
+ message: `"${name}" is not a valid skill name. Use 1-${NAME_MAX} characters of ` +
30
+ 'lowercase letters, digits and hyphens only.',
31
+ });
32
+ }
33
+ for (const word of RESERVED_WORDS) {
34
+ if (name.toLowerCase().includes(word)) {
35
+ issues.push({
36
+ field: 'name',
37
+ message: `Skill names may not contain the reserved word "${word}".`,
38
+ });
39
+ }
40
+ }
41
+ return issues;
42
+ }
43
+ export function validateDescription(description) {
44
+ const issues = [];
45
+ if (!description.trim()) {
46
+ issues.push({ field: 'description', message: 'description must not be empty.' });
47
+ }
48
+ if (description.length > DESCRIPTION_MAX) {
49
+ issues.push({
50
+ field: 'description',
51
+ message: `description is ${description.length} characters; the limit is ${DESCRIPTION_MAX}.`,
52
+ });
53
+ }
54
+ // XML tags are rejected by the Skills API validator.
55
+ if (/<[a-zA-Z/][^>]*>/.test(description)) {
56
+ issues.push({
57
+ field: 'description',
58
+ message: 'description must not contain XML/HTML tags.',
59
+ });
60
+ }
61
+ return issues;
62
+ }
63
+ //# sourceMappingURL=spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.js","sourceRoot":"","sources":["../src/spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,MAAM;IACN,aAAa;IACb,SAAS;IACT,eAAe;IACf,UAAU;IACV,eAAe;CACP,CAAC;AAIX,MAAM,CAAC,MAAM,YAAY,GAAG,mBAAmB,CAAC;AAChD,MAAM,CAAC,MAAM,QAAQ,GAAG,EAAE,CAAC;AAC3B,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AACpC,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAErC,4EAA4E;AAC5E,MAAM,cAAc,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;AAO/C,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC;YACV,KAAK,EAAE,MAAM;YACb,OAAO,EACL,IAAI,IAAI,sCAAsC,QAAQ,iBAAiB;gBACvE,6CAA6C;SAChD,CAAC,CAAC;IACL,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;QAClC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC;gBACV,KAAK,EAAE,MAAM;gBACb,OAAO,EAAE,kDAAkD,IAAI,IAAI;aACpE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,WAAmB;IACrD,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC;QACxB,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,gCAAgC,EAAE,CAAC,CAAC;IACnF,CAAC;IACD,IAAI,WAAW,CAAC,MAAM,GAAG,eAAe,EAAE,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC;YACV,KAAK,EAAE,aAAa;YACpB,OAAO,EAAE,kBAAkB,WAAW,CAAC,MAAM,6BAA6B,eAAe,GAAG;SAC7F,CAAC,CAAC;IACL,CAAC;IACD,qDAAqD;IACrD,IAAI,kBAAkB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC;YACV,KAAK,EAAE,aAAa;YACpB,OAAO,EAAE,6CAA6C;SACvD,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,42 @@
1
+ import type { ToolManifest } from '@invokable/core';
2
+ /**
3
+ * Where each agent looks for instructions.
4
+ *
5
+ * Two kinds of target, because the ecosystem has two conventions:
6
+ *
7
+ * - `skill`: the Agent Skills standard (agentskills.io). One portable SKILL.md
8
+ * directory, identical bytes for every one of these tools — that is the point
9
+ * of the standard, and why this generator emits only the six spec-portable
10
+ * frontmatter fields rather than the wider set Claude Code alone accepts.
11
+ * - `section`: a shared instruction file (AGENTS.md and friends) that many other
12
+ * tools read. We own only a marked region of it and never touch the rest.
13
+ */
14
+ export type TargetKind = 'skill' | 'section' | 'mdc';
15
+ export interface Target {
16
+ id: string;
17
+ label: string;
18
+ kind: TargetKind;
19
+ /** Directory (kind `skill`) or file (kind `section`/`mdc`), relative to root. */
20
+ path: (toolName: string) => string;
21
+ /** Why this target exists, shown by `--check`. */
22
+ note: string;
23
+ }
24
+ export declare const TARGETS: readonly Target[];
25
+ export declare const DEFAULT_TARGET_IDS: string[];
26
+ export declare function targetById(id: string): Target | undefined;
27
+ /** Just the `@AGENTS.md` import, for when AGENTS.md carries the content. */
28
+ export declare function renderClaudeMdPointer(): string;
29
+ /**
30
+ * The section written into a shared instruction file. Deliberately short: these
31
+ * files load into context on every request for every tool that reads them, so
32
+ * the detail lives in the skill and this only says enough to route the agent
33
+ * there and prevent the two expensive mistakes.
34
+ */
35
+ export declare function renderSection(manifest: ToolManifest, skillPath: string): string;
36
+ /**
37
+ * Cursor's legacy rule format. `alwaysApply: false` with a description makes it
38
+ * agent-requested: Cursor pulls it in when the description matches the task,
39
+ * rather than taxing every request.
40
+ */
41
+ export declare function renderMdc(manifest: ToolManifest, description: string, skillPath: string): string;
42
+ //# sourceMappingURL=targets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"targets.d.ts","sourceRoot":"","sources":["../src/targets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,SAAS,GAAG,KAAK,CAAC;AAErD,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,UAAU,CAAC;IACjB,iFAAiF;IACjF,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IACnC,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,OAAO,EAAE,SAAS,MAAM,EAmEpC,CAAC;AAEF,eAAO,MAAM,kBAAkB,UAA2B,CAAC;AAE3D,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEzD;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAsB/E;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAQhG"}
@@ -0,0 +1,114 @@
1
+ export const TARGETS = [
2
+ // ---- Agent Skills standard ----------------------------------------------
3
+ {
4
+ id: 'claude-code',
5
+ label: 'Claude Code',
6
+ kind: 'skill',
7
+ path: (t) => `.claude/skills/${t}`,
8
+ note: 'Claude Code loads project skills from .claude/skills/.',
9
+ },
10
+ {
11
+ id: 'codex',
12
+ label: 'OpenAI Codex',
13
+ kind: 'skill',
14
+ path: (t) => `.codex/skills/${t}`,
15
+ note: 'Codex loads skills from .codex/skills/.',
16
+ },
17
+ {
18
+ id: 'cursor',
19
+ label: 'Cursor',
20
+ kind: 'skill',
21
+ path: (t) => `.cursor/skills/${t}`,
22
+ note: 'Cursor 2.4+ loads .cursor/skills/, and also reads .claude/skills/ and .codex/skills/.',
23
+ },
24
+ {
25
+ id: 'gemini',
26
+ label: 'Gemini CLI',
27
+ kind: 'skill',
28
+ path: (t) => `.gemini/skills/${t}`,
29
+ note: 'Gemini CLI loads skills from .gemini/skills/.',
30
+ },
31
+ {
32
+ id: 'agents-skills',
33
+ label: 'Cross-agent (.agents)',
34
+ kind: 'skill',
35
+ path: (t) => `.agents/skills/${t}`,
36
+ note: 'Emerging vendor-neutral location; harmless where unsupported.',
37
+ },
38
+ // ---- Shared instruction files -------------------------------------------
39
+ {
40
+ id: 'agents-md',
41
+ label: 'AGENTS.md',
42
+ kind: 'section',
43
+ path: () => 'AGENTS.md',
44
+ note: 'Read by 30+ agents (Codex, Copilot, Cursor, Gemini, Jules, Aider, Zed, Windsurf, Devin).',
45
+ },
46
+ {
47
+ id: 'claude-md',
48
+ label: 'CLAUDE.md',
49
+ kind: 'section',
50
+ path: () => 'CLAUDE.md',
51
+ note: 'Claude Code reads CLAUDE.md, not AGENTS.md.',
52
+ },
53
+ {
54
+ id: 'copilot',
55
+ label: 'GitHub Copilot',
56
+ kind: 'section',
57
+ path: () => '.github/copilot-instructions.md',
58
+ note: 'Repository-wide Copilot instructions.',
59
+ },
60
+ {
61
+ id: 'cursor-rules',
62
+ label: 'Cursor rules (legacy)',
63
+ kind: 'mdc',
64
+ path: (t) => `.cursor/rules/${t}.mdc`,
65
+ note: 'For Cursor versions predating skills support.',
66
+ },
67
+ ];
68
+ export const DEFAULT_TARGET_IDS = TARGETS.map((t) => t.id);
69
+ export function targetById(id) {
70
+ return TARGETS.find((t) => t.id === id);
71
+ }
72
+ /** Just the `@AGENTS.md` import, for when AGENTS.md carries the content. */
73
+ export function renderClaudeMdPointer() {
74
+ return '@AGENTS.md';
75
+ }
76
+ /**
77
+ * The section written into a shared instruction file. Deliberately short: these
78
+ * files load into context on every request for every tool that reads them, so
79
+ * the detail lives in the skill and this only says enough to route the agent
80
+ * there and prevent the two expensive mistakes.
81
+ */
82
+ export function renderSection(manifest, skillPath) {
83
+ const spending = manifest.commands.filter((c) => c.spends);
84
+ return `## ${manifest.name}
85
+
86
+ ${manifest.description ?? `The \`${manifest.name}\` CLI.`}
87
+
88
+ - Full instructions: \`${skillPath}/SKILL.md\`
89
+ - Always pass \`--json\`: one JSON document on stdout, semantic exit code. Never parse stderr.
90
+ - Check \`${manifest.name} doctor --json\` first. If \`.data.auth.ok\` is false, ask the
91
+ **user** to run \`${manifest.name} login\` — it needs a browser and will hang if you run it.
92
+ - \`status: "error"\` carries \`remediation\` (the exact next command) and \`retryable\`.
93
+ Never retry exit 7 (rate limited), 4 (insufficient balance) or 20 (declined).
94
+ ${spending.length
95
+ ? `- ${spending.map((c) => `\`${manifest.name} ${c.name}\``).join(', ')} ${spending.length === 1 ? 'spends' : 'spend'} money. ${spending.length === 1 ? 'It exits' : 'They exit'} **10**
96
+ with \`status: "checkpoint"\`. Print \`.display\` verbatim, ask the user, and only then
97
+ run \`.next.approve\`. Never pass \`--yes\`.`
98
+ : ''}`;
99
+ }
100
+ /**
101
+ * Cursor's legacy rule format. `alwaysApply: false` with a description makes it
102
+ * agent-requested: Cursor pulls it in when the description matches the task,
103
+ * rather than taxing every request.
104
+ */
105
+ export function renderMdc(manifest, description, skillPath) {
106
+ return `---
107
+ description: ${JSON.stringify(description)}
108
+ alwaysApply: false
109
+ ---
110
+
111
+ ${renderSection(manifest, skillPath)}
112
+ `;
113
+ }
114
+ //# sourceMappingURL=targets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"targets.js","sourceRoot":"","sources":["../src/targets.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@invokable/skills",
3
+ "version": "0.1.0",
4
+ "description": "Generates portable Agent Skills (SKILL.md) and agent instruction files from an invokable tool schema.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "engines": {
8
+ "node": ">=20"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ }
15
+ },
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "files": [
19
+ "dist",
20
+ "LICENSE"
21
+ ],
22
+ "dependencies": {
23
+ "@invokable/core": "^0.1.0"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/beinvokable/invokable.git",
28
+ "directory": "packages/skills"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "scripts": {
34
+ "build": "tsc -b",
35
+ "clean": "rm -rf dist *.tsbuildinfo"
36
+ }
37
+ }