From lazy-obsidian
Recompte les compteurs par cluster (total, stable, draft, review, orphelines) et reecrit la section entre AUTO-START et AUTO-END dans 99-Meta/Audit.md. Refus d'ecrire si sentinelles malformees. Encoding UTF-8 systematique.
How this skill is triggered — by the user, by Claude, or both
Slash command
/lazy-obsidian:audit-updateThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Scanner `pages/{01|02|03}-*/**/*.md`, agreger des stats par cluster, reecrire UNIQUEMENT la section delimitee par les sentinelles `<!-- AUTO-START -->` et `<!-- AUTO-END -->` dans `99-Meta/Audit.md`.
Scanner pages/{01|02|03}-*/**/*.md, agreger des stats par cluster, reecrire UNIQUEMENT la section delimitee par les sentinelles <!-- AUTO-START --> et <!-- AUTO-END --> dans 99-Meta/Audit.md.
Set-Content -Encoding utf8 -NoNewline.Get-Content -Raw -Encoding utf8..tmp + Move-Item -Force.$statePath = "D:\tommyDossier\Documents\Obsidian Vault\99-Meta\lazy-obsidian-state.json"
$state = Get-Content $statePath -Raw -Encoding utf8 | ConvertFrom-Json
$lastRun = $state.last_run.'audit-update'
Si $lastRun correspond a aujourd'hui --> SKIP_IDEMPOTENT.
$pagesRoots = @("pages\01-Concepts", "pages\02-Technique", "pages\03-Gouvernance")
$allPages = @()
foreach ($root in $pagesRoots) {
$fullRoot = Join-Path $VaultRoot $root
if (Test-Path $fullRoot) {
$allPages += Get-ChildItem -Path $fullRoot -Recurse -Filter "*.md" -File
}
}
foreach ($f in $allPages) {
$lines = Get-Content $f.FullName -TotalCount 30 -Encoding utf8
$cluster = ($lines | Where-Object { $_ -match '^cluster:\s*(\S+)' } | ForEach-Object { $matches[1] }) | Select-Object -First 1
$statut = ($lines | Where-Object { $_ -match '^statut:\s*(\S+)' } | ForEach-Object { $matches[1] }) | Select-Object -First 1
if (-not $cluster) {
# Page malformee -- compter dans "malformees" mais ne pas affecter le cluster
$malformedCount++
continue
}
# Compter wikilinks sortants (regex \[\[.*?\]\])
$contentRaw = Get-Content $f.FullName -Raw -Encoding utf8
$wikilinksOut = ([regex]::Matches($contentRaw, '\[\[[^\]]+\]\]')).Count
# Wikilinks entrants : scan O(n) sur l'ensemble des pages curated pour le filename de cette page
# (Couteux mais OK jusqu'a ~500 pages)
$thisFilename = $f.BaseName
$wikilinksIn = 0
foreach ($other in $allPages) {
if ($other.FullName -eq $f.FullName) { continue }
$otherContent = Get-Content $other.FullName -Raw -Encoding utf8
if ($otherContent -match "\[\[[^\]]*$([regex]::Escape($thisFilename))[^\]]*\]\]") { $wikilinksIn++ }
}
$totalLinks = $wikilinksOut + $wikilinksIn
$isOrphan = $totalLinks -lt 3
# Aggreger par cluster
$stats[$cluster].total++
switch ($statut) {
"stable" { $stats[$cluster].stable++ }
"draft" { $stats[$cluster].draft++ }
"review" { $stats[$cluster].review++ }
}
if ($isOrphan) { $stats[$cluster].orphelines++ }
}
$auditMd = Join-Path $VaultRoot "99-Meta\Audit.md"
if (-not (Test-Path $auditMd)) {
# FAIL : log dans state.errors[]
return
}
$content = Get-Content $auditMd -Raw -Encoding utf8
$startMatches = [regex]::Matches($content, '<!-- AUTO-START -->')
$endMatches = [regex]::Matches($content, '<!-- AUTO-END -->')
if ($startMatches.Count -eq 0 -and $endMatches.Count -eq 0) {
# Injection auto en fin de fichier
$injection = @"
---
## Compteurs auto
> Section geree par /audit-update -- ne pas editer manuellement entre les sentinelles.
<!-- AUTO-START -->
<!-- AUTO-END -->
"@
Add-Content -Path $auditMd -Value $injection -Encoding utf8
# Re-lire le fichier
$content = Get-Content $auditMd -Raw -Encoding utf8
$startMatches = [regex]::Matches($content, '<!-- AUTO-START -->')
$endMatches = [regex]::Matches($content, '<!-- AUTO-END -->')
} elseif ($startMatches.Count -ne 1 -or $endMatches.Count -ne 1) {
# FAIL : sentinelles malformees, refus d'ecrire
$state.errors += "audit-update: sentinelles malformees ($($startMatches.Count) START, $($endMatches.Count) END)"
return
}
$startIdx = $startMatches[0].Index
$endIdx = $endMatches[0].Index
if ($startIdx -ge $endIdx) {
# FAIL : ordre inverse
$state.errors += "audit-update: AUTO-START apres AUTO-END (ordre invalide)"
return
}
<!-- AUTO-START -->
> Genere par `/audit-update` le <timestamp ISO 8601 UTC>. Ne pas editer manuellement.
| Cluster | Total | Stable | Draft | Review | Orphelines |
|---|---|---|---|---|---|
| `01-Red-Team` | <int> | <int> | <int> | <int> | <int> |
| `02-Blue-Team` | <int> | <int> | <int> | <int> | <int> |
| ... |
| `16-Sujets-Controverses` | <int> | <int> | <int> | <int> | <int> |
| **Total** | **<int>** | **<int>** | **<int>** | **<int>** | **<int>** |
<!-- AUTO-END -->
Inclure les 16 clusters meme si zero pages (afficher 0 partout).
$startTagLen = "<!-- AUTO-START -->".Length
$endTagLen = "<!-- AUTO-END -->".Length
$contentBefore = $content.Substring(0, $startIdx + $startTagLen)
$contentAfter = $content.Substring($endIdx)
$autoSection = "`n" + $tableMarkdown + "`n"
$newContent = $contentBefore + $autoSection + $contentAfter
$tmp = "$auditMd.tmp"
Set-Content -Path $tmp -Value $newContent -Encoding utf8 -NoNewline
Move-Item -Path $tmp -Destination $auditMd -Force
$state.last_run.'audit-update' = $now
$state | ConvertTo-Json -Depth 5 | Set-Content -Path $statePath -Encoding utf8 -NoNewline
$runEntry = @{
ts = $now
skill = "audit-update"
status = "OK" # ou FAIL, SKIP_IDEMPOTENT
total_pages = $totalPagesScanned
clusters = 16
malformed = $malformedCount
errors = $errorCount
} | ConvertTo-Json -Compress
Add-Content -Path "...\99-Meta\lazy-obsidian-runs.jsonl" -Value $runEntry -Encoding utf8
✅ audit-update -- 102 pages totales scannees sur 16 clusters.
- 95 stable / 5 draft / 2 review / 12 orphelines
- Section <!-- AUTO-START --> mise a jour dans 99-Meta/Audit.md
- 0 page malformee
| Cause | Action |
|---|---|
| Audit.md absent | FAIL, log erreur |
| Sentinelles manquantes (les 2) | Injection auto en fin de fichier |
| Une seule sentinelle | FAIL, refus d'ecrire |
| Ordre inverse START/END | FAIL, refus d'ecrire |
| > 1 occurrence d'une sentinelle | FAIL, refus d'ecrire |
| Page sans frontmatter | Comptee mais classee "malformee" (info, pas un fail) |
| Audit.md verrouille (Obsidian) | Retry 2s, si echec --> skip ce run |
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub tommyrequillard/lazy-obsidian --plugin lazy-obsidian