scip-query 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -716,7 +716,7 @@ ${n}`:""}`)}return JSON.parse(t.stdout)}function lu(e,t){let n=[];for(let r=0;r<
716
716
  Affected consumer files:`),_.list(e.affectedConsumers,t=>` ${t.file} (${t.consumedSymbols} symbol(s))`))}var yu=hs,R=(e=>parseInt(e,10)),ui=(e=>{let t=parseInt(e,10);if(!Number.isFinite(t)||t<1)throw new Error(`Expected a positive integer, got ${e}`);return t}),sr=parseFloat,bs=parseInt;function g(e,t,n,...r){return r.length>0?{flags:e,description:t,parser:n,defaultValue:r[0]}:{flags:e,description:t,parser:n}}function x(e,t=[]){return{category:e,examples:t}}import{existsSync as Rs,mkdirSync as mR,symlinkSync as dR,readlinkSync as pR,unlinkSync as fR}from"fs";import{join as ar,dirname as Cu,resolve as Is}from"path";import{homedir as vs,platform as gR}from"os";import{fileURLToPath as hR}from"url";import{execFileSync as bu}from"child_process";import{platform as Ss,arch as lR}from"os";import{execFileSync as sR}from"child_process";import{platform as aR}from"os";var cR=aR()==="win32";function De(e){let t=cR?"where":"which";try{return sR(t,[e],{stdio:"pipe"}),!0}catch{return!1}}var Su="v0.7.0";function xs(){return De("scip")}function uR(){let e=Ss(),t=lR(),n,r,i;switch(e){case"darwin":n="darwin",i="tar.gz";break;case"linux":n="linux",i="tar.gz";break;case"win32":n="windows",i="zip";break;default:return null}switch(t){case"arm64":r="arm64";break;case"x64":r="amd64";break;default:return null}let o=`scip-${n}-${r}.${i}`;return{url:`https://github.com/sourcegraph/scip/releases/download/${Su}/${o}`,filename:o}}function Cs(){let e=uR();console.log("\nThe `scip` CLI is required but not found on PATH.\n"),Ss()==="darwin"?(console.log("Install via Homebrew:"),console.log(` brew install sourcegraph/scip/scip
717
717
  `),console.log("Or download manually:")):console.log("Download from:"),console.log(e?` ${e.url}
718
718
  `:` https://github.com/sourcegraph/scip/releases/tag/${Su}
719
- `),console.log("After installing, ensure `scip` is on your PATH and run `scip-query reindex`.")}function xu(e){if(Ss()==="darwin"&&De("brew")){e("Installing scip CLI via Homebrew...");try{if(bu("brew",["install","sourcegraph/scip/scip"],{stdio:"inherit",timeout:3e5,env:process.env}),De("scip"))return e("Successfully installed scip CLI via Homebrew"),!0}catch(t){let n=t instanceof Error?t.message:String(t);e(`Homebrew install failed: ${n}`)}}if(De("go")){e("Installing scip CLI via go install...");try{if(bu("go",["install","github.com/sourcegraph/scip/cmd/scip@latest"],{stdio:"inherit",timeout:3e5,env:process.env}),De("scip"))return e("Successfully installed scip CLI via go install"),!0}catch(t){let n=t instanceof Error?t.message:String(t);e(`go install failed: ${n}`)}}return e("Could not auto-install scip CLI."),e("Install manually from: https://github.com/sourcegraph/scip/releases"),!1}var yR=gR()==="win32",Ds=["concrete-plan","scip-ai-cleanup","scip-explore","scip-debloat","scip-maintainability","scip-verify","scip-language-playbook"];function Ru(e={}){let t=e.quiet?()=>{}:console.log,n=hR(import.meta.url),r=Is(Cu(n),"..","skills"),i=[ar(vs(),".claude","skills"),ar(vs(),".codex","skills"),ar(vs(),".agents","skills")],o={installed:[],skipped:[],alreadyLinked:[]};for(let s of i){let a=Cu(s);if(!Rs(a))continue;mR(s,{recursive:!0});let c=bR(s);for(let l of Ds){let u=ar(r,l),m=ar(s,l);if(!Rs(u)){o.skipped.push(`${c}/${l}`);continue}if(Rs(m)){try{let d=pR(m);if(Is(d)===Is(u)){o.alreadyLinked.push(`${c}/${l}`),t(` ok: ${l} \u2192 ${c} (already linked)`);continue}}catch{o.skipped.push(`${c}/${l}`),t(` skip: ${l} \u2192 ${c} (exists, not a symlink)`);continue}fR(m)}dR(u,m,yR?"junction":"dir"),o.installed.push(`${c}/${l}`),t(` done: ${l} \u2192 ${c}`)}}return o}function bR(e){return e.includes(".codex")?"Codex":e.includes(".agents")?"Agents":"Claude"}import{existsSync as Dm}from"fs";import{execFileSync as kv}from"child_process";import{closeSync as Pv,existsSync as qs,mkdirSync as pm,mkdtempSync as Fv,openSync as Tv,readFileSync as Av,renameSync as Vs,rmSync as Ws,writeFileSync as fm}from"fs";import{basename as bi,dirname as Vt,extname as Mv,join as Xe}from"path";import SR from"better-sqlite3";import{execFileSync as xR}from"child_process";import{existsSync as CR,readdirSync as RR,readFileSync as IR}from"fs";import{extname as mi,join as Iu}from"path";function pt(e){let t=new Set((e.extensions??ec).map(o=>o.toLowerCase()));if(t.size===0)return{scanned:0,inserted:0,existing:0};if(!CR(e.dbPath))throw new Error(`SCIP SQLite database not found at ${e.dbPath}`);let n=yn(e.projectRoot),r=vR(e.projectRoot,t).filter(o=>!n.isIgnored(o)),i=new SR(e.dbPath);try{let o=NR(i,r),s=i.prepare(`INSERT OR IGNORE INTO documents (language, relative_path, position_encoding, text)
719
+ `),console.log("After installing, ensure `scip` is on your PATH and run `scip-query reindex`.")}function xu(e){if(Ss()==="darwin"&&De("brew")){e("Installing scip CLI via Homebrew...");try{if(bu("brew",["install","sourcegraph/scip/scip"],{stdio:"inherit",timeout:3e5,env:process.env}),De("scip"))return e("Successfully installed scip CLI via Homebrew"),!0}catch(t){let n=t instanceof Error?t.message:String(t);e(`Homebrew install failed: ${n}`)}}if(De("go")){e("Installing scip CLI via go install...");try{if(bu("go",["install","github.com/sourcegraph/scip/cmd/scip@latest"],{stdio:"inherit",timeout:3e5,env:process.env}),De("scip"))return e("Successfully installed scip CLI via go install"),!0}catch(t){let n=t instanceof Error?t.message:String(t);e(`go install failed: ${n}`)}}return e("Could not auto-install scip CLI."),e("Install manually from: https://github.com/sourcegraph/scip/releases"),!1}var yR=gR()==="win32",Ds=["concrete-plan","scip-ai-cleanup","scip-explore","scip-debloat","scip-doc-reconcile","scip-maintainability","scip-verify","scip-language-playbook"];function Ru(e={}){let t=e.quiet?()=>{}:console.log,n=hR(import.meta.url),r=Is(Cu(n),"..","skills"),i=[ar(vs(),".claude","skills"),ar(vs(),".codex","skills"),ar(vs(),".agents","skills")],o={installed:[],skipped:[],alreadyLinked:[]};for(let s of i){let a=Cu(s);if(!Rs(a))continue;mR(s,{recursive:!0});let c=bR(s);for(let l of Ds){let u=ar(r,l),m=ar(s,l);if(!Rs(u)){o.skipped.push(`${c}/${l}`);continue}if(Rs(m)){try{let d=pR(m);if(Is(d)===Is(u)){o.alreadyLinked.push(`${c}/${l}`),t(` ok: ${l} \u2192 ${c} (already linked)`);continue}}catch{o.skipped.push(`${c}/${l}`),t(` skip: ${l} \u2192 ${c} (exists, not a symlink)`);continue}fR(m)}dR(u,m,yR?"junction":"dir"),o.installed.push(`${c}/${l}`),t(` done: ${l} \u2192 ${c}`)}}return o}function bR(e){return e.includes(".codex")?"Codex":e.includes(".agents")?"Agents":"Claude"}import{existsSync as Dm}from"fs";import{execFileSync as kv}from"child_process";import{closeSync as Pv,existsSync as qs,mkdirSync as pm,mkdtempSync as Fv,openSync as Tv,readFileSync as Av,renameSync as Vs,rmSync as Ws,writeFileSync as fm}from"fs";import{basename as bi,dirname as Vt,extname as Mv,join as Xe}from"path";import SR from"better-sqlite3";import{execFileSync as xR}from"child_process";import{existsSync as CR,readdirSync as RR,readFileSync as IR}from"fs";import{extname as mi,join as Iu}from"path";function pt(e){let t=new Set((e.extensions??ec).map(o=>o.toLowerCase()));if(t.size===0)return{scanned:0,inserted:0,existing:0};if(!CR(e.dbPath))throw new Error(`SCIP SQLite database not found at ${e.dbPath}`);let n=yn(e.projectRoot),r=vR(e.projectRoot,t).filter(o=>!n.isIgnored(o)),i=new SR(e.dbPath);try{let o=NR(i,r),s=i.prepare(`INSERT OR IGNORE INTO documents (language, relative_path, position_encoding, text)
720
720
  VALUES (?, ?, NULL, ?)`),c=i.transaction(u=>{let m=0;for(let d of u){if(o.has(d))continue;let p=IR(Iu(e.projectRoot,d),"utf-8"),h=s.run(LR(d),d,p);m+=Number(h.changes)}return m})(r),l={scanned:r.length,inserted:c,existing:r.length-c};return e.onStatus?.(`Augmented SQLite documents with ${c} auxiliary source file${c===1?"":"s"} (${l.existing} already present).`),l}finally{i.close()}}function vR(e,t){let n=DR(e,t);if(n)return n;let r=[],i=o=>{let s=o?Iu(e,o):e,a;try{a=RR(s,{withFileTypes:!0})}catch{return}for(let c of a){if(zi.has(c.name))continue;let l=o?`${o}/${c.name}`:c.name;if(c.isDirectory()){i(l);continue}t.has(mi(c.name).toLowerCase())&&r.push(l)}};return i(""),r.sort()}function DR(e,t){try{return xR("git",["-C",e,"ls-files","-co","--exclude-standard","--","."],{encoding:"utf-8",maxBuffer:26214400,stdio:["ignore","pipe","ignore"]}).split(`
721
721
  `).filter(r=>r&&t.has(mi(r).toLowerCase())).sort()}catch{return null}}function NR(e,t){let n=new Set,r=500;for(let i=0;i<t.length;i+=r){let o=t.slice(i,i+r),s=e.prepare(`SELECT relative_path FROM documents WHERE relative_path IN (${o.map(()=>"?").join(",")})`).all(...o);for(let a of s)n.add(a.relative_path)}return n}function LR(e){return mi(e).toLowerCase()===".vue"?"vue":mi(e).replace(/^\./,"").toLowerCase()||"source"}import{execFileSync as _R}from"child_process";import{existsSync as wR,readdirSync as Du}from"fs";import{extname as Nu,join as Lu}from"path";var ER=new Set([".git",".hg",".svn",".idea",".vscode","node_modules","vendor","dist","build","target","bin","obj",".dart_tool",".gradle",".next",".venv","venv","__pycache__"]),kR=[{language:"typescript",files:["tsconfig.json","tsconfig.base.json"],extensions:[".ts",".tsx",".mts",".cts"]},{language:"rust",files:["Cargo.toml"],extensions:[".rs"]},{language:"go",files:["go.mod"],extensions:[".go"]},{language:"java",files:["pom.xml","build.gradle","build.gradle.kts"],extensions:[".java"]},{language:"kotlin",files:["build.gradle.kts"],extensions:[".kt",".kts"]},{language:"scala",files:["build.sbt"],extensions:[".scala"]},{language:"python",files:["pyproject.toml","setup.py","setup.cfg","Pipfile"],extensions:[".py",".pyi"]},{language:"ruby",files:["Gemfile"],extensions:[".rb"]},{language:"cpp",files:["compile_commands.json","CMakeLists.txt","Makefile"],extensions:[".cc",".cpp",".cxx",".hpp",".hh",".hxx"]},{language:"c",files:["compile_commands.json","CMakeLists.txt","Makefile"],extensions:[".c",".h"]},{language:"csharp",globs:["*.csproj","*.sln"],extensions:[".cs"]},{language:"vb",globs:["*.vbproj"],extensions:[".vb"]},{language:"dart",files:["pubspec.yaml"],extensions:[".dart"]},{language:"php",files:["composer.json"],extensions:[".php"]},{language:"javascript",files:["package.json"],extensions:[".js",".jsx",".mjs",".cjs"]}];function Ht(e){let t=[],n=PR(e),r=MR(e);for(let i of kR){if(FR(e,i.files)){t.push(i.language);continue}if(TR(n,i.globs)){t.push(i.language);continue}$R(r,i.extensions)&&t.push(i.language)}return t.includes("typescript")&&vu(t,"javascript"),t.includes("cpp")&&!r.has(".c")&&vu(t,"c"),t}function PR(e){try{return Du(e)}catch{return[]}}function FR(e,t){return t?.length?t.some(n=>wR(Lu(e,n))):!1}function TR(e,t){return t?.length?e.some(n=>t.some(r=>AR(n,r))):!1}function AR(e,t){if(t==="*")return!0;if(!t.includes("*"))return e===t;let[n,r]=t.split("*");return e.startsWith(n??"")&&e.endsWith(r??"")}function MR(e){let t=OR(e);if(t)return t;let n=new Set,r=[e];for(;r.length>0;){let i=r.pop();if(!i)continue;let o;try{o=Du(i,{withFileTypes:!0})}catch{continue}for(let s of o){if(s.name.startsWith(".")&&!s.name.endsWith("proj")&&!s.name.endsWith("sln")&&s.isDirectory())continue;let a=Lu(i,s.name);if(s.isDirectory()){ER.has(s.name)||r.push(a);continue}let c=Nu(s.name).toLowerCase();c&&n.add(c)}}return n}function OR(e){try{let t=_R("git",["-C",e,"ls-files","-co","--exclude-standard","--","."],{encoding:"utf-8",maxBuffer:26214400,stdio:["ignore","pipe","ignore"]}),n=new Set;for(let r of t.split(`
722
722
  `)){if(!r)continue;let i=Nu(r).toLowerCase();i&&n.add(i)}return n}catch{return null}}function $R(e,t){return t?.length?t.some(n=>e.has(n)):!1}function vu(e,t){let n=e.indexOf(t);n!==-1&&e.splice(n,1)}import{existsSync as _u,readdirSync as jR}from"fs";import{join as Ns}from"path";var BR={typescript:{language:"typescript",indexerBinary:"scip-typescript",checkCommand:"npx scip-typescript --version",indexArgs:({outputPath:e,pnpmWorkspaces:t,indexerBinary:n})=>{let r=["index","--infer-tsconfig","--output",e,"--no-progress-bar"];return t&&r.splice(1,0,"--pnpm-workspaces"),{binary:n,args:r}},markerFiles:["tsconfig.json"],installMethods:[{label:"npm",prerequisite:"npm",binary:"npm",args:["install","-g","@sourcegraph/scip-typescript"]}],installUrl:"https://github.com/sourcegraph/scip-typescript",bundledNpmPackage:"@sourcegraph/scip-typescript"},javascript:{language:"javascript",indexerBinary:"scip-typescript",checkCommand:"npx scip-typescript --version",indexArgs:({outputPath:e,indexerBinary:t})=>({binary:t,args:["index","--infer-tsconfig","--output",e,"--no-progress-bar"]}),markerFiles:["package.json"],installMethods:[{label:"npm",prerequisite:"npm",binary:"npm",args:["install","-g","@sourcegraph/scip-typescript"]}],installUrl:"https://github.com/sourcegraph/scip-typescript",bundledNpmPackage:"@sourcegraph/scip-typescript"},java:{language:"java",indexerBinary:"scip-java",checkCommand:"scip-java --version",indexArgs:({outputPath:e})=>({binary:"scip-java",args:["index","--output",e]}),markerFiles:["pom.xml","build.gradle"],installMethods:[],installUrl:"https://github.com/sourcegraph/scip-java/releases"},scala:{language:"scala",indexerBinary:"scip-java",checkCommand:"scip-java --version",indexArgs:({outputPath:e})=>({binary:"scip-java",args:["index","--output",e]}),markerFiles:["build.sbt"],installMethods:[],installUrl:"https://github.com/sourcegraph/scip-java/releases"},kotlin:{language:"kotlin",indexerBinary:"scip-java",checkCommand:"scip-java --version",indexArgs:({outputPath:e})=>({binary:"scip-java",args:["index","--output",e]}),markerFiles:["build.gradle.kts"],installMethods:[],installUrl:"https://github.com/sourcegraph/scip-java/releases"},rust:{language:"rust",indexerBinary:"rust-analyzer",checkCommand:"rust-analyzer --version",indexArgs:({outputPath:e})=>({binary:"rust-analyzer",args:["scip",".","--output",e]}),markerFiles:["Cargo.toml"],installMethods:[{label:"rustup",prerequisite:"rustup",binary:"rustup",args:["component","add","rust-analyzer"]}],installUrl:"https://github.com/rust-lang/rust-analyzer"},python:{language:"python",indexerBinary:"scip-python-plus",binaryAliases:["scip-python"],checkCommand:"scip-python-plus --version",indexArgs:({outputPath:e,indexerBinary:t})=>({binary:t,args:["index","--output",e,"--project-name","project"]}),markerFiles:["pyproject.toml","setup.py"],installMethods:[{label:"npm",prerequisite:"npm",binary:"npm",args:["install","-g","scip-python-plus"]}],installUrl:"https://github.com/PlunderStruck/scip-python",bundledNpmPackage:"scip-python-plus"},ruby:{language:"ruby",indexerBinary:"scip-ruby",checkCommand:"scip-ruby --version",indexArgs:({indexerBinary:e})=>({binary:e,args:["--dir","."]}),defaultOutputPath:"index.scip",markerFiles:["Gemfile"],installMethods:[],installUrl:"https://github.com/sourcegraph/scip-ruby/releases"},go:{language:"go",indexerBinary:"scip-go",checkCommand:"scip-go --version",indexArgs:({outputPath:e})=>({binary:"scip-go",args:["--output",e]}),markerFiles:["go.mod"],installMethods:[{label:"go install",prerequisite:"go",binary:"go",args:["install","github.com/sourcegraph/scip-go@latest"]}],installUrl:"https://github.com/sourcegraph/scip-go"},cpp:{language:"cpp",indexerBinary:"scip-clang",checkCommand:"scip-clang --version",indexArgs:({outputPath:e})=>({binary:"scip-clang",args:["--compdb-path","compile_commands.json","--index-output-path",e]}),markerFiles:["CMakeLists.txt","Makefile"],installMethods:[],installUrl:"https://github.com/sourcegraph/scip-clang/releases"},c:{language:"c",indexerBinary:"scip-clang",checkCommand:"scip-clang --version",indexArgs:({outputPath:e})=>({binary:"scip-clang",args:["--compdb-path","compile_commands.json","--index-output-path",e]}),markerFiles:["CMakeLists.txt","Makefile"],installMethods:[],installUrl:"https://github.com/sourcegraph/scip-clang/releases"},csharp:{language:"csharp",indexerBinary:"scip-dotnet",checkCommand:"scip-dotnet --version",indexArgs:({projectRoot:e,outputPath:t})=>({binary:"scip-dotnet",args:["index",wu(e,[".sln",".csproj"])??e,"--output",t,"--working-directory",e]}),markerFiles:["*.csproj","*.sln"],installMethods:[{label:"dotnet",prerequisite:"dotnet",binary:"dotnet",args:["tool","install","--global","scip-dotnet"]}],installUrl:"https://github.com/sourcegraph/scip-dotnet/releases"},vb:{language:"vb",indexerBinary:"scip-dotnet",checkCommand:"scip-dotnet --version",indexArgs:({projectRoot:e,outputPath:t})=>({binary:"scip-dotnet",args:["index",wu(e,[".sln",".vbproj"])??e,"--output",t,"--working-directory",e]}),markerFiles:["*.vbproj","*.sln"],installMethods:[{label:"dotnet",prerequisite:"dotnet",binary:"dotnet",args:["tool","install","--global","scip-dotnet"]}],installUrl:"https://github.com/sourcegraph/scip-dotnet/releases"},dart:{language:"dart",indexerBinary:"scip-dart",checkCommand:"scip-dart --version",indexArgs:({indexerBinary:e,outputPath:t})=>({binary:e,args:["--output",t]}),markerFiles:["pubspec.yaml"],installMethods:[{label:"dart pub",prerequisite:"dart",binary:"dart",args:["pub","global","activate","scip_dart"]}],installUrl:"https://github.com/Workiva/scip-dart/releases"},php:{language:"php",indexerBinary:"scip-php",projectLocalBinaries:["vendor/davidrjenni/scip-php/bin/scip-php","vendor/bin/scip-php"],checkCommand:"scip-php --version",indexArgs:({projectRoot:e,indexerBinary:t})=>{let n=Ns(e,"vendor","bin","scip-php"),r=Ns(e,"vendor","davidrjenni","scip-php","bin","scip-php");return{binary:"php",args:["-d","error_reporting=E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED",_u(r)?r:_u(n)?n:t]}},defaultOutputPath:"index.scip",markerFiles:["composer.json"],installMethods:[],installUrl:"https://github.com/davidrjenni/scip-php/releases"}};function di(e){return BR[e]}function wu(e,t){let n;try{n=jR(e)}catch{return null}for(let r of n)if(t.some(i=>r.endsWith(i)))return Ns(e,r);return null}import{readFileSync as HR,writeFileSync as qR}from"fs";import{create as Ls}from"@bufbuild/protobuf";import{deserializeSCIP as VR,serializeSCIP as WR,DocumentSchema as UR,IndexSchema as GR,SymbolInformationSchema as JR}from"@c4312/scip";function zR(e){if(e.length===0)throw new Error("Cannot merge zero SCIP indexes");if(e.length===1)return e[0];let t=QR(e),n=KR(e.flatMap(i=>i.documents??[])),r=ku(e.flatMap(i=>i.externalSymbols??[]));return Ls(GR,{metadata:t,documents:n,externalSymbols:r})}function Eu(e,t){if(e.length===0)throw new Error("Cannot merge zero SCIP files");let n=e.map(i=>VR(HR(i))),r=zR(n);return qR(t,Buffer.from(WR(r))),{documentCount:r.documents.length,externalSymbolCount:r.externalSymbols.length,inputCount:e.length}}function QR(e){let t=e[0]?.metadata;if(!t)return;let n=t.projectRoot;for(let r of e.slice(1)){let i=r.metadata?.projectRoot;if(n&&i&&i!==n)throw new Error(`Cannot merge SCIP indexes with different project roots: ${n} vs ${i}`)}return t}function KR(e){let t=new Map;for(let n of e){let r=t.get(n.relativePath);if(!r){t.set(n.relativePath,n);continue}t.set(n.relativePath,Ls(UR,{language:r.language||n.language,relativePath:r.relativePath||n.relativePath,occurrences:[...r.occurrences,...n.occurrences],symbols:ku([...r.symbols,...n.symbols]),text:XR(r.text,n.text),positionEncoding:r.positionEncoding||n.positionEncoding}))}return[...t.values()]}function ku(e){let t=new Map;for(let n of e){let r=t.get(n.symbol);if(!r){t.set(n.symbol,n);continue}t.set(n.symbol,Ls(JR,{symbol:r.symbol,documentation:ZR([...r.documentation,...n.documentation]),relationships:YR([...r.relationships,...n.relationships]),kind:r.kind||n.kind,displayName:r.displayName||n.displayName,enclosingSymbol:r.enclosingSymbol||n.enclosingSymbol,signatureDocumentation:r.signatureDocumentation??n.signatureDocumentation}))}return[...t.values()]}function YR(e){let t=new Set,n=[];for(let r of e){let i=[r.symbol,r.isReference?"1":"0",r.isImplementation?"1":"0",r.isTypeDefinition?"1":"0",r.isDefinition?"1":"0"].join("|");t.has(i)||(t.add(i),n.push(r))}return n}function XR(e,t){return e?t?e.length>=t.length?e:t:e:t}function ZR(e){return[...new Set(e)]}import{execFileSync as eI}from"child_process";import{createHash as tI}from"crypto";import{readdirSync as nI,readFileSync as rI}from"fs";import{join as Pu}from"path";function iI(e){return(oI(e)??sI(e)).filter(t=>t&&!Fu(t)).sort()}function pi(e){return iI(e).map(t=>{let n=Pu(e,t);try{let r=rI(n);return{path:t,size:r.byteLength,hash:tI("sha256").update(r).digest("hex")}}catch{return{path:t,size:-1,hash:"unreadable"}}})}function oI(e){try{return eI("git",["-C",e,"ls-files","-co","--exclude-standard","--","."],{encoding:"utf-8",maxBuffer:50*1024*1024,stdio:["ignore","pipe","ignore"]}).split(`
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{b as k,c as g,d as f,e as m}from"./chunk-LWYIGRHR.js";import{existsSync as p,mkdirSync as L,symlinkSync as v,readlinkSync as b,unlinkSync as C}from"fs";import{join as s,dirname as S,resolve as d}from"path";import{homedir as u,platform as q}from"os";import{fileURLToPath as N}from"url";var R=q()==="win32",T=["concrete-plan","scip-ai-cleanup","scip-explore","scip-debloat","scip-maintainability","scip-verify","scip-language-playbook"];function A(n={}){let e=n.quiet?()=>{}:console.log,o=N(import.meta.url),I=d(S(o),"..","skills"),$=[s(u(),".claude","skills"),s(u(),".codex","skills"),s(u(),".agents","skills")],i={installed:[],skipped:[],alreadyLinked:[]};for(let r of $){let h=S(r);if(!p(h))continue;L(r,{recursive:!0});let t=j(r);for(let l of T){let a=s(I,l),c=s(r,l);if(!p(a)){i.skipped.push(`${t}/${l}`);continue}if(p(c)){try{let x=b(c);if(d(x)===d(a)){i.alreadyLinked.push(`${t}/${l}`),e(` ok: ${l} \u2192 ${t} (already linked)`);continue}}catch{i.skipped.push(`${t}/${l}`),e(` skip: ${l} \u2192 ${t} (exists, not a symlink)`);continue}C(c)}v(a,c,R?"junction":"dir"),i.installed.push(`${t}/${l}`),e(` done: ${l} \u2192 ${t}`)}}return i}function j(n){return n.includes(".codex")?"Codex":n.includes(".agents")?"Agents":"Claude"}function y(){console.log("scip-query: installing skills...");let n=A({quiet:!1});if(n.installed.length+n.alreadyLinked.length>0&&console.log(`
2
+ import{b as k,c as g,d as f,e as m}from"./chunk-LWYIGRHR.js";import{existsSync as p,mkdirSync as L,symlinkSync as v,readlinkSync as b,unlinkSync as C}from"fs";import{join as s,dirname as S,resolve as d}from"path";import{homedir as u,platform as q}from"os";import{fileURLToPath as N}from"url";var R=q()==="win32",T=["concrete-plan","scip-ai-cleanup","scip-explore","scip-debloat","scip-doc-reconcile","scip-maintainability","scip-verify","scip-language-playbook"];function A(n={}){let e=n.quiet?()=>{}:console.log,o=N(import.meta.url),I=d(S(o),"..","skills"),$=[s(u(),".claude","skills"),s(u(),".codex","skills"),s(u(),".agents","skills")],t={installed:[],skipped:[],alreadyLinked:[]};for(let r of $){let h=S(r);if(!p(h))continue;L(r,{recursive:!0});let l=j(r);for(let i of T){let a=s(I,i),c=s(r,i);if(!p(a)){t.skipped.push(`${l}/${i}`);continue}if(p(c)){try{let x=b(c);if(d(x)===d(a)){t.alreadyLinked.push(`${l}/${i}`),e(` ok: ${i} \u2192 ${l} (already linked)`);continue}}catch{t.skipped.push(`${l}/${i}`),e(` skip: ${i} \u2192 ${l} (exists, not a symlink)`);continue}C(c)}v(a,c,R?"junction":"dir"),t.installed.push(`${l}/${i}`),e(` done: ${i} \u2192 ${l}`)}}return t}function j(n){return n.includes(".codex")?"Codex":n.includes(".agents")?"Agents":"Claude"}function y(){console.log("scip-query: installing skills...");let n=A({quiet:!1});if(n.installed.length+n.alreadyLinked.length>0&&console.log(`
3
3
  ${n.installed.length} skill(s) installed, ${n.alreadyLinked.length} already linked.`),!k())console.log(`
4
4
  scip CLI not found on PATH. Attempting auto-install...`),m(console.log)||f();else{let o=g();console.log(`
5
5
  scip CLI: ${o??"installed"}`)}console.log("")}y();
package/dist/runtime.js CHANGED
@@ -1,3 +1,3 @@
1
1
  import{b,c as j,d as W}from"./chunk-LWYIGRHR.js";import{a as w}from"./chunk-TKDJQ2WD.js";import{readFileSync as _,writeFileSync as D,existsSync as v,mkdirSync as F}from"fs";import{join as l,resolve as C}from"path";import{createHash as L}from"crypto";import{homedir as $}from"os";var P=".scipquery.json",A={enabled:!1,debounceMs:3e4,cooldownMs:6e4,ignore:[]};function N(i){let t=l(i,P);if(!v(t))return{};try{let e=_(t,"utf-8");return JSON.parse(e)}catch{return{}}}function R(i){return{...A,...i.watch}}function O(i,t){let e=process.env.SCIP_QUERY_CACHE_DIR;if(e)return g(e);if(t?.dbPath)return g(C(i,t.dbPath));let n=process.env.XDG_CACHE_HOME||l($(),".cache"),o=L("sha256").update(C(i)).digest("hex").slice(0,12),r=l(n,"scip-query","projects",o);return g(r)}function f(i,t){let e=O(i,t);return{cacheDir:e,dbPath:l(e,"index.db"),indexPath:l(e,"index.scip"),metaPath:l(e,"meta.json")}}function M(i,t){let e=l(i,P);return v(e)||D(e,JSON.stringify({languages:t,watch:{enabled:!1,debounceMs:3e4,cooldownMs:6e4}},null,2)+`
2
- `),e}function g(i){return F(i,{recursive:!0}),i}import{watch as q}from"fs";import{existsSync as I,renameSync as T}from"fs";import{join as U,relative as H}from"path";import{fork as G}from"child_process";import X from"ignore";var m=class{projectRoot;watchConfig;indexPaths;languages;pnpmWorkspaces;onStatus;onReindexComplete;onError;status={state:"idle"};debounceTimer=null;cooldownTimer=null;dirty=!1;changedFiles=0;reindexInFlight=!1;lastReindexEnd=0;fsWatchers=[];gitignoreFilter;extraIgnore;stopped=!1;constructor(t){this.projectRoot=t.projectRoot,this.watchConfig=R(t.config),this.indexPaths=f(t.projectRoot,t.config),this.languages=t.languages,this.pnpmWorkspaces=t.config.indexer?.typescript?.pnpmWorkspaces??!1,this.onStatus=t.onStatus??(()=>{}),this.onReindexComplete=t.onReindexComplete??(()=>{}),this.onError=t.onError??(e=>console.error(e.message)),this.gitignoreFilter=w(t.projectRoot),this.extraIgnore=X(),this.watchConfig.ignore.length>0&&this.extraIgnore.add(this.watchConfig.ignore)}start(){this.stopped=!1,this.setStatus({state:"idle"});try{let t=q(this.projectRoot,{recursive:!0},(e,s)=>{s&&!this.stopped&&this.handleFileChange(s)});this.fsWatchers.push(t)}catch{this.onError(new Error("Failed to start file watcher. On Linux, you may need to increase inotify limits: sysctl -w fs.inotify.max_user_watches=524288"))}}stop(){this.stopped=!0;for(let t of this.fsWatchers)t.close();this.fsWatchers=[],this.clearDebounceTimer(),this.clearCooldownTimer(),this.setStatus({state:"idle"})}handleFileChange(t){let e=H(this.projectRoot,U(this.projectRoot,t));if(this.gitignoreFilter.isIgnored(e)||this.extraIgnore.ignores(e)||t.endsWith("index.db")||t.endsWith("index.scip")||t.endsWith("index.db.tmp")||t.endsWith(".scipquery.json"))return;if(this.changedFiles++,this.reindexInFlight){this.dirty=!0,this.setStatus({state:"indexing",startedAt:this.status.startedAt});return}if(this.status.state==="cooldown"){this.dirty=!0,this.setStatus({state:"cooldown",until:this.status.until,dirty:!0});return}this.clearDebounceTimer();let s=Date.now()+this.watchConfig.debounceMs;this.setStatus({state:"waiting",changedFiles:this.changedFiles,reindexAt:s}),this.debounceTimer=setTimeout(()=>{this.debounceTimer=null,this.triggerReindex()},this.watchConfig.debounceMs)}triggerReindex(){if(this.reindexInFlight||this.stopped)return;let t=Date.now()-this.lastReindexEnd;if(this.lastReindexEnd>0&&t<this.watchConfig.cooldownMs){let s=this.watchConfig.cooldownMs-t;this.dirty=!0;let n=Date.now()+s;this.setStatus({state:"cooldown",until:n,dirty:!0}),this.cooldownTimer=setTimeout(()=>{this.cooldownTimer=null,this.dirty&&!this.stopped&&(this.dirty=!1,this.triggerReindex())},s);return}this.reindexInFlight=!0,this.dirty=!1,this.changedFiles=0;let e=Date.now();this.setStatus({state:"indexing",startedAt:e}),this.runReindex().then(s=>{if(this.reindexInFlight=!1,this.lastReindexEnd=Date.now(),this.onReindexComplete(s),this.dirty&&!this.stopped){let n=Date.now()+this.watchConfig.cooldownMs;this.setStatus({state:"cooldown",until:n,dirty:!0}),this.cooldownTimer=setTimeout(()=>{this.cooldownTimer=null,this.dirty&&!this.stopped?(this.dirty=!1,this.triggerReindex()):this.setStatus({state:"idle"})},this.watchConfig.cooldownMs)}else this.setStatus({state:"idle"})}).catch(s=>{this.reindexInFlight=!1,this.lastReindexEnd=Date.now(),this.onError(s instanceof Error?s:new Error(String(s))),this.setStatus({state:"idle"})})}runReindex(){return new Promise((t,e)=>{let s=Date.now(),n=this.indexPaths.dbPath+".tmp",o=B(this.indexPaths.indexPath),r=G(new URL("./reindex-worker.js",import.meta.url).pathname,[],{env:{...process.env,SCIP_REINDEX_PROJECT_ROOT:this.projectRoot,SCIP_REINDEX_OUTPUT_SCIP:o,SCIP_REINDEX_OUTPUT_DB:n,SCIP_REINDEX_LANGUAGES:this.languages?.join(",")??"",SCIP_REINDEX_PNPM_WORKSPACES:this.pnpmWorkspaces?"1":""},stdio:"pipe"});r.on("exit",h=>{if(h===0)try{I(n)&&T(n,this.indexPaths.dbPath),I(o)&&T(o,this.indexPaths.indexPath),t(Date.now()-s)}catch(a){e(new Error(`Atomic swap failed: ${a}`))}else e(new Error(`Reindex worker exited with code ${h}`))}),r.on("error",e)})}setStatus(t){this.status=t,this.onStatus(t)}clearDebounceTimer(){this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=null)}clearCooldownTimer(){this.cooldownTimer&&(clearTimeout(this.cooldownTimer),this.cooldownTimer=null)}};function B(i){return i.endsWith(".scip")?i.slice(0,-5)+".tmp.scip":i+".tmp.scip"}import{existsSync as x,mkdirSync as J,symlinkSync as V,readlinkSync as K,unlinkSync as Q}from"fs";import{join as d,dirname as k,resolve as S}from"path";import{homedir as y,platform as Y}from"os";import{fileURLToPath as z}from"url";var Z=Y()==="win32",tt=["concrete-plan","scip-ai-cleanup","scip-explore","scip-debloat","scip-maintainability","scip-verify","scip-language-playbook"];function et(i={}){let t=i.quiet?()=>{}:console.log,e=z(import.meta.url),s=S(k(e),"..","skills"),n=[d(y(),".claude","skills"),d(y(),".codex","skills"),d(y(),".agents","skills")],o={installed:[],skipped:[],alreadyLinked:[]};for(let r of n){let h=k(r);if(!x(h))continue;J(r,{recursive:!0});let a=it(r);for(let c of tt){let u=d(s,c),p=d(r,c);if(!x(u)){o.skipped.push(`${a}/${c}`);continue}if(x(p)){try{let E=K(p);if(S(E)===S(u)){o.alreadyLinked.push(`${a}/${c}`),t(` ok: ${c} \u2192 ${a} (already linked)`);continue}}catch{o.skipped.push(`${a}/${c}`),t(` skip: ${c} \u2192 ${a} (exists, not a symlink)`);continue}Q(p)}V(u,p,Z?"junction":"dir"),o.installed.push(`${a}/${c}`),t(` done: ${c} \u2192 ${a}`)}}return o}function it(i){return i.includes(".codex")?"Codex":i.includes(".agents")?"Agents":"Claude"}export{m as Watcher,j as getScipVersion,M as initProjectConfig,et as installSkills,b as isScipInstalled,N as loadProjectConfig,W as printScipInstallInstructions,f as resolveIndexPaths};
2
+ `),e}function g(i){return F(i,{recursive:!0}),i}import{watch as q}from"fs";import{existsSync as I,renameSync as T}from"fs";import{join as U,relative as H}from"path";import{fork as G}from"child_process";import X from"ignore";var m=class{projectRoot;watchConfig;indexPaths;languages;pnpmWorkspaces;onStatus;onReindexComplete;onError;status={state:"idle"};debounceTimer=null;cooldownTimer=null;dirty=!1;changedFiles=0;reindexInFlight=!1;lastReindexEnd=0;fsWatchers=[];gitignoreFilter;extraIgnore;stopped=!1;constructor(t){this.projectRoot=t.projectRoot,this.watchConfig=R(t.config),this.indexPaths=f(t.projectRoot,t.config),this.languages=t.languages,this.pnpmWorkspaces=t.config.indexer?.typescript?.pnpmWorkspaces??!1,this.onStatus=t.onStatus??(()=>{}),this.onReindexComplete=t.onReindexComplete??(()=>{}),this.onError=t.onError??(e=>console.error(e.message)),this.gitignoreFilter=w(t.projectRoot),this.extraIgnore=X(),this.watchConfig.ignore.length>0&&this.extraIgnore.add(this.watchConfig.ignore)}start(){this.stopped=!1,this.setStatus({state:"idle"});try{let t=q(this.projectRoot,{recursive:!0},(e,s)=>{s&&!this.stopped&&this.handleFileChange(s)});this.fsWatchers.push(t)}catch{this.onError(new Error("Failed to start file watcher. On Linux, you may need to increase inotify limits: sysctl -w fs.inotify.max_user_watches=524288"))}}stop(){this.stopped=!0;for(let t of this.fsWatchers)t.close();this.fsWatchers=[],this.clearDebounceTimer(),this.clearCooldownTimer(),this.setStatus({state:"idle"})}handleFileChange(t){let e=H(this.projectRoot,U(this.projectRoot,t));if(this.gitignoreFilter.isIgnored(e)||this.extraIgnore.ignores(e)||t.endsWith("index.db")||t.endsWith("index.scip")||t.endsWith("index.db.tmp")||t.endsWith(".scipquery.json"))return;if(this.changedFiles++,this.reindexInFlight){this.dirty=!0,this.setStatus({state:"indexing",startedAt:this.status.startedAt});return}if(this.status.state==="cooldown"){this.dirty=!0,this.setStatus({state:"cooldown",until:this.status.until,dirty:!0});return}this.clearDebounceTimer();let s=Date.now()+this.watchConfig.debounceMs;this.setStatus({state:"waiting",changedFiles:this.changedFiles,reindexAt:s}),this.debounceTimer=setTimeout(()=>{this.debounceTimer=null,this.triggerReindex()},this.watchConfig.debounceMs)}triggerReindex(){if(this.reindexInFlight||this.stopped)return;let t=Date.now()-this.lastReindexEnd;if(this.lastReindexEnd>0&&t<this.watchConfig.cooldownMs){let s=this.watchConfig.cooldownMs-t;this.dirty=!0;let n=Date.now()+s;this.setStatus({state:"cooldown",until:n,dirty:!0}),this.cooldownTimer=setTimeout(()=>{this.cooldownTimer=null,this.dirty&&!this.stopped&&(this.dirty=!1,this.triggerReindex())},s);return}this.reindexInFlight=!0,this.dirty=!1,this.changedFiles=0;let e=Date.now();this.setStatus({state:"indexing",startedAt:e}),this.runReindex().then(s=>{if(this.reindexInFlight=!1,this.lastReindexEnd=Date.now(),this.onReindexComplete(s),this.dirty&&!this.stopped){let n=Date.now()+this.watchConfig.cooldownMs;this.setStatus({state:"cooldown",until:n,dirty:!0}),this.cooldownTimer=setTimeout(()=>{this.cooldownTimer=null,this.dirty&&!this.stopped?(this.dirty=!1,this.triggerReindex()):this.setStatus({state:"idle"})},this.watchConfig.cooldownMs)}else this.setStatus({state:"idle"})}).catch(s=>{this.reindexInFlight=!1,this.lastReindexEnd=Date.now(),this.onError(s instanceof Error?s:new Error(String(s))),this.setStatus({state:"idle"})})}runReindex(){return new Promise((t,e)=>{let s=Date.now(),n=this.indexPaths.dbPath+".tmp",o=B(this.indexPaths.indexPath),r=G(new URL("./reindex-worker.js",import.meta.url).pathname,[],{env:{...process.env,SCIP_REINDEX_PROJECT_ROOT:this.projectRoot,SCIP_REINDEX_OUTPUT_SCIP:o,SCIP_REINDEX_OUTPUT_DB:n,SCIP_REINDEX_LANGUAGES:this.languages?.join(",")??"",SCIP_REINDEX_PNPM_WORKSPACES:this.pnpmWorkspaces?"1":""},stdio:"pipe"});r.on("exit",h=>{if(h===0)try{I(n)&&T(n,this.indexPaths.dbPath),I(o)&&T(o,this.indexPaths.indexPath),t(Date.now()-s)}catch(a){e(new Error(`Atomic swap failed: ${a}`))}else e(new Error(`Reindex worker exited with code ${h}`))}),r.on("error",e)})}setStatus(t){this.status=t,this.onStatus(t)}clearDebounceTimer(){this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=null)}clearCooldownTimer(){this.cooldownTimer&&(clearTimeout(this.cooldownTimer),this.cooldownTimer=null)}};function B(i){return i.endsWith(".scip")?i.slice(0,-5)+".tmp.scip":i+".tmp.scip"}import{existsSync as x,mkdirSync as J,symlinkSync as V,readlinkSync as K,unlinkSync as Q}from"fs";import{join as d,dirname as k,resolve as S}from"path";import{homedir as y,platform as Y}from"os";import{fileURLToPath as z}from"url";var Z=Y()==="win32",tt=["concrete-plan","scip-ai-cleanup","scip-explore","scip-debloat","scip-doc-reconcile","scip-maintainability","scip-verify","scip-language-playbook"];function et(i={}){let t=i.quiet?()=>{}:console.log,e=z(import.meta.url),s=S(k(e),"..","skills"),n=[d(y(),".claude","skills"),d(y(),".codex","skills"),d(y(),".agents","skills")],o={installed:[],skipped:[],alreadyLinked:[]};for(let r of n){let h=k(r);if(!x(h))continue;J(r,{recursive:!0});let a=it(r);for(let c of tt){let u=d(s,c),p=d(r,c);if(!x(u)){o.skipped.push(`${a}/${c}`);continue}if(x(p)){try{let E=K(p);if(S(E)===S(u)){o.alreadyLinked.push(`${a}/${c}`),t(` ok: ${c} \u2192 ${a} (already linked)`);continue}}catch{o.skipped.push(`${a}/${c}`),t(` skip: ${c} \u2192 ${a} (exists, not a symlink)`);continue}Q(p)}V(u,p,Z?"junction":"dir"),o.installed.push(`${a}/${c}`),t(` done: ${c} \u2192 ${a}`)}}return o}function it(i){return i.includes(".codex")?"Codex":i.includes(".agents")?"Agents":"Claude"}export{m as Watcher,j as getScipVersion,M as initProjectConfig,et as installSkills,b as isScipInstalled,N as loadProjectConfig,W as printScipInstallInstructions,f as resolveIndexPaths};
3
3
  //# sourceMappingURL=runtime.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scip-query",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Language-agnostic code intelligence CLI powered by SCIP indexes",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,100 @@
1
+ ---
2
+ name: scip-doc-reconcile
3
+ description: Reconcile standards docs and living documentation with the current code using scip-query doc-drift evidence. Updates descriptive claims, repairs broken file references, and escalates normative violations instead of silently blessing them. Ends with staleness driven to zero.
4
+ allowed-tools: [Bash, Read, Write, Edit, Glob, Agent, TaskCreate, TaskUpdate, TaskGet, TaskList]
5
+ keywords: [docs, standards, reconcile, doc-drift, stale-docs, agent-os, spec, documentation, drift, sync]
6
+ ---
7
+
8
+ # Doc Reconciliation with scip-query
9
+
10
+ Standards docs exist so agents implement consistently. When the code moves and
11
+ the doc doesn't, every agent that reads it implements against a dead spec.
12
+ This skill reconciles docs with reality — using evidence, not memory.
13
+
14
+ ## The One Rule That Matters
15
+
16
+ A doc contains two kinds of statements, and they drift differently:
17
+
18
+ - **Descriptive** ("the horses workflow lives in `workflows/horses.ts` and
19
+ exposes `listHorses`") — when code moved on, **update the doc**.
20
+ - **Normative** ("all routes MUST validate stable scope before querying") —
21
+ when code violates it, **do NOT rewrite the standard to bless the
22
+ violation**. Either fix the code to comply, or record the contradiction in
23
+ the report for a human decision. Silently weakening a standard to match
24
+ drifted code is worse than the drift.
25
+
26
+ When unsure which kind a statement is: MUST/SHOULD/NEVER language is
27
+ normative; file paths, symbol names, and behavior descriptions are
28
+ descriptive.
29
+
30
+ ## Workflow
31
+
32
+ ### 1. Build the evidence-backed worklist
33
+
34
+ ```bash
35
+ scip-query reindex
36
+ scip-query doc-drift # all living docs, ranked by staleness
37
+ scip-query doc-drift agent-os/standards # scoped to a standards tree
38
+ ```
39
+
40
+ Triage order:
41
+ 1. Docs with `BROKEN REFERENCE` lines (the spec cites deleted code — actively wrong)
42
+ 2. Highest staleness score
43
+ 3. Docs agents read most (AGENTS.md, CLAUDE.md, standards indexes)
44
+
45
+ Track each doc as a task. Archival docs (dated plans, ADRs, reports) are
46
+ excluded automatically — do not "reconcile" records of past decisions.
47
+
48
+ ### 2. Reconcile one doc at a time
49
+
50
+ For each doc, gather what actually changed:
51
+
52
+ ```bash
53
+ scip-query doc-drift <doc> # its subjects + changes since
54
+ git log --oneline -15 -- <subject-file> # WHY the subject changed
55
+ scip-query outline <subject-file> # what it looks like NOW
56
+ scip-query system <module> # current module shape
57
+ scip-query trace <symbol-the-doc-mentions> # does it still exist? who uses it?
58
+ ```
59
+
60
+ Then edit the doc:
61
+
62
+ - **Broken references**: find where the code went (`scip-query files <stem>`,
63
+ `git log --follow`) and update the citation — or delete the claim if the
64
+ capability is gone.
65
+ - **Stale descriptive claims**: re-read the subject files and rewrite the
66
+ claims to match current behavior. Every concrete claim you write must be
67
+ something you verified with a scip-query command this session — no claims
68
+ from memory.
69
+ - **Examples and snippets**: re-derive them from current code
70
+ (`scip-query code <symbol>`), don't patch them by eye.
71
+ - **Normative violations found while reading**: add them to the report under
72
+ "Standard vs code contradictions" with file:line evidence. Do not edit the
73
+ normative text.
74
+
75
+ ### 3. Verify, per doc and overall
76
+
77
+ ```bash
78
+ scip-query doc-drift <doc> # staleness must drop to 0, broken refs to none
79
+ scip-query diff-gate # your own doc edits gate clean before commit
80
+ ```
81
+
82
+ A doc still showing staleness after your edit means a subject changed in
83
+ ways you haven't reflected — go back.
84
+
85
+ ### 4. Report
86
+
87
+ - Per doc: staleness before → after, broken references fixed, claims updated.
88
+ - **Standard vs code contradictions** (normative): each with the standard's
89
+ requirement, the violating file:line, and a recommendation (fix code /
90
+ amend standard) — explicitly awaiting a human call.
91
+ - Docs recommended for deletion (describe removed capabilities entirely).
92
+
93
+ ## Hard Rules
94
+
95
+ 1. Every updated claim cites the scip-query command that verified it (in the
96
+ commit message or PR description).
97
+ 2. Never weaken normative language to match drifted code.
98
+ 3. Never reconcile archival docs (plans, ADRs, reports) — they are records.
99
+ 4. One commit per doc (or tight group) so review is per-standard.
100
+ 5. Re-run `scip-query doc-drift` at the end; the summary line is the result.