@paniolo/scan 0.0.1 → 0.0.2

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 (3) hide show
  1. package/README.md +8 -4
  2. package/dist/cli.js +62 -38
  3. package/package.json +17 -2
package/README.md CHANGED
@@ -28,14 +28,18 @@ npx @paniolo/scan ../songshare-effect
28
28
  ## Documentation
29
29
 
30
30
  - [Overview](./docs/overview.md) — problem, scope, supported harnesses
31
- - [Meta-harness model](./docs/meta-harness.md) — what “good” means, evaluation dimensions, calibration
31
+ - [Meta-harness model](./docs/meta-harness.md) — what “good” means, evaluation dimensions,
32
+ calibration
32
33
  - [Harness analysis](./docs/harness-analysis.md) — sharing vs unique guidance, per-harness scores
33
- - [Full-spectrum roadmap](./docs/full-spectrum-intelligence-layer.md) — implementation path for intelligence-layer scanning
34
+ - [Full-spectrum roadmap](./docs/full-spectrum-intelligence-layer.md) — implementation path for
35
+ intelligence-layer scanning
34
36
  - [Architecture](./docs/architecture.md) — pipeline, rule model, roadmap
35
37
  - [Rules catalog](./docs/rules-catalog.md) — built-in rules by dimension and status
36
38
  - [CI integration](./docs/ci.md) — exit codes and GitHub Actions
37
- - [npm publishing steps](./docs/npm-publishing-steps.md) — release runbook for publishing `npx @paniolo/scan`
38
- - [npm packaging and privacy](./docs/npm-packaging-privacy.md) — publishing `npx @paniolo/scan` without publishing `src/`
39
+ - [npm publishing steps](./docs/npm-publishing-steps.md) — release runbook for publishing
40
+ `npx @paniolo/scan`
41
+ - [npm packaging and privacy](./docs/npm-packaging-privacy.md) — publishing `npx @paniolo/scan`
42
+ without publishing `src/`
39
43
  - [Diagrams](./docs/diagrams.md) — Mermaid conventions for docs
40
44
 
41
45
  ## Development
package/dist/cli.js CHANGED
@@ -1,61 +1,85 @@
1
1
  #!/usr/bin/env node
2
- import ds from"node:path";function B(e){process.stdout.write(`${JSON.stringify(e,null,2)}
3
- `)}import{readFile as Ce}from"node:fs/promises";import Ae from"node:path";var E=["copilot","cursor","codex","antigravity","claude","gemini"],me=new Set(["shared","skills","agents"]);function ye(e,n){return e.harness!==void 0?n.includes(e.harness)?[e.harness]:[]:me.has(e.layer)?n:e.path.startsWith(".cursor/")?n.includes("cursor")?["cursor"]:[]:e.path.startsWith(".codex/")?n.includes("codex")?["codex"]:[]:e.path.startsWith(".agent/")?n.includes("antigravity")?["antigravity"]:[]:e.path==="AGENTS.md"||e.path.startsWith("docs/ai/")?n:[]}function ke(e,n){return n.length>=2?!0:me.has(e.layer)||e.path==="AGENTS.md"}import{access as cn}from"node:fs/promises";import un from"node:path";var b=75,J=300,$=["CLAUDE.md","GEMINI.md",".github/copilot-instructions.md"];function f(e,n){return e.files.some(t=>t.path===n)}function d(e,n){return e.fileContents.get(n)??null}function x(e,n){return e.files.find(s=>s.path===n)?.lines??null}function Se(e){return e.includes("AGENTS.md")||e.includes("docs/ai/rules.md")}function Q(e){return e.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1]??null}function L(e,n){return new RegExp(`^${n}:`,"m").test(e)}async function I(e,n){try{return await cn(un.join(e,n)),!0}catch{return!1}}function j(e,n){return e.files.filter(t=>t.path.startsWith(n)).map(t=>t.path)}function g(e){return e.files.some(n=>n.path.startsWith("skills/")&&n.path.endsWith("/SKILL.md"))}function O(e){return e.includes("available-skills.md")||e.includes("skills/*/SKILL.md")||e.includes("npm run qmd")||e.includes("skills/")&&e.includes("available-skills")}function _(e){return/agent routing/i.test(e)&&e.includes("|")&&/agents\/.*\.agent\.md/.test(e)}function m(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}var dn=50,pn={copilot:".github/copilot-instructions.md",claude:"CLAUDE.md",gemini:"GEMINI.md"};function F(e,n,t){let s=e.settings?.[n];if(m(s)&&Object.keys(s).some(a=>a.startsWith(t)))return!0;if(e.settingsRaw===null)return!1;let i=n.replaceAll(".",String.raw`\.`);return new RegExp(`"${i}"\\s*:\\s*\\{[^}]*"${t}[^"]*"`).test(e.settingsRaw)}function we(e){return e.files.some(n=>n.path.startsWith("agents/")&&n.path.endsWith(".agent.md"))}function W(e,n){let t=x(e,n);return t===null?!1:t<=dn}function A(e){return pn[e]??null}function be(e,n){let t=d(e,n);return t===null?!1:t.includes("AGENTS.md")||t.includes("docs/ai/rules.md")}function Z(e){return e.files.some(n=>n.path.startsWith(".agent/workflows/"))}function ee(e,n){if(!g(e))return!1;if(f(e,"docs/ai/available-skills.md"))return!0;let t=d(e,"AGENTS.md");if(t!==null&&O(t))return!0;let s=A(n);if(s===null)return!1;let i=d(e,s);return i!==null&&i.includes("skills/")}function xe(e){let n=d(e,"CLAUDE.md");return n!==null&&_(n)}var Re={sharedSkillsDiscoverable:"Shared skills discoverable",customAgents:"Custom agents",lifecycleHooks:"Lifecycle hooks",thinAdapter:"Thin adapter",workflows:"Workflows / playbooks"},U=["sharedSkillsDiscoverable","customAgents","lifecycleHooks","thinAdapter","workflows"],fn="Harness not detected";async function hn(e){try{return await Ce(Ae.join(e,".claude/settings.json"),"utf8")}catch{return null}}async function gn(e){try{return await Ce(Ae.join(e,".codex/hooks.json"),"utf8"),!0}catch{return!1}}function l(e,n){return{status:e,detail:n}}function mn(){function e(){return{copilot:l("na",""),cursor:l("na",""),codex:l("na",""),antigravity:l("na",""),claude:l("na",""),gemini:l("na","")}}return{sharedSkillsDiscoverable:e(),customAgents:e(),lifecycleHooks:e(),thinAdapter:e(),workflows:e()}}function yn(e,n){if(!g(n.context))return l("no","No skills/ tree");switch(e){case"copilot":case"cursor":return F(n.vscode,"chat.agentSkillsLocations","skills")?l("yes","skills/ wired in chat.agentSkillsLocations"):l("partial","skills/ present but not wired in VS Code settings");case"codex":return ee(n.context,"codex")?l("partial","skills/ with prose discovery via AGENTS.md or skill index"):l("partial","skills/ present; no documented discovery workflow");case"claude":case"gemini":return ee(n.context,e)?l("partial","Prose discovery via skill index or adapter pointers"):l("no","skills/ without index or adapter pointers");case"antigravity":return Z(n.context)?l("yes","skills/ with workflow playbooks"):l("partial","skills/ present without workflow playbooks");default:return l("no","Unknown harness")}}function kn(e,n){let t=we(n.context);switch(e){case"copilot":case"cursor":return F(n.vscode,"chat.agentFilesLocations","agents")&&t?l("yes","agents/ wired in chat.agentFilesLocations"):t?l("partial","agents/ present but not wired in VS Code settings"):l("no","No agents/ tree or VS Code agent locations");case"codex":return n.context.files.some(i=>i.path.startsWith(".codex/agents/"))&&t?l("yes",".codex/agents wrappers with shared agents/"):t?l("partial","agents/ present without .codex/agents wrappers"):l("no","No custom agents configured");case"claude":return xe(n.context)?l("yes","Agent routing table in CLAUDE.md"):t?l("partial","agents/ present without routing table in CLAUDE.md"):l("no","No agent routing or agents/ tree");case"gemini":{if(!t)return l("no","No agents/ tree");let s=A("gemini"),i=s===null?null:n.context.fileContents.get(s)??null;return i!==null&&i.includes("agents/")?l("partial","agents/ referenced from GEMINI.md"):l("partial","agents/ present; document routing in GEMINI.md")}case"antigravity":return l("na","Uses workflow playbooks instead of custom agents");default:return l("no","Unknown harness")}}function Sn(e,n){let t=n.vscode.hooksDirExists,s=n.vscode.settings?.["chat.useCustomAgentHooks"]===!0;switch(e){case"copilot":case"cursor":return t&&s?l("yes",".github/hooks/ with chat.useCustomAgentHooks"):t||s?l("partial","Hooks partially configured in VS Code workspace"):l("no","No lifecycle hooks configured");case"codex":return n.codexHooksExists?l("yes",".codex/hooks.json present"):t?l("partial","Shared .github/hooks/ only; no .codex/hooks.json"):l("no","No Codex hooks configured");case"claude":return n.claudeSettingsRaw!==null&&/"hooks"\s*:/.test(n.claudeSettingsRaw)?l("yes","Hooks in .claude/settings.json"):t?l("partial","Shared .github/hooks/ only; no .claude hooks"):l("no","No Claude hooks configured");case"gemini":return t&&s?l("partial","Shared VS Code hooks only; no Gemini-native hooks"):l("no","No lifecycle hooks for Gemini");case"antigravity":return l("na","No hook surface for Antigravity workflows");default:return l("no","Unknown harness")}}function wn(e,n){switch(e){case"copilot":{let t=A("copilot");return t===null||!n.context.files.some(s=>s.path===t)?l("no","Missing .github/copilot-instructions.md"):W(n.context,t)?l("yes","Copilot instructions within line budget"):l("partial","Copilot instructions exceed thin adapter budget")}case"cursor":{let t=n.context.files.some(i=>i.path.startsWith(".cursor/rules/")),s=F(n.vscode,"chat.agentSkillsLocations","skills")&&F(n.vscode,"chat.agentFilesLocations","agents");return t||s?l("yes","Cursor rules or VS Code agent settings configured"):l("partial","Cursor detected without rules or agent settings")}case"codex":return n.context.files.some(s=>s.path.startsWith(".codex/"))?n.context.files.some(s=>s.path==="AGENTS.md")?l("yes",".codex/ with shared AGENTS.md entry"):l("partial",".codex/ without root AGENTS.md"):l("no","No .codex/ config surface");case"claude":{let t=A("claude");if(t===null||!n.context.files.some(r=>r.path===t))return l("no","Missing CLAUDE.md");let s=W(n.context,t),i=be(n.context,t);return s&&i?l("yes","Thin CLAUDE.md referencing shared layers"):s?l("partial","CLAUDE.md thin but missing shared layer pointers"):l("partial","CLAUDE.md exceeds thin adapter budget")}case"gemini":{let t=A("gemini");return t===null||!n.context.files.some(s=>s.path===t)?l("no","Missing GEMINI.md"):W(n.context,t)?l("yes","Thin GEMINI.md adapter"):l("partial","GEMINI.md exceeds thin adapter budget")}case"antigravity":return l("partial","Workflows only; no classic root adapter");default:return l("no","Unknown harness")}}function bn(e,n){return e!=="antigravity"?l("na","Workflow playbooks are Antigravity-specific"):Z(n.context)?l("yes",".agent/workflows/ playbooks present"):l("no","No .agent/workflows/ playbooks")}function xn(e,n,t){switch(e){case"sharedSkillsDiscoverable":return yn(n,t);case"customAgents":return kn(n,t);case"lifecycleHooks":return Sn(n,t);case"thinAdapter":return wn(n,t);case"workflows":return bn(n,t);default:return l("no","Unknown capability")}}async function Cn(e,n){let t=E.filter(r=>e.harnesses.includes(r)),s={context:e,vscode:n,claudeSettingsRaw:await hn(e.repoRoot),codexHooksExists:await gn(e.repoRoot)},i=mn();for(let r of U){let o=i[r];for(let a of E){if(!e.harnesses.includes(a)){o[a]=l("na",fn);continue}o[a]=xn(r,a,s)}}return{capabilities:U,harnesses:t,matrix:i}}function ve(e){switch(e){case"yes":return"\u2713";case"partial":return"~";case"no":return"\u2717";case"na":return"\u2014"}}var He=Cn;var Ee=["error","warn","info"],An={error:"ERROR",warn:"WARN",info:"INFO"},Rn={layering:"Layering",sharing:"Sharing",discoverability:"Discoverability",harnessWiring:"Harness wiring",maintainability:"Maintainability",guardrails:"Guardrails"},vn=["layering","sharing","discoverability","harnessWiring","maintainability","guardrails"],Hn=["copilot","cursor","codex","antigravity","claude","gemini"];function En(e){let n=An[e.severity],t=e.file===null?"":` ${e.file}${e.line===null?"":`:${String(e.line)}`}`,s=e.harnesses.length===0?"":`
2
+ import Qi from"node:path";function fe(e){process.stdout.write(`${JSON.stringify(e,null,2)}
3
+ `)}import{readFile as Ke}from"node:fs/promises";import Ye from"node:path";var E=["copilot","cursor","codex","antigravity","claude","gemini"],We=new Set(["shared","skills","agents"]);function Ue(e,t){return e.harness!==void 0?t.includes(e.harness)?[e.harness]:[]:We.has(e.layer)?t:e.path.startsWith(".cursor/")?t.includes("cursor")?["cursor"]:[]:e.path.startsWith(".codex/")?t.includes("codex")?["codex"]:[]:e.path.startsWith(".agent/")?t.includes("antigravity")?["antigravity"]:[]:e.path==="AGENTS.md"||e.path.startsWith("docs/ai/")?t:[]}function qe(e,t){return t.length>=2?!0:We.has(e.layer)||e.path==="AGENTS.md"}import{access as fn}from"node:fs/promises";import gn from"node:path";var v=75,ge=300,Y=["CLAUDE.md","GEMINI.md",".github/copilot-instructions.md"];function y(e,t){return e.files.some(n=>n.path===t)}function f(e,t){return e.fileContents.get(t)??null}function I(e,t){return e.files.find(s=>s.path===t)?.lines??null}function Be(e){return e.includes("AGENTS.md")||e.includes("docs/ai/rules.md")}function me(e){return e.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1]??null}function G(e,t){return new RegExp(`^${t}:`,"m").test(e)}async function _(e,t){try{return await fn(gn.join(e,t)),!0}catch{return!1}}function X(e,t){return e.files.filter(n=>n.path.startsWith(t)).map(n=>n.path)}function S(e){return e.files.some(t=>t.path.startsWith("skills/")&&t.path.endsWith("/SKILL.md"))}function Q(e){return e.includes("available-skills.md")||e.includes("skills/*/SKILL.md")||e.includes("npm run qmd")||e.includes("skills/")&&e.includes("available-skills")}function Z(e){return/agent routing/i.test(e)&&e.includes("|")&&/agents\/.*\.agent\.md/.test(e)}function w(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}var mn=50,hn={copilot:".github/copilot-instructions.md",claude:"CLAUDE.md",gemini:"GEMINI.md"};function O(e,t,n){let s=e.settings?.[t];if(w(s)&&Object.keys(s).some(a=>a.startsWith(n)))return!0;if(e.settingsRaw===null)return!1;let i=t.replaceAll(".",String.raw`\.`);return new RegExp(`"${i}"\\s*:\\s*\\{[^}]*"${n}[^"]*"`).test(e.settingsRaw)}function Ve(e){return e.files.some(t=>t.path.startsWith("agents/")&&t.path.endsWith(".agent.md"))}function ee(e,t){let n=I(e,t);return n===null?!1:n<=mn}function N(e){return hn[e]??null}function ze(e,t){let n=f(e,t);return n===null?!1:n.includes("AGENTS.md")||n.includes("docs/ai/rules.md")}function he(e){return e.files.some(t=>t.path.startsWith(".agent/workflows/"))}function ye(e,t){if(!S(e))return!1;if(y(e,"docs/ai/available-skills.md"))return!0;let n=f(e,"AGENTS.md");if(n!==null&&Q(n))return!0;let s=N(t);if(s===null)return!1;let i=f(e,s);return i!==null&&i.includes("skills/")}function Je(e){let t=f(e,"CLAUDE.md");return t!==null&&Z(t)}var Xe={sharedSkillsDiscoverable:"Shared skills discoverable",customAgents:"Custom agents",lifecycleHooks:"Lifecycle hooks",thinAdapter:"Thin adapter",workflows:"Workflows / playbooks"},te=["sharedSkillsDiscoverable","customAgents","lifecycleHooks","thinAdapter","workflows"],yn="Harness not detected";async function kn(e){try{return await Ke(Ye.join(e,".claude/settings.json"),"utf8")}catch{return null}}async function Sn(e){try{return await Ke(Ye.join(e,".codex/hooks.json"),"utf8"),!0}catch{return!1}}function c(e,t){return{status:e,detail:t}}function wn(){function e(){return{copilot:c("na",""),cursor:c("na",""),codex:c("na",""),antigravity:c("na",""),claude:c("na",""),gemini:c("na","")}}return{sharedSkillsDiscoverable:e(),customAgents:e(),lifecycleHooks:e(),thinAdapter:e(),workflows:e()}}function bn(e,t){if(!S(t.context))return c("no","No skills/ tree");switch(e){case"copilot":case"cursor":return O(t.vscode,"chat.agentSkillsLocations","skills")?c("yes","skills/ wired in chat.agentSkillsLocations"):c("partial","skills/ present but not wired in VS Code settings");case"codex":return ye(t.context,"codex")?c("partial","skills/ with prose discovery via AGENTS.md or skill index"):c("partial","skills/ present; no documented discovery workflow");case"claude":case"gemini":return ye(t.context,e)?c("partial","Prose discovery via skill index or adapter pointers"):c("no","skills/ without index or adapter pointers");case"antigravity":return he(t.context)?c("yes","skills/ with workflow playbooks"):c("partial","skills/ present without workflow playbooks");default:return c("no","Unknown harness")}}function Cn(e,t){let n=Ve(t.context);switch(e){case"copilot":case"cursor":return O(t.vscode,"chat.agentFilesLocations","agents")&&n?c("yes","agents/ wired in chat.agentFilesLocations"):n?c("partial","agents/ present but not wired in VS Code settings"):c("no","No agents/ tree or VS Code agent locations");case"codex":return t.context.files.some(i=>i.path.startsWith(".codex/agents/"))&&n?c("yes",".codex/agents wrappers with shared agents/"):n?c("partial","agents/ present without .codex/agents wrappers"):c("no","No custom agents configured");case"claude":return Je(t.context)?c("yes","Agent routing table in CLAUDE.md"):n?c("partial","agents/ present without routing table in CLAUDE.md"):c("no","No agent routing or agents/ tree");case"gemini":{if(!n)return c("no","No agents/ tree");let s=N("gemini"),i=s===null?null:t.context.fileContents.get(s)??null;return i!==null&&i.includes("agents/")?c("partial","agents/ referenced from GEMINI.md"):c("partial","agents/ present; document routing in GEMINI.md")}case"antigravity":return c("na","Uses workflow playbooks instead of custom agents");default:return c("no","Unknown harness")}}function xn(e,t){let n=t.vscode.hooksDirExists,s=t.vscode.settings?.["chat.useCustomAgentHooks"]===!0;switch(e){case"copilot":case"cursor":return n&&s?c("yes",".github/hooks/ with chat.useCustomAgentHooks"):n||s?c("partial","Hooks partially configured in VS Code workspace"):c("no","No lifecycle hooks configured");case"codex":return t.codexHooksExists?c("yes",".codex/hooks.json present"):n?c("partial","Shared .github/hooks/ only; no .codex/hooks.json"):c("no","No Codex hooks configured");case"claude":return t.claudeSettingsRaw!==null&&/"hooks"\s*:/.test(t.claudeSettingsRaw)?c("yes","Hooks in .claude/settings.json"):n?c("partial","Shared .github/hooks/ only; no .claude hooks"):c("no","No Claude hooks configured");case"gemini":return n&&s?c("partial","Shared VS Code hooks only; no Gemini-native hooks"):c("no","No lifecycle hooks for Gemini");case"antigravity":return c("na","No hook surface for Antigravity workflows");default:return c("no","Unknown harness")}}function Ln(e,t){switch(e){case"copilot":{let n=N("copilot");return n===null||!t.context.files.some(s=>s.path===n)?c("no","Missing .github/copilot-instructions.md"):ee(t.context,n)?c("yes","Copilot instructions within line budget"):c("partial","Copilot instructions exceed thin adapter budget")}case"cursor":{let n=t.context.files.some(i=>i.path.startsWith(".cursor/rules/")),s=O(t.vscode,"chat.agentSkillsLocations","skills")&&O(t.vscode,"chat.agentFilesLocations","agents");return n||s?c("yes","Cursor rules or VS Code agent settings configured"):c("partial","Cursor detected without rules or agent settings")}case"codex":return t.context.files.some(s=>s.path.startsWith(".codex/"))?t.context.files.some(s=>s.path==="AGENTS.md")?c("yes",".codex/ with shared AGENTS.md entry"):c("partial",".codex/ without root AGENTS.md"):c("no","No .codex/ config surface");case"claude":{let n=N("claude");if(n===null||!t.context.files.some(o=>o.path===n))return c("no","Missing CLAUDE.md");let s=ee(t.context,n),i=ze(t.context,n);return s&&i?c("yes","Thin CLAUDE.md referencing shared layers"):s?c("partial","CLAUDE.md thin but missing shared layer pointers"):c("partial","CLAUDE.md exceeds thin adapter budget")}case"gemini":{let n=N("gemini");return n===null||!t.context.files.some(s=>s.path===n)?c("no","Missing GEMINI.md"):ee(t.context,n)?c("yes","Thin GEMINI.md adapter"):c("partial","GEMINI.md exceeds thin adapter budget")}case"antigravity":return c("partial","Workflows only; no classic root adapter");default:return c("no","Unknown harness")}}function An(e,t){return e!=="antigravity"?c("na","Workflow playbooks are Antigravity-specific"):he(t.context)?c("yes",".agent/workflows/ playbooks present"):c("no","No .agent/workflows/ playbooks")}function Rn(e,t,n){switch(e){case"sharedSkillsDiscoverable":return bn(t,n);case"customAgents":return Cn(t,n);case"lifecycleHooks":return xn(t,n);case"thinAdapter":return Ln(t,n);case"workflows":return An(t,n);default:return c("no","Unknown capability")}}async function En(e,t){let n=E.filter(o=>e.harnesses.includes(o)),s={context:e,vscode:t,claudeSettingsRaw:await kn(e.repoRoot),codexHooksExists:await Sn(e.repoRoot)},i=wn();for(let o of te){let r=i[o];for(let a of E){if(!e.harnesses.includes(a)){r[a]=c("na",yn);continue}r[a]=Rn(o,a,s)}}return{capabilities:te,harnesses:n,matrix:i}}function Qe(e){switch(e){case"yes":return"\u2713";case"partial":return"~";case"no":return"\u2717";case"na":return"\u2014"}}var Ze=En;var et=["error","warn","info"],vn={error:"ERROR",warn:"WARN",info:"INFO"},In={layering:"Layering",sharing:"Sharing",discoverability:"Discoverability",harnessWiring:"Harness wiring",maintainability:"Maintainability",guardrails:"Guardrails"},Fn=["layering","sharing","discoverability","harnessWiring","maintainability","guardrails"],Hn=["copilot","cursor","codex","antigravity","claude","gemini"];function Pn(e){let t=vn[e.severity],n=e.file===null?"":` ${e.file}${e.line===null?"":`:${String(e.line)}`}`,s=e.harnesses.length===0?"":`
4
4
  Harnesses: ${e.harnesses.join(", ")}`,i=e.hint===void 0?"":`
5
- Hint: ${e.hint}`;return`[${n}] ${e.ruleId}
6
- ${e.message}${t?`
7
- ${t}`:""}${s}${i}`}function Ln(e){process.stdout.write(`## Guidance sharing
5
+ Hint: ${e.hint}`;return`[${t}] ${e.ruleId}
6
+ ${e.message}${n?`
7
+ ${n}`:""}${s}${i}`}function Tn(e){process.stdout.write(`## Guidance sharing
8
8
 
9
9
  `),process.stdout.write(`Shared (canonical / multi-harness): ${String(e.shared.lines)} lines (${String(e.shared.percentLines)}%) across ${String(e.shared.files)} file(s)
10
10
  `),process.stdout.write(`Harness-specific: ${String(e.unique.totals.lines)} lines (${String(e.unique.totals.percentLines)}%) across ${String(e.unique.totals.files)} file(s)
11
- `);let n=Hn.filter(t=>e.unique.byHarness[t].lines>0);if(n.length>0){process.stdout.write(`
11
+ `);let t=Hn.filter(n=>e.unique.byHarness[n].lines>0);if(t.length>0){process.stdout.write(`
12
12
  Per-harness unique guidance:
13
- `);for(let t of n){let s=e.unique.byHarness[t];process.stdout.write(` ${t}: ${String(s.lines)} lines (${String(s.files)} file(s))
13
+ `);for(let n of t){let s=e.unique.byHarness[n];process.stdout.write(` ${n}: ${String(s.lines)} lines (${String(s.files)} file(s))
14
14
  `)}}process.stdout.write(`
15
- `)}function In(e){process.stdout.write(`## Meta-harness dimensions
15
+ `)}function Nn(e){process.stdout.write(`## Meta-harness dimensions
16
16
 
17
- `);for(let n of vn){let t=e.dimensions[n],s=Rn[n],i=t.applicableRules===0?" (no rules yet)":` (${String(t.passedRules)}/${String(t.applicableRules)} rules)`;process.stdout.write(`${s}: ${String(t.score)}/100 (${t.grade})${i}
17
+ `);for(let t of Fn){let n=e.dimensions[t],s=In[t],i=n.applicableRules===0?" (no rules yet)":` (${String(n.passedRules)}/${String(n.applicableRules)} rules)`;process.stdout.write(`${s}: ${String(n.score)}/100 (${n.grade})${i}
18
18
  `)}process.stdout.write(`
19
- `)}function Fn(e){if(process.stdout.write(`## Harness gap matrix
19
+ `)}function Dn(e){e!==void 0&&(process.stdout.write(`## Intelligence layer
20
+
21
+ `),process.stdout.write(`Surfaces: ${String(e.surfaces.length)} detected
22
+ `),process.stdout.write(`Enforcement: ${String(e.enforcement.scripts.length)} script(s), ${String(e.enforcement.configs.length)} config(s), ${String(e.enforcement.ciWorkflows.length)} CI workflow(s)
23
+ `),process.stdout.write(`Validation: ${String(e.validation.scripts.length)} script(s), ${String(e.validation.configs.length)} config(s), ${String(e.validation.ciWorkflows.length)} CI workflow(s)
24
+ `),process.stdout.write(`Memory docs: ${String(e.memory.docs.length)}; indexes: ${String(e.memory.indexes.length)}
25
+ `),process.stdout.write(`Local context: ${String(e.localContext.conventions.length)} convention doc(s), ${String(e.localContext.nestedAgentsFiles.length)} nested AGENTS.md
26
+ `),process.stdout.write(`Correction loop: ${String(e.correctionLoop.scripts.length)} script(s), ${String(e.correctionLoop.prTemplates.length)} PR template(s), ${String(e.correctionLoop.ciWorkflows.length)} CI workflow(s)
27
+ `),process.stdout.write(`Correction checks: ${String(e.correctionLoop.maintenanceScripts.length)} maintenance script(s), ${String(e.correctionLoop.prTemplateChecks.length)} PR reminder(s), ${String(e.correctionLoop.guidanceCiWorkflows.length)} CI guidance gate(s)
28
+
29
+ `))}function Mn(e){if(e===void 0)return;let t=e.harnesses.filter(n=>n.detected);if(t.length!==0){process.stdout.write(`## Context budget
30
+
31
+ `);for(let n of t){process.stdout.write(`${n.harness}: ${String(n.totalLines)} always-loaded lines, ~${String(n.estimatedTokens)} tokens (${n.status})
32
+ `);for(let s of n.files.slice(0,3))process.stdout.write(` ${s.path}: ${String(s.lines)} lines
33
+ `)}process.stdout.write(`
34
+ `)}}function $n(e){if(process.stdout.write(`## Harness gap matrix
20
35
 
21
36
  `),e.harnesses.length===0){process.stdout.write(`No harnesses detected \u2014 add tool adapters to compare capabilities.
22
37
 
23
- `);return}let n=28,t=Math.max(...e.harnesses.map(i=>i.length),6)+2,s=`${"Capability".padEnd(n)}${e.harnesses.map(i=>i.padEnd(t)).join("")}`;process.stdout.write(`${s}
24
- `);for(let i of U){let r=Re[i].padEnd(n),o=e.harnesses.map(a=>{let{status:c}=e.matrix[i][a];return ve(c).padEnd(t)}).join("");process.stdout.write(`${r}${o}
38
+ `);return}let t=28,n=Math.max(...e.harnesses.map(i=>i.length),6)+2,s=`${"Capability".padEnd(t)}${e.harnesses.map(i=>i.padEnd(n)).join("")}`;process.stdout.write(`${s}
39
+ `);for(let i of te){let o=Xe[i].padEnd(t),r=e.harnesses.map(a=>{let{status:l}=e.matrix[i][a];return Qe(l).padEnd(n)}).join("");process.stdout.write(`${o}${r}
25
40
  `)}process.stdout.write(`
26
41
  Legend: \u2713 yes ~ partial \u2717 no \u2014 not applicable
27
42
 
28
- `)}function Dn(e){process.stdout.write(`## Harness optimization
43
+ `)}function jn(e){process.stdout.write(`## Harness optimization
29
44
 
30
- `);let n=e.filter(s=>s.detected);if(n.length===0){process.stdout.write(`No harnesses detected \u2014 add tool adapters to evaluate optimization.
45
+ `);let t=e.filter(s=>s.detected);if(t.length===0){process.stdout.write(`No harnesses detected \u2014 add tool adapters to evaluate optimization.
31
46
 
32
- `);return}for(let s of n){let i=s.score===null?"n/a":`${String(s.score)}/100 (${s.grade})`;process.stdout.write(`${s.harness}: ${i}
47
+ `);return}for(let s of t){let i=s.score===null?"n/a":`${String(s.score)}/100 (${s.grade})`;process.stdout.write(`${s.harness}: ${i}
33
48
  `),s.loadProfile!==null&&process.stdout.write(` Load profile: ${String(s.loadProfile.sharedLines)} shared + ${String(s.loadProfile.uniqueLines)} unique lines (${String(s.loadProfile.sharedPercent)}% shared)
34
- `);let r=s.checks.filter(a=>!a.passed),o=s.checks.length-r.length;process.stdout.write(` Checks: ${String(o)}/${String(s.checks.length)} passed`),s.findingsCount.error+s.findingsCount.warn>0&&process.stdout.write(`; findings: ${String(s.findingsCount.error)} error, ${String(s.findingsCount.warn)} warn`),process.stdout.write(`
35
- `);for(let a of r.slice(0,5))process.stdout.write(` \u2717 ${a.label}
36
- `);r.length>5&&process.stdout.write(` \u2026 and ${String(r.length-5)} more
49
+ `);let o=s.checks.filter(a=>!a.passed),r=s.checks.length-o.length;process.stdout.write(` Checks: ${String(r)}/${String(s.checks.length)} passed`),s.findingsCount.error+s.findingsCount.warn>0&&process.stdout.write(`; findings: ${String(s.findingsCount.error)} error, ${String(s.findingsCount.warn)} warn`),process.stdout.write(`
50
+ `);for(let a of o.slice(0,5))process.stdout.write(` \u2717 ${a.label}
51
+ `);o.length>5&&process.stdout.write(` \u2026 and ${String(o.length-5)} more
37
52
  `),process.stdout.write(`
38
- `)}let t=e.filter(s=>!s.detected);t.length>0&&process.stdout.write(`Not detected: ${t.map(s=>s.harness).join(", ")}
53
+ `)}let n=e.filter(s=>!s.detected);n.length>0&&process.stdout.write(`Not detected: ${n.map(s=>s.harness).join(", ")}
39
54
 
40
- `)}function ne(e){if(process.stdout.write(`paniolo-scan \u2014 AI harness diagnostic
55
+ `)}function ke(e){if(process.stdout.write(`paniolo-scan \u2014 AI harness diagnostic
41
56
 
42
57
  `),process.stdout.write(`Root: ${e.root}
43
58
  `),process.stdout.write(`Harnesses: ${e.harnesses.length===0?"(none detected)":e.harnesses.join(", ")}
44
59
  `),process.stdout.write(`Guidance files: ${String(e.inventory.fileCount)}
45
60
  `),process.stdout.write(`Findings: ${String(e.summary.error)} error(s), ${String(e.summary.warn)} warn(s), ${String(e.summary.info)} info
46
61
 
47
- `),Ln(e.sharing),In(e.metaHarness),Fn(e.harnessGapMatrix),Dn(e.harnessOptimization),process.stdout.write(`## Findings
62
+ `),Tn(e.sharing),Dn(e.intelligenceLayer),Mn(e.contextBudget),Nn(e.metaHarness),$n(e.harnessGapMatrix),jn(e.harnessOptimization),process.stdout.write(`## Findings
48
63
 
49
64
  `),e.findings.length===0){process.stdout.write(`No findings.
50
- `);return}let n=[...e.findings].sort((t,s)=>{let i=Ee.indexOf(t.severity)-Ee.indexOf(s.severity);return i!==0?i:t.ruleId.localeCompare(s.ruleId)});for(let t of n)process.stdout.write(`${En(t)}
65
+ `);return}let t=[...e.findings].sort((n,s)=>{let i=et.indexOf(n.severity)-et.indexOf(s.severity);return i!==0?i:n.ruleId.localeCompare(s.ruleId)});for(let n of t)process.stdout.write(`${Pn(n)}
51
66
 
52
- `)}import ls from"node:path";function Nn(){return{copilot:{files:0,lines:0,bytes:0},cursor:{files:0,lines:0,bytes:0},codex:{files:0,lines:0,bytes:0},antigravity:{files:0,lines:0,bytes:0},claude:{files:0,lines:0,bytes:0},gemini:{files:0,lines:0,bytes:0}}}function q(e,n){return n===0?0:Math.round(e/n*1e3)/10}function te(e,n){let t={files:0,lines:0,bytes:0},s=Nn(),i={files:0,lines:0,bytes:0},r={files:0,lines:0,bytes:0},o=[];for(let a of e){let c=ye(a,n);if(c.length===0)continue;r.files+=1,r.lines+=a.lines,r.bytes+=a.bytes;let u=ke(a,c),k=u?"shared":"unique";if(o.push({path:a.path,lines:a.lines,scope:k,harnesses:c}),u)t.files+=1,t.lines+=a.lines,t.bytes+=a.bytes;else{let[p]=c;p!==void 0&&(s[p].files+=1,s[p].lines+=a.lines,s[p].bytes+=a.bytes),i.files+=1,i.lines+=a.lines,i.bytes+=a.bytes}}return{totals:r,shared:{...t,percentLines:q(t.lines,r.lines)},unique:{byHarness:s,totals:{...i,percentLines:q(i.lines,r.lines)}},files:o}}function Le(e,n){let t={sharedLines:0,uniqueLines:0,totalLines:0,sharedPercent:0,uniquePercent:0},s={copilot:{...t},cursor:{...t},codex:{...t},antigravity:{...t},claude:{...t},gemini:{...t}};for(let i of n){let r=e.unique.byHarness[i].lines,o=e.shared.lines,a=o+r;s[i]={sharedLines:o,uniqueLines:r,totalLines:a,sharedPercent:q(o,a),uniquePercent:q(r,a)}}return s}import{access as Tn,readFile as Gn}from"node:fs/promises";import h from"node:path";function D(e){try{let n=JSON.parse(e);if(m(n))return n}catch{}return Pn(e)}function Pn(e){let n={},t=/"([^"]+)"\s*:\s*(true|false|null|\d+(?:\.\d+)?|"[^"]*")/g;for(let[,s,i]of e.matchAll(t))s===void 0||i===void 0||(n[s]=Mn(i));return Object.keys(n).length===0?null:n}function Mn(e){if(e==="true")return!0;if(e==="false")return!1;if(e==="null")return null;if(e.startsWith('"')&&e.endsWith('"'))return e.slice(1,-1);let n=Number(e);return Number.isNaN(n)?e:n}async function y(e){try{return await Tn(e),!0}catch{return!1}}async function $n(e){try{let n=await Gn(e,"utf8");return D(n)}catch{return null}}function jn(e){return e===null?!1:e["chat.agentSkillsLocations"]!==void 0||e["chat.agentFilesLocations"]!==void 0||e["chat.useCustomAgentHooks"]===!0}async function se(e){let n=new Set;await y(h.join(e,".github/copilot-instructions.md"))&&n.add("copilot"),await y(h.join(e,".github/hooks"))&&n.add("copilot");let t=await $n(h.join(e,".vscode/settings.json"));return jn(t)&&(n.add("cursor"),n.add("copilot")),await y(h.join(e,".cursor"))&&n.add("cursor"),await y(h.join(e,".codex/config.toml"))&&n.add("codex"),await y(h.join(e,".codex/hooks.json"))&&n.add("codex"),await y(h.join(e,".codex/agents"))&&n.add("codex"),await y(h.join(e,".agent/workflows"))&&n.add("antigravity"),await y(h.join(e,".agent/README.md"))&&n.add("antigravity"),await y(h.join(e,"CLAUDE.md"))&&n.add("claude"),await y(h.join(e,".claude/settings.json"))&&n.add("claude"),await y(h.join(e,"GEMINI.md"))&&n.add("gemini"),[...n].sort()}import{access as On}from"node:fs/promises";import N from"node:path";var _n=[{min:85,grade:"excellent"},{min:70,grade:"good"},{min:50,grade:"fair"},{min:0,grade:"poor"}],ie={error:12,warn:5,info:1},Wn=40;async function P(e){try{return await On(e),!0}catch{return!1}}function S(e,n){return e.files.some(t=>t.path===n)}function Fe(e,n){return e.fileContents.get(n)??null}function Un(e,n){return e.files.find(s=>s.path===n)?.lines??null}function qn(e){return e.includes("AGENTS.md")||e.includes("docs/ai/rules.md")}function R(e){return e.files.some(n=>n.path.startsWith("skills/"))}function V(e){return e.files.some(n=>n.path.startsWith("agents/"))}function Ie(e,n){return e.settings?.[n]===!0}function M(e,n,t){let s=e.settings?.[n];if(m(s)&&Object.keys(s).some(a=>a.startsWith(t)))return!0;if(e.settingsRaw===null)return!1;let i=n.replaceAll(".",String.raw`\.`);return new RegExp(`"${i}"\\s*:\\s*\\{[^}]*"${t}[^"]*"`).test(e.settingsRaw)}function re(e,n){let t=Un(e,n);return t===null?!1:t<=b}function oe(e,n){let t=Fe(e,n);return t===null?!1:qn(t)}function Vn(e){let n=Fe(e,".agent/README.md");return n===null?!1:/\.agent\/workflows\/|workflows\//i.test(n)&&/(workflow|playbook|slash command|mission|turbo)/i.test(n)}var zn={copilot:[{id:"copilot-instructions",label:"Copilot instructions file present",weight:15,run:({context:e})=>S(e,".github/copilot-instructions.md")},{id:"copilot-references-shared",label:"Copilot instructions reference shared layers",weight:15,run:({context:e})=>oe(e,".github/copilot-instructions.md")},{id:"copilot-adapter-thin",label:"Copilot instructions stay thin",weight:10,run:({context:e})=>re(e,".github/copilot-instructions.md")},{id:"copilot-skills-location",label:"chat.agentSkillsLocations points at skills/",weight:15,run:({vscode:e})=>M(e,"chat.agentSkillsLocations","skills")},{id:"copilot-agents-location",label:"chat.agentFilesLocations points at agents/",weight:15,run:({vscode:e})=>M(e,"chat.agentFilesLocations","agents")},{id:"copilot-hooks-enabled",label:"Custom agent hooks enabled when .github/hooks/ exists",weight:15,run:({vscode:e})=>!e.hooksDirExists||Ie(e,"chat.useCustomAgentHooks")},{id:"copilot-shared-skills",label:"Shared skills/ tree present",weight:8,run:({context:e})=>R(e)},{id:"copilot-shared-agents",label:"Shared agents/ tree present",weight:7,run:({context:e})=>V(e)}],cursor:[{id:"cursor-rules-or-settings",label:"Cursor rules or VS Code agent settings present",weight:20,run:({context:e,vscode:n})=>e.files.some(t=>t.path.startsWith(".cursor/rules/"))||M(n,"chat.agentSkillsLocations","skills")},{id:"cursor-skills-location",label:"chat.agentSkillsLocations points at skills/",weight:20,run:({vscode:e})=>M(e,"chat.agentSkillsLocations","skills")},{id:"cursor-agents-location",label:"chat.agentFilesLocations points at agents/",weight:20,run:({vscode:e})=>M(e,"chat.agentFilesLocations","agents")},{id:"cursor-hooks-enabled",label:"Custom agent hooks enabled when .github/hooks/ exists",weight:15,run:({vscode:e})=>!e.hooksDirExists||Ie(e,"chat.useCustomAgentHooks")},{id:"cursor-shared-skills",label:"Shared skills/ tree present",weight:13,run:({context:e})=>R(e)},{id:"cursor-shared-agents",label:"Shared agents/ tree present",weight:12,run:({context:e})=>V(e)}],codex:[{id:"codex-config-surface",label:"Codex config surface present (.codex/ or nested AGENTS.md)",weight:25,run:async({context:e})=>e.files.some(n=>n.path.startsWith(".codex/"))?!0:await P(N.join(e.repoRoot,"api/AGENTS.md"))||await P(N.join(e.repoRoot,"react/AGENTS.md"))||await P(N.join(e.repoRoot,"shared/AGENTS.md"))},{id:"codex-hooks",label:"Codex hooks.json present",weight:20,run:({context:e})=>P(N.join(e.repoRoot,".codex/hooks.json"))},{id:"codex-agents-wrappers",label:"Codex agent wrappers present",weight:20,run:({context:e})=>P(N.join(e.repoRoot,".codex/agents"))},{id:"codex-shared-entry",label:"Root AGENTS.md present",weight:20,run:({context:e})=>S(e,"AGENTS.md")},{id:"codex-shared-skills",label:"Shared skills/ tree present",weight:8,run:({context:e})=>R(e)},{id:"codex-shared-rules",label:"Canonical rules doc present",weight:7,run:({context:e})=>S(e,"docs/ai/rules.md")}],antigravity:[{id:"antigravity-workflows",label:"Workflow playbooks present (.agent/workflows/)",weight:35,run:({context:e})=>e.files.some(n=>n.path.startsWith(".agent/workflows/"))},{id:"antigravity-readme",label:".agent/README.md documents workflow layout",weight:15,run:({context:e})=>Vn(e)},{id:"antigravity-shared-entry",label:"Root AGENTS.md present",weight:20,run:({context:e})=>S(e,"AGENTS.md")},{id:"antigravity-shared-skills",label:"Shared skills/ tree present",weight:15,run:({context:e})=>R(e)},{id:"antigravity-ai-system-doc",label:"AI system layout doc present",weight:15,run:({context:e})=>S(e,"docs/ai/ai-system.md")}],claude:[{id:"claude-adapter",label:"CLAUDE.md adapter present",weight:25,run:({context:e})=>S(e,"CLAUDE.md")},{id:"claude-references-shared",label:"CLAUDE.md references shared layers",weight:25,run:({context:e})=>oe(e,"CLAUDE.md")},{id:"claude-adapter-thin",label:"CLAUDE.md stays thin",weight:20,run:({context:e})=>re(e,"CLAUDE.md")},{id:"claude-shared-skills",label:"Shared skills/ tree present",weight:15,run:({context:e})=>R(e)},{id:"claude-skills-index",label:"Skill index doc for prose discovery",weight:8,run:({context:e})=>S(e,"docs/ai/available-skills.md")},{id:"claude-shared-agents",label:"Shared agents/ tree present",weight:7,run:({context:e})=>V(e)}],gemini:[{id:"gemini-adapter",label:"GEMINI.md adapter present",weight:30,run:({context:e})=>S(e,"GEMINI.md")},{id:"gemini-references-shared",label:"GEMINI.md references shared layers",weight:25,run:({context:e})=>oe(e,"GEMINI.md")},{id:"gemini-adapter-thin",label:"GEMINI.md stays thin",weight:20,run:({context:e})=>re(e,"GEMINI.md")},{id:"gemini-shared-skills",label:"Shared skills/ tree present",weight:15,run:({context:e})=>R(e)},{id:"gemini-shared-agents",label:"Shared agents/ tree present",weight:10,run:({context:e})=>V(e)}]};function Kn(e){for(let{min:n,grade:t}of _n)if(e>=n)return t;return"poor"}function Yn(e,n){let t={error:0,warn:0,info:0};for(let s of e)s.harnesses.includes(n)&&(s.severity==="error"?t.error+=1:s.severity==="warn"?t.warn+=1:t.info+=1);return t}function Xn(e){let n=e.error*ie.error+e.warn*ie.warn+e.info*ie.info;return Math.min(n,Wn)}async function Bn(e,n){let t=zn[e],s=[];for(let i of t){let r=await i.run(n);s.push({id:i.id,label:i.label,passed:r,weight:i.weight})}return s}function Jn(e){let n=e.reduce((s,i)=>s+i.weight,0);if(n===0)return 0;let t=e.filter(s=>s.passed).reduce((s,i)=>s+i.weight,0);return Math.round(t/n*100)}async function Qn(e,n,t,s,i,r){if(!n.harnesses.includes(e))return{harness:e,detected:!1,score:null,grade:"not-detected",checks:[],findingsCount:{error:0,warn:0,info:0},loadProfile:null};let c=await Bn(e,{context:n,vscode:t,harness:e}),u=Yn(s,e),k=Jn(c),p=Xn(u),G=Math.max(0,Math.min(100,k-p));return{harness:e,detected:!0,score:G,grade:Kn(G),checks:c,findingsCount:u,loadProfile:r[e]??null}}async function ae(e,n,t,s){let i=Le(s,e.harnesses),r=[];for(let o of E){let a=await Qn(o,e,n,t,s,i);r.push(a)}return r}import{readFile as Zn,stat as et}from"node:fs/promises";import De from"node:path";async function C(e){let n=De.join(e,".github/hooks"),t=!1;try{t=(await et(n)).isDirectory()}catch{t=!1}let s=De.join(e,".vscode/settings.json"),i=null,r=null;try{let o=await Zn(s,"utf8");r=o,i=D(o)}catch{i=null,r=null}return{settings:i,settingsRaw:r,hooksDirExists:t}}var le="docs/ai/rules.md",z=3,nt=80;function ce(e){return e.trim().replaceAll(/\s+/g," ")}function tt(e){let n=ce(e);return!(n.length<20||/^#{1,6}\s/.test(n)||/^[-*]\s/.test(n)&&n.length<40||n.includes("AGENTS.md")||n.includes("docs/ai/rules.md"))}function Ne(e){return e.map(n=>ce(n)).filter(n=>n.length>0).join(`
53
- `)}function st(e,n){let t=e.split(`
54
- `),s=Ne(n.split(`
55
- `)),i=[],r=new Set;for(let o=0;o<=t.length-z;o+=1){if(r.has(o))continue;let a=t.slice(o,o+z);if(!a.every(u=>tt(u)))continue;let c=Ne(a);if(!(c.length<nt)&&s.includes(c)){i.push({startLine:o+1,lineCount:z,sample:ce(a[0]??"")}),r.add(o);for(let u=1;u<z;u+=1)r.add(o+u)}}return i}function Pe(e){let n=d(e,le);if(n===null)return[];let t=[];for(let s of $){let i=d(e,s);if(i===null)continue;let r=st(i,n);for(let o of r)t.push({ruleId:"adapter-content-duplication",severity:"warn",message:`${s} duplicates ${String(o.lineCount)} lines from ${le} near line ${String(o.startLine)}.`,file:s,line:o.startLine,harnesses:[],hint:`Replace duplicated rules with a pointer to ${le}.`})}return t}import{readFile as ot}from"node:fs/promises";import at from"node:path";var Me=/!?\[([^\]]*)\]\(([^)]+)\)/g;function v(e){let n=[],t=Me.exec(e);for(;t!==null;){let[s]=t;s.startsWith("!")||n.push({label:t[1]??"",href:(t[2]??"").trim()}),t=Me.exec(e)}return n}var it=new Set(["url","path","href","link","..."]);function K(e){return e.length===0||it.has(e.toLowerCase())||e.startsWith("http://")||e.startsWith("https://")||e.startsWith("mailto:")||e.startsWith("tel:")?!0:(e.split("#")[0]??"").length===0}function Y(e,n){let t=(n.split("#")[0]??"").trim();if(t.length===0)return null;let s=t;try{s=decodeURIComponent(t)}catch{s=t}if(s.startsWith("/"))return s.slice(1).replaceAll("\\","/");let i=e.includes("/")?e.slice(0,e.lastIndexOf("/")):"",r=i.length===0?s:`${i}/${s}`;return rt(r)}function rt(e){let n=e.replaceAll("\\","/").split("/"),t=[];for(let s of n)if(!(s===""||s===".")){if(s===".."){t.pop();continue}t.push(s)}return t.join("/")}function X(e,n){let t=e.split(`
56
- `);for(let s=0;s<t.length;s+=1){let i=t[s];if(i!==void 0&&i.includes(n))return s+1}return null}var Te="docs/ai/available-skills.md";async function lt(e){try{let n=await ot(at.join(e,"package.json"),"utf8"),t=JSON.parse(n);if(m(t)&&"scripts"in t){let{scripts:s}=t;if(m(s))return s}return null}catch{return null}}function ct(e){return e===null?!1:Object.entries(e).some(([n,t])=>n.toLowerCase().includes("qmd")||typeof t=="string"&&t.toLowerCase().includes("qmd"))}function Ge(e){return g(e)?f(e,Te)?[]:[{ruleId:"skills-index",severity:"info",message:`${Te} is missing; add a skill slug index when using skills/.`,file:null,line:null,harnesses:[],hint:"List each skills/<slug>/ folder so agents can pick a minimal skill set."}]:[]}function $e(e){if(!e.harnesses.includes("claude")||!f(e,"CLAUDE.md"))return[];let n=d(e,"CLAUDE.md");return n!==null&&_(n)?[]:[{ruleId:"claude-agent-routing",severity:"info",message:"CLAUDE.md should include an Agent Routing table pointing at agents/*.agent.md.",file:"CLAUDE.md",line:null,harnesses:["claude"],hint:"Add a markdown table mapping task types to shared agents/*.agent.md files."}]}function je(e){if(!g(e)||!f(e,"AGENTS.md"))return[];let n=d(e,"AGENTS.md");return n!==null&&O(n)?[]:[{ruleId:"agents-md-mentions-skills",severity:"info",message:"AGENTS.md should point at docs/ai/available-skills.md or the qmd search workflow.",file:"AGENTS.md",line:null,harnesses:[],hint:"Document how agents discover skills (index doc or npm run qmd -- search)."}]}var ut=50;function dt(e){return!e.endsWith(".md")&&!e.endsWith(".mdc")?!1:e.startsWith("docs/")||e.includes("/docs/")}function pt(e,n){for(let t of v(n)){if(K(t.href))continue;let s=Y(e,t.href);if(s!==null&&dt(s))return!0}return!1}function Oe(e){let n=[];for(let t of e.files){if(!t.path.startsWith("skills/")||!t.path.endsWith("SKILL.md")||(t.lines??0)<=ut)continue;let s=d(e,t.path);s===null||pt(t.path,s)||n.push({ruleId:"skill-doc-deep-links",severity:"info",message:`${t.path} does not deep-link into a durable doc under docs/.`,file:t.path,line:null,harnesses:[],hint:"Pair the skill with a docs/ reference and link it (for example a **Details:** link to a docs/ page) instead of inlining all detail."})}return n}async function _e(e){let n=g(e),t=e.files.filter(i=>i.path.startsWith("docs/ai/")).length;if(!n&&t<2)return[];let s=await lt(e.repoRoot);return ct(s)?[]:[{ruleId:"qmd-script-present",severity:"info",message:'package.json is missing a "qmd" (or equivalent) script for skill/doc search.',file:"package.json",line:null,harnesses:[],hint:'Add something like "qmd": "bun run ./scripts/qmd/qmd.bun.ts" and document usage in AGENTS.md.'}]}var ft=[{id:"github-skills-shadow",pattern:/\.github\/skills\//,message:"use skills/ instead of the removed .github/skills/ path"},{id:"github-agents-shadow",pattern:/\.github\/agents\//,message:"use agents/ instead of the removed .github/agents/ path"},{id:"github-hooks-scripts",pattern:/\.github\/hooks\/scripts\//,message:"use agents/scripts/ instead of .github/hooks/scripts/"},{id:"cursor-skills-shadow",pattern:/\.cursor\/skills\//,message:"use skills/ instead of .cursor/skills/"},{id:"legacy-agent-docs",pattern:/\.agent\/(rules|codebase-map|troubleshooting)\.md/,message:"move legacy .agent docs to docs/ai/ equivalents"}];function ht(e){return/do not|don't|not create|instead of|removed|never use|avoid|legacy|not use|no longer|there is no|\(not `|not `\.cursor\/skills|use \/skills\/|use \/agents\//i.test(e)}function gt(e,n){let t=e.split(`
57
- `);for(let s=0;s<t.length;s+=1){let i=t[s]??"";if(!ht(i)&&(n.lastIndex=0,n.test(i)))return s+1}return null}function mt(e,n){return n.lastIndex=0,n.test(e)}function We(e){let n=[];for(let t of e.files){if(!t.path.endsWith(".md")&&!t.path.endsWith(".mdc"))continue;let s=d(e,t.path);if(s!==null)for(let i of ft){let r=gt(s,i.pattern);if(r!==null){n.push({ruleId:"forbidden-legacy-paths",severity:"warn",message:`${t.path} references a legacy guidance path (${i.id}).`,file:t.path,line:r,harnesses:[],hint:i.message});continue}for(let o of v(s))mt(o.href,i.pattern)&&n.push({ruleId:"forbidden-legacy-paths",severity:"warn",message:`${t.path} links to a legacy guidance path (${i.id}).`,file:t.path,line:X(s,o.href),harnesses:[],hint:i.message})}}return n}var yt=["docs/","skills/","agents/",".cursor/",".github/",".codex/",".agent/",".claude/"],kt=new Set(["AGENTS.md","CLAUDE.md","GEMINI.md","README.md",".github/copilot-instructions.md"]);function Ue(e){let n=e.replaceAll("\\","/").replace(/\/+$/,"");return kt.has(n)?!0:yt.some(t=>n===t.slice(0,-1)||n.startsWith(t))}var St=["",".md","/README.md","/index.md"];async function qe(e,n){for(let t of St){let s=`${n}${t}`.replaceAll("\\","/");if(await I(e,s))return!0}return!1}var wt=8;function bt(e){return/my-doc\.md|example\.spec\.|\/example\//i.test(e)}function xt(e){return e.endsWith("/")?!0:e.endsWith(".md")||e.endsWith(".mdc")}async function Ve(e){let n=[];for(let t of e.files){if(!t.path.endsWith(".md")&&!t.path.endsWith(".mdc"))continue;let s=d(e,t.path);if(s===null)continue;let i=0;for(let r of v(s)){if(K(r.href))continue;let o=Y(t.path,r.href);if(!(o===null||!Ue(o)||!xt(o)||bt(o)||await qe(e.repoRoot,o))&&(n.push({ruleId:"guidance-links-resolve",severity:"warn",message:`Broken link in ${t.path}: (${r.href}) does not resolve.`,file:t.path,line:X(s,r.href),harnesses:[],hint:"Fix the path or add the missing guidance file."}),i+=1,i>=wt))break}}return n}function ze(e,n,t,s){let i=n?.[t];if(m(i)&&Object.keys(i).some(c=>c.startsWith(s)))return!0;if(e===null)return!1;let r=t.replaceAll(".",String.raw`\.`);return new RegExp(`"${r}"\\s*:\\s*\\{[^}]*"${s}[^"]*"`).test(e)}async function Ke(e){let n=await C(e.repoRoot),t=[];return ze(n.settingsRaw,n.settings,"chat.agentSkillsLocations","skills")||t.push({ruleId:"vscode-skills-location",severity:"warn",message:"chat.agentSkillsLocations should point at skills/ in .vscode/settings.json.",file:".vscode/settings.json",line:null,harnesses:["cursor","copilot"],hint:'Set "chat.agentSkillsLocations": { "skills/": true }.'}),ze(n.settingsRaw,n.settings,"chat.agentFilesLocations","agents")||t.push({ruleId:"vscode-agents-location",severity:"warn",message:"chat.agentFilesLocations should point at agents/ in .vscode/settings.json.",file:".vscode/settings.json",line:null,harnesses:["cursor","copilot"],hint:'Set "chat.agentFilesLocations": { "agents/": true }.'}),t}function Ye(e,n){return e.length===0?!0:e.some(t=>n.includes(t))}function ue(e,n,t,s){return{ruleId:e,severity:"warn",message:`${n} has ${String(t)} lines; keep root adapters thin (max ${String(b)}).`,file:n,line:null,harnesses:[s],hint:"Move detailed guidance to docs/ai/ and skills/; keep the adapter as a pointer."}}function Ct(e){let n=[];for(let t of j(e,"skills/")){if(!t.endsWith("/SKILL.md")&&!t.endsWith("SKILL.md"))continue;let s=d(e,t);if(s===null)continue;let i=Q(s);if(i===null){n.push({ruleId:"skill-frontmatter",severity:"error",message:`${t} is missing YAML frontmatter.`,file:t,line:null,harnesses:[]});continue}L(i,"name")||n.push({ruleId:"skill-frontmatter",severity:"error",message:`${t} is missing a name field in frontmatter.`,file:t,line:null,harnesses:[]}),L(i,"description")||n.push({ruleId:"skill-frontmatter",severity:"error",message:`${t} is missing a description field in frontmatter.`,file:t,line:null,harnesses:[]})}return n}function At(e){let n=[];for(let t of j(e,"skills/")){if(!t.endsWith("SKILL.md"))continue;let s=x(e,t);s!==null&&s>J&&n.push({ruleId:"skill-line-count",severity:"warn",message:`${t} has ${String(s)} lines; keep skills under ${String(J)}.`,file:t,line:null,harnesses:[],hint:"Move detail to docs/; keep SKILL.md as a pointer."})}return n}function Rt(e){let n=[];for(let t of j(e,"agents/")){if(!t.endsWith(".agent.md"))continue;let s=d(e,t);if(s===null)continue;let i=Q(s);if(i===null){n.push({ruleId:"agent-frontmatter",severity:"error",message:`${t} is missing YAML frontmatter.`,file:t,line:null,harnesses:[]});continue}L(i,"name")||n.push({ruleId:"agent-frontmatter",severity:"error",message:`${t} is missing a name field in frontmatter.`,file:t,line:null,harnesses:[]}),L(i,"description")||n.push({ruleId:"agent-frontmatter",severity:"error",message:`${t} is missing a description field in frontmatter.`,file:t,line:null,harnesses:[]})}return n}function vt(e){let n=[];for(let t of $){if(!f(e,t))continue;let s=d(e,t);s===null||Se(s)||n.push({ruleId:"adapter-points-to-shared",severity:"warn",message:`${t} should reference AGENTS.md and docs/ai/rules.md.`,file:t,line:null,harnesses:[],hint:"Keep adapters thin; point at shared layers instead of duplicating rules."})}return n}async function Ht(e){if(!e.files.some(i=>i.path.startsWith("skills/")))return[];let t=[],s=[".github/skills",".cursor/skills"];for(let i of s)await I(e.repoRoot,i)&&t.push({ruleId:"no-duplicate-skill-trees",severity:"error",message:`${i}/ duplicates root skills/; use skills/ only.`,file:i,line:null,harnesses:[],hint:"Remove the shadow tree and keep canonical skills/ at the repo root."});return t}async function Et(e){return e.files.some(t=>t.path.startsWith("agents/"))?await I(e.repoRoot,".github/agents")?[{ruleId:"no-duplicate-agent-trees",severity:"error",message:".github/agents/ duplicates root agents/; use agents/ only.",file:".github/agents",line:null,harnesses:[],hint:"Remove .github/agents/ and keep canonical agents/ at the repo root."}]:[]:[]}async function Lt(e){let n=await C(e.repoRoot);return n.hooksDirExists?n.settings?.["chat.useCustomAgentHooks"]===!0?[]:[{ruleId:"vscode-custom-hooks",severity:"warn",message:".github/hooks/ exists but chat.useCustomAgentHooks is not true in .vscode/settings.json.",file:".vscode/settings.json",line:null,harnesses:["cursor","copilot"],hint:'Set "chat.useCustomAgentHooks": true so Cursor and Copilot load workspace hooks.'}]:[]}var It={id:"shared-agents-md",title:"Shared AGENTS.md entry point",severity:"warn",category:"structure",dimension:"layering",harnesses:[],check(e){return f(e,"AGENTS.md")?[]:[{ruleId:"shared-agents-md",severity:"warn",message:"AGENTS.md is missing.",file:null,line:null,harnesses:[],hint:"Add AGENTS.md as the repo-wide AI entry point with workflow, safety, and pointers to canonical docs."}]}},Ft={id:"shared-rules-doc",title:"Canonical rules doc",severity:"warn",category:"structure",dimension:"layering",harnesses:[],check(e){return f(e,"docs/ai/rules.md")?[]:[{ruleId:"shared-rules-doc",severity:"warn",message:"docs/ai/rules.md is missing.",file:null,line:null,harnesses:[],hint:"Add docs/ai/rules.md as the canonical coding-rules reference."}]}},Dt={id:"adapter-thin-claude",title:"Thin CLAUDE.md adapter",severity:"warn",category:"adapter",dimension:"layering",harnesses:["claude"],check(e){let n=x(e,"CLAUDE.md");return n===null||n<=b?[]:[ue("adapter-thin-claude","CLAUDE.md",n,"claude")]}},Nt={id:"adapter-thin-gemini",title:"Thin GEMINI.md adapter",severity:"warn",category:"adapter",dimension:"layering",harnesses:["gemini"],check(e){let n=x(e,"GEMINI.md");return n===null||n<=b?[]:[ue("adapter-thin-gemini","GEMINI.md",n,"gemini")]}},Pt={id:"adapter-thin-copilot",title:"Thin Copilot instructions",severity:"warn",category:"adapter",dimension:"layering",harnesses:["copilot"],check(e){let n=".github/copilot-instructions.md",t=x(e,n);return t===null||t<=b?[]:[ue("adapter-thin-copilot",n,t,"copilot")]}},Mt={id:"adapter-points-to-shared",title:"Adapters reference shared layers",severity:"warn",category:"adapter",dimension:"layering",harnesses:[],check:vt},Tt={id:"skill-frontmatter",title:"Skill frontmatter",severity:"error",category:"skills",dimension:"maintainability",harnesses:[],check:Ct},Gt={id:"skill-line-count",title:"Skill line budget",severity:"warn",category:"skills",dimension:"maintainability",harnesses:[],check:At},$t={id:"agent-frontmatter",title:"Agent frontmatter",severity:"error",category:"agents",dimension:"maintainability",harnesses:[],check:Rt},jt={id:"skills-index",title:"Skill slug index",severity:"info",category:"discoverability",dimension:"discoverability",harnesses:[],check:Ge},Ot={id:"claude-agent-routing",title:"Claude agent routing table",severity:"info",category:"discoverability",dimension:"discoverability",harnesses:["claude"],check:$e},_t={id:"agents-md-mentions-skills",title:"AGENTS.md skill discovery",severity:"info",category:"discoverability",dimension:"discoverability",harnesses:[],check:je},Wt={id:"skill-doc-deep-links",title:"Skills deep-link into docs",severity:"info",category:"discoverability",dimension:"discoverability",harnesses:[],check:Oe},Xe=[It,Ft,Dt,Nt,Pt,Mt,Tt,Gt,$t,jt,Ot,_t,Wt];function Ut(e,n,t){return Ye(e.harnesses,n.harnesses)?t===null||t.length===0||e.harnesses.length===0?!0:e.harnesses.some(s=>t.includes(s)):!1}function qt(e,n,t){return Ye(t,e.harnesses)?n===null||n.length===0?!0:t.some(s=>n.includes(s)):!1}async function Be(e,n){let t=[];for(let a of Xe)Ut(a,e,n)&&t.push(...a.check(e));if(qt(e,n,["cursor","copilot"])){let a=await Ke(e);t.push(...a);let c=await Lt(e);t.push(...c)}let s=await Ht(e);t.push(...s);let i=await Et(e);t.push(...i);let r=await _e(e);t.push(...r);let o=await Ve(e);return t.push(...o),t.push(...Pe(e)),t.push(...We(e)),t}var Vt=new Set(["skills-index","agents-md-mentions-skills","skill-doc-deep-links"]);function Je(e,n){return Vt.has(e)?g(n):e==="qmd-script-present"?zt(n):e==="claude-agent-routing"?n.harnesses.includes("claude")&&f(n,"CLAUDE.md"):!0}function zt(e){return g(e)?!0:e.files.filter(t=>t.path.startsWith("docs/ai/")).length>=2}var de={"shared-agents-md":"layering","shared-rules-doc":"layering","adapter-thin-claude":"layering","adapter-thin-gemini":"layering","adapter-thin-copilot":"layering","adapter-points-to-shared":"layering","skill-frontmatter":"maintainability","skill-line-count":"maintainability","agent-frontmatter":"maintainability","no-duplicate-skill-trees":"maintainability","no-duplicate-agent-trees":"maintainability","vscode-skills-location":"harnessWiring","vscode-agents-location":"harnessWiring","vscode-custom-hooks":"guardrails","skills-index":"discoverability","claude-agent-routing":"discoverability","agents-md-mentions-skills":"discoverability","skill-doc-deep-links":"discoverability","qmd-script-present":"discoverability","guidance-links-resolve":"maintainability","adapter-content-duplication":"sharing","forbidden-legacy-paths":"maintainability"};function Qe(e){return de[e]??null}var Kt=[{min:85,grade:"excellent"},{min:70,grade:"good"},{min:50,grade:"fair"},{min:0,grade:"poor"}];function H(e){for(let{min:n,grade:t}of Kt)if(e>=n)return t;return"poor"}function Ze(e,n){return n===0?0:e>=80?95:e>=65?82:e>=50?68:e>=30?52:35}var Yt=["layering","sharing","discoverability","harnessWiring","maintainability","guardrails"],pe={error:15,warn:6,info:2},Xt=35;function Bt(e){let n=e.filter(s=>s.detected&&s.score!==null);if(n.length===0)return 0;let t=n.reduce((s,i)=>s+(i.score??0),0);return Math.round(t/n.length)}function Jt(e){return Object.entries(de).filter(([,n])=>n===e).map(([n])=>n)}function nn(e,n){return e.filter(t=>Qe(t.ruleId)===n)}function Qt(e){let n=0;for(let t of e)t.severity==="error"?n+=pe.error:t.severity==="warn"?n+=pe.warn:n+=pe.info;return Math.min(n,Xt)}function en(e,n,t){let s=Jt(e).filter(c=>Je(c,t)),i=s.length,r=new Set(nn(n,e).map(c=>c.ruleId)),o=s.filter(c=>!r.has(c)).length,a=i===0?0:Math.round(o/i*100);return{score:a,grade:H(a),passedRules:o,applicableRules:i}}function Zt(){let e={score:0,grade:H(0),passedRules:0,applicableRules:0};return{layering:{...e},sharing:{...e},discoverability:{...e},harnessWiring:{...e},maintainability:{...e},guardrails:{...e}}}function fe(e,n,t,s){let i=Zt();for(let r of Yt){if(r==="sharing"){let o=Ze(n.shared.percentLines,n.totals.lines),a=Qt(nn(e,r)),c=Math.max(0,Math.min(100,o-a));i.sharing={score:c,grade:H(c),passedRules:n.shared.percentLines>=65?1:0,applicableRules:n.totals.lines>0?1:0};continue}if(r==="harnessWiring"){let o=en(r,e,s),a=Bt(t),c=a===0?o.score:Math.round((o.score+a)/2),u=Math.max(0,Math.min(100,c));i.harnessWiring={score:u,grade:H(u),passedRules:o.passedRules,applicableRules:o.applicableRules};continue}i[r]=en(r,e,s)}return{profile:"meta-harness",dimensions:i}}import{readFile as tn,readdir as es,stat as sn}from"node:fs/promises";import T from"node:path";var ns=[".md",".mdc"],ts=["AGENTS.md","docs/ai/rules.md","docs/ai/ai-system.md","docs/ai/available-skills.md","docs/ai/hooks.md"],ss=[{path:"CLAUDE.md",harness:"claude"},{path:"GEMINI.md",harness:"gemini"},{path:".github/copilot-instructions.md",harness:"copilot"},{path:".agent/README.md",harness:"antigravity"}],is=[{dir:"skills",layer:"skills"},{dir:"agents",layer:"agents"},{dir:".cursor/rules",layer:"adapter",harness:"cursor"},{dir:".agent/workflows",layer:"workflows",harness:"antigravity"},{dir:".codex",layer:"adapter",harness:"codex"},{dir:"docs/ai",layer:"shared"}];function rs(e){return ns.some(n=>e.endsWith(n))}function os(e){return e.length===0?0:e.split(`
58
- `).length}async function rn(e){let n=[],t=[];try{t=await es(e)}catch{return n}for(let s of t){let i=T.join(e,s),r=await sn(i);r.isDirectory()?n.push(...await rn(i)):r.isFile()&&rs(i)&&n.push(i)}return n}async function as(e){let{repoRoot:n,relativePath:t,layer:s,harness:i}=e,r=T.join(n,t);try{let o=await sn(r);if(!o.isFile())return null;let a=await tn(r,"utf8"),c={path:t,layer:s,lines:os(a),bytes:o.size};return i!==void 0&&(c.harness=i),c}catch{return null}}async function he(e){let n=[],t=new Map,s=new Set;async function i(r,o,a){let c=r.replaceAll("\\","/");if(s.has(c))return;let u=await as({repoRoot:e,relativePath:c,layer:o,harness:a});if(u===null)return;s.add(c),n.push(u);let k=T.join(e,c);try{let p=await tn(k,"utf8");t.set(c,p)}catch{}}for(let r of ts)await i(r,"shared");for(let r of ss)await i(r.path,"adapter",r.harness);for(let{dir:r,layer:o,harness:a}of is){let c=T.join(e,r),u=await rn(c);for(let k of u){let p=T.relative(e,k).replaceAll("\\","/");await i(p,o,a)}}return n.sort((r,o)=>r.path.localeCompare(o.path)),{files:n,contents:t}}var cs="0.0.1",on={error:3,warn:2,info:1};function us(e){let n=0,t=0,s=0;for(let i of e)i.severity==="error"?n+=1:i.severity==="warn"?t+=1:s+=1;return{error:n,warn:t,info:s}}async function ge(e){let n=ls.resolve(e.repoRoot),t=await se(n),{files:s,contents:i}=await he(n),r={repoRoot:n,harnesses:t,files:s,fileContents:i},o=await Be(r,e.harnessFilter),a=us(o),c=te(s,t),u=await C(n),k=await ae(r,u,o,c),p=fe(o,c,k,r),G=await He(r,u);return{version:cs,root:n,harnesses:t,inventory:{fileCount:s.length,files:s},sharing:c,metaHarness:p,harnessGapMatrix:G,harnessOptimization:k,findings:o,summary:a}}function an(e,n){let t=on[n];return e.some(s=>on[s.severity]>=t)}var ln=0,ps=1,w=2;function fs(){process.stdout.write(`paniolo-scan \u2014 AI harness diagnostic (read-only)
67
+ `)}import Yi from"node:path";function Gn(){return{copilot:{files:0,lines:0,bytes:0},cursor:{files:0,lines:0,bytes:0},codex:{files:0,lines:0,bytes:0},antigravity:{files:0,lines:0,bytes:0},claude:{files:0,lines:0,bytes:0},gemini:{files:0,lines:0,bytes:0}}}function ne(e,t){return t===0?0:Math.round(e/t*1e3)/10}function Se(e,t){let n={files:0,lines:0,bytes:0},s=Gn(),i={files:0,lines:0,bytes:0},o={files:0,lines:0,bytes:0},r=[];for(let a of e){let l=Ue(a,t);if(l.length===0)continue;o.files+=1,o.lines+=a.lines,o.bytes+=a.bytes;let d=qe(a,l),h=d?"shared":"unique";if(r.push({path:a.path,lines:a.lines,scope:h,harnesses:l}),d)n.files+=1,n.lines+=a.lines,n.bytes+=a.bytes;else{let[p]=l;p!==void 0&&(s[p].files+=1,s[p].lines+=a.lines,s[p].bytes+=a.bytes),i.files+=1,i.lines+=a.lines,i.bytes+=a.bytes}}return{totals:o,shared:{...n,percentLines:ne(n.lines,o.lines)},unique:{byHarness:s,totals:{...i,percentLines:ne(i.lines,o.lines)}},files:r}}function tt(e,t){let n={sharedLines:0,uniqueLines:0,totalLines:0,sharedPercent:0,uniquePercent:0},s={copilot:{...n},cursor:{...n},codex:{...n},antigravity:{...n},claude:{...n},gemini:{...n}};for(let i of t){let o=e.unique.byHarness[i].lines,r=e.shared.lines,a=r+o;s[i]={sharedLines:r,uniqueLines:o,totalLines:a,sharedPercent:ne(r,a),uniquePercent:ne(o,a)}}return s}import{access as Wn,readFile as Un}from"node:fs/promises";import k from"node:path";function W(e){try{let t=JSON.parse(e);if(w(t))return t}catch{}return _n(e)}function _n(e){let t={},n=/"([^"]+)"\s*:\s*(true|false|null|\d+(?:\.\d+)?|"[^"]*")/g;for(let[,s,i]of e.matchAll(n))s===void 0||i===void 0||(t[s]=On(i));return Object.keys(t).length===0?null:t}function On(e){if(e==="true")return!0;if(e==="false")return!1;if(e==="null")return null;if(e.startsWith('"')&&e.endsWith('"'))return e.slice(1,-1);let t=Number(e);return Number.isNaN(t)?e:t}async function b(e){try{return await Wn(e),!0}catch{return!1}}async function qn(e){try{let t=await Un(e,"utf8");return W(t)}catch{return null}}function Bn(e){return e===null?!1:e["chat.agentSkillsLocations"]!==void 0||e["chat.agentFilesLocations"]!==void 0||e["chat.useCustomAgentHooks"]===!0}async function we(e){let t=new Set;await b(k.join(e,".github/copilot-instructions.md"))&&t.add("copilot"),await b(k.join(e,".github/hooks"))&&t.add("copilot");let n=await qn(k.join(e,".vscode/settings.json"));return Bn(n)&&(t.add("cursor"),t.add("copilot")),await b(k.join(e,".cursor"))&&t.add("cursor"),await b(k.join(e,".codex/config.toml"))&&t.add("codex"),await b(k.join(e,".codex/hooks.json"))&&t.add("codex"),await b(k.join(e,".codex/agents"))&&t.add("codex"),await b(k.join(e,".agent/workflows"))&&t.add("antigravity"),await b(k.join(e,".agent/README.md"))&&t.add("antigravity"),await b(k.join(e,"CLAUDE.md"))&&t.add("claude"),await b(k.join(e,".claude/settings.json"))&&t.add("claude"),await b(k.join(e,"GEMINI.md"))&&t.add("gemini"),[...t].sort()}var se=250,Vn=150;function nt(e){return Math.ceil(e/4)}function U(e,t){return e.files.find(n=>n.path===t)??null}function F(e,t){return{path:e.path,lines:e.lines,bytes:e.bytes,estimatedTokens:nt(e.bytes),reason:t}}function zn(e,t){let n=e.fileContents.get(t.path);return n===void 0?!1:/^alwaysApply:\s*true\s*$/im.test(n)||/^always_apply:\s*true\s*$/im.test(n)}function Jn(e,t){let n=[];if(t==="copilot"){let s=U(e,".github/copilot-instructions.md");s!==null&&n.push(F(s,"Copilot workspace instructions are loaded by Copilot Chat."))}if(t==="cursor")for(let s of e.files)s.path.startsWith(".cursor/rules/")&&zn(e,s)&&n.push(F(s,"Cursor rule declares alwaysApply: true."));if(t==="codex"){let s=U(e,"AGENTS.md");s!==null&&n.push(F(s,"Root AGENTS.md is the Codex repo instruction entry."));for(let i of e.files)i.path.startsWith(".codex/")&&i.layer==="adapter"&&n.push(F(i,"Codex adapter guidance under .codex/."))}if(t==="antigravity"){let s=U(e,".agent/README.md");s!==null&&n.push(F(s,"Antigravity workspace adapter README."))}if(t==="claude"){let s=U(e,"CLAUDE.md");s!==null&&n.push(F(s,"Claude Code root instruction file."))}if(t==="gemini"){let s=U(e,"GEMINI.md");s!==null&&n.push(F(s,"Gemini root instruction file."))}return n.toSorted((s,i)=>s.path.localeCompare(i.path))}function Kn(e,t){if(!e.harnesses.includes(t))return{harness:t,detected:!1,files:[],totalLines:0,totalBytes:0,estimatedTokens:0,maxRecommendedLines:se,status:"ok"};let s=Jn(e,t),i=s.reduce((r,a)=>r+a.lines,0),o=s.reduce((r,a)=>r+a.bytes,0);return{harness:t,detected:!0,files:s,totalLines:i,totalBytes:o,estimatedTokens:nt(o),maxRecommendedLines:se,status:i>se?"high":"ok"}}function be(e){return{harnesses:E.map(t=>Kn(e,t)),maxRecommendedLines:se,maxRecommendedFileLines:Vn}}import{access as Yn}from"node:fs/promises";import q from"node:path";var Xn=[{min:85,grade:"excellent"},{min:70,grade:"good"},{min:50,grade:"fair"},{min:0,grade:"poor"}],Ce={error:12,warn:5,info:1},Qn=40;async function B(e){try{return await Yn(e),!0}catch{return!1}}function x(e,t){return e.files.some(n=>n.path===t)}function it(e,t){return e.fileContents.get(t)??null}function Zn(e,t){return e.files.find(s=>s.path===t)?.lines??null}function es(e){return e.includes("AGENTS.md")||e.includes("docs/ai/rules.md")}function D(e){return e.files.some(t=>t.path.startsWith("skills/"))}function ie(e){return e.files.some(t=>t.path.startsWith("agents/"))}function st(e,t){return e.settings?.[t]===!0}function V(e,t,n){let s=e.settings?.[t];if(w(s)&&Object.keys(s).some(a=>a.startsWith(n)))return!0;if(e.settingsRaw===null)return!1;let i=t.replaceAll(".",String.raw`\.`);return new RegExp(`"${i}"\\s*:\\s*\\{[^}]*"${n}[^"]*"`).test(e.settingsRaw)}function xe(e,t){let n=Zn(e,t);return n===null?!1:n<=v}function Le(e,t){let n=it(e,t);return n===null?!1:es(n)}function ts(e){let t=it(e,".agent/README.md");return t===null?!1:/\.agent\/workflows\/|workflows\//i.test(t)&&/(workflow|playbook|slash command|mission|turbo)/i.test(t)}var ns={copilot:[{id:"copilot-instructions",label:"Copilot instructions file present",weight:15,run:({context:e})=>x(e,".github/copilot-instructions.md")},{id:"copilot-references-shared",label:"Copilot instructions reference shared layers",weight:15,run:({context:e})=>Le(e,".github/copilot-instructions.md")},{id:"copilot-adapter-thin",label:"Copilot instructions stay thin",weight:10,run:({context:e})=>xe(e,".github/copilot-instructions.md")},{id:"copilot-skills-location",label:"chat.agentSkillsLocations points at skills/",weight:15,run:({vscode:e})=>V(e,"chat.agentSkillsLocations","skills")},{id:"copilot-agents-location",label:"chat.agentFilesLocations points at agents/",weight:15,run:({vscode:e})=>V(e,"chat.agentFilesLocations","agents")},{id:"copilot-hooks-enabled",label:"Custom agent hooks enabled when .github/hooks/ exists",weight:15,run:({vscode:e})=>!e.hooksDirExists||st(e,"chat.useCustomAgentHooks")},{id:"copilot-shared-skills",label:"Shared skills/ tree present",weight:8,run:({context:e})=>D(e)},{id:"copilot-shared-agents",label:"Shared agents/ tree present",weight:7,run:({context:e})=>ie(e)}],cursor:[{id:"cursor-rules-or-settings",label:"Cursor rules or VS Code agent settings present",weight:20,run:({context:e,vscode:t})=>e.files.some(n=>n.path.startsWith(".cursor/rules/"))||V(t,"chat.agentSkillsLocations","skills")},{id:"cursor-skills-location",label:"chat.agentSkillsLocations points at skills/",weight:20,run:({vscode:e})=>V(e,"chat.agentSkillsLocations","skills")},{id:"cursor-agents-location",label:"chat.agentFilesLocations points at agents/",weight:20,run:({vscode:e})=>V(e,"chat.agentFilesLocations","agents")},{id:"cursor-hooks-enabled",label:"Custom agent hooks enabled when .github/hooks/ exists",weight:15,run:({vscode:e})=>!e.hooksDirExists||st(e,"chat.useCustomAgentHooks")},{id:"cursor-shared-skills",label:"Shared skills/ tree present",weight:13,run:({context:e})=>D(e)},{id:"cursor-shared-agents",label:"Shared agents/ tree present",weight:12,run:({context:e})=>ie(e)}],codex:[{id:"codex-config-surface",label:"Codex config surface present (.codex/ or nested AGENTS.md)",weight:25,run:async({context:e})=>e.files.some(t=>t.path.startsWith(".codex/"))?!0:await B(q.join(e.repoRoot,"api/AGENTS.md"))||await B(q.join(e.repoRoot,"react/AGENTS.md"))||await B(q.join(e.repoRoot,"shared/AGENTS.md"))},{id:"codex-hooks",label:"Codex hooks.json present",weight:20,run:({context:e})=>B(q.join(e.repoRoot,".codex/hooks.json"))},{id:"codex-agents-wrappers",label:"Codex agent wrappers present",weight:20,run:({context:e})=>B(q.join(e.repoRoot,".codex/agents"))},{id:"codex-shared-entry",label:"Root AGENTS.md present",weight:20,run:({context:e})=>x(e,"AGENTS.md")},{id:"codex-shared-skills",label:"Shared skills/ tree present",weight:8,run:({context:e})=>D(e)},{id:"codex-shared-rules",label:"Canonical rules doc present",weight:7,run:({context:e})=>x(e,"docs/ai/rules.md")}],antigravity:[{id:"antigravity-workflows",label:"Workflow playbooks present (.agent/workflows/)",weight:35,run:({context:e})=>e.files.some(t=>t.path.startsWith(".agent/workflows/"))},{id:"antigravity-readme",label:".agent/README.md documents workflow layout",weight:15,run:({context:e})=>ts(e)},{id:"antigravity-shared-entry",label:"Root AGENTS.md present",weight:20,run:({context:e})=>x(e,"AGENTS.md")},{id:"antigravity-shared-skills",label:"Shared skills/ tree present",weight:15,run:({context:e})=>D(e)},{id:"antigravity-ai-system-doc",label:"AI system layout doc present",weight:15,run:({context:e})=>x(e,"docs/ai/ai-system.md")}],claude:[{id:"claude-adapter",label:"CLAUDE.md adapter present",weight:25,run:({context:e})=>x(e,"CLAUDE.md")},{id:"claude-references-shared",label:"CLAUDE.md references shared layers",weight:25,run:({context:e})=>Le(e,"CLAUDE.md")},{id:"claude-adapter-thin",label:"CLAUDE.md stays thin",weight:20,run:({context:e})=>xe(e,"CLAUDE.md")},{id:"claude-shared-skills",label:"Shared skills/ tree present",weight:15,run:({context:e})=>D(e)},{id:"claude-skills-index",label:"Skill index doc for prose discovery",weight:8,run:({context:e})=>x(e,"docs/ai/available-skills.md")},{id:"claude-shared-agents",label:"Shared agents/ tree present",weight:7,run:({context:e})=>ie(e)}],gemini:[{id:"gemini-adapter",label:"GEMINI.md adapter present",weight:30,run:({context:e})=>x(e,"GEMINI.md")},{id:"gemini-references-shared",label:"GEMINI.md references shared layers",weight:25,run:({context:e})=>Le(e,"GEMINI.md")},{id:"gemini-adapter-thin",label:"GEMINI.md stays thin",weight:20,run:({context:e})=>xe(e,"GEMINI.md")},{id:"gemini-shared-skills",label:"Shared skills/ tree present",weight:15,run:({context:e})=>D(e)},{id:"gemini-shared-agents",label:"Shared agents/ tree present",weight:10,run:({context:e})=>ie(e)}]};function ss(e){for(let{min:t,grade:n}of Xn)if(e>=t)return n;return"poor"}function is(e,t){let n={error:0,warn:0,info:0};for(let s of e)s.harnesses.includes(t)&&(s.severity==="error"?n.error+=1:s.severity==="warn"?n.warn+=1:n.info+=1);return n}function rs(e){let t=e.error*Ce.error+e.warn*Ce.warn+e.info*Ce.info;return Math.min(t,Qn)}async function os(e,t){let n=ns[e],s=[];for(let i of n){let o=await i.run(t);s.push({id:i.id,label:i.label,passed:o,weight:i.weight})}return s}function as(e){let t=e.reduce((s,i)=>s+i.weight,0);if(t===0)return 0;let n=e.filter(s=>s.passed).reduce((s,i)=>s+i.weight,0);return Math.round(n/t*100)}async function cs(e,t,n,s,i,o){if(!t.harnesses.includes(e))return{harness:e,detected:!1,score:null,grade:"not-detected",checks:[],findingsCount:{error:0,warn:0,info:0},loadProfile:null};let l=await os(e,{context:t,vscode:n,harness:e}),d=is(s,e),h=as(l),p=rs(d),u=Math.max(0,Math.min(100,h-p));return{harness:e,detected:!0,score:u,grade:ss(u),checks:l,findingsCount:d,loadProfile:o[e]??null}}async function Ae(e,t,n,s){let i=tt(s,e.harnesses),o=[];for(let r of E){let a=await cs(r,e,t,n,s,i);o.push(a)}return o}import{readFile as ls,stat as us}from"node:fs/promises";import rt from"node:path";async function H(e){let t=rt.join(e,".github/hooks"),n=!1;try{n=(await us(t)).isDirectory()}catch{n=!1}let s=rt.join(e,".vscode/settings.json"),i=null,o=null;try{let r=await ls(s,"utf8");o=r,i=W(r)}catch{i=null,o=null}return{settings:i,settingsRaw:o,hooksDirExists:n}}function ds(e){return e.contextBudget!==void 0&&e.contextBudget.harnesses.length>0}function ot(e){return ds(e)}function at(e){let t=e.contextBudget;if(t===void 0)return[];let n=[];for(let s of t.harnesses)!s.detected||s.status==="ok"||n.push({ruleId:"always-loaded-budget",severity:"info",message:`${s.harness} always-loaded context is ${String(s.totalLines)} lines; recommended maximum is ${String(s.maxRecommendedLines)}.`,file:null,line:null,harnesses:[s.harness],hint:"Keep root adapters as short indexes and move deep guidance into linked docs, skills, agents, or workflows."});return n}function ct(e){let t=e.contextBudget;if(t===void 0)return[];let n=[];for(let s of t.harnesses)if(s.detected)for(let i of s.files)i.lines<=t.maxRecommendedFileLines||n.push({ruleId:"adapter-context-budget",severity:"info",message:`${i.path} contributes ${String(i.lines)} always-loaded lines for ${s.harness}; recommended maximum per file is ${String(t.maxRecommendedFileLines)}.`,file:i.path,line:null,harnesses:[s.harness],hint:"Split detailed guidance into linked docs and keep always-loaded instruction files compact."});return n}import{readFile as gs}from"node:fs/promises";import ms from"node:path";var lt=/!?\[([^\]]*)\]\(([^)]+)\)/g;function M(e){let t=[],n=lt.exec(e);for(;n!==null;){let[s]=n;s.startsWith("!")||t.push({label:n[1]??"",href:(n[2]??"").trim()}),n=lt.exec(e)}return t}var ps=new Set(["url","path","href","link","..."]);function re(e){return e.length===0||ps.has(e.toLowerCase())||e.startsWith("http://")||e.startsWith("https://")||e.startsWith("mailto:")||e.startsWith("tel:")?!0:(e.split("#")[0]??"").length===0}function oe(e,t){let n=(t.split("#")[0]??"").trim();if(n.length===0)return null;let s=n;try{s=decodeURIComponent(n)}catch{s=n}if(s.startsWith("/"))return s.slice(1).replaceAll("\\","/");let i=e.includes("/")?e.slice(0,e.lastIndexOf("/")):"",o=i.length===0?s:`${i}/${s}`;return fs(o)}function fs(e){let t=e.replaceAll("\\","/").split("/"),n=[];for(let s of t)if(!(s===""||s===".")){if(s===".."){n.pop();continue}n.push(s)}return n.join("/")}function ae(e,t){let n=e.split(`
68
+ `);for(let s=0;s<n.length;s+=1){let i=n[s];if(i!==void 0&&i.includes(t))return s+1}return null}var ut="docs/ai/available-skills.md";async function hs(e){try{let t=await gs(ms.join(e,"package.json"),"utf8"),n=JSON.parse(t);if(w(n)&&"scripts"in n){let{scripts:s}=n;if(w(s))return s}return null}catch{return null}}function ys(e){return e===null?!1:Object.entries(e).some(([t,n])=>t.toLowerCase().includes("qmd")||typeof n=="string"&&n.toLowerCase().includes("qmd"))}function dt(e){return S(e)?y(e,ut)?[]:[{ruleId:"skills-index",severity:"info",message:`${ut} is missing; add a skill slug index when using skills/.`,file:null,line:null,harnesses:[],hint:"List each skills/<slug>/ folder so agents can pick a minimal skill set."}]:[]}function pt(e){if(!e.harnesses.includes("claude")||!y(e,"CLAUDE.md"))return[];let t=f(e,"CLAUDE.md");return t!==null&&Z(t)?[]:[{ruleId:"claude-agent-routing",severity:"info",message:"CLAUDE.md should include an Agent Routing table pointing at agents/*.agent.md.",file:"CLAUDE.md",line:null,harnesses:["claude"],hint:"Add a markdown table mapping task types to shared agents/*.agent.md files."}]}function ft(e){if(!S(e)||!y(e,"AGENTS.md"))return[];let t=f(e,"AGENTS.md");return t!==null&&Q(t)?[]:[{ruleId:"agents-md-mentions-skills",severity:"info",message:"AGENTS.md should point at docs/ai/available-skills.md or the qmd search workflow.",file:"AGENTS.md",line:null,harnesses:[],hint:"Document how agents discover skills (index doc or npm run qmd -- search)."}]}var ks=50;function Ss(e){return!e.endsWith(".md")&&!e.endsWith(".mdc")?!1:e.startsWith("docs/")||e.includes("/docs/")}function ws(e,t){for(let n of M(t)){if(re(n.href))continue;let s=oe(e,n.href);if(s!==null&&Ss(s))return!0}return!1}function gt(e){let t=[];for(let n of e.files){if(!n.path.startsWith("skills/")||!n.path.endsWith("SKILL.md")||(n.lines??0)<=ks)continue;let s=f(e,n.path);s===null||ws(n.path,s)||t.push({ruleId:"skill-doc-deep-links",severity:"info",message:`${n.path} does not deep-link into a durable doc under docs/.`,file:n.path,line:null,harnesses:[],hint:"Pair the skill with a docs/ reference and link it (for example a **Details:** link to a docs/ page) instead of inlining all detail."})}return t}async function mt(e){let t=S(e),n=e.files.filter(i=>i.path.startsWith("docs/ai/")).length;if(!t&&n<2)return[];let s=await hs(e.repoRoot);return ys(s)?[]:[{ruleId:"qmd-script-present",severity:"info",message:'package.json is missing a "qmd" (or equivalent) script for skill/doc search.',file:"package.json",line:null,harnesses:[],hint:'Add something like "qmd": "bun run ./scripts/qmd/qmd.bun.ts" and document usage in AGENTS.md.'}]}var bs=["docs/","skills/","agents/",".cursor/",".github/",".codex/",".agent/",".claude/"],Cs=new Set(["AGENTS.md","CLAUDE.md","GEMINI.md","README.md",".github/copilot-instructions.md"]);function ht(e){let t=e.replaceAll("\\","/").replace(/\/+$/,"");return Cs.has(t)?!0:bs.some(n=>t===n.slice(0,-1)||t.startsWith(n))}var xs=["",".md","/README.md","/index.md"];async function yt(e,t){for(let n of xs){let s=`${t}${n}`.replaceAll("\\","/");if(await _(e,s))return!0}return!1}var Ls=8;function As(e){return/my-doc\.md|example\.spec\.|\/example\//i.test(e)}function Rs(e){return e.endsWith("/")?!0:e.endsWith(".md")||e.endsWith(".mdc")}async function kt(e){let t=[];for(let n of e.files){if(!n.path.endsWith(".md")&&!n.path.endsWith(".mdc"))continue;let s=f(e,n.path);if(s===null)continue;let i=0;for(let o of M(s)){if(re(o.href))continue;let r=oe(n.path,o.href);if(!(r===null||!ht(r)||!Rs(r)||As(r)||await yt(e.repoRoot,r))&&(t.push({ruleId:"guidance-links-resolve",severity:"warn",message:`Broken link in ${n.path}: (${o.href}) does not resolve.`,file:n.path,line:ae(s,o.href),harnesses:[],hint:"Fix the path or add the missing guidance file."}),i+=1,i>=Ls))break}}return t}var Re="docs/ai/rules.md",ce=3,Es=80;function Ee(e){return e.trim().replaceAll(/\s+/g," ")}function vs(e){let t=Ee(e);return!(t.length<20||/^#{1,6}\s/.test(t)||/^[-*]\s/.test(t)&&t.length<40||t.includes("AGENTS.md")||t.includes("docs/ai/rules.md"))}function St(e){return e.map(t=>Ee(t)).filter(t=>t.length>0).join(`
69
+ `)}function Is(e,t){let n=e.split(`
70
+ `),s=St(t.split(`
71
+ `)),i=[],o=new Set;for(let r=0;r<=n.length-ce;r+=1){if(o.has(r))continue;let a=n.slice(r,r+ce);if(!a.every(d=>vs(d)))continue;let l=St(a);if(!(l.length<Es)&&s.includes(l)){i.push({startLine:r+1,lineCount:ce,sample:Ee(a[0]??"")}),o.add(r);for(let d=1;d<ce;d+=1)o.add(r+d)}}return i}function wt(e){let t=f(e,Re);if(t===null)return[];let n=[];for(let s of Y){let i=f(e,s);if(i===null)continue;let o=Is(i,t);for(let r of o)n.push({ruleId:"adapter-content-duplication",severity:"warn",message:`${s} duplicates ${String(r.lineCount)} lines from ${Re} near line ${String(r.startLine)}.`,file:s,line:r.startLine,harnesses:[],hint:`Replace duplicated rules with a pointer to ${Re}.`})}return n}function le(e){return e.intelligenceLayer??null}function ue(e){return e.packageJson.path!==null||e.workflows.length>0||e.prTemplates.length>0}function bt(e){let t=le(e);return t!==null&&ue(t)}function ve(e,t,n,s){return{ruleId:e,severity:"info",message:t,file:s,line:null,harnesses:[],hint:n}}function Ct(e){let t=le(e);return t===null||!ue(t)?[]:t.guidanceMaintenanceScripts.length>0?[]:[ve("guidance-maintenance-script","No guidance maintenance script was detected.","Expose a script such as check:ai-system, lint:md, check:links, or scan:self so agents and CI can validate AI guidance drift.",t.packageJson.path)]}function xt(e){let t=le(e);return t===null||!ue(t)?[]:t.aiHarnessPrTemplates.length>0?[]:[ve("pr-template-ai-harness-check","No PR template reminder for durable AI harness updates was detected.","Add a PR checklist item for recurring AI mistakes: update rules, docs, skills, tests, or diagnostics when needed.",t.prTemplates[0]??".github/PULL_REQUEST_TEMPLATE.md")]}function Lt(e){let t=le(e);return t===null||!ue(t)?[]:t.guidanceCiWorkflows.length>0?[]:[ve("ci-guidance-lint","No CI guidance-maintenance gate was detected.","Run lint:md, check:ai-system, check:links, scan:self, or an equivalent guidance check in CI.",t.workflows[0]?.path??t.packageJson.path)]}function L(e){return e.intelligenceLayer??null}function de(e){return Object.entries(e.packageJson.scripts).map(([t,n])=>({name:t,command:n}))}function Ie(e,t,n){return de(e).some(s=>t.test(s.name)||n.test(s.command))}function Fs(e,t){return de(e).filter(n=>t.test(n.name)||t.test(n.command)).map(n=>n.name).toSorted()}function Fe(e){return e.workflows.map(t=>t.commands.join(`
72
+ `)).join(`
73
+ `)}function A(e){return e.packageJson.path!==null||e.configs.length>0||e.workflows.length>0}function At(e){let t=L(e);return t!==null&&A(t)}function P(e,t,n,s){return{ruleId:e,severity:"info",message:t,file:s,line:null,harnesses:[],hint:n}}function Rt(e){let t=L(e);if(t===null||!A(t))return[];let n=Ie(t,/(^|:)(typecheck|check:types)(:|$)/i,/\b(tsc\s+--noEmit|vue-tsc|svelte-check|mypy|pyright|cargo\s+check)\b/i),s=t.configs.some(o=>o.capabilities.includes("strict-typecheck")),i=de(t).some(o=>/\b(mypy|pyright|cargo\s+check)\b/i.test(o.command));return n&&(s||i)?[]:[P("strict-typecheck-present","No strict typecheck/static-analysis gate was detected.","Expose a discoverable typecheck script and strict type/static-analysis config so agents can verify structural correctness.",t.packageJson.path)]}function Et(e){let t=L(e);if(t===null||!A(t))return[];let n=Ie(t,/(^|:)(lint|check:lint)(:|$)/i,/\b(eslint|oxlint|biome|ruff)\b/i),s=t.configs.some(i=>i.kind==="lint");return n||s?[]:[P("lint-gate-present","No lint/static-analysis gate was detected.","Expose a lint script or static-analysis config so agents get deterministic correction messages.",t.packageJson.path)]}function vt(e){let t=L(e);if(t===null||!A(t))return[];let n=Ie(t,/(^|:)(test|e2e|integration)(:|$)/i,/\b(vitest|jest|playwright|cypress|pytest|cargo\s+test|go\s+test)\b/i),s=t.configs.some(i=>i.kind==="test");return n||s?[]:[P("test-gates-present","No validation test gate was detected.","Expose discoverable unit, integration, or E2E test scripts so agents can choose a safe verification loop.",t.packageJson.path)]}function It(e){let t=L(e);return t===null||!A(t)?[]:Fs(t,/(^|:)(test|e2e).*(file|focused|changed|related|unit|integration|watch|grep)|(--grep|--filter|--testNamePattern|--findRelatedTests)/i).length>0?[]:[P("focused-test-commands-present","No focused validation command was detected.","Add targeted test scripts for common changed areas or file/grep-based workflows so agents do not over-run broad suites.",t.packageJson.path)]}function Ft(e){let t=L(e);if(t===null||!A(t))return[];let n=Fe(t);return/(typecheck|tsc\s+--noEmit|lint|oxlint|eslint|biome|ruff|check:md|lint:md)/i.test(n)?[]:[P("ci-enforcement-gates","CI workflow enforcement gates were not detected.","Wire typecheck, lint, or static-analysis scripts into CI so AI-facing standards stay enforced.",t.workflows[0]?.path??t.packageJson.path)]}function Ht(e){let t=L(e);if(t===null||!A(t))return[];let n=Fe(t);return/(npm\s+test|npm\s+run\s+test|vitest|jest|playwright|cypress|pytest|cargo\s+test|go\s+test)/i.test(n)?[]:[P("ci-validation-gates","CI workflow validation gates were not detected.","Wire test scripts into CI so generated changes are checked by the same validation loop humans use.",t.workflows[0]?.path??t.packageJson.path)]}function Pt(e){let t=L(e);if(t===null||!A(t))return[];let n=de(t).map(a=>`${a.name} ${a.command}`).join(`
74
+ `),s=Fe(t),i=/(scan:self|check:ai-system|lint:md|check:links|qmd)/i.test(n),o=t.prTemplates.length>0,r=/(scan:self|check:ai-system|lint:md|check:links)/i.test(s);return i&&(o||r)?[]:[P("correction-loop-documented","No durable AI correction-loop signal was detected.","Document when repeated AI mistakes should become rules, docs, skills, tests, hooks, or CI checks, and wire guidance checks into routine validation.",t.prTemplates[0]??t.packageJson.path)]}var Hs=[{id:"github-skills-shadow",pattern:/\.github\/skills\//,message:"use skills/ instead of the removed .github/skills/ path"},{id:"github-agents-shadow",pattern:/\.github\/agents\//,message:"use agents/ instead of the removed .github/agents/ path"},{id:"github-hooks-scripts",pattern:/\.github\/hooks\/scripts\//,message:"use agents/scripts/ instead of .github/hooks/scripts/"},{id:"cursor-skills-shadow",pattern:/\.cursor\/skills\//,message:"use skills/ instead of .cursor/skills/"},{id:"legacy-agent-docs",pattern:/\.agent\/(rules|codebase-map|troubleshooting)\.md/,message:"move legacy .agent docs to docs/ai/ equivalents"}];function Ps(e){return/do not|don't|not create|instead of|removed|never use|avoid|legacy|not use|no longer|there is no|\(not `|not `\.cursor\/skills|use \/skills\/|use \/agents\//i.test(e)}function Ts(e,t){let n=e.split(`
75
+ `);for(let s=0;s<n.length;s+=1){let i=n[s]??"";if(!Ps(i)&&(t.lastIndex=0,t.test(i)))return s+1}return null}function Ns(e,t){return t.lastIndex=0,t.test(e)}function Tt(e){let t=[];for(let n of e.files){if(!n.path.endsWith(".md")&&!n.path.endsWith(".mdc"))continue;let s=f(e,n.path);if(s!==null)for(let i of Hs){let o=Ts(s,i.pattern);if(o!==null){t.push({ruleId:"forbidden-legacy-paths",severity:"warn",message:`${n.path} references a legacy guidance path (${i.id}).`,file:n.path,line:o,harnesses:[],hint:i.message});continue}for(let r of M(s))Ns(r.href,i.pattern)&&t.push({ruleId:"forbidden-legacy-paths",severity:"warn",message:`${n.path} links to a legacy guidance path (${i.id}).`,file:n.path,line:ae(s,r.href),harnesses:[],hint:i.message})}}return t}function pe(e){return e.intelligenceLayer??null}function Nt(e){let t=pe(e);return t===null?!1:e.files.some(n=>n.path==="AGENTS.md"||n.path.startsWith("docs/ai/")||n.path.startsWith("skills/")||n.path.startsWith("agents/"))||t.memoryDocs.length>0||t.nestedAgentsFiles.length>0}function Dt(e){let t=pe(e);return t!==null&&t.memoryDocs.length>0}function Mt(e){return Nt(e)}function Ds(e){return Object.entries(e.packageJson.scripts).some(([t,n])=>/qmd|search/i.test(t)||/qmd|search/i.test(n))}function $t(e){let t=pe(e);if(t===null||t.memoryDocs.length===0)return[];let n=t.memoryIndexes.length>0,i=!(t.packageJson.path!==null)||Ds(t);return n&&i?[]:[{ruleId:"memory-docs-indexed",severity:"info",message:"Durable memory docs should be indexed and searchable.",file:t.memoryDocs[0]??null,line:null,harnesses:[],hint:"Add a memory index such as docs/ai/codebase-map.md or docs/ai/ai-system.md, and expose a qmd/search script when package.json exists."}]}function jt(e){let t=pe(e);if(t===null||!Nt(e))return[];let n=t.localContextConventions.join(`
76
+ `),s=/naming|codebase-map|local-context/i.test(n),i=/comment|codebase-map|local-context/i.test(n);return t.nestedAgentsFiles.length>0||s&&i?[]:[{ruleId:"local-context-patterns",severity:"info",message:"Local context conventions for AI-readable code were not detected.",file:"docs/ai",line:null,harnesses:[],hint:'Document naming conventions and useful "why" comment patterns, or add nested AGENTS.md files for subprojects.'}]}var Ms=[wt,Tt,Rt,Et,vt,It,Ft,Ht,Pt,Ct,xt,Lt,$t,jt];function Gt(e){let t=[];for(let n of Ms)t.push(...n(e));return t}function _t(e,t,n,s){let i=t?.[n];if(w(i)&&Object.keys(i).some(l=>l.startsWith(s)))return!0;if(e===null)return!1;let o=n.replaceAll(".",String.raw`\.`);return new RegExp(`"${o}"\\s*:\\s*\\{[^}]*"${s}[^"]*"`).test(e)}async function Ot(e){let t=await H(e.repoRoot),n=[];return _t(t.settingsRaw,t.settings,"chat.agentSkillsLocations","skills")||n.push({ruleId:"vscode-skills-location",severity:"warn",message:"chat.agentSkillsLocations should point at skills/ in .vscode/settings.json.",file:".vscode/settings.json",line:null,harnesses:["cursor","copilot"],hint:'Set "chat.agentSkillsLocations": { "skills/": true }.'}),_t(t.settingsRaw,t.settings,"chat.agentFilesLocations","agents")||n.push({ruleId:"vscode-agents-location",severity:"warn",message:"chat.agentFilesLocations should point at agents/ in .vscode/settings.json.",file:".vscode/settings.json",line:null,harnesses:["cursor","copilot"],hint:'Set "chat.agentFilesLocations": { "agents/": true }.'}),n}function Wt(e,t){return e.length===0?!0:e.some(n=>t.includes(n))}function He(e,t,n,s){return{ruleId:e,severity:"warn",message:`${t} has ${String(n)} lines; keep root adapters thin (max ${String(v)}).`,file:t,line:null,harnesses:[s],hint:"Move detailed guidance to docs/ai/ and skills/; keep the adapter as a pointer."}}function $s(e){let t=[];for(let n of X(e,"skills/")){if(!n.endsWith("/SKILL.md")&&!n.endsWith("SKILL.md"))continue;let s=f(e,n);if(s===null)continue;let i=me(s);if(i===null){t.push({ruleId:"skill-frontmatter",severity:"error",message:`${n} is missing YAML frontmatter.`,file:n,line:null,harnesses:[]});continue}G(i,"name")||t.push({ruleId:"skill-frontmatter",severity:"error",message:`${n} is missing a name field in frontmatter.`,file:n,line:null,harnesses:[]}),G(i,"description")||t.push({ruleId:"skill-frontmatter",severity:"error",message:`${n} is missing a description field in frontmatter.`,file:n,line:null,harnesses:[]})}return t}function js(e){let t=[];for(let n of X(e,"skills/")){if(!n.endsWith("SKILL.md"))continue;let s=I(e,n);s!==null&&s>ge&&t.push({ruleId:"skill-line-count",severity:"warn",message:`${n} has ${String(s)} lines; keep skills under ${String(ge)}.`,file:n,line:null,harnesses:[],hint:"Move detail to docs/; keep SKILL.md as a pointer."})}return t}function Gs(e){let t=[];for(let n of X(e,"agents/")){if(!n.endsWith(".agent.md"))continue;let s=f(e,n);if(s===null)continue;let i=me(s);if(i===null){t.push({ruleId:"agent-frontmatter",severity:"error",message:`${n} is missing YAML frontmatter.`,file:n,line:null,harnesses:[]});continue}G(i,"name")||t.push({ruleId:"agent-frontmatter",severity:"error",message:`${n} is missing a name field in frontmatter.`,file:n,line:null,harnesses:[]}),G(i,"description")||t.push({ruleId:"agent-frontmatter",severity:"error",message:`${n} is missing a description field in frontmatter.`,file:n,line:null,harnesses:[]})}return t}function _s(e){let t=[];for(let n of Y){if(!y(e,n))continue;let s=f(e,n);s===null||Be(s)||t.push({ruleId:"adapter-points-to-shared",severity:"warn",message:`${n} should reference AGENTS.md and docs/ai/rules.md.`,file:n,line:null,harnesses:[],hint:"Keep adapters thin; point at shared layers instead of duplicating rules."})}return t}async function Os(e){if(!e.files.some(i=>i.path.startsWith("skills/")))return[];let n=[],s=[".github/skills",".cursor/skills"];for(let i of s)await _(e.repoRoot,i)&&n.push({ruleId:"no-duplicate-skill-trees",severity:"error",message:`${i}/ duplicates root skills/; use skills/ only.`,file:i,line:null,harnesses:[],hint:"Remove the shadow tree and keep canonical skills/ at the repo root."});return n}async function Ws(e){return e.files.some(n=>n.path.startsWith("agents/"))?await _(e.repoRoot,".github/agents")?[{ruleId:"no-duplicate-agent-trees",severity:"error",message:".github/agents/ duplicates root agents/; use agents/ only.",file:".github/agents",line:null,harnesses:[],hint:"Remove .github/agents/ and keep canonical agents/ at the repo root."}]:[]:[]}async function Us(e){let t=await H(e.repoRoot);return t.hooksDirExists?t.settings?.["chat.useCustomAgentHooks"]===!0?[]:[{ruleId:"vscode-custom-hooks",severity:"warn",message:".github/hooks/ exists but chat.useCustomAgentHooks is not true in .vscode/settings.json.",file:".vscode/settings.json",line:null,harnesses:["cursor","copilot"],hint:'Set "chat.useCustomAgentHooks": true so Cursor and Copilot load workspace hooks.'}]:[]}var qs={id:"shared-agents-md",title:"Shared AGENTS.md entry point",severity:"warn",category:"structure",dimension:"layering",harnesses:[],check(e){return y(e,"AGENTS.md")?[]:[{ruleId:"shared-agents-md",severity:"warn",message:"AGENTS.md is missing.",file:null,line:null,harnesses:[],hint:"Add AGENTS.md as the repo-wide AI entry point with workflow, safety, and pointers to canonical docs."}]}},Bs={id:"shared-rules-doc",title:"Canonical rules doc",severity:"warn",category:"structure",dimension:"layering",harnesses:[],check(e){return y(e,"docs/ai/rules.md")?[]:[{ruleId:"shared-rules-doc",severity:"warn",message:"docs/ai/rules.md is missing.",file:null,line:null,harnesses:[],hint:"Add docs/ai/rules.md as the canonical coding-rules reference."}]}},Vs={id:"adapter-thin-claude",title:"Thin CLAUDE.md adapter",severity:"warn",category:"adapter",dimension:"layering",harnesses:["claude"],check(e){let t=I(e,"CLAUDE.md");return t===null||t<=v?[]:[He("adapter-thin-claude","CLAUDE.md",t,"claude")]}},zs={id:"adapter-thin-gemini",title:"Thin GEMINI.md adapter",severity:"warn",category:"adapter",dimension:"layering",harnesses:["gemini"],check(e){let t=I(e,"GEMINI.md");return t===null||t<=v?[]:[He("adapter-thin-gemini","GEMINI.md",t,"gemini")]}},Js={id:"adapter-thin-copilot",title:"Thin Copilot instructions",severity:"warn",category:"adapter",dimension:"layering",harnesses:["copilot"],check(e){let t=".github/copilot-instructions.md",n=I(e,t);return n===null||n<=v?[]:[He("adapter-thin-copilot",t,n,"copilot")]}},Ks={id:"adapter-points-to-shared",title:"Adapters reference shared layers",severity:"warn",category:"adapter",dimension:"layering",harnesses:[],check:_s},Ys={id:"skill-frontmatter",title:"Skill frontmatter",severity:"error",category:"skills",dimension:"maintainability",harnesses:[],check:$s},Xs={id:"skill-line-count",title:"Skill line budget",severity:"warn",category:"skills",dimension:"maintainability",harnesses:[],check:js},Qs={id:"agent-frontmatter",title:"Agent frontmatter",severity:"error",category:"agents",dimension:"maintainability",harnesses:[],check:Gs},Zs={id:"skills-index",title:"Skill slug index",severity:"info",category:"discoverability",dimension:"discoverability",harnesses:[],check:dt},ei={id:"claude-agent-routing",title:"Claude agent routing table",severity:"info",category:"discoverability",dimension:"discoverability",harnesses:["claude"],check:pt},ti={id:"agents-md-mentions-skills",title:"AGENTS.md skill discovery",severity:"info",category:"discoverability",dimension:"discoverability",harnesses:[],check:ft},ni={id:"skill-doc-deep-links",title:"Skills deep-link into docs",severity:"info",category:"discoverability",dimension:"discoverability",harnesses:[],check:gt},Ut=[qs,Bs,Vs,zs,Js,Ks,Ys,Xs,Qs,Zs,ei,ti,ni];function si(e,t,n){return Wt(e.harnesses,t.harnesses)?n===null||n.length===0||e.harnesses.length===0?!0:e.harnesses.some(s=>n.includes(s)):!1}function ii(e,t,n){return Wt(n,e.harnesses)?t===null||t.length===0?!0:n.some(s=>t.includes(s)):!1}async function qt(e,t){let n=[];for(let l of Ut)si(l,e,t)&&n.push(...l.check(e));if(ii(e,t,["cursor","copilot"])){let l=await Ot(e);n.push(...l);let d=await Us(e);n.push(...d)}let s=await Os(e);n.push(...s);let i=await Ws(e);n.push(...i);let o=await mt(e);n.push(...o);let r=await kt(e);n.push(...r),n.push(...Gt(e));let a=[...ct(e),...at(e)];return t===null||t.length===0?n.push(...a):n.push(...a.filter(l=>l.harnesses.some(d=>t.includes(d)))),n}var ri=new Set(["skills-index","agents-md-mentions-skills","skill-doc-deep-links"]),oi=new Set(["strict-typecheck-present","lint-gate-present","test-gates-present","focused-test-commands-present","ci-enforcement-gates","ci-validation-gates","correction-loop-documented"]),ai=new Set(["adapter-context-budget","always-loaded-budget"]),ci=new Set(["guidance-maintenance-script","pr-template-ai-harness-check","ci-guidance-lint"]);function Pe(e,t){return ri.has(e)?S(t):e==="qmd-script-present"?li(t):e==="claude-agent-routing"?t.harnesses.includes("claude")&&y(t,"CLAUDE.md"):oi.has(e)?At(t):ai.has(e)?ot(t):ci.has(e)?bt(t):e==="memory-docs-indexed"?Dt(t):e==="local-context-patterns"?Mt(t):!0}function li(e){return S(e)?!0:e.files.filter(n=>n.path.startsWith("docs/ai/")).length>=2}var Te={"shared-agents-md":"layering","shared-rules-doc":"layering","adapter-thin-claude":"layering","adapter-thin-gemini":"layering","adapter-thin-copilot":"layering","adapter-points-to-shared":"layering","skill-frontmatter":"maintainability","skill-line-count":"maintainability","agent-frontmatter":"maintainability","no-duplicate-skill-trees":"maintainability","no-duplicate-agent-trees":"maintainability","vscode-skills-location":"harnessWiring","vscode-agents-location":"harnessWiring","vscode-custom-hooks":"guardrails","skills-index":"discoverability","claude-agent-routing":"discoverability","agents-md-mentions-skills":"discoverability","skill-doc-deep-links":"discoverability","qmd-script-present":"discoverability","guidance-links-resolve":"maintainability","adapter-content-duplication":"sharing","forbidden-legacy-paths":"maintainability","strict-typecheck-present":"guardrails","lint-gate-present":"guardrails","test-gates-present":"guardrails","focused-test-commands-present":"discoverability","ci-enforcement-gates":"guardrails","ci-validation-gates":"guardrails","correction-loop-documented":"maintainability","guidance-maintenance-script":"maintainability","pr-template-ai-harness-check":"maintainability","ci-guidance-lint":"guardrails","adapter-context-budget":"sharing","always-loaded-budget":"sharing","memory-docs-indexed":"discoverability","local-context-patterns":"maintainability"};function Bt(e){return Te[e]??null}var ui=[{min:85,grade:"excellent"},{min:70,grade:"good"},{min:50,grade:"fair"},{min:0,grade:"poor"}];function $(e){for(let{min:t,grade:n}of ui)if(e>=t)return n;return"poor"}function Vt(e,t){return t===0?0:e>=80?95:e>=65?82:e>=50?68:e>=30?52:35}var di=["layering","sharing","discoverability","harnessWiring","maintainability","guardrails"],Ne={error:15,warn:6,info:2},pi=35;function fi(e){let t=e.filter(s=>s.detected&&s.score!==null);if(t.length===0)return 0;let n=t.reduce((s,i)=>s+(i.score??0),0);return Math.round(n/t.length)}function Jt(e){return Object.entries(Te).filter(([,t])=>t===e).map(([t])=>t)}function De(e,t){return e.filter(n=>Bt(n.ruleId)===t)}function gi(e){let t=0;for(let n of e)n.severity==="error"?t+=Ne.error:n.severity==="warn"?t+=Ne.warn:t+=Ne.info;return Math.min(t,pi)}function zt(e,t,n){let s=Jt(e).filter(l=>Pe(l,n)),i=s.length,o=new Set(De(t,e).map(l=>l.ruleId)),r=s.filter(l=>!o.has(l)).length,a=i===0?0:Math.round(r/i*100);return{score:a,grade:$(a),passedRules:r,applicableRules:i}}function mi(e,t,n){let s=Jt("sharing").filter(a=>Pe(a,t)),i=new Set(De(e,"sharing").map(a=>a.ruleId)),o=n.totals.lines>0,r=n.shared.percentLines>=65;return{passedRules:s.filter(a=>!i.has(a)).length+(r?1:0),applicableRules:s.length+(o?1:0)}}function hi(){let e={score:0,grade:$(0),passedRules:0,applicableRules:0};return{layering:{...e},sharing:{...e},discoverability:{...e},harnessWiring:{...e},maintainability:{...e},guardrails:{...e}}}function Me(e,t,n,s){let i=hi();for(let o of di){if(o==="sharing"){let r=Vt(t.shared.percentLines,t.totals.lines),a=gi(De(e,o)),l=Math.max(0,Math.min(100,r-a)),d=mi(e,s,t);i.sharing={score:l,grade:$(l),passedRules:d.passedRules,applicableRules:d.applicableRules};continue}if(o==="harnessWiring"){let r=zt(o,e,s),a=fi(n),l=a===0?r.score:Math.round((r.score+a)/2),d=Math.max(0,Math.min(100,l));i.harnessWiring={score:d,grade:$(d),passedRules:r.passedRules,applicableRules:r.applicableRules};continue}i[o]=zt(o,e,s)}return{profile:"meta-harness",dimensions:i}}import{readFile as Kt,readdir as yi,stat as Yt}from"node:fs/promises";import z from"node:path";var ki=[".md",".mdc"],Si=["AGENTS.md","docs/ai/rules.md","docs/ai/ai-system.md","docs/ai/available-skills.md","docs/ai/hooks.md"],wi=[{path:"CLAUDE.md",harness:"claude"},{path:"GEMINI.md",harness:"gemini"},{path:".github/copilot-instructions.md",harness:"copilot"},{path:".agent/README.md",harness:"antigravity"}],bi=[{dir:"skills",layer:"skills"},{dir:"agents",layer:"agents"},{dir:".cursor/rules",layer:"adapter",harness:"cursor"},{dir:".agent/workflows",layer:"workflows",harness:"antigravity"},{dir:".codex",layer:"adapter",harness:"codex"},{dir:"docs/ai",layer:"shared"}];function Ci(e){return ki.some(t=>e.endsWith(t))}function xi(e){return e.length===0?0:e.split(`
77
+ `).length}async function Xt(e){let t=[],n=[];try{n=await yi(e)}catch{return t}for(let s of n){let i=z.join(e,s),o=await Yt(i);o.isDirectory()?t.push(...await Xt(i)):o.isFile()&&Ci(i)&&t.push(i)}return t}async function Li(e){let{repoRoot:t,relativePath:n,layer:s,harness:i}=e,o=z.join(t,n);try{let r=await Yt(o);if(!r.isFile())return null;let a=await Kt(o,"utf8"),l={path:n,layer:s,lines:xi(a),bytes:r.size};return i!==void 0&&(l.harness=i),l}catch{return null}}async function $e(e){let t=[],n=new Map,s=new Set;async function i(o,r,a){let l=o.replaceAll("\\","/");if(s.has(l))return;let d=await Li({repoRoot:e,relativePath:l,layer:r,harness:a});if(d===null)return;s.add(l),t.push(d);let h=z.join(e,l);try{let p=await Kt(h,"utf8");n.set(l,p)}catch{}}for(let o of Si)await i(o,"shared");for(let o of wi)await i(o.path,"adapter",o.harness);for(let{dir:o,layer:r,harness:a}of bi){let l=z.join(e,o),d=await Xt(l);for(let h of d){let p=z.relative(e,h).replaceAll("\\","/");await i(p,r,a)}}return t.sort((o,r)=>o.path.localeCompare(r.path)),{files:t,contents:n}}import{readFile as Ai}from"node:fs/promises";import Qt from"node:path";import{fileURLToPath as Ri}from"node:url";var Zt=null;function en(){return Zt??=Ei(),Zt}async function Ei(){let e=Qt.resolve(Qt.dirname(Ri(import.meta.url)),"../package.json"),t=await Ai(e,"utf8"),n=JSON.parse(t);if(!vi(n))throw new Error("package.json is missing a string version field");return{version:n.version}}function vi(e){return typeof e=="object"&&e!==null&&"version"in e&&typeof e.version=="string"}import{readFile as Ii,readdir as Fi,stat as Ge}from"node:fs/promises";import T from"node:path";var Hi=new Set([".git","node_modules","dist","coverage"]),Pi=[/^tsconfig(?:\..+)?\.json$/],Ti=[/^eslint\.config\.[cm]?[jt]s$/,/^\.eslintrc(?:\..+)?$/,/^\.oxlintrc(?:\..+)?$/,/^biome\.jsonc?$/,/^ruff\.toml$/,/^\.ruff\.toml$/],Ni=[/^\.prettierrc(?:\..+)?$/,/^\.markdownlint(?:rc)?(?:\..+)?$/],Di=[/^vitest\.config\.[cm]?[jt]s$/,/^jest\.config\.[cm]?[jt]s$/,/^playwright\.config\.[cm]?[jt]s$/,/^cypress\.config\.[cm]?[jt]s$/],Mi=[/^package\.json$/,/^pyproject\.toml$/,/^Cargo\.toml$/,/^go\.mod$/],tn=/(scan:self|check:ai-system|check:md|lint:md|check:links|qmd|markdownlint|textlint|remark|cspell|paniolo-scan)/i,$i=/(correction loop|intelligence layer|diagnostic rule|rules catalog|canonical rules|paired skill|guidance hygiene|adapters stayed thin|json output keys are stable)/i;function ji(e){return e.length===0?0:e.split(`
78
+ `).length}async function Gi(e,t){try{return(await Ge(T.join(e,t))).isFile()}catch{return!1}}async function K(e,t){try{return await Ii(T.join(e,t),"utf8")}catch{return null}}async function nn(e,t=""){let n=T.join(e,t),s=[];try{s=await Fi(n)}catch{return[]}let i=[];for(let o of s){if(Hi.has(o))continue;let r=T.join(t,o).replaceAll("\\","/"),a=T.join(e,r),l=await Ge(a);l.isDirectory()?i.push(...await nn(e,r)):l.isFile()&&i.push(r)}return i}function _i(e){if(e===null)return{path:null,scripts:{},dependencies:[],devDependencies:[]};try{let t=JSON.parse(e);if(!sn(t))return{path:"package.json",scripts:{},dependencies:[],devDependencies:[]};let n=je(t.scripts),s=Object.keys(je(t.dependencies)).toSorted(),i=Object.keys(je(t.devDependencies)).toSorted();return{path:"package.json",scripts:n,dependencies:s,devDependencies:i}}catch{return{path:"package.json",scripts:{},dependencies:[],devDependencies:[]}}}function sn(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function je(e){if(!sn(e))return{};let t={};for(let[n,s]of Object.entries(e))typeof s=="string"&&(t[n]=s);return t}function J(e,t){return t.some(n=>n.test(e))}function Oi(e){let t=T.posix.basename(e);return J(t,Pi)?{path:e,kind:"typecheck",capabilities:["typecheck"]}:J(t,Ti)?{path:e,kind:"lint",capabilities:["lint"]}:J(t,Ni)?{path:e,kind:"format",capabilities:["format"]}:J(t,Di)?{path:e,kind:"test",capabilities:["test"]}:J(t,Mi)?{path:e,kind:"manifest",capabilities:["project-manifest"]}:null}function Wi(e){let t=[],n=/^\s*-\s*run:\s*(.+)$/,s=/^\s*-\s*run:\s*[>|]/,i=!1;for(let o of e.split(/\r?\n/)){let r=o.match(n);if(r!==null){t.push(r[1]?.trim()??""),i=!1;continue}if(s.test(o)){i=!0;continue}i&&(/^\s{6,}\S/.test(o)?t.push(o.trim()):/^\s*-\s/.test(o)&&(i=!1))}return t.filter(o=>o.length>0)}function Ui(e){return/^\.github\/workflows\/.+\.ya?ml$/.test(e)}function qi(e){return e===".github/PULL_REQUEST_TEMPLATE.md"||/^\.github\/PULL_REQUEST_TEMPLATE\/.+\.md$/.test(e)||/^docs\/pull_request_template\.md$/i.test(e)}function rn(e,t){return tn.test(`${e}
79
+ ${t}`)}function on(e){return tn.test(e.commands.join(`
80
+ `))}function Bi(e){return $i.test(e)}function Vi(e){let t=e.toLowerCase();return t==="docs/architecture.md"||t==="docs/overview.md"||t==="docs/meta-harness.md"||t==="docs/ai/codebase-map.md"||t==="docs/ai/ai-system.md"||t.includes("/adr")||t.includes("architecture")||t.includes("codebase-map")||t.includes("design")}function zi(e){let t=e.toLowerCase();return t==="docs/ai/codebase-map.md"||t==="docs/ai/ai-system.md"||t==="docs/overview.md"||t.endsWith("/adr/index.md")||t.endsWith("/adrs/index.md")}function Ji(e){let t=e.toLowerCase();return t.includes("naming")||t.includes("comment")||t.includes("local-context")||t.includes("codebase-map")}async function j(e,t,n,s,i){let o=T.join(e,t),r=await Ge(o),a=await K(e,t),l={path:t,kind:n,layer:s,capabilities:i,bytes:r.size};return a!==null&&(l.lines=ji(a)),l}async function Ki(e,t){let n=await K(e,t);return n===null?null:{path:t,commands:Wi(n),content:n}}function an(e){let t=Object.keys(e.packageJson.scripts),n=t.filter(r=>/(^|:)(typecheck|lint|format|check)(:|$)/i.test(r)),s=t.filter(r=>/(^|:)(test|e2e)(:|$)/i.test(r)),i=t.filter(r=>rn(r,e.packageJson.scripts[r]??"")),o=e.workflows.filter(on).map(r=>r.path).toSorted();return{surfaces:e.surfaces,enforcement:{scripts:n.toSorted(),configs:e.configs.filter(r=>r.kind==="typecheck"||r.kind==="lint").map(r=>r.path).toSorted(),ciWorkflows:e.workflows.filter(r=>/(typecheck|lint|format|check)/i.test(r.commands.join(`
81
+ `))).map(r=>r.path).toSorted()},validation:{scripts:s.toSorted(),configs:e.configs.filter(r=>r.kind==="test").map(r=>r.path).toSorted(),ciWorkflows:e.workflows.filter(r=>/(npm test|npm run test|vitest|jest|playwright|cypress)/i.test(r.commands.join(`
82
+ `))).map(r=>r.path).toSorted()},memory:{docs:e.memoryDocs.toSorted(),indexes:e.memoryIndexes.toSorted()},localContext:{conventions:e.localContextConventions.toSorted(),nestedAgentsFiles:e.nestedAgentsFiles.toSorted()},correctionLoop:{scripts:i.toSorted(),prTemplates:e.prTemplates.toSorted(),ciWorkflows:o,maintenanceScripts:e.guidanceMaintenanceScripts.toSorted(),prTemplateChecks:e.aiHarnessPrTemplates.toSorted(),guidanceCiWorkflows:o}}}async function _e(e){let t=await nn(e),n=_i(await K(e,"package.json")),s=[],i=[],o=[],r=[],a=[],l=[],d=[],h=[],p=[];for(let u of t){let g=Oi(u);if(g!==null){let m={...g};if(g.kind==="typecheck"){let C=await K(e,u);C!==null&&/"strict"\s*:\s*true/.test(C)&&(m.capabilities=[...g.capabilities,"strict-typecheck"])}s.push(m),p.push(await j(e,u,"enforcement","config",m.capabilities))}if(Ui(u)){let m=await Ki(e,u);if(m!==null){i.push(m);let C=await j(e,u,"workflow","automation",["ci-workflow"]);p.push(C)}}if(qi(u)){o.push(u);let m=await K(e,u);m!==null&&Bi(m)&&r.push(u);let C=await j(e,u,"automation","pull-request",["pr-template"]);p.push(C)}if(u.endsWith(".md")&&Vi(u)&&(a.push(u),zi(u)&&l.push(u),p.push(await j(e,u,"memory","docs",["memory"]))),u.endsWith(".md")&&Ji(u)&&d.push(u),u.endsWith("/AGENTS.md")){h.push(u);let m=await j(e,u,"local-context","nested-entry",["nested-agents-md"]);p.push(m)}}return n.path!==null&&await Gi(e,n.path)&&p.push(await j(e,n.path,"automation","project",["package-scripts"])),{packageJson:n,configs:s.toSorted((u,g)=>u.path.localeCompare(g.path)),workflows:i.toSorted((u,g)=>u.path.localeCompare(g.path)),prTemplates:o,guidanceMaintenanceScripts:Object.entries(n.scripts).filter(([u,g])=>rn(u,g)).map(([u])=>u).toSorted(),aiHarnessPrTemplates:r.toSorted(),guidanceCiWorkflows:i.filter(u=>on(u)).map(u=>u.path).toSorted(),memoryDocs:a,memoryIndexes:l.toSorted(),localContextConventions:d.toSorted(),nestedAgentsFiles:h,surfaces:p.toSorted((u,g)=>u.path.localeCompare(g.path))}}var cn={error:3,warn:2,info:1};function Xi(e){let t=0,n=0,s=0;for(let i of e)i.severity==="error"?t+=1:i.severity==="warn"?n+=1:s+=1;return{error:t,warn:n,info:s}}async function Oe(e){let t=Yi.resolve(e.repoRoot),n=en(),s=await we(t),{files:i,contents:o}=await $e(t),r=await _e(t),a={repoRoot:t,harnesses:s,files:i,fileContents:o,intelligenceLayer:r},l=be(a);a.contextBudget=l;let d=await qt(a,e.harnessFilter),h=Xi(d),p=Se(i,s),u=await H(t),g=await Ae(a,u,d,p),m=Me(d,p,g,a),C=await Ze(a,u),dn=await n,pn=an(r);return{version:dn.version,root:t,harnesses:s,inventory:{fileCount:i.length,files:i},sharing:p,metaHarness:m,harnessGapMatrix:C,harnessOptimization:g,intelligenceLayer:pn,contextBudget:l,findings:d,summary:h}}function ln(e,t){let n=cn[t];return e.some(s=>cn[s.severity]>=n)}var un=0,Zi=1,R=2;function er(){process.stdout.write(`paniolo-scan \u2014 AI harness diagnostic (read-only)
59
83
 
60
84
  Usage:
61
85
  paniolo-scan [options] [path]
@@ -70,12 +94,12 @@ Examples:
70
94
  npx @paniolo/scan
71
95
  npx @paniolo/scan --format json
72
96
  npx @paniolo/scan --harness cursor,codex --fail-on warn
73
- `)}function hs(e){let n=e.split(",").map(i=>i.trim()).filter(i=>i.length>0);if(n.length===0)return null;let t=[],s=[];for(let i of n)i==="copilot"||i==="cursor"||i==="codex"||i==="antigravity"||i==="claude"||i==="gemini"?t.push(i):s.push(i);return s.length>0?(process.stderr.write(`error: unknown harness(es): ${s.join(", ")}
74
- `),process.exit(w),[]):t}function gs(e){return e==="error"||e==="warn"||e==="info"?e:(process.stderr.write(`error: invalid --fail-on value: ${e}
75
- `),process.exit(w),"error")}function ms(e){return e==="terminal"||e==="json"?e:(process.stderr.write(`error: invalid --format value: ${e}
76
- `),process.exit(w),"terminal")}function ys(e){let n=process.cwd(),t=null,s="terminal",i="error",r=!1;for(let o=0;o<e.length;o+=1){let a=e[o];if(a!==void 0){if(a==="-h"||a==="--help"){r=!0;continue}if(a==="--harness"){let c=e[o+1];c===void 0&&(process.stderr.write(`error: --harness requires a value
77
- `),process.exit(w)),t=hs(c),o+=1;continue}if(a==="--format"){let c=e[o+1];c===void 0&&(process.stderr.write(`error: --format requires a value
78
- `),process.exit(w)),s=ms(c),o+=1;continue}if(a==="--fail-on"){let c=e[o+1];c===void 0&&(process.stderr.write(`error: --fail-on requires a value
79
- `),process.exit(w)),i=gs(c),o+=1;continue}a.startsWith("-")&&(process.stderr.write(`error: unknown option: ${a}
80
- `),process.exit(w)),n=ds.resolve(a)}}return{repoRoot:n,harnessFilter:t,format:s,failOn:i,help:r}}async function ks(){let e=ys(process.argv.slice(2));e.help&&(fs(),process.exit(ln));try{let n=await ge({repoRoot:e.repoRoot,harnessFilter:e.harnessFilter});e.format==="json"?B(n):ne(n),an(n.findings,e.failOn)&&process.exit(ps),process.exit(ln)}catch(n){let t=n instanceof Error?n.message:String(n);process.stderr.write(`error: ${t}
81
- `),process.exit(w)}}await ks();
97
+ `)}function tr(e){let t=e.split(",").map(i=>i.trim()).filter(i=>i.length>0);if(t.length===0)return null;let n=[],s=[];for(let i of t)i==="copilot"||i==="cursor"||i==="codex"||i==="antigravity"||i==="claude"||i==="gemini"?n.push(i):s.push(i);return s.length>0?(process.stderr.write(`error: unknown harness(es): ${s.join(", ")}
98
+ `),process.exit(R),[]):n}function nr(e){return e==="error"||e==="warn"||e==="info"?e:(process.stderr.write(`error: invalid --fail-on value: ${e}
99
+ `),process.exit(R),"error")}function sr(e){return e==="terminal"||e==="json"?e:(process.stderr.write(`error: invalid --format value: ${e}
100
+ `),process.exit(R),"terminal")}function ir(e){let t=process.cwd(),n=null,s="terminal",i="error",o=!1;for(let r=0;r<e.length;r+=1){let a=e[r];if(a!==void 0){if(a==="-h"||a==="--help"){o=!0;continue}if(a==="--harness"){let l=e[r+1];l===void 0&&(process.stderr.write(`error: --harness requires a value
101
+ `),process.exit(R)),n=tr(l),r+=1;continue}if(a==="--format"){let l=e[r+1];l===void 0&&(process.stderr.write(`error: --format requires a value
102
+ `),process.exit(R)),s=sr(l),r+=1;continue}if(a==="--fail-on"){let l=e[r+1];l===void 0&&(process.stderr.write(`error: --fail-on requires a value
103
+ `),process.exit(R)),i=nr(l),r+=1;continue}a.startsWith("-")&&(process.stderr.write(`error: unknown option: ${a}
104
+ `),process.exit(R)),t=Qi.resolve(a)}}return{repoRoot:t,harnessFilter:n,format:s,failOn:i,help:o}}async function rr(){let e=ir(process.argv.slice(2));e.help&&(er(),process.exit(un));try{let t=await Oe({repoRoot:e.repoRoot,harnessFilter:e.harnessFilter});e.format==="json"?fe(t):ke(t),ln(t.findings,e.failOn)&&process.exit(Zi),process.exit(un)}catch(t){let n=t instanceof Error?t.message:String(t);process.stderr.write(`error: ${n}
105
+ `),process.exit(R)}}await rr();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paniolo/scan",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Scan a repository for AI coding agent harness best practices (diagnostic only)",
5
5
  "keywords": [
6
6
  "agents",
@@ -29,20 +29,35 @@
29
29
  "lint:fix": "oxlint --config .oxlintrc.json --type-aware --fix src vitest.config.ts && oxlint --config .oxlintrc.json --type-aware --fix scripts",
30
30
  "pack:dry-run": "npm pack --dry-run",
31
31
  "prepack": "npm run build",
32
- "qmd": "node scripts/qmd.mjs",
32
+ "qmd": "node scripts/qmd/qmd.mjs",
33
33
  "scan:self": "node dist/cli.js . --fail-on warn",
34
34
  "check:ai-system": "npm run build && npm run scan:self",
35
+ "check:md": "npm run check:md:style && npm run check:md:text",
36
+ "check:md:style": "markdownlint-cli2",
37
+ "check:md:text": "textlint --rulesdir textlint-rules --config .textlintrc.json \"docs/**/*.md\" && textlint --config .textlintrc.skills.json \"skills/*/SKILL.md\" \"agents/**/*.md\" && textlint --config .textlintrc.instructions.json \"AGENTS.md\" \"CLAUDE.md\" \"GEMINI.md\" \".github/copilot-instructions.md\"",
38
+ "check:links": "remark --quiet --frail \"docs/**/*.md\" \"skills/**/*.md\" \"agents/**/*.md\" \".github/*.md\" \"*.md\"",
39
+ "check:spelling": "cspell --no-progress --no-summary \"**/*.md\"",
40
+ "lint:md": "npm run check:md && npm run check:links && npm run check:spelling",
35
41
  "start": "node dist/cli.js",
36
42
  "test": "vitest run",
43
+ "test:file": "vitest run",
37
44
  "typecheck": "tsc --noEmit"
38
45
  },
39
46
  "devDependencies": {
47
+ "@tobilu/qmd": "^2.5.3",
40
48
  "@types/node": "^22.19.20",
49
+ "cspell": "^9.2.1",
41
50
  "esbuild": "^0.27.7",
51
+ "markdownlint-cli2": "^0.22.1",
42
52
  "oxfmt": "^0.36.0",
43
53
  "oxlint": "^1.68.0",
44
54
  "oxlint-tsgolint": "^0.23.0",
55
+ "remark-cli": "^12.0.1",
56
+ "remark-validate-links": "^13.1.0",
57
+ "textlint": "^15.7.0",
58
+ "textlint-rule-max-number-of-lines": "^1.0.3",
45
59
  "typescript": "^5.8.3",
60
+ "unist-util-visit": "^5.0.0",
46
61
  "vitest": "^3.2.4"
47
62
  },
48
63
  "engines": {