kushi-agents 5.0.4 → 5.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.
- package/README.md +78 -0
- package/bin/cli.mjs +201 -1
- package/package.json +2 -2
- package/plugin/agents/kushi.agent.md +6 -2
- package/plugin/instructions/hooks.instructions.md +84 -0
- package/plugin/instructions/living-wiki.instructions.md +88 -0
- package/plugin/instructions/log-format.instructions.md +78 -0
- package/plugin/instructions/otel.instructions.md +75 -0
- package/plugin/instructions/parallel-execution.instructions.md +81 -0
- package/plugin/instructions/schema-evolve.instructions.md +73 -0
- package/plugin/instructions/wiki-lint.instructions.md +110 -0
- package/plugin/skills/_shared/Append-StateLog.ps1 +73 -0
- package/plugin/skills/_shared/Emit-OtelSpan.ps1 +111 -0
- package/plugin/skills/_shared/Invoke-Hooks.ps1 +177 -0
- package/plugin/skills/_shared/Update-StateIndex.ps1 +47 -0
- package/plugin/skills/_shared/hook-templates/console-debug.ps1 +15 -0
- package/plugin/skills/_shared/hook-templates/teams-notify.ps1 +47 -0
- package/plugin/skills/ask-project/SKILL.md +30 -0
- package/plugin/skills/build-state/SKILL.md +18 -2
- package/plugin/skills/lint-state/.created-by-skill-creator +0 -0
- package/plugin/skills/lint-state/SKILL.md +98 -0
- package/plugin/skills/lint-state/evals/evals.json +34 -0
- package/plugin/skills/lint-state/lint.ps1 +218 -0
- package/plugin/skills/refresh-project/SKILL.md +8 -4
- package/plugin/skills/schema-evolve/.created-by-skill-creator +0 -0
- package/plugin/skills/schema-evolve/SKILL.md +106 -0
- package/plugin/skills/schema-evolve/evals/evals.json +37 -0
- package/plugin/skills/self-check/SKILL.md +12 -55
- package/plugin/skills/self-check/references/algorithm.md +55 -0
- package/plugin/skills/self-check/run.ps1 +225 -3
- package/plugin/skills/skill-checker/check-skill.ps1 +1 -1
- package/plugin/skills/teach/.created-by-skill-creator +0 -0
- package/plugin/skills/teach/SKILL.md +77 -0
- package/plugin/skills/teach/evals/evals.json +37 -0
- package/plugin/templates/state/answers.README.md +7 -0
- package/plugin/templates/state/hot.template.md +12 -0
- package/plugin/templates/state/review-queue.template.md +10 -0
- package/src/eval-runner.test.mjs +1 -1
- package/src/hooks-dispatcher.test.mjs +135 -0
- package/src/otel-emit.test.mjs +73 -0
- package/src/parallel-refresh.test.mjs +50 -0
- package/src/schema-evolve.test.mjs +78 -0
- package/src/teach.test.mjs +45 -0
|
@@ -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
|
|
@@ -91,6 +91,8 @@ After drain, proceed to Step 2 even if some markers remain — the orchestrator
|
|
|
91
91
|
|
|
92
92
|
### Step 2 — Per-source dispatch
|
|
93
93
|
|
|
94
|
+
> **v5.2.0 parallel dispatch**: By default, enabled sources run in parallel (up to `parallel.max_workers`, default 4). Pass `--sequential` to force serial execution. See `parallel-execution.instructions.md`.
|
|
95
|
+
|
|
94
96
|
For each enabled source (or just the requested one), call its `pull-<source>` skill with the effective window.
|
|
95
97
|
|
|
96
98
|
**OneNote pre-dispatch gate (kushi v3.10.0+, HARD):** Before invoking `pull-onenote`, run the browser-URL completeness check:
|
|
@@ -200,10 +202,12 @@ Re-running refresh with the same window is safe:
|
|
|
200
202
|
- "pull `<X>` since `<date>`"
|
|
201
203
|
- "refresh `<X>` last `<N>` days"
|
|
202
204
|
- "backfill `<X>` from `<from>` to `<to>`"
|
|
203
|
-
## References (v4.4.7)
|
|
204
|
-
|
|
205
|
-
- Name → ID resolution (any source) follows `..\..\instructions\fuzzy-disambiguation.instructions.md`.
|
|
206
|
-
- After each per-source pull, run the gate per `..\..\instructions\per-source-verification-gate.instructions.md` (retry once → FOLLOW-UPS.md on failure).
|
|
205
|
+
## References (v4.4.7)
|
|
206
|
+
|
|
207
|
+
- Name → ID resolution (any source) follows `..\..\instructions\fuzzy-disambiguation.instructions.md`.
|
|
208
|
+
- After each per-source pull, run the gate per `..\..\instructions\per-source-verification-gate.instructions.md` (retry once → FOLLOW-UPS.md on failure).
|
|
209
|
+
- Parallel dispatch follows `..\..\instructions\parallel-execution.instructions.md` (v5.2.0+).
|
|
210
|
+
- Post-pull hooks fired per source via `..\..\skills\_shared\Invoke-Hooks.ps1` (v5.2.0+).
|
|
207
211
|
|
|
208
212
|
|
|
209
213
|
## Issue Recovery
|
|
File without changes
|
|
@@ -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 'HCA' not 'Healthcare Accelerator' in summaries",
|
|
8
|
+
"expected_assertions": [
|
|
9
|
+
{ "type": "contains", "value": "CLAUDE.md" },
|
|
10
|
+
{ "type": "contains", "value": "Rule" },
|
|
11
|
+
{ "type": "contains", "value": "HCA" }
|
|
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
|
+
}
|
|
@@ -71,6 +71,11 @@ Checks split into **core** (always run) and **deep** (opt-in).
|
|
|
71
71
|
| D32.multi-host | Multi-host install integrity | validates `src/multi-host.mjs` exports + `bin/cli.mjs` flag handling, then performs a temp-dir dry-run install for BOTH supported hosts (Clawpilot + VS Code Chat) under a fake `$HOME` in `$env:TEMP`. Asserts SKILL.md + agent file + skills/ + prompts/ + skills-metadata.json with a kushi entry are present, then asserts a clean uninstall. NEVER touches the real `~/.copilot/` or `~/.vscode/`. See `multi-host-install.instructions.md`. |
|
|
72
72
|
| D33.evals | Skill evals framework integrity | every `plugin/skills/<name>/` (except `eval`) ships `evals/evals.json` with ≥ 2 cases and ≥ 1 assertion per case; the runner (`plugin/skills/eval/run-evals.ps1`) and schema (`plugin/skills/eval/evals.schema.json`) are present; `evals/baseline.json` exists (warn-only). Six sub-checks: `D33.evals-exist`, `D33.evals-schema`, `D33.evals-min-cases`, `D33.evals-have-assertions`, `D33.eval-runner-exists`, `D33.baseline-exists`. See `skill-evals.instructions.md`. |
|
|
73
73
|
| D34.creator-conformance | skill-creator + skill-checker harness integrity (v5.0.4+) | validates `scaffold.ps1` + `check-skill.ps1` ship and are parseable; every skill carrying the `.created-by-skill-creator` marker passes `check-skill --lint` clean; `check-skill --all --retrofit --dry-run` shows no non-additive gaps; the dogfood report at `docs/audits/v5.0.4-skill-creator-dogfood.md` is fresh (≤14 days). Five sub-checks: `D34.skill-creator-exists`, `D34.skill-checker-exists`, `D34.creator-output-conforms`, `D34.retrofit-clean`, `D34.dogfood-report-fresh`. See `skill-authoring.instructions.md`. |
|
|
74
|
+
| D35.log | State log (v5.1.0+) | `State/log.md` exists, `## [` headings match canonical format, timestamps reverse-chronological. Sub-checks: `D35.log-exists`, `D35.log-format`, `D35.log-monotonic`. |
|
|
75
|
+
| D36.contradictions | Contradictions (v5.1.0+) | `> [!warning] Contradicted` callouts well-formed, `_review-queue.md` fresh, `<!-- kushi:auto -->` fences balanced. Sub-checks: `D36.callout-syntax`, `D36.review-queue-fresh`, `D36.no-silent-overwrite`. |
|
|
76
|
+
| D37.hooks | Hooks system (v5.2.0+) | `hooks.instructions.md` exists, `Invoke-Hooks.ps1` helper exists, hook-templates/ has ≥2 templates, any `hooks-log.md` uses canonical heading format. Sub-checks: `D37.hooks-doctrine-exists`, `D37.hooks-helper-exists`, `D37.hooks-templates-exist`, `D37.hooks-log-format`. |
|
|
77
|
+
| D38.parallel | Parallel execution (v5.2.0+) | `parallel-execution.instructions.md` exists, `refresh-project/SKILL.md` references parallel dispatch, doctrine documents deterministic output ordering. Sub-checks: `D38.parallel-doctrine-exists`, `D38.refresh-supports-parallel`, `D38.deterministic-order`. |
|
|
78
|
+
| D39.otel | OpenTelemetry export (v5.2.0+) | `otel.instructions.md` exists, `Emit-OtelSpan.ps1` helper exists, helper short-circuits when `KUSHI_OTEL_ENDPOINT` is unset. Sub-checks: `D39.otel-doctrine-exists`, `D39.otel-helper-exists`, `D39.otel-noop-when-unset`. |
|
|
74
79
|
| **CSC weekly-layout checks (kushi v4.9.0)** | | gated on `Resolve-EngagementRoots` — no-ops on the kushi repo itself. |
|
|
75
80
|
| D11.csc | CSC entity coverage + depth | every `Evidence/<alias>/<source>/weekly/*-csc.md` has ≥ 1 entity heading; per-source minimum bullet count + populated-section count (meetings 25/6, email 8/4, teams 6/3, onenote 10/4, sharepoint 8/3, crm 12/5, ado 8/4). Coverage-Notes-only blocks (low-signal escape) are exempt. |
|
|
76
81
|
| D12.csc | CSC section order | every entity block's `###` section headings appear in the canonical order: Participants → Topics → Q&A → Who Said What → Decisions → Dates & Numbers → Action Items → Next Steps → Open Questions → Risks → Customer Asks → Artifacts → Coverage Notes. |
|
|
@@ -83,7 +88,7 @@ Checks split into **core** (always run) and **deep** (opt-in).
|
|
|
83
88
|
| D19.csc | CSC legacy write guard | WARN if `snapshot/` or `stream/` contains a file modified after the v4.9.0 release date (2026-05-26). Reads of legacy layouts remain supported. |
|
|
84
89
|
| **v5.0.0 cross-source / state / dashboard / tour checks** | | gated on `Resolve-EngagementRoots` — no-ops on the kushi repo itself. |
|
|
85
90
|
| D20.graph | Entity-graph well-formed | `Evidence/_graph/project-graph.json`: valid JSON, has `schema/project/generated_at/nodes/edges`, every edge `kind` is in the closed taxonomy (`references`/`decides`/`action-item-tracks`/`discusses`/`produced-by`/`follow-up-of`/`same-thread`/`participant-of`), every edge endpoint resolves to a node. See `entity-graph.instructions.md`. |
|
|
86
|
-
| D21.state | Karpathy State layout |
|
|
91
|
+
| D21.state | Karpathy State layout | `kushi_state_page: true` pages require sibling `index.md`, `log.md`, `CLAUDE.md`, `AGENTS.md`; pages live under closed-set category folders. See `karpathy-state-layout.instructions.md`. |
|
|
87
92
|
| D22.dashboard | Dashboard produced + substituted | when `project-graph.json` exists, `<project>/dashboard.html` MUST exist, MUST NOT still contain the literal `__GRAPH_JSON__` placeholder (would indicate embedder failure), and MUST reference Cytoscape. See `dashboard-artifact.instructions.md`. |
|
|
88
93
|
| D23.tour | Tour citations resolve | every `- **Cite:** [`<path>`]` row in `<project>/State/tour.md` MUST resolve to a real file under `<project>/Evidence/`. See `guided-tour.instructions.md`. |
|
|
89
94
|
|
|
@@ -105,59 +110,7 @@ Checks split into **core** (always run) and **deep** (opt-in).
|
|
|
105
110
|
|
|
106
111
|
## Algorithm (per check)
|
|
107
112
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
For each `plugin/skills/<dir>/SKILL.md`: parse YAML frontmatter (text between leading `---` lines). Required keys: `name`, `version`, `description`.
|
|
111
|
-
|
|
112
|
-
> ⚠️ self-check C1 — Skill `<dir>` is missing required frontmatter key `<key>`. Add it before the first `---` separator.
|
|
113
|
-
|
|
114
|
-
### C2 — Skill name matches directory
|
|
115
|
-
|
|
116
|
-
For each skill: `frontmatter.name` (case-insensitive) must equal directory name.
|
|
117
|
-
|
|
118
|
-
> ⚠️ self-check C2 — Directory `<dir>/` declares `name: "<name>"`. Rename one or the other so they match.
|
|
119
|
-
|
|
120
|
-
### C3 — Skills inventoried in agent
|
|
121
|
-
|
|
122
|
-
`plugin/agents/kushi.agent.md` must contain a backtick-wrapped reference to every skill directory name. Look in any markdown table column or any inline `\`<name>\`` token.
|
|
123
|
-
|
|
124
|
-
> ⚠️ self-check C3 — Skill `<dir>` is not listed in the kushi agent. Suggested row for the routing table:
|
|
125
|
-
>
|
|
126
|
-
> `| <verb> | <dir> | <one-line description from frontmatter> |`
|
|
127
|
-
|
|
128
|
-
### C4 — Prompts route to real skills
|
|
129
|
-
|
|
130
|
-
For each `plugin/prompts/*.prompt.md`: extract any reference of the form `` `<skill-name>` `` or `delegates to <skill-name>` (case-insensitive). Each must exist as a directory under `plugin/skills/`.
|
|
131
|
-
|
|
132
|
-
> ⚠️ self-check C4 — Prompt `<file>` references skill `<name>` which does not exist under `plugin/skills/`. Either create the skill or update the prompt.
|
|
133
|
-
|
|
134
|
-
### C5 — Instructions referenced
|
|
135
|
-
|
|
136
|
-
For each `plugin/instructions/*.instructions.md`: at least one SKILL.md OR `plugin/agents/kushi.agent.md` must reference it by filename (with or without path).
|
|
137
|
-
|
|
138
|
-
> ⚠️ self-check C5 — Instruction `<file>` is not referenced anywhere. Either delete it or add a "## References" line to the relevant skill(s).
|
|
139
|
-
|
|
140
|
-
### C6 — Cross-links resolve
|
|
141
|
-
|
|
142
|
-
For every markdown link with a relative target (`](./...)`, `](../...)`, or `](path/...)` not starting with `http`) inside `plugin/**/*.md`: target must exist on disk relative to the file containing it.
|
|
143
|
-
|
|
144
|
-
> ⚠️ self-check C6 — `<source-file>:<line>` links to `<target>` which does not exist. Update or remove the link.
|
|
145
|
-
|
|
146
|
-
### C7 — Verbs table matches prompts
|
|
147
|
-
|
|
148
|
-
`README.md` contains a "Verbs" / "Quick start" table. Every `plugin/prompts/<verb>.prompt.md` must have a row in that table; every row in the table must have a corresponding prompt file.
|
|
149
|
-
|
|
150
|
-
> ⚠️ self-check C7 — Verb `<v>` exists as `plugin/prompts/<v>.prompt.md` but is missing from README's verbs table. Suggested row:
|
|
151
|
-
>
|
|
152
|
-
> `| \`<v>\` | <default window> | <one-line description> |`
|
|
153
|
-
|
|
154
|
-
### C8 — Distribution layout matches docs
|
|
155
|
-
|
|
156
|
-
`docs/reference/where-things-live.md` contains an ASCII tree of `plugin/`. Every top-level subdirectory and notable file under `plugin/` must appear; every entry in the tree must exist on disk.
|
|
157
|
-
|
|
158
|
-
> ⚠️ self-check C8 — `plugin/<x>` exists but is missing from the docs/reference/where-things-live.md tree. Add an entry under section "1. This repo".
|
|
159
|
-
|
|
160
|
-
### D-checks: see `run.ps1` for the exact regexes.
|
|
113
|
+
> Load on trigger: see [`references/algorithm.md`](references/algorithm.md) for per-check pseudocode and finding templates (C1–C8, D-checks).
|
|
161
114
|
|
|
162
115
|
## Validation rules
|
|
163
116
|
|
|
@@ -202,4 +155,8 @@ This skill is not invoked by `@Kushi <verb>` — it's a meta-skill. Triggers:
|
|
|
202
155
|
## References
|
|
203
156
|
|
|
204
157
|
- `instructions/citation-ledger.instructions.md` (findings cite source files / line numbers)
|
|
205
|
-
- `instructions/side-by-side-config.instructions.md` (D6 enforces this rule for skills that read/write user config)
|
|
158
|
+
- `instructions/side-by-side-config.instructions.md` (D6 enforces this rule for skills that read/write user config)
|
|
159
|
+
- `instructions/hooks.instructions.md` (D37 validates hooks system)
|
|
160
|
+
- `instructions/parallel-execution.instructions.md` (D38 validates parallel dispatch)
|
|
161
|
+
- `instructions/otel.instructions.md` (D39 validates OTel export)
|
|
162
|
+
- `instructions/schema-evolve.instructions.md` (D39 validates schema evolution)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Algorithm (per check)
|
|
2
|
+
|
|
3
|
+
### C1 — Frontmatter completeness
|
|
4
|
+
|
|
5
|
+
For each `plugin/skills/<dir>/SKILL.md`: parse YAML frontmatter (text between leading `---` lines). Required keys: `name`, `version`, `description`.
|
|
6
|
+
|
|
7
|
+
> ⚠️ self-check C1 — Skill `<dir>` is missing required frontmatter key `<key>`. Add it before the first `---` separator.
|
|
8
|
+
|
|
9
|
+
### C2 — Skill name matches directory
|
|
10
|
+
|
|
11
|
+
For each skill: `frontmatter.name` (case-insensitive) must equal directory name.
|
|
12
|
+
|
|
13
|
+
> ⚠️ self-check C2 — Directory `<dir>/` declares `name: "<name>"`. Rename one or the other so they match.
|
|
14
|
+
|
|
15
|
+
### C3 — Skills inventoried in agent
|
|
16
|
+
|
|
17
|
+
`plugin/agents/kushi.agent.md` must contain a backtick-wrapped reference to every skill directory name. Look in any markdown table column or any inline `` `<name>` `` token.
|
|
18
|
+
|
|
19
|
+
> ⚠️ self-check C3 — Skill `<dir>` is not listed in the kushi agent. Suggested row for the routing table:
|
|
20
|
+
>
|
|
21
|
+
> `| <verb> | <dir> | <one-line description from frontmatter> |`
|
|
22
|
+
|
|
23
|
+
### C4 — Prompts route to real skills
|
|
24
|
+
|
|
25
|
+
For each `plugin/prompts/*.prompt.md`: extract any reference of the form `` `<skill-name>` `` or `delegates to <skill-name>` (case-insensitive). Each must exist as a directory under `plugin/skills/`.
|
|
26
|
+
|
|
27
|
+
> ⚠️ self-check C4 — Prompt `<file>` references skill `<name>` which does not exist under `plugin/skills/`. Either create the skill or update the prompt.
|
|
28
|
+
|
|
29
|
+
### C5 — Instructions referenced
|
|
30
|
+
|
|
31
|
+
For each `plugin/instructions/*.instructions.md`: at least one SKILL.md OR `plugin/agents/kushi.agent.md` must reference it by filename (with or without path).
|
|
32
|
+
|
|
33
|
+
> ⚠️ self-check C5 — Instruction `<file>` is not referenced anywhere. Either delete it or add a "## References" line to the relevant skill(s).
|
|
34
|
+
|
|
35
|
+
### C6 — Cross-links resolve
|
|
36
|
+
|
|
37
|
+
For every markdown link with a relative target (`](./...)`, `](../...)`, or `](path/...)` not starting with `http`) inside `plugin/**/*.md`: target must exist on disk relative to the file containing it.
|
|
38
|
+
|
|
39
|
+
> ⚠️ self-check C6 — `<source-file>:<line>` links to `<target>` which does not exist. Update or remove the link.
|
|
40
|
+
|
|
41
|
+
### C7 — Verbs table matches prompts
|
|
42
|
+
|
|
43
|
+
`README.md` contains a "Verbs" / "Quick start" table. Every `plugin/prompts/<verb>.prompt.md` must have a row in that table; every row in the table must have a corresponding prompt file.
|
|
44
|
+
|
|
45
|
+
> ⚠️ self-check C7 — Verb `<v>` exists as `plugin/prompts/<v>.prompt.md` but is missing from README's verbs table. Suggested row:
|
|
46
|
+
>
|
|
47
|
+
> `| \`<v>\` | <default window> | <one-line description> |`
|
|
48
|
+
|
|
49
|
+
### C8 — Distribution layout matches docs
|
|
50
|
+
|
|
51
|
+
`docs/reference/where-things-live.md` contains an ASCII tree of `plugin/`. Every top-level subdirectory and notable file under `plugin/` must appear; every entry in the tree must exist on disk.
|
|
52
|
+
|
|
53
|
+
> ⚠️ self-check C8 — `plugin/<x>` exists but is missing from the docs/reference/where-things-live.md tree. Add an entry under section "1. This repo".
|
|
54
|
+
|
|
55
|
+
### D-checks: see `run.ps1` for the exact regexes.
|