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.
Files changed (62) hide show
  1. package/README.md +35 -0
  2. package/bin/cli.mjs +103 -0
  3. package/package.json +6 -2
  4. package/plugin/agents/kushi.agent.md +3 -1
  5. package/plugin/instructions/skill-authoring.instructions.md +147 -0
  6. package/plugin/instructions/skill-evals.instructions.md +130 -0
  7. package/plugin/skills/aggregate-project/evals/evals.json +33 -0
  8. package/plugin/skills/apply-ado-update/evals/evals.json +33 -0
  9. package/plugin/skills/ask-project/SKILL.md +10 -0
  10. package/plugin/skills/ask-project/evals/evals.json +34 -0
  11. package/plugin/skills/bootstrap-project/evals/evals.json +34 -0
  12. package/plugin/skills/build-state/evals/evals.json +31 -0
  13. package/plugin/skills/consolidate-evidence/evals/evals.json +33 -0
  14. package/plugin/skills/dashboard/evals/evals.json +33 -0
  15. package/plugin/skills/emit-vertex/evals/evals.json +33 -0
  16. package/plugin/skills/eval/SKILL.md +90 -0
  17. package/plugin/skills/eval/evals.schema.json +73 -0
  18. package/plugin/skills/eval/run-evals.ps1 +372 -0
  19. package/plugin/skills/fde-intake/evals/evals.json +33 -0
  20. package/plugin/skills/fde-report/evals/evals.json +33 -0
  21. package/plugin/skills/fde-triage/evals/evals.json +33 -0
  22. package/plugin/skills/intro/SKILL.md +160 -451
  23. package/plugin/skills/intro/evals/evals.json +33 -0
  24. package/plugin/skills/intro/references/walkthrough.md +310 -0
  25. package/plugin/skills/link-entities/evals/evals.json +31 -0
  26. package/plugin/skills/project-status/SKILL.md +10 -1
  27. package/plugin/skills/project-status/evals/evals.json +33 -0
  28. package/plugin/skills/propose-ado-update/evals/evals.json +33 -0
  29. package/plugin/skills/pull-ado/evals/evals.json +35 -0
  30. package/plugin/skills/pull-crm/evals/evals.json +35 -0
  31. package/plugin/skills/pull-email/evals/evals.json +35 -0
  32. package/plugin/skills/pull-loop/evals/evals.json +35 -0
  33. package/plugin/skills/pull-meetings/evals/evals.json +35 -0
  34. package/plugin/skills/pull-misc/evals/evals.json +35 -0
  35. package/plugin/skills/pull-onenote/evals/evals.json +35 -0
  36. package/plugin/skills/pull-sharepoint/evals/evals.json +35 -0
  37. package/plugin/skills/pull-teams/evals/evals.json +35 -0
  38. package/plugin/skills/refresh-project/evals/evals.json +31 -0
  39. package/plugin/skills/self-check/SKILL.md +2 -0
  40. package/plugin/skills/self-check/evals/evals.json +28 -0
  41. package/plugin/skills/self-check/run.ps1 +144 -0
  42. package/plugin/skills/setup/SKILL.md +10 -0
  43. package/plugin/skills/setup/evals/evals.json +33 -0
  44. package/plugin/skills/skill-checker/SKILL.md +136 -0
  45. package/plugin/skills/skill-checker/check-skill.ps1 +416 -0
  46. package/plugin/skills/skill-checker/evals/evals.json +41 -0
  47. package/plugin/skills/skill-creator/SKILL.md +134 -0
  48. package/plugin/skills/skill-creator/evals/evals.json +40 -0
  49. package/plugin/skills/skill-creator/generate-eval-review.ps1 +101 -0
  50. package/plugin/skills/skill-creator/optimize-description.ps1 +87 -0
  51. package/plugin/skills/skill-creator/scaffold.ps1 +180 -0
  52. package/plugin/skills/skill-creator/templates/evals-starter.template.json +27 -0
  53. package/plugin/skills/skill-creator/templates/gotchas-stub.template.md +9 -0
  54. package/plugin/skills/skill-creator/templates/skill-skeleton.template.md +28 -0
  55. package/plugin/skills/tour/evals/evals.json +33 -0
  56. package/plugin/skills/vertex-link/SKILL.md +10 -0
  57. package/plugin/skills/vertex-link/evals/evals.json +33 -0
  58. package/src/eval-aggregator.mjs +209 -0
  59. package/src/eval-aggregator.test.mjs +64 -0
  60. package/src/eval-runner.test.mjs +69 -0
  61. package/src/skill-checker.test.mjs +118 -0
  62. 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
+ }