@skilly-hand/skilly-hand 0.17.0 → 0.18.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/CHANGELOG.md +20 -0
- package/catalog/skills/accessibility-audit/SKILL.md +21 -0
- package/catalog/skills/agents-root-orchestrator/SKILL.md +19 -0
- package/catalog/skills/angular-guidelines/SKILL.md +21 -0
- package/catalog/skills/figma-mcp-0to1/SKILL.md +21 -0
- package/catalog/skills/frontend-design/SKILL.md +17 -0
- package/catalog/skills/output-optimizer/SKILL.md +18 -0
- package/catalog/skills/project-security/SKILL.md +19 -0
- package/catalog/skills/project-teacher/SKILL.md +17 -0
- package/catalog/skills/react-guidelines/SKILL.md +21 -0
- package/catalog/skills/review-rangers/SKILL.md +17 -0
- package/catalog/skills/skill-creator/SKILL.md +34 -0
- package/catalog/skills/skill-creator/assets/SKILL-TEMPLATE.md +6 -0
- package/catalog/skills/spec-driven-development/SKILL.md +19 -0
- package/catalog/skills/test-driven-development/SKILL.md +17 -0
- package/catalog/skills/token-optimizer/SKILL.md +18 -0
- package/package.json +2 -2
- package/packages/catalog/package.json +1 -1
- package/packages/catalog/src/index.js +400 -4
- package/packages/cli/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/packages/detectors/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,26 @@ All notable changes to this project are documented in this file.
|
|
|
16
16
|
### Removed
|
|
17
17
|
- _None._
|
|
18
18
|
|
|
19
|
+
## [0.18.0] - 2026-04-08
|
|
20
|
+
[View on npm](https://www.npmjs.com/package/@skilly-hand/skilly-hand/v/0.18.0)
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- Added `sync-catalog` orchestration script to compute catalog README + skill frontmatter updates up front and apply writes atomically with rollback on failure.
|
|
24
|
+
- Added `sync-skill-frontmatter` CLI script with `--check`, `--json`, and `--skill` filtering support.
|
|
25
|
+
- Added regression coverage for catalog sync rollback/idempotency and frontmatter normalization edge cases (`tests/sync-catalog.test.js`, `tests/skill-frontmatter.test.js`).
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- Updated root `catalog:sync` script to run `scripts/sync-catalog.mjs` for unified catalog synchronization.
|
|
29
|
+
- Expanded script JSON contract coverage for `sync-catalog` and `sync-skill-frontmatter` in `tests/scripts-output.test.js`.
|
|
30
|
+
- Updated catalog validation flow to verify catalog README drift through dry-run sync checks.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
- Hardened skill frontmatter parsing and verification to avoid false frontmatter detection and preserve markdown content for malformed leading YAML-like blocks.
|
|
34
|
+
- Improved catalog README sync behavior to treat CRLF/LF-equivalent content as in sync.
|
|
35
|
+
|
|
36
|
+
### Removed
|
|
37
|
+
- _None._
|
|
38
|
+
|
|
19
39
|
## [0.17.0] - 2026-04-08
|
|
20
40
|
[View on npm](https://www.npmjs.com/package/@skilly-hand/skilly-hand/v/0.17.0)
|
|
21
41
|
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Audit web accessibility against W3C WCAG 2.2 Level AA using framework-agnostic checks, remediation patterns, and portable command-line scanning."
|
|
3
|
+
skillMetadata:
|
|
4
|
+
author: "skilly-hand"
|
|
5
|
+
last-edit: "2026-04-04"
|
|
6
|
+
license: "Apache-2.0"
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
changelog: "Added portable WCAG 2.2 Level AA accessibility auditing skill with W3C-only references and scanner script; enables consistent web accessibility review across frameworks; affects catalog skill coverage and install plans for stacks recommending accessibility-audit"
|
|
9
|
+
auto-invoke: "Auditing, reviewing, or implementing web accessibility against WCAG 2.2 Level AA"
|
|
10
|
+
allowed-tools:
|
|
11
|
+
- "Read"
|
|
12
|
+
- "Edit"
|
|
13
|
+
- "Write"
|
|
14
|
+
- "Glob"
|
|
15
|
+
- "Grep"
|
|
16
|
+
- "Bash"
|
|
17
|
+
- "WebFetch"
|
|
18
|
+
- "WebSearch"
|
|
19
|
+
- "Task"
|
|
20
|
+
- "SubAgent"
|
|
21
|
+
---
|
|
1
22
|
# Accessibility Audit Guide
|
|
2
23
|
|
|
3
24
|
## When to Use
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Author root AGENTS.md as a Where/What/When orchestrator that routes tasks and skill invocation clearly."
|
|
3
|
+
skillMetadata:
|
|
4
|
+
author: "skilly-hand"
|
|
5
|
+
last-edit: "2026-04-03"
|
|
6
|
+
license: "Apache-2.0"
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
changelog: "Added root AGENTS orchestration guidance around Where/What/When structure; improves AI task routing clarity and trigger recognition; affects root AGENTS authoring workflow"
|
|
9
|
+
auto-invoke: "Creating or updating root AGENTS.md orchestration guidance"
|
|
10
|
+
allowed-tools:
|
|
11
|
+
- "Read"
|
|
12
|
+
- "Edit"
|
|
13
|
+
- "Write"
|
|
14
|
+
- "Glob"
|
|
15
|
+
- "Grep"
|
|
16
|
+
- "Bash"
|
|
17
|
+
- "Task"
|
|
18
|
+
- "SubAgent"
|
|
19
|
+
---
|
|
1
20
|
# AGENTS Root Orchestrator Guide
|
|
2
21
|
|
|
3
22
|
## When to Use
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Guide Angular code generation and review using latest stable Angular verification and modern framework best practices."
|
|
3
|
+
skillMetadata:
|
|
4
|
+
author: "skilly-hand"
|
|
5
|
+
last-edit: "2026-04-03"
|
|
6
|
+
license: "Apache-2.0"
|
|
7
|
+
version: "1.1.1"
|
|
8
|
+
changelog: "Added allowed-modes metadata to declare angular-guidelines sub-agent routing targets; improves discoverability of component-creator and angular-tester delegation modes; affects angular-guidelines manifest metadata"
|
|
9
|
+
auto-invoke: "Generating, reviewing, or refactoring Angular code artifacts in Angular projects"
|
|
10
|
+
allowed-tools:
|
|
11
|
+
- "Read"
|
|
12
|
+
- "Edit"
|
|
13
|
+
- "Write"
|
|
14
|
+
- "Glob"
|
|
15
|
+
- "Grep"
|
|
16
|
+
- "Bash"
|
|
17
|
+
- "WebFetch"
|
|
18
|
+
- "WebSearch"
|
|
19
|
+
- "Task"
|
|
20
|
+
- "SubAgent"
|
|
21
|
+
---
|
|
1
22
|
# Angular Guidelines
|
|
2
23
|
|
|
3
24
|
## When to Use
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Guide users from Figma MCP installation and authentication through first canvas creation, with function-level tool coverage and operational recovery patterns."
|
|
3
|
+
skillMetadata:
|
|
4
|
+
author: "skilly-hand"
|
|
5
|
+
last-edit: "2026-04-03"
|
|
6
|
+
license: "Apache-2.0"
|
|
7
|
+
version: "1.0.1"
|
|
8
|
+
changelog: "Added allowed-modes metadata to declare figma-mcp-0to1 sub-agent routing targets; improves discoverability of install-auth, tool-function-catalog, canvas-creation-playbook, and troubleshooting-ops delegation modes; affects figma-mcp-0to1 manifest metadata"
|
|
9
|
+
auto-invoke: "Installing, configuring, or using Figma MCP from setup through first canvas creation"
|
|
10
|
+
allowed-tools:
|
|
11
|
+
- "Read"
|
|
12
|
+
- "Edit"
|
|
13
|
+
- "Write"
|
|
14
|
+
- "Glob"
|
|
15
|
+
- "Grep"
|
|
16
|
+
- "Bash"
|
|
17
|
+
- "WebFetch"
|
|
18
|
+
- "WebSearch"
|
|
19
|
+
- "Task"
|
|
20
|
+
- "SubAgent"
|
|
21
|
+
---
|
|
1
22
|
# Figma MCP 0-to-1 Guide
|
|
2
23
|
|
|
3
24
|
## When to Use
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Project-aware frontend design skill that detects the existing tech stack, UI libraries, CSS variables, and design tokens before proposing any UI work. Supports greenfield projects via DESIGN.md context setup, and includes post-generation motion and visual refinement phases."
|
|
3
|
+
skillMetadata:
|
|
4
|
+
author: "skilly-hand"
|
|
5
|
+
last-edit: "2026-04-05"
|
|
6
|
+
license: "Apache-2.0"
|
|
7
|
+
version: "1.1.0"
|
|
8
|
+
changelog: "v1.1.0: Added design-context-setter agent for greenfield/DESIGN.md workflow; added visual-refiner agent for post-generation quality evaluation; added motion-designer agent for stack-aware micro-interactions; added aesthetic-archetypes reference asset; expanded SKILL.md routing map with optional motion and refinement phases; upgraded component-designer with interaction states checklist and aesthetic principles"
|
|
9
|
+
auto-invoke: "Designing or generating UI components, pages, or layouts in a web or mobile project; setting up visual direction for a greenfield project; adding motion or micro-interactions to existing UI; refining or polishing generated UI output"
|
|
10
|
+
allowed-tools:
|
|
11
|
+
- "Read"
|
|
12
|
+
- "Grep"
|
|
13
|
+
- "Glob"
|
|
14
|
+
- "Bash"
|
|
15
|
+
- "Edit"
|
|
16
|
+
- "Write"
|
|
17
|
+
---
|
|
1
18
|
# Frontend Design Guide
|
|
2
19
|
|
|
3
20
|
## When to Use
|
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Optimize output token consumption through compact interpreter modes with controlled expansion when complexity, ambiguity, or risk requires more detail. Trigger: minimizing response verbosity while preserving clarity and correctness."
|
|
3
|
+
skillMetadata:
|
|
4
|
+
author: "skilly-hand"
|
|
5
|
+
last-edit: "2026-04-07"
|
|
6
|
+
license: "Apache-2.0"
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
changelog: "Added a new portable output compression skill with deterministic interpreter modes and guarded detail expansion; reduces response token costs while preserving safety and clarity; affects response shaping workflows and catalog routing"
|
|
9
|
+
auto-invoke: "When minimizing output verbosity or selecting compact communication modes"
|
|
10
|
+
allowed-tools:
|
|
11
|
+
- "Read"
|
|
12
|
+
- "Edit"
|
|
13
|
+
- "Write"
|
|
14
|
+
- "Glob"
|
|
15
|
+
- "Grep"
|
|
16
|
+
- "Bash"
|
|
17
|
+
- "Task"
|
|
18
|
+
---
|
|
1
19
|
# Output Optimizer Guide
|
|
2
20
|
|
|
3
21
|
## When to Use
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Scan project configuration and release surfaces for leak and security risks, and enforce security gates on commit, push, and publish workflows across GitHub, GitLab, npm, pnpm, yarn, and generic CI. Trigger: validating repository security posture, preventing secret leaks, or hardening delivery pipelines."
|
|
3
|
+
skillMetadata:
|
|
4
|
+
author: "skilly-hand"
|
|
5
|
+
last-edit: "2026-04-07"
|
|
6
|
+
license: "Apache-2.0"
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
changelog: "Added portable project-security skill with commit/push/publish gating assets and CI templates; reduces secret leak and misconfiguration risk before delivery; affects catalog security workflow coverage and auto-invoke routing"
|
|
9
|
+
auto-invoke: "Scanning project configuration and delivery workflows for leaks or security issues before commit, push, or publish"
|
|
10
|
+
allowed-tools:
|
|
11
|
+
- "Read"
|
|
12
|
+
- "Edit"
|
|
13
|
+
- "Write"
|
|
14
|
+
- "Glob"
|
|
15
|
+
- "Grep"
|
|
16
|
+
- "Bash"
|
|
17
|
+
- "Task"
|
|
18
|
+
- "SubAgent"
|
|
19
|
+
---
|
|
1
20
|
# Project Security Guide
|
|
2
21
|
|
|
3
22
|
## When to Use
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Scan the active project and teach any concept, code path, or decision using verified information, interactive questions, and simple explanations. Trigger: user asks to explain, understand, clarify, or learn about anything in the project or codebase."
|
|
3
|
+
skillMetadata:
|
|
4
|
+
author: "skilly-hand"
|
|
5
|
+
last-edit: "2026-04-04"
|
|
6
|
+
license: "Apache-2.0"
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
changelog: "Initial release of project-teacher skill; provides interactive, project-grounded teaching for any concept or code path; affects education and clarification workflows across all projects"
|
|
9
|
+
auto-invoke: "User needs to understand, explain, or learn about any aspect of the project or codebase"
|
|
10
|
+
allowed-tools:
|
|
11
|
+
- "Read"
|
|
12
|
+
- "Glob"
|
|
13
|
+
- "Grep"
|
|
14
|
+
- "Bash"
|
|
15
|
+
- "WebFetch"
|
|
16
|
+
- "WebSearch"
|
|
17
|
+
---
|
|
1
18
|
# Project Teacher Guide
|
|
2
19
|
|
|
3
20
|
## When to Use
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Guide React code generation and review using latest stable React verification and modern framework best practices."
|
|
3
|
+
skillMetadata:
|
|
4
|
+
author: "skilly-hand"
|
|
5
|
+
last-edit: "2026-04-04"
|
|
6
|
+
license: "Apache-2.0"
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
changelog: "Added new react-guidelines skill with component and testing sub-agent routing; improves React-specific generation and review consistency with latest-stable preflight checks; affects portable catalog skill discovery and React workflow guidance"
|
|
9
|
+
auto-invoke: "Generating, reviewing, or refactoring React code artifacts in React projects"
|
|
10
|
+
allowed-tools:
|
|
11
|
+
- "Read"
|
|
12
|
+
- "Edit"
|
|
13
|
+
- "Write"
|
|
14
|
+
- "Glob"
|
|
15
|
+
- "Grep"
|
|
16
|
+
- "Bash"
|
|
17
|
+
- "WebFetch"
|
|
18
|
+
- "WebSearch"
|
|
19
|
+
- "Task"
|
|
20
|
+
- "SubAgent"
|
|
21
|
+
---
|
|
1
22
|
# React Guidelines
|
|
2
23
|
|
|
3
24
|
## When to Use
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Review code, decisions, and artifacts through a multi-perspective committee and a domain expert safety guard, then synthesize a structured verdict."
|
|
3
|
+
skillMetadata:
|
|
4
|
+
author: "skilly-hand"
|
|
5
|
+
last-edit: "2026-04-04"
|
|
6
|
+
license: "Apache-2.0"
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
changelog: "Added multi-perspective review skill with committee + safety guard synthesis; enables adversarial evaluation without permanent agent files; affects catalog skill coverage for review and quality workflows"
|
|
9
|
+
auto-invoke: "Reviewing code, decisions, or artifacts where adversarial multi-perspective evaluation adds value"
|
|
10
|
+
allowed-tools:
|
|
11
|
+
- "Read"
|
|
12
|
+
- "Grep"
|
|
13
|
+
- "Glob"
|
|
14
|
+
- "Bash"
|
|
15
|
+
- "Task"
|
|
16
|
+
- "SubAgent"
|
|
17
|
+
---
|
|
1
18
|
# Review Rangers Guide
|
|
2
19
|
|
|
3
20
|
## When to Use
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Create and standardize AI skills with reusable structure, metadata rules, and templates."
|
|
3
|
+
skillMetadata:
|
|
4
|
+
author: "skilly-hand"
|
|
5
|
+
last-edit: "2026-03-27"
|
|
6
|
+
license: "Apache-2.0"
|
|
7
|
+
version: "1.2.3"
|
|
8
|
+
changelog: "Metadata updated to ensure compliance with current standards; maintains skill integrity and version tracking; affects metadata section"
|
|
9
|
+
auto-invoke: "Creating a new skill"
|
|
10
|
+
allowed-tools:
|
|
11
|
+
- "Read"
|
|
12
|
+
- "Edit"
|
|
13
|
+
- "Write"
|
|
14
|
+
- "Glob"
|
|
15
|
+
- "Grep"
|
|
16
|
+
- "Bash"
|
|
17
|
+
- "WebFetch"
|
|
18
|
+
- "WebSearch"
|
|
19
|
+
- "Task"
|
|
20
|
+
- "SubAgent"
|
|
21
|
+
---
|
|
1
22
|
# Skill Creator Guide
|
|
2
23
|
|
|
3
24
|
## When to Create a Skill
|
|
@@ -87,6 +108,17 @@ Generic skill needs {product-name} info? -> Add references/ pointing to {produ
|
|
|
87
108
|
| `skillMetadata.allowed-tools` | Yes | String list | All tools this skill can invoke (e.g., `Read`, `Edit`, `Write`, `SubAgent`) |
|
|
88
109
|
| `skillMetadata.allowed-modes` | Optional | String list | Use only when skill has an `agents/` folder |
|
|
89
110
|
|
|
111
|
+
### SKILL.md Frontmatter Mirroring
|
|
112
|
+
|
|
113
|
+
Top-level `SKILL.md` files now include managed YAML frontmatter mirrored from `manifest.json`.
|
|
114
|
+
|
|
115
|
+
Rules:
|
|
116
|
+
|
|
117
|
+
- `manifest.json` is the single source of truth.
|
|
118
|
+
- Mirror only `description` and `skillMetadata.{author,last-edit,license,version,changelog,auto-invoke,allowed-tools}`.
|
|
119
|
+
- Do not manually edit mirrored frontmatter in `SKILL.md`; run sync automation instead.
|
|
120
|
+
- Keep instruction body content in `SKILL.md` focused on workflow guidance.
|
|
121
|
+
|
|
90
122
|
---
|
|
91
123
|
|
|
92
124
|
## Metadata Standards
|
|
@@ -153,6 +185,7 @@ Do not:
|
|
|
153
185
|
- Use web URLs in references.
|
|
154
186
|
- Leave `changelog` empty or informal.
|
|
155
187
|
- Use non-ISO date formats.
|
|
188
|
+
- Manually drift `SKILL.md` frontmatter away from `manifest.json`.
|
|
156
189
|
|
|
157
190
|
---
|
|
158
191
|
|
|
@@ -167,6 +200,7 @@ Do not:
|
|
|
167
200
|
- [ ] `changelog` uses structured format: `what; why; where`.
|
|
168
201
|
- [ ] `allowed-modes` is present only when `agents/` exists.
|
|
169
202
|
- [ ] `allowed-tools` matches actual tool usage.
|
|
203
|
+
- [ ] `SKILL.md` frontmatter is synced from `manifest.json`.
|
|
170
204
|
- [ ] Critical patterns are clear and concise.
|
|
171
205
|
- [ ] Code examples are minimal and focused.
|
|
172
206
|
- [ ] Commands section exists with copy-paste commands.
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# {Name of the Skill} Guide
|
|
2
2
|
|
|
3
|
+
<!--
|
|
4
|
+
Managed frontmatter is mirrored from manifest.json by automation.
|
|
5
|
+
Do not hand-author frontmatter in this template.
|
|
6
|
+
-->
|
|
7
|
+
|
|
3
8
|
## When to Use
|
|
4
9
|
|
|
5
10
|
Use this skill when:
|
|
@@ -74,3 +79,4 @@ Otherwise -> {Default action}
|
|
|
74
79
|
|
|
75
80
|
- Template assets: Place reusable templates, schemas, and examples in `assets/`.
|
|
76
81
|
- Define metadata in `manifest.json` (`id`, `description`, `skillMetadata`, `allowed-tools`, optional `allowed-modes`).
|
|
82
|
+
- Run skill frontmatter sync so top-level `SKILL.md` mirrors manifest metadata.
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Plan, execute, and verify multi-step work through versioned specs with small, testable tasks."
|
|
3
|
+
skillMetadata:
|
|
4
|
+
author: "skilly-hand"
|
|
5
|
+
last-edit: "2026-04-03"
|
|
6
|
+
license: "Apache-2.0"
|
|
7
|
+
version: "1.0.3"
|
|
8
|
+
changelog: "Added OpenSpec complementary support routing guidance to spec-driven-development instructions; improves planning continuity and review clarity when local SDD needs reinforcement; affects spec-driven-development SKILL guidance and manifest metadata"
|
|
9
|
+
auto-invoke: "Planning or executing feature work, bug fixes, and multi-phase implementation"
|
|
10
|
+
allowed-tools:
|
|
11
|
+
- "Read"
|
|
12
|
+
- "Edit"
|
|
13
|
+
- "Write"
|
|
14
|
+
- "Glob"
|
|
15
|
+
- "Grep"
|
|
16
|
+
- "Bash"
|
|
17
|
+
- "Task"
|
|
18
|
+
- "SubAgent"
|
|
19
|
+
---
|
|
1
20
|
# Spec-Driven Development Guide
|
|
2
21
|
|
|
3
22
|
## When to Use
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Guide implementation using the RED → GREEN → REFACTOR TDD cycle: write a failing test first, write the minimum code to pass, then refactor while tests stay green."
|
|
3
|
+
skillMetadata:
|
|
4
|
+
author: "skilly-hand"
|
|
5
|
+
last-edit: "2026-04-04"
|
|
6
|
+
license: "Apache-2.0"
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
changelog: "Initial TDD skill ported from legacy scannlab-sdd tdd-templates; enables RED→GREEN→REFACTOR workflow across any stack; affects catalog skill coverage for test-first development"
|
|
9
|
+
auto-invoke: "Implementing features, services, or components using test-driven development (TDD) or RED→GREEN→REFACTOR cycles"
|
|
10
|
+
allowed-tools:
|
|
11
|
+
- "Read"
|
|
12
|
+
- "Edit"
|
|
13
|
+
- "Write"
|
|
14
|
+
- "Glob"
|
|
15
|
+
- "Grep"
|
|
16
|
+
- "Bash"
|
|
17
|
+
---
|
|
1
18
|
# Test-Driven Development Guide
|
|
2
19
|
|
|
3
20
|
## When to Use
|
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Classify task complexity and right-size reasoning depth, context gathering, and response detail to reduce wasted tokens."
|
|
3
|
+
skillMetadata:
|
|
4
|
+
author: "skilly-hand"
|
|
5
|
+
last-edit: "2026-04-03"
|
|
6
|
+
license: "Apache-2.0"
|
|
7
|
+
version: "1.0.3"
|
|
8
|
+
changelog: "Migrated token-optimizer into portable catalog format with curated model-agnostic guidance; improves default reasoning and token-efficiency behavior across installs; affects skill discovery, auto-invoke routing, and install baseline"
|
|
9
|
+
auto-invoke: "Classifying task complexity and choosing reasoning depth/token budget"
|
|
10
|
+
allowed-tools:
|
|
11
|
+
- "Read"
|
|
12
|
+
- "Edit"
|
|
13
|
+
- "Write"
|
|
14
|
+
- "Glob"
|
|
15
|
+
- "Grep"
|
|
16
|
+
- "Bash"
|
|
17
|
+
- "Task"
|
|
18
|
+
---
|
|
1
19
|
# Token Optimizer Guide
|
|
2
20
|
|
|
3
21
|
## When to Use
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skilly-hand/skilly-hand",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"license": "CC-BY-NC-4.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"scripts": {
|
|
27
27
|
"build": "node ./scripts/build-catalog-index.mjs",
|
|
28
28
|
"catalog:check": "node ./scripts/check-catalog.mjs",
|
|
29
|
-
"catalog:sync": "node ./scripts/sync-catalog
|
|
29
|
+
"catalog:sync": "node ./scripts/sync-catalog.mjs",
|
|
30
30
|
"agentic:self:sync": "node ./scripts/sync-self-agentic.mjs",
|
|
31
31
|
"test": "node --test tests/*.test.js && node ./scripts/test-in-sandbox.mjs",
|
|
32
32
|
"security:check": "node ./scripts/security-check.mjs",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cp, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
1
|
+
import { cp, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
|
|
@@ -21,6 +21,16 @@ const REQUIRED_FIELDS = [
|
|
|
21
21
|
"dependencies"
|
|
22
22
|
];
|
|
23
23
|
|
|
24
|
+
const MIRRORED_SKILL_METADATA_KEYS = [
|
|
25
|
+
"author",
|
|
26
|
+
"last-edit",
|
|
27
|
+
"license",
|
|
28
|
+
"version",
|
|
29
|
+
"changelog",
|
|
30
|
+
"auto-invoke",
|
|
31
|
+
"allowed-tools"
|
|
32
|
+
];
|
|
33
|
+
|
|
24
34
|
export function getCatalogRoot() {
|
|
25
35
|
return catalogDir;
|
|
26
36
|
}
|
|
@@ -70,9 +80,372 @@ export function validateSkillManifest(manifest) {
|
|
|
70
80
|
throw new Error(`Skill "${manifest.id}" must declare files`);
|
|
71
81
|
}
|
|
72
82
|
|
|
83
|
+
const hasSkillInstruction = manifest.files.some((file) => file.path === "SKILL.md" && file.kind === "instruction");
|
|
84
|
+
if (!hasSkillInstruction) {
|
|
85
|
+
throw new Error(`Skill "${manifest.id}" must include files entry for SKILL.md as instruction`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
assertFrontmatterFields(manifest);
|
|
89
|
+
|
|
73
90
|
return true;
|
|
74
91
|
}
|
|
75
92
|
|
|
93
|
+
function toLf(text) {
|
|
94
|
+
return text.replaceAll("\r\n", "\n");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function splitLinesWithOffsets(text) {
|
|
98
|
+
const lines = [];
|
|
99
|
+
let start = 0;
|
|
100
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
101
|
+
if (text[index] === "\n") {
|
|
102
|
+
lines.push({
|
|
103
|
+
text: text.slice(start, index),
|
|
104
|
+
start,
|
|
105
|
+
end: index + 1
|
|
106
|
+
});
|
|
107
|
+
start = index + 1;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (start < text.length) {
|
|
111
|
+
lines.push({
|
|
112
|
+
text: text.slice(start),
|
|
113
|
+
start,
|
|
114
|
+
end: text.length
|
|
115
|
+
});
|
|
116
|
+
} else if (text.length === 0) {
|
|
117
|
+
lines.push({ text: "", start: 0, end: 0 });
|
|
118
|
+
}
|
|
119
|
+
return lines;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function yamlQuote(value) {
|
|
123
|
+
return JSON.stringify(String(value));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function assertFrontmatterFields(manifest) {
|
|
127
|
+
if (!manifest || typeof manifest !== "object") {
|
|
128
|
+
throw new Error("Invalid manifest while building SKILL.md frontmatter");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (typeof manifest.description !== "string" || manifest.description.length === 0) {
|
|
132
|
+
throw new Error(`Skill "${manifest.id}" is missing required manifest.description for frontmatter mirroring`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!manifest.skillMetadata || typeof manifest.skillMetadata !== "object") {
|
|
136
|
+
throw new Error(`Skill "${manifest.id}" is missing required manifest.skillMetadata for frontmatter mirroring`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
for (const key of MIRRORED_SKILL_METADATA_KEYS) {
|
|
140
|
+
if (!(key in manifest.skillMetadata)) {
|
|
141
|
+
throw new Error(`Skill "${manifest.id}" is missing required skillMetadata.${key} for frontmatter mirroring`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const scalarKeys = ["author", "last-edit", "license", "version", "changelog", "auto-invoke"];
|
|
146
|
+
for (const key of scalarKeys) {
|
|
147
|
+
if (typeof manifest.skillMetadata[key] !== "string" || manifest.skillMetadata[key].trim().length === 0) {
|
|
148
|
+
throw new Error(`Skill "${manifest.id}" has invalid skillMetadata.${key}; expected a non-empty string`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!Array.isArray(manifest.skillMetadata["allowed-tools"])) {
|
|
153
|
+
throw new Error(`Skill "${manifest.id}" must declare skillMetadata.allowed-tools as an array`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
for (const tool of manifest.skillMetadata["allowed-tools"]) {
|
|
157
|
+
if (typeof tool !== "string" || tool.trim().length === 0) {
|
|
158
|
+
throw new Error(`Skill "${manifest.id}" has invalid skillMetadata.allowed-tools; expected non-empty strings`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function buildSkillFrontmatterPayload(manifest) {
|
|
164
|
+
assertFrontmatterFields(manifest);
|
|
165
|
+
return {
|
|
166
|
+
description: manifest.description,
|
|
167
|
+
skillMetadata: {
|
|
168
|
+
author: manifest.skillMetadata.author,
|
|
169
|
+
"last-edit": manifest.skillMetadata["last-edit"],
|
|
170
|
+
license: manifest.skillMetadata.license,
|
|
171
|
+
version: manifest.skillMetadata.version,
|
|
172
|
+
changelog: manifest.skillMetadata.changelog,
|
|
173
|
+
"auto-invoke": manifest.skillMetadata["auto-invoke"],
|
|
174
|
+
"allowed-tools": [...manifest.skillMetadata["allowed-tools"]]
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function renderSkillFrontmatterInner(payload) {
|
|
180
|
+
const lines = [
|
|
181
|
+
`description: ${yamlQuote(payload.description)}`,
|
|
182
|
+
"skillMetadata:",
|
|
183
|
+
` author: ${yamlQuote(payload.skillMetadata.author)}`,
|
|
184
|
+
` last-edit: ${yamlQuote(payload.skillMetadata["last-edit"])}`,
|
|
185
|
+
` license: ${yamlQuote(payload.skillMetadata.license)}`,
|
|
186
|
+
` version: ${yamlQuote(payload.skillMetadata.version)}`,
|
|
187
|
+
` changelog: ${yamlQuote(payload.skillMetadata.changelog)}`,
|
|
188
|
+
` auto-invoke: ${yamlQuote(payload.skillMetadata["auto-invoke"])}`,
|
|
189
|
+
" allowed-tools:"
|
|
190
|
+
];
|
|
191
|
+
|
|
192
|
+
for (const tool of payload.skillMetadata["allowed-tools"]) {
|
|
193
|
+
lines.push(` - ${yamlQuote(tool)}`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return lines.join("\n");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function renderSkillFrontmatter(manifest) {
|
|
200
|
+
const payload = buildSkillFrontmatterPayload(manifest);
|
|
201
|
+
return `---\n${renderSkillFrontmatterInner(payload)}\n---\n`;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function splitSkillMarkdown(content) {
|
|
205
|
+
const normalized = toLf(content);
|
|
206
|
+
const source = normalized.startsWith("\uFEFF") ? normalized.slice(1) : normalized;
|
|
207
|
+
const lines = splitLinesWithOffsets(source);
|
|
208
|
+
const mirroredKeys = new Set([
|
|
209
|
+
"description",
|
|
210
|
+
"skillMetadata",
|
|
211
|
+
"author",
|
|
212
|
+
"last-edit",
|
|
213
|
+
"license",
|
|
214
|
+
"version",
|
|
215
|
+
"changelog",
|
|
216
|
+
"auto-invoke",
|
|
217
|
+
"allowed-tools"
|
|
218
|
+
]);
|
|
219
|
+
const isYamlLike = (line) => (
|
|
220
|
+
line.trim().length === 0 ||
|
|
221
|
+
/^\s*[A-Za-z0-9_-]+:(?:\s.*)?$/.test(line) ||
|
|
222
|
+
/^\s*-\s+.*$/.test(line)
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
let firstNonBlankIndex = 0;
|
|
226
|
+
while (firstNonBlankIndex < lines.length && lines[firstNonBlankIndex].text.trim().length === 0) {
|
|
227
|
+
firstNonBlankIndex += 1;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (firstNonBlankIndex >= lines.length || lines[firstNonBlankIndex].text !== "---") {
|
|
231
|
+
return {
|
|
232
|
+
hasFrontmatter: false,
|
|
233
|
+
malformedFrontmatter: false,
|
|
234
|
+
frontmatter: null,
|
|
235
|
+
body: source
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const openLine = lines[firstNonBlankIndex];
|
|
240
|
+
let sawKeyValue = false;
|
|
241
|
+
let sawMirroredKey = false;
|
|
242
|
+
const detectedKeys = new Set();
|
|
243
|
+
|
|
244
|
+
for (let index = firstNonBlankIndex + 1; index < lines.length; index += 1) {
|
|
245
|
+
const line = lines[index].text;
|
|
246
|
+
const nextLine = index + 1 < lines.length ? lines[index + 1].text : null;
|
|
247
|
+
|
|
248
|
+
if (
|
|
249
|
+
sawMirroredKey &&
|
|
250
|
+
line.trim().length === 0 &&
|
|
251
|
+
nextLine &&
|
|
252
|
+
/^(?:[-*+]\s+|\d+\.\s+|#{1,6}\s+|>\s+|```)/.test(nextLine)
|
|
253
|
+
) {
|
|
254
|
+
return {
|
|
255
|
+
hasFrontmatter: true,
|
|
256
|
+
malformedFrontmatter: true,
|
|
257
|
+
frontmatter: null,
|
|
258
|
+
body: source.slice(lines[index + 1].start)
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (line === "---") {
|
|
263
|
+
if (!sawKeyValue || !sawMirroredKey) {
|
|
264
|
+
return {
|
|
265
|
+
hasFrontmatter: false,
|
|
266
|
+
malformedFrontmatter: false,
|
|
267
|
+
frontmatter: null,
|
|
268
|
+
body: source
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
const end = lines[index].end;
|
|
272
|
+
const frontmatter = source.slice(openLine.start, end);
|
|
273
|
+
return {
|
|
274
|
+
hasFrontmatter: true,
|
|
275
|
+
malformedFrontmatter: false,
|
|
276
|
+
frontmatter: frontmatter.endsWith("\n") ? frontmatter : `${frontmatter}\n`,
|
|
277
|
+
body: source.slice(end),
|
|
278
|
+
detectedKeys: Array.from(detectedKeys)
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (/^\s*[A-Za-z0-9_-]+:(?:\s.*)?$/.test(line)) {
|
|
283
|
+
sawKeyValue = true;
|
|
284
|
+
const key = line.split(":", 1)[0].trim();
|
|
285
|
+
detectedKeys.add(key);
|
|
286
|
+
if (mirroredKeys.has(key)) {
|
|
287
|
+
sawMirroredKey = true;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (!isYamlLike(line)) {
|
|
292
|
+
if (!sawKeyValue || !sawMirroredKey) {
|
|
293
|
+
return {
|
|
294
|
+
hasFrontmatter: false,
|
|
295
|
+
malformedFrontmatter: false,
|
|
296
|
+
frontmatter: null,
|
|
297
|
+
body: source
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
hasFrontmatter: true,
|
|
302
|
+
malformedFrontmatter: true,
|
|
303
|
+
frontmatter: null,
|
|
304
|
+
body: source.slice(lines[index].start),
|
|
305
|
+
detectedKeys: Array.from(detectedKeys)
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (!sawKeyValue) {
|
|
311
|
+
return {
|
|
312
|
+
hasFrontmatter: false,
|
|
313
|
+
malformedFrontmatter: false,
|
|
314
|
+
frontmatter: null,
|
|
315
|
+
body: source
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (!sawMirroredKey) {
|
|
320
|
+
return {
|
|
321
|
+
hasFrontmatter: false,
|
|
322
|
+
malformedFrontmatter: false,
|
|
323
|
+
frontmatter: null,
|
|
324
|
+
body: source
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return {
|
|
329
|
+
hasFrontmatter: true,
|
|
330
|
+
malformedFrontmatter: true,
|
|
331
|
+
frontmatter: null,
|
|
332
|
+
body: "",
|
|
333
|
+
detectedKeys: Array.from(detectedKeys)
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export function applyManifestFrontmatterToSkill(content, manifest) {
|
|
338
|
+
const expectedFrontmatter = renderSkillFrontmatter(manifest);
|
|
339
|
+
const parts = splitSkillMarkdown(content);
|
|
340
|
+
return `${expectedFrontmatter}${parts.body}`;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export function verifySkillFrontmatterContent(content, manifest) {
|
|
344
|
+
const expectedFrontmatter = renderSkillFrontmatter(manifest);
|
|
345
|
+
const parts = splitSkillMarkdown(content);
|
|
346
|
+
if (!parts.hasFrontmatter) {
|
|
347
|
+
return { ok: false, reason: "missing" };
|
|
348
|
+
}
|
|
349
|
+
if (parts.malformedFrontmatter || !parts.frontmatter) {
|
|
350
|
+
return { ok: false, reason: "malformed" };
|
|
351
|
+
}
|
|
352
|
+
if (parts.frontmatter !== expectedFrontmatter) {
|
|
353
|
+
return { ok: false, reason: "mismatch" };
|
|
354
|
+
}
|
|
355
|
+
const residual = splitSkillMarkdown(parts.body);
|
|
356
|
+
if (residual.hasFrontmatter && !residual.malformedFrontmatter && residual.frontmatter === expectedFrontmatter) {
|
|
357
|
+
return { ok: false, reason: "residual-frontmatter" };
|
|
358
|
+
}
|
|
359
|
+
return { ok: true, reason: null };
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export async function syncSkillFrontmatter({ skillId, dryRun = false } = {}) {
|
|
363
|
+
const plan = await planSkillFrontmatterSync({ skillId });
|
|
364
|
+
if (!dryRun) {
|
|
365
|
+
await applyTextUpdatesAtomically(plan.updates);
|
|
366
|
+
}
|
|
367
|
+
return {
|
|
368
|
+
skillCount: plan.skillCount,
|
|
369
|
+
updatedSkillIds: plan.updatedSkillIds
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export async function planSkillFrontmatterSync({ skillId } = {}) {
|
|
374
|
+
const allIds = await listSkillIds();
|
|
375
|
+
if (skillId && !allIds.includes(skillId)) {
|
|
376
|
+
throw new Error(`Unknown skill id: ${skillId}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const ids = skillId ? [skillId] : allIds;
|
|
380
|
+
const updatedSkillIds = [];
|
|
381
|
+
const updates = [];
|
|
382
|
+
|
|
383
|
+
for (const id of ids) {
|
|
384
|
+
const manifest = await loadSkillManifest(id);
|
|
385
|
+
const skillPath = path.join(skillsDir, id, "SKILL.md");
|
|
386
|
+
const current = await readFile(skillPath, "utf8");
|
|
387
|
+
const next = applyManifestFrontmatterToSkill(current, manifest);
|
|
388
|
+
if (next !== toLf(current)) {
|
|
389
|
+
updatedSkillIds.push(id);
|
|
390
|
+
updates.push({
|
|
391
|
+
skillId: id,
|
|
392
|
+
path: skillPath,
|
|
393
|
+
content: next
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
skillCount: ids.length,
|
|
400
|
+
updatedSkillIds,
|
|
401
|
+
updates
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export async function applyTextUpdatesAtomically(updates) {
|
|
406
|
+
const deduped = [];
|
|
407
|
+
const seenPaths = new Set();
|
|
408
|
+
for (let index = updates.length - 1; index >= 0; index -= 1) {
|
|
409
|
+
const update = updates[index];
|
|
410
|
+
if (!seenPaths.has(update.path)) {
|
|
411
|
+
seenPaths.add(update.path);
|
|
412
|
+
deduped.push(update);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
deduped.reverse();
|
|
416
|
+
|
|
417
|
+
const originals = new Map();
|
|
418
|
+
for (const update of deduped) {
|
|
419
|
+
try {
|
|
420
|
+
originals.set(update.path, await readFile(update.path, "utf8"));
|
|
421
|
+
} catch (error) {
|
|
422
|
+
if (error?.code === "ENOENT") {
|
|
423
|
+
originals.set(update.path, null);
|
|
424
|
+
} else {
|
|
425
|
+
throw error;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const writtenPaths = [];
|
|
431
|
+
try {
|
|
432
|
+
for (const update of deduped) {
|
|
433
|
+
await writeFile(update.path, update.content, "utf8");
|
|
434
|
+
writtenPaths.push(update.path);
|
|
435
|
+
}
|
|
436
|
+
} catch (error) {
|
|
437
|
+
for (const targetPath of writtenPaths.reverse()) {
|
|
438
|
+
const original = originals.get(targetPath);
|
|
439
|
+
if (original === null) {
|
|
440
|
+
await rm(targetPath, { force: true });
|
|
441
|
+
} else {
|
|
442
|
+
await writeFile(targetPath, original, "utf8");
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
throw error;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
76
449
|
export async function copySkillTo(targetCatalogDir, skillId) {
|
|
77
450
|
const source = path.join(skillsDir, skillId);
|
|
78
451
|
const destination = path.join(targetCatalogDir, skillId);
|
|
@@ -88,6 +461,7 @@ export async function readTemplate(templateName) {
|
|
|
88
461
|
}
|
|
89
462
|
|
|
90
463
|
export function renderAgentsMarkdown({ skills, detections, generatedAt, projectName }) {
|
|
464
|
+
const escapeTableCell = (value) => String(value).replaceAll("|", "\\|").replaceAll("\n", "<br>");
|
|
91
465
|
const sortedSkills = [...skills].sort((a, b) => a.id.localeCompare(b.id));
|
|
92
466
|
const sortedDetections = [...detections].sort((a, b) => a.technology.localeCompare(b.technology));
|
|
93
467
|
const autoInvokeSkills = sortedSkills.filter((skill) => skill.skillMetadata?.["auto-invoke"]);
|
|
@@ -114,7 +488,7 @@ export function renderAgentsMarkdown({ skills, detections, generatedAt, projectN
|
|
|
114
488
|
];
|
|
115
489
|
|
|
116
490
|
for (const skill of sortedSkills) {
|
|
117
|
-
lines.push(`| \`${skill.id}\` | ${skill.description} | ${skill.tags.join(", ")} |`);
|
|
491
|
+
lines.push(`| \`${skill.id}\` | ${escapeTableCell(skill.description)} | ${escapeTableCell(skill.tags.join(", "))} |`);
|
|
118
492
|
}
|
|
119
493
|
|
|
120
494
|
lines.push(
|
|
@@ -126,7 +500,7 @@ export function renderAgentsMarkdown({ skills, detections, generatedAt, projectN
|
|
|
126
500
|
"1. Always run `token-optimizer` first to classify complexity and set the minimum viable reasoning depth.",
|
|
127
501
|
"2. Always run `output-optimizer` immediately after `token-optimizer` for response-shape control.",
|
|
128
502
|
"3. `output-optimizer` mode policy:",
|
|
129
|
-
" - Default:
|
|
503
|
+
" - Default: use `step-brief` when there is no explicit mode or strong phrasing signal.",
|
|
130
504
|
" - Override: if user explicitly requests a mode (for example `mode: step-brief`), that explicit mode wins.",
|
|
131
505
|
" - Persistence: keep the explicitly requested mode active until the user asks for a different mode.",
|
|
132
506
|
"",
|
|
@@ -154,7 +528,7 @@ export function renderAgentsMarkdown({ skills, detections, generatedAt, projectN
|
|
|
154
528
|
} else {
|
|
155
529
|
lines.push("| Action | Skill |", "| ------ | ----- |");
|
|
156
530
|
for (const skill of autoInvokeSkills) {
|
|
157
|
-
lines.push(`| ${skill.skillMetadata["auto-invoke"]} | \`${skill.id}\` |`);
|
|
531
|
+
lines.push(`| ${escapeTableCell(skill.skillMetadata["auto-invoke"])} | \`${skill.id}\` |`);
|
|
158
532
|
}
|
|
159
533
|
}
|
|
160
534
|
|
|
@@ -230,6 +604,28 @@ export async function verifyCatalogFiles() {
|
|
|
230
604
|
issues.push(`Missing file for ${skillId}: ${file.path}`);
|
|
231
605
|
}
|
|
232
606
|
}
|
|
607
|
+
|
|
608
|
+
const skillDocPath = path.join(skillPath, "SKILL.md");
|
|
609
|
+
let skillDocContent;
|
|
610
|
+
try {
|
|
611
|
+
skillDocContent = await readFile(skillDocPath, "utf8");
|
|
612
|
+
} catch (error) {
|
|
613
|
+
if (error?.code === "ENOENT") {
|
|
614
|
+
// Missing file is already surfaced above via manifest file verification.
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
issues.push(`Cannot read ${skillId}/SKILL.md: ${error.message}`);
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
try {
|
|
622
|
+
const status = verifySkillFrontmatterContent(skillDocContent, manifest);
|
|
623
|
+
if (!status.ok) {
|
|
624
|
+
issues.push(`Frontmatter ${status.reason} for ${skillId}: SKILL.md`);
|
|
625
|
+
}
|
|
626
|
+
} catch (error) {
|
|
627
|
+
issues.push(`Frontmatter validation failed for ${skillId}: ${error.message}`);
|
|
628
|
+
}
|
|
233
629
|
}
|
|
234
630
|
|
|
235
631
|
return issues;
|