@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 +21 -0
- package/README.md +77 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/init-command.d.ts +25 -0
- package/dist/init-command.d.ts.map +1 -0
- package/dist/init-command.js +61 -0
- package/dist/init-command.js.map +1 -0
- package/dist/install.d.ts +36 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +90 -0
- package/dist/install.js.map +1 -0
- package/dist/markers.d.ts +28 -0
- package/dist/markers.d.ts.map +1 -0
- package/dist/markers.js +88 -0
- package/dist/markers.js.map +1 -0
- package/dist/render.d.ts +39 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +319 -0
- package/dist/render.js.map +1 -0
- package/dist/spec.d.ts +22 -0
- package/dist/spec.d.ts.map +1 -0
- package/dist/spec.js +63 -0
- package/dist/spec.js.map +1 -0
- package/dist/targets.d.ts +42 -0
- package/dist/targets.d.ts.map +1 -0
- package/dist/targets.js +114 -0
- package/dist/targets.js.map +1 -0
- package/package.json +37 -0
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`.
|
package/dist/index.d.ts
ADDED
|
@@ -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":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD;;;;;GAKG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,OAAO,CAAC;QACb,WAAW,EAAE,oEAAoE;QACjF,OAAO,EAAE;YACP,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,kDAAkD,EAAE;YACxF,OAAO,EAAE;gBACP,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,0CAA0C,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;aACzF;YACD,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,6CAA6C,EAAE;YACtF,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,sCAAsC,EAAE;SAChF;QACD,SAAS,EAAE,EAAE,EAAE,EAAE,yDAAyD,EAAE;QAC5E,2EAA2E;QAC3E,gEAAgE;QAChE,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI;QACvB,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE;YACrB,MAAM,MAAM,GAAG,aAAa,CAAC;gBAC3B,QAAQ,EAAE,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;gBACjC,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACrD,GAAG,CAAC,IAAI,CAAC,OAAO,KAAK,SAAS;oBAC5B,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;oBAC3E,CAAC,CAAC,EAAE,CAAC;gBACP,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,KAAK,EAAE,IAAI,CAAC,KAAK;aAClB,CAAC,CAAC;YAEH,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC3D,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC;YACrE,MAAM,IAAI,GAAG,CAAC,MAAc,EAAU,EAAE,CACtC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YACjF,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;gBAC3B,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YACpD,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,MAAM;gBAAE,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;YAEvE,IAAI,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;gBACnC,sEAAsE;gBACtE,mCAAmC;gBACnC,MAAM,IAAI,cAAc,CAAC;oBACvB,IAAI,EAAE,oBAAoB;oBAC1B,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,qCAAqC;oBAC/D,WAAW,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,OAAO;oBACpC,QAAQ,EAAE,EAAE;oBACZ,SAAS,EAAE,KAAK;iBACjB,CAAC,CAAC;YACL,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -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"}
|
package/dist/install.js
ADDED
|
@@ -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"}
|
package/dist/markers.js
ADDED
|
@@ -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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"}
|
package/dist/render.d.ts
ADDED
|
@@ -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":"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"}
|
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
|
package/dist/spec.js.map
ADDED
|
@@ -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"}
|
package/dist/targets.js
ADDED
|
@@ -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
|
+
}
|