@jawache/flow 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENCE +21 -0
- package/README.md +309 -0
- package/dist/flow.mjs +167 -0
- package/dist/index.mjs +18 -0
- package/dist/packs.mjs +116 -0
- package/dist/types/adapter/archive.d.ts +45 -0
- package/dist/types/adapter/claude.d.ts +157 -0
- package/dist/types/adapter/domain.d.ts +1112 -0
- package/dist/types/adapter/product.d.ts +10 -0
- package/dist/types/checks/domain.d.ts +792 -0
- package/dist/types/engine/domain.d.ts +747 -0
- package/dist/types/engine/state.d.ts +104 -0
- package/dist/types/errors.d.ts +14 -0
- package/dist/types/flow.d.ts +1 -0
- package/dist/types/glob.d.ts +53 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/language/domain.d.ts +641 -0
- package/dist/types/packs/docs.d.ts +49 -0
- package/dist/types/packs/fcis.d.ts +71 -0
- package/dist/types/packs/flow.d.ts +50 -0
- package/dist/types/packs/git.d.ts +107 -0
- package/dist/types/packs/index.d.ts +10 -0
- package/dist/types/packs/justfile.d.ts +83 -0
- package/dist/types/packs/node.d.ts +39 -0
- package/dist/types/packs/secrets.d.ts +117 -0
- package/dist/types/packs/tdd.d.ts +52 -0
- package/dist/types/packs/typescript.d.ts +101 -0
- package/dist/types/packs/work.d.ts +49 -0
- package/dist/types/version.d.ts +2 -0
- package/package.json +61 -0
package/dist/flow.mjs
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{existsSync as Ai}from"node:fs";import{pathToFileURL as ji}from"node:url";import{join as Mi,resolve as Gn}from"node:path";var me="0.1.0";function ee(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Ye(e){let n=[],t="",r=()=>{t!==""&&n.push({kind:"literal",text:t}),t=""};for(let o=0;o<e.length;o++){let s=e[o];if(s==="*")r(),e[o+1]==="*"?(n.push({kind:"globstar"}),o++,e[o+1]==="/"&&o++):n.push({kind:"star"});else if(s==="?")r(),n.push({kind:"single"});else if(s==="{"){let i=e.indexOf("}",o);if(i===-1){t+=s;continue}r(),n.push({kind:"options",options:e.slice(o+1,i).split(",")}),o=i}else t+=s??""}return r(),n}function Qe(e){switch(e.kind){case"literal":return ee(e.text);case"star":return"[^/]*";case"globstar":return".*";case"single":return"[^/]";case"options":return`(${e.options.map(ee).join("|")})`}}function Kr(e){return new RegExp(`^${Ye(e).map(Qe).join("")}$`)}function Vr(e,n){return Kr(n).test(e)}function M(e,n){return(n||[]).some(t=>Vr(e,t))}function qr(e){let n=e.includes("/")?e.slice(0,e.lastIndexOf("/")):".",t=e.slice(e.lastIndexOf("/")+1),r=t.lastIndexOf("."),o=r>0?t.slice(0,r):t,s=n==="."?o:`${n}/${o}`;return{dir:n,base:t,name:o,path:s}}function Ze(e,n){let t=qr(n);return e.replace(/\{(dir|base|name|path)\}/g,(r,o)=>t[o])}var Jn={write:["edit"],command:["edit","commit"],commit:["commit"],delete:["delete"],"turn-end":["turn"]},Jr=Object.keys(Jn),zr=["session","touch","turn-end"];function Yr(e){let n=[];for(let t of e)for(let r of Jn[t])n.includes(r)||n.push(r);return n}function en(e){return Jr.includes(e)}function nn(e){return zr.includes(e)}var zn=["write","delete","commit","touch"];function Qr(e){return zn.includes(e)}var Zr=Symbol.for("flow.category");function Xe(e,n){if(!(typeof e!="object"||e===null))return e[n]}function tn(e){return Xe(e,Zr)===!0}var ve={ok:()=>({ok:!0}),fail:e=>e===void 0?{ok:!1}:{ok:!1,detail:e}};function Ee(e,n,t){return{moment:e,...n,exec:r=>t.exec(r),fs:t.fs,git:t.git,ok:ve.ok,fail:ve.fail}}var Yn=Symbol.for("flow.settings");function N(e){return n=>{let t=e(n);return Object.defineProperty(t,Yn,{value:n,enumerable:!1,configurable:!0}),t}}function Xr(e){return e===void 0?void 0:e[Yn]}function eo(e){return e?(e.pass?.length??0)+(e.block?.length??0)>0:!1}function Ce(e,n,t){let r=e.exec??{},o=e.fs??{};return{exec:s=>{let i=Object.hasOwn(r,s)?s:Object.keys(r).find(d=>s.includes(d)),a=i===void 0?void 0:r[i];return a===void 0?(t.push({kind:"exec",asked:s}),Promise.resolve({stdout:"",stderr:"",code:0})):Promise.resolve({stdout:a.stdout??"",stderr:a.stderr??"",code:a.code??0})},fs:{exists:s=>Promise.resolve(Object.hasOwn(o,s)),read:s=>{let i=o[s];return i===void 0?(t.push({kind:"read",asked:s}),Promise.resolve("")):Promise.resolve(i)}},git:{diff:()=>Promise.resolve(e.gitDiff??""),stagedFiles:()=>Promise.resolve([...n??e.staged??[]])}}}function ne(e,n){return!(e.on&&!M(n,e.on)||e.ignore&&M(n,e.ignore))}var no=["at","for","on","ignore","check","message","disabled","test","description"],to=["at","for","on","ignore","text","file","disabled","description"];function ro(e){let n=Xe(Xe(e,"spec"),"kind");return n==="guardrail"||n==="breadcrumb"}function qn(e,n,t){let r={};for(let o of Object.keys(e))r[o]=t?.[o]??"pack";for(let o of Object.keys(n))r[o]="override";return{spec:{...e,...n},source:r}}function te(e){return[`flow: the config will not load \u2014 ${e.length} refusal${e.length===1?"":"s"}.`,...e.map(n=>` ${n.code}: ${n.detail}`)].join(`
|
|
3
|
+
`)}function ue(e){let n=[],t=[],r=new Map;for(let o of e.bindings){if(o.kind!=="pack")continue;if(!o.pack||o.pack.name===""){n.push({code:"not-a-pack",entry:"",detail:"A `pack(\u2026)` line was given a value that no `definePack` ever made. Import the pack's default export, not one of its entries."});continue}let{name:s,build:i,takesParams:a}=o.pack;if(a&&!o.hasParams){n.push({code:"missing-parameter",entry:s,detail:`The pack \`${s}\` declares parameters and was bound without any \u2014 write \`pack(${s}, { \u2026 })\`. Which fields it needs is stated by its factory's type.`});continue}for(let{key:d,spec:l}of Qn(i(o.params))){let c={id:`${s}.${d}`,pack:s,key:d,...qn(l,{})};if(r.get(c.id)){n.push({code:"duplicate-id",entry:c.id,detail:`Two entries claim the id \`${c.id}\`. An id is its property name inside its pack, so this is one pack bound twice, or two packs sharing a name.`});continue}r.set(c.id,c),t.push(c)}}for(let o of e.bindings){if(o.kind!=="override")continue;let s=`${o.ref.pack}.${o.ref.id}`,i=r.get(s);if(!i){n.push({code:"unknown-entry",entry:s,detail:`\`override(${s})\` names an entry no bound pack has. Bind the pack it lives in, or correct the reference.`});continue}let a=qn(i.spec,o.spoken,i.source);i.spec=a.spec,i.source=a.source}for(let o of t)n.push(...oo(o));return n.length>0?{ok:!1,refusals:n}:{ok:!0,entries:t.map(o=>({id:o.id,pack:o.pack,key:o.key,spec:o.spec,source:o.source,categories:(o.spec.for??[]).map(s=>s.name),phases:o.spec.kind==="guardrail"?Yr(o.spec.at??[]):[],settings:o.spec.kind==="guardrail"?Xr(o.spec.check):void 0}))}}function Qn(e,n=[]){let t=[];for(let[r,o]of Object.entries(e))ro(o)?t.push({key:[...n,r].join("."),spec:o.spec}):typeof o=="object"&&o!==null&&t.push(...Qn(o,[...n,r]));return t}function oo(e){let{id:n,spec:t}=e,r=[],o=(g,m)=>r.push({code:g,entry:n,detail:m}),s=t.kind==="guardrail"?no:to,i=Object.keys(t).filter(g=>g!=="kind"&&!s.includes(g));i.length>0&&o("unknown-key",`\`${n}\` carries ${i.map(g=>`\`${g}\``).join(", ")}, which the ${t.kind} grammar does not have. It is closed: ${s.join(" \xB7 ")}.`);let a=t.at??[],d=t.kind==="guardrail"?en:nn,l=a.filter(g=>!d(g));if(l.length>0&&o("unknown-moment",`\`${n}\` fires at ${l.map(g=>`\`${g}\``).join(", ")}, which is not a moment a ${t.kind} has a rail for.`),(t.for??[]).some(g=>!tn(g))&&o("undeclared-category",`\`${n}\` is scoped with \`.for(\u2026)\` to something \`defineCategory\` never made. A category is a value with its recognizer aboard \u2014 import the one you mean.`),t.disabled)return r;let u=[];return a.length===0&&u.push(".at(\u2026)"),t.kind==="guardrail"?(t.check||u.push(".check(\u2026)"),t.message===void 0&&u.push(".message(\u2026)")):t.text===void 0&&t.file===void 0&&u.push(".text(\u2026) or .file(\u2026)"),u.length>0&&o("missing-mandatory",`\`${n}\` never said ${u.join(" and ")}. Nothing in this grammar defaults \u2014 an absent key is absent, not a fallback.`),((t.on?.length??0)>0||(t.ignore?.length??0)>0)&&a.length>0&&!a.some(g=>Qr(g))&&o("dead-scope",`\`${n}\` narrows by path but fires only at ${a.map(g=>`\`${g}\``).join(", ")}, which name no path \u2014 its \`on\`/\`ignore\` would be read by nothing. Drop the scope, or add a moment that carries a file (${zn.join(" \xB7 ")}).`),t.kind==="guardrail"&&!eo(t.test)&&o("no-cases",`\`${n}\` carries no cases. A rule declares what must pass it and what it must block (\`.test({ pass, block })\`) \u2014 a fence whose block-case was never written is a fence nothing proves is alive.`),r}var fe=class extends Error{refusals;constructor(n){super(te(n)),this.name="FlowConfigError",this.refusals=n}};function Zn(e){if(e.ok)return e.entries;throw new fe(e.refusals)}function q(e,n,t=" \xB7 "){return n.length===0?e.ok():e.fail(n.join(t))}function oe(e){return e.file??{path:"",content:""}}function so(e,n){let t=e.split(`
|
|
4
|
+
`),r=[];for(let o of n){let s=new RegExp(o);t.forEach((i,a)=>{s.test(i)&&r.push({pattern:o,line:a+1})})}return r}var qi=N(e=>n=>{let t=so(oe(n).content,e.ban);return q(n,t.map(r=>`line ${r.line}: matches /${r.pattern}/`))}),Ji=N(e=>n=>{let t=n.command??"",r=e.ban.filter(o=>new RegExp(o).test(t));return q(n,r.map(o=>`matches banned /${o}/`))});var zi=["You are recording prose through a shell command, and a backtick inside a DOUBLE-quoted argument is not Markdown \u2014 the shell RUNS it and splices its output into your text before the command ever sees it.","On an append-only record there is no undo: no amend, no redact. And once the binary has argv the splice has already happened, so this is the only moment it can be caught.","Fix: SINGLE-quote the argument (backticks are literal there), or feed the prose on stdin with a QUOTED heredoc \u2014 `git commit -F -` plus `<<'EOF'`, which this rule treats as safe.","ALREADY single-quoted and still blocked? Then your PROSE carries an unbalanced double quote before the backtick, and the matcher cannot tell that quote from a real open argument. Nothing would splice \u2014 this one is the rule's cost, not your mistake. Balance the quote, drop it, or use the heredoc; do not go back to double quotes to make it pass."].join(`
|
|
5
|
+
`),Yi=N(e=>async n=>{let{path:t}=oe(n);return e.existingOnly===!0?n.moment!=="delete"&&!await n.fs.exists(t)?n.ok():n.fail(`'${t}' is append-only \u2014 an existing file here is history and may not be edited or deleted (adding a new one is fine)`):n.fail(`'${t}' is a protected path (do not edit directly)`)}),Qi=N(e=>async n=>{let t=Ze(e.sibling,oe(n).path);return await n.fs.exists(t)?n.ok():n.fail(`required sibling missing: ${t}`)}),io=/\.(ts|tsx|mts|cts|js|mjs|cjs|jsx)$/;function ao(e,n){let t=e.replace(/\\/g,"/"),r=n.root.replace(/^\/+|\/+$/g,"")+"/",o=t.indexOf(r);if(o===-1)return{ok:!0};let s=t.slice(o+r.length);if(n.folders?.length){let c=s.split("/")[0]??"",u=c.replace(/\.[^.]+$/,"");if(c!==""&&!n.folders.includes(c)&&!n.folders.includes(u))return{ok:!1,tier:"folder",found:c}}if(!n.allow.length)return{ok:!0};let i=io.exec(t);if(!i)return{ok:!0};if(t.endsWith(".d.ts"))return{ok:!0};if(!s.includes("/"))return{ok:!0};let d=s.slice(s.lastIndexOf("/")+1).slice(0,-i[0].length);return(d.endsWith(".test")?d.slice(0,-5)!==(n.thinking??""):!n.allow.includes(d))?{ok:!1,tier:"filename"}:{ok:!0}}function lo(e,n,t){return e.ok?"":e.tier==="folder"?`'${e.found}' is not one of the names ${t.root}/ may hold: ${t.folders?.join(" \xB7 ")??""} (found ${n})`:`non-canonical file in a ${t.root} feature folder: ${n} (allowed: ${t.allow.join(" \xB7 ")}${t.thinking===void 0?"":` \u2014 only ${t.thinking} may carry a .test sibling`})`}var Zi=N(e=>n=>{let{path:t}=oe(n),r=ao(t,e);return r.ok?n.ok():n.fail(lo(r,t,e))});function co(e,n){let t=e;for(let r of(n??"").split(".").filter(Boolean)){if(t==null||typeof t!="object"||!(r in t))return{found:!1};t=t[r]}return{found:!0,value:t}}function uo(e,n){let t=[],{path:r}=n,o=co(e,r);if(!o.found)return n.required===!0&&t.push(`"${r}" is missing (required)`),t;let s=o.value;if(n.keysPrefixedWith!==void 0){if(!s||typeof s!="object")return t;for(let i of Object.keys(s))i.startsWith(n.keysPrefixedWith)||t.push(`"${r}.${i}" must be prefixed "${n.keysPrefixedWith}"`)}return"equals"in n&&JSON.stringify(s)!==JSON.stringify(n.equals)&&t.push(`"${r}" must equal ${JSON.stringify(n.equals)} (is ${JSON.stringify(s)})`),n.matches!==void 0&&!new RegExp(n.matches).test(String(s))&&t.push(`"${r}" (${JSON.stringify(s)}) must match /${n.matches}/`),t}function fo(e,n){let t;try{t=JSON.parse(e)}catch{return[]}return n.flatMap(r=>uo(t,r))}var Xi=N(e=>n=>q(n,fo(oe(n).content,e.assert)));function go(e,n){return e.filter(t=>!new RegExp(`\\b${ee(t)}\\b`).test(n))}async function po(e){let{parse:n,Lang:t}=await import("@ast-grep/napi"),r=n(t.Tsx,e).root(),o=new Set;for(let s of["export function $N($$$) { $$$ }","export class $N { $$$ }","export const $N = $V"])for(let i of r.findAll(s)){let a=i.getMatch("N");a&&o.add(a.text())}return[...o]}var ea=N(e=>async n=>{let{path:t,content:r}=oe(n),o=Ze(e.sibling,t);if(!await n.fs.exists(o))return n.fail(`sibling test not found: ${o}`);let s=await n.fs.read(o),i=go(await po(r),s);return q(n,i.map(a=>`exported '${a}' is never referenced in ${o}`))});async function nt(e){return e.staged?e.staged:e.git.stagedFiles()}function yo(e,n){let t=[];for(let r of n)e.some(o=>M(o,r.if))&&(e.some(o=>M(o,r.thenAny))||t.push(`${r.if.join(", ")} changed but none of [${r.thenAny.join(", ")}] did in the same commit`));return t}var na=N(e=>async n=>q(n,yo(await nt(n),e.groups))),Pe=new Set([";","&&","||","|","&","|&",`
|
|
6
|
+
`]),mo=new Set(["-C","-c","--git-dir","--work-tree","--namespace","--exec-path","--config-env"]);function ho(e,n){for(let t=n;t<e.length;t++){if(e[t]==="\\"){t++;continue}if(e[t]==='"')return t}return-1}function Ae(e){let n=e,t=[],r="",o=!1,s=0,i=()=>{o&&t.push(r),r="",o=!1};for(;s<n.length;){let a=n[s];if(a==="'"||a==='"'){let d=a==="'"?n.indexOf("'",s+1):ho(n,s+1);if(d===-1)return null;r+=a==='"'?n.slice(s+1,d).replace(/\\(["\\$`])/g,"$1"):n.slice(s+1,d),o=!0,s=d+1}else if(a==="\\"&&s+1<n.length)r+=n[s+1],o=!0,s+=2;else if(a===`
|
|
7
|
+
`)i(),t.push(`
|
|
8
|
+
`),s++;else if(/\s/.test(a))i(),s++;else if(a===";"||a==="|"||a==="&"){i();let d=n.slice(s,s+2);d==="&&"||d==="||"||d==="|&"?(t.push(d),s+=2):(t.push(a),s++)}else r+=a,o=!0,s++}return i(),t}function tt(e){let n=Ae(e);if(!n)return null;let t=[],r=0,o=!0;for(;r<n.length;){let s=n[r];if(Pe.has(s)){o=!0,r++;continue}if(o&&s==="git"){let i=[],a=r+1;for(;a<n.length&&!Pe.has(n[a]);)i.push(n[a++]);let d=0;for(;d<i.length;){let l=i[d];if(mo.has(l))d+=2;else if(l.startsWith("-"))d+=1;else break}t.push({globals:i.slice(0,d),subcommand:i[d]??null,args:i.slice(d+1)}),r=a,o=!1;continue}o=!1,r++}return t}function ko(e){let n=/<<-?\s*(['"]?)([A-Za-z_][A-Za-z0-9_]*)\1/.exec(e);if(!n)return null;let t=n[2],o=e.slice(n.index+n[0].length).split(`
|
|
9
|
+
`).slice(1),s=o.findIndex(a=>a.trim()===t);if(s===-1)return null;let i=o.slice(0,s).join(`
|
|
10
|
+
`).trim();return i===""?null:i}function bo(e){let n=tt(e);if(!n)return null;let t=n.find(o=>o.subcommand==="commit");if(!t)return null;let r=[];for(let o=0;o<t.args.length;o++){let s=t.args[o];if(s==="-m"||s==="--message"){let i=t.args[o+1];if(i===void 0)break;r.push(i),o++;continue}s.startsWith("-m")&&s!=="-m"?r.push(s.slice(2)):s.startsWith("--message=")&&r.push(s.slice(10))}return r.length?r.join(`
|
|
11
|
+
|
|
12
|
+
`):ko(e)}function re(e){return`'${e.replace(/'/g,"'\\''")}'`}function wo(e){let n=[],t=Ae(e),r=t?.[1];t?.[0]==="cd"&&r!==void 0&&!r.startsWith("-")&&!Pe.has(r)&&Pe.has(t[2]??"")&&n.push("-C",re(r));let o=tt(e)?.[0];if(!o)return n.join(" ");for(let s=0;s<o.globals.length-1;s++)o.globals[s]==="-C"&&n.push("-C",re(o.globals[s+1]));return n.join(" ")}function xo(e,n){return new RegExp(`^[ \\t]*${ee(n)}[ \\t]*:`,"im").test(e)}function So(e,n,t=[]){let r=new Set(t),o=new Set;for(let s of e.split(`
|
|
13
|
+
`)){if(!s.startsWith("+")||s.startsWith("+++"))continue;let i=s.slice(1);for(let a of n){let d=new RegExp(a,"g"),l;for(;(l=d.exec(i))!==null;)l[1]!==void 0&&!r.has(l[1])&&o.add(l[1]),l[0]===""&&d.lastIndex++}}return[...o]}function Ro(e){let{message:n,state:t,whenChanged:r,whenAdded:o,except:s,namesAdded:i,token:a}=e,d=new Set(t.added),l=[];for(let c of t.changed)s&&M(c,s)||(d.has(c)&&o&&M(c,o)?l.push(`\`${c}\` (new)`):r&&M(c,r)&&l.push(`\`${c}\` (changed)`));for(let{name:c,file:u}of i??[])l.push(`\`${c}\` (added in ${u})`);return l.length?xo(n,a)?[]:[`${l.join(" \xB7 ")} \u2014 and this commit's message records no reason. Put \`${a}: <why>\` on a line of its own in the message; the act is fine, doing it silently is not.`]:[]}function rn(e){return e.split(`
|
|
14
|
+
`).map(n=>n.trim()).filter(Boolean)}var ta=N(e=>async n=>{let t=bo(n.command??"");if(t==null)return n.ok();let r=`git ${wo(n.command??"")}`.trimEnd(),o={changed:[],added:[]};if(e.whenChanged||e.whenAdded){let[a,d,l]=await Promise.all([n.exec(`${r} diff HEAD --name-only`),n.exec(`${r} diff HEAD --name-only --diff-filter=A`),n.exec(`${r} ls-files --others --exclude-standard`)]);if(a.code!==0||d.code!==0||l.code!==0)return n.ok();let c=rn(l.stdout);o={changed:[...new Set([...rn(a.stdout),...c])],added:[...rn(d.stdout),...c]}}let s=[];for(let a of e.diffAdds??[]){let d=await n.exec(`${r} diff HEAD -- ${re(a.file)}`);if(d.code!==0)return n.ok();for(let l of So(d.stdout,a.patterns,a.known??[]))s.push({name:l,file:a.file})}let i=Ro({message:t,state:o,whenChanged:e.whenChanged,whenAdded:e.whenAdded,except:e.except,namesAdded:s,token:e.token});return q(n,i)});function $o(e,n,t){if(t===""||n.length===0)return!1;let r=-1,o=-1;return e.forEach((s,i)=>{s.did==="edit"&&M(s.path,n)&&(r=i),s.did==="run"&&s.command.includes(t)&&(o=i)}),r>=0&&r>o}var ra=N(e=>n=>n.turn&&$o(n.turn,e.edited,e.mustRun)?n.fail(`edited ${e.edited.join(", ")} this turn but never ran \`${e.mustRun}\` afterwards`):n.ok());function vo(e,n){let t=n??[];return!t.length||!e.length?!1:!e.some(r=>M(r,t))}function rt(e,n=8){let t=e.trim().split(`
|
|
15
|
+
`).filter(Boolean),r=t.filter(o=>/\b(fail|failed|error|not ok|assert|expected|cannot|undefined)\b/i.test(o)||/[✖✗✘]/.test(o));return(r.length?r:t.slice(-6)).slice(0,n).join(`
|
|
16
|
+
`)}var oa=N(e=>async n=>{let t=await nt(n);if(vo(t,e.changed))return n.ok();let r=e.run.includes("{files}");if(r&&t.length===0)return n.ok();let o=r?e.run.replace("{files}",t.map(re).join(" ")):e.run,s=await n.exec(o);return s.code===0?n.ok():n.fail(`\`${o}\` failed (exit ${s.code}):
|
|
17
|
+
${rt(s.stderr||s.stdout)}`)});function Te(e){let n=Ye(e),t=n[n.length-1]?.kind==="globstar";return`^${(t?n.slice(0,-1):n).map(Qe).join("")}${t?"":"$"}`}function Eo(e){return e==="node:*"?{core:!0}:e.startsWith("node:")?{path:`^${ee(e)}$`}:e.includes("/")||e.includes("*")||e.includes(".")?{path:Te(e)}:{path:`node_modules/${ee(e)}(/|$)`}}function Oe(e,n){let t=[],r=!1;for(let s of e)for(let i of n[s]??[]){let a=Eo(i);a.core?r=!0:a.path&&t.push(a.path)}let o={};return t.length&&(o.path=t),r&&(o.dependencyTypes=["core"]),o}function Co(e){let n=e.layers,t=[],r=[],o=(e.except??[]).includes("type-only"),s=a=>o?{...a,dependencyTypesNot:["type-only"]}:a;for(let[a,d]of Object.entries(e.only??{})){let c={pathNot:Oe(d,n).path??[]};t.push({name:`${a}-only`,severity:"error",comment:`${a} may import only: ${d.join(", ")} \u2014 an unlisted import blocks here (add it to a layer, or keep it out).`,from:Oe([a],n),to:s(c)})}for(let a of e.forbid??[])if(a==="circular")t.push({name:"no-circular",severity:"error",comment:"no dependency cycles",from:{pathNot:"node_modules"},to:{circular:!0}});else if(a==="orphans")t.push({name:"no-orphans",severity:"warn",comment:"unreferenced module",from:{orphan:!0,pathNot:"node_modules|\\.d\\.ts$"},to:{}});else if(a.cyclesBetween!==void 0){let d=a.cyclesBetween,l=(n[d]??[])[0]??"",c=Te(l).replace(/\$$/,"").replace(/\[\^\/\]\*$/,"([^/]+)");t.push({name:`no-cycles-between-${d}`,severity:"error",comment:a.why??`${d} must not import sideways`,from:{path:`${c}/`},to:s({path:`${c.replace(/\(\[\^\/\]\+\)/,"[^/]+")}/`,pathNot:"$1/"})})}else if(a.from!==void 0&&a.to!==void 0){let d={...Oe([a.to],n)};a.transitive===!0&&(d.reachable=!0),t.push({name:`no-${a.from}-to-${a.to}${a.transitive===!0?"-transitive":""}`,severity:"error",comment:a.why??`${a.from} must not import ${a.to}`,from:Oe([a.from],n),to:s(d)})}for(let a of e.require??[])r.push({name:`must-import-${a.import}`,severity:"error",comment:a.why??`modules in ${a.in} must import ${a.import}`,module:{path:Te(a.in)},to:{path:Te(a.import)}});for(let a of e.warn??[])a==="sdp"&&t.push({name:"sdp",severity:"warn",comment:"depends on a more-unstable module (Stable Dependencies Principle)",from:{},to:{moreUnstable:!0}});let i={enhancedResolveOptions:{extensions:[".js",".jsx",".ts",".tsx",".mjs",".cjs",".json"]},doNotFollow:{path:"node_modules"},tsPreCompilationDeps:!1,...e.options??{}};return{forbidden:t,required:r,options:i}}var Xn="FLOW_DEPCRUISE_CONFIG";function Oo(e){let n=JSON.stringify(Co(e));return["d=$(mktemp -d)",`cat > "$d/dc.json" <<'${Xn}'`,n,Xn,`npx --no-install depcruise --config "$d/dc.json" --output-type json ${re(e.scan)}`,"rc=$?",'rm -rf "$d"',"exit $rc"].join(`
|
|
18
|
+
`)}function To(e){let n;try{n=JSON.parse(e.stdout||"")}catch{return e.code===0?[]:[`depcruise failed (exit ${e.code}): ${rt(e.stderr||e.stdout,3)}`]}return(n.summary?.violations??[]).map(t=>{let r=t.rule?.name??"fence",o=t.comment??t.rule?.comment??"";return`${r}: ${t.from} \u2192 ${t.to}${o?` \u2014 ${o}`:""}`})}var sa=N(e=>async n=>q(n,To(await n.exec(Oo(e))),`
|
|
19
|
+
`)),ot={html:"Html",css:"Css",javascript:"JavaScript",js:"JavaScript",typescript:"TypeScript",ts:"TypeScript",tsx:"Tsx",jsx:"Tsx"},Po=Object.keys(ot),et=new Set;async function Ao(e){let{Lang:n,registerDynamicLanguage:t}=await import("@ast-grep/napi"),r=ot[e];if(r!==void 0)return{ok:!0,lang:n[r]};if(et.has(e))return{ok:!0,lang:e};try{let o=await import(`@ast-grep/lang-${e}`);return t({[e]:o.default??o}),et.add(e),{ok:!0,lang:e}}catch{return{ok:!1,detail:`unknown ast-grep grammar: '${e}'. Built in: ${Po.join(", ")}. Anything else installs as a package \u2014 \`npm i -D @ast-grep/lang-${e}\` \u2014 and is picked up here on the next run.`}}}async function jo(e,n,t){let r=await Ao(t);if(!r.ok)return r;let{parse:o}=await import("@ast-grep/napi");return{ok:!0,hits:o(r.lang,e).root().findAll({rule:n}).map(i=>`${i.range().start.line+1}: ${i.text()}`)}}var ia=N(e=>async n=>{let t=await jo(oe(n).content,e.rule,e.language);return t.ok?q(n,t.hits):n.fail(t.detail)}),Mo={command:["command"],write:["write","delete"],commit:["commit"],"turn-end":["turn-end"]};function st(e){if(typeof e=="string")return{dialect:"command",facts:{command:e},world:{}};let n=e.world??{};return"command"in e?{dialect:"command",facts:{command:e.command},world:n}:"path"in e?{dialect:"write",facts:{file:{path:e.path,content:e.content}},world:n}:"staged"in e?{dialect:"commit",facts:{staged:e.staged},world:n}:{dialect:"turn-end",facts:{turn:e.actions},world:n}}function No(e,n){let{world:t,facts:r}=st(e);return{world:Ce(t,r.staged,n),facts:r}}async function _o(e,n,t,r){let{world:o,facts:s}=No(t,r);return(n==="commit"?await Fo(e,s,o):[s]).map(a=>Ee(n,a,o))}async function Fo(e,n,t){let r=n.staged??[];if(!e.spec.on)return[n];let o=[];for(let s of r)ne(e.spec,s)&&await t.fs.exists(s)&&o.push({file:{path:s,content:await t.fs.read(s)},staged:r});return o}async function Io(e,n,t,r,o){let{dialect:s}=st(t),i=e.spec.kind==="guardrail"?e.spec.at??[]:[],a=i.find(m=>Mo[s].includes(m)),d={entry:e.id,expect:r,index:o};if(a===void 0)return{...d,ok:!1,detail:`this case speaks the ${s} dialect, and the entry fires at ${i.length?i.join(", "):"no moment at all"} \u2014 nothing here would ever be handed one`};let l=[],c,u={ok:!0};try{let m=[];for(let w of await _o(e,a,t,l))m.push(await n(w));u=m.find(w=>!w.ok)??{ok:!0},c=!u.ok}catch(m){return{...d,ok:!1,detail:`the check threw: ${m.message}`}}if(l.length)return{...d,ok:!1,detail:Bo(l)};let p=r==="block";if(c===p)return{...d,ok:!0,detail:""};if(p)return{...d,ok:!1,detail:"expected a block and the check passed it \u2014 the rule does not catch what it says it catches"};let g=u.ok||u.detail===void 0?"":`: ${u.detail}`;return{...d,ok:!1,detail:`expected a pass and the check blocked it${g}`}}function Bo(e){return e.map(n=>n.kind==="exec"?`the check ran \`${n.asked}\` and the case records no answer \u2014 add it under \`world.exec\``:`the check read \`${n.asked}\` and the case records no content \u2014 add it under \`world.fs\``).join(" \xB7 ")}async function it(e){let n=[];for(let t of e){let{spec:r}=t;if(r.kind!=="guardrail"||r.disabled||!r.check)continue;let o=r.check,s=r.test??{};for(let[i,a]of[["pass",s.pass],["block",s.block]])for(let[d,l]of(a??[]).entries())n.push(await Io(t,o,l,i,d))}return n}import{spawnSync as Vs}from"node:child_process";import{existsSync as vr,readFileSync as Re,statSync as qs}from"node:fs";import{registerHooks as Js,stripTypeScriptTypes as zs}from"node:module";import{isAbsolute as Ys,join as Ke,resolve as Er}from"node:path";import{fileURLToPath as Qs,pathToFileURL as Zs}from"node:url";import{join as W}from"node:path";function ct(e){let n=[];for(let t of e)for(let r of t.spec.for??[])tn(r)&&!n.some(o=>o.name===r.name)&&n.push(r);return n}function ut(e,n,t){if(e)return{wearing:e,faults:[],fresh:!1};let r=[],o=[];for(let s of t)try{s.classify(n)&&r.push(s.name)}catch(i){o.push({category:s.name,error:i.message})}return{wearing:r,faults:o,fresh:!0}}function sn(e,n){return e.disabled?!1:(e.at??[]).includes(n)}function ft(e,n){return e.categories.length===0?!0:e.categories.some(t=>n.includes(t))}function he(e,n){return(e.on?.length??0)>0&&ne(e,n)}function je(e){return e.filter(n=>n.disabled===null&&(n.on?.length??0)>0)}async function Go(e,n,t){let{spec:r}=e;if(n.moment==="commit"){let o=n.staged??[];if(!r.on)return[{name:null,facts:{staged:o}}];let s=[];for(let i of o)ne(r,i)&&await t.fs.exists(i)&&s.push({name:i,facts:{file:{path:i,content:await t.fs.read(i)},staged:o}});return s}if(n.moment==="write"||n.moment==="delete"){let o=n.file;return!o||!ne(r,o.path)?[]:[{name:o.path,facts:{file:o}}]}if(n.moment==="command"){let o=n.command??"";return[{name:o,facts:{command:o}}]}return[{name:null,facts:{turn:n.turn??[]}}]}var on=["session","first-touch","drift","fault"],Do={effects:[],tallies:[]};function se(e){return{do:"block",entry:null,message:e,subject:null,detail:""}}async function Me({load:e,event:n,world:t,faults:r=[],off:o=!1}){if(o)return Do;if(!e.ok)return{effects:[se(te(e.refusals))],tallies:[]};if(r.length>0)return{effects:r.map(a=>se(`flow: the category \`${a.category}\` could not classify this session, so a rule scoped to it cannot be trusted either way \u2014 ${a.error}`)),tallies:[]};let s=[],i=[];for(let a of e.entries){if(a.spec.kind!=="guardrail"||!sn(a.spec,n.moment))continue;if(!ft(a,n.wearing)){i.push({id:a.id,evaluated:0,hits:0,silenced:1});continue}let d=a.spec.check;if(!d){s.push(se(`flow: \`${a.id}\` fires at ${n.moment} and carries no check. A rail that cannot ask a question is not a rail that passes.`));continue}let l=0,c=0;for(let u of await Go(a,n,t)){l+=1;let p=await Lo(d,Ee(n.moment,u.facts,t));p.ok||(c+=1,s.push({do:"block",entry:a.id,message:a.spec.message??"",subject:u.name,detail:p.detail??""}))}i.push({id:a.id,evaluated:l,hits:c,silenced:0})}return{effects:s,tallies:i}}async function Lo(e,n){try{return await e(n)}catch(t){return ve.fail(`the check threw: ${t.message}`)}}var Wo=2e5;function Ho(e){return e.driftTokens??Wo}function an({load:e,event:n,marks:t,settings:r,off:o=!1}){if(o)return{notices:[],marks:t};if(!e.ok)return{notices:[{do:"notice",entry:null,text:te(e.refusals),file:void 0,cause:"fault"}],marks:t};let s=[],i={...t},a=Ho(r);for(let d of e.entries){if(d.spec.kind!=="breadcrumb"||!sn(d.spec,n.moment)||!ft(d,n.wearing)||n.path!==void 0&&!ne(d.spec,n.path))continue;let l=i[d.id],c=n.moment==="session"?"session":l===void 0?"first-touch":"drift";l!==void 0&&(c!=="drift"||n.tokens-l<a)||(s.push({do:"notice",entry:d.id,text:d.spec.text,file:d.spec.file,cause:c}),i[d.id]=n.tokens)}return{notices:s,marks:i}}function dn(e,n){let t=new Set(n.filter(o=>sn(o.spec,"session")).map(o=>o.id)),r={};for(let[o,s]of Object.entries(e))t.has(o)&&(r[o]=s);return r}function gt(e){let t=[e.entry===null?e.message:`\u2717 ${e.entry}${e.subject?` \xB7 ${e.subject}`:""}`];return e.entry!==null&&e.message&&t.push(` ${e.message}`),e.detail&&t.push(...e.detail.split(`
|
|
20
|
+
`).map(r=>` ${r}`)),t.join(`
|
|
21
|
+
`)}var J=".flow",pt=`# flow's own state \u2014 telemetry and session marks, never committed.
|
|
22
|
+
*
|
|
23
|
+
`,Uo=1;function G(e){return W(e,J)}function ie(e){return(e||"nosession").replace(/[^A-Za-z0-9_-]/g,"-")}function ln(e,n){return W(G(e),"log",`${ie(n)}.jsonl`)}function cn(e,n,t){return W(G(e),"state",`${ie(n)}-${ie(t)}.json`)}function un(e){if(e==null||e.trim()==="")return{marks:{}};let n;try{n=JSON.parse(e)}catch{return{marks:{}}}if(typeof n!="object"||n===null)return{marks:{}};let t=n,r=Array.isArray(t.categories)&&t.categories.every(s=>typeof s=="string")?t.categories:void 0,o={};if(typeof t.marks=="object"&&t.marks!==null)for(let[s,i]of Object.entries(t.marks))typeof i=="number"&&Number.isFinite(i)&&(o[s]=i);return r===void 0?{marks:o}:{categories:r,marks:o}}function fn(e,n){return W(G(e),n?`.session-${ie(n)}`:".session")}function yt(e){return W(G(e),"off")}function mt(e){return W(G(e),"record")}function ht(e,n){return W(G(e),"replay",`${ie(n)}.jsonl`)}function Ne(e){return W(G(e),"read")}function kt(e,n){return W(Ne(e),ie(n))}var Ko=14400*1e3;function bt(e,n,t,r=Ko){let o={session:n,agent:null};if(!e||typeof e.session!="string"||!e.session)return o;let s=Date.parse(String(e.ts));return!Number.isFinite(s)||Math.abs(t-s)>r?o:{session:e.session,agent:typeof e.agent=="string"&&e.agent?e.agent:null}}function wt(e){let n=e.split(`
|
|
24
|
+
`);for(let t=n.length-1;t>=0;t--){let r=n[t];if(!(!r||!r.trim()))try{let o=JSON.parse(r).seq;if(typeof o=="number"&&Number.isFinite(o)&&o>=0)return Math.floor(o)+1}catch{continue}}return 1}function xt(e,n,t,r){return{kind:"meta",v:Uo,session:ie(e),started:r,branch:n,worktree:t}}function ge(e,n,t){let r=[{kind:"run",moment:e,subjects:t,rules:n.tallies}];for(let o of n.effects)o.do==="block"?r.push({kind:"guardrail",moment:e,id:o.entry,subject:o.subject,out:"deny",message:o.message,detail:o.detail}):r.push({kind:"breadcrumb",moment:e,id:o.entry,cause:o.cause});return r}function _e(e){return e.map(n=>{let t=n.spec,r=t.kind==="guardrail"?t.message??null:t.text??t.file??null;return{kind:t.kind,id:n.id,pack:n.pack,at:[...t.at??[]],on:t.on,ignore:t.ignore,for:[...n.categories],description:t.description??null,says:r,disabled:t.disabled?t.disabled.reason??"":null}})}var at=864e5;var Vo=()=>({median:null,buckets:{0:0,"1-5":0,"6-19":0,"20+":0},noEdit:0});function qo(e){return e<=0?"0":e<=5?"1-5":e<=19?"6-19":"20+"}function Jo(e){if(e.length===0)return null;let n=[...e].sort((r,o)=>r-o),t=Math.floor(n.length/2);return n.length%2?n[t]:(n[t-1]+n[t])/2}function dt(e,n){let t={0:0,"1-5":0,"6-19":0,"20+":0};for(let r of e)t[qo(r)]+=1;return{median:Jo(e),buckets:t,noEdit:n}}var lt=e=>e.includes("/")?e.slice(0,e.lastIndexOf("/")):".";function Fe(e){return e!==""&&!e.startsWith("/")&&!e.startsWith("..")}function A(e,n){let t=e[n];return typeof t=="string"&&t!==""?t:null}function gn(e){return A(e,"subject")??A(e,"path")??A(e,"command")}function zo(e){return e.kind==="tool"&&e.edit===!0&&gn(e)!==null&&A(e,"path")!==null}function St({sessions:e,entries:n,nowMs:t,minSessions:r=15,minDays:o=30}){let s=n.filter(f=>f.disabled===null),i=new Map(s.map(f=>[f.id,f])),a=je(n),d=new Map,l=new Map,c={},u={},p={},g=[],m=f=>{let h=d.get(f);return h===void 0&&(h={count:0,byCause:Object.fromEntries(on.map(F=>[F,0])),last:null,samples:[],noEdit:0},d.set(f,h)),h},w=f=>{let h=l.get(f);return h===void 0&&(h={runs:0,evaluated:0,hits:0,silenced:0,sessions:new Set,lastBlock:null,lastHit:null,repeats:0},l.set(f,h)),h},$=0,b=0;for(let{session:f,rows:h}of e){let F=[],ce=[],Z=new Set,X=0;for(let E of h){let _=A(E,"ts");if(_!==null&&g.push(_),E.kind==="tool"){X+=1,b+=1;let R=A(E,"path");if(R===null||!Fe(R)||(c[R]=(c[R]??0)+1,!zo(E)))continue;u[R]=(u[R]??0)+1,F.push({at:X,path:R}),a.some(v=>he(v,R))||(p[R]=(p[R]??0)+1);continue}if(E.kind==="compaction"){Z.clear();continue}if(E.kind==="breadcrumb"){let R=A(E,"id");if(R===null)continue;let v=m(R);v.count+=1;let j=A(E,"cause");j!==null&&on.includes(j)&&(v.byCause[j]+=1),_!==null&&(v.last===null||_>v.last)&&(v.last=_),A(E,"moment")==="touch"&&(i.get(R)?.on?.length??0)>0&&ce.push({id:R,at:X});continue}if(E.kind==="run"){let R=E.rules??[];for(let v of R){if(typeof v?.id!="string")continue;let j=w(v.id);j.runs+=1,j.evaluated+=v.evaluated??0,j.silenced+=v.silenced??0,j.sessions.add(f)}continue}if(E.kind==="guardrail"&&E.out==="deny"){let R=A(E,"id");if(R===null)continue;let v=w(R);v.hits+=1,$+=1;let j=gn(E);v.lastBlock={ts:_,subject:j},_!==null&&(v.lastHit===null||_>v.lastHit)&&(v.lastHit=_);let Vn=`${R} ${j??""}`;Z.has(Vn)?v.repeats+=1:Z.add(Vn)}}for(let E of ce){let _=i.get(E.id),R=_===void 0?void 0:F.find(j=>j.at>=E.at&&he(_,j.path)),v=m(E.id);R===void 0?v.noEdit+=1:v.samples.push(R.at-E.at)}}let P=[...g].sort(),y=P[0]??null,O=P[P.length-1]??null,C=e.filter(f=>f.session!=="commit"&&f.rows.length>0).length,k=y!==null&&O!==null?Math.round((Date.parse(O)-Date.parse(y))/at):0,x=C>=r||k>=o,B=new Set;for(let f of s)f.kind==="breadcrumb"&&(d.get(f.id)?.count??0)>0&&B.add(f.pack);let V=s.filter(f=>f.kind==="breadcrumb").map(f=>{let h=d.get(f.id);return{id:f.id,pack:f.pack,shown:h?.count??0,byCause:h?.byCause??Object.fromEntries(on.map(F=>[F,0])),lead:h===void 0?Vo():dt(h.samples,h.noEdit),last:h?.last??null,dead:x&&(f.on?.length??0)>0&&(h?.count??0)===0&&B.has(f.pack)}}),$e=s.filter(f=>f.kind==="guardrail").map(f=>{let h=l.get(f.id),F=h?.runs??0,ce=h?.evaluated??0;return{id:f.id,pack:f.pack,runs:F,evaluated:ce,hits:h?.hits??0,silenced:h?.silenced??0,lastBlock:h?.lastBlock??null,repeats:h?.repeats??0,dead:x&&(f.on?.length??0)>0&&F>0&&ce===0}}),Q=new Map;for(let[f,h]of Object.entries(p))Q.set(lt(f),(Q.get(lt(f))??0)+h);let Wn=[...Q.entries()].map(([f,h])=>({area:f,edits:h})).sort((f,h)=>h.edits-f.edits||f.area.localeCompare(h.area)),Hn=[],Un=[],Kn=[];for(let f of s){let h=l.get(f.id),F=d.get(f.id);if((h?.evaluated??0)+(F?.count??0)===0){(x?Hn:Un).push(f.id);continue}let Z=h?.lastHit??null;if(Z===null)continue;let X=Math.round((t-Date.parse(Z))/at);x&&Number.isFinite(X)&&X>o&&Kn.push({id:f.id,lastHit:Z,daysSince:X})}let Wr=[...d.values()].flatMap(f=>f.samples),Hr=[...d.values()].reduce((f,h)=>f+h.noEdit,0),Ur=[...V,...$e].filter(f=>f.dead).map(f=>f.id).sort();return{headline:{blocks:$,lead:dt(Wr,Hr),gaps:Wn,dead:Ur,retire:Hn.sort(),unproven:Un.sort(),quiet:Kn.sort((f,h)=>h.daysSince-f.daysSince)},breadcrumbs:V,guardrails:$e,gaps:Wn,span:{sessions:C,days:k,first:y,last:O,ample:x,tools:b},heat:{touches:c,edits:u,uncovered:p}}}function Rt({paths:e,heat:n,entries:t}){let r=new Set([...e,...Object.keys(n.touches),...Object.keys(n.edits)]),o=je(t),s=new Map,i=(a,d)=>{let l=s.get(a);return l===void 0&&(l={path:a,name:a.slice(a.lastIndexOf("/")+1),dir:d,depth:a.split("/").length-1,touches:0,edits:0,entries:[],uncovered:0},s.set(a,l)),l};for(let a of r){let d=a.split("/"),l=o.filter(g=>he(g,a)).map(g=>g.id),c=n.touches[a]??0,u=n.edits[a]??0,p=n.uncovered[a]??0;for(let g=0;g<d.length;g++){let m=d.slice(0,g+1).join("/"),w=i(m,g<d.length-1);w.touches+=c,w.edits+=u,w.uncovered+=p;for(let $ of l)w.entries.includes($)||w.entries.push($)}}for(let a of s.values())a.entries.sort();return[...s.values()].sort(Yo)}function Yo(e,n){let t=e.path.split("/"),r=n.path.split("/");for(let o=0;o<Math.min(t.length,r.length);o++){let s=t[o],i=r[o];if(s===i)continue;let a=o===t.length-1&&!e.dir,d=o===r.length-1&&!n.dir;return a!==d?a?1:-1:s.localeCompare(i)}return t.length-r.length}var Qo=["session","touch","write","delete","command","commit","turn-end"];function Ie(e,n){let t=new Map((n?.breadcrumbs??[]).map(s=>[s.id,s])),r=new Map((n?.guardrails??[]).map(s=>[s.id,s])),o=e.map(s=>({...s,record:(s.kind==="breadcrumb"?t.get(s.id):r.get(s.id))??null}));return{moments:Qo.map(s=>({moment:s,entries:o.filter(i=>i.at.includes(s))})).filter(s=>s.entries.length>0),totals:{breadcrumbs:o.filter(s=>s.kind==="breadcrumb").length,guardrails:o.filter(s=>s.kind==="guardrail").length,disabled:o.filter(s=>s.disabled!==null).length}}}var $t=1;function pn(e,n=""){let t=n,r=$t,o=[];for(let s of e.split(`
|
|
25
|
+
`)){if(s.trim()==="")continue;let i;try{i=JSON.parse(s)}catch{continue}if(typeof i!="object"||i===null||Array.isArray(i))continue;let a=i;if(typeof a.v=="number"){r=a.v,typeof a.session=="string"&&(t=a.session);continue}(a.rail==="guard"||a.rail==="brief"||a.rail==="compaction")&&o.push(a)}return{v:r,session:t,steps:o}}function vt(e){return{v:$t,session:e}}async function Et({load:e,recording:n,settings:t,marks:r={}}){let o=[],s=[],i=[],a=r;for(let d of n.steps){if(d.rail==="compaction"){a=e.ok?dn(a,e.entries):a;let u={kind:"compaction"};s.push(u),o.push({step:d,effects:[],rows:[u]});continue}if(d.rail==="guard"){let u=await Me({load:e,event:{moment:d.moment,file:d.file,command:d.command,staged:d.staged,turn:d.turn,wearing:d.wearing},world:Ce(d.world??{},d.staged,i)}),p=ge(d.moment,u,d.moment==="commit"?d.staged?.length??0:1);s.push(...p),o.push({step:d,effects:u.effects,rows:p});continue}let l=an({load:e,event:{moment:d.moment,path:d.path,wearing:d.wearing,tokens:d.tokens},marks:a,settings:t});a=l.marks;let c=l.notices.length===0?[]:ge(d.moment,{effects:l.notices,tallies:[]},1);s.push(...c),o.push({step:d,effects:l.notices,rows:c})}return{steps:o,rows:s,unanswered:i,marks:a}}function Ct(e,n){let t=i=>i.filter(a=>a.kind==="guardrail"||a.kind==="breadcrumb").map(a=>a.kind==="guardrail"?`guardrail ${A(a,"moment")??""} ${A(a,"id")??"\u2014"} ${a.out} ${gn(a)??""}`.trim():`breadcrumb ${A(a,"moment")??""} ${A(a,"id")??"\u2014"} ${A(a,"cause")??""}`.trim()),r=t(e),o=t(n),s=[];for(let i=0;i<Math.max(r.length,o.length);i++){let a=r[i],d=o[i];a!==d&&(a!==void 0&&s.push({side:"replay",at:i,row:a}),d!==void 0&&s.push({side:"live",at:i,row:d}))}return s}function yn(e){let n={},t={},r,o;return{world:{exec:async s=>{let i=await e.exec(s);return n[s]={stdout:i.stdout,stderr:i.stderr,code:i.code},i},fs:{read:async s=>{let i=await e.fs.read(s);return t[s]=i,i},exists:async s=>{let i=await e.fs.exists(s);return i&&(t[s]??=""),i}},git:{diff:async s=>{let i=await e.git.diff(s);return r??=i,i},stagedFiles:async()=>{let s=await e.git.stagedFiles();return o=[...s],s}}},taken:()=>({...Object.keys(n).length>0?{exec:n}:{},...Object.keys(t).length>0?{fs:t}:{},...r===void 0?{}:{gitDiff:r},...o===void 0?{}:{staged:o}})}}import{appendFileSync as Ot,closeSync as Zo,existsSync as ke,mkdirSync as be,openSync as Xo,readFileSync as Be,readSync as es,readdirSync as Tt,statSync as ns,writeFileSync as pe}from"node:fs";import{dirname as hn,join as ts}from"node:path";function ae(e){try{let n=G(e);be(n,{recursive:!0});let t=`${n}/.gitignore`;return ke(t)||pe(t,pt),!0}catch{return!1}}function we(e){try{return ke(yt(e))}catch{return!1}}function Ge(e,n,t){try{return un(Be(cn(e,n,t),"utf8"))}catch{return un(null)}}function kn(e,n,t,r){try{let o=cn(e,n,t);return ae(e),be(hn(o),{recursive:!0}),pe(o,JSON.stringify(r)),!0}catch{return!1}}function Pt(e,n,t,r,o){let s=Ge(e,n,t),i=ut(s.categories,r,o);return i.fresh&&i.faults.length===0&&kn(e,n,t,{...s,categories:i.wearing}),i}var rs=4096,mn=new Map;function os(e){let n=null;try{let t=ns(e).size,r=Math.min(t,rs);if(r<=0)return"";let o=Buffer.alloc(r);return n=Xo(e,"r"),es(n,o,0,r,t-r),o.toString("utf8")}catch{return""}finally{if(n!==null)try{Zo(n)}catch{}}}function bn(e,n){try{if(n.length===0)return!0;let{root:t,session:r,branch:o}=e,s=e.now??(()=>new Date),i=ln(t,r);ae(t),be(hn(i),{recursive:!0}),ke(i)||(pe(i,`${JSON.stringify(xt(r,o,t,s().toISOString()))}
|
|
26
|
+
`),mn.set(i,1));let a=mn.get(i)??wt(os(i)),d="";for(let l of n)d+=`${JSON.stringify({ts:s().toISOString(),seq:a,...l})}
|
|
27
|
+
`,a+=1;return mn.set(i,a),Ot(i,d),!0}catch{return!1}}function wn(e){let n=[];try{let t=Be(e,"utf8");for(let r of t.split(`
|
|
28
|
+
`))if(r.trim())try{let o=JSON.parse(r);typeof o.kind=="string"&&n.push(o)}catch{continue}}catch{}return n}function ss(e,n){return wn(ln(e,n))}function is(e){try{return Tt(ts(G(e),"log")).filter(n=>n.endsWith(".jsonl")).map(n=>n.slice(0,-6)).sort()}catch{return[]}}function At(e){return is(e).map(n=>({session:n,rows:ss(e,n)}))}function jt(e){try{return new Set(Tt(Ne(e)).filter(n=>!n.startsWith(".")))}catch{return new Set}}function Mt(e,n){try{ae(e),be(Ne(e),{recursive:!0})}catch{return 0}let t=0;for(let r of n)try{pe(kt(e,r),""),t+=1}catch{}return t}function xn(e){try{return ke(mt(e))}catch{return!1}}function Sn(e,n,t){try{if(t.length===0)return!0;let r=ht(e,n);return ae(e),be(hn(r),{recursive:!0}),ke(r)||pe(r,`${JSON.stringify(vt(n))}
|
|
29
|
+
`),Ot(r,`${t.map(o=>JSON.stringify(o)).join(`
|
|
30
|
+
`)}
|
|
31
|
+
`),!0}catch{return!1}}function Nt(e){let n=e.slice(e.lastIndexOf("/")+1).replace(/\.jsonl$/,"");try{return pn(Be(e,"utf8"),n)}catch{return pn("",n)}}function _t(e,n,t,r,o=new Date){try{return ae(e),pe(fn(e,r),`${JSON.stringify({session:n,agent:t,ts:o.toISOString()})}
|
|
32
|
+
`),!0}catch{return!1}}function Ft(e,n,t=Date.now()){let r=null;try{r=JSON.parse(Be(fn(e,n),"utf8"))}catch{}return bt(r,"commit",t)}var T="flow.config.ts";function as(e){let n=[];for(let[,,t]of e.matchAll(/\b(?:from|import)\s*\(?\s*(['"])([^'"\n]+)\1/g)){if(t===void 0||!t.startsWith("."))continue;let r=t.replace(/^\.\//,"");r===""||r.split("/").includes("..")||n.includes(r)||n.push(r)}return n}function $n(e){let n=[T];for(let t of as(e??"")){n.includes(t)||n.push(t);let r=t.includes("/")?`${t.slice(0,t.lastIndexOf("/"))}/**`:null;r!==null&&!n.includes(r)&&n.push(r)}return n}var vn=["session-start","pre-tool-use","post-tool-use","stop","notification"];function En(e){return vn.includes(e)}var Bt={"session-start":"SessionStart","pre-tool-use":"PreToolUse","post-tool-use":"PostToolUse",stop:"Stop",notification:"Notification"},Le={guard:["write","delete","command","commit","turn-end"],brief:["session","touch"]};function S(e){return typeof e=="string"?e:typeof e=="number"||typeof e=="boolean"?String(e):""}function H(e,n){return n&&e.startsWith(`${n}/`)?e.slice(n.length+1):e}function It(e,{old_string:n="",new_string:t="",replace_all:r=!1}){if(n==="")return t;if(r)return e.split(n).join(t);let o=e.indexOf(n);return o===-1?null:e.slice(0,o)+t+e.slice(o+n.length)}function ds(e,n,t){let r=n.file_path;if(!r)return null;let o=H(S(r),t.root);if(e==="Write")return{path:o,content:S(n.content??"")};if(e==="Edit"){let s=It(t.read(o)??"",n);return s===null?null:{path:o,content:s}}if(e==="MultiEdit"){let s=t.read(o)??"";for(let i of n.edits??[])if(s=It(s,i),s===null)return null;return{path:o,content:s}}return null}var ls=[{words:["rm"],renames:!1},{words:["git","rm"],renames:!1},{words:["mv"],renames:!0},{words:["git","mv"],renames:!0}],cs=new Set([";","&&","||","|","&","|&",`
|
|
33
|
+
`]);function us(e){let n=Ae(e);if(n===null)return[];let t=[],r=[],o=()=>{let s=r;r=[];for(let i of ls){if(!i.words.every((d,l)=>s[l]===d))continue;let a=s.slice(i.words.length).filter(d=>!d.startsWith("-"));t.push(...i.renames?a.length>=2?a.slice(0,-1):[]:a);return}};for(let s of n)cs.has(s)?o():r.push(s);return o(),t}function Cn(e,n,t){switch(e){case"session-start":return[{rail:"brief",moment:"session"}];case"pre-tool-use":{let r=S(n.tool_name),o=n.tool_input??{};if(!r)return[];if(r==="Bash"){let i=S(o.command);if(!i)return[];let a=[{rail:"guard",moment:"command",command:i}];for(let d of us(i)){let l=H(d,t.root),c=t.read(l);c!==null&&a.push({rail:"guard",moment:"delete",file:{path:l,content:c}})}return a}let s=ds(r,o,t);return s===null?[]:[{rail:"guard",moment:"write",file:s}]}case"post-tool-use":{let r=fs(n,t.root);return r===null?[]:[{rail:"brief",moment:"touch",path:r}]}case"stop":return n.stop_hook_active===!0?[]:[{rail:"guard",moment:"turn-end",turn:t.turn?.()??[]}];case"notification":return[]}}function fs(e,n){let t=e.tool_input??{},r=t.file_path??t.notebook_path??t.path??t.glob;return r?H(S(r),n):null}var D={stdout:"",stderr:"",exitCode:0},gs={write:{head:"flow \u2014 blocked before the write landed",tail:"Adjust the change so it passes, then retry."},delete:{head:"flow \u2014 blocked before the delete",tail:"Keep the file, or change the rule that protects it."},command:{head:"flow \u2014 command blocked before it ran",tail:"Adjust the command, then retry."},commit:{head:"flow \u2014 commit blocked",tail:"Fix the above, then commit again."},"turn-end":{head:"flow \u2014 turn held open",tail:"Do the above, then end your turn."}};function ps(e,n){return e.rail!=="guard"||e.moment!=="write"&&e.moment!=="delete"?!1:e.file!==void 0&&M(e.file.path,n)}function Gt(e,n,t){let r=e.filter(o=>o.rail==="guard");return{refused:r.filter(o=>o.moment!=="turn-end"&&!ps(o,n)).map(o=>({moment:o.moment,block:se(t)})),shown:e.filter(o=>o.rail==="brief").map(()=>({entry:null,cause:"fault",body:t})),told:r.some(o=>o.moment==="turn-end")?t:null}}function Dt(e){return e.refusals.length>0?2:e.green?0:1}function ys(e){return e.filter(n=>n.body.trim()!=="").map(n=>`# breadcrumb: ${n.entry??"flow"} (${n.cause})
|
|
34
|
+
${n.body}`).join(`
|
|
35
|
+
|
|
36
|
+
`)}function On(e,n){let t=We(n.refused);if(t!==null)return t;let r=ys(n.shown);return r===""?D:{stdout:ms(e,r),stderr:"",exitCode:0}}function We(e){let n=e[0];if(n===void 0)return null;let t=gs[n.moment],r=e.map(o=>gt(o.block)).join(`
|
|
37
|
+
|
|
38
|
+
`);return{stdout:"",stderr:`
|
|
39
|
+
${t.head}:
|
|
40
|
+
|
|
41
|
+
${r}
|
|
42
|
+
|
|
43
|
+
${t.tail}
|
|
44
|
+
`,exitCode:2}}function ms(e,n){return JSON.stringify({hookSpecificOutput:{hookEventName:Bt[e],additionalContext:n}})}function Lt(e,n,t){let r=n==="unreadable"?`could not READ its payload from stdin (${t})`:`could not PARSE its payload (${t})`;return`[flow hook ${e}] the guard ${r}.`}function Tn(e,n=0){let t=Pn(e);if(t===null)return null;let r=S(t.timestamp);return{kind:S(t.type),record:t,line:n,ts:r===""?null:r}}function Pn(e){if(e===null||e.trim()==="")return null;let n;try{n=JSON.parse(e)}catch{return null}return typeof n=="object"&&n!==null&&!Array.isArray(n)?n:null}function He(e){let n=[],t=e.split(`
|
|
45
|
+
`);for(let r=0;r<t.length;r++){let o=Tn(t[r],r+1);o!==null&&n.push(o)}return n}function hs(e){return(e?.input_tokens??0)+(e?.cache_read_input_tokens??0)+(e?.cache_creation_input_tokens??0)}function Wt(e){let n=e.split(`
|
|
46
|
+
`);for(let t=n.length-1;t>=0;t--){let r=Tn(n[t])?.record.message;if(r?.usage)return hs(r.usage)}return 0}var An=new Set(["Edit","Write","MultiEdit","NotebookEdit"]),ks=["<system-reminder>","<command-name>","<command-message>","<local-command","<user-prompt-submit-hook","Caveat:","Result of calling","This session is being continued","[Request interrupted","DO NOT respond","<budget:","tool_use_id"];function bs(e){let n=e.trimStart();return ks.some(t=>n.startsWith(t))||n.slice(0,40).includes("<system-reminder>")}function jn(e){if(e.kind!=="user"||e.record.isMeta===!0)return null;let n=e.record.message?.content;return typeof n!="string"||bs(n)?null:n}function Ht(e,n=""){let t=He(e),r=0;for(let s=t.length-1;s>=0;s--)if(jn(t[s])!==null){r=s;break}let o=[];for(let s of t.slice(r)){let i=s.record.message?.content;if(Array.isArray(i))for(let a of i){let d=a;if(!d||d.type!=="tool_use")continue;let l=S(d.name),c=d.input??{},u=c.file_path??c.notebook_path;An.has(l)&&u?o.push({did:"edit",path:H(S(u),n)}):l==="Bash"&&c.command&&o.push({did:"run",command:S(c.command)})}}return o}function ws(e,n=8e3){for(let t of e.split(`
|
|
47
|
+
`)){let r=Tn(t),o=r===null?null:jn(r);if(o!==null)return o.slice(0,n)}return""}function Ut(e){let n=e.trim();return/^ref:\s*refs\/heads\/(.+)$/.exec(n)?.[1]??(n===""?null:n.slice(0,12))}function Kt(e){return e.endsWith(".jsonl")?`${e.slice(0,-6)}.meta.json`:null}function Vt(e){let n=typeof e.payload.agent_id=="string"&&e.payload.agent_id!=="",t=Pn(e.sidecar),r=typeof t?.agentType=="string"?t.agentType:void 0,o=typeof t?.description=="string"?t.description:void 0;return{head:e.transcript===null?"":ws(e.transcript),subagent:n||t!==null,...r===void 0?{}:{agentType:r},...o===void 0?{}:{description:o}}}var xs=["file_path","notebook_path"];function qt(e,n=""){let t=S(e.tool_name);if(!t)return null;let r=e.tool_input??{};for(let s of xs)if(r[s])return{kind:"tool",tool:t,path:H(S(r[s]),n),edit:An.has(t)};if(t==="Bash"&&r.command)return{kind:"tool",tool:t,command:S(r.command)};if(!r.pattern)return{kind:"tool",tool:t};let o=r.path?{scope:H(S(r.path),n)}:{};return{kind:"tool",tool:t,pattern:S(r.pattern),...o}}var Ss=["GIT_DIR","GIT_INDEX_FILE","GIT_WORK_TREE","GIT_PREFIX","GIT_OBJECT_DIRECTORY","GIT_COMMON_DIR","GIT_INDEX_VERSION","GIT_QUARANTINE_PATH","GIT_QUARANTINE_ID"];function Jt(e){let n=new Set(Ss);return Object.fromEntries(Object.entries(e).filter(([t])=>!n.has(t)))}function zt(e){return e.replace(/[^a-zA-Z0-9]/g,"-")}function Yt(e){let n=new Set(e),t=[],r=[],o=[];for(let s of e){let i=s.split("/");if(i.length===1&&s.endsWith(".jsonl")){t.push({path:s,meta:null,session:s.slice(0,-6),agent:null});continue}if(i.length===3&&i[1]==="subagents"&&s.endsWith(".jsonl")){let a=`${s.slice(0,-6)}.meta.json`;r.push({path:s,meta:n.has(a)?a:null,session:i[0]??"",agent:(i[2]??"").slice(0,-6)});continue}i.length===3&&i[1]==="subagents"&&s.endsWith(".meta.json")||o.push(s)}return{transcripts:[...t,...r],ignored:o}}function Qt(e){let n=Pn(e);if(n===null)return null;let t=r=>typeof n[r]=="string"&&n[r]!==""?n[r]:null;return{agentType:t("agentType"),description:t("description"),toolUseId:t("toolUseId"),model:t("model"),spawnDepth:typeof n.spawnDepth=="number"?n.spawnDepth:null}}function Zt(e){let n=null,t=null,r=null;for(let o of He(e))if(n??=S(o.record.cwd)||null,t??=S(o.record.gitBranch)||null,r??=o.ts,n!==null&&t!==null&&r!==null)break;return{cwd:n,branch:t,started:r}}var Rs="Agent";function Xt(e){let n=new Map;for(let t of He(e)){if(t.kind!=="assistant")continue;let r=t.record.message?.content;if(Array.isArray(r))for(let o of r){let s=o;if(!s||s.type!=="tool_use"||s.name!==Rs)continue;let i=S(s.id);if(i==="")continue;let a=s.input??{};n.set(i,{toolUseId:i,asked:S(a.subagent_type)||null,description:S(a.description)||null,ts:t.ts,line:t.line})}}return n}var $s=/\x1b\[[0-9;]*m/g;function er(e){return S(e).replace($s,"")}function de(e,n=140){let t=er(e).replace(/\s+/g," ").trim();return t.length>n?`${t.slice(0,n-1)}\u2026`:t}function nr(e,n=""){let t=[];for(let r of He(e)){let o=r.record.message?.content;if(r.kind==="user"){let s=jn(r);if(s!==null){t.push({line:r.line,kind:"prompt",text:s});continue}if(!Array.isArray(o))continue;for(let i of o){let a=i;!a||a.type!=="tool_result"||t.push({line:r.line,kind:"result",id:S(a.tool_use_id),content:vs(a.content),failed:a.is_error===!0})}continue}if(!(r.kind!=="assistant"||!Array.isArray(o)))for(let s of o){let i=s;if(!i||i.type!=="tool_use")continue;let a=i.input??{};t.push({line:r.line,kind:"use",ts:r.ts,id:S(i.id),name:S(i.name),input:a,path:H(S(a.file_path??a.notebook_path),n)})}}return t}function vs(e){return typeof e=="string"?e:Array.isArray(e)?e.map(n=>n.text??"").join(`
|
|
48
|
+
`):e===void 0?"":JSON.stringify(e)}var Es=new RegExp([String.raw`\b(actually|hold on|nope|revert|undo|instead|hmm)\b`,String.raw`\b(wait|no)[,!.](\s|$)`,String.raw`\b(stop|don'?t|do not)\s`,String.raw`\bthat'?s (wrong|not right|not what|not)\b`,String.raw`\byou (broke|shouldn'?t|missed|forgot|didn'?t)\b`,String.raw`\bwhy (did|are) you\b`,String.raw`\b(rather than|let'?s not)\b`].join("|"),"i");function Cs(e){return Es.test(e)}var Os=new Set(["set","node","npx","just","cd","export","echo","then","fi","do","done","if","for","while","source"]);function Ts(e){if(e.startsWith("["))return!1;let n=e.indexOf(":");if(n<0)return!1;let t=e.slice(0,n).trim();return t!==""&&/^[@A-Za-z_][\w-]*(\s|$)/.test(t)&&!t.includes("=")}function tr(e){let n=new Set,t=!1;for(let r of e.split(`
|
|
49
|
+
`)){if(r.trim()==="")continue;let o=/^\s/.test(r),s=r.trim();if(!s.startsWith("#")){if(!o){t=Ts(s);continue}if(t)for(let i of s.split(/&&|\|\||\||;/)){let a=i.trim().split(/\s+/),d=(a[0]??"").replace(/^@/,""),l=d==="npx"?a[1]??"":d;l===""||!/^[A-Za-z]/.test(l)||l.includes("/")||Os.has(l)||n.add(l)}}}return[...n]}function Ps(e,n){let t=er(e);if(/<<-?\s*['"]?\w/.test(t))return{recipes:[],bypasses:[],commits:!1};let r=[];for(let s of t.matchAll(/(?:^|\n|;|\||&&|\()\s*just\s+([a-z][\w-]*)/g))r.push(s[1]);let o=[];for(let s of n){let i=s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),a=new RegExp(`(?:^|\\n|;|\\||&&|\\(|node_modules/\\.bin/|npx\\s+)\\s*(?:\\./)?${i}\\b`),d=new RegExp(`just\\s+\\S*\\b${i}\\b`);a.test(t)&&!d.test(t)&&o.push(s)}return{recipes:r,bypasses:o,commits:/\bgit\s+commit\b/.test(t)}}var As=5,js=3;function rr(e,n=[]){let t=0,r=0,o=0,s={},i={},a={},d=[],l=[],c=[],u=[],p=[],g={},m=null,w=0,$=0,b=0,P=()=>{b>=As&&m!==null&&u.push({path:m,count:b,from:w,to:$})};for(let y of e){if(y.kind==="prompt"){d.push({line:y.line,text:de(y.text,120)}),Cs(y.text)&&l.push({line:y.line,text:de(y.text,140)});continue}if(y.kind!=="use")continue;if(An.has(y.name)){y.name==="Write"?r+=1:t+=1;let x=Fe(y.path);x&&y.path===m?(b+=1,$=y.line):(P(),m=x?y.path:null,w=y.line,$=y.line,b=x?1:0),x&&(a[y.path]=(a[y.path]??0)+1);continue}if(y.name==="Read"){Fe(y.path)&&(a[y.path]=(a[y.path]??0)+1);continue}if(y.name!=="Bash")continue;let O=S(y.input.command),C=Ps(O,n);for(let x of C.recipes)s[x]=(s[x]??0)+1;for(let x of C.bypasses)i[x]=(i[x]??0)+1,c.push({line:y.line,tool:x,text:de(O,120)});C.commits&&(o+=1);let k=de(O,400);(g[k]??=[]).push(y.line)}P();for(let[y,O]of Object.entries(g))O.length>=js&&p.push({command:y,count:O.length,lines:O});return{stats:{edits:t,writes:r,commits:o,recipes:s,bypasses:i},prompts:d,corrections:l,bypassSites:c,loops:u,retries:p,touched:a}}function or(e){let n=(l,c)=>l.map(u=>({...u,session:c})),t={},r={},o={},s={prompts:[],corrections:[],bypassSites:[],loops:[],retries:[]},i=0,a=0,d=0;for(let{session:l,read:c}of e){s.prompts.push(...n(c.prompts,l)),s.corrections.push(...n(c.corrections,l)),s.bypassSites.push(...n(c.bypassSites,l)),s.loops.push(...n(c.loops,l)),s.retries.push(...n(c.retries,l)),i+=c.stats.edits,a+=c.stats.writes,d+=c.stats.commits;for(let[u,p]of Object.entries(c.stats.recipes))t[u]=(t[u]??0)+p;for(let[u,p]of Object.entries(c.stats.bypasses))r[u]=(r[u]??0)+p;for(let[u,p]of Object.entries(c.touched))o[u]=(o[u]??0)+p}return{stats:{edits:i,writes:a,commits:d,recipes:t,bypasses:r},...s,touched:o}}var Ms=[".claude/settings.json",".claude/settings.local.json",".claude/agents/**"];function sr(e){return[...$n(e),...Ms]}function Ns(e,n){return e!==""&&M(e,n.flatMap(t=>[t,`**/${t}`]))}function ir(e,n,t){let r=e.filter(i=>i.kind==="guardrail"&&i.out==="deny"&&typeof i.ts=="string").map(i=>({entry:typeof i.id=="string"?i.id:null,ts:i.ts})),o=n.filter(i=>i.kind==="use").filter(i=>(i.name==="Edit"||i.name==="Write")&&i.ts!==null&&Ns(i.path,t)).sort((i,a)=>i.ts.localeCompare(a.ts)),s=[];for(let i of r){let a=o.find(d=>d.ts>i.ts);a!==void 0&&s.push({entry:i.entry,line:a.line,path:a.path,gapSeconds:Math.round((Date.parse(a.ts)-Date.parse(i.ts))/1e3)})}return s}function De(e){let n=Date.parse(String(e.started));return Number.isFinite(n)?n:e.mtimeMs}function ar(e,n,t={}){let{limit:r=null,all:o=!1,largest:s=!1,since:i=null}=t,a=e.filter(m=>!n.has(m.id)),d=(m,w)=>De(w)-De(m)||m.id.localeCompare(w.id),l=(m,w)=>w.bytes-m.bytes||d(m,w),c=[...a].sort(s?l:d),u=[],p=c;if(i!==null){p=c.filter(m=>De(m)>=i);for(let m of c)De(m)<i&&u.push({id:m.id,reason:"since"})}let g=p;if(!o&&r!==null&&r>=0&&p.length>r){g=p.slice(0,r);for(let m of p.slice(r))u.push({id:m.id,reason:"limit"})}return{analyse:g,excluded:u,counts:{backlog:a.length,analysed:g.length,excluded:u.length}}}function dr({armed:e,rows:n,withRecord:t,analysed:r}){if(!e)return{blocked:'the record is NOT ARMED \u2014 no stream has ever been written here, so there are no numbers. This is not "nothing happened".',warn:[]};let o=[];return r>0&&t===0?o.push("none of the conversations read has a stream of its own \u2014 the hooks did not record for them, so everything below is narrative only."):n===0&&o.push("the record is armed and holds no events for what was read."),{blocked:null,warn:o}}function lr(e){let{session:n,agent:t,meta:r,spawns:o}=e,s=r?.toolUseId===void 0||r.toolUseId===null?void 0:o.get(r.toolUseId);return{session:n,agent:t,agentType:r?.agentType??null,description:r?.description??s?.description??null,model:r?.model??null,asked:s?.asked??null,at:s?.ts??null,line:s?.line??null}}function cr(e,n){let t=je(n);return Object.entries(e).filter(([r])=>!t.some(o=>he(o,r))).map(([r,o])=>({path:r,touches:o})).sort((r,o)=>o.touches-r.touches||r.path.localeCompare(o.path))}var xe=5;function ur(e){let n=[];e.health.blocked!==null&&n.push(`\u2717 ${e.health.blocked}`);for(let l of e.health.warn)n.push(`\u26A0 ${l}`);let{span:t,headline:r}=e.metrics;n.push(`flow facts \u2014 ${e.coverage.analysed} of ${e.selection.counts.backlog} unread conversations \xB7 ${e.coverage.withRecord} with a record \xB7 `+(e.store.exists?e.store.dir:`no store at ${e.store.dir}`),` recorded ${t.tools.toLocaleString()} tool calls \xB7 ${t.sessions} chats \xB7 ${t.days}d`+(t.ample?"":" (too thin to call anything dead)"),` blocks ${r.blocks}`," lead "+(r.lead.median===null?"nothing measurable yet":`median ${r.lead.median} tool calls`));for(let l of r.gaps.slice(0,xe))n.push(` gap ${l.area} \u2014 ${l.edits} edit${l.edits===1?"":"s"}, nothing watches it`);r.dead.length>0&&n.push(` dead ${r.dead.join(" \xB7 ")}`),r.retire.length>0&&n.push(` retire ${r.retire.join(" \xB7 ")} \u2014 bound, never once reached`),r.quiet.length>0&&n.push(` quiet ${r.quiet.map(l=>`${l.id} (${l.daysSince}d)`).join(" \xB7 ")}`);let{stats:o,corrections:s,loops:i,retries:a,bypassSites:d}=e.narrative;n.push(` session ${o.edits} edits \xB7 ${o.writes} writes \xB7 ${o.commits} commits \xB7 ${s.length} corrections \xB7 ${i.length} edit loops \xB7 ${a.length} retries \xB7 ${d.length} bypasses`);for(let l of e.uncovered.slice(0,xe))n.push(` unwatched ${l.path} \u2014 touched ${l.touches}\xD7, no entry reaches it`);for(let l of e.actors.slice(0,xe))n.push(` actor ${l.agentType??"unknown"}${l.asked!==null&&l.asked!==l.agentType?` (spawned as ${l.asked})`:""}`+(l.description===null?"":` \u2014 ${l.description}`));e.actors.length>xe&&n.push(` \u2026and ${e.actors.length-xe} more`);for(let l of e.weakened)n.push(` \u26A0 the guard was edited after ${l.entry??"a block"} refused \u2014 ${l.path}:L${l.line}, ${l.gapSeconds}s later`);return e.marked!==null&&n.push(` marked ${e.marked} conversation(s) read`),n}var _s=24;function fr(e){if(!e.startsWith("file://"))return!1;let n=e.split(/[?#]/)[0]??e;return!n.endsWith(".ts")||n.endsWith(".d.ts")?!1:!n.includes("/node_modules/")}function Ue(e,n,t){return/Unexpected token|Unknown file extension|strip|SyntaxError/i.test(n)?`flow: ${e} could not be loaded as TypeScript. flow needs Node >= ${_s}, where type stripping is on by default; this is node ${t}. (${n})`:`flow: ${e} could not be loaded \u2014 ${n}`}var Fs=[{hook:"session-start",matcher:"startup|resume|clear|compact"},{hook:"pre-tool-use",matcher:"*"},{hook:"post-tool-use",matcher:"Read|Glob|Grep|Edit|Write|Bash"},{hook:"stop"}],gr=Fs.map(({hook:e,matcher:n})=>({event:Bt[e],command:`flow hook ${e}`,...n===void 0?{}:{matcher:n}}));function Is(e){let n=/(^|[\s"'/\\])(?:flow|flow\.mjs)["']?\s+hook\s+([a-z-]+)\s*$/.exec(e.trim());return n?.[2]!==void 0&&En(n[2])}function pr(e){let n=e&&typeof e=="object"?e:{},t=n.hooks&&typeof n.hooks=="object"?n.hooks:{},r={};for(let[o,s]of Object.entries(t))Array.isArray(s)&&(r[o]=s);return r}function yr(e){let n=[];for(let[t,r]of Object.entries(pr(e)))r.some(s=>{let i=s?.hooks;return Array.isArray(i)?i.some(a=>{let d=a?.command;return typeof d=="string"&&Is(d)}):!1})&&n.push(t);return n}function Bs(e){let n=e&&typeof e=="object"?e:{},t=new Set(yr(e)),r=gr.filter(i=>!t.has(i.event));if(r.length===0)return{settings:n,added:[]};let o=pr(e),s={...n.hooks};for(let i of r)s[i.event]=[...o[i.event]??[],{...i.matcher===void 0?{}:{matcher:i.matcher},hooks:[{type:"command",command:i.command}]}];return{settings:{...n,hooks:s},added:r.map(i=>i.event)}}var z=".githooks",Y=`${z}/pre-commit`,Mn=`git config core.hooksPath ${z}`,mr="add `flow commit $(git diff --cached --name-only)` to it",Gs=`#!/bin/sh
|
|
50
|
+
# The flow commit gate. Written by \`flow init\`; \`${Mn}\` arms it.
|
|
51
|
+
#
|
|
52
|
+
# Every at(commit) guardrail runs over the staged set and a block exits non-zero, which is how git
|
|
53
|
+
# refuses the commit. This is the half of the promise the PreToolUse rail cannot make: it fires for
|
|
54
|
+
# a human's commit, a merge and CI, with no session in the room.
|
|
55
|
+
staged=$(git diff --cached --name-only --diff-filter=ACMR)
|
|
56
|
+
[ -z "$staged" ] && exit 0
|
|
57
|
+
# shellcheck disable=SC2086
|
|
58
|
+
exec flow commit $staged
|
|
59
|
+
`;function hr(e){return e!==null&&/(^|[\s"'/\\])flow\b[^\n]*\bcommit\b/m.test(e)}var Ds=String.raw`// flow.config.ts — this repo's whole guard.
|
|
60
|
+
//
|
|
61
|
+
// A rule not reachable from this file does not run. Bind a pack with `+"`pack(x)`"+String.raw`, refine one of
|
|
62
|
+
// its entries with `+"`override(x.entry)`"+String.raw`, and ask `+"`flow status`"+String.raw` what is live.
|
|
63
|
+
|
|
64
|
+
import { defineConfig } from "@jawache/flow";
|
|
65
|
+
|
|
66
|
+
export default defineConfig([]);
|
|
67
|
+
`,Ls=String.raw`// flow.config.ts — this repo's whole guard, and the only file that turns anything on.
|
|
68
|
+
//
|
|
69
|
+
// A rule not reachable from here does not run. Everything below is ordinary code: a pack is a
|
|
70
|
+
// module, a check is a function of its ctx, and a category is a name with its recognizer aboard.
|
|
71
|
+
// Nothing defaults — an absent key is absent — so a mistake fails in your editor, not at 3am.
|
|
72
|
+
//
|
|
73
|
+
// flow status what is bound, at which moments, and what is not wired yet
|
|
74
|
+
// flow test every rule's own cases, run
|
|
75
|
+
|
|
76
|
+
import { command, commit, defineCategory, defineConfig, definePack, guardrail, pack } from "@jawache/flow";
|
|
77
|
+
import { flow } from "@jawache/flow/packs";
|
|
78
|
+
|
|
79
|
+
// A category is a name and the HOST-WRITTEN evidence that recognises it — never a claim a session
|
|
80
|
+
// made about itself. This one is "the harness wrote a sidecar for you", which is what a spawned
|
|
81
|
+
// subagent is and a chat is not.
|
|
82
|
+
const subagent = defineCategory("subagent", (facts) => facts.subagent);
|
|
83
|
+
|
|
84
|
+
// Spelled in pieces on purpose. Written whole it would appear in this very file, and the gate below
|
|
85
|
+
// greps staged content — so the demo would refuse the commit that added it.
|
|
86
|
+
const MARKER = ["DO", "NOT", "COMMIT"].join("-");
|
|
87
|
+
|
|
88
|
+
export const demo = definePack("demo", {
|
|
89
|
+
noForcePush: guardrail()
|
|
90
|
+
.at(command)
|
|
91
|
+
.check((ctx) =>
|
|
92
|
+
/git\s+push\b[^\n]*(--force|(^|\s)-f(\s|$))/.test(ctx.command ?? "")
|
|
93
|
+
? ctx.fail("rewrites history other clones already have")
|
|
94
|
+
: ctx.ok(),
|
|
95
|
+
)
|
|
96
|
+
.message("Force-pushing rewrites history everyone else has. Push a correcting commit, or ask first.")
|
|
97
|
+
.test({ pass: ["git push origin main"], block: ["git push --force origin main"] }),
|
|
98
|
+
|
|
99
|
+
// No .on() here, and that is the sentence rather than an omission: an entry that names no paths is
|
|
100
|
+
// about THE COMMIT, so it is asked once and handed the whole staged set. (Name paths with .on()
|
|
101
|
+
// and the gate asks it once per staged file in scope instead, handing over each file.)
|
|
102
|
+
noMarkedFiles: guardrail()
|
|
103
|
+
.at(commit)
|
|
104
|
+
.check(async (ctx) => {
|
|
105
|
+
for (const path of ctx.staged ?? [])
|
|
106
|
+
if ((await ctx.fs.read(path)).includes(MARKER)) return ctx.fail(path + " carries the marker");
|
|
107
|
+
return ctx.ok();
|
|
108
|
+
})
|
|
109
|
+
.message("A file carrying the do-not-commit marker is staged. Take the marker out, or unstage the file.")
|
|
110
|
+
.test({
|
|
111
|
+
pass: [{ staged: ["a.txt"], world: { fs: { "a.txt": "fine" } } }],
|
|
112
|
+
block: [{ staged: ["a.txt"], world: { fs: { "a.txt": MARKER } } }],
|
|
113
|
+
}),
|
|
114
|
+
|
|
115
|
+
// The same act, a different wearer, a different answer — a rule bound to WHO rather than to what.
|
|
116
|
+
chatCommits: guardrail()
|
|
117
|
+
.at(commit)
|
|
118
|
+
.for(subagent)
|
|
119
|
+
.check((ctx) => ctx.fail((ctx.staged ?? []).length + " staged file(s)"))
|
|
120
|
+
.message("A subagent does not commit — hand the change back to the chat that spawned it.")
|
|
121
|
+
.test({ pass: [], block: [{ staged: ["a.txt"] }] }),
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
export default defineConfig([
|
|
125
|
+
// flow's own self-protection: what is steering you, a nudge when you edit it, and no deleting it.
|
|
126
|
+
// Its packs list is where YOUR OWN packs live. Everything here is in this file, so it is empty —
|
|
127
|
+
// move a pack out to a folder of its own and add that folder's glob, or the nudge and the delete
|
|
128
|
+
// refusal stop covering it.
|
|
129
|
+
pack(flow, { packs: [] }),
|
|
130
|
+
pack(demo),
|
|
131
|
+
]);
|
|
132
|
+
`;function Ws(e){return e?Ds:Ls}function kr(e){let n=e.settingsUnreadable?{settings:e.settings,added:[]}:Bs(e.settings);return{config:e.hasConfig?null:{path:T,body:Ws(e.empty)},gate:!e.isGit||e.gateText!==null?null:{path:Y,body:Gs,mode:493},gateKept:e.isGit&&e.gateText!==null&&!hr(e.gateText),notGit:!e.isGit,flowDir:!e.hasFlowDir,link:e.resolves?null:e.packageRoot,hooksPath:e.isGit&&e.hooksPath!==z,settings:n.added.length===0?null:{path:e.settingsPath,value:n.settings},registered:n.added,settingsUnreadable:e.settingsUnreadable?e.settingsPath:null}}function br(e,n){let t=[];e.config&&t.push(e.config.path),e.gate&&t.push(e.gate.path),e.flowDir&&t.push(`${J}/ \u2014 flow's own state, self-ignoring, never committed`),e.link&&t.push(`node_modules/@jawache/flow \u2192 ${e.link} (a link to the flow that is running \u2014 nothing here resolved @jawache/flow)`),e.settings&&t.push(`${e.settings.path} \u2014 ${e.registered.join(" \xB7 ")}`),e.hooksPath&&t.push(Mn);let r=t.length===0?["flow init \u2014 already set up here; nothing to do."]:["flow init \u2014 created:",...t.map(o=>` + ${o}`)];e.notGit&&r.push(" \xB7 not a git repo, so no commit gate was armed. Run `git init`, then `flow init` again."),e.gateKept&&r.push(` \xB7 kept the ${Y} already here \u2014 flow never overwrites one, so ${mr} yourself.`),e.settingsUnreadable!==null&&r.push(` \u2717 ${e.settingsUnreadable} is not valid JSON, so nothing was registered and NOTHING was written to it \u2014 every other key in that file is somebody's, and there is no safe merge into bytes nobody could parse. Fix the JSON (a trailing comma, or a // comment) and run \`flow init\` again. Until then no rail fires.`);for(let o of n)r.push(` \u2717 ${o}`);return r.push(" next: `flow status` \u2014 what is bound, and what is not wired yet."),r}function Hs(e){let n=yr(e.settings),t=gr.filter(o=>!n.includes(o.event)).map(o=>o.event),r=hr(e.gateText);return[{id:"config",ok:e.hasConfig,detail:e.hasConfig?e.configPath:`no ${T} in ${e.root} \u2014 \`flow init\` writes one, with a demo you can read in a minute.`},{id:"commit-gate",ok:r,detail:r?`${Y} runs \`flow commit\` over the staged set`:e.gateText===null?`no ${Y} \u2014 \`flow init\` arms it. Until then nothing runs at the commit.`:`${Y} is here but does not call flow \u2014 ${mr}.`},{id:"hooks-path",ok:e.hooksPath===z,detail:e.hooksPath===z?`core.hooksPath = ${z}`:`core.hooksPath is ${e.hooksPath??"unset"} \u2014 run \`${Mn}\`. It is per-clone, so every fresh checkout needs it.`},{id:"hooks",ok:t.length===0,detail:t.length===0?`${e.settingsPath} \u2014 ${n.join(" \xB7 ")}`:`${e.settingsPath} does not call flow for ${t.join(" \xB7 ")} \u2014 re-run \`flow init\`. No live rail fires until it does.`}]}function wr(e){return[...e.refusals.map(n=>`${n.code}: ${n.detail}`),...e.dark,...e.fittings.filter(n=>!n.ok).map(n=>`${n.id}: ${n.detail}`)]}function Us(e,n){return e==="guardrail"?en(n)&&Le.guard.includes(n):nn(n)&&Le.brief.includes(n)}function xr(e){let n=e.load?.ok===!0?e.load.entries:[],t=e.load?.ok===!1?e.load.refusals:[],r=t.length>0?[]:_e(n),o=r.filter(i=>i.disabled===null&&i.at.length>0&&!i.at.some(a=>Us(i.kind,a))).map(i=>i.id),s=Hs(e);return{root:e.root,version:e.version,armed:!e.off,configPath:e.configPath,moments:Ie(r,null),categories:[...new Set(n.flatMap(i=>i.categories))].sort(),session:e.session,dark:o,fittings:s,refusals:t,green:wr({refusals:t,dark:o,fittings:s}).length===0}}function Ks(e){let n=[(e.on?.length??0)>0?`on ${e.on?.join(" ")}`:"",(e.ignore?.length??0)>0?`not ${e.ignore?.join(" ")}`:"",e.for.length>0?`for ${e.for.join(" \xB7 ")}`:""].filter(o=>o!==""),r=[` ${e.disabled!==null?"\u25CB":e.kind==="guardrail"?"\u2717":"\u{1F35E}"} ${e.id}${n.length===0?"":` \u2014 ${n.join(" \xB7 ")}`}`];return e.disabled!==null?r.push(` off: ${e.disabled||"no reason given"}`):e.says!==null&&r.push(` ${de(e.says,100)}`),r}function Sr(e){if(!e.armed)return[`flow is OFF \u2014 every rail is silenced (${J}/off is there). Delete that file to turn it back on.`];let{breadcrumbs:n,guardrails:t,disabled:r}=e.moments.totals,o=[e.refusals.length>0?`flow ${e.version} \u2014 the config will not load, so every guardrail and breadcrumb here is OFF.`:`flow is ON \u2014 ${Rn(t,"guardrail")} \xB7 ${Rn(n,"breadcrumb")}`+(r===0?"":` \xB7 ${r} disabled`)+", every one resolved and able to fire",` config ${e.configPath}`,` state ${e.root}/${J}`," session "+(e.session===null?"no live session has marked this worktree yet":`${e.session.id}${e.session.agent===null?"":` \xB7 ${e.session.agent}`}, wearing ${e.session.wearing.length===0?"no category":e.session.wearing.join(" \xB7 ")}`)];e.categories.length>0&&o.push(` categories ${e.categories.join(" \xB7 ")} \u2014 what the entries below bind to`);for(let{moment:s,entries:i}of e.moments.moments){o.push(` ${s}`);for(let a of i)o.push(...Ks(a))}for(let s of e.fittings)o.push(` ${s.ok?"\u2713":"\u2717"} ${s.id}: ${s.detail}`);for(let s of e.refusals)o.push(` \u2717 ${s.code}: ${s.detail}`);for(let s of e.dark)o.push(` \u2717 ${s}: bound only at a moment nothing delivers here \u2014 it loads, it counts, and it will never fire. This adapter carries ${Le.guard.join(" \xB7 ")} for a guardrail and ${Le.brief.join(" \xB7 ")} for a breadcrumb.`);return o.push(e.green?"green \u2014 every rule loads, every fitting is in place.":`${Rn(wr(e).length,"red line")} \u2014 flow is NOT fully in force here.`),o}var Rn=(e,n)=>`${e} ${n}${e===1?"":"s"}`;var Xs=16*1024*1024;function Se(e,n){let t=Vs("bash",["-lc",e],{cwd:n,encoding:"utf8",maxBuffer:Xs,env:Jt(process.env)});return t.error?{stdout:"",stderr:`flow could not run a command: ${t.error.message}`,code:127}:{stdout:t.stdout,stderr:t.stderr,code:t.status??1}}function Cr(e,n){return Ys(n)?n:Ke(e,n)}function I(e,n){try{return Re(Cr(e,n),"utf8")}catch{return null}}function ei(e){return e.split(`
|
|
133
|
+
`).map(n=>n.trim()).filter(n=>n!=="")}function _n(e){return{exec:n=>Promise.resolve(Se(n,e)),fs:{read:n=>Promise.resolve(I(e,n)??""),exists:n=>Promise.resolve(vr(Cr(e,n)))},git:{diff:n=>Promise.resolve(Se(n?`git diff -- ${re(n)}`:"git diff",e).stdout),stagedFiles:()=>Promise.resolve(ei(Se("git diff --cached --name-only --diff-filter=ACM",e).stdout))}}}function Or(e){try{let n=Ke(e,".git"),t=qs(n).isDirectory()?n:Er(e,/gitdir:\s*(.+)/.exec(Re(n,"utf8"))?.[1]?.trim()??"");return Ut(Re(Ke(t,"HEAD"),"utf8"))}catch{return null}}function Fn(e){let n=Or(e),{session:t,agent:r}=Ft(e,n);return{branch:n,session:t,agent:r,wearing:r===null?[]:Ge(e,t,r).categories??[]}}function ni(e){return process.env.CLAUDE_PROJECT_DIR||e}var Rr=!1;function Tr(){Rr||(Rr=!0,Js({load(e,n,t){if(!fr(e))return t(e,n);let r=Re(Qs(e),"utf8");return{format:"module",shortCircuit:!0,source:zs(r,{mode:"strip",sourceUrl:e})}}}))}async function Ve(e){let n=Ke(e,T);if(!vr(n))return{kind:"none"};try{let r=(await import(Zs(n).href)).default;return!r||typeof r!="object"||!Array.isArray(r.bindings)?{kind:"broken",message:`flow: ${T} has no default export from \`defineConfig([\u2026])\` \u2014 that call IS the config, and its result is what flow reads.`}:{kind:"loaded",config:r,load:ue(r)}}catch(t){return{kind:"broken",message:Ue(T,t.message,process.versions.node)}}}function Nn(e){let n=null;return()=>(n??={value:e()},n.value)}function ti(e,n){let t=typeof e.transcript_path=="string"?e.transcript_path:"",r=Nn(()=>t===""?null:I(n,t));return{root:n,id:typeof e.session_id=="string"&&e.session_id!==""?e.session_id:"unknown",agent:typeof e.agent_id=="string"&&e.agent_id!==""?e.agent_id:"main",branch:Or(n),transcriptPath:t,transcript:r,tokens:Nn(()=>{let o=r();return o===null?0:Wt(o)}),turn:Nn(()=>{let o=r();return o===null?[]:Ht(o,n)})}}function ri(e,n,t){if(t.length===0)return{wearing:[],faults:[],fresh:!1};let r=e.transcriptPath===""?null:Kt(e.transcriptPath),o=Vt({payload:n,transcript:e.transcript(),sidecar:r===null?null:I(e.root,r)});return Pt(e.root,e.id,e.agent,o,t)}function $r(e,n){return{root:e,read:t=>I(e,t),turn:n.turn}}function oi(e,n){return n.text!==void 0?n.text:n.file===void 0?"":I(e,n.file)??""}function si(e){return e.rail==="guard"&&e.moment==="commit"?e.staged?.length??0:1}async function ii(e,n,t){let r=await Ve(t);if(r.kind==="none")return D;let o=we(t),s=ti(n,t);e==="pre-tool-use"&&!o&&s.id!=="unknown"&&_t(t,s.id,s.agent,s.branch);let i=$=>{if(o)return D;let b=$n(I(t,T)),P=Gt(Cn(e,n,$r(t,s)),b,$),y=On(e,P);return P.told===null||y.exitCode!==0?y:{...y,stderr:`${P.told}
|
|
134
|
+
`}};if(r.kind==="broken")return i(r.message);let{load:a,config:d}=r;if(!a.ok)return i(te(a.refusals));let l=a.entries,c=ri(s,n,ct(l)),u=Cn(e,n,$r(t,s)),p=[];if(e==="pre-tool-use"&&!o){let $=qt(n,t);$&&p.push($)}let g=[],m=xn(t),w=await ai({events:u,session:s,load:a,settings:d.settings,identity:c,off:o,hook:e,payload:n,rows:p,steps:m?g:null});return o||bn({root:t,session:s.id,branch:s.branch},p),m&&Sn(t,s.id,g),On(e,w)}async function ai(e){let{events:n,session:t,load:r,settings:o,identity:s,off:i,hook:a,payload:d,rows:l,steps:c}=e,u=[],p=[],g=_n(t.root),m=a==="session-start"&&d.source==="compact",w=Ge(t.root,t.id,t.agent),$=m&&r.ok?dn(w.marks,r.entries):w.marks;m&&!i&&l.push({kind:"compaction"}),m&&c?.push({rail:"compaction"});for(let b of n){if(b.rail==="guard"){let C=c===null?null:yn(g),k=await Me({load:r,event:{moment:b.moment,file:b.file,command:b.command,staged:b.staged,turn:b.turn,wearing:s.wearing},world:C?.world??g,faults:s.faults,off:i});c?.push({rail:"guard",moment:b.moment,...b.file===void 0?{}:{file:b.file},...b.command===void 0?{}:{command:b.command},...b.staged===void 0?{}:{staged:b.staged},...b.turn===void 0?{}:{turn:b.turn},wearing:s.wearing,world:C?.taken()??{}});for(let x of k.effects)x.do==="block"&&u.push({moment:b.moment,block:x});i||l.push(...ge(b.moment,k,si(b)));continue}c?.push({rail:"brief",moment:b.moment,...b.path===void 0?{}:{path:b.path},wearing:s.wearing,tokens:t.tokens()});let P=an({load:r,event:{moment:b.moment,path:b.path,wearing:s.wearing,tokens:t.tokens()},marks:$,settings:o,off:i}),y=[],O=[];for(let C of P.notices){let k=oi(t.root,C);if(k.trim()===""){C.entry!==null&&O.push(C.entry);continue}y.push(C),p.push({entry:C.entry,cause:C.cause,body:k})}$=di(P.marks,$,O),!i&&y.length>0&&l.push(...ge(b.moment,{effects:y,tallies:[]},1))}return!i&&$!==w.marks&&kn(t.root,t.id,t.agent,{...w,marks:$}),{refused:u,shown:p}}function di(e,n,t){if(t.length===0)return e;let r={};for(let[o,s]of Object.entries(e))t.includes(o)||(r[o]=s);for(let o of t){let s=n[o];s!==void 0&&(r[o]=s)}return r}async function li(e,n){let t=await Ve(e);if(t.kind==="none")return D;if(we(e))return D;if(t.kind==="broken")return We([{moment:"commit",block:se(t.message)}])??D;if(n.length===0)return D;let{branch:r,session:o,wearing:s}=Fn(e),{load:i}=t,a=xn(e)?yn(_n(e)):null,d=await Me({load:i,event:{moment:"commit",staged:n,wearing:s},world:a?.world??_n(e),off:!1});a!==null&&Sn(e,o,[{rail:"guard",moment:"commit",staged:n,wearing:s,world:a.taken()}]),bn({root:e,session:o,branch:r},ge("commit",d,n.length));let l=d.effects.filter(c=>c.do==="block").map(c=>({moment:"commit",block:c}));return We(l)??D}function ci(){if(process.stdin.isTTY)return{ok:!0,payload:{}};let e;try{e=Re(0,"utf8")}catch(t){return{ok:!1,why:"unreadable",detail:t.code??"unknown error"}}if(e.trim()==="")return{ok:!0,payload:{}};let n;try{n=JSON.parse(e)}catch{return{ok:!1,why:"unparseable",detail:`${e.length} bytes on stdin, and not JSON`}}return!n||typeof n!="object"||Array.isArray(n)?{ok:!1,why:"unparseable",detail:`JSON, but ${n===null?"null":Array.isArray(n)?"an array":typeof n} \u2014 a payload is an object`}:{ok:!0,payload:n}}async function Pr(e,n){try{if(!En(e))return D;let t=ci();if(!t.ok){let r=Lt(e,t.why,t.detail);return e==="pre-tool-use"?{stdout:"",stderr:`
|
|
135
|
+
${r}
|
|
136
|
+
`,exitCode:2}:{stdout:"",stderr:`${r}
|
|
137
|
+
`,exitCode:0}}return await ii(e,t.payload,ni(n))}catch(t){return{stdout:"",stderr:`[flow hook ${e}] the guard itself failed \u2014 ${t.message}
|
|
138
|
+
`,exitCode:0}}}async function Ar(e,n){let t=Er(n);return li(t,e.map(r=>H(r,t)))}import{existsSync as ui,readFileSync as fi,readdirSync as gi,statSync as pi}from"node:fs";import{homedir as In}from"node:os";import{join as ye}from"node:path";function qe(e){try{return fi(e,"utf8")}catch{return null}}function yi(e,n=In()){return ye(n,".claude","projects",zt(e))}function mi(e){let n=r=>{try{return gi(r,{withFileTypes:!0}).map(o=>o.isDirectory()?`${o.name}/`:o.name)}catch{return[]}},t=[];for(let r of n(e)){if(!r.endsWith("/")){t.push(r);continue}let o=r.slice(0,-1);for(let s of n(ye(e,o,"subagents")))s.endsWith("/")||t.push(`${o}/subagents/${s}`)}return t.sort()}function hi(e,n=In()){let t=yi(e,n);if(!ui(t))return{dir:t,exists:!1,transcripts:[],ignored:[]};let{transcripts:r,ignored:o}=Yt(mi(t));return{dir:t,exists:!0,transcripts:r.map(s=>{let i=ye(t,s.path),a=0,d=0;try{let l=pi(i);a=l.size,d=l.mtimeMs}catch{}return{ref:s,file:i,bytes:a,mtimeMs:d}}),ignored:o}}function ki(e){let n=Se("git ls-files",e);return n.code===0?n.stdout.split(`
|
|
139
|
+
`).filter(t=>t.trim()!==""):[]}function jr(e,n,t={}){let r=t.home??In(),o=t.nowMs??Date.now(),s=hi(e,r),i=[],a=new Map,d=new Map;for(let k of s.transcripts){if(k.ref.agent!==null){let V=d.get(k.ref.session)??[];V.push(k),d.set(k.ref.session,V);continue}let x=qe(k.file)??"",B=Zt(x);B.cwd!==null&&B.cwd!==e||(a.set(k.ref.session,x),i.push({id:k.ref.session,bytes:k.bytes,mtimeMs:k.mtimeMs,started:B.started,branch:B.branch}))}let l=t.branch?i.filter(k=>k.branch===t.branch):i,c=t.session?{analyse:l.filter(k=>k.id===t.session),excluded:[],counts:{backlog:l.length,analysed:l.filter(k=>k.id===t.session).length,excluded:0}}:ar(l,jt(e),t),u=At(e),p=new Map(u.map(k=>[k.session,k.rows])),g=n.ok?_e(n.entries):[],m=tr(qe(ye(e,"justfile"))??""),w=sr(qe(ye(e,T))),$=[],b=[],P=[],y=0;for(let k of c.analyse){let x=p.get(k.id)??[];x.length>0&&(y+=1);let B=a.get(k.id)??"",V=nr(B,e);$.push({session:k.id,read:rr(V,m)}),b.push(...ir(x,V,w));let $e=Xt(B);for(let Q of d.get(k.id)??[])P.push({...lr({session:k.id,agent:Q.ref.agent??"",meta:Q.ref.meta===null?null:Qt(qe(ye(s.dir,Q.ref.meta))),spawns:$e})})}let O=St({sessions:u,entries:g,nowMs:o}),C=or($);return{root:e,store:{dir:s.dir,exists:s.exists,ignored:s.ignored},selection:c,coverage:{analysed:c.analyse.length,withRecord:y,narrativeOnly:c.analyse.length-y},health:dr({armed:u.length>0,rows:u.reduce((k,x)=>k+x.rows.length,0),withRecord:y,analysed:c.analyse.length}),metrics:O,moments:Ie(g,O),terrain:Rt({paths:ki(e),heat:O.heat,entries:g}),narrative:C,uncovered:cr(C.touched,g),actors:P,weakened:b,marked:t.mark===!0?Mt(e,c.analyse.map(k=>k.id)):null}}import{execFileSync as Nr}from"node:child_process";import{createRequire as bi}from"node:module";import{chmodSync as wi,existsSync as Je,lstatSync as xi,mkdirSync as Bn,symlinkSync as Si,unlinkSync as Ri,writeFileSync as Mr}from"node:fs";import{homedir as $i}from"node:os";import{dirname as ze,join as L,resolve as _r}from"node:path";import{fileURLToPath as vi}from"node:url";function Fr(){return L(process.env.CLAUDE_CONFIG_DIR||L($i(),".claude"),"settings.json")}function Ei(){let e=ze(vi(import.meta.url));for(let n=0;n<6;n++){let t=I(e,"package.json");if(t!==null)try{if(JSON.parse(t).name==="@jawache/flow")return e}catch{}let r=ze(e);if(r===e)break;e=r}return null}function Ci(e){try{return bi(L(e,"noop.js")).resolve("@jawache/flow"),!0}catch{return!1}}function Ir(e){try{let n=Nr("git",["-C",e,"config","--get","core.hooksPath"],{encoding:"utf8"}).trim();return n===""?null:n}catch{return null}}function Br(e,n){let t=I(e,n);if(t===null||t.trim()==="")return{value:null,unreadable:!1};try{return{value:JSON.parse(t),unreadable:!1}}catch{return{value:null,unreadable:!0}}}function Oi(e){let{session:n,agent:t,wearing:r}=Fn(e);return t===null?null:{id:n,agent:t,wearing:r}}function Ti(e,n){let t=Fr(),r=Br(e,t);return{root:e,empty:n,isGit:Je(L(e,".git")),hasConfig:Je(L(e,T)),gateText:I(e,Y),hasFlowDir:Je(L(e,J)),hooksPath:Ir(e),settingsPath:t,settings:r.value,settingsUnreadable:r.unreadable,resolves:Ci(e),packageRoot:Ei()}}function Pi(e,n){let t=[],r=(o,s)=>{try{s()}catch(i){t.push(`${o}: ${i.message}`)}};for(let o of[n.config,n.gate])o!==null&&r(`could not write ${o.path}`,()=>{let s=L(e,o.path);Bn(ze(s),{recursive:!0}),Mr(s,o.body),o.mode!==void 0&&wi(s,o.mode)});return n.flowDir&&r(`could not create ${J}/`,()=>{ae(e)}),n.link!==null&&r("could not link @jawache/flow",()=>{let o=L(e,"node_modules","@jawache");Bn(o,{recursive:!0});let s=L(o,"flow");xi(s,{throwIfNoEntry:!1})?.isSymbolicLink()&&!Je(s)&&Ri(s),Si(n.link,s,"dir")}),n.settings!==null&&r(`could not write ${n.settings.path}`,()=>{Bn(ze(n.settings?.path??""),{recursive:!0}),Mr(n.settings?.path??"",`${JSON.stringify(n.settings?.value,null,2)}
|
|
140
|
+
`)}),n.hooksPath&&r("could not set core.hooksPath",()=>Nr("git",["-C",e,"config","core.hooksPath",z],{stdio:"ignore"})),t}function Gr(e,n){let t=_r(e),r=kr(Ti(t,n.includes("--empty"))),o=Pi(t,r);return{stdout:`${br(r,o).join(`
|
|
141
|
+
`)}
|
|
142
|
+
`,stderr:"",exitCode:o.length===0?0:1}}async function Dr(e,n){let t=_r(e),r=L(t,T),o=await Ve(t);if(o.kind==="broken")return{stdout:"",stderr:`${o.message}
|
|
143
|
+
`,exitCode:2};let s=Fr(),i={root:t,version:me,off:we(t),configPath:r,hasConfig:o.kind==="loaded",load:o.kind==="loaded"?o.load:null,gateText:I(t,Y),hooksPath:Ir(t),settingsPath:s,settings:Br(t,s).value,session:Oi(t)},a=xr(i),d=Dt(a);return n.includes("--json")?{stdout:`${JSON.stringify(a,null,2)}
|
|
144
|
+
`,stderr:"",exitCode:d}:{stdout:`${Sr(a).join(`
|
|
145
|
+
`)}
|
|
146
|
+
`,stderr:"",exitCode:d}}var Dn=process.argv.slice(2);process.removeAllListeners("warning");process.on("warning",e=>{e.name!=="ExperimentalWarning"&&process.stderr.write(`${e.stack??e.message}
|
|
147
|
+
`)});Tr();async function Ln(e){let n=Gn(process.cwd(),e),t;try{t=await import(ji(n).href)}catch(o){throw new Error(Ue(e,o.message,process.versions.node))}let r=t.default;if(!r||typeof r!="object"||!Array.isArray(r.bindings))throw new Error(`${e} has no default export from \`defineConfig([\u2026])\` \u2014 that call IS the config, and its result is what flow reads.`);return r}function Ni(e,n){let t=n.filter(i=>!i.ok),r=e.filter(i=>i.spec.kind==="guardrail"&&!i.spec.disabled).length;for(let i of t)process.stdout.write(` \u2717 ${i.entry} \xB7 ${i.expect} case ${i.index+1}
|
|
148
|
+
${i.detail}
|
|
149
|
+
`);let o=e.filter(i=>i.spec.disabled).length,s=o?` \xB7 ${o} disabled, not run`:"";return t.length===0?(process.stdout.write(`flow test \u2014 ${n.length} cases over ${r} guardrails, all green${s}
|
|
150
|
+
`),0):(process.stdout.write(`flow test \u2014 ${t.length} of ${n.length} cases failed, over ${r} guardrails${s}
|
|
151
|
+
`),1)}async function _i(e){let n=await Ln(e),t=Zn(ue(n));return Ni(t,await it(t))}function Lr(e){e.stdout&&process.stdout.write(e.stdout),e.stderr&&process.stderr.write(e.stderr),process.exitCode=e.exitCode}function K(e,n){let t=e.indexOf(`--${n}`);if(t===-1)return null;let r=e[t+1];return r===void 0||r.startsWith("--")?"":r}var Fi=e=>{let n=[];for(let t=0;t<e.length;t++){let r=e[t];if(!r.startsWith("--")){n.push(r);continue}let o=e[t+1];o!==void 0&&!o.startsWith("--")&&(t+=1)}return n};async function Ii(e){let[n,t]=Fi(e);if(n===void 0)return process.stderr.write(`flow replay <recording.jsonl> [config] [--against <log.jsonl>]
|
|
152
|
+
`),2;let r=await Ln(t??T),o=ue(r),s=Nt(Gn(process.cwd(),n));if(s.steps.length===0)return process.stderr.write(`flow replay \u2014 ${n} holds no steps. Arm the recorder with \`touch .flow/record\`.
|
|
153
|
+
`),2;let i=await Et({load:o,recording:s,settings:r.settings}),a=(u,p)=>de(u[p],100),d=i.rows.filter(u=>u.kind==="guardrail"||u.kind==="breadcrumb");for(let u of d){let p=a(u,"subject");process.stdout.write(u.kind==="guardrail"?` \u2717 ${a(u,"id")} \xB7 ${a(u,"moment")}${p===""?"":` \xB7 ${p}`}
|
|
154
|
+
`:` \u{1F35E} ${a(u,"id")} \xB7 ${a(u,"moment")} \xB7 ${a(u,"cause")}
|
|
155
|
+
`)}process.stdout.write(`flow replay \u2014 ${s.steps.length} steps \xB7 ${d.filter(u=>u.kind==="guardrail").length} blocked \xB7 ${d.filter(u=>u.kind==="breadcrumb").length} shown, with no repo and no harness
|
|
156
|
+
`);for(let u of i.unanswered)process.stderr.write(` ! the recording never answered a ${u.kind} of \`${u.asked}\`
|
|
157
|
+
`);let l=K(e,"against");if(l===null)return i.unanswered.length>0?1:0;let c=Ct(i.rows,wn(Gn(process.cwd(),l)));for(let u of c)process.stderr.write(` ${u.side==="replay"?"+":"-"} [${u.at}] ${u.row}
|
|
158
|
+
`);return process.stdout.write(c.length===0?`flow replay \u2014 the replay and ${l} agree on every block and every injection. Diff empty.
|
|
159
|
+
`:`flow replay \u2014 ${c.length} difference(s) against ${l}.
|
|
160
|
+
`),c.length===0&&i.unanswered.length===0?0:1}async function Bi(e){let n=process.cwd(),t=Ai(Mi(n,T))?ue(await Ln(T)):{ok:!0,entries:[]},r=K(e,"limit"),o=K(e,"since"),s=jr(n,t,{session:K(e,"session"),branch:K(e,"branch"),limit:r===null||r===""?null:Number(r),all:K(e,"all")!==null,largest:K(e,"largest")!==null,since:o===null||o===""?null:Date.parse(o),mark:K(e,"mark")!==null});if(K(e,"json")!==null)return process.stdout.write(`${JSON.stringify(s,null,2)}
|
|
161
|
+
`),s.health.blocked===null?0:1;let i=ur(s);return process.stdout.write(`${i.join(`
|
|
162
|
+
`)}
|
|
163
|
+
`),s.health.blocked===null?0:1}var[U,...le]=Dn;if(Dn.includes("--version")||Dn.includes("-v"))process.stdout.write(`${me}
|
|
164
|
+
`);else if(U==="test")try{process.exitCode=await _i(le[0]??"flow.config.ts")}catch(e){process.stderr.write(`${e instanceof fe,e.message}
|
|
165
|
+
`),process.exitCode=2}else if(U==="hook")Lr(await Pr(le[0]??"",process.cwd()));else if(U==="commit")Lr(await Ar(le,process.cwd()));else if(U==="init"||U==="status"){let e=U==="init"?Gr(process.cwd(),le):await Dr(process.cwd(),le);e.stdout&&process.stdout.write(e.stdout),e.stderr&&process.stderr.write(e.stderr),process.exitCode=e.exitCode}else if(U==="replay"||U==="facts")try{process.exitCode=U==="replay"?await Ii(le):await Bi(le)}catch(e){process.stderr.write(`${e instanceof fe,e.message}
|
|
166
|
+
`),process.exitCode=2}else process.stderr.write([`flow ${me}`,""," flow init [--empty] scaffold flow.config.ts, arm the git gate, register the hooks"," flow status [--json] what is bound here, at which moments \u2014 and what is not wired yet"," flow test [config] run every bound entry's cases (default: flow.config.ts)",` flow hook <event> a harness hook, payload on stdin \u2014 ${vn.join(" \xB7 ")}`," flow commit <files\u2026> the git pre-commit gate, over the staged set"," flow replay <file> a recorded session, back through the engine \u2014 --against <log> diffs it"," flow facts what the record and the conversations say about this repo \u2014 --json for all of it",""].join(`
|
|
167
|
+
`)),process.exitCode=2;
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
function y(n){return n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function $(n){let e=[],t="",r=()=>{t!==""&&e.push({kind:"literal",text:t}),t=""};for(let o=0;o<n.length;o++){let s=n[o];if(s==="*")r(),n[o+1]==="*"?(e.push({kind:"globstar"}),o++,n[o+1]==="/"&&o++):e.push({kind:"star"});else if(s==="?")r(),e.push({kind:"single"});else if(s==="{"){let a=n.indexOf("}",o);if(a===-1){t+=s;continue}r(),e.push({kind:"options",options:n.slice(o+1,a).split(",")}),o=a}else t+=s??""}return r(),e}function v(n){switch(n.kind){case"literal":return y(n.text);case"star":return"[^/]*";case"globstar":return".*";case"single":return"[^/]";case"options":return`(${n.options.map(y).join("|")})`}}function Z(n){return new RegExp(`^${$(n).map(v).join("")}$`)}function X(n,e){return Z(e).test(n)}function g(n,e){return(e||[]).some(t=>X(n,t))}function ee(n){let e=n.includes("/")?n.slice(0,n.lastIndexOf("/")):".",t=n.slice(n.lastIndexOf("/")+1),r=t.lastIndexOf("."),o=r>0?t.slice(0,r):t,s=e==="."?o:`${e}/${o}`;return{dir:e,base:t,name:o,path:s}}function O(n,e){let t=ee(e);return n.replace(/\{(dir|base|name|path)\}/g,(r,o)=>t[o])}var T={write:["edit"],command:["edit","commit"],commit:["commit"],delete:["delete"],"turn-end":["turn"]},ne=Object.keys(T),te=["session","touch","turn-end"],re="write",oe="command",se="commit",ie="delete",ae="turn-end",de="session",le="touch";function ce(n){let e=[];for(let t of n)for(let r of T[t])e.includes(r)||e.push(r);return e}function ue(n){return ne.includes(n)}function fe(n){return te.includes(n)}var j=["write","delete","commit","touch"];function pe(n){return j.includes(n)}var G=Symbol.for("flow.category");function ge(n,e){return{[G]:!0,name:n,classify:e}}function h(n,e){if(!(typeof n!="object"||n===null))return n[e]}function D(n){return h(n,G)===!0}var N=Symbol.for("flow.settings");function f(n){return e=>{let t=n(e);return Object.defineProperty(t,N,{value:e,enumerable:!1,configurable:!0}),t}}function ye(n){return n===void 0?void 0:n[N]}function me(n){return n?(n.pass?.length??0)+(n.block?.length??0)>0:!1}var he=["at","for","on","ignore","check","message","disabled","test","description"],ke=["at","for","on","ignore","text","file","disabled","description"];function x(n,e){let t=r=>x({...n,...r},e);return{...e(n),at:(...r)=>t({at:r}),for:(...r)=>t({for:r}),on:(...r)=>t({on:r}),ignore:(...r)=>t({ignore:r}),check:r=>t({check:r}),message:r=>t({message:r}),text:r=>t({text:r}),file:r=>t({file:r}),disabled:r=>t({disabled:r===void 0?{}:{reason:r}}),test:r=>t({test:r}),description:r=>t({description:r})}}var L=n=>({spec:n});function be(){return x({kind:"guardrail"},L)}function xe(){return x({kind:"breadcrumb"},L)}function we(n){let e=h(h(n,"spec"),"kind");return e==="guardrail"||e==="breadcrumb"}var F=Symbol.for("flow.pack"),I=Symbol.for("flow.ref");function Se(n,e){let t=typeof e=="function"&&e.length>0;return W(n,[],{name:n,build:o=>typeof e=="function"?e(o):e,takesParams:t})}function W(n,e,t){let r={[I]:{pack:n,id:e.join(".")}};return t&&(r[F]=t),new Proxy(r,{get(o,s){return s in o?o[s]:typeof s=="string"?W(n,[...e,s]):void 0}})}function Re(n){let e=h(n,F);return Ce(e)?e:void 0}function Ce(n){return typeof n=="object"&&n!==null&&typeof n.name=="string"&&typeof n.build=="function"}function Ee(n){let e=h(n,I),t=h(e,"pack"),r=h(e,"id");return typeof t=="string"&&typeof r=="string"?{pack:t,id:r}:void 0}function $e(n,...e){return{kind:"pack",pack:Re(n),params:e[0],hasParams:e.length>0}}function ve(n){let e=Ee(n)??{pack:"",id:""};return x({},t=>({binding:{kind:"override",ref:e,spoken:t}}))}function Oe(n,e={}){return{bindings:n.map(t=>"binding"in t?t.binding:t),settings:e}}function B(n,e,t){let r={};for(let o of Object.keys(n))r[o]=t?.[o]??"pack";for(let o of Object.keys(e))r[o]="override";return{spec:{...n,...e},source:r}}var Me=["not-a-pack","missing-parameter","unknown-entry","unknown-key","unknown-moment","missing-mandatory","undeclared-category","no-cases","duplicate-id","dead-scope"];function w(n){return[`flow: the config will not load \u2014 ${n.length} refusal${n.length===1?"":"s"}.`,...n.map(e=>` ${e.code}: ${e.detail}`)].join(`
|
|
2
|
+
`)}function Ae(n){let e=[],t=[],r=new Map;for(let o of n.bindings){if(o.kind!=="pack")continue;if(!o.pack||o.pack.name===""){e.push({code:"not-a-pack",entry:"",detail:"A `pack(\u2026)` line was given a value that no `definePack` ever made. Import the pack's default export, not one of its entries."});continue}let{name:s,build:a,takesParams:i}=o.pack;if(i&&!o.hasParams){e.push({code:"missing-parameter",entry:s,detail:`The pack \`${s}\` declares parameters and was bound without any \u2014 write \`pack(${s}, { \u2026 })\`. Which fields it needs is stated by its factory's type.`});continue}for(let{key:d,spec:c}of K(a(o.params))){let l={id:`${s}.${d}`,pack:s,key:d,...B(c,{})};if(r.get(l.id)){e.push({code:"duplicate-id",entry:l.id,detail:`Two entries claim the id \`${l.id}\`. An id is its property name inside its pack, so this is one pack bound twice, or two packs sharing a name.`});continue}r.set(l.id,l),t.push(l)}}for(let o of n.bindings){if(o.kind!=="override")continue;let s=`${o.ref.pack}.${o.ref.id}`,a=r.get(s);if(!a){e.push({code:"unknown-entry",entry:s,detail:`\`override(${s})\` names an entry no bound pack has. Bind the pack it lives in, or correct the reference.`});continue}let i=B(a.spec,o.spoken,a.source);a.spec=i.spec,a.source=i.source}for(let o of t)e.push(...Pe(o));return e.length>0?{ok:!1,refusals:e}:{ok:!0,entries:t.map(o=>({id:o.id,pack:o.pack,key:o.key,spec:o.spec,source:o.source,categories:(o.spec.for??[]).map(s=>s.name),phases:o.spec.kind==="guardrail"?ce(o.spec.at??[]):[],settings:o.spec.kind==="guardrail"?ye(o.spec.check):void 0}))}}function K(n,e=[]){let t=[];for(let[r,o]of Object.entries(n))we(o)?t.push({key:[...e,r].join("."),spec:o.spec}):typeof o=="object"&&o!==null&&t.push(...K(o,[...e,r]));return t}function Pe(n){let{id:e,spec:t}=n,r=[],o=(u,Q)=>r.push({code:u,entry:e,detail:Q}),s=t.kind==="guardrail"?he:ke,a=Object.keys(t).filter(u=>u!=="kind"&&!s.includes(u));a.length>0&&o("unknown-key",`\`${e}\` carries ${a.map(u=>`\`${u}\``).join(", ")}, which the ${t.kind} grammar does not have. It is closed: ${s.join(" \xB7 ")}.`);let i=t.at??[],d=t.kind==="guardrail"?ue:fe,c=i.filter(u=>!d(u));if(c.length>0&&o("unknown-moment",`\`${e}\` fires at ${c.map(u=>`\`${u}\``).join(", ")}, which is not a moment a ${t.kind} has a rail for.`),(t.for??[]).some(u=>!D(u))&&o("undeclared-category",`\`${e}\` is scoped with \`.for(\u2026)\` to something \`defineCategory\` never made. A category is a value with its recognizer aboard \u2014 import the one you mean.`),t.disabled)return r;let p=[];return i.length===0&&p.push(".at(\u2026)"),t.kind==="guardrail"?(t.check||p.push(".check(\u2026)"),t.message===void 0&&p.push(".message(\u2026)")):t.text===void 0&&t.file===void 0&&p.push(".text(\u2026) or .file(\u2026)"),p.length>0&&o("missing-mandatory",`\`${e}\` never said ${p.join(" and ")}. Nothing in this grammar defaults \u2014 an absent key is absent, not a fallback.`),((t.on?.length??0)>0||(t.ignore?.length??0)>0)&&i.length>0&&!i.some(u=>pe(u))&&o("dead-scope",`\`${e}\` narrows by path but fires only at ${i.map(u=>`\`${u}\``).join(", ")}, which name no path \u2014 its \`on\`/\`ignore\` would be read by nothing. Drop the scope, or add a moment that carries a file (${j.join(" \xB7 ")}).`),t.kind==="guardrail"&&!me(t.test)&&o("no-cases",`\`${e}\` carries no cases. A rule declares what must pass it and what it must block (\`.test({ pass, block })\`) \u2014 a fence whose block-case was never written is a fence nothing proves is alive.`),r}function m(n,e,t=" \xB7 "){return e.length===0?n.ok():n.fail(e.join(t))}function k(n){return n.file??{path:"",content:""}}function Ge(n,e){let t=n.split(`
|
|
3
|
+
`),r=[];for(let o of e){let s=new RegExp(o);t.forEach((a,i)=>{s.test(a)&&r.push({pattern:o,line:i+1})})}return r}var De=f(n=>e=>{let t=Ge(k(e).content,n.ban);return m(e,t.map(r=>`line ${r.line}: matches /${r.pattern}/`))}),Ne=f(n=>e=>{let t=e.command??"",r=n.ban.filter(o=>new RegExp(o).test(t));return m(e,r.map(o=>`matches banned /${o}/`))});function Le(n){return`(?:${n.join("|")})[^"\`]*(?:"(?:(?!<<-?')[^"])*"[^"\`]*)*"(?:(?!<<-?')[^"])*\``}var Fe=["You are recording prose through a shell command, and a backtick inside a DOUBLE-quoted argument is not Markdown \u2014 the shell RUNS it and splices its output into your text before the command ever sees it.","On an append-only record there is no undo: no amend, no redact. And once the binary has argv the splice has already happened, so this is the only moment it can be caught.","Fix: SINGLE-quote the argument (backticks are literal there), or feed the prose on stdin with a QUOTED heredoc \u2014 `git commit -F -` plus `<<'EOF'`, which this rule treats as safe.","ALREADY single-quoted and still blocked? Then your PROSE carries an unbalanced double quote before the backtick, and the matcher cannot tell that quote from a real open argument. Nothing would splice \u2014 this one is the rule's cost, not your mistake. Balance the quote, drop it, or use the heredoc; do not go back to double quotes to make it pass."].join(`
|
|
4
|
+
`),Ie=f(n=>async e=>{let{path:t}=k(e);return n.existingOnly===!0?e.moment!=="delete"&&!await e.fs.exists(t)?e.ok():e.fail(`'${t}' is append-only \u2014 an existing file here is history and may not be edited or deleted (adding a new one is fine)`):e.fail(`'${t}' is a protected path (do not edit directly)`)}),We=f(n=>async e=>{let t=O(n.sibling,k(e).path);return await e.fs.exists(t)?e.ok():e.fail(`required sibling missing: ${t}`)}),Ke=/\.(ts|tsx|mts|cts|js|mjs|cjs|jsx)$/;function _e(n,e){let t=n.replace(/\\/g,"/"),r=e.root.replace(/^\/+|\/+$/g,"")+"/",o=t.indexOf(r);if(o===-1)return{ok:!0};let s=t.slice(o+r.length);if(e.folders?.length){let l=s.split("/")[0]??"",p=l.replace(/\.[^.]+$/,"");if(l!==""&&!e.folders.includes(l)&&!e.folders.includes(p))return{ok:!1,tier:"folder",found:l}}if(!e.allow.length)return{ok:!0};let a=Ke.exec(t);if(!a)return{ok:!0};if(t.endsWith(".d.ts"))return{ok:!0};if(!s.includes("/"))return{ok:!0};let d=s.slice(s.lastIndexOf("/")+1).slice(0,-a[0].length);return(d.endsWith(".test")?d.slice(0,-5)!==(e.thinking??""):!e.allow.includes(d))?{ok:!1,tier:"filename"}:{ok:!0}}function qe(n,e,t){return n.ok?"":n.tier==="folder"?`'${n.found}' is not one of the names ${t.root}/ may hold: ${t.folders?.join(" \xB7 ")??""} (found ${e})`:`non-canonical file in a ${t.root} feature folder: ${e} (allowed: ${t.allow.join(" \xB7 ")}${t.thinking===void 0?"":` \u2014 only ${t.thinking} may carry a .test sibling`})`}var Ue=f(n=>e=>{let{path:t}=k(e),r=_e(t,n);return r.ok?e.ok():e.fail(qe(r,t,n))});function Ve(n,e){let t=n;for(let r of(e??"").split(".").filter(Boolean)){if(t==null||typeof t!="object"||!(r in t))return{found:!1};t=t[r]}return{found:!0,value:t}}function He(n,e){let t=[],{path:r}=e,o=Ve(n,r);if(!o.found)return e.required===!0&&t.push(`"${r}" is missing (required)`),t;let s=o.value;if(e.keysPrefixedWith!==void 0){if(!s||typeof s!="object")return t;for(let a of Object.keys(s))a.startsWith(e.keysPrefixedWith)||t.push(`"${r}.${a}" must be prefixed "${e.keysPrefixedWith}"`)}return"equals"in e&&JSON.stringify(s)!==JSON.stringify(e.equals)&&t.push(`"${r}" must equal ${JSON.stringify(e.equals)} (is ${JSON.stringify(s)})`),e.matches!==void 0&&!new RegExp(e.matches).test(String(s))&&t.push(`"${r}" (${JSON.stringify(s)}) must match /${e.matches}/`),t}function Je(n,e){let t;try{t=JSON.parse(n)}catch{return[]}return e.flatMap(r=>He(t,r))}var Ye=f(n=>e=>m(e,Je(k(e).content,n.assert)));function ze(n,e){return n.filter(t=>!new RegExp(`\\b${y(t)}\\b`).test(e))}async function Qe(n){let{parse:e,Lang:t}=await import("@ast-grep/napi"),r=e(t.Tsx,n).root(),o=new Set;for(let s of["export function $N($$$) { $$$ }","export class $N { $$$ }","export const $N = $V"])for(let a of r.findAll(s)){let i=a.getMatch("N");i&&o.add(i.text())}return[...o]}var Ze=f(n=>async e=>{let{path:t,content:r}=k(e),o=O(n.sibling,t);if(!await e.fs.exists(o))return e.fail(`sibling test not found: ${o}`);let s=await e.fs.read(o),a=ze(await Qe(r),s);return m(e,a.map(i=>`exported '${i}' is never referenced in ${o}`))});async function U(n){return n.staged?n.staged:n.git.stagedFiles()}function Xe(n,e){let t=[];for(let r of e)n.some(o=>g(o,r.if))&&(n.some(o=>g(o,r.thenAny))||t.push(`${r.if.join(", ")} changed but none of [${r.thenAny.join(", ")}] did in the same commit`));return t}var en=f(n=>async e=>m(e,Xe(await U(e),n.groups))),C=new Set([";","&&","||","|","&","|&",`
|
|
5
|
+
`]),nn=new Set(["-C","-c","--git-dir","--work-tree","--namespace","--exec-path","--config-env"]);function tn(n,e){for(let t=e;t<n.length;t++){if(n[t]==="\\"){t++;continue}if(n[t]==='"')return t}return-1}function A(n){let e=n,t=[],r="",o=!1,s=0,a=()=>{o&&t.push(r),r="",o=!1};for(;s<e.length;){let i=e[s];if(i==="'"||i==='"'){let d=i==="'"?e.indexOf("'",s+1):tn(e,s+1);if(d===-1)return null;r+=i==='"'?e.slice(s+1,d).replace(/\\(["\\$`])/g,"$1"):e.slice(s+1,d),o=!0,s=d+1}else if(i==="\\"&&s+1<e.length)r+=e[s+1],o=!0,s+=2;else if(i===`
|
|
6
|
+
`)a(),t.push(`
|
|
7
|
+
`),s++;else if(/\s/.test(i))a(),s++;else if(i===";"||i==="|"||i==="&"){a();let d=e.slice(s,s+2);d==="&&"||d==="||"||d==="|&"?(t.push(d),s+=2):(t.push(i),s++)}else r+=i,o=!0,s++}return a(),t}function P(n){let e=A(n);if(!e)return null;let t=[],r=0,o=!0;for(;r<e.length;){let s=e[r];if(C.has(s)){o=!0,r++;continue}if(o&&s==="git"){let a=[],i=r+1;for(;i<e.length&&!C.has(e[i]);)a.push(e[i++]);let d=0;for(;d<a.length;){let c=a[d];if(nn.has(c))d+=2;else if(c.startsWith("-"))d+=1;else break}t.push({globals:a.slice(0,d),subcommand:a[d]??null,args:a.slice(d+1)}),r=i,o=!1;continue}o=!1,r++}return t}function rn(n){let e=/<<-?\s*(['"]?)([A-Za-z_][A-Za-z0-9_]*)\1/.exec(n);if(!e)return null;let t=e[2],o=n.slice(e.index+e[0].length).split(`
|
|
8
|
+
`).slice(1),s=o.findIndex(i=>i.trim()===t);if(s===-1)return null;let a=o.slice(0,s).join(`
|
|
9
|
+
`).trim();return a===""?null:a}function V(n){let e=P(n);if(!e)return null;let t=e.find(o=>o.subcommand==="commit");if(!t)return null;let r=[];for(let o=0;o<t.args.length;o++){let s=t.args[o];if(s==="-m"||s==="--message"){let a=t.args[o+1];if(a===void 0)break;r.push(a),o++;continue}s.startsWith("-m")&&s!=="-m"?r.push(s.slice(2)):s.startsWith("--message=")&&r.push(s.slice(10))}return r.length?r.join(`
|
|
10
|
+
|
|
11
|
+
`):rn(n)}function b(n){return`'${n.replace(/'/g,"'\\''")}'`}function H(n){let e=[],t=A(n),r=t?.[1];t?.[0]==="cd"&&r!==void 0&&!r.startsWith("-")&&!C.has(r)&&C.has(t[2]??"")&&e.push("-C",b(r));let o=P(n)?.[0];if(!o)return e.join(" ");for(let s=0;s<o.globals.length-1;s++)o.globals[s]==="-C"&&e.push("-C",b(o.globals[s+1]));return e.join(" ")}function J(n,e){return new RegExp(`^[ \\t]*${y(e)}[ \\t]*:`,"im").test(n)}function on(n,e,t=[]){let r=new Set(t),o=new Set;for(let s of n.split(`
|
|
12
|
+
`)){if(!s.startsWith("+")||s.startsWith("+++"))continue;let a=s.slice(1);for(let i of e){let d=new RegExp(i,"g"),c;for(;(c=d.exec(a))!==null;)c[1]!==void 0&&!r.has(c[1])&&o.add(c[1]),c[0]===""&&d.lastIndex++}}return[...o]}function sn(n){let{message:e,state:t,whenChanged:r,whenAdded:o,except:s,namesAdded:a,token:i}=n,d=new Set(t.added),c=[];for(let l of t.changed)s&&g(l,s)||(d.has(l)&&o&&g(l,o)?c.push(`\`${l}\` (new)`):r&&g(l,r)&&c.push(`\`${l}\` (changed)`));for(let{name:l,file:p}of a??[])c.push(`\`${l}\` (added in ${p})`);return c.length?J(e,i)?[]:[`${c.join(" \xB7 ")} \u2014 and this commit's message records no reason. Put \`${i}: <why>\` on a line of its own in the message; the act is fine, doing it silently is not.`]:[]}function M(n){return n.split(`
|
|
13
|
+
`).map(e=>e.trim()).filter(Boolean)}var an=f(n=>async e=>{let t=V(e.command??"");if(t==null)return e.ok();let r=`git ${H(e.command??"")}`.trimEnd(),o={changed:[],added:[]};if(n.whenChanged||n.whenAdded){let[i,d,c]=await Promise.all([e.exec(`${r} diff HEAD --name-only`),e.exec(`${r} diff HEAD --name-only --diff-filter=A`),e.exec(`${r} ls-files --others --exclude-standard`)]);if(i.code!==0||d.code!==0||c.code!==0)return e.ok();let l=M(c.stdout);o={changed:[...new Set([...M(i.stdout),...l])],added:[...M(d.stdout),...l]}}let s=[];for(let i of n.diffAdds??[]){let d=await e.exec(`${r} diff HEAD -- ${b(i.file)}`);if(d.code!==0)return e.ok();for(let c of on(d.stdout,i.patterns,i.known??[]))s.push({name:c,file:i.file})}let a=sn({message:t,state:o,whenChanged:n.whenChanged,whenAdded:n.whenAdded,except:n.except,namesAdded:s,token:n.token});return m(e,a)});function dn(n,e,t){if(t===""||e.length===0)return!1;let r=-1,o=-1;return n.forEach((s,a)=>{s.did==="edit"&&g(s.path,e)&&(r=a),s.did==="run"&&s.command.includes(t)&&(o=a)}),r>=0&&r>o}var ln=f(n=>e=>e.turn&&dn(e.turn,n.edited,n.mustRun)?e.fail(`edited ${n.edited.join(", ")} this turn but never ran \`${n.mustRun}\` afterwards`):e.ok());function cn(n,e){let t=e??[];return!t.length||!n.length?!1:!n.some(r=>g(r,t))}function Y(n,e=8){let t=n.trim().split(`
|
|
14
|
+
`).filter(Boolean),r=t.filter(o=>/\b(fail|failed|error|not ok|assert|expected|cannot|undefined)\b/i.test(o)||/[✖✗✘]/.test(o));return(r.length?r:t.slice(-6)).slice(0,e).join(`
|
|
15
|
+
`)}var un=f(n=>async e=>{let t=await U(e);if(cn(t,n.changed))return e.ok();let r=n.run.includes("{files}");if(r&&t.length===0)return e.ok();let o=r?n.run.replace("{files}",t.map(b).join(" ")):n.run,s=await e.exec(o);return s.code===0?e.ok():e.fail(`\`${o}\` failed (exit ${s.code}):
|
|
16
|
+
${Y(s.stderr||s.stdout)}`)});function R(n){let e=$(n),t=e[e.length-1]?.kind==="globstar";return`^${(t?e.slice(0,-1):e).map(v).join("")}${t?"":"$"}`}function fn(n){return n==="node:*"?{core:!0}:n.startsWith("node:")?{path:`^${y(n)}$`}:n.includes("/")||n.includes("*")||n.includes(".")?{path:R(n)}:{path:`node_modules/${y(n)}(/|$)`}}function S(n,e){let t=[],r=!1;for(let s of n)for(let a of e[s]??[]){let i=fn(a);i.core?r=!0:i.path&&t.push(i.path)}let o={};return t.length&&(o.path=t),r&&(o.dependencyTypes=["core"]),o}function pn(n){let e=n.layers,t=[],r=[],o=(n.except??[]).includes("type-only"),s=i=>o?{...i,dependencyTypesNot:["type-only"]}:i;for(let[i,d]of Object.entries(n.only??{})){let l={pathNot:S(d,e).path??[]};t.push({name:`${i}-only`,severity:"error",comment:`${i} may import only: ${d.join(", ")} \u2014 an unlisted import blocks here (add it to a layer, or keep it out).`,from:S([i],e),to:s(l)})}for(let i of n.forbid??[])if(i==="circular")t.push({name:"no-circular",severity:"error",comment:"no dependency cycles",from:{pathNot:"node_modules"},to:{circular:!0}});else if(i==="orphans")t.push({name:"no-orphans",severity:"warn",comment:"unreferenced module",from:{orphan:!0,pathNot:"node_modules|\\.d\\.ts$"},to:{}});else if(i.cyclesBetween!==void 0){let d=i.cyclesBetween,c=(e[d]??[])[0]??"",l=R(c).replace(/\$$/,"").replace(/\[\^\/\]\*$/,"([^/]+)");t.push({name:`no-cycles-between-${d}`,severity:"error",comment:i.why??`${d} must not import sideways`,from:{path:`${l}/`},to:s({path:`${l.replace(/\(\[\^\/\]\+\)/,"[^/]+")}/`,pathNot:"$1/"})})}else if(i.from!==void 0&&i.to!==void 0){let d={...S([i.to],e)};i.transitive===!0&&(d.reachable=!0),t.push({name:`no-${i.from}-to-${i.to}${i.transitive===!0?"-transitive":""}`,severity:"error",comment:i.why??`${i.from} must not import ${i.to}`,from:S([i.from],e),to:s(d)})}for(let i of n.require??[])r.push({name:`must-import-${i.import}`,severity:"error",comment:i.why??`modules in ${i.in} must import ${i.import}`,module:{path:R(i.in)},to:{path:R(i.import)}});for(let i of n.warn??[])i==="sdp"&&t.push({name:"sdp",severity:"warn",comment:"depends on a more-unstable module (Stable Dependencies Principle)",from:{},to:{moreUnstable:!0}});let a={enhancedResolveOptions:{extensions:[".js",".jsx",".ts",".tsx",".mjs",".cjs",".json"]},doNotFollow:{path:"node_modules"},tsPreCompilationDeps:!1,...n.options??{}};return{forbidden:t,required:r,options:a}}var _="FLOW_DEPCRUISE_CONFIG";function gn(n){let e=JSON.stringify(pn(n));return["d=$(mktemp -d)",`cat > "$d/dc.json" <<'${_}'`,e,_,`npx --no-install depcruise --config "$d/dc.json" --output-type json ${b(n.scan)}`,"rc=$?",'rm -rf "$d"',"exit $rc"].join(`
|
|
17
|
+
`)}function yn(n){let e;try{e=JSON.parse(n.stdout||"")}catch{return n.code===0?[]:[`depcruise failed (exit ${n.code}): ${Y(n.stderr||n.stdout,3)}`]}return(e.summary?.violations??[]).map(t=>{let r=t.rule?.name??"fence",o=t.comment??t.rule?.comment??"";return`${r}: ${t.from} \u2192 ${t.to}${o?` \u2014 ${o}`:""}`})}var mn=f(n=>async e=>m(e,yn(await e.exec(gn(n))),`
|
|
18
|
+
`)),z={html:"Html",css:"Css",javascript:"JavaScript",js:"JavaScript",typescript:"TypeScript",ts:"TypeScript",tsx:"Tsx",jsx:"Tsx"},hn=Object.keys(z),q=new Set;async function kn(n){let{Lang:e,registerDynamicLanguage:t}=await import("@ast-grep/napi"),r=z[n];if(r!==void 0)return{ok:!0,lang:e[r]};if(q.has(n))return{ok:!0,lang:n};try{let o=await import(`@ast-grep/lang-${n}`);return t({[n]:o.default??o}),q.add(n),{ok:!0,lang:n}}catch{return{ok:!1,detail:`unknown ast-grep grammar: '${n}'. Built in: ${hn.join(", ")}. Anything else installs as a package \u2014 \`npm i -D @ast-grep/lang-${n}\` \u2014 and is picked up here on the next run.`}}}async function bn(n,e,t){let r=await kn(t);if(!r.ok)return r;let{parse:o}=await import("@ast-grep/napi");return{ok:!0,hits:o(r.lang,n).root().findAll({rule:e}).map(a=>`${a.range().start.line+1}: ${a.text()}`)}}var xn=f(n=>async e=>{let t=await bn(k(e).content,n.rule,n.language);return t.ok?m(e,t.hits):e.fail(t.detail)});function wn(n){return e=>{let t=e.agentType;return n.parent===!0?!e.subagent&&t===void 0:t===void 0?!1:(n.types??[]).includes(t)?!0:(n.generic??[]).includes(t)?(n.brief??[]).some(r=>e.head.includes(r)):!1}}var Bn=14400*1e3;var E=class extends Error{refusals;constructor(e){super(w(e)),this.name="FlowConfigError",this.refusals=e}};function Sn(n){if(n.ok)return n.entries;throw new E(n.refusals)}export{E as FlowConfigError,Me as REFUSAL_CODES,Fe as SUBSTITUTION_MESSAGE,xn as astGrep,Ne as banCommands,xe as breadcrumb,Ue as canonicalFiles,en as changeTogether,oe as command,se as commit,V as commitMessage,an as commitReason,ge as defineCategory,f as defineCheck,Oe as defineConfig,Se as definePack,ie as deletion,mn as depcruise,Sn as entriesOrThrow,y as escapeRe,un as execPasses,H as gitDirPrefix,P as gitInvocations,J as givesReason,be as guardrail,Ye as jsonInvariant,Ae as loadConfig,ve as override,$e as pack,Ie as protectedPath,b as quoteArg,ln as ranSinceEdit,w as refusalText,de as session,We as siblingExists,wn as spawnedAs,Le as substitutionInProse,Ze as symbolsInSibling,De as textBan,A as tokenizeCommand,le as touch,ae as turnEnd,re as write};
|