kushi-agents 5.0.2 → 5.0.4
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/README.md +35 -0
- package/bin/cli.mjs +103 -0
- package/package.json +6 -2
- package/plugin/agents/kushi.agent.md +3 -1
- package/plugin/instructions/skill-authoring.instructions.md +147 -0
- package/plugin/instructions/skill-evals.instructions.md +130 -0
- package/plugin/skills/aggregate-project/evals/evals.json +33 -0
- package/plugin/skills/apply-ado-update/evals/evals.json +33 -0
- package/plugin/skills/ask-project/SKILL.md +10 -0
- package/plugin/skills/ask-project/evals/evals.json +34 -0
- package/plugin/skills/bootstrap-project/evals/evals.json +34 -0
- package/plugin/skills/build-state/evals/evals.json +31 -0
- package/plugin/skills/consolidate-evidence/evals/evals.json +33 -0
- package/plugin/skills/dashboard/evals/evals.json +33 -0
- package/plugin/skills/emit-vertex/evals/evals.json +33 -0
- package/plugin/skills/eval/SKILL.md +90 -0
- package/plugin/skills/eval/evals.schema.json +73 -0
- package/plugin/skills/eval/run-evals.ps1 +372 -0
- package/plugin/skills/fde-intake/evals/evals.json +33 -0
- package/plugin/skills/fde-report/evals/evals.json +33 -0
- package/plugin/skills/fde-triage/evals/evals.json +33 -0
- package/plugin/skills/intro/SKILL.md +160 -451
- package/plugin/skills/intro/evals/evals.json +33 -0
- package/plugin/skills/intro/references/walkthrough.md +310 -0
- package/plugin/skills/link-entities/evals/evals.json +31 -0
- package/plugin/skills/project-status/SKILL.md +10 -1
- package/plugin/skills/project-status/evals/evals.json +33 -0
- package/plugin/skills/propose-ado-update/evals/evals.json +33 -0
- package/plugin/skills/pull-ado/evals/evals.json +35 -0
- package/plugin/skills/pull-crm/evals/evals.json +35 -0
- package/plugin/skills/pull-email/evals/evals.json +35 -0
- package/plugin/skills/pull-loop/evals/evals.json +35 -0
- package/plugin/skills/pull-meetings/evals/evals.json +35 -0
- package/plugin/skills/pull-misc/evals/evals.json +35 -0
- package/plugin/skills/pull-onenote/evals/evals.json +35 -0
- package/plugin/skills/pull-sharepoint/evals/evals.json +35 -0
- package/plugin/skills/pull-teams/evals/evals.json +35 -0
- package/plugin/skills/refresh-project/evals/evals.json +31 -0
- package/plugin/skills/self-check/SKILL.md +2 -0
- package/plugin/skills/self-check/evals/evals.json +28 -0
- package/plugin/skills/self-check/run.ps1 +144 -0
- package/plugin/skills/setup/SKILL.md +10 -0
- package/plugin/skills/setup/evals/evals.json +33 -0
- package/plugin/skills/skill-checker/SKILL.md +136 -0
- package/plugin/skills/skill-checker/check-skill.ps1 +416 -0
- package/plugin/skills/skill-checker/evals/evals.json +41 -0
- package/plugin/skills/skill-creator/SKILL.md +134 -0
- package/plugin/skills/skill-creator/evals/evals.json +40 -0
- package/plugin/skills/skill-creator/generate-eval-review.ps1 +101 -0
- package/plugin/skills/skill-creator/optimize-description.ps1 +87 -0
- package/plugin/skills/skill-creator/scaffold.ps1 +180 -0
- package/plugin/skills/skill-creator/templates/evals-starter.template.json +27 -0
- package/plugin/skills/skill-creator/templates/gotchas-stub.template.md +9 -0
- package/plugin/skills/skill-creator/templates/skill-skeleton.template.md +28 -0
- package/plugin/skills/tour/evals/evals.json +33 -0
- package/plugin/skills/vertex-link/SKILL.md +10 -0
- package/plugin/skills/vertex-link/evals/evals.json +33 -0
- package/src/eval-aggregator.mjs +209 -0
- package/src/eval-aggregator.test.mjs +64 -0
- package/src/eval-runner.test.mjs +69 -0
- package/src/skill-checker.test.mjs +118 -0
- package/src/skill-creator.test.mjs +92 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
skill-checker — lint, retrofit, apply, optimize-description, and review for
|
|
4
|
+
plugin/skills/<name>/ trees.
|
|
5
|
+
|
|
6
|
+
.DESCRIPTION
|
|
7
|
+
Default mode is -Lint: reuses self-check D30 + D33 logic to validate that a
|
|
8
|
+
skill matches the agentskills.io blueprint as codified in
|
|
9
|
+
plugin/instructions/skill-authoring.instructions.md.
|
|
10
|
+
|
|
11
|
+
-Retrofit emits a non-destructive fix plan; -Apply executes only the additive
|
|
12
|
+
parts. -OptimizeDescription runs the description optimizer; -Review renders the
|
|
13
|
+
HTML eval-review viewer.
|
|
14
|
+
|
|
15
|
+
The skill blueprint:
|
|
16
|
+
- YAML frontmatter with `name:` matching the directory.
|
|
17
|
+
- `description:` containing "USE WHEN" in the first 160 chars.
|
|
18
|
+
- At least one of: ## Gotchas / ## Validation loop / ## Step checklist.
|
|
19
|
+
- For pull-* skills: ## Gotchas REQUIRED.
|
|
20
|
+
- For writer skills: ## Validation loop REQUIRED.
|
|
21
|
+
- For orchestrators: ## Step checklist REQUIRED.
|
|
22
|
+
- SKILL.md size: <=500 lines, <=5000 tokens.
|
|
23
|
+
- evals/evals.json present with >=2 cases.
|
|
24
|
+
|
|
25
|
+
The "writer" + "orchestrator" lists below mirror self-check/run.ps1.
|
|
26
|
+
#>
|
|
27
|
+
[CmdletBinding(DefaultParameterSetName='Lint')]
|
|
28
|
+
param(
|
|
29
|
+
[string]$Skill,
|
|
30
|
+
[switch]$All,
|
|
31
|
+
[switch]$Retrofit,
|
|
32
|
+
[switch]$Apply,
|
|
33
|
+
[switch]$DryRun,
|
|
34
|
+
[switch]$OptimizeDescription,
|
|
35
|
+
[switch]$Review,
|
|
36
|
+
[string]$Output,
|
|
37
|
+
[switch]$Json,
|
|
38
|
+
[switch]$StrictExit,
|
|
39
|
+
[string]$Root = (Resolve-Path (Join-Path $PSScriptRoot "..\..\..")).Path
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
$ErrorActionPreference = 'Stop'
|
|
43
|
+
|
|
44
|
+
# ---- helpers ---------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
$writerSkills = @(
|
|
47
|
+
'bootstrap-project', 'refresh-project', 'build-state', 'consolidate-evidence',
|
|
48
|
+
'link-entities', 'dashboard', 'tour', 'aggregate-project', 'project-status',
|
|
49
|
+
'fde-intake', 'fde-report', 'fde-triage', 'emit-vertex', 'vertex-link',
|
|
50
|
+
'propose-ado-update', 'apply-ado-update'
|
|
51
|
+
)
|
|
52
|
+
$orchestratorSkills = @(
|
|
53
|
+
'bootstrap-project', 'refresh-project', 'build-state', 'link-entities',
|
|
54
|
+
'dashboard', 'tour'
|
|
55
|
+
)
|
|
56
|
+
$excludeFromAll = @('eval', 'self-check', 'skill-creator', 'skill-checker')
|
|
57
|
+
|
|
58
|
+
function Get-Frontmatter {
|
|
59
|
+
param([string]$Path)
|
|
60
|
+
$text = Get-Content -Raw -Path $Path
|
|
61
|
+
if ($text -notmatch '^(?s)---\r?\n(.*?)\r?\n---') { return @{} }
|
|
62
|
+
$yaml = $Matches[1]
|
|
63
|
+
$fm = @{}
|
|
64
|
+
foreach ($line in $yaml -split "`n") {
|
|
65
|
+
if ($line -match '^\s*([a-zA-Z0-9_-]+)\s*:\s*(.+?)\s*$') {
|
|
66
|
+
$key = $Matches[1]
|
|
67
|
+
$val = $Matches[2].Trim('"', "'", ' ')
|
|
68
|
+
$fm[$key] = $val
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return $fm
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function Get-SkillType {
|
|
75
|
+
param([string]$Name)
|
|
76
|
+
if ($Name -like 'pull-*') { return 'pull' }
|
|
77
|
+
if ($orchestratorSkills -contains $Name) { return 'orchestrator' }
|
|
78
|
+
if ($writerSkills -contains $Name) { return 'writer' }
|
|
79
|
+
return 'other'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function Get-TargetSkills {
|
|
83
|
+
param([string]$Root, [string]$Skill, [switch]$All)
|
|
84
|
+
$skillsRoot = Join-Path $Root 'plugin/skills'
|
|
85
|
+
if ($Skill) {
|
|
86
|
+
$d = Join-Path $skillsRoot $Skill
|
|
87
|
+
if (-not (Test-Path $d)) { throw "No such skill: $Skill (looked at $d)" }
|
|
88
|
+
return @(Get-Item -LiteralPath $d)
|
|
89
|
+
}
|
|
90
|
+
if ($All) {
|
|
91
|
+
return Get-ChildItem -Path $skillsRoot -Directory | Where-Object { $excludeFromAll -notcontains $_.Name }
|
|
92
|
+
}
|
|
93
|
+
throw "Specify -Skill <name> or -All."
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function Lint-Skill {
|
|
97
|
+
param([System.IO.DirectoryInfo]$Dir)
|
|
98
|
+
$findings = New-Object System.Collections.Generic.List[object]
|
|
99
|
+
$skillMd = Join-Path $Dir.FullName 'SKILL.md'
|
|
100
|
+
if (-not (Test-Path $skillMd)) {
|
|
101
|
+
$findings.Add([pscustomobject]@{ code='SC1.no-skill-md'; severity='error'; message="SKILL.md missing in $($Dir.Name)/"; kind='non-additive' })
|
|
102
|
+
return $findings
|
|
103
|
+
}
|
|
104
|
+
$text = Get-Content -Raw $skillMd
|
|
105
|
+
$lines = (Get-Content $skillMd).Count
|
|
106
|
+
$tokens = [math]::Round($text.Length / 4)
|
|
107
|
+
$fm = Get-Frontmatter $skillMd
|
|
108
|
+
$type = Get-SkillType -Name $Dir.Name
|
|
109
|
+
|
|
110
|
+
# Frontmatter name match.
|
|
111
|
+
if ($fm['name'] -and $fm['name'] -ne $Dir.Name) {
|
|
112
|
+
$findings.Add([pscustomobject]@{ code='SC2.name-mismatch'; severity='error'; message="frontmatter name '$($fm['name'])' != directory '$($Dir.Name)'"; kind='non-additive' })
|
|
113
|
+
}
|
|
114
|
+
if (-not $fm['name']) {
|
|
115
|
+
$findings.Add([pscustomobject]@{ code='SC2.no-name'; severity='error'; message="frontmatter missing 'name:'"; kind='non-additive' })
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# Description shape.
|
|
119
|
+
$desc = $fm['description']
|
|
120
|
+
if (-not $desc) {
|
|
121
|
+
$findings.Add([pscustomobject]@{ code='SC3.no-description'; severity='error'; message="frontmatter missing 'description:'"; kind='non-additive' })
|
|
122
|
+
} else {
|
|
123
|
+
$head = $desc.Substring(0, [Math]::Min(160, $desc.Length))
|
|
124
|
+
if ($head -notmatch '\bUSE WHEN\b') {
|
|
125
|
+
$findings.Add([pscustomobject]@{ code='SC3.description-no-use-when'; severity='warning'; message="description does not lead with 'USE WHEN' in first 160 chars"; kind='non-additive' })
|
|
126
|
+
}
|
|
127
|
+
if ($desc.Length -gt 1024) {
|
|
128
|
+
$findings.Add([pscustomobject]@{ code='SC3.description-too-long'; severity='warning'; message="description is $($desc.Length) chars (cap 1024)"; kind='non-additive' })
|
|
129
|
+
}
|
|
130
|
+
# Marketing-word check: case-sensitive lowercase only so title-cased
|
|
131
|
+
# proper nouns (e.g. "Comprehensive Structured Capture") are not flagged.
|
|
132
|
+
$marketing = @('powerful', 'comprehensive', 'blazing', 'seamless', 'effortless', 'cutting-edge', 'world-class')
|
|
133
|
+
foreach ($w in $marketing) {
|
|
134
|
+
if ($desc -cmatch "\b$w\b") {
|
|
135
|
+
$findings.Add([pscustomobject]@{ code='SC3.description-marketing'; severity='warning'; message="description contains marketing word '$w'"; kind='non-additive' })
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# Size caps.
|
|
141
|
+
if ($lines -gt 500) {
|
|
142
|
+
$findings.Add([pscustomobject]@{ code='SC4.skill-too-long'; severity='warning'; message="SKILL.md is $lines lines (cap 500)"; kind='non-additive' })
|
|
143
|
+
}
|
|
144
|
+
if ($tokens -gt 5000) {
|
|
145
|
+
$findings.Add([pscustomobject]@{ code='SC4.skill-too-many-tokens'; severity='warning'; message="SKILL.md is ~$tokens tokens (cap 5000)"; kind='non-additive' })
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
# Section presence per type.
|
|
149
|
+
$hasGotchas = $text -match '(?m)^##\s+Gotchas\b'
|
|
150
|
+
$hasValidation = $text -match '(?m)^##\s+Validation loop\b'
|
|
151
|
+
$hasChecklist = ($text -match '(?m)^##\s+Step checklist\b') -or ($text -match '(?m)^\s*-\s+\[ \]')
|
|
152
|
+
|
|
153
|
+
if ($type -eq 'pull' -and -not $hasGotchas) {
|
|
154
|
+
$findings.Add([pscustomobject]@{ code='SC5.missing-gotchas'; severity='warning'; message="pull-* skill missing '## Gotchas' section"; kind='additive'; fix='append-gotchas' })
|
|
155
|
+
}
|
|
156
|
+
if ($type -eq 'writer' -and -not $hasValidation) {
|
|
157
|
+
$findings.Add([pscustomobject]@{ code='SC5.missing-validation-loop'; severity='warning'; message="writer skill missing '## Validation loop' section"; kind='additive'; fix='append-validation' })
|
|
158
|
+
}
|
|
159
|
+
if ($type -eq 'orchestrator' -and -not $hasChecklist) {
|
|
160
|
+
$findings.Add([pscustomobject]@{ code='SC5.missing-checklist'; severity='warning'; message="orchestrator missing '## Step checklist' (or '- [ ]' items)"; kind='additive'; fix='append-checklist' })
|
|
161
|
+
}
|
|
162
|
+
if ($type -eq 'other' -and -not ($hasGotchas -or $hasValidation -or $hasChecklist)) {
|
|
163
|
+
$findings.Add([pscustomobject]@{ code='SC5.missing-procedure-section'; severity='warning'; message="skill needs at least one of '## Gotchas' / '## Validation loop' / '## Step checklist'"; kind='additive'; fix='append-validation' })
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
# Evals presence.
|
|
167
|
+
$evalsFile = Join-Path $Dir.FullName 'evals/evals.json'
|
|
168
|
+
if (-not (Test-Path $evalsFile)) {
|
|
169
|
+
$findings.Add([pscustomobject]@{ code='SC6.no-evals'; severity='warning'; message="evals/evals.json missing"; kind='additive'; fix='create-evals' })
|
|
170
|
+
} else {
|
|
171
|
+
try {
|
|
172
|
+
$obj = Get-Content -Raw $evalsFile | ConvertFrom-Json
|
|
173
|
+
if (-not $obj.cases -or $obj.cases.Count -lt 2) {
|
|
174
|
+
$findings.Add([pscustomobject]@{ code='SC6.evals-fewer-than-2'; severity='warning'; message="evals.json has fewer than 2 cases"; kind='non-additive' })
|
|
175
|
+
}
|
|
176
|
+
if ($obj.skill -ne $Dir.Name) {
|
|
177
|
+
$findings.Add([pscustomobject]@{ code='SC6.evals-skill-mismatch'; severity='warning'; message="evals.json declares skill='$($obj.skill)' (expected '$($Dir.Name)')"; kind='non-additive' })
|
|
178
|
+
}
|
|
179
|
+
} catch {
|
|
180
|
+
$findings.Add([pscustomobject]@{ code='SC6.evals-invalid-json'; severity='error'; message="evals.json is not valid JSON: $($_.Exception.Message)"; kind='non-additive' })
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
# References load-on-trigger.
|
|
185
|
+
$refsDir = Join-Path $Dir.FullName 'references'
|
|
186
|
+
if (Test-Path $refsDir) {
|
|
187
|
+
if ($text -notmatch 'references/[A-Za-z0-9_\-]+\.md') {
|
|
188
|
+
$findings.Add([pscustomobject]@{ code='SC7.references-no-trigger'; severity='warning'; message="references/ folder present but SKILL.md never cites 'references/<file>.md'"; kind='non-additive' })
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return $findings
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function Get-AppendBlock {
|
|
196
|
+
param([string]$Fix)
|
|
197
|
+
switch ($Fix) {
|
|
198
|
+
'append-gotchas' {
|
|
199
|
+
return @"
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
## Gotchas
|
|
203
|
+
|
|
204
|
+
<!-- TODO(retrofit): fill in — top-5 failure modes for this skill. Each bullet starts with the concrete failure mode, then the correction. Auto-added by skill-checker --retrofit --apply per skill-authoring.instructions.md. -->
|
|
205
|
+
|
|
206
|
+
- **<failure mode 1>** — <correction>
|
|
207
|
+
- **<failure mode 2>** — <correction>
|
|
208
|
+
- **<failure mode 3>** — <correction>
|
|
209
|
+
- **<failure mode 4>** — <correction>
|
|
210
|
+
- **<failure mode 5>** — <correction>
|
|
211
|
+
"@
|
|
212
|
+
}
|
|
213
|
+
'append-validation' {
|
|
214
|
+
return @"
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
## Validation loop
|
|
218
|
+
|
|
219
|
+
<!-- TODO(retrofit): fill in — describe how to verify this skill ran correctly. Auto-added by skill-checker --retrofit --apply per skill-authoring.instructions.md. -->
|
|
220
|
+
|
|
221
|
+
1. Run `pwsh plugin/skills/self-check/run.ps1 -Targeted <area>`.
|
|
222
|
+
2. Fix any findings, then re-run the affected step.
|
|
223
|
+
3. Repeat until self-check exits 0.
|
|
224
|
+
4. Only then update `run-log.yml` with success status.
|
|
225
|
+
"@
|
|
226
|
+
}
|
|
227
|
+
'append-checklist' {
|
|
228
|
+
return @"
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
## Step checklist
|
|
232
|
+
|
|
233
|
+
<!-- TODO(retrofit): fill in — convert the procedure into GitHub checkbox syntax. Auto-added by skill-checker --retrofit --apply per skill-authoring.instructions.md. -->
|
|
234
|
+
|
|
235
|
+
- [ ] Step 1 — <first concrete action>
|
|
236
|
+
- [ ] Step 2 — <second concrete action>
|
|
237
|
+
- [ ] Step 3 — <third concrete action>
|
|
238
|
+
- [ ] Final — Run self-check + evals; only commit on green.
|
|
239
|
+
"@
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return ''
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function Get-StarterEvals {
|
|
246
|
+
param([string]$Name)
|
|
247
|
+
$slug = ($Name -replace '[^a-z0-9-]', '-')
|
|
248
|
+
return @"
|
|
249
|
+
{
|
|
250
|
+
"skill": "$Name",
|
|
251
|
+
"version": "0.1.0",
|
|
252
|
+
"description": "Starter evals auto-added by skill-checker --retrofit --apply. Replace as soon as the skill has real behavior to assert.",
|
|
253
|
+
"cases": [
|
|
254
|
+
{
|
|
255
|
+
"id": "$slug-skillmd-exists",
|
|
256
|
+
"name": "SKILL.md exists for $Name",
|
|
257
|
+
"input": "verify $Name skill artifacts",
|
|
258
|
+
"canary": false,
|
|
259
|
+
"grader_type": "script",
|
|
260
|
+
"expected_assertions": [
|
|
261
|
+
{ "type": "file-exists", "path": "plugin/skills/$Name/SKILL.md" }
|
|
262
|
+
]
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
"id": "$slug-description-shape",
|
|
266
|
+
"name": "SKILL.md description leads with USE WHEN",
|
|
267
|
+
"input": "verify $Name description shape",
|
|
268
|
+
"canary": false,
|
|
269
|
+
"grader_type": "script",
|
|
270
|
+
"expected_assertions": [
|
|
271
|
+
{ "type": "file-contains", "path": "plugin/skills/$Name/SKILL.md", "value": "USE WHEN" }
|
|
272
|
+
]
|
|
273
|
+
}
|
|
274
|
+
]
|
|
275
|
+
}
|
|
276
|
+
"@
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function Apply-Fixes {
|
|
280
|
+
param([System.IO.DirectoryInfo]$Dir, $Findings)
|
|
281
|
+
$applied = New-Object System.Collections.Generic.List[string]
|
|
282
|
+
$skillMd = Join-Path $Dir.FullName 'SKILL.md'
|
|
283
|
+
foreach ($f in $Findings) {
|
|
284
|
+
if ($f.kind -ne 'additive') { continue }
|
|
285
|
+
switch ($f.fix) {
|
|
286
|
+
'create-evals' {
|
|
287
|
+
$evalsDir = Join-Path $Dir.FullName 'evals'
|
|
288
|
+
$evalsFile = Join-Path $evalsDir 'evals.json'
|
|
289
|
+
if (-not (Test-Path $evalsFile)) {
|
|
290
|
+
if (-not $DryRun) {
|
|
291
|
+
New-Item -ItemType Directory -Force -Path $evalsDir | Out-Null
|
|
292
|
+
Set-Content -LiteralPath $evalsFile -Value (Get-StarterEvals -Name $Dir.Name) -Encoding UTF8
|
|
293
|
+
}
|
|
294
|
+
[void]$applied.Add("create $evalsFile")
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
{ $_ -in 'append-gotchas','append-validation','append-checklist' } {
|
|
298
|
+
$block = Get-AppendBlock -Fix $f.fix
|
|
299
|
+
if (-not $DryRun) {
|
|
300
|
+
Add-Content -LiteralPath $skillMd -Value $block -Encoding UTF8
|
|
301
|
+
}
|
|
302
|
+
[void]$applied.Add("append $($f.fix) to $skillMd")
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return $applied
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
# ---- main ------------------------------------------------------------------
|
|
310
|
+
|
|
311
|
+
# Review mode delegates to skill-creator.
|
|
312
|
+
if ($Review) {
|
|
313
|
+
if (-not $Skill) { throw "-Review requires -Skill <name>." }
|
|
314
|
+
$reviewer = Join-Path $Root 'plugin/skills/skill-creator/generate-eval-review.ps1'
|
|
315
|
+
& pwsh -NoProfile -File $reviewer -Skill $Skill -Root $Root
|
|
316
|
+
exit $LASTEXITCODE
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
# OptimizeDescription mode delegates to skill-creator.
|
|
320
|
+
if ($OptimizeDescription) {
|
|
321
|
+
if (-not $Skill) { throw "-OptimizeDescription requires -Skill <name>." }
|
|
322
|
+
$skillMd = Join-Path $Root "plugin/skills/$Skill/SKILL.md"
|
|
323
|
+
if (-not (Test-Path $skillMd)) { throw "No SKILL.md for '$Skill' at $skillMd" }
|
|
324
|
+
$fm = Get-Frontmatter $skillMd
|
|
325
|
+
$current = $fm['description']
|
|
326
|
+
if (-not $current) { throw "No description: in $skillMd frontmatter." }
|
|
327
|
+
$optimizer = Join-Path $Root 'plugin/skills/skill-creator/optimize-description.ps1'
|
|
328
|
+
& pwsh -NoProfile -File $optimizer -Description $current
|
|
329
|
+
exit $LASTEXITCODE
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
# Apply requires Retrofit.
|
|
333
|
+
if ($Apply -and -not $Retrofit) {
|
|
334
|
+
throw "-Apply requires -Retrofit."
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
$targets = Get-TargetSkills -Root $Root -Skill $Skill -All:$All
|
|
338
|
+
$report = [ordered]@{
|
|
339
|
+
mode = if ($Apply) { 'apply' } elseif ($Retrofit) { 'retrofit' } else { 'lint' }
|
|
340
|
+
root = $Root
|
|
341
|
+
timestamp = (Get-Date -Format o)
|
|
342
|
+
skills = @()
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
$totalFindings = 0
|
|
346
|
+
$totalApplied = 0
|
|
347
|
+
$totalSkillsClean = 0
|
|
348
|
+
$skillsNeedManual = New-Object System.Collections.Generic.List[string]
|
|
349
|
+
|
|
350
|
+
foreach ($d in $targets) {
|
|
351
|
+
$findings = Lint-Skill -Dir $d
|
|
352
|
+
$applied = @()
|
|
353
|
+
if ($Apply -and ($findings | Where-Object { $_.kind -eq 'additive' })) {
|
|
354
|
+
$applied = Apply-Fixes -Dir $d -Findings $findings
|
|
355
|
+
# Re-lint after apply.
|
|
356
|
+
$findings = Lint-Skill -Dir $d
|
|
357
|
+
}
|
|
358
|
+
$nonAdditive = @($findings | Where-Object { $_.kind -eq 'non-additive' })
|
|
359
|
+
if ($findings.Count -eq 0) { $totalSkillsClean++ }
|
|
360
|
+
if ($nonAdditive.Count -gt 0) { [void]$skillsNeedManual.Add($d.Name) }
|
|
361
|
+
$totalFindings += $findings.Count
|
|
362
|
+
$totalApplied += $applied.Count
|
|
363
|
+
$report.skills += [ordered]@{
|
|
364
|
+
name = $d.Name
|
|
365
|
+
type = Get-SkillType -Name $d.Name
|
|
366
|
+
findings = @($findings)
|
|
367
|
+
applied = @($applied)
|
|
368
|
+
non_additive_count = $nonAdditive.Count
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
# Write per-skill fix plan during retrofit.
|
|
372
|
+
if ($Retrofit) {
|
|
373
|
+
$planDir = Join-Path $Root "Evidence/_skill-checker/$($d.Name)"
|
|
374
|
+
$planFile = Join-Path $planDir 'fix-plan.json'
|
|
375
|
+
if (-not $DryRun) {
|
|
376
|
+
New-Item -ItemType Directory -Force -Path $planDir | Out-Null
|
|
377
|
+
($findings | ConvertTo-Json -Depth 6) | Set-Content -LiteralPath $planFile -Encoding UTF8
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
$report.summary = [ordered]@{
|
|
383
|
+
total_skills = $targets.Count
|
|
384
|
+
total_findings = $totalFindings
|
|
385
|
+
skills_clean = $totalSkillsClean
|
|
386
|
+
skills_with_manual_work = $skillsNeedManual.Count
|
|
387
|
+
skills_needing_manual = @($skillsNeedManual)
|
|
388
|
+
applied = $totalApplied
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if ($Output) {
|
|
392
|
+
$report | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $Output -Encoding UTF8
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if ($Json) {
|
|
396
|
+
$report | ConvertTo-Json -Depth 8
|
|
397
|
+
} else {
|
|
398
|
+
Write-Host ""
|
|
399
|
+
Write-Host "### skill-checker ($($report.mode)) — $($targets.Count) skill(s) scanned"
|
|
400
|
+
Write-Host ""
|
|
401
|
+
foreach ($s in $report.skills) {
|
|
402
|
+
$tag = if ($s.findings.Count -eq 0) { 'OK ' } else { '!! ' }
|
|
403
|
+
Write-Host "$tag $($s.name) [type=$($s.type)] — findings=$($s.findings.Count) applied=$($s.applied.Count)"
|
|
404
|
+
foreach ($f in $s.findings) {
|
|
405
|
+
Write-Host " [$($f.code)] $($f.message) ($($f.kind))"
|
|
406
|
+
}
|
|
407
|
+
foreach ($a in $s.applied) {
|
|
408
|
+
Write-Host " + $a"
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
Write-Host ""
|
|
412
|
+
Write-Host "summary: $($report.summary.skills_clean)/$($report.summary.total_skills) clean; $($report.summary.applied) fix(es) applied; $($report.summary.skills_with_manual_work) skill(s) need manual review."
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if ($StrictExit -and $totalFindings -gt 0) { exit 1 }
|
|
416
|
+
exit 0
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill": "skill-checker",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Meta — skill-checker runner ships and is parseable; SKILL.md description leads with USE WHEN.",
|
|
5
|
+
"cases": [
|
|
6
|
+
{
|
|
7
|
+
"id": "check-skill-exists",
|
|
8
|
+
"name": "check-skill.ps1 ships in the skill folder",
|
|
9
|
+
"input": "verify skill-checker artifacts",
|
|
10
|
+
"canary": false,
|
|
11
|
+
"grader_type": "script",
|
|
12
|
+
"expected_assertions": [
|
|
13
|
+
{ "type": "file-exists", "path": "plugin/skills/skill-checker/check-skill.ps1" },
|
|
14
|
+
{ "type": "file-exists", "path": "plugin/skills/skill-checker/SKILL.md" }
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"id": "description-leads-use-when",
|
|
19
|
+
"name": "skill-checker description leads with USE WHEN",
|
|
20
|
+
"input": "verify SKILL.md description shape",
|
|
21
|
+
"canary": false,
|
|
22
|
+
"grader_type": "script",
|
|
23
|
+
"expected_assertions": [
|
|
24
|
+
{ "type": "file-contains", "path": "plugin/skills/skill-checker/SKILL.md", "value": "USE WHEN" }
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "modes-documented",
|
|
29
|
+
"name": "all four operating modes are documented",
|
|
30
|
+
"input": "verify SKILL.md mode coverage",
|
|
31
|
+
"canary": false,
|
|
32
|
+
"grader_type": "script",
|
|
33
|
+
"expected_assertions": [
|
|
34
|
+
{ "type": "file-contains", "path": "plugin/skills/skill-checker/SKILL.md", "value": "-Retrofit" },
|
|
35
|
+
{ "type": "file-contains", "path": "plugin/skills/skill-checker/SKILL.md", "value": "-Apply" },
|
|
36
|
+
{ "type": "file-contains", "path": "plugin/skills/skill-checker/SKILL.md", "value": "-OptimizeDescription" },
|
|
37
|
+
{ "type": "file-contains", "path": "plugin/skills/skill-checker/SKILL.md", "value": "-Review" }
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "skill-creator"
|
|
3
|
+
version: "1.0.0"
|
|
4
|
+
description: "USE WHEN the user says \"create skill\", \"new skill\", \"scaffold a skill\", \"npx kushi-agents create-skill ...\", or wants to author a new kushi skill that conforms to the agentskills.io blueprint. DO NOT USE for editing existing skills (use skill-checker --retrofit). Capability: scaffolds plugin/skills/<name>/ with SKILL.md, evals/evals.json, optional references/, and a .created-by-skill-creator marker; provides description-optimization and an HTML eval-review viewer. Inspired by Anthropic's skill-creator."
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Skill: skill-creator
|
|
8
|
+
|
|
9
|
+
The meta-skill that authors NEW kushi skills. Adapted from Anthropic's [skill-creator](https://github.com/anthropics/skills/blob/main/skills/skill-creator/SKILL.md) to kushi's PowerShell-first stack + 2-host install matrix.
|
|
10
|
+
|
|
11
|
+
User triggers: "create skill", "new skill", "scaffold a skill", "kushi create-skill", "make me a pull-something skill".
|
|
12
|
+
|
|
13
|
+
Doctrine: [`plugin/instructions/skill-authoring.instructions.md`](../../instructions/skill-authoring.instructions.md).
|
|
14
|
+
|
|
15
|
+
## USE WHEN
|
|
16
|
+
|
|
17
|
+
- The user wants to add a new skill to `plugin/skills/`.
|
|
18
|
+
- A new evidence source / orchestrator / utility needs a skill home and you want it conformant on day one.
|
|
19
|
+
- Bootstrapping the SKILL.md + evals/evals.json + optional references/ tree from a single prompt.
|
|
20
|
+
|
|
21
|
+
## DO NOT USE FOR
|
|
22
|
+
|
|
23
|
+
- Editing an existing skill (use `skill-checker --retrofit`).
|
|
24
|
+
- Authoring `*.instructions.md` doctrine (write those by hand; they have their own front-matter rules).
|
|
25
|
+
- Producing release docs (CHANGELOG, genealogy) — those are human-curated.
|
|
26
|
+
|
|
27
|
+
## Gotchas
|
|
28
|
+
|
|
29
|
+
- **Type selection matters.** `pull` → forces `## Gotchas`; `writer` → forces `## Validation loop`; `orchestrator` → forces `## Step checklist`; `other` → at least one of the three. Picking the wrong type ships a non-conformant skill that `check-skill --lint` will immediately flag.
|
|
30
|
+
- **Description must lead with `USE WHEN`** in the first 160 chars or `D30.description-optimized` fails. The scaffold inserts this for you; if you replace it, run `optimize-description.ps1` afterward.
|
|
31
|
+
- **Marker file is the strict-gate opt-in.** `.created-by-skill-creator` is written by `scaffold.ps1`. `D34.creator-output-conforms` requires every marked skill to be lint-clean. Don't add the marker to half-built skills.
|
|
32
|
+
- **Templates are starter content, not boilerplate to ignore.** Every `TODO(skill-creator)` must be replaced before the first PR.
|
|
33
|
+
- **`Evidence/_skill-creator/` is gitignored** — eval-review HTML stays local.
|
|
34
|
+
|
|
35
|
+
## Step checklist
|
|
36
|
+
|
|
37
|
+
- [ ] **Pick a name** (kebab-case, verb-led: `pull-foo`, `apply-foo`, `consolidate-foo`).
|
|
38
|
+
- [ ] **Pick a type** (`pull` | `writer` | `orchestrator` | `other`).
|
|
39
|
+
- [ ] **Run scaffold**:
|
|
40
|
+
|
|
41
|
+
```powershell
|
|
42
|
+
pwsh plugin/skills/skill-creator/scaffold.ps1 `
|
|
43
|
+
-Name my-new-skill `
|
|
44
|
+
-Type other `
|
|
45
|
+
-Description "USE WHEN ... DO NOT USE FOR ..."
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- [ ] **Optimize the description** if you handcrafted it:
|
|
49
|
+
|
|
50
|
+
```powershell
|
|
51
|
+
pwsh plugin/skills/skill-creator/optimize-description.ps1 `
|
|
52
|
+
-Description "<your draft>"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- [ ] **Fill placeholders** (grep `TODO(skill-creator)`).
|
|
56
|
+
- [ ] **Lint**: `npx kushi-agents check-skill my-new-skill`
|
|
57
|
+
- [ ] **Run evals**: `npm run eval -- my-new-skill`
|
|
58
|
+
- [ ] **Self-check targeted**: `pwsh plugin/skills/self-check/run.ps1 -Deep -Targeted my-new-skill`
|
|
59
|
+
- [ ] **Commit**.
|
|
60
|
+
|
|
61
|
+
## Validation loop
|
|
62
|
+
|
|
63
|
+
After scaffolding (or running any sub-command):
|
|
64
|
+
|
|
65
|
+
1. Run `check-skill --lint <name>` — must be clean.
|
|
66
|
+
2. Run `npm run eval -- <name>` — must pass (the starter evals exercise file-exists + regex-match).
|
|
67
|
+
3. Run `pwsh plugin/skills/self-check/run.ps1 -Targeted <name>` — only known-allowed findings.
|
|
68
|
+
4. If any of the above fails, fix in-place and re-run from step 1.
|
|
69
|
+
5. Only then add `.created-by-skill-creator` (the scaffold does this for you).
|
|
70
|
+
|
|
71
|
+
## What `scaffold.ps1` does
|
|
72
|
+
|
|
73
|
+
1. Validates `-Name` is kebab-case and doesn't already exist (unless `-Force`).
|
|
74
|
+
2. Creates `plugin/skills/<name>/`.
|
|
75
|
+
3. Renders `templates/skill-skeleton.template.md` → `SKILL.md`, picking the right type-specific section.
|
|
76
|
+
4. Renders `templates/evals-starter.template.json` → `evals/evals.json` with 2 starter cases.
|
|
77
|
+
5. If `pull` or `writer`, renders `templates/gotchas-stub.template.md` → into `SKILL.md`.
|
|
78
|
+
6. Writes the `.created-by-skill-creator` marker.
|
|
79
|
+
7. Prints next-step guidance.
|
|
80
|
+
|
|
81
|
+
`-Force` overwrites an existing skill folder (use carefully). `-DryRun` prints actions without writing.
|
|
82
|
+
|
|
83
|
+
## What `optimize-description.ps1` does
|
|
84
|
+
|
|
85
|
+
Applies the agentskills.io description-optimization rules:
|
|
86
|
+
|
|
87
|
+
- Lead with `USE WHEN`.
|
|
88
|
+
- Append `DO NOT USE FOR` if missing.
|
|
89
|
+
- Strip marketing words (`powerful`, `comprehensive`, `blazing`, `seamless`, `simple`).
|
|
90
|
+
- Hard-cap 1024 chars.
|
|
91
|
+
- Returns the rewritten string on stdout. Idempotent.
|
|
92
|
+
|
|
93
|
+
Can be invoked standalone (just to test a draft) or via `skill-checker --optimize-description <name>` (compares against the current SKILL.md description).
|
|
94
|
+
|
|
95
|
+
## What `generate-eval-review.ps1` does
|
|
96
|
+
|
|
97
|
+
Takes an eval workspace (`Evidence/_evals/<skill>/iterations/`) and renders a static HTML side-by-side viewer at `Evidence/_skill-creator/<skill>/review.html`. Helps a maintainer compare two eval runs (e.g. before/after a prompt change) without spinning up infra. Output is gitignored.
|
|
98
|
+
|
|
99
|
+
## Arguments
|
|
100
|
+
|
|
101
|
+
### `scaffold.ps1`
|
|
102
|
+
|
|
103
|
+
| Flag | Purpose |
|
|
104
|
+
|---|---|
|
|
105
|
+
| `-Name <kebab>` | REQUIRED — skill directory + front-matter `name`. |
|
|
106
|
+
| `-Type <pull\|writer\|orchestrator\|other>` | REQUIRED — picks template sections. |
|
|
107
|
+
| `-Description <string>` | REQUIRED — front-matter `description` (will be auto-optimized). |
|
|
108
|
+
| `-Force` | Overwrite existing folder. |
|
|
109
|
+
| `-DryRun` | Print actions only. |
|
|
110
|
+
| `-Root <path>` | Override repo root (default: 3 levels above the script). |
|
|
111
|
+
|
|
112
|
+
### `optimize-description.ps1`
|
|
113
|
+
|
|
114
|
+
| Flag | Purpose |
|
|
115
|
+
|---|---|
|
|
116
|
+
| `-Description <string>` | REQUIRED — string to rewrite. |
|
|
117
|
+
| `-Quiet` | Suppress diff output; print only the rewritten string. |
|
|
118
|
+
|
|
119
|
+
### `generate-eval-review.ps1`
|
|
120
|
+
|
|
121
|
+
| Flag | Purpose |
|
|
122
|
+
|---|---|
|
|
123
|
+
| `-Skill <name>` | REQUIRED — skill whose eval iterations to review. |
|
|
124
|
+
| `-Output <path>` | Override output path. Default `Evidence/_skill-creator/<skill>/review.html`. |
|
|
125
|
+
| `-Root <path>` | Override repo root. |
|
|
126
|
+
|
|
127
|
+
## References
|
|
128
|
+
|
|
129
|
+
- `plugin/instructions/skill-authoring.instructions.md` — the doctrine this skill enforces.
|
|
130
|
+
- `plugin/instructions/agentskills-compliance.instructions.md` — section/size rules.
|
|
131
|
+
- `plugin/instructions/skill-evals.instructions.md` — evals schema.
|
|
132
|
+
- `plugin/skills/skill-checker/SKILL.md` — the linter that validates scaffold output.
|
|
133
|
+
- `plugin/skills/skill-creator/templates/` — load templates from here when filling.
|
|
134
|
+
- <https://github.com/anthropics/skills/blob/main/skills/skill-creator/SKILL.md>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill": "skill-creator",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Meta — skill-creator scripts ship, are parseable, and templates are present.",
|
|
5
|
+
"cases": [
|
|
6
|
+
{
|
|
7
|
+
"id": "scaffold-exists",
|
|
8
|
+
"name": "scaffold.ps1 ships in the skill folder",
|
|
9
|
+
"input": "verify skill-creator artifacts",
|
|
10
|
+
"canary": false,
|
|
11
|
+
"grader_type": "script",
|
|
12
|
+
"expected_assertions": [
|
|
13
|
+
{ "type": "file-exists", "path": "plugin/skills/skill-creator/scaffold.ps1" },
|
|
14
|
+
{ "type": "file-exists", "path": "plugin/skills/skill-creator/SKILL.md" }
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"id": "templates-exist",
|
|
19
|
+
"name": "all three templates ship",
|
|
20
|
+
"input": "verify templates folder",
|
|
21
|
+
"canary": false,
|
|
22
|
+
"grader_type": "script",
|
|
23
|
+
"expected_assertions": [
|
|
24
|
+
{ "type": "file-exists", "path": "plugin/skills/skill-creator/templates/skill-skeleton.template.md" },
|
|
25
|
+
{ "type": "file-exists", "path": "plugin/skills/skill-creator/templates/evals-starter.template.json" },
|
|
26
|
+
{ "type": "file-exists", "path": "plugin/skills/skill-creator/templates/gotchas-stub.template.md" }
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "description-leads-use-when",
|
|
31
|
+
"name": "skill-creator description leads with USE WHEN",
|
|
32
|
+
"input": "verify SKILL.md description shape",
|
|
33
|
+
"canary": false,
|
|
34
|
+
"grader_type": "script",
|
|
35
|
+
"expected_assertions": [
|
|
36
|
+
{ "type": "file-contains", "path": "plugin/skills/skill-creator/SKILL.md", "value": "USE WHEN" }
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|