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,101 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Renders an HTML side-by-side eval-review viewer for a skill.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Scans Evidence/_evals/ for the most recent per-run JSON files belonging to
|
|
7
|
+
-Skill, picks the two newest (or up to -MaxRuns), and renders a static HTML page
|
|
8
|
+
that shows per-case pass/fail + duration side-by-side. Output is gitignored.
|
|
9
|
+
|
|
10
|
+
If no eval runs are found, emits a stub page noting "no runs yet — run
|
|
11
|
+
`npm run eval -- <skill>` first" so the viewer command never silently fails.
|
|
12
|
+
|
|
13
|
+
.PARAMETER Skill
|
|
14
|
+
Skill name (matches plugin/skills/<name>/).
|
|
15
|
+
|
|
16
|
+
.PARAMETER Output
|
|
17
|
+
Override output path. Default: Evidence/_skill-creator/<skill>/review.html.
|
|
18
|
+
|
|
19
|
+
.PARAMETER MaxRuns
|
|
20
|
+
Maximum number of runs to include side-by-side (default 2).
|
|
21
|
+
|
|
22
|
+
.PARAMETER Root
|
|
23
|
+
Repo root (default: 3 levels above this script).
|
|
24
|
+
#>
|
|
25
|
+
[CmdletBinding()]
|
|
26
|
+
param(
|
|
27
|
+
[Parameter(Mandatory=$true)][string]$Skill,
|
|
28
|
+
[string]$Output,
|
|
29
|
+
[int]$MaxRuns = 2,
|
|
30
|
+
[string]$Root = (Resolve-Path (Join-Path $PSScriptRoot "..\..\..")).Path
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
$ErrorActionPreference = 'Stop'
|
|
34
|
+
|
|
35
|
+
if (-not $Output) {
|
|
36
|
+
$Output = Join-Path $Root "Evidence/_skill-creator/$Skill/review.html"
|
|
37
|
+
}
|
|
38
|
+
$outDir = Split-Path -Parent $Output
|
|
39
|
+
New-Item -ItemType Directory -Force -Path $outDir | Out-Null
|
|
40
|
+
|
|
41
|
+
$evalsDir = Join-Path $Root 'Evidence/_evals'
|
|
42
|
+
$runs = @()
|
|
43
|
+
if (Test-Path $evalsDir) {
|
|
44
|
+
$runs = Get-ChildItem -Path $evalsDir -Filter '*.json' -File -ErrorAction SilentlyContinue |
|
|
45
|
+
Sort-Object LastWriteTime -Descending |
|
|
46
|
+
Where-Object {
|
|
47
|
+
try {
|
|
48
|
+
$j = Get-Content -Raw $_.FullName | ConvertFrom-Json
|
|
49
|
+
$j.cases | Where-Object { $_.skill -eq $Skill -or $j.skill -eq $Skill }
|
|
50
|
+
} catch { $false }
|
|
51
|
+
} |
|
|
52
|
+
Select-Object -First $MaxRuns
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function HtmlEncode { param([string]$s) [System.Net.WebUtility]::HtmlEncode([string]$s) }
|
|
56
|
+
|
|
57
|
+
$sb = New-Object System.Text.StringBuilder
|
|
58
|
+
[void]$sb.AppendLine('<!doctype html><html><head><meta charset="utf-8">')
|
|
59
|
+
[void]$sb.AppendLine("<title>Skill review — $(HtmlEncode $Skill)</title>")
|
|
60
|
+
[void]$sb.AppendLine('<style>body{font-family:system-ui,sans-serif;margin:2em;}table{border-collapse:collapse;width:100%;}th,td{border:1px solid #ccc;padding:.4em .6em;text-align:left;vertical-align:top;}.pass{background:#dfd}.fail{background:#fdd}.skip{background:#eee;color:#666}.meta{color:#666;font-size:.9em}code{background:#f4f4f4;padding:0 .2em}</style>')
|
|
61
|
+
[void]$sb.AppendLine('</head><body>')
|
|
62
|
+
[void]$sb.AppendLine("<h1>Skill review — <code>$(HtmlEncode $Skill)</code></h1>")
|
|
63
|
+
[void]$sb.AppendLine("<p class='meta'>Generated $(Get-Date -Format o). Source: <code>Evidence/_evals/</code> (gitignored).</p>")
|
|
64
|
+
|
|
65
|
+
if (-not $runs -or $runs.Count -eq 0) {
|
|
66
|
+
[void]$sb.AppendLine("<p><strong>No eval runs found for <code>$(HtmlEncode $Skill)</code>.</strong></p>")
|
|
67
|
+
[void]$sb.AppendLine("<p>Run <code>npm run eval -- $(HtmlEncode $Skill)</code> first, then re-render.</p>")
|
|
68
|
+
} else {
|
|
69
|
+
[void]$sb.AppendLine('<table><thead><tr><th>Case</th>')
|
|
70
|
+
foreach ($r in $runs) { [void]$sb.AppendLine("<th>$(HtmlEncode $r.Name)</th>") }
|
|
71
|
+
[void]$sb.AppendLine('</tr></thead><tbody>')
|
|
72
|
+
|
|
73
|
+
# Build a unified case-id list across runs.
|
|
74
|
+
$parsed = $runs | ForEach-Object { Get-Content -Raw $_.FullName | ConvertFrom-Json }
|
|
75
|
+
$caseIds = @()
|
|
76
|
+
foreach ($p in $parsed) {
|
|
77
|
+
foreach ($c in $p.cases) { if ($caseIds -notcontains $c.id) { $caseIds += $c.id } }
|
|
78
|
+
}
|
|
79
|
+
foreach ($cid in $caseIds) {
|
|
80
|
+
[void]$sb.AppendLine("<tr><td><code>$(HtmlEncode $cid)</code></td>")
|
|
81
|
+
for ($i = 0; $i -lt $parsed.Count; $i++) {
|
|
82
|
+
$hit = $parsed[$i].cases | Where-Object { $_.id -eq $cid } | Select-Object -First 1
|
|
83
|
+
if (-not $hit) {
|
|
84
|
+
[void]$sb.AppendLine("<td class='skip'>—</td>")
|
|
85
|
+
} else {
|
|
86
|
+
$cls = if ($hit.skipped) { 'skip' } elseif ($hit.pass) { 'pass' } else { 'fail' }
|
|
87
|
+
$label = if ($hit.skipped) { 'skip' } elseif ($hit.pass) { 'pass' } else { 'fail' }
|
|
88
|
+
$dur = if ($hit.duration_ms) { "$($hit.duration_ms) ms" } else { '' }
|
|
89
|
+
[void]$sb.AppendLine("<td class='$cls'><strong>$label</strong> <span class='meta'>$dur</span></td>")
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
[void]$sb.AppendLine('</tr>')
|
|
93
|
+
}
|
|
94
|
+
[void]$sb.AppendLine('</tbody></table>')
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
[void]$sb.AppendLine('</body></html>')
|
|
98
|
+
|
|
99
|
+
Set-Content -LiteralPath $Output -Value $sb.ToString() -Encoding UTF8
|
|
100
|
+
Write-Host "skill-creator: wrote review viewer → $Output"
|
|
101
|
+
exit 0
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Rewrites a kushi skill description per the agentskills.io optimization rules.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Applies these transforms (idempotent):
|
|
7
|
+
1. Ensure the description leads with "USE WHEN " in the first 160 chars.
|
|
8
|
+
2. Ensure a "DO NOT USE" clause is present.
|
|
9
|
+
3. Strip marketing words: powerful, comprehensive, blazing, seamless, simple,
|
|
10
|
+
effortless, robust, cutting-edge, world-class.
|
|
11
|
+
4. Collapse internal whitespace; trim to 1024 chars.
|
|
12
|
+
|
|
13
|
+
Prints a unified diff (current → rewritten) by default; -Quiet emits only the
|
|
14
|
+
rewritten string for piping into scaffold.ps1.
|
|
15
|
+
|
|
16
|
+
.PARAMETER Description
|
|
17
|
+
The description string to rewrite.
|
|
18
|
+
|
|
19
|
+
.PARAMETER Quiet
|
|
20
|
+
Suppress the diff; print only the rewritten string.
|
|
21
|
+
#>
|
|
22
|
+
[CmdletBinding()]
|
|
23
|
+
param(
|
|
24
|
+
[Parameter(Mandatory=$true)][string]$Description,
|
|
25
|
+
[switch]$Quiet
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
$ErrorActionPreference = 'Stop'
|
|
29
|
+
|
|
30
|
+
function Optimize-Description {
|
|
31
|
+
param([string]$Text)
|
|
32
|
+
if (-not $Text) { return '' }
|
|
33
|
+
|
|
34
|
+
$out = $Text.Trim()
|
|
35
|
+
|
|
36
|
+
# 1) Marketing-word strip.
|
|
37
|
+
$bad = @('powerful', 'comprehensive', 'blazing', 'seamless', 'simple',
|
|
38
|
+
'effortless', 'robust', 'cutting-edge', 'world-class', 'next-generation')
|
|
39
|
+
foreach ($w in $bad) {
|
|
40
|
+
$out = [regex]::Replace($out, "\b$w\s*", '', 'IgnoreCase')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# 2) Collapse whitespace.
|
|
44
|
+
$out = [regex]::Replace($out, '\s+', ' ').Trim()
|
|
45
|
+
|
|
46
|
+
# 3) Ensure leading USE WHEN.
|
|
47
|
+
if ($out.Substring(0, [Math]::Min(160, $out.Length)) -notmatch '\bUSE WHEN\b') {
|
|
48
|
+
# If a plain "When " is present, upgrade it; else prepend a placeholder.
|
|
49
|
+
if ($out -match '^\s*[Ww]hen\b') {
|
|
50
|
+
$out = $out -replace '^\s*[Ww]hen\b', 'USE WHEN'
|
|
51
|
+
} else {
|
|
52
|
+
$out = "USE WHEN <TODO: trigger>. $out"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# 4) Ensure a DO NOT USE clause.
|
|
57
|
+
if ($out -notmatch '(?i)\bDO NOT USE\b') {
|
|
58
|
+
# Insert after the first sentence terminator if any.
|
|
59
|
+
if ($out -match '^(.+?\.)\s+(.+)$') {
|
|
60
|
+
$out = "$($Matches[1]) DO NOT USE FOR <TODO: near-miss>. $($Matches[2])"
|
|
61
|
+
} else {
|
|
62
|
+
$out = "$out DO NOT USE FOR <TODO: near-miss>."
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# 5) Re-collapse whitespace after edits.
|
|
67
|
+
$out = [regex]::Replace($out, '\s+', ' ').Trim()
|
|
68
|
+
|
|
69
|
+
# 6) Hard cap 1024 chars.
|
|
70
|
+
if ($out.Length -gt 1024) { $out = $out.Substring(0, 1024) }
|
|
71
|
+
|
|
72
|
+
return $out
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
$rewritten = Optimize-Description -Text $Description
|
|
76
|
+
|
|
77
|
+
if (-not $Quiet) {
|
|
78
|
+
Write-Host "--- before"
|
|
79
|
+
Write-Host $Description
|
|
80
|
+
Write-Host "+++ after"
|
|
81
|
+
Write-Host $rewritten
|
|
82
|
+
Write-Host ""
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Always emit the rewritten string on stdout (consumed by scaffold.ps1).
|
|
86
|
+
[Console]::Out.Write($rewritten)
|
|
87
|
+
exit 0
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
skill-creator scaffold — emits a conformant plugin/skills/<name>/ tree.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Reads templates/skill-skeleton.template.md + evals-starter.template.json + (for
|
|
7
|
+
pull/writer) gotchas-stub.template.md, substitutes {{NAME}}/{{DESCRIPTION}}/
|
|
8
|
+
{{TYPE_SECTION}}, and writes the new skill folder. Adds a `.created-by-skill-
|
|
9
|
+
creator` marker so D34.creator-output-conforms can enforce strict lint.
|
|
10
|
+
|
|
11
|
+
Doctrine: plugin/instructions/skill-authoring.instructions.md.
|
|
12
|
+
|
|
13
|
+
.PARAMETER Name
|
|
14
|
+
Skill directory + front-matter name. Kebab-case, verb-led.
|
|
15
|
+
|
|
16
|
+
.PARAMETER Type
|
|
17
|
+
One of: pull, writer, orchestrator, other.
|
|
18
|
+
pull → injects ## Gotchas section
|
|
19
|
+
writer → injects ## Validation loop section
|
|
20
|
+
orchestrator → injects ## Step checklist section
|
|
21
|
+
other → injects ## Steps (generic) — author must add at least one of
|
|
22
|
+
Gotchas / Validation loop / Step checklist before lint passes.
|
|
23
|
+
|
|
24
|
+
.PARAMETER Description
|
|
25
|
+
Front-matter description string. Auto-rewritten via optimize-description.ps1.
|
|
26
|
+
|
|
27
|
+
.PARAMETER Force
|
|
28
|
+
Overwrite an existing skill folder.
|
|
29
|
+
|
|
30
|
+
.PARAMETER DryRun
|
|
31
|
+
Print actions only; write nothing.
|
|
32
|
+
|
|
33
|
+
.PARAMETER Root
|
|
34
|
+
Repo root (default: 3 levels above this script).
|
|
35
|
+
#>
|
|
36
|
+
[CmdletBinding()]
|
|
37
|
+
param(
|
|
38
|
+
[Parameter(Mandatory=$true)][string]$Name,
|
|
39
|
+
[Parameter(Mandatory=$true)][ValidateSet('pull','writer','orchestrator','other')][string]$Type,
|
|
40
|
+
[Parameter(Mandatory=$true)][string]$Description,
|
|
41
|
+
[switch]$Force,
|
|
42
|
+
[switch]$DryRun,
|
|
43
|
+
[string]$Root = (Resolve-Path (Join-Path $PSScriptRoot "..\..\..")).Path
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
$ErrorActionPreference = 'Stop'
|
|
47
|
+
|
|
48
|
+
if ($Name -notmatch '^[a-z][a-z0-9]*(-[a-z0-9]+)*$') {
|
|
49
|
+
throw "Name '$Name' is not kebab-case. Use lowercase letters, digits, and single hyphens (e.g. 'pull-onenote')."
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
$templatesDir = Join-Path $PSScriptRoot 'templates'
|
|
53
|
+
$optimizer = Join-Path $PSScriptRoot 'optimize-description.ps1'
|
|
54
|
+
$skillDir = Join-Path $Root "plugin/skills/$Name"
|
|
55
|
+
$skillMd = Join-Path $skillDir 'SKILL.md'
|
|
56
|
+
$evalsDir = Join-Path $skillDir 'evals'
|
|
57
|
+
$evalsJson = Join-Path $evalsDir 'evals.json'
|
|
58
|
+
$marker = Join-Path $skillDir '.created-by-skill-creator'
|
|
59
|
+
|
|
60
|
+
if ((Test-Path $skillDir) -and -not $Force) {
|
|
61
|
+
throw "Skill folder already exists: $skillDir. Pass -Force to overwrite."
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# 1) Optimize description (best-effort; falls back to as-is on error).
|
|
65
|
+
$optimized = $Description
|
|
66
|
+
if (Test-Path $optimizer) {
|
|
67
|
+
try {
|
|
68
|
+
$optimized = (& pwsh -NoProfile -File $optimizer -Description $Description -Quiet).Trim()
|
|
69
|
+
if (-not $optimized) { $optimized = $Description }
|
|
70
|
+
} catch {
|
|
71
|
+
Write-Warning "optimize-description failed: $($_.Exception.Message); using as-is."
|
|
72
|
+
$optimized = $Description
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
# Escape double quotes for safe YAML embedding.
|
|
76
|
+
$escaped = $optimized -replace '"', '\"'
|
|
77
|
+
|
|
78
|
+
# 2) Pick type-specific section.
|
|
79
|
+
function Get-TypeSection {
|
|
80
|
+
param([string]$Type, [string]$TemplatesDir)
|
|
81
|
+
switch ($Type) {
|
|
82
|
+
'pull' {
|
|
83
|
+
$g = Join-Path $TemplatesDir 'gotchas-stub.template.md'
|
|
84
|
+
return (Get-Content -Raw $g)
|
|
85
|
+
}
|
|
86
|
+
'writer' {
|
|
87
|
+
return @'
|
|
88
|
+
## Validation loop
|
|
89
|
+
|
|
90
|
+
After writing outputs:
|
|
91
|
+
|
|
92
|
+
1. Run self-check targeted at this skill: `pwsh plugin/skills/self-check/run.ps1 -Targeted <area>`
|
|
93
|
+
2. If failures: fix and re-run the affected step (not the whole skill).
|
|
94
|
+
3. Repeat until self-check exits 0.
|
|
95
|
+
4. Only then update `run-log.yml` with success status.
|
|
96
|
+
|
|
97
|
+
<!-- TODO(skill-creator): replace <area> with the targeted-scope substring for this skill. -->
|
|
98
|
+
'@
|
|
99
|
+
}
|
|
100
|
+
'orchestrator' {
|
|
101
|
+
return @'
|
|
102
|
+
## Step checklist
|
|
103
|
+
|
|
104
|
+
- [ ] Step 1 — <!-- TODO(skill-creator): first concrete action -->
|
|
105
|
+
- [ ] Step 2 — <!-- TODO(skill-creator): second concrete action -->
|
|
106
|
+
- [ ] Step 3 — <!-- TODO(skill-creator): third concrete action -->
|
|
107
|
+
- [ ] Final — Run self-check + evals; only commit on green.
|
|
108
|
+
'@
|
|
109
|
+
}
|
|
110
|
+
default {
|
|
111
|
+
return @'
|
|
112
|
+
## Steps
|
|
113
|
+
|
|
114
|
+
1. <!-- TODO(skill-creator): first step -->
|
|
115
|
+
2. <!-- TODO(skill-creator): second step -->
|
|
116
|
+
|
|
117
|
+
## Validation loop
|
|
118
|
+
|
|
119
|
+
<!-- TODO(skill-creator): describe how to verify this skill ran correctly.
|
|
120
|
+
Every kushi skill SHOULD ship one of: Gotchas / Validation loop / Step checklist.
|
|
121
|
+
Replace this block with the right section for your skill, or keep it. -->
|
|
122
|
+
|
|
123
|
+
1. Run `pwsh plugin/skills/self-check/run.ps1 -Targeted <area>`.
|
|
124
|
+
2. Fix any findings, then re-run.
|
|
125
|
+
'@
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
$typeSection = Get-TypeSection -Type $Type -TemplatesDir $templatesDir
|
|
130
|
+
|
|
131
|
+
# 3) Render SKILL.md.
|
|
132
|
+
$skeleton = Get-Content -Raw (Join-Path $templatesDir 'skill-skeleton.template.md')
|
|
133
|
+
$skillContent = $skeleton `
|
|
134
|
+
-replace '\{\{NAME\}\}', $Name `
|
|
135
|
+
-replace '\{\{DESCRIPTION\}\}', $escaped `
|
|
136
|
+
-replace '\{\{TYPE_SECTION\}\}', [regex]::Escape($typeSection)
|
|
137
|
+
# Undo escape (we used [regex]::Escape to preserve $ etc — restore literal).
|
|
138
|
+
$skillContent = $skillContent -replace [regex]::Escape([regex]::Escape($typeSection)), $typeSection
|
|
139
|
+
# Simpler approach: do the replacement once with literal substitution.
|
|
140
|
+
$skillContent = $skeleton.Replace('{{NAME}}', $Name).Replace('{{DESCRIPTION}}', $escaped).Replace('{{TYPE_SECTION}}', $typeSection)
|
|
141
|
+
|
|
142
|
+
# 4) Render evals/evals.json.
|
|
143
|
+
$evalsTemplate = Get-Content -Raw (Join-Path $templatesDir 'evals-starter.template.json')
|
|
144
|
+
$nameSlug = ($Name -replace '[^a-z0-9-]', '-')
|
|
145
|
+
$evalsContent = $evalsTemplate.Replace('{{NAME}}', $Name).Replace('{{NAME_SLUG}}', $nameSlug)
|
|
146
|
+
|
|
147
|
+
# 5) Write everything (or just print under -DryRun).
|
|
148
|
+
$actions = @(
|
|
149
|
+
"mkdir $skillDir",
|
|
150
|
+
"mkdir $evalsDir",
|
|
151
|
+
"write $skillMd ($(($skillContent -split "`n").Count) lines)",
|
|
152
|
+
"write $evalsJson",
|
|
153
|
+
"write $marker"
|
|
154
|
+
)
|
|
155
|
+
Write-Host ""
|
|
156
|
+
Write-Host "skill-creator: scaffolding '$Name' (type=$Type) under $Root"
|
|
157
|
+
foreach ($a in $actions) { Write-Host " - $a" }
|
|
158
|
+
Write-Host ""
|
|
159
|
+
|
|
160
|
+
if ($DryRun) {
|
|
161
|
+
Write-Host "skill-creator: -DryRun set; no files written."
|
|
162
|
+
exit 0
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if ((Test-Path $skillDir) -and $Force) {
|
|
166
|
+
Remove-Item -LiteralPath $skillDir -Recurse -Force
|
|
167
|
+
}
|
|
168
|
+
New-Item -ItemType Directory -Force -Path $skillDir | Out-Null
|
|
169
|
+
New-Item -ItemType Directory -Force -Path $evalsDir | Out-Null
|
|
170
|
+
|
|
171
|
+
Set-Content -LiteralPath $skillMd -Value $skillContent -Encoding UTF8 -NoNewline
|
|
172
|
+
Set-Content -LiteralPath $evalsJson -Value $evalsContent -Encoding UTF8 -NoNewline
|
|
173
|
+
Set-Content -LiteralPath $marker -Value "Created by skill-creator on $(Get-Date -Format o).`nDelete this file only if the skill is intentionally non-conformant.`n" -Encoding UTF8
|
|
174
|
+
|
|
175
|
+
Write-Host "skill-creator: done. Next steps:"
|
|
176
|
+
Write-Host " 1. grep 'TODO(skill-creator)' $skillMd — fill in placeholders"
|
|
177
|
+
Write-Host " 2. pwsh plugin/skills/skill-checker/check-skill.ps1 -Skill $Name"
|
|
178
|
+
Write-Host " 3. npm run eval -- $Name"
|
|
179
|
+
Write-Host ""
|
|
180
|
+
exit 0
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill": "{{NAME}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Starter evals for {{NAME}}. Replace cases as soon as the skill has real behavior to assert.",
|
|
5
|
+
"cases": [
|
|
6
|
+
{
|
|
7
|
+
"id": "{{NAME_SLUG}}-skillmd-exists",
|
|
8
|
+
"name": "SKILL.md exists for {{NAME}}",
|
|
9
|
+
"input": "verify {{NAME}} skill artifacts",
|
|
10
|
+
"canary": false,
|
|
11
|
+
"grader_type": "script",
|
|
12
|
+
"expected_assertions": [
|
|
13
|
+
{ "type": "file-exists", "path": "plugin/skills/{{NAME}}/SKILL.md" }
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": "{{NAME_SLUG}}-description-optimized",
|
|
18
|
+
"name": "SKILL.md description leads with USE WHEN",
|
|
19
|
+
"input": "verify {{NAME}} description shape",
|
|
20
|
+
"canary": false,
|
|
21
|
+
"grader_type": "script",
|
|
22
|
+
"expected_assertions": [
|
|
23
|
+
{ "type": "file-contains", "path": "plugin/skills/{{NAME}}/SKILL.md", "value": "USE WHEN" }
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
## Gotchas
|
|
2
|
+
|
|
3
|
+
<!-- TODO(skill-creator): top-5 failure modes. Each bullet starts with the concrete failure, then the correction. -->
|
|
4
|
+
|
|
5
|
+
- **<failure mode 1>** → <correction>
|
|
6
|
+
- **<failure mode 2>** → <correction>
|
|
7
|
+
- **<failure mode 3>** → <correction>
|
|
8
|
+
- **<failure mode 4>** → <correction>
|
|
9
|
+
- **<failure mode 5>** → <correction>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "{{NAME}}"
|
|
3
|
+
version: "0.1.0"
|
|
4
|
+
description: "{{DESCRIPTION}}"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Skill: {{NAME}}
|
|
8
|
+
|
|
9
|
+
<!-- TODO(skill-creator): one-paragraph purpose. What does this skill DO, and when does it trigger? -->
|
|
10
|
+
|
|
11
|
+
User triggers: <!-- TODO(skill-creator): list the literal user phrases / verbs that should route here. -->
|
|
12
|
+
|
|
13
|
+
## USE WHEN
|
|
14
|
+
|
|
15
|
+
- <!-- TODO(skill-creator): situational trigger 1 -->
|
|
16
|
+
- <!-- TODO(skill-creator): situational trigger 2 -->
|
|
17
|
+
|
|
18
|
+
## DO NOT USE FOR
|
|
19
|
+
|
|
20
|
+
- <!-- TODO(skill-creator): the most likely near-miss invocation -->
|
|
21
|
+
|
|
22
|
+
{{TYPE_SECTION}}
|
|
23
|
+
|
|
24
|
+
## References
|
|
25
|
+
|
|
26
|
+
- `plugin/instructions/skill-authoring.instructions.md`
|
|
27
|
+
- `plugin/instructions/agentskills-compliance.instructions.md`
|
|
28
|
+
<!-- TODO(skill-creator): add doctrine + reference pack links relevant to this skill. -->
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill": "tour",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Auto-seeded evals for tour. Replace with real cases as the skill matures.",
|
|
5
|
+
"cases": [
|
|
6
|
+
{
|
|
7
|
+
"id": "tour-smoke-1",
|
|
8
|
+
"name": "tour produces a non-empty response",
|
|
9
|
+
"input": "synthetic tour probe — canary smoke",
|
|
10
|
+
"canary": false,
|
|
11
|
+
"grader_type": "script",
|
|
12
|
+
"expected_assertions": [
|
|
13
|
+
{
|
|
14
|
+
"type": "regex-match",
|
|
15
|
+
"pattern": ".+"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "tour-smoke-2",
|
|
21
|
+
"name": "tour echoes case id",
|
|
22
|
+
"input": "case-id tour-smoke-2",
|
|
23
|
+
"canary": false,
|
|
24
|
+
"grader_type": "script",
|
|
25
|
+
"expected_assertions": [
|
|
26
|
+
{
|
|
27
|
+
"type": "regex-match",
|
|
28
|
+
"pattern": "tour-smoke-2"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
@@ -141,3 +141,13 @@ Write one row to `<project>/Evidence/<alias>/tracking.md` per per-source-verific
|
|
|
141
141
|
|
|
142
142
|
- [`emit-vertex/SKILL.md`](../emit-vertex/SKILL.md)
|
|
143
143
|
- [`vertex-emit.instructions.md`](../../instructions/vertex-emit.instructions.md)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
## Validation loop
|
|
147
|
+
|
|
148
|
+
<!-- TODO(retrofit): fill in — describe how to verify this skill ran correctly. Auto-added by skill-checker --retrofit --apply per skill-authoring.instructions.md. -->
|
|
149
|
+
|
|
150
|
+
1. Run pwsh plugin/skills/self-check/run.ps1 -Targeted <area>.
|
|
151
|
+
2. Fix any findings, then re-run the affected step.
|
|
152
|
+
3. Repeat until self-check exits 0.
|
|
153
|
+
4. Only then update
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill": "vertex-link",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Auto-seeded evals for vertex-link. Replace with real cases as the skill matures.",
|
|
5
|
+
"cases": [
|
|
6
|
+
{
|
|
7
|
+
"id": "vertex-link-smoke-1",
|
|
8
|
+
"name": "vertex-link produces a non-empty response",
|
|
9
|
+
"input": "synthetic vertex-link probe — canary smoke",
|
|
10
|
+
"canary": false,
|
|
11
|
+
"grader_type": "script",
|
|
12
|
+
"expected_assertions": [
|
|
13
|
+
{
|
|
14
|
+
"type": "regex-match",
|
|
15
|
+
"pattern": ".+"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "vertex-link-smoke-2",
|
|
21
|
+
"name": "vertex-link echoes case id",
|
|
22
|
+
"input": "case-id vertex-link-smoke-2",
|
|
23
|
+
"canary": false,
|
|
24
|
+
"grader_type": "script",
|
|
25
|
+
"expected_assertions": [
|
|
26
|
+
{
|
|
27
|
+
"type": "regex-match",
|
|
28
|
+
"pattern": "vertex-link-smoke-2"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|