kushi-agents 6.1.2 → 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/package.json +1 -1
  2. package/plugin/instructions/agentskills-compliance.instructions.md +144 -0
  3. package/plugin/instructions/dashboard-artifact.instructions.md +132 -0
  4. package/plugin/instructions/guided-tour.instructions.md +100 -0
  5. package/plugin/instructions/karpathy-state-layout.instructions.md +124 -0
  6. package/plugin/instructions/schema-evolve.instructions.md +73 -0
  7. package/plugin/instructions/skill-authoring.instructions.md +147 -0
  8. package/plugin/instructions/skill-evals.instructions.md +130 -0
  9. package/plugin/runners/bootstrap.mjs +55 -22
  10. package/plugin/runners/lib/runlog.mjs +153 -6
  11. package/plugin/runners/migrate-to-v550.mjs +192 -0
  12. package/plugin/runners/pull-email.mjs +194 -3
  13. package/plugin/runners/pull-meetings.mjs +207 -4
  14. package/plugin/runners/pull-onenote.mjs +239 -3
  15. package/plugin/runners/pull-sharepoint.mjs +284 -3
  16. package/plugin/runners/pull-state.mjs +297 -0
  17. package/plugin/runners/pull-teams.mjs +170 -3
  18. package/plugin/runners/refresh.mjs +9 -1
  19. package/plugin/runners/test/fixtures/email-abn-amro.json +13 -0
  20. package/plugin/runners/test/fixtures/email-novel-error.json +9 -0
  21. package/plugin/runners/test/fixtures/meetings-abn-amro.json +10 -0
  22. package/plugin/runners/test/fixtures/meetings-body-unavailable.json +10 -0
  23. package/plugin/runners/test/fixtures/onenote-abn-amro.json +30 -0
  24. package/plugin/runners/test/fixtures/onenote-partial.json +21 -0
  25. package/plugin/runners/test/fixtures/refresh-dir/email.json +7 -4
  26. package/plugin/runners/test/fixtures/refresh-dir/teams.json +6 -4
  27. package/plugin/runners/test/fixtures/sharepoint-abn-amro.json +12 -0
  28. package/plugin/runners/test/fixtures/teams-abn-amro.json +11 -0
  29. package/plugin/runners/test/integration/migrate-to-v550.integration.test.mjs +138 -0
  30. package/plugin/runners/test/integration/pull-email.integration.test.mjs +149 -0
  31. package/plugin/runners/test/integration/pull-meetings.integration.test.mjs +92 -0
  32. package/plugin/runners/test/integration/pull-onenote.integration.test.mjs +86 -0
  33. package/plugin/runners/test/integration/pull-sharepoint.integration.test.mjs +93 -0
  34. package/plugin/runners/test/integration/pull-teams.integration.test.mjs +91 -0
  35. package/plugin/runners/test/unit/runlog.test.mjs +1 -1
  36. package/plugin/skills/build-state/SKILL.md +195 -0
  37. package/plugin/skills/build-state/evals/evals.json +31 -0
  38. package/plugin/skills/dashboard/SKILL.md +132 -0
  39. package/plugin/skills/dashboard/evals/evals.json +33 -0
  40. package/plugin/skills/lint-state/.created-by-skill-creator +0 -0
  41. package/plugin/skills/lint-state/SKILL.md +98 -0
  42. package/plugin/skills/lint-state/evals/evals.json +34 -0
  43. package/plugin/skills/lint-state/lint.ps1 +218 -0
  44. package/plugin/skills/promote/.created-by-skill-creator +1 -0
  45. package/plugin/skills/promote/SKILL.md +125 -0
  46. package/plugin/skills/promote/evals/evals.json +35 -0
  47. package/plugin/skills/schema-evolve/.created-by-skill-creator +0 -0
  48. package/plugin/skills/schema-evolve/SKILL.md +106 -0
  49. package/plugin/skills/schema-evolve/evals/evals.json +37 -0
  50. package/plugin/skills/skill-checker/SKILL.md +136 -0
  51. package/plugin/skills/skill-checker/check-skill.ps1 +416 -0
  52. package/plugin/skills/skill-checker/evals/evals.json +41 -0
  53. package/plugin/skills/skill-creator/SKILL.md +134 -0
  54. package/plugin/skills/skill-creator/evals/evals.json +40 -0
  55. package/plugin/skills/skill-creator/generate-eval-review.ps1 +101 -0
  56. package/plugin/skills/skill-creator/optimize-description.ps1 +87 -0
  57. package/plugin/skills/skill-creator/scaffold.ps1 +180 -0
  58. package/plugin/skills/skill-creator/templates/evals-starter.template.json +27 -0
  59. package/plugin/skills/skill-creator/templates/gotchas-stub.template.md +9 -0
  60. package/plugin/skills/skill-creator/templates/skill-skeleton.template.md +28 -0
  61. package/plugin/skills/teach/.created-by-skill-creator +0 -0
  62. package/plugin/skills/teach/SKILL.md +79 -0
  63. package/plugin/skills/teach/evals/evals.json +59 -0
  64. package/plugin/skills/tour/SKILL.md +85 -0
  65. package/plugin/skills/tour/build-tour.ps1 +185 -0
  66. package/plugin/skills/tour/evals/evals.json +33 -0
  67. package/plugin/templates/state/00_overview.template.md +44 -0
  68. package/plugin/templates/state/01_decisions.template.md +41 -0
  69. package/plugin/templates/state/02_stakeholders.template.md +48 -0
  70. package/plugin/templates/state/03_architecture-and-solution.template.md +56 -0
  71. package/plugin/templates/state/04_workshops-and-key-meetings.template.md +43 -0
  72. package/plugin/templates/state/05_action-items.template.md +29 -0
  73. package/plugin/templates/state/06_risks-and-issues.template.md +43 -0
  74. package/plugin/templates/state/07_timeline-and-milestones.template.md +45 -0
  75. package/plugin/templates/state/08_artifacts-and-deliverables.template.md +55 -0
  76. package/plugin/templates/state/09_open-questions.template.md +62 -0
  77. package/plugin/templates/state/AGENTS.template.md +33 -0
  78. package/plugin/templates/state/CLAUDE.template.md +33 -0
  79. package/plugin/templates/state/README.md +41 -0
  80. package/plugin/templates/state/answers.README.md +7 -0
  81. package/plugin/templates/state/hot.template.md +12 -0
  82. package/plugin/templates/state/index.template.md +41 -0
  83. package/plugin/templates/state/log.template.md +14 -0
  84. package/plugin/templates/state/page.template.md +22 -0
  85. package/plugin/templates/state/review-queue.template.md +10 -0
  86. package/plugin/runners/test/integration/csc-pull.integration.test.mjs +0 -160
@@ -0,0 +1,218 @@
1
+ <#
2
+ .SYNOPSIS
3
+ lint-state — runs wiki-lint checks against a project's State/ directory.
4
+
5
+ .DESCRIPTION
6
+ Implements the finding classes defined in wiki-lint.instructions.md:
7
+ - contradiction-flagged
8
+ - stale-claim
9
+ - orphan-page
10
+ - missing-cross-ref
11
+ - data-gap
12
+
13
+ Writes a dated lint report to State/reports/, appends to State/log.md, updates
14
+ State/index.md "Last touched" pointer.
15
+
16
+ .PARAMETER StateDir
17
+ Path to the State/ directory to lint.
18
+
19
+ .PARAMETER GraphFile
20
+ Optional path to project-graph.json for missing-cross-ref checks.
21
+
22
+ .PARAMETER Quiet
23
+ Suppress console output (for programmatic use).
24
+ #>
25
+ [CmdletBinding()]
26
+ param(
27
+ [Parameter(Mandatory)][string]$StateDir,
28
+ [string]$GraphFile,
29
+ [switch]$Quiet
30
+ )
31
+
32
+ $ErrorActionPreference = 'Stop'
33
+ $findings = @()
34
+
35
+ # --- Helpers ---
36
+ function Add-LintFinding {
37
+ param([string]$Class, [string]$Severity, [string]$Entity, [string]$Description, [string]$File, [int]$Line, [string]$Fix)
38
+ $script:findings += [PSCustomObject]@{
39
+ class = $Class
40
+ severity = $Severity
41
+ entity = $Entity
42
+ description = $Description
43
+ file = $File
44
+ line = $Line
45
+ fix = $Fix
46
+ }
47
+ }
48
+
49
+ # --- Check 1: contradiction-flagged ---
50
+ $stateFiles = Get-ChildItem -Path $StateDir -Recurse -Filter '*.md' -ErrorAction SilentlyContinue
51
+ foreach ($f in $stateFiles) {
52
+ $lines = Get-Content -Path $f.FullName -ErrorAction SilentlyContinue
53
+ for ($i = 0; $i -lt $lines.Count; $i++) {
54
+ if ($lines[$i] -match '^\s*>\s*\[!warning\]\s*Contradicted') {
55
+ $entity = $f.BaseName -replace '-', ' '
56
+ Add-LintFinding 'contradiction-flagged' 'warning' $entity "Unresolved contradiction callout" $f.FullName ($i + 1) "Review both values, pick the correct one. Delete callout pair and keep winner, or add <!-- kushi:human-override --> to prevent auto-resolve."
57
+ }
58
+ }
59
+ }
60
+
61
+ # --- Check 2: stale-claim ---
62
+ $staleDays = 60
63
+ $now = Get-Date
64
+ foreach ($f in $stateFiles) {
65
+ $content = Get-Content -Raw $f.FullName -ErrorAction SilentlyContinue
66
+ if (-not $content) { continue }
67
+ $citations = [regex]::Matches($content, '\[source:[^\]]*\xB7\s*(\d{4}-\d{2}-\d{2})\]')
68
+ if ($citations.Count -eq 0) { continue }
69
+ $newestDate = $null
70
+ foreach ($m in $citations) {
71
+ try {
72
+ $d = [datetime]::ParseExact($m.Groups[1].Value, 'yyyy-MM-dd', $null)
73
+ if (-not $newestDate -or $d -gt $newestDate) { $newestDate = $d }
74
+ } catch {}
75
+ }
76
+ if ($newestDate -and ($now - $newestDate).TotalDays -gt $staleDays) {
77
+ $entity = $f.BaseName -replace '-', ' '
78
+ Add-LintFinding 'stale-claim' 'warning' $entity "Newest citation is $([math]::Round(($now - $newestDate).TotalDays)) days old (threshold: $staleDays)" $f.FullName 0 "Run '@Kushi refresh <project>' then '@Kushi state <project>' to pull fresh evidence. If claim is still valid, add a corroborating citation from a recent source."
79
+ }
80
+ }
81
+
82
+ # --- Check 3: orphan-page ---
83
+ $indexFile = Join-Path $StateDir 'index.md'
84
+ $indexContent = ''
85
+ if (Test-Path $indexFile) { $indexContent = Get-Content -Raw $indexFile }
86
+ $allPageContent = ''
87
+ foreach ($f in $stateFiles) {
88
+ if ($f.FullName -ne (Resolve-Path $indexFile -ErrorAction SilentlyContinue)) {
89
+ $allPageContent += (Get-Content -Raw $f.FullName -ErrorAction SilentlyContinue) + "`n"
90
+ }
91
+ }
92
+ foreach ($f in $stateFiles) {
93
+ $content = Get-Content -Raw $f.FullName -ErrorAction SilentlyContinue
94
+ if ($content -notmatch 'kushi_state_page:\s*true') { continue }
95
+ $slug = $f.BaseName
96
+ $relPath = $f.FullName.Replace($StateDir, '').TrimStart('\', '/') -replace '\\', '/'
97
+ if ($indexContent -notmatch [regex]::Escape($slug) -and $allPageContent -notmatch [regex]::Escape($slug)) {
98
+ Add-LintFinding 'orphan-page' 'warning' $slug "Page not linked from index.md or any sibling" $f.FullName 0 "Add [[$(Split-Path $relPath -Parent)/$slug]] to index.md under the correct category heading, or delete this page if generated in error."
99
+ }
100
+ }
101
+
102
+ # --- Check 4: missing-cross-ref ---
103
+ if ($GraphFile -and (Test-Path $GraphFile)) {
104
+ try {
105
+ $graph = Get-Content -Raw $GraphFile | ConvertFrom-Json
106
+ $entityNames = @()
107
+ if ($graph.nodes) { $entityNames = $graph.nodes | ForEach-Object { $_.name } | Where-Object { $_ } }
108
+ foreach ($f in $stateFiles) {
109
+ $content = Get-Content -Raw $f.FullName -ErrorAction SilentlyContinue
110
+ if ($content -notmatch 'kushi_state_page:\s*true') { continue }
111
+ foreach ($name in $entityNames) {
112
+ if ($name.Length -lt 3) { continue }
113
+ if ($content -match [regex]::Escape($name) -and $content -notmatch "entity_ids:.*$([regex]::Escape($name))" -and $content -notmatch "related:.*$([regex]::Escape($name))") {
114
+ # Check frontmatter more carefully
115
+ if ($content -match '(?ms)^---\r?\n(.*?)\r?\n---') {
116
+ $fm = $Matches[1]
117
+ if ($fm -notmatch [regex]::Escape($name)) {
118
+ Add-LintFinding 'missing-cross-ref' 'info' ($f.BaseName -replace '-',' ') "Entity '$name' mentioned in body but not in frontmatter" $f.FullName 0 "Add to related: array in front-matter."
119
+ }
120
+ }
121
+ }
122
+ }
123
+ }
124
+ } catch {
125
+ Write-Warning "Could not parse graph file: $_"
126
+ }
127
+ }
128
+
129
+ # --- Check 5: data-gap ---
130
+ foreach ($f in $stateFiles) {
131
+ $content = Get-Content -Raw $f.FullName -ErrorAction SilentlyContinue
132
+ if ($content -notmatch 'kushi_state_page:\s*true') { continue }
133
+ $lines = Get-Content -Path $f.FullName
134
+ for ($i = 0; $i -lt $lines.Count; $i++) {
135
+ if ($lines[$i] -match '^#{2,3}\s+(.+)$') {
136
+ $heading = $Matches[1]
137
+ # Look at content until next heading or EOF
138
+ $bodyLines = @()
139
+ for ($j = $i + 1; $j -lt $lines.Count; $j++) {
140
+ if ($lines[$j] -match '^#{2,3}\s+') { break }
141
+ if ($lines[$j].Trim() -and $lines[$j].Trim() -ne '<!-- TODO -->' -and $lines[$j].Trim() -ne '<!-- TODO(retrofit) -->') {
142
+ $bodyLines += $lines[$j]
143
+ }
144
+ }
145
+ if ($bodyLines.Count -lt 2 -and $heading -notmatch '^(State Log|State Index|Review Queue)') {
146
+ Add-LintFinding 'data-gap' 'info' ($f.BaseName -replace '-',' ') "Section '$heading' has no meaningful content" $f.FullName ($i + 1) "Run '@Kushi refresh <project>' to pull evidence for this section, or remove the header if not applicable."
147
+ }
148
+ }
149
+ }
150
+ }
151
+
152
+ # --- Generate report ---
153
+ $reportsDir = Join-Path $StateDir 'reports'
154
+ if (-not (Test-Path $reportsDir)) { New-Item -Path $reportsDir -ItemType Directory -Force | Out-Null }
155
+
156
+ $dateStr = Get-Date -Format 'yyyy-MM-dd'
157
+ $reportFile = Join-Path $reportsDir "lint-$dateStr.md"
158
+ $timestamp = Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ'
159
+
160
+ $classCounts = @{}
161
+ foreach ($f2 in $findings) {
162
+ if (-not $classCounts.ContainsKey($f2.class)) { $classCounts[$f2.class] = 0 }
163
+ $classCounts[$f2.class]++
164
+ }
165
+
166
+ $report = @"
167
+ ---
168
+ generated_at: "$timestamp"
169
+ generated_by: "lint-state v1.0.0"
170
+ findings_count: $($findings.Count)
171
+ ---
172
+
173
+ # Lint Report — $dateStr
174
+
175
+ ## Summary
176
+
177
+ | Class | Count | Severity |
178
+ |---|---|---|
179
+ "@
180
+
181
+ $classOrder = @('contradiction-flagged','stale-claim','orphan-page','missing-cross-ref','data-gap')
182
+ foreach ($cls in $classOrder) {
183
+ $count = if ($classCounts.ContainsKey($cls)) { $classCounts[$cls] } else { 0 }
184
+ $sev = if ($cls -match 'contradiction|stale|orphan') { 'warning' } else { 'info' }
185
+ $report += "| $cls | $count | $sev |`n"
186
+ }
187
+
188
+ $report += "`n**Total findings: $($findings.Count)**`n`n## Findings`n"
189
+
190
+ foreach ($f2 in $findings) {
191
+ $report += "`n### $($f2.class): $($f2.entity) — $($f2.description)`n`n"
192
+ $report += "**File:** ``$($f2.file)```n"
193
+ if ($f2.line -gt 0) { $report += "**Line:** $($f2.line)`n" }
194
+ $report += "**Fix:** $($f2.fix)`n"
195
+ }
196
+
197
+ Set-Content -Path $reportFile -Value $report -Encoding utf8NoBOM
198
+
199
+ # --- Update log + index ---
200
+ $sharedDir = Join-Path $PSScriptRoot '..\_shared'
201
+ $appendLog = Join-Path $sharedDir 'Append-StateLog.ps1'
202
+ $updateIndex = Join-Path $sharedDir 'Update-StateIndex.ps1'
203
+
204
+ if (Test-Path $appendLog) {
205
+ & $appendLog -StateDir $StateDir -Op 'lint-state' -Title "Lint pass ($($findings.Count) findings)" -Summary "Classes: $(($classCounts.Keys | Sort-Object | ForEach-Object { "$_=$($classCounts[$_])" }) -join ', ')" -Sources "State/**/*.md"
206
+ }
207
+ if (Test-Path $updateIndex) {
208
+ & $updateIndex -StateDir $StateDir -Op 'lint-state'
209
+ }
210
+
211
+ # --- Console output ---
212
+ if (-not $Quiet) {
213
+ $breakdown = ($classCounts.Keys | Sort-Object | ForEach-Object { "$_=$($classCounts[$_])" }) -join ', '
214
+ Write-Host "lint-state: $($findings.Count) findings ($breakdown). Report: $reportFile"
215
+ }
216
+
217
+ # Return findings as output for programmatic use
218
+ $findings
@@ -0,0 +1,125 @@
1
+ ---
2
+ name: "promote"
3
+ version: "1.0.0"
4
+ description: "USE WHEN the user says 'kushi promote <project> <page>', 'promote this answer to global', 'move this page to my global wiki', or wants to copy a project State page into the cross-engagement global wiki at ~/.kushi-global/. DO NOT USE for project Q&A (use ask-project) or for initializing the global wiki itself (use global-wiki). Capability: identifier-scan + redact + write + back-link + dual-log. Refuses without --force when customer identifiers are detected."
5
+ ---
6
+
7
+ # Skill: promote
8
+
9
+ Copies a single project State page into the global wiki (`$KUSHI_GLOBAL_ROOT/State/answers/`) with provenance metadata, an identifier-detection gate, and a back-link in the source page.
10
+
11
+ This is the **only** path from project → global. Auto-promotion is intentionally not supported. See `multi-wiki-routing.instructions.md` § Promote operation for the contract.
12
+
13
+ ## Triggers
14
+
15
+ - `kushi promote <project> <page-path>`
16
+ - `kushi promote <project> <page-path> --force`
17
+ - "promote this answer to global"
18
+ - "move <page> into my global wiki"
19
+
20
+ ## Inputs
21
+
22
+ - `<project>` — project name relative to cwd (or under `..`).
23
+ - `<page-path>` — path to the source page. Accepted forms:
24
+ - Absolute path.
25
+ - Path relative to the project root (e.g. `Evidence/<alias>/State/answers/2026-05-27_<slug>.md`).
26
+ - Path relative to a discovered `Evidence/<alias>/State/` (e.g. `answers/<slug>.md`).
27
+ - Path relative to a plain `<project>/State/` (e.g. `answers/<slug>.md`).
28
+ - `--force` — required when the identifier scan finds hits.
29
+
30
+ ## Step checklist
31
+
32
+ - [ ] Step 1 — Resolve project root + source page.
33
+ - [ ] Step 2 — Read source + run identifier scan.
34
+ - [ ] Step 3 — Refuse without `--force` if hits detected; print review prompt.
35
+ - [ ] Step 4 — Write redacted target with `scope: global` frontmatter.
36
+ - [ ] Step 5 — Add `[!info] Promoted to global wiki` back-link in source.
37
+ - [ ] Step 6 — Append `promote` entry to project `State/log.md`.
38
+ - [ ] Step 7 — Append `promote-in` entry to global `State/log.md`.
39
+ - [ ] Step 8 — Print summary.
40
+
41
+ ### Step 1 — Resolve
42
+
43
+ Find the source via `resolveProjectRoot` + `resolveSourcePage` in `src/global-wiki-cli.mjs`. Echo the resolved absolute path before any write.
44
+
45
+ ### Step 2 — Identifier scan
46
+
47
+ Use `detectIdentifiers()` from `src/global-wiki.mjs`. Patterns checked:
48
+
49
+ - The literal project name (case-insensitive, word boundary).
50
+ - The alias folder name (inferred from `Evidence/<alias>/` in the path).
51
+ - Any extra aliases supplied (optional flag, future).
52
+ - Non-Microsoft email addresses (`@*` where domain does not end in `microsoft.com`).
53
+
54
+ Each hit records `{ pattern, kind, count, line_numbers }`.
55
+
56
+ ### Step 3 — Refuse gate
57
+
58
+ If hits > 0 AND `--force` not supplied → refuse with exit code 2, print the hits with line numbers, suggest review. **No filesystem writes occur on refusal.**
59
+
60
+ ### Step 4 — Write target
61
+
62
+ Target path: `$KUSHI_GLOBAL_ROOT/State/answers/<slug>.md` where `<slug>` = slugified source filename (lowercase, alnum + hyphens, ≤60 chars). Collisions append `-N`.
63
+
64
+ Frontmatter MUST include:
65
+
66
+ ```yaml
67
+ ---
68
+ kushi_state_page: true
69
+ scope: global
70
+ promoted_from: "<project>/<relative-source-path>"
71
+ promoted_at: "<ISO-8601 UTC>"
72
+ redactions: ["<pattern1>", "<pattern2>"]
73
+ ---
74
+ ```
75
+
76
+ If any redactions occurred, append a `> [!warning] potential-customer-leak` callout listing the patterns so `global lint` tracks the open review item.
77
+
78
+ ### Step 5 — Back-link
79
+
80
+ Append to the source page (idempotent — skip if the target slug is already linked):
81
+
82
+ ```markdown
83
+ > [!info] Promoted to global wiki
84
+ > answers/<slug>.md @ <iso> · redactions: <N>
85
+ ```
86
+
87
+ ### Step 6 / 7 — Dual log
88
+
89
+ - Project `State/log.md`: `promote | Promoted <slug> to global (redactions: N)`.
90
+ - Global `State/log.md`: `promote-in | Imported <slug> from <project> (redactions: N)`.
91
+
92
+ ### Step 8 — Print summary
93
+
94
+ Echo `source`, `target`, `slug`, `redactions`, `promoted_at`.
95
+
96
+ ## Hard rules
97
+
98
+ - **Atomic.** No partial state. If any step fails, no writes persist (stage-then-commit).
99
+ - **--force is mandatory** when identifiers are detected. Never auto-redact without it.
100
+ - **Tests use `$env:KUSHI_GLOBAL_ROOT='.testtmp/.kushi-global'`.** Real `~/.kushi-global/` is user data.
101
+ - **Never modify project Evidence beyond the back-link.** No edits to the redacted lines in the source — only the global copy is redacted.
102
+ - **Provenance frontmatter required.** `promoted_from`, `promoted_at`, `redactions` are MANDATORY on the global copy.
103
+
104
+ ## Stop conditions
105
+
106
+ - Project unresolved → exit 1, ask the user to verify cwd.
107
+ - Source page unresolved → exit 1.
108
+ - Identifier hits without `--force` → exit 2, print hits.
109
+
110
+ ## Validation loop
111
+
112
+ 1. Run `pwsh plugin/skills/self-check/run.ps1 -Targeted D40`.
113
+ 2. Run `node --test src/promote.test.mjs`.
114
+ 3. Fix any findings + re-run.
115
+
116
+ ## References
117
+
118
+ - `../../instructions/global-wiki.instructions.md`
119
+ - `../../instructions/multi-wiki-routing.instructions.md`
120
+ - `../../instructions/schema-evolve.instructions.md`
121
+ - `../global-wiki/SKILL.md`
122
+
123
+ ## Issue Recovery
124
+
125
+ If the identifier scan misses a customer pattern (false negative) or flags a non-identifier (false positive), fix the heuristic in `src/global-wiki.mjs::detectIdentifiers` first, add a regression case to `src/promote.test.mjs`, then re-run.
@@ -0,0 +1,35 @@
1
+ {
2
+ "skill": "promote",
3
+ "cases": [
4
+ {
5
+ "id": "promote-refuses-on-customer-name",
6
+ "name": "kushi promote refuses without --force when a customer name is detected",
7
+ "input": "kushi promote acme answers/2026-05-27_confidence-ladder.md",
8
+ "expected_assertions": [
9
+ { "type": "contains", "value": "refused" },
10
+ { "type": "contains", "value": "identifier" }
11
+ ],
12
+ "grader_type": "script"
13
+ },
14
+ {
15
+ "id": "promote-force-writes-redacted",
16
+ "name": "kushi promote --force writes the redacted target + back-link + dual logs",
17
+ "input": "kushi promote acme answers/2026-05-27_confidence-ladder.md --force",
18
+ "expected_assertions": [
19
+ { "type": "contains", "value": "Promoted" },
20
+ { "type": "contains", "value": "redacts" }
21
+ ],
22
+ "grader_type": "script"
23
+ },
24
+ {
25
+ "id": "promote-clean-page-no-redactions",
26
+ "name": "kushi promote on a customer-free page writes immediately with zero redactions",
27
+ "input": "kushi promote acme answers/2026-05-27_generic-pattern.md",
28
+ "expected_assertions": [
29
+ { "type": "contains", "value": "Promoted" },
30
+ { "type": "contains", "value": "redacts: 0" }
31
+ ],
32
+ "grader_type": "script"
33
+ }
34
+ ]
35
+ }
@@ -0,0 +1,106 @@
1
+ ---
2
+ name: "schema-evolve"
3
+ version: "1.0.0"
4
+ description: "USE WHEN the user says 'from now on always do X', 'remember this rule', 'kushi remember <rule>', or wants to teach kushi a project-specific convention that persists across runs. DO NOT USE for one-time instructions (just answer inline) or for modifying doctrine (edit the .instructions.md file directly). Capability: captures user-stated conventions to Evidence/<alias>/State/CLAUDE.md, reads them back at the start of build-state/ask-project/refresh runs."
5
+ ---
6
+
7
+ # Skill: schema-evolve
8
+
9
+ Captures user-stated project conventions and persists them to `Evidence/<alias>/State/CLAUDE.md` so future runs respect them automatically.
10
+
11
+ ## Triggers
12
+
13
+ - "from now on always do X for this project"
14
+ - "remember that Y means Z"
15
+ - `kushi remember <rule>` (CLI verb)
16
+ - "for this project, always treat A as B"
17
+ - "never include X in summaries for this project"
18
+
19
+ ## Inputs
20
+
21
+ - `<rule>` — free-text convention statement.
22
+ - `<project>` — engagement name (resolved from context or explicit).
23
+
24
+ ## Procedure
25
+
26
+ 1. **Resolve project** — identify the target project via fuzzy match or current context.
27
+ 2. **Parse rule** — extract the convention from user's statement. Normalize to declarative form.
28
+ 3. **Validate against doctrine** — check if the rule conflicts with hard doctrine (WorkIQ-only, CSC format, etc.). If conflict: warn user, explain why, do NOT persist.
29
+ 4. **Write to CLAUDE.md** — append the rule with timestamp + scope metadata to `Evidence/<alias>/State/CLAUDE.md`.
30
+ 5. **Confirm** — echo back the persisted rule and explain when it will be applied.
31
+
32
+ ## Step checklist
33
+
34
+ - [ ] Step 1 — Resolve project + State path
35
+ - [ ] Step 2 — Parse + validate rule
36
+ - [ ] Step 3 — Append to CLAUDE.md
37
+ - [ ] Step 4 — Confirm to user
38
+
39
+ ### Step 1 — Resolve project
40
+
41
+ Resolve `<engagement-root>/<project>/Evidence/<alias>/State/`. If CLAUDE.md doesn't exist, create it with the v5.0.0 header:
42
+
43
+ ```markdown
44
+ ---
45
+ kushi_state_page: true
46
+ ---
47
+
48
+ # CLAUDE.md — Project Conventions
49
+
50
+ Rules captured via `schema-evolve` skill. Read by build-state, ask-project, refresh-project at run start.
51
+ ```
52
+
53
+ ### Step 2 — Parse + validate
54
+
55
+ Normalize user's statement:
56
+ - "from now on always X" → "Always X"
57
+ - "never do Y" → "Never Y"
58
+ - "treat A as B" → "Treat A as B"
59
+
60
+ Validate: if the rule would override hard doctrine (`workiq-only`, `verbatim-by-default`, `csc` format), reject with explanation.
61
+
62
+ ### Step 3 — Append to CLAUDE.md
63
+
64
+ Append after existing content:
65
+
66
+ ```markdown
67
+ ## Rule: <short-title>
68
+
69
+ - **Added**: <ISO-8601 timestamp>
70
+ - **Scope**: project
71
+ - **Source**: user (natural language)
72
+
73
+ <normalized rule text>
74
+ ```
75
+
76
+ ### Step 4 — Confirm
77
+
78
+ Print:
79
+ > ✅ Rule captured: "<rule>"
80
+ > Applied to: <project> (project scope)
81
+ > Active in: build-state, ask-project, refresh-project
82
+
83
+ ## Gotchas
84
+
85
+ 1. **Hard doctrine override**: Rules CANNOT override WorkIQ-only, CSC format, verbatim-by-default, or evidence layout. Warn and refuse.
86
+ 2. **Duplicate rules**: If a near-identical rule exists, update the existing one (bump timestamp) rather than duplicating.
87
+ 3. **CLAUDE.md missing**: Create it with proper frontmatter on first rule capture.
88
+ 4. **No evidence modification**: Rules affect interpretation/presentation only — never modify raw evidence files.
89
+ 5. **Scope creep**: v5.2.0 is project-scope only. Don't promise global scope.
90
+
91
+ ## Validation loop
92
+
93
+ After writing:
94
+ 1. Re-read `CLAUDE.md` — verify the new rule appears.
95
+ 2. Parse all rules — verify valid structure (heading, metadata, text).
96
+ 3. Run self-check: `pwsh plugin/skills/self-check/run.ps1 -Targeted schema-evolve`
97
+
98
+ ## References
99
+
100
+ - `schema-evolve.instructions.md` — doctrine for convention persistence
101
+ - `karpathy-state-layout.instructions.md` — CLAUDE.md role in State layout
102
+ - `living-wiki.instructions.md` — incremental State maintenance
103
+
104
+ ## Issue Recovery
105
+
106
+ When this skill exposes a reusable defect (doctrine gap, missing cross-reference), apply the [Issue Recovery Rule](../../instructions/issue-recovery.instructions.md): fix the smallest correct repo-owned artifact first, prefer durable fixes over per-run workarounds, then re-run the narrowest failed check. Do NOT use memory as a substitute for correcting the workflow surface.
@@ -0,0 +1,37 @@
1
+ {
2
+ "skill": "schema-evolve",
3
+ "cases": [
4
+ {
5
+ "id": "schema-evolve-capture",
6
+ "name": "Captures a user rule to CLAUDE.md",
7
+ "input": "kushi remember always use 'Northwind' not 'Healthcare Accelerator' in summaries",
8
+ "expected_assertions": [
9
+ { "type": "contains", "value": "CLAUDE.md" },
10
+ { "type": "contains", "value": "Rule" },
11
+ { "type": "contains", "value": "Northwind" }
12
+ ],
13
+ "grader_type": "script"
14
+ },
15
+ {
16
+ "id": "schema-evolve-doctrine-conflict",
17
+ "name": "Rejects rule that conflicts with hard doctrine",
18
+ "input": "kushi remember never use WorkIQ, always use Graph API directly",
19
+ "expected_assertions": [
20
+ { "type": "contains", "value": "conflict" },
21
+ { "type": "contains", "value": "WorkIQ" },
22
+ { "type": "not_contains", "value": "Rule captured" }
23
+ ],
24
+ "grader_type": "script"
25
+ },
26
+ {
27
+ "id": "schema-evolve-natural-language",
28
+ "name": "Auto-detects convention from natural language",
29
+ "input": "from now on for this project always treat John as the EM",
30
+ "expected_assertions": [
31
+ { "type": "contains", "value": "John" },
32
+ { "type": "contains", "value": "rule" }
33
+ ],
34
+ "grader_type": "script"
35
+ }
36
+ ]
37
+ }
@@ -0,0 +1,136 @@
1
+ ---
2
+ name: "skill-checker"
3
+ version: "1.0.0"
4
+ description: "USE WHEN the user says \"check skill\", \"lint skill\", \"retrofit skill\", \"audit skills\", \"npx kushi-agents check-skill ...\", or before merging a PR that touches plugin/skills/. DO NOT USE for runtime evidence validation (use ask-project) or for running evals (use the eval skill). Capability: lints any plugin/skills/<name>/ against the agentskills.io blueprint (frontmatter, sections, size caps, evals presence); offers a non-destructive --retrofit + --apply that adds missing section stubs without overwriting existing content; can invoke the skill-creator eval-review viewer."
5
+ ---
6
+
7
+ # Skill: skill-checker
8
+
9
+ The lint + retrofit harness for `plugin/skills/`. Companion to `skill-creator`. The two together close the authoring loop: `create-skill` scaffolds conformant; `check-skill` keeps it conformant + drags legacy skills up to spec.
10
+
11
+ User triggers: "check skill", "lint skill", "audit skills", "retrofit skill", "is this skill conformant?", "kushi check-skill --all".
12
+
13
+ Doctrine: [`plugin/instructions/skill-authoring.instructions.md`](../../instructions/skill-authoring.instructions.md).
14
+
15
+ ## USE WHEN
16
+
17
+ - Before merging a PR that touches `plugin/skills/`.
18
+ - After running `skill-creator/scaffold.ps1`, to verify the scaffold output passes lint.
19
+ - Auditing the entire `plugin/skills/` tree against the blueprint (`-All`).
20
+ - Migrating legacy skills written before v5.0.4 (`-Retrofit`, then `-Apply`).
21
+ - Refreshing a skill's description per the optimization rules (`-OptimizeDescription`).
22
+
23
+ ## DO NOT USE FOR
24
+
25
+ - Validating real customer evidence (use `ask-project` / `project-status`).
26
+ - Running per-case evals (use the `eval` skill + `npm run eval`).
27
+ - Authoring new skills (use `skill-creator`).
28
+
29
+ ## Gotchas
30
+
31
+ - **Lint reuses self-check D30 + D33 logic** — it does not duplicate the checks. If you change blueprint rules, change them in `self-check/run.ps1` and re-run; this skill picks them up by delegating.
32
+ - **Retrofit is additive only.** It never deletes or rewrites existing content. Missing sections are appended with `<!-- TODO(retrofit): fill in -->` markers. If a section exists but is wrong, retrofit flags it but won't touch it.
33
+ - **`-Apply` requires `-Retrofit`.** Lint mode is read-only by design.
34
+ - **No auto-commit.** Even after `-Apply`, the human reviews the diff and commits manually.
35
+ - **`Evidence/_skill-checker/` is gitignored** — fix plans + scratch HTML stay local.
36
+
37
+ ## Step checklist
38
+
39
+ - [ ] **Pick scope**: `-Skill <name>` (one) OR `-All`.
40
+ - [ ] **Pick mode**: default (`-Lint`), `-Retrofit`, `-Retrofit -Apply`, `-OptimizeDescription`, `-Review`.
41
+ - [ ] **Run**:
42
+
43
+ ```powershell
44
+ pwsh plugin/skills/skill-checker/check-skill.ps1 -Skill my-skill
45
+ pwsh plugin/skills/skill-checker/check-skill.ps1 -All -Retrofit -DryRun
46
+ pwsh plugin/skills/skill-checker/check-skill.ps1 -Skill my-skill -Retrofit -Apply
47
+ ```
48
+
49
+ - [ ] **Inspect output** under `Evidence/_skill-checker/<skill>/` (fix plans) + stdout (findings).
50
+ - [ ] **Re-run lint** after `-Apply` to confirm green.
51
+
52
+ ## Validation loop
53
+
54
+ After running:
55
+
56
+ 1. Lint mode → if exit ≠ 0, fix the findings in the SKILL.md / evals.json directly and re-run.
57
+ 2. Retrofit mode → review the stdout diff; if it looks safe, re-run with `-Apply`.
58
+ 3. Apply mode → run `pwsh plugin/skills/self-check/run.ps1 -Deep -Targeted <skill>` to confirm no regressions.
59
+ 4. Optimize-description → review the diff; if good, copy the rewritten string into SKILL.md frontmatter manually.
60
+ 5. Commit.
61
+
62
+ ## Modes
63
+
64
+ ### `-Lint` (default)
65
+
66
+ Runs the agentskills.io blueprint check. Calls `self-check/run.ps1 -Deep -Targeted <skill>` and filters its findings to the D30/D33-class codes that map to the blueprint. Exits non-zero if any blocking finding remains.
67
+
68
+ ### `-Retrofit`
69
+
70
+ Compares actual structure to the blueprint and emits a fix plan:
71
+
72
+ - Missing `## Gotchas` (pull-* / discovery skills) → append.
73
+ - Missing `## Validation loop` (writer skills) → append.
74
+ - Missing `## Step checklist` (orchestrators) → append.
75
+ - Missing `evals/evals.json` → create starter.
76
+ - Missing `USE WHEN` in description → flag (not auto-fixed — needs human review).
77
+ - Description has marketing fluff → flag.
78
+
79
+ Writes fix-plan JSON to `Evidence/_skill-checker/<skill>/fix-plan.json`. Stdout: human diff.
80
+
81
+ ### `-Apply` (requires `-Retrofit`)
82
+
83
+ Executes the additive parts of the fix plan. Never overwrites; only appends sections + creates missing evals files. Stops short of any change marked `requires_human` in the plan.
84
+
85
+ ### `-OptimizeDescription`
86
+
87
+ Reads the skill's current SKILL.md description, runs `skill-creator/optimize-description.ps1`, prints the diff. Never auto-applies.
88
+
89
+ ### `-Review`
90
+
91
+ Invokes `skill-creator/generate-eval-review.ps1 -Skill <name>` — renders the HTML side-by-side viewer.
92
+
93
+ ## Arguments
94
+
95
+ | Flag | Purpose |
96
+ |---|---|
97
+ | `-Skill <name>` | Target one skill. |
98
+ | `-All` | Target every `plugin/skills/<name>/` except `eval`, `self-check`, `skill-creator`, `skill-checker`. |
99
+ | `-Retrofit` | Emit the fix plan. |
100
+ | `-Apply` | With `-Retrofit`, execute additive fixes. |
101
+ | `-DryRun` | Print actions without writing. |
102
+ | `-OptimizeDescription` | Run the description optimizer + diff. |
103
+ | `-Review` | Render the eval-review HTML viewer. |
104
+ | `-Output <path>` | Override the report / plan output path. |
105
+ | `-Root <path>` | Override repo root. |
106
+ | `-Json` | Emit a JSON report on stdout. |
107
+ | `-StrictExit` | Exit 1 if any finding (else 0). |
108
+
109
+ ## How retrofit categorises gaps
110
+
111
+ | Gap | Kind | Apply behaviour |
112
+ |---|---|---|
113
+ | No `evals/evals.json` | additive | creates starter file with 2 cases |
114
+ | No `## Gotchas` (pull-*) | additive | appends stub block |
115
+ | No `## Validation loop` (writer) | additive | appends stub block |
116
+ | No `## Step checklist` (orchestrator) | additive | appends stub block |
117
+ | Description missing `USE WHEN` | non-additive | flagged; needs human |
118
+ | Description >1024 chars | non-additive | flagged; needs human |
119
+ | SKILL.md >500 lines | non-additive | flagged; split into references/ |
120
+ | Marketing words in description | non-additive | flagged; needs human |
121
+
122
+ ## Output paths
123
+
124
+ - Fix plan JSON: `Evidence/_skill-checker/<skill>/fix-plan.json`
125
+ - Dogfood report (manual rollup): `docs/audits/v5.0.4-skill-creator-dogfood.md`
126
+ - Per-run summary (with `-Output`): wherever you direct it.
127
+
128
+ ## References
129
+
130
+ - `plugin/instructions/skill-authoring.instructions.md` (doctrine)
131
+ - `plugin/instructions/agentskills-compliance.instructions.md` (D30 rules this delegates to)
132
+ - `plugin/instructions/skill-evals.instructions.md` (D33 rules)
133
+ - `plugin/skills/self-check/run.ps1` (the underlying linter)
134
+ - `plugin/skills/skill-creator/SKILL.md` (the partner authoring skill)
135
+ - `docs/audits/v5.0.4-skill-creator-dogfood.md` (baseline retrofit run)
136
+ - <https://github.com/anthropics/skills/blob/main/skills/skill-creator/SKILL.md>