infernoflow 0.10.16 → 0.10.18

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.
@@ -1,13 +1,13 @@
1
- import*as C from"node:fs";import*as b from"node:path";import*as D from"node:readline";function M(t){return t.replace(/[^a-zA-Z0-9]+/g," ").trim().split(/\s+/).filter(Boolean).map(l=>l[0].toUpperCase()+l.slice(1)).join("")}function z(t){return t.replace(/([A-Z])/g," $1").trim()}function S(t){try{return C.readFileSync(t,"utf8")}catch{return""}}const R=[{id:"CreateItem",title:"Create Item",regex:/\b(post|create|add)\b/i},{id:"ReadItems",title:"Read Items",regex:/\b(get|read|list|fetch)\b/i},{id:"UpdateItem",title:"Update Item",regex:/\b(put|patch|update|edit)\b/i},{id:"DeleteItem",title:"Delete Item",regex:/\b(delete|remove)\b/i},{id:"SearchItems",title:"Search Items",regex:/\bsearch\b/i},{id:"FilterItems",title:"Filter Items",regex:/\bfilter\b/i},{id:"SetDueDate",title:"Set Due Date",regex:/\bdueDate|deadline|due\b/i},{id:"SetPriority",title:"Set Priority",regex:/\bpriority\b/i},{id:"ToggleComplete",title:"Toggle Complete",regex:/\bcomplete|completed|toggle\b/i},{id:"ClearCompleted",title:"Clear Completed",regex:/\bclearCompleted|clear completed\b/i}];function K(t){return J(t).capabilities}function U(t){const l=[],r=["src","server","app","backend","frontend","api","Controllers"];for(const i of r){const n=b.join(t,i);if(!C.existsSync(n))continue;const e=[n];for(;e.length;){const c=e.pop();for(const s of C.readdirSync(c,{withFileTypes:!0})){const o=b.join(c,s.name);if(s.isDirectory()){if(["node_modules",".git","dist","build"].includes(s.name))continue;e.push(o)}else/\.(js|jsx|ts|tsx|mjs|cjs|json|md|html|htm|cs|csproj)$/.test(s.name)&&l.push(o)}}}for(const i of C.readdirSync(t,{withFileTypes:!0}))i.isFile()&&/^(Program\.cs|.+\.csproj)$/i.test(i.name)&&l.push(b.join(t,i.name));return l}function O(t,l){const r=new Set;for(const i of t){const n=b.relative(l,i),e=S(i),c=e.matchAll(/\bclass\s+([A-Z][A-Za-z0-9_]*?(?:Component|Page|View|Widget|Card))\b/g);for(const A of c)r.add(A[1]);const s=e.matchAll(/\bselector\s*:\s*["']([^"']+)["']/g);for(const A of s)r.add(A[1]);const o=e.matchAll(/\bfunction\s+([A-Z][A-Za-z0-9_]*)\s*\(/g);for(const A of o)/component|page|view|card|chart|dashboard/i.test(A[1])&&r.add(A[1]);const p=n.match(/([^/\\]+)\.(component|page|view|widget|card)\.(ts|tsx|js|jsx)$/i);p&&r.add(p[1])}return Array.from(r).sort()}function H(t){const l=new Set,r=new Set,i=new Set(["if","for","while","const","let","var","return","function","class","import","export","null","undefined","true","false","string","number","boolean","any","unknown","never","selector","templateUrl","styleUrl","standalone","imports","providers","providedIn","options","scales","responsive","display","title","type","label","component","service","routes","appConfig","ApplicationConfig"]),n=e=>{e&&/^[A-Za-z_][A-Za-z0-9_]*$/.test(e)&&(e.length<=1||i.has(e)||/^[A-Z0-9_]+$/.test(e)||l.add(e))};for(const e of t){const c=S(e);if(/\.(html|htm)$/i.test(e)){const s=c.matchAll(/\{\{\s*(?:this\.)?([a-zA-Z_][a-zA-Z0-9_]*)/g);for(const f of s)n(f[1]);const o=c.matchAll(/\[\(ngModel\)\]\s*=\s*["']([a-zA-Z_][a-zA-Z0-9_]*)["']/g);for(const f of o)n(f[1]);const p=c.matchAll(/\[[a-zA-Z0-9_-]+\]\s*=\s*["'](?:this\.)?([a-zA-Z_][a-zA-Z0-9_]*)["']/g);for(const f of p)n(f[1]);const A=c.matchAll(/\*ngIf\s*=\s*["'](?:this\.)?([a-zA-Z_][a-zA-Z0-9_]*)/g);for(const f of A)n(f[1])}if(/\.(ts|tsx|js|jsx|mjs|cjs)$/i.test(e)){const s=c.matchAll(/(?:^|\n)\s*(?:public|private|protected)?\s*(?:async\s+)?([a-zA-Z_][a-zA-Z0-9_]*)\s*\([^)]*\)\s*\{/g);for(const d of s)r.add(d[1]);const o=c.matchAll(/\bthis\.([a-zA-Z_][a-zA-Z0-9_]*)\b/g);for(const d of o)n(d[1]);const p=c.matchAll(/(?:^|\n)\s*(?:public|private|protected)?\s*(?:readonly\s+)?([a-zA-Z_][a-zA-Z0-9_]*)\s*(?::|=)/g);for(const d of p)n(d[1]);const A=c.matchAll(/@Input\([^)]*\)\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*[:=]/g);for(const d of A)n(d[1]);const f=c.matchAll(/forEach\(\((\w+)\)\s*=>/g);for(const d of f){const w=d[1],j=new RegExp(`\\b${w}\\.([a-zA-Z_][a-zA-Z0-9_]*)\\b`,"g");for(const g of c.matchAll(j))n(g[1])}}}return Array.from(l).filter(e=>!r.has(e)).sort().slice(0,80)}function W(t){const l=new Set,r=b.join(t,"package.json");if(!C.existsSync(r))return[];try{const i=JSON.parse(S(r)||"{}"),n={...i.dependencies||{},...i.devDependencies||{}};for(const e of Object.keys(n))l.add(e)}catch{}return Array.from(l).sort()}function G(t,l,r){const i=l.filter(s=>/\.(css|scss|sass|less|styl)$/i.test(s)).map(s=>b.relative(t,s)).sort(),n=[],e=s=>r.includes(s);e("tailwindcss")&&n.push("Tailwind CSS"),e("bootstrap")&&n.push("Bootstrap"),r.some(s=>s.startsWith("@angular/material"))&&n.push("Angular Material"),e("antd")&&n.push("Ant Design"),e("styled-components")&&n.push("styled-components"),(e("emotion")||e("@emotion/react"))&&n.push("Emotion");const c=new Set;for(const s of l){if(!/\.(css|scss|sass|less|styl|html|htm|ts|tsx|js|jsx|mjs|cjs)$/i.test(s))continue;const o=S(s);for(const p of o.matchAll(/--([a-zA-Z][a-zA-Z0-9_-]*)/g))c.add(`--${p[1]}`)}return{cssFrameworks:n,styleFileCount:i.length,styleFilesSample:i.slice(0,12),designTokens:Array.from(c).sort().slice(0,24)}}function N(t){let l=!1,r=!1;const i=new Set;for(const e of t){if(!/\.(html|htm|tsx|jsx|ts|js|mjs|cjs)$/i.test(e))continue;const c=S(e);/\bgrid\b|grid-template|grid-cols-|display:\s*grid/i.test(c)&&(l=!0),/\bflex\b|display:\s*flex|flex-row|flex-col|justify-|items-/i.test(c)&&(r=!0);for(const s of c.matchAll(/<(main|header|footer|section|aside|nav)\b/gi))i.add(s[1].toLowerCase());for(const s of c.matchAll(/class(?:Name)?\s*=\s*["'`][^"'`]*(dashboard|chart|card|sidebar|content|toolbar|filter|panel|table)[^"'`]*["'`]/gi)){const o=s[1].toLowerCase();i.add(o==="filter"?"filters":o)}}return{layoutType:l&&r?"mixed":l?"grid":r?"flex":"unknown",usesGrid:l,usesFlex:r,sections:Array.from(i).sort()}}function B(t,l,r,i={}){const n={ts:0,js:0,py:0,java:0,go:0,rb:0,rs:0,cs:0,php:0};for(const g of l){const y=b.extname(g).toLowerCase();(y===".ts"||y===".tsx")&&(n.ts+=1),(y===".js"||y===".jsx"||y===".mjs"||y===".cjs")&&(n.js+=1),y===".py"&&(n.py+=1),y===".java"&&(n.java+=1),y===".go"&&(n.go+=1),y===".rb"&&(n.rb+=1),y===".rs"&&(n.rs+=1),y===".cs"&&(n.cs+=1),y===".php"&&(n.php+=1)}const e=Object.entries(n).sort((g,y)=>y[1]-g[1]),c=e[0]?.[1]>0?e[0][0]:"unknown";let s="unknown",o=!1,p=!1,A=!1;for(const g of l){const y=b.basename(g).toLowerCase();if(y.endsWith(".csproj")){const k=S(g);/Microsoft\.NET\.Sdk\.Web/i.test(k)&&(o=!0),(/Blazor/i.test(k)||/Microsoft\.AspNetCore\.Components/i.test(k))&&(A=!0)}if(y==="program.cs"){const k=S(g);/app\.Map(Get|Post|Put|Delete|Patch)\s*\(/i.test(k)&&(p=!0)}}const f=g=>r.includes(g);r.some(g=>g.startsWith("@angular/"))?s="angular":f("react")?s="react":f("vue")?s="vue":f("svelte")?s="svelte":f("next")?s="nextjs":f("nuxt")?s="nuxt":f("express")?s="express":f("@nestjs/core")?s="nestjs":f("fastify")?s="fastify":f("flask")?s="flask":f("django")?s="django":f("spring-boot")?s="spring":A?s="blazor":p?s="minimalapi":(o||n.cs>0)&&(s="aspnet");let d="fullstack";const w=["src","frontend","app"].some(g=>C.existsSync(b.join(t,g))),j=["server","backend","api"].some(g=>C.existsSync(b.join(t,g)));return["react","angular","vue","svelte","nextjs","nuxt"].includes(s)&&(d="frontend"),["express","nestjs","fastify","flask","django","spring","aspnet","minimalapi"].includes(s)&&(d="backend"),w&&j&&(d="fullstack"),!w&&!j&&(d="library"),s==="blazor"&&(d="frontend"),{language:i.language||c,framework:i.framework||s,projectType:i.projectType||d,detected:{language:c,framework:s,projectType:d}}}function q(t,l){const r=[],i=new Set,n=s=>{let o=String(s||"").trim();return o?(o=o.replace(/https?:\/\/[^/]+/gi,""),o=o.replace(/\$\{[^}]+\}/g,"{var}"),o=o.replace(/\{[A-Za-z_][A-Za-z0-9_]*\}/g,"{var}"),o=o.replace(/:[A-Za-z_][A-Za-z0-9_]*/g,"{var}"),o=o.replace(/\/\d+(?=\/|$)/g,"/{id}"),o=o.replace(/=[^&\s]+/g,"={value}"),o=o.replace(/\/+/g,"/"),o):""},e=s=>{const o=n(s.endpointPattern);if(!o)return;const p=`${s.method}|${o}|${s.sourceFile}|${s.style}`;i.has(p)||(i.add(p),r.push({...s,endpointPattern:o}))};for(const s of l){if(!/\.(ts|tsx|js|jsx|mjs|cjs|cs)$/i.test(s))continue;const o=b.relative(t,s),p=S(s);if(!(/service|api|client|controller|program\.cs/i.test(o)||/HttpClient|fetch\(|app\.Map(Get|Post|Put|Delete|Patch)\(/i.test(p)))continue;const f=p.replace(/\r\n/g,`
2
- `),d={},w=u=>{let a=String(u||"").trim();if(!a)return"";for(a=a.replace(/;+$/,"").trim(),a=a.replace(/\(\s*$/,"");a.startsWith("'")&&a.endsWith("'")||a.startsWith('"')&&a.endsWith('"')||a.startsWith("`")&&a.endsWith("`");)a=a.slice(1,-1).trim();return a},j=u=>{const a=String(u||"").trim();return a?!!(/^https?:\/\//i.test(a)||a.startsWith("/")||/\bapi\b/i.test(a)||/\$\{[^}]+\}/.test(a)||/\?[^=\s]+=?/.test(a)||/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_./${}-]+$/.test(a)):!1},g=(u,a)=>{!u||!a||(d[u]=w(a))},y=/(?:const|let|var)\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([\s\S]*?);/g;for(const u of f.matchAll(y))g(u[1],u[2]);const k=/(?:public|private|protected)?\s*(?:readonly\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([\s\S]*?);/g;for(const u of f.matchAll(k))g(u[1],u[2]);const F=u=>{const a=w(u);if(!a)return"";if(/^['"`][\s\S]*['"`]$/.test(a))return a.replace(/^['"`]|['"`]$/g,"");if(d[a]&&j(d[a]))return d[a];const m=a.match(/^this\.([A-Za-z_][A-Za-z0-9_]*)$/);if(m&&d[m[1]]&&j(d[m[1]]))return d[m[1]];const $=a.split("+").map(h=>h.trim()).filter(Boolean);if($.length>1){const h=$.map(x=>{if(/^['"`][\s\S]*['"`]$/.test(x))return x.replace(/^['"`]|['"`]$/g,"");if(d[x]&&j(d[x]))return d[x];const v=x.match(/^this\.([A-Za-z_][A-Za-z0-9_]*)$/);return v&&d[v[1]]&&j(d[v[1]])?d[v[1]]:`{${x}}`}).join("");if(h)return h}const P=a.match(/^(.+?)\?(.+?):(.+)$/s);if(P){const h=F(P[2]),x=F(P[3]);if(h||x)return`${h||"{optionA}"} | ${x||"{optionB}"}`}return a},Z=/\.\s*(get|post|put|patch|delete)\s*(?:<[\s\S]*?>)?\s*\(\s*([\s\S]*?)(?:,|\))/gi;for(const u of f.matchAll(Z)){const a=u[1].toUpperCase(),m=F(u[2]);!m||!j(m)||e({method:a,endpointPattern:m,style:"httpClient",sourceFile:o})}const _=/\bfetch\s*\(\s*([\s\S]*?)(?:,|\))/gi;for(const u of f.matchAll(_)){const a=F(u[1]);if(!a||!j(a))continue;const m=u.index||0,$=f.slice(m,m+260),h=(/method\s*:\s*["'](GET|POST|PUT|PATCH|DELETE)["']/i.exec($)?.[1]||"GET").toUpperCase();e({method:h,endpointPattern:a,style:"fetch",sourceFile:o})}const I=/\baxios\.(get|post|put|patch|delete)\s*\(\s*([\s\S]*?)(?:,|\))/gi;for(const u of f.matchAll(I)){const a=u[1].toUpperCase(),m=F(u[2]);!m||!j(m)||e({method:a,endpointPattern:m,style:"axios",sourceFile:o})}const T=/\baxios\s*\(\s*\{([\s\S]*?)\}\s*\)/gi;for(const u of f.matchAll(T)){const a=u[1],m=/\bmethod\s*:\s*["']?(get|post|put|patch|delete)["']?/i.exec(a),$=/\burl\s*:\s*([^,\n]+)/i.exec(a),P=(m?.[1]||"get").toUpperCase(),h=F($?.[1]||"");!h||!j(h)||e({method:P,endpointPattern:h,style:"axios-config",sourceFile:o})}const E=/\.\s*request\s*\(\s*["'](GET|POST|PUT|PATCH|DELETE)["']\s*,\s*([\s\S]*?)(?:,|\))/gi;for(const u of f.matchAll(E)){const a=u[1].toUpperCase(),m=F(u[2]);!m||!j(m)||e({method:a,endpointPattern:m,style:"request",sourceFile:o})}if(/\.cs$/i.test(s)){const u=/\bapp\.Map(Get|Post|Put|Delete|Patch)\s*\(\s*"([^"]+)"/gi;for(const h of f.matchAll(u))e({method:h[1].toUpperCase(),endpointPattern:h[2],style:"csharp-map",sourceFile:o});const a=/\[Route\("([^"]+)"\)\][\s\S]*?class\s+\w+/i.exec(f),m=a?a[1]:"",$=/\[(HttpGet|HttpPost|HttpPut|HttpDelete|HttpPatch)(?:\("([^"]*)"\))?\]/gi;for(const h of f.matchAll($)){const x=h[1].replace("Http","").toUpperCase(),v=h[2]||"",L=[m,v].filter(Boolean).join("/").replace(/\/+/g,"/").replace(/\[controller\]/gi,"{controller}");e({method:x,endpointPattern:L||m||"{controller-route}",style:"csharp-controller",sourceFile:o})}const P=/\b(GetAsync|PostAsync|PutAsync|DeleteAsync|SendAsync)\s*\(\s*"([^"]+)"/gi;for(const h of f.matchAll(P)){const x=h[1].replace("Async","").replace("Send","SEND").toUpperCase();e({method:x,endpointPattern:h[2],style:"csharp-httpclient",sourceFile:o})}}}const c=r.reduce((s,o)=>(s[o.method]=(s[o.method]||0)+1,s),{});return{totalCalls:r.length,byMethod:c,calls:r.slice(0,80)}}function J(t,l={}){const r=U(t),i=new Map,n=(o,p)=>{i.has(o.id)||i.set(o.id,{id:o.id,title:o.title,reason:"Detected from code signals",sourceFiles:new Set}),i.get(o.id).sourceFiles.add(b.relative(t,p))};for(const o of r){const p=S(o);for(const A of R)A.regex.test(p)&&n(A,o)}const e=b.join(t,"package.json");if(C.existsSync(e)){const o=JSON.parse(S(e)||"{}"),p=typeof o.name=="string"?o.name:b.basename(t);M(p)&&!i.size&&(i.set("ReadItems",{id:"ReadItems",title:"Read Items",reason:`Fallback default for ${p}`,sourceFiles:new Set}),i.set("CreateItem",{id:"CreateItem",title:"Create Item",reason:`Fallback default for ${p}`,sourceFiles:new Set}))}i.size||(i.set("CreateItem",{id:"CreateItem",title:"Create Item",reason:"Fallback default",sourceFiles:new Set}),i.set("ReadItems",{id:"ReadItems",title:"Read Items",reason:"Fallback default",sourceFiles:new Set}));const c=Array.from(i.values()).map(o=>({...o,sourceFiles:Array.from(o.sourceFiles||[])})),s=W(t);return{capabilities:c,components:O(r,t),displayFields:H(r),externalLibraries:s,uiLayout:N(r),styling:G(t,r,s),developmentProfile:B(t,r,s,l),apiCalls:q(t,r)}}async function Q(t,l=!1){if(l)return t;const r=D.createInterface({input:process.stdin,output:process.stdout}),i=t.map(c=>c.id).join(", "),n=await new Promise(c=>r.question(` Inferred capabilities (${i}). Press Enter to accept or type comma list: `,c));r.close();const e=String(n).trim();return e?e.split(",").map(c=>c.trim()).filter(Boolean).map(c=>({id:c,title:z(c),reason:"User provided during adopt review"})):t}function X(t){if(!t.length)return"No capabilities inferred.";const l=V(t),r=l.reduce((e,c)=>e+c.signalCount,0),i={high:l.filter(e=>e.confidence==="high").length,medium:l.filter(e=>e.confidence==="medium").length,low:l.filter(e=>e.confidence==="low").length},n=[];n.push("Adoption Analysis"),n.push("=".repeat(56)),n.push(`Capabilities detected : ${l.length}`),n.push(`Signal hits total : ${r}`),n.push(`Confidence mix : high=${i.high}, medium=${i.medium}, low=${i.low}`),n.push("-".repeat(56)),n.push("Capability Breakdown"),n.push("-".repeat(56)),n.push("Confidence Signals Capability"),n.push("-".repeat(56));for(const e of l){const c=e.confidence.toUpperCase().padEnd(10," "),s=String(e.signalCount).padEnd(7," ");if(n.push(`${c} ${s} ${e.id} (${e.title})`),e.signalCount>0){const o=e.sourceFiles.slice(0,3).join(", ");n.push(` sources: ${o}`),e.sourceFiles.length>3&&n.push(` more : +${e.sourceFiles.length-3} additional files`)}else n.push(" sources: inferred fallback (no strong code signal)")}return n.push("=".repeat(56)),n.join(`
3
- `)}function Y(t){const l=(r,i,n=10)=>{const e=[`${r} (${i.length})`];if(e.push("-".repeat(56)),!i.length)return e.push(" - none"),e.join(`
4
- `);for(const c of i.slice(0,n))e.push(` - ${c}`);return i.length>n&&e.push(` - ... +${i.length-n} more`),e.join(`
5
- `)};return["Project Structure Signals","=".repeat(56),l("Components",t.components||[]),l("Display fields",t.displayFields||[]),l("External libraries",t.externalLibraries||[]),"UI layout","-".repeat(56),` - layout type: ${t.uiLayout?.layoutType||"unknown"}`,` - uses grid : ${t.uiLayout?.usesGrid?"yes":"no"}`,` - uses flex : ${t.uiLayout?.usesFlex?"yes":"no"}`,` - sections : ${(t.uiLayout?.sections||[]).slice(0,10).join(", ")||"none"}`,"Styling","-".repeat(56),` - frameworks : ${(t.styling?.cssFrameworks||[]).join(", ")||"none detected"}`,` - style files: ${t.styling?.styleFileCount??0}`,` - tokens : ${(t.styling?.designTokens||[]).slice(0,8).join(", ")||"none detected"}`,"Development profile","-".repeat(56),` - language : ${t.developmentProfile?.language||"unknown"} (auto: ${t.developmentProfile?.detected?.language||"unknown"})`,` - framework : ${t.developmentProfile?.framework||"unknown"} (auto: ${t.developmentProfile?.detected?.framework||"unknown"})`,` - project type: ${t.developmentProfile?.projectType||"unknown"} (auto: ${t.developmentProfile?.detected?.projectType||"unknown"})`,"API calls","-".repeat(56),` - total calls : ${t.apiCalls?.totalCalls??0}`,` - by method : ${Object.entries(t.apiCalls?.byMethod||{}).map(([r,i])=>`${r}:${i}`).join(", ")||"none"}`,...(t.apiCalls?.calls||[]).slice(0,6).map(r=>` - ${r.method} ${r.endpointPattern} [${r.style}] (${r.sourceFile})`),...(t.apiCalls?.calls||[]).length>6?[` - ... +${(t.apiCalls?.calls||[]).length-6} more`]:[],"=".repeat(56)].join(`
6
- `)}function V(t){return t.map(l=>{const r=l.sourceFiles?.length||0,i=r>=3?"high":r>=1?"medium":"low";return{id:l.id,title:l.title,reason:l.reason,confidence:i,sourceFiles:l.sourceFiles||[],signalCount:r}})}function ee(t,l,r,i=null){const n=r.map(p=>p.id),e={policyId:l,policyVersion:1,capabilities:n,rules:{docsRequiredOnCapabilityChange:!0,requireScenarioForEachCapability:!0,requireChangelogOnCapabilityChange:!0}};C.mkdirSync(b.join(t,"scenarios"),{recursive:!0}),C.writeFileSync(b.join(t,"contract.json"),JSON.stringify(e,null,2)+`
7
- `);const c={schemaVersion:1,capabilities:r.map(p=>({id:p.id,title:p.title||z(p.id),since:"0.1.0"}))};C.writeFileSync(b.join(t,"capabilities.json"),JSON.stringify(c,null,2)+`
8
- `);const s={scenarioId:"adoption_baseline",description:"Baseline inferred from existing codebase during adoption",capabilitiesCovered:n,steps:n.map(p=>({action:p,expect:`${p} behavior exists in the current project`}))};if(C.writeFileSync(b.join(t,"scenarios","adoption_baseline.json"),JSON.stringify(s,null,2)+`
9
- `),i){const p={profileId:"adoption_profile",generatedAt:new Date().toISOString(),components:i.components||[],displayFields:i.displayFields||[],externalLibraries:i.externalLibraries||[],uiLayout:i.uiLayout||{layoutType:"unknown",usesGrid:!1,usesFlex:!1,sections:[]},styling:i.styling||{cssFrameworks:[],styleFileCount:0,styleFilesSample:[],designTokens:[]},developmentProfile:i.developmentProfile||{language:"unknown",framework:"unknown",projectType:"unknown",detected:{language:"unknown",framework:"unknown",projectType:"unknown"}},apiCalls:i.apiCalls||{totalCalls:0,byMethod:{},calls:[]}};C.writeFileSync(b.join(t,"adoption_profile.json"),JSON.stringify(p,null,2)+`
10
- `)}const o=`# Changelog \u2014 ${l}
1
+ import*as j from"node:fs";import*as A from"node:path";import*as D from"node:readline";import{seedProfileFromAdoption as R}from"../learning/profile.mjs";import{scanAngular as U}from"../adopters/angular.mjs";import{scanReact as M}from"../adopters/react.mjs";import{scanCSS as O}from"../adopters/css.mjs";function H(e){return e.replace(/[^a-zA-Z0-9]+/g," ").trim().split(/\s+/).filter(Boolean).map(r=>r[0].toUpperCase()+r.slice(1)).join("")}function _(e){return e.replace(/([A-Z])/g," $1").trim()}function k(e){try{return j.readFileSync(e,"utf8")}catch{return""}}const W=[{id:"CreateItem",title:"Create Item",regex:/\b(post|create|add)\b/i},{id:"ReadItems",title:"Read Items",regex:/\b(get|read|list|fetch)\b/i},{id:"UpdateItem",title:"Update Item",regex:/\b(put|patch|update|edit)\b/i},{id:"DeleteItem",title:"Delete Item",regex:/\b(delete|remove)\b/i},{id:"SearchItems",title:"Search Items",regex:/\bsearch\b/i},{id:"FilterItems",title:"Filter Items",regex:/\bfilter\b/i},{id:"SetDueDate",title:"Set Due Date",regex:/\bdueDate|deadline|due\b/i},{id:"SetPriority",title:"Set Priority",regex:/\bpriority\b/i},{id:"ToggleComplete",title:"Toggle Complete",regex:/\bcomplete|completed|toggle\b/i},{id:"ClearCompleted",title:"Clear Completed",regex:/\bclearCompleted|clear completed\b/i}];function ie(e){return X(e).capabilities}function G(e){const r=[],i=["src","server","app","backend","frontend","api","Controllers"];for(const o of i){const n=A.join(e,o);if(!j.existsSync(n))continue;const t=[n];for(;t.length;){const p=t.pop();for(const s of j.readdirSync(p,{withFileTypes:!0})){const a=A.join(p,s.name);if(s.isDirectory()){if(new Set(["node_modules",".git","dist","build","out","www","tmp",".tmp","vendor","assets","public","static","coverage",".nyc_output",".angular",".vite",".cache",".parcel-cache",".next",".nuxt","__pycache__","e2e","test","tests","spec","__tests__","fixtures","mocks",".turbo","storybook-static"]).has(s.name))continue;t.push(a)}else if(/\.(js|jsx|ts|tsx|mjs|cjs|json|md|html|htm|cs|csproj)$/.test(s.name)){if(/\.(min|bundle)\.(js|css)$/.test(s.name)||/\.map$/.test(s.name)||/\.(spec|test)\.(ts|js|tsx|jsx)$/.test(s.name))continue;r.push(a)}}}}for(const o of j.readdirSync(e,{withFileTypes:!0}))o.isFile()&&/^(Program\.cs|.+\.csproj)$/i.test(o.name)&&r.push(A.join(e,o.name));return r}function N(e,r){const i=new Set;for(const o of e){const n=A.relative(r,o),t=k(o),p=t.matchAll(/\bclass\s+([A-Z][A-Za-z0-9_]*?(?:Component|Page|View|Widget|Card))\b/g);for(const m of p)i.add(m[1]);const s=t.matchAll(/\bselector\s*:\s*["']([^"']+)["']/g);for(const m of s)i.add(m[1]);const a=t.matchAll(/\bfunction\s+([A-Z][A-Za-z0-9_]*)\s*\(/g);for(const m of a)/component|page|view|card|chart|dashboard/i.test(m[1])&&i.add(m[1]);const d=n.match(/([^/\\]+)\.(component|page|view|widget|card)\.(ts|tsx|js|jsx)$/i);d&&i.add(d[1])}return Array.from(i).sort()}function B(e){const r=new Set,i=new Set,o=new Set(["if","for","while","const","let","var","return","function","class","import","export","null","undefined","true","false","string","number","boolean","any","unknown","never","selector","templateUrl","styleUrl","standalone","imports","providers","providedIn","options","scales","responsive","display","title","type","label","component","service","routes","appConfig","ApplicationConfig"]),n=t=>{t&&/^[A-Za-z_][A-Za-z0-9_]*$/.test(t)&&(t.length<=1||o.has(t)||/^[A-Z0-9_]+$/.test(t)||r.add(t))};for(const t of e){const p=k(t);if(/\.(html|htm)$/i.test(t)){const s=p.matchAll(/\{\{\s*(?:this\.)?([a-zA-Z_][a-zA-Z0-9_]*)/g);for(const c of s)n(c[1]);const a=p.matchAll(/\[\(ngModel\)\]\s*=\s*["']([a-zA-Z_][a-zA-Z0-9_]*)["']/g);for(const c of a)n(c[1]);const d=p.matchAll(/\[[a-zA-Z0-9_-]+\]\s*=\s*["'](?:this\.)?([a-zA-Z_][a-zA-Z0-9_]*)["']/g);for(const c of d)n(c[1]);const m=p.matchAll(/\*ngIf\s*=\s*["'](?:this\.)?([a-zA-Z_][a-zA-Z0-9_]*)/g);for(const c of m)n(c[1])}if(/\.(ts|tsx|js|jsx|mjs|cjs)$/i.test(t)){const s=p.matchAll(/(?:^|\n)\s*(?:public|private|protected)?\s*(?:async\s+)?([a-zA-Z_][a-zA-Z0-9_]*)\s*\([^)]*\)\s*\{/g);for(const f of s)i.add(f[1]);const a=p.matchAll(/\bthis\.([a-zA-Z_][a-zA-Z0-9_]*)\b/g);for(const f of a)n(f[1]);const d=p.matchAll(/(?:^|\n)\s*(?:public|private|protected)?\s*(?:readonly\s+)?([a-zA-Z_][a-zA-Z0-9_]*)\s*(?::|=)/g);for(const f of d)n(f[1]);const m=p.matchAll(/@Input\([^)]*\)\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*[:=]/g);for(const f of m)n(f[1]);const c=p.matchAll(/forEach\(\((\w+)\)\s*=>/g);for(const f of c){const C=f[1],S=new RegExp(`\\b${C}\\.([a-zA-Z_][a-zA-Z0-9_]*)\\b`,"g");for(const y of p.matchAll(S))n(y[1])}}}return Array.from(r).filter(t=>!i.has(t)).sort().slice(0,80)}function V(e){const r=new Set,i=A.join(e,"package.json");if(!j.existsSync(i))return[];try{const o=JSON.parse(k(i)||"{}"),n={...o.dependencies||{},...o.devDependencies||{}};for(const t of Object.keys(n))r.add(t)}catch{}return Array.from(r).sort()}function q(e,r,i){const o=r.filter(s=>/\.(css|scss|sass|less|styl)$/i.test(s)).map(s=>A.relative(e,s)).sort(),n=[],t=s=>i.includes(s);t("tailwindcss")&&n.push("Tailwind CSS"),t("bootstrap")&&n.push("Bootstrap"),i.some(s=>s.startsWith("@angular/material"))&&n.push("Angular Material"),t("antd")&&n.push("Ant Design"),t("styled-components")&&n.push("styled-components"),(t("emotion")||t("@emotion/react"))&&n.push("Emotion");const p=new Set;for(const s of r){if(!/\.(css|scss|sass|less|styl|html|htm|ts|tsx|js|jsx|mjs|cjs)$/i.test(s))continue;const a=k(s);for(const d of a.matchAll(/--([a-zA-Z][a-zA-Z0-9_-]*)/g))p.add(`--${d[1]}`)}return{cssFrameworks:n,styleFileCount:o.length,styleFilesSample:o.slice(0,12),designTokens:Array.from(p).sort().slice(0,24)}}function J(e){let r=!1,i=!1;const o=new Set;for(const t of e){if(!/\.(html|htm|tsx|jsx|ts|js|mjs|cjs)$/i.test(t))continue;const p=k(t);/\bgrid\b|grid-template|grid-cols-|display:\s*grid/i.test(p)&&(r=!0),/\bflex\b|display:\s*flex|flex-row|flex-col|justify-|items-/i.test(p)&&(i=!0);for(const s of p.matchAll(/<(main|header|footer|section|aside|nav)\b/gi))o.add(s[1].toLowerCase());for(const s of p.matchAll(/class(?:Name)?\s*=\s*["'`][^"'`]*(dashboard|chart|card|sidebar|content|toolbar|filter|panel|table)[^"'`]*["'`]/gi)){const a=s[1].toLowerCase();o.add(a==="filter"?"filters":a)}}return{layoutType:r&&i?"mixed":r?"grid":i?"flex":"unknown",usesGrid:r,usesFlex:i,sections:Array.from(o).sort()}}function K(e,r,i,o={}){const n={ts:0,js:0,py:0,java:0,go:0,rb:0,rs:0,cs:0,php:0};for(const y of r){const b=A.extname(y).toLowerCase();(b===".ts"||b===".tsx")&&(n.ts+=1),(b===".js"||b===".jsx"||b===".mjs"||b===".cjs")&&(n.js+=1),b===".py"&&(n.py+=1),b===".java"&&(n.java+=1),b===".go"&&(n.go+=1),b===".rb"&&(n.rb+=1),b===".rs"&&(n.rs+=1),b===".cs"&&(n.cs+=1),b===".php"&&(n.php+=1)}const t=Object.entries(n).sort((y,b)=>b[1]-y[1]),p=t[0]?.[1]>0?t[0][0]:"unknown";let s="unknown",a=!1,d=!1,m=!1;for(const y of r){const b=A.basename(y).toLowerCase();if(b.endsWith(".csproj")){const P=k(y);/Microsoft\.NET\.Sdk\.Web/i.test(P)&&(a=!0),(/Blazor/i.test(P)||/Microsoft\.AspNetCore\.Components/i.test(P))&&(m=!0)}if(b==="program.cs"){const P=k(y);/app\.Map(Get|Post|Put|Delete|Patch)\s*\(/i.test(P)&&(d=!0)}}const c=y=>i.includes(y);i.some(y=>y.startsWith("@angular/"))?s="angular":c("react")?s="react":c("vue")?s="vue":c("svelte")?s="svelte":c("next")?s="nextjs":c("nuxt")?s="nuxt":c("express")?s="express":c("@nestjs/core")?s="nestjs":c("fastify")?s="fastify":c("flask")?s="flask":c("django")?s="django":c("spring-boot")?s="spring":m?s="blazor":d?s="minimalapi":(a||n.cs>0)&&(s="aspnet");let f="fullstack";const C=["src","frontend","app"].some(y=>j.existsSync(A.join(e,y))),S=["server","backend","api"].some(y=>j.existsSync(A.join(e,y)));return["react","angular","vue","svelte","nextjs","nuxt"].includes(s)&&(f="frontend"),["express","nestjs","fastify","flask","django","spring","aspnet","minimalapi"].includes(s)&&(f="backend"),C&&S&&(f="fullstack"),!C&&!S&&(f="library"),s==="blazor"&&(f="frontend"),{language:o.language||p,framework:o.framework||s,projectType:o.projectType||f,detected:{language:p,framework:s,projectType:f}}}function Q(e,r){const i=[],o=new Set,n=s=>{let a=String(s||"").trim();return a?(a=a.replace(/https?:\/\/[^/]+/gi,""),a=a.replace(/\$\{[^}]+\}/g,"{var}"),a=a.replace(/\{[A-Za-z_][A-Za-z0-9_]*\}/g,"{var}"),a=a.replace(/:[A-Za-z_][A-Za-z0-9_]*/g,"{var}"),a=a.replace(/\/\d+(?=\/|$)/g,"/{id}"),a=a.replace(/=[^&\s]+/g,"={value}"),a=a.replace(/\/+/g,"/"),a):""},t=s=>{const a=n(s.endpointPattern);if(!a)return;const d=`${s.method}|${a}|${s.sourceFile}|${s.style}`;o.has(d)||(o.add(d),i.push({...s,endpointPattern:a}))};for(const s of r){if(!/\.(ts|tsx|js|jsx|mjs|cjs|cs)$/i.test(s))continue;const a=A.relative(e,s),d=k(s);if(!(/service|api|client|controller|program\.cs/i.test(a)||/HttpClient|fetch\(|app\.Map(Get|Post|Put|Delete|Patch)\(/i.test(d)))continue;const c=d.replace(/\r\n/g,`
2
+ `),f={},C=u=>{let l=String(u||"").trim();if(!l)return"";for(l=l.replace(/;+$/,"").trim(),l=l.replace(/\(\s*$/,"");l.startsWith("'")&&l.endsWith("'")||l.startsWith('"')&&l.endsWith('"')||l.startsWith("`")&&l.endsWith("`");)l=l.slice(1,-1).trim();return l},S=u=>{const l=String(u||"").trim();return l?!!(/^https?:\/\//i.test(l)||l.startsWith("/")||/\bapi\b/i.test(l)||/\$\{[^}]+\}/.test(l)||/\?[^=\s]+=?/.test(l)||/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_./${}-]+$/.test(l)):!1},y=(u,l)=>{!u||!l||(f[u]=C(l))},b=/(?:const|let|var)\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([\s\S]*?);/g;for(const u of c.matchAll(b))y(u[1],u[2]);const P=/(?:public|private|protected)?\s*(?:readonly\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([\s\S]*?);/g;for(const u of c.matchAll(P))y(u[1],u[2]);const F=u=>{const l=C(u);if(!l)return"";if(/^['"`][\s\S]*['"`]$/.test(l))return l.replace(/^['"`]|['"`]$/g,"");if(f[l]&&S(f[l]))return f[l];const h=l.match(/^this\.([A-Za-z_][A-Za-z0-9_]*)$/);if(h&&f[h[1]]&&S(f[h[1]]))return f[h[1]];const w=l.split("+").map(g=>g.trim()).filter(Boolean);if(w.length>1){const g=w.map(x=>{if(/^['"`][\s\S]*['"`]$/.test(x))return x.replace(/^['"`]|['"`]$/g,"");if(f[x]&&S(f[x]))return f[x];const v=x.match(/^this\.([A-Za-z_][A-Za-z0-9_]*)$/);return v&&f[v[1]]&&S(f[v[1]])?f[v[1]]:`{${x}}`}).join("");if(g)return g}const $=l.match(/^(.+?)\?(.+?):(.+)$/s);if($){const g=F($[2]),x=F($[3]);if(g||x)return`${g||"{optionA}"} | ${x||"{optionB}"}`}return l},T=/\.\s*(get|post|put|patch|delete)\s*(?:<[\s\S]*?>)?\s*\(\s*([\s\S]*?)(?:,|\))/gi;for(const u of c.matchAll(T)){const l=u[1].toUpperCase(),h=F(u[2]);!h||!S(h)||t({method:l,endpointPattern:h,style:"httpClient",sourceFile:a})}const z=/\bfetch\s*\(\s*([\s\S]*?)(?:,|\))/gi;for(const u of c.matchAll(z)){const l=F(u[1]);if(!l||!S(l))continue;const h=u.index||0,w=c.slice(h,h+260),g=(/method\s*:\s*["'](GET|POST|PUT|PATCH|DELETE)["']/i.exec(w)?.[1]||"GET").toUpperCase();t({method:g,endpointPattern:l,style:"fetch",sourceFile:a})}const I=/\baxios\.(get|post|put|patch|delete)\s*\(\s*([\s\S]*?)(?:,|\))/gi;for(const u of c.matchAll(I)){const l=u[1].toUpperCase(),h=F(u[2]);!h||!S(h)||t({method:l,endpointPattern:h,style:"axios",sourceFile:a})}const Z=/\baxios\s*\(\s*\{([\s\S]*?)\}\s*\)/gi;for(const u of c.matchAll(Z)){const l=u[1],h=/\bmethod\s*:\s*["']?(get|post|put|patch|delete)["']?/i.exec(l),w=/\burl\s*:\s*([^,\n]+)/i.exec(l),$=(h?.[1]||"get").toUpperCase(),g=F(w?.[1]||"");!g||!S(g)||t({method:$,endpointPattern:g,style:"axios-config",sourceFile:a})}const E=/\.\s*request\s*\(\s*["'](GET|POST|PUT|PATCH|DELETE)["']\s*,\s*([\s\S]*?)(?:,|\))/gi;for(const u of c.matchAll(E)){const l=u[1].toUpperCase(),h=F(u[2]);!h||!S(h)||t({method:l,endpointPattern:h,style:"request",sourceFile:a})}if(/\.cs$/i.test(s)){const u=/\bapp\.Map(Get|Post|Put|Delete|Patch)\s*\(\s*"([^"]+)"/gi;for(const g of c.matchAll(u))t({method:g[1].toUpperCase(),endpointPattern:g[2],style:"csharp-map",sourceFile:a});const l=/\[Route\("([^"]+)"\)\][\s\S]*?class\s+\w+/i.exec(c),h=l?l[1]:"",w=/\[(HttpGet|HttpPost|HttpPut|HttpDelete|HttpPatch)(?:\("([^"]*)"\))?\]/gi;for(const g of c.matchAll(w)){const x=g[1].replace("Http","").toUpperCase(),v=g[2]||"",L=[h,v].filter(Boolean).join("/").replace(/\/+/g,"/").replace(/\[controller\]/gi,"{controller}");t({method:x,endpointPattern:L||h||"{controller-route}",style:"csharp-controller",sourceFile:a})}const $=/\b(GetAsync|PostAsync|PutAsync|DeleteAsync|SendAsync)\s*\(\s*"([^"]+)"/gi;for(const g of c.matchAll($)){const x=g[1].replace("Async","").replace("Send","SEND").toUpperCase();t({method:x,endpointPattern:g[2],style:"csharp-httpclient",sourceFile:a})}}}const p=i.reduce((s,a)=>(s[a.method]=(s[a.method]||0)+1,s),{});return{totalCalls:i.length,byMethod:p,calls:i.slice(0,80)}}function X(e,r={}){const i=G(e),o=new Map,n=(c,f)=>{o.has(c.id)||o.set(c.id,{id:c.id,title:c.title,reason:"Detected from code signals",sourceFiles:new Set}),o.get(c.id).sourceFiles.add(A.relative(e,f))};for(const c of i){const f=k(c);for(const C of W)C.regex.test(f)&&n(C,c)}const t=A.join(e,"package.json");if(j.existsSync(t)){const c=JSON.parse(k(t)||"{}"),f=typeof c.name=="string"?c.name:A.basename(e);H(f)&&!o.size&&(o.set("ReadItems",{id:"ReadItems",title:"Read Items",reason:`Fallback default for ${f}`,sourceFiles:new Set}),o.set("CreateItem",{id:"CreateItem",title:"Create Item",reason:`Fallback default for ${f}`,sourceFiles:new Set}))}o.size||(o.set("CreateItem",{id:"CreateItem",title:"Create Item",reason:"Fallback default",sourceFiles:new Set}),o.set("ReadItems",{id:"ReadItems",title:"Read Items",reason:"Fallback default",sourceFiles:new Set}));const p=V(e),s=K(e,i,p,r);let a=[];try{if(s.framework==="angular"){const c=U(e,i);for(const f of c.capabilities)o.has(f.id)||o.set(f.id,{...f,sourceFiles:new Set(f.sourceFiles)})}else if(s.framework==="react"||s.framework==="nextjs"){const c=M(e,i);for(const f of c.capabilities)o.has(f.id)||o.set(f.id,{...f,sourceFiles:new Set(f.sourceFiles)})}}catch{}let d={designTokens:[],colorTokens:[],spacingTokens:[],componentClasses:[],themeVars:[]};try{d=O(e,i)}catch{}return{capabilities:Array.from(o.values()).map(c=>({...c,sourceFiles:Array.from(c.sourceFiles||[])})),components:N(i,e),displayFields:B(i),externalLibraries:p,uiLayout:J(i),styling:{...q(e,i,p),designTokens:d.designTokens.length>0?d.designTokens:void 0,colorTokens:d.colorTokens,spacingTokens:d.spacingTokens,componentClasses:d.componentClasses,themeVars:d.themeVars},developmentProfile:s,apiCalls:Q(e,i)}}async function re(e,r=!1){if(r)return e;const i=D.createInterface({input:process.stdin,output:process.stdout}),o=e.map(p=>p.id).join(", "),n=await new Promise(p=>i.question(` Inferred capabilities (${o}). Press Enter to accept or type comma list: `,p));i.close();const t=String(n).trim();return t?t.split(",").map(p=>p.trim()).filter(Boolean).map(p=>({id:p,title:_(p),reason:"User provided during adopt review"})):e}function ae(e){if(!e.length)return"No capabilities inferred.";const r=Y(e),i=r.reduce((t,p)=>t+p.signalCount,0),o={high:r.filter(t=>t.confidence==="high").length,medium:r.filter(t=>t.confidence==="medium").length,low:r.filter(t=>t.confidence==="low").length},n=[];n.push("Adoption Analysis"),n.push("=".repeat(56)),n.push(`Capabilities detected : ${r.length}`),n.push(`Signal hits total : ${i}`),n.push(`Confidence mix : high=${o.high}, medium=${o.medium}, low=${o.low}`),n.push("-".repeat(56)),n.push("Capability Breakdown"),n.push("-".repeat(56)),n.push("Confidence Signals Capability"),n.push("-".repeat(56));for(const t of r){const p=t.confidence.toUpperCase().padEnd(10," "),s=String(t.signalCount).padEnd(7," ");if(n.push(`${p} ${s} ${t.id} (${t.title})`),t.signalCount>0){const a=t.sourceFiles.slice(0,3).join(", ");n.push(` sources: ${a}`),t.sourceFiles.length>3&&n.push(` more : +${t.sourceFiles.length-3} additional files`)}else n.push(" sources: inferred fallback (no strong code signal)")}return n.push("=".repeat(56)),n.join(`
3
+ `)}function ce(e){const r=(i,o,n=10)=>{const t=[`${i} (${o.length})`];if(t.push("-".repeat(56)),!o.length)return t.push(" - none"),t.join(`
4
+ `);for(const p of o.slice(0,n))t.push(` - ${p}`);return o.length>n&&t.push(` - ... +${o.length-n} more`),t.join(`
5
+ `)};return["Project Structure Signals","=".repeat(56),r("Components",e.components||[]),r("Display fields",e.displayFields||[]),r("External libraries",e.externalLibraries||[]),"UI layout","-".repeat(56),` - layout type: ${e.uiLayout?.layoutType||"unknown"}`,` - uses grid : ${e.uiLayout?.usesGrid?"yes":"no"}`,` - uses flex : ${e.uiLayout?.usesFlex?"yes":"no"}`,` - sections : ${(e.uiLayout?.sections||[]).slice(0,10).join(", ")||"none"}`,"Styling","-".repeat(56),` - frameworks : ${(e.styling?.cssFrameworks||[]).join(", ")||"none detected"}`,` - style files: ${e.styling?.styleFileCount??0}`,` - tokens : ${(e.styling?.designTokens||[]).slice(0,8).join(", ")||"none detected"}`,"Development profile","-".repeat(56),` - language : ${e.developmentProfile?.language||"unknown"} (auto: ${e.developmentProfile?.detected?.language||"unknown"})`,` - framework : ${e.developmentProfile?.framework||"unknown"} (auto: ${e.developmentProfile?.detected?.framework||"unknown"})`,` - project type: ${e.developmentProfile?.projectType||"unknown"} (auto: ${e.developmentProfile?.detected?.projectType||"unknown"})`,"API calls","-".repeat(56),` - total calls : ${e.apiCalls?.totalCalls??0}`,` - by method : ${Object.entries(e.apiCalls?.byMethod||{}).map(([i,o])=>`${i}:${o}`).join(", ")||"none"}`,...(e.apiCalls?.calls||[]).slice(0,6).map(i=>` - ${i.method} ${i.endpointPattern} [${i.style}] (${i.sourceFile})`),...(e.apiCalls?.calls||[]).length>6?[` - ... +${(e.apiCalls?.calls||[]).length-6} more`]:[],"=".repeat(56)].join(`
6
+ `)}function Y(e){return e.map(r=>{const i=r.sourceFiles?.length||0,o=i>=3?"high":i>=1?"medium":"low";return{id:r.id,title:r.title,reason:r.reason,confidence:o,sourceFiles:r.sourceFiles||[],signalCount:i}})}function ee(e){if(!e)return null;const r=[...e.components||[]].filter(Boolean).slice(0,40),i=(e.styling?.designTokens||[]).slice(0,20),o=e.uiLayout?.layoutType||"unknown",n=(e.uiLayout?.sections||[]).slice(0,12),t=e.styling?.cssFrameworks||[],p=(e.styling?.colorTokens||[]).slice(0,10),s=(e.styling?.themeVars||[]).slice(0,10);return{components:r,designTokens:i,colorTokens:p,themeVars:s,cssFrameworks:t,layout:o,sections:n,lastScanned:new Date().toISOString()}}function le(e,r){return r?e?{...r,components:[...new Set([...r.components||[],...e.components||[]])].slice(0,50),designTokens:[...new Set([...r.designTokens||[],...e.designTokens||[]])].slice(0,30)}:r:e}function pe(e,r,i,o=null){const n=i.map(m=>m.id),t=ee(o),p={policyId:r,policyVersion:1,capabilities:n,rules:{docsRequiredOnCapabilityChange:!0,requireScenarioForEachCapability:!0,requireChangelogOnCapabilityChange:!0},...t?{ui:t}:{}};j.mkdirSync(A.join(e,"scenarios"),{recursive:!0}),j.writeFileSync(A.join(e,"contract.json"),JSON.stringify(p,null,2)+`
7
+ `);const s={schemaVersion:1,capabilities:i.map(m=>({id:m.id,title:m.title||_(m.id),since:"0.1.0"}))};j.writeFileSync(A.join(e,"capabilities.json"),JSON.stringify(s,null,2)+`
8
+ `);const a={scenarioId:"adoption_baseline",description:"Baseline inferred from existing codebase during adoption",capabilitiesCovered:n,steps:n.map(m=>({action:m,expect:`${m} behavior exists in the current project`}))};if(j.writeFileSync(A.join(e,"scenarios","adoption_baseline.json"),JSON.stringify(a,null,2)+`
9
+ `),o){const m={profileId:"adoption_profile",generatedAt:new Date().toISOString(),components:o.components||[],displayFields:o.displayFields||[],externalLibraries:o.externalLibraries||[],uiLayout:o.uiLayout||{layoutType:"unknown",usesGrid:!1,usesFlex:!1,sections:[]},styling:o.styling||{cssFrameworks:[],styleFileCount:0,styleFilesSample:[],designTokens:[]},developmentProfile:o.developmentProfile||{language:"unknown",framework:"unknown",projectType:"unknown",detected:{language:"unknown",framework:"unknown",projectType:"unknown"}},apiCalls:o.apiCalls||{totalCalls:0,byMethod:{},calls:[]}};j.writeFileSync(A.join(e,"adoption_profile.json"),JSON.stringify(m,null,2)+`
10
+ `);try{R(e,o,i)}catch{}}const d=`# Changelog \u2014 ${r}
11
11
 
12
12
  ## Unreleased
13
13
 
@@ -17,4 +17,4 @@ import*as C from"node:fs";import*as b from"node:path";import*as D from"node:read
17
17
  ## 0.1.0 \u2014 Adoption baseline
18
18
 
19
19
  - Initial baseline generated by infernoflow init --adopt
20
- `;C.writeFileSync(b.join(t,"CHANGELOG.md"),o,"utf8")}export{X as buildAdoptionReport,Y as buildSignalsReport,K as discoverCapabilities,J as discoverProjectSignals,Q as reviewCapabilitiesInteractive,V as summarizeCapabilities,ee as writeAdoptionBaseline};
20
+ `;j.writeFileSync(A.join(e,"CHANGELOG.md"),d,"utf8")}export{ae as buildAdoptionReport,ce as buildSignalsReport,ee as buildUiContractSection,ie as discoverCapabilities,X as discoverProjectSignals,le as mergeUiSection,re as reviewCapabilitiesInteractive,Y as summarizeCapabilities,pe as writeAdoptionBaseline};
@@ -1,20 +1,27 @@
1
- import l from"node:fs";import d from"node:path";import{execSync as w}from"node:child_process";import{bold as f,gray as S,cyan as r,red as P,green as a,yellow as I}from"../ui/output.mjs";import{buildCursorImplementPrompt as q,buildGenericImplementPrompt as z}from"../ui/prompts.mjs";function K(t){try{const o=process.platform;if(o==="win32")w("clip",{input:t});else if(o==="darwin")w("pbcopy",{input:t});else try{w("xclip -selection clipboard",{input:t})}catch{w("xsel --clipboard --input",{input:t})}return!0}catch{return!1}}const g="inferno",D=d.join(g,"CONTEXT.md"),T=d.join(g,"context-state.json");function L(t){try{return JSON.parse(l.readFileSync(t,"utf8"))}catch{return null}}function v(t){try{return l.readFileSync(t,"utf8")}catch{return null}}function Q(){const t=v(T);if(!t)return{};try{return JSON.parse(t)}catch{return{}}}function Y(t){l.writeFileSync(T,JSON.stringify(t,null,2),"utf8")}function _(t){return t?new Date(t).toLocaleDateString("en-GB",{day:"2-digit",month:"short",year:"numeric"}):"unknown"}function Z(t,o){if(!t)return[];const i=[];let s=null;for(const c of t.split(`
2
- `))if(c.startsWith("## ")){if(s&&i.length<o&&i.push(s),i.length>=o)break;s={title:c.replace("## ","").trim(),items:[]}}else s&&c.startsWith("- ")&&s.items.push(c.replace("- ","").trim());return s&&i.length<o&&i.push(s),i.filter(c=>c.items.length>0)}async function cn(t){const o=e=>t.includes(e),i=e=>{const u=t.indexOf(e);return u!==-1&&t[u+1]?t[u+1]:null},s=i("--intent")||i("-i"),c=i("--working")||i("-w"),m=i("--decision")||i("-d"),E=o("--show")||o("-s"),x=o("--copy")||o("-c"),W=o("--cursor"),G=o("--copilot"),R=o("--reset");console.log(`
3
- `+f("\uFFFD\uFFFD\uFFFD infernoflow \u2014 context")),console.log(" "+"\u2500".repeat(50)+`
4
- `),l.existsSync(g)||(console.error(P(" \u2718 inferno/ not found")),console.error(S(` \u2192 Run: infernoflow init
5
- `)),process.exit(1));const p=L(d.join(g,"contract.json")),b=L(d.join(g,"capabilities.json")),A=v(d.join(g,"CHANGELOG.md"));(!p||!b)&&(console.error(P(` \u2718 Missing contract.json or capabilities.json
6
- `)),process.exit(1));let n=Q();R&&(n={},console.log(I(` \u26A0 State reset
7
- `))),s&&(n.intent=s,n.intentUpdated=new Date().toISOString(),console.log(a(' \u2714 Intent saved: "'+s+'"'))),c&&(n.working=c,n.workingUpdated=new Date().toISOString(),console.log(a(' \u2714 Working on: "'+c+'"'))),m&&(n.decisions||(n.decisions=[]),n.decisions.push({text:m,date:new Date().toISOString()}),console.log(a(' \u2714 Decision recorded: "'+m+'"'))),(s||c||m)&&Y(n);const h=b.capabilities||[],j=h.length===(p.capabilities||[]).length,O=Z(A,3),C=String(p.policyVersion).replace(/^v/i,""),U=new Date().toLocaleDateString("en-GB",{day:"2-digit",month:"short",year:"numeric"}),N=j?"\u2713 validated":"\u26A0 out of sync",k=n.intent||"describe the exact task to implement",F={task:k,contract:p,caps:b,scenarios:[],state:n},J=q(F),V=z(F),X=h.map(e=>"- **"+e.id+"** \u2014 "+e.title).join(`
8
- `),B=O.length>0?O.map(e=>"### "+e.title+`
9
- `+e.items.map(u=>" - "+u).join(`
1
+ import l from"node:fs";import h from"node:path";import{execSync as b}from"node:child_process";import{bold as m,gray as d,cyan as r,red as G,green as a,yellow as _}from"../ui/output.mjs";import{buildCursorImplementPrompt as ct,buildGenericImplementPrompt as rt}from"../ui/prompts.mjs";import{detectDrift as lt}from"../git/detect-drift.mjs";function at(n){try{const o=process.platform;if(o==="win32")b("clip",{input:n});else if(o==="darwin")b("pbcopy",{input:n});else try{b("xclip -selection clipboard",{input:n})}catch{b("xsel --clipboard --input",{input:n})}return!0}catch{return!1}}const u="inferno",R=h.join(u,"CONTEXT.md"),A=h.join(u,"context-state.json");function U(n){try{return JSON.parse(l.readFileSync(n,"utf8"))}catch{return null}}function $(n){try{return l.readFileSync(n,"utf8")}catch{return null}}function J(){const n=$(A);if(!n)return{};try{return JSON.parse(n)}catch{return{}}}function V(n){l.writeFileSync(A,JSON.stringify(n,null,2),"utf8")}function O(n){return n?new Date(n).toLocaleDateString("en-GB",{day:"2-digit",month:"short",year:"numeric"}):"unknown"}function pt(n,o){if(!n)return[];const i=[];let s=null;for(const c of n.split(`
2
+ `))if(c.startsWith("## ")){if(s&&i.length<o&&i.push(s),i.length>=o)break;s={title:c.replace("## ","").trim(),items:[]}}else s&&c.startsWith("- ")&&s.items.push(c.replace("- ","").trim());return s&&i.length<o&&i.push(s),i.filter(c=>c.items.length>0)}async function gt(n){const o=e=>n.includes(e),i=e=>{const p=n.indexOf(e);return p!==-1&&n[p+1]?n[p+1]:null},s=i("--intent")||i("-i"),c=i("--working")||i("-w"),w=i("--decision")||i("-d"),X=o("--show")||o("-s"),N=o("--copy")||o("-c"),B=o("--cursor"),H=o("--copilot"),K=o("--reset"),M=o("--watch"),v=parseInt(i("--interval")||"30",10)*1e3;console.log(`
3
+ `+m("\uFFFD\uFFFD\uFFFD infernoflow \u2014 context")),console.log(" "+"\u2500".repeat(50)+`
4
+ `),l.existsSync(u)||(console.error(G(" \u2718 inferno/ not found")),console.error(d(` \u2192 Run: infernoflow init
5
+ `)),process.exit(1));const f=U(h.join(u,"contract.json")),k=U(h.join(u,"capabilities.json")),q=$(h.join(u,"CHANGELOG.md"));(!f||!k)&&(console.error(G(` \u2718 Missing contract.json or capabilities.json
6
+ `)),process.exit(1));let t=J();K&&(t={},console.log(_(` \u26A0 State reset
7
+ `))),s&&(t.intent=s,t.intentUpdated=new Date().toISOString(),console.log(a(' \u2714 Intent saved: "'+s+'"'))),c&&(t.working=c,t.workingUpdated=new Date().toISOString(),console.log(a(' \u2714 Working on: "'+c+'"'))),w&&(t.decisions||(t.decisions=[]),t.decisions.push({text:w,date:new Date().toISOString()}),console.log(a(' \u2714 Decision recorded: "'+w+'"'))),(s||c||w)&&V(t);const y=k.capabilities||[],P=y.length===(f.capabilities||[]).length,D=pt(q,3),I=String(f.policyVersion).replace(/^v/i,""),z=new Date().toLocaleDateString("en-GB",{day:"2-digit",month:"short",year:"numeric"}),T=P?"\u2713 validated":"\u26A0 out of sync",F=t.intent||"describe the exact task to implement",W={task:F,contract:f,caps:k,scenarios:[],state:t},Q=ct(W),Y=rt(W),Z=y.map(e=>"- **"+e.id+"** \u2014 "+e.title).join(`
8
+ `),tt=D.length>0?D.map(e=>"### "+e.title+`
9
+ `+e.items.map(p=>" - "+p).join(`
10
10
  `)).join(`
11
11
 
12
- `):"_No recent changes_",$=n.intent?n.intent+" _("+_(n.intentUpdated)+")_":'_Not set \u2014 run: infernoflow context --intent "..."_',H=n.working?n.working+" _("+_(n.workingUpdated)+")_":'_Not set \u2014 run: infernoflow context --working "..."_',M=n.decisions&&n.decisions.length>0?n.decisions.slice(-5).map(e=>"- "+e.text+" _("+_(e.date)+")_").join(`
13
- `):"_No decisions recorded_",y=["# Project Context \u2014 "+p.policyId+" v"+C,"> Generated by infernoflow | "+U+" | "+N,"","---","","## What this system does","",X,"","---","","## Recent changes","",B,"","---","","## Current state","","- **Capabilities:** "+h.length,"- **Version:** v"+C,"- **Sync:** "+N,"","---","","## What I am working on right now","",H,"","---","","## Intent \u2014 what I want to build next","",$,"","---","","## Decisions & notes","",M,"","---","","## Implementation Prompt Seed","","Use this to start coding immediately with an agent:","","```bash",`infernoflow implement "${k}" --mode both`,"```","","### Cursor Agent Prompt","","```text",J,"```","","### Generic Agent Prompt","","```text",V,"```","","---","_Paste this block at the start of any new AI session._"].join(`
14
- `);if(E||(l.writeFileSync(D,y,"utf8"),console.log(a(`
15
- \u2714 Context written \u2192 `+D))),x){const e=K(y);console.log(e?a(" \u2714 Copied to clipboard \u2014 paste with Ctrl+V"):I(" \u26A0 Clipboard copy failed \u2014 open inferno/CONTEXT.md manually"))}W&&(l.writeFileSync(".cursorrules",y,"utf8"),console.log(a(" \u2714 Written to .cursorrules \u2014 Cursor loads this automatically"))),G&&(l.existsSync(".github")||l.mkdirSync(".github"),l.writeFileSync(".github/copilot-instructions.md",y,"utf8"),console.log(a(" \u2714 Written to .github/copilot-instructions.md \u2014 Copilot loads this automatically"))),console.log(`
16
- `+f("Context Summary")),console.log(" "+"\u2500".repeat(50)),console.log(" Project "+p.policyId+" \u2014 v"+C),console.log(" Capabilities "+h.length+" registered"),console.log(" Sync "+(j?a("\u2713 in sync"):I("\u26A0 check needed"))),console.log(" Working on "+(n.working?r(n.working):S("not set"))),console.log(" Intent "+(n.intent?r(n.intent):S("not set"))),console.log(" Decisions "+(n.decisions?n.decisions.length:0)+` recorded
17
- `),console.log(" "+f("Implementation Prompt")),console.log(" "+r("\u2192")+" Run "+r(`infernoflow implement "${k}" --mode both`)+`
18
- `),x?(console.log(" "+f("Ready to use:")),console.log(" "+r("\u2192")+" Paste into Claude / Cursor / Copilot with "+r("Ctrl+V")+`
19
- `)):(console.log(" "+f("Ready to use:")),console.log(" "+r("1.")+" Open "+r("inferno/CONTEXT.md")),console.log(" "+r("2.")+" Copy everything"),console.log(" "+r("3.")+" Paste at the start of your next AI session"),console.log(" "+S(" tip: use --copy to skip steps 1-2 automatically")+`
20
- `))}export{cn as contextCommand};
12
+ `):"_No recent changes_",nt=t.intent?t.intent+" _("+O(t.intentUpdated)+")_":'_Not set \u2014 run: infernoflow context --intent "..."_',et=t.working?t.working+" _("+O(t.workingUpdated)+")_":'_Not set \u2014 run: infernoflow context --working "..."_',ot=t.decisions&&t.decisions.length>0?t.decisions.slice(-5).map(e=>"- "+e.text+" _("+O(e.date)+")_").join(`
13
+ `):"_No decisions recorded_",S=["# Project Context \u2014 "+f.policyId+" v"+I,"> Generated by infernoflow | "+z+" | "+T,"","---","","## What this system does","",Z,"","---","","## Recent changes","",tt,"","---","","## Current state","","- **Capabilities:** "+y.length,"- **Version:** v"+I,"- **Sync:** "+T,"","---","","## What I am working on right now","",et,"","---","","## Intent \u2014 what I want to build next","",nt,"","---","","## Decisions & notes","",ot,"","---","","## Implementation Prompt Seed","","Use this to start coding immediately with an agent:","","```bash",`infernoflow implement "${F}" --mode both`,"```","","### Cursor Agent Prompt","","```text",Q,"```","","### Generic Agent Prompt","","```text",Y,"```","","---","_Paste this block at the start of any new AI session._"].join(`
14
+ `);if(X||(l.writeFileSync(R,S,"utf8"),console.log(a(`
15
+ \u2714 Context written \u2192 `+R))),N){const e=at(S);console.log(e?a(" \u2714 Copied to clipboard \u2014 paste with Ctrl+V"):_(" \u26A0 Clipboard copy failed \u2014 open inferno/CONTEXT.md manually"))}if(B&&(l.writeFileSync(".cursorrules",S,"utf8"),console.log(a(" \u2714 Written to .cursorrules \u2014 Cursor loads this automatically"))),H&&(l.existsSync(".github")||l.mkdirSync(".github"),l.writeFileSync(".github/copilot-instructions.md",S,"utf8"),console.log(a(" \u2714 Written to .github/copilot-instructions.md \u2014 Copilot loads this automatically"))),console.log(`
16
+ `+m("Context Summary")),console.log(" "+"\u2500".repeat(50)),console.log(" Project "+f.policyId+" \u2014 v"+I),console.log(" Capabilities "+y.length+" registered"),console.log(" Sync "+(P?a("\u2713 in sync"):_("\u26A0 check needed"))),console.log(" Working on "+(t.working?r(t.working):d("not set"))),console.log(" Intent "+(t.intent?r(t.intent):d("not set"))),console.log(" Decisions "+(t.decisions?t.decisions.length:0)+` recorded
17
+ `),console.log(" "+m("Implementation Prompt")),console.log(" "+r("\u2192")+" Run "+r(`infernoflow implement "${F}" --mode both`)+`
18
+ `),N?(console.log(" "+m("Ready to use:")),console.log(" "+r("\u2192")+" Paste into Claude / Cursor / Copilot with "+r("Ctrl+V")+`
19
+ `)):(console.log(" "+m("Ready to use:")),console.log(" "+r("1.")+" Open "+r("inferno/CONTEXT.md")),console.log(" "+r("2.")+" Copy everything"),console.log(" "+r("3.")+" Paste at the start of your next AI session"),console.log(" "+d(" tip: use --copy to skip steps 1-2 automatically")+`
20
+ `)),M){console.log(" "+r("\u{1F441} Watch mode active")+d(" \u2014 polling git diff every "+v/1e3+"s")),console.log(" "+d(`Press Ctrl+C to stop
21
+ `));let e="";const p=async()=>{try{const st=process.cwd(),g=lt(st,{sinceCommits:1}),L=g.changedFiles.sort().join("|");if(L===e||(e=L,g.changedFiles.length===0))return;const j=g.affectedCapabilities.map(x=>x.id),E=j.length>0?`Working on: ${j.join(", ")} (${g.changedFiles.length} files changed)`:`${g.changedFiles.length} files changed \u2014 no capability match yet`,C=J();C.working!==E&&(C.working=E,C.workingUpdated=new Date().toISOString(),V(C),await gt(n.filter(x=>x!=="--watch")),process.stderr.write(`
22
+ `+a("\u2714")+" Context updated \u2014 "+j.length+` capabilities affected
23
+ `+d(g.changedFiles.slice(0,3).join(", ")+(g.changedFiles.length>3?` +${g.changedFiles.length-3} more`:""))+`
24
+ `))}catch{}};await p();const it=setInterval(p,v);process.on("SIGINT",()=>{clearInterval(it),process.stderr.write(`
25
+ `+d(`Watch stopped.
26
+
27
+ `)),process.exit(0)}),await new Promise(()=>{})}}export{gt as contextCommand};
@@ -0,0 +1,114 @@
1
+ import*as l from"node:fs";import*as s from"node:path";import{readProfile as k}from"../learning/profile.mjs";import{header as y,ok as w,warn as b,done as $,nextSteps as C,cyan as m,yellow as j}from"../ui/output.mjs";const p=s.join("inferno","generated-skills");function f(e,n,i){return l.existsSync(e)&&!i?(b(`Already exists (use --force to overwrite): ${s.relative(process.cwd(),e)}`),!1):(l.mkdirSync(s.dirname(e),{recursive:!0}),l.writeFileSync(e,n,"utf8"),w(`Generated: ${s.relative(process.cwd(),e)}`),!0)}function S(e,n){const i=e.namingStyle!=="unknown"?e.namingStyle:"PascalCase",r=e.preferredVerbs.length>0?e.preferredVerbs.slice(0,5).join(", "):"Add, Update, Remove",t=e.stack?.framework||"unknown",o=e.featureClusters.slice(0,3),a=e.sessionCount>=20?"experienced":e.sessionCount>=5?"intermediate":"new",c=o.map(u=>`- When touching [${u.slice(0,3).join(", ")}], check whether related capabilities also need updating`).join(`
2
+ `);return`# infernoflow \u2014 Cursor Rules (auto-generated)
3
+ # Project: ${n?.policyId||"unknown"} | Stack: ${t}
4
+ # Regenerate with: infernoflow generate-skills --cursor
5
+
6
+ ## Contract awareness
7
+ - This project uses infernoflow to track capability contracts
8
+ - After implementing any feature, always call \`infernoflow_run\` then \`infernoflow_apply\`
9
+ - Run \`infernoflow_check\` before every commit
10
+ - Current capabilities: [${(n?.capabilities||[]).join(", ")}]
11
+
12
+ ## Naming conventions (detected from this developer's history)
13
+ - Capability IDs use **${i}** (e.g. ${r.split(", ").slice(0,2).map(u=>u+"Item").join(", ")})
14
+ - Preferred action verbs: ${r}
15
+ - Match this style when suggesting new capability names
16
+
17
+ ## Feature clusters (capabilities this developer adds together)
18
+ ${c||"- No clusters detected yet \u2014 build more features to train this"}
19
+
20
+ ## Session style (${a})
21
+ ${a==="experienced"?`- Skip basic explanations \u2014 this developer knows the codebase well
22
+ - Be direct and minimal in responses`:`- Include brief context for non-obvious decisions
23
+ - Explain infernoflow commands when used`}
24
+
25
+ ## Workflow reminders
26
+ - Start sessions with: \`infernoflow context --show\`
27
+ - Use \`infernoflow_git_drift\` to check what's changed before starting work
28
+ - Use \`infernoflow_implement\` to get a structured coding prompt before writing code
29
+ - Changelog entries should be ${e.changelogVerbosity==="detailed"?"detailed (include context and impact)":"brief (one line, action-focused)"}
30
+ `}function R(e,n){const r=e.stack?.framework||"unknown",t=(n?.capabilities||[]).slice(0,6);return`# Quick Restore \u2014 ${n?.policyId||"this project"}
31
+ # Paste this at the start of any new AI session to restore context instantly.
32
+ # Regenerate with: infernoflow generate-skills
33
+
34
+ ## Project snapshot
35
+ - **Project:** ${n?.policyId||"unknown"}
36
+ - **Stack:** ${r} / ${e.stack?.language||"unknown"} (${e.stack?.projectType||"unknown"})
37
+ - **Capabilities:** ${t.join(", ")}${(n?.capabilities||[]).length>6?` +${(n?.capabilities||[]).length-6} more`:""}
38
+
39
+ ## How to start a session
40
+ 1. Run: \`infernoflow context --show\`
41
+ 2. Check \`inferno/CONTEXT.md\` for current intent
42
+ 3. Run: \`infernoflow_git_drift\` to see what changed since last session
43
+ 4. Pick up where you left off
44
+
45
+ ## infernoflow tools available (in Cursor / VS Code Agent mode)
46
+ - \`infernoflow_run\` \u2014 generate a contract update prompt
47
+ - \`infernoflow_apply\` \u2014 apply a JSON response
48
+ - \`infernoflow_implement\` \u2014 get a structured coding prompt
49
+ - \`infernoflow_git_drift\` \u2014 see what capabilities may have drifted
50
+ - \`infernoflow_check\` \u2014 validate contract is in sync
51
+ - \`infernoflow_status\` \u2014 quick health check
52
+
53
+ ## Definition of done (every feature branch)
54
+ - [ ] Code works as intended
55
+ - [ ] \`infernoflow_run\` \u2192 \`infernoflow_apply\` completed
56
+ - [ ] \`infernoflow_check\` passes
57
+ - [ ] Commit message references the capability changed
58
+ `}function _(e){const n=e.namingStyle!=="unknown"?e.namingStyle:"PascalCase",i=e.preferredVerbs.length>0?e.preferredVerbs:["Create","Read","Update","Delete","Search"],r=i.slice(0,5).map(t=>n==="PascalCase"?` - ${t}Item, ${t}Task, ${t}User`:n==="camelCase"?` - ${t.toLowerCase()}Item, ${t.toLowerCase()}Task`:` - ${t.toLowerCase()}-item, ${t.toLowerCase()}-task`).join(`
59
+ `);return`# Naming Guide \u2014 auto-generated from your capability history
60
+
61
+ ## Detected style: ${n}
62
+
63
+ ### Your preferred action verbs
64
+ ${i.map(t=>`- **${t}**`).join(`
65
+ `)}
66
+
67
+ ### Examples matching your style
68
+ ${r}
69
+
70
+ ### Rules
71
+ - All capability IDs in \`inferno/contract.json\` must follow this style
72
+ - New capabilities suggested by AI should match \u2014 reject any that don't
73
+ - If you rename a capability, update contract.json + capabilities.json + any scenarios
74
+
75
+ ### When naming a new capability, ask:
76
+ 1. Does it describe a single, discrete behavior? (If not, split it)
77
+ 2. Does it start with one of your preferred verbs?
78
+ 3. Is it in ${n}?
79
+ 4. Is it unique \u2014 not already in contract.json?
80
+ `}function I(e,n){const r=e.featureClusters.slice(0,2)[0]||[],t=e.stack?.framework||"unknown",o=r.slice(0,4).map(a=>`- [ ] Does **${a}** need updating? (check inferno/capabilities.json)`).join(`
81
+ `);return`# Feature Scaffold \u2014 ${t} project
82
+ # Use this checklist whenever starting a new feature.
83
+ # Regenerate with: infernoflow generate-skills
84
+
85
+ ## Before you start
86
+ - [ ] Run \`infernoflow context --show\` to load current state
87
+ - [ ] Run \`infernoflow_git_drift\` to see any pending drift
88
+ - [ ] Set intent: \`infernoflow context --intent "what I'm building"\`
89
+
90
+ ## Implementation checklist
91
+ - [ ] Create feature branch
92
+ - [ ] Implement the feature
93
+ - [ ] Write / update tests
94
+ - [ ] Verify it works end-to-end
95
+
96
+ ## Capability cluster check
97
+ ${o||"- [ ] Review existing capabilities \u2014 do any need updating?"}
98
+
99
+ ## Contract update (required before merge)
100
+ - [ ] Run \`infernoflow_run\` with a description of what changed
101
+ - [ ] Review the suggested JSON
102
+ - [ ] Run \`infernoflow_apply\` with the JSON
103
+ - [ ] Run \`infernoflow_check\` \u2014 must pass
104
+
105
+ ## Commit message
106
+ - Reference the capability: "feat: add SearchItems endpoint (#42)"
107
+ - Update CHANGELOG.md if not auto-updated
108
+
109
+ ## Done when
110
+ - [ ] Feature works
111
+ - [ ] \`infernoflow_check\` passes
112
+ - [ ] PR description mentions which capabilities changed
113
+ `}async function A(e){const n=process.cwd(),i=e.includes("--force")||e.includes("-f"),r=e.includes("--cursor");y("generate-skills");const t=s.join(n,"inferno");l.existsSync(t)||(console.error(` \u2718 inferno/ not found \u2014 run: infernoflow init
114
+ `),process.exit(1));const o=k(t);let a=null;try{a=JSON.parse(l.readFileSync(s.join(t,"contract.json"),"utf8"))}catch{}const c=s.join(n,p);if(f(s.join(c,"cursor-rules.md"),S(o,a),i),f(s.join(c,"quick-restore.md"),R(o,a),i),f(s.join(c,"naming-guide.md"),_(o),i),f(s.join(c,"feature-scaffold.md"),I(o,a),i),r){const d=s.join(n,".cursor","rules");l.mkdirSync(d,{recursive:!0});const h=s.join(c,"cursor-rules.md"),g=s.join(d,"infernoflow.md");l.copyFileSync(h,g),w("Installed to: .cursor/rules/infernoflow.md")}const u=[o.namingStyle!=="unknown"?`naming: ${o.namingStyle}`:null,o.stack?.framework!=="unknown"?`stack: ${o.stack.framework}`:null,o.sessionCount>0?`sessions: ${o.sessionCount}`:null].filter(Boolean).join(" \xB7 ");$(`Skills generated${u?` (${u})`:""}`),C([`Review files in ${j(p+"/")}`,`Copy to Cursor: ${m("infernoflow generate-skills --cursor")}`,`Re-run any time to refresh after more sessions: ${m("infernoflow generate-skills --force")}`,o.sessionCount<5?`Run more commands to improve personalisation (${o.sessionCount} sessions so far)`:`Profile has ${o.sessionCount} sessions \u2014 personalisation is well-trained`])}export{A as generateSkillsCommand};
@@ -0,0 +1,4 @@
1
+ import*as g from"node:fs";import*as s from"node:path";import{fileURLToPath as y}from"node:url";import{execSync as $}from"node:child_process";import{detectIdeContext as k}from"../ai/ideDetection.mjs";import{header as v,ok as i,warn as S,done as R,cyan as l,yellow as r,bold as x}from"../ui/output.mjs";import{installCursorHooksArtifacts as C}from"../cursorHooksInstall.mjs";import{installVsCodeCopilotHooksArtifacts as P}from"../vsCodeCopilotHooksInstall.mjs";const j=s.dirname(y(import.meta.url));function b(){return s.resolve(j,"../../templates")}function A(n){try{return $(`npx infernoflow ${n}`,{encoding:"utf8",cwd:process.cwd(),timeout:6e4,stdio:["inherit","pipe","pipe"]})}catch(o){return o.stdout||o.stderr||o.message}}async function T(n){const o=process.cwd(),d=n.includes("--force")||n.includes("-f"),w=n.includes("--yes")||n.includes("-y"),c=b();v("infernoflow setup");const{ideDetected:e}=k("auto"),a=e==="cursor"?"Cursor":e==="vscode"?"VS Code + Copilot":e==="windsurf"?"Windsurf":"unknown";console.log(` IDE detected: ${x(a)}`);const h=s.join(o,"inferno"),u=s.join(h,"contract.json");if(g.existsSync(u))i("inferno/contract.json already exists \u2014 skipping init");else{console.log(`
2
+ ${r("inferno/")} not found \u2014 running init --adopt ...
3
+ `);const t=["--adopt",w?"--yes":""].filter(Boolean).join(" ");A(`init ${t}`)}const p=t=>i(t),m=t=>S(t);(e==="cursor"||e==="unknown")&&(C({cwd:o,templatesRoot:c,force:d,silent:!1,logOk:p,logWarn:m}),i("Cursor hooks + MCP server installed"),console.log(` \u2192 Restart Cursor, then go to Settings \u2192 MCP and verify ${r("infernoflow")} shows 4 tools`)),e==="vscode"&&(P({cwd:o,templatesRoot:c,force:d,silent:!1,logOk:p,logWarn:m}),i("VS Code Copilot hooks installed"),console.log(` \u2192 Restart VS Code, then open GitHub Copilot Chat in ${r("Agent")} mode`),C({cwd:o,templatesRoot:c,force:!1,silent:!0,logOk:()=>{},logWarn:()=>{}}));let f=0;try{f=(JSON.parse(g.readFileSync(u,"utf8")).capabilities||[]).length}catch{}console.log(),R(f>0?`infernoflow ready \u2014 ${f} capabilities tracked, MCP server installed for ${a}`:`infernoflow ready \u2014 MCP server installed for ${a}`),console.log(`
4
+ ${l("Next steps:")}`),e==="cursor"?(console.log(" 1. Restart Cursor"),console.log(` 2. In Cursor chat, try: ${l('Use infernoflow_run with task "add a new feature"')}`)):e==="vscode"?(console.log(" 1. Restart VS Code"),console.log(` 2. Switch Copilot Chat to ${r("Agent")} mode`),console.log(` 3. Try: ${l('Use infernoflow_run with task "add a new feature"')}`)):(console.log(` 1. Open your IDE and install the MCP server from ${r("inferno-mcp-server.mjs")}`),console.log(` 2. Run: ${l("infernoflow status")} to verify everything is working`)),console.log()}export{T as setupCommand};
@@ -1,6 +1,6 @@
1
- import*as c from"node:fs";import*as l from"node:path";import*as J from"node:readline";import{header as V,ok as E,warn as F,info as U,done as R,section as D,nextSteps as W,bold as H,cyan as C,gray as A,yellow as Y,green as L,red as G,errorAndExit as I}from"../ui/output.mjs";function v(n){try{return JSON.parse(c.readFileSync(n,"utf8"))}catch{return null}}function T(n,e){return new Promise(i=>{n.question(e,d=>i(d.trim()))})}function q(n){return n.replace(/[-_]+/g," ").split(" ").map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join("")}function z({description:n,contract:e,capabilities:i,scenarios:d}){const g=e.capabilities||[],f=(i?.capabilities||[]).map(u=>` - ${u.id}: ${u.title||u.id}`).join(`
2
- `),s=d.map(u=>{const w=(u.capabilitiesCovered||[]).join(", "),y=(u.steps||[]).map(h=>` {action: "${h.action}", expect: "${h.expect}"}`).join(`
3
- `);return` File: ${u._file}
1
+ import*as c from"node:fs";import*as l from"node:path";import*as J from"node:readline";import{header as U,ok as N,warn as G,info as R,done as W,section as D,nextSteps as H,bold as Y,cyan as C,gray as A,yellow as z,green as L,red as T,errorAndExit as E}from"../ui/output.mjs";import{personalisePrompt as M}from"../learning/adapt.mjs";function $(o){try{return JSON.parse(c.readFileSync(o,"utf8"))}catch{return null}}function V(o,e){return new Promise(i=>{o.question(e,p=>i(p.trim()))})}function ie(o){return o.replace(/[-_]+/g," ").split(" ").map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join("")}function Z({description:o,contract:e,capabilities:i,scenarios:p}){const g=e.capabilities||[],d=(i?.capabilities||[]).map(f=>` - ${f.id}: ${f.title||f.id}`).join(`
2
+ `),s=p.map(f=>{const w=(f.capabilitiesCovered||[]).join(", "),y=(f.steps||[]).map(h=>` {action: "${h.action}", expect: "${h.expect}"}`).join(`
3
+ `);return` File: ${f._file}
4
4
  capabilitiesCovered: [${w}]
5
5
  steps:
6
6
  ${y}`}).join(`
@@ -16,13 +16,13 @@ policyVersion: ${e.policyVersion}
16
16
  capabilities: [${g.join(", ")}]
17
17
 
18
18
  ## Current capabilities registry
19
- ${f||" (none)"}
19
+ ${d||" (none)"}
20
20
 
21
21
  ## Current scenarios
22
22
  ${s||" (none)"}
23
23
 
24
24
  ## Developer's description of what changed
25
- "${n}"
25
+ "${o}"
26
26
 
27
27
  ## Your task
28
28
 
@@ -52,11 +52,11 @@ Rules:
52
52
  - Capability IDs must be PascalCase (e.g. SendEmail, not send_email)
53
53
  - If nothing changed capability-wise, return empty arrays
54
54
  - changelogEntry should start with "- "
55
- - Keep it minimal and accurate`}function M(n){const e=[];if(!n||typeof n!="object")return["AI response must be a JSON object."];n.summary!=null&&typeof n.summary!="string"&&e.push('"summary" must be a string.'),Array.isArray(n.newCapabilities)||e.push('"newCapabilities" must be an array.'),Array.isArray(n.removedCapabilities)||e.push('"removedCapabilities" must be an array.'),Array.isArray(n.updatedScenarios)||e.push('"updatedScenarios" must be an array.'),n.changelogEntry!=null&&typeof n.changelogEntry!="string"&&e.push('"changelogEntry" must be a string.');for(const i of n.newCapabilities||[]){if(!i||typeof i!="object"){e.push('Each item in "newCapabilities" must be an object.');continue}(typeof i.id!="string"||!/^[A-Z][A-Za-z0-9]*$/.test(i.id))&&e.push("newCapabilities[].id must be PascalCase (example: SendEmail)."),(typeof i.title!="string"||!i.title.trim())&&e.push("newCapabilities[].title must be a non-empty string.")}for(const i of n.removedCapabilities||[])(typeof i!="string"||!i.trim())&&e.push("removedCapabilities[] must contain non-empty strings.");for(const i of n.updatedScenarios||[]){if(!i||typeof i!="object"){e.push('Each item in "updatedScenarios" must be an object.');continue}(typeof i.file!="string"||!i.file.endsWith(".json"))&&e.push("updatedScenarios[].file must be a .json filename."),typeof i.isNew!="boolean"&&e.push("updatedScenarios[].isNew must be boolean."),(!Array.isArray(i.capabilitiesCovered)||!Array.isArray(i.stepsToAdd))&&e.push("updatedScenarios[].capabilitiesCovered and stepsToAdd must be arrays.")}return e}function Z(n,e){const i=[],d=new Set(n.capabilities||[]),g=new Set((e.newCapabilities||[]).map(s=>s.id)),f=new Set(e.removedCapabilities||[]);for(const s of g)f.has(s)&&i.push(`Capability "${s}" appears in both newCapabilities and removedCapabilities.`),d.has(s)&&i.push(`Capability "${s}" already exists in contract capabilities.`);for(const s of f)d.has(s)||i.push(`Capability "${s}" cannot be removed because it does not exist in contract.`);return i}function K({cwd:n,contract:e,capabilities:i,suggestion:d,version:g,quiet:f=!1}){const s=l.join(n,"inferno"),u=l.join(s,"contract.json"),w=l.join(s,"capabilities.json"),y=l.join(s,"CHANGELOG.md"),h=l.join(s,"scenarios"),m=d.newCapabilities||[],S=d.removedCapabilities||[],k=d.updatedScenarios||[],P=d.changelogEntry||"";let $=!1;const N=[],p=(t,o)=>N.push({filePath:t,content:o});if(m.length>0||S.length>0){const t=[...e.capabilities.filter(b=>!S.includes(b)),...m.map(b=>b.id)],o=Number(e.policyVersion||1)+1,r={...e,capabilities:t,policyVersion:o};p(u,JSON.stringify(r,null,2)+`
56
- `),f||E(`contract.json updated \u2192 policyVersion: v${o}`),$=!0}if(m.length>0||S.length>0){const t=i?{...i}:{schemaVersion:1,capabilities:[]};t.capabilities=(t.capabilities||[]).filter(o=>!S.includes(o.id));for(const o of m)t.capabilities.find(r=>r.id===o.id)||t.capabilities.push({id:o.id,title:o.title,since:g});p(w,JSON.stringify(t,null,2)+`
57
- `),f||E("capabilities.json updated")}for(const t of k){const o=l.join(h,t.file);let r;if(t.isNew||!c.existsSync(o))r={scenarioId:t.file.replace(".json",""),description:d.summary||"",capabilitiesCovered:t.capabilitiesCovered||[],steps:t.stepsToAdd||[]},p(o,JSON.stringify(r,null,2)+`
58
- `),f||E(`Created scenario: ${C(t.file)}`);else{r=v(o);const b=new Set(r.capabilitiesCovered||[]);(t.capabilitiesCovered||[]).forEach(O=>b.add(O)),r.capabilitiesCovered=[...b],r.steps=[...r.steps||[],...t.stepsToAdd||[]],p(o,JSON.stringify(r,null,2)+`
59
- `),f||E(`Updated scenario: ${C(t.file)}`)}$=!0}if(P&&c.existsSync(y)){let t=c.readFileSync(y,"utf8");/##\s+Unreleased/i.test(t)&&(t=t.replace(/(##\s+Unreleased[^\n]*\n)/i,`$1
60
- ${P}
61
- `),p(y,t),f||E("CHANGELOG.md updated"),$=!0)}const x=new Map;try{for(const t of N){c.existsSync(t.filePath)?x.set(t.filePath,c.readFileSync(t.filePath,"utf8")):x.set(t.filePath,null);const o=`${t.filePath}.tmp`;c.writeFileSync(o,t.content),c.renameSync(o,t.filePath)}}catch(t){for(const[o,r]of x.entries())r===null?c.existsSync(o)&&c.unlinkSync(o):c.writeFileSync(o,r);throw new Error(`Failed applying changes. Rolled back. Details: ${t.message}`)}return $}function B(n){const e=String(n||"").trim().replace(/^```json?\n?/,"").replace(/\n?```$/,"");return JSON.parse(e)}function ee(n){const e=l.join(n,"inferno"),i=l.join(e,"contract.json"),d=l.join(e,"capabilities.json"),g=l.join(e,"scenarios"),f=v(i),s=v(d),u=[];if(c.existsSync(g))for(const h of c.readdirSync(g).filter(m=>m.endsWith(".json"))){const m=v(l.join(g,h));m&&u.push({...m,_file:h})}let w="0.1.0";const y=l.join(n,"package.json");if(c.existsSync(y)){const h=v(y);h?.version&&(w=h.version)}return{contract:f,capabilities:s,scenarios:u,version:w}}async function te(n){const e=process.cwd(),i=l.join(e,"inferno");V("suggest"),c.existsSync(i)||I("inferno/ not found","Run: infernoflow init");const d=l.join(i,"contract.json"),g=l.join(i,"capabilities.json"),f=l.join(i,"scenarios"),s=v(d);s||I("contract.json not found or invalid");const u=v(g),w=[];if(c.existsSync(f))for(const a of c.readdirSync(f).filter(j=>j.endsWith(".json"))){const j=v(l.join(f,a));j&&w.push({...j,_file:a})}let y="0.1.0";const h=l.join(e,"package.json");if(c.existsSync(h)){const a=v(h);a?.version&&(y=a.version)}let S=n.filter(a=>!a.startsWith("-")).slice(1).join(" ");if(!S){const a=J.createInterface({input:process.stdin,output:process.stdout});console.log(A(" Describe what changed in your code (e.g. 'added email notifications'):")),S=await T(a,` ${C(">")} `),a.close(),console.log()}S||I("No description provided",'Usage: infernoflow suggest "what changed"');const k=z({description:S,contract:s,capabilities:u,scenarios:w});D("Generated Prompt"),console.log(),console.log(A("\u2500".repeat(50))),console.log(k),console.log(A("\u2500".repeat(50))),console.log(),U("Copy the prompt above and paste it into:"),console.log(` ${C("\u2022")} Claude \u2192 https://claude.ai`),console.log(` ${C("\u2022")} ChatGPT \u2192 https://chatgpt.com`),console.log(` ${C("\u2022")} Copilot, Cursor, or any AI you use`),console.log(),F("The AI will respond with a JSON object."),console.log();const P=J.createInterface({input:process.stdin,output:process.stdout});console.log(A(" Paste the AI's JSON response below, then press Enter twice:")),console.log();let $="",N=0;await new Promise(a=>{P.on("line",j=>{j.trim()===""?(N++,N>=2&&$.trim()&&a()):(N=0,$+=j+`
62
- `)}),P.on("close",a)}),P.close();let p;try{p=B($)}catch{I("Could not parse the AI response as JSON","Make sure you copied the full JSON response from the AI")}const x=M(p);x.length>0&&I("AI response schema is invalid",x[0]+(x.length>1?` (+${x.length-1} more)`:""));const t=Z(s,p);t.length>0&&I("AI response contains conflicting capability operations",t[0]+(t.length>1?` (+${t.length-1} more)`:"")),D("Proposed Changes"),console.log(),p.summary&&(console.log(` ${H("Summary:")} ${p.summary}`),console.log());const o=p.newCapabilities||[],r=p.removedCapabilities||[],b=p.updatedScenarios||[];o.length===0&&r.length===0&&b.length===0&&(E("No capability changes detected \u2014 nothing to apply."),console.log(),process.exit(0)),o.length>0&&(console.log(` ${L("+")} New capabilities:`),o.forEach(a=>console.log(` ${L(a.id)} \u2014 ${A(a.title)}`)),console.log()),r.length>0&&(console.log(` ${G("-")} Removed capabilities:`),r.forEach(a=>console.log(` ${G(a)}`)),console.log()),b.length>0&&(console.log(` ${C("~")} Scenario updates:`),b.forEach(a=>{const j=a.isNew?L("[new]"):C("[update]");console.log(` ${j} ${a.file}`)}),console.log()),p.changelogEntry&&(console.log(` ${Y("\u{1F4DD}")} Changelog: ${A(p.changelogEntry)}`),console.log());const O=J.createInterface({input:process.stdin,output:process.stdout}),_=await T(O,` Apply these changes? ${A("(y/n)")} `);O.close(),console.log(),_.toLowerCase()!=="y"&&_.toLowerCase()!=="yes"&&(F("Cancelled \u2014 no changes made."),console.log(),process.exit(0)),D("Applying Changes"),console.log(),K({cwd:e,contract:s,capabilities:u,suggestion:p,version:y}),R("suggest complete!"),W([C("infernoflow status")+" \u2014 verify the updated contract",C("infernoflow check")+" \u2014 validate everything"])}export{K as applyChanges,z as buildPrompt,Z as detectSuggestionConflicts,ee as loadSuggestContext,B as parseSuggestionJson,v as readJson,te as suggestCommand,M as validateSuggestion};
55
+ - Keep it minimal and accurate`}function K(o){const e=[];if(!o||typeof o!="object")return["AI response must be a JSON object."];o.summary!=null&&typeof o.summary!="string"&&e.push('"summary" must be a string.'),Array.isArray(o.newCapabilities)||e.push('"newCapabilities" must be an array.'),Array.isArray(o.removedCapabilities)||e.push('"removedCapabilities" must be an array.'),Array.isArray(o.updatedScenarios)||e.push('"updatedScenarios" must be an array.'),o.changelogEntry!=null&&typeof o.changelogEntry!="string"&&e.push('"changelogEntry" must be a string.');for(const i of o.newCapabilities||[]){if(!i||typeof i!="object"){e.push('Each item in "newCapabilities" must be an object.');continue}(typeof i.id!="string"||!/^[A-Z][A-Za-z0-9]*$/.test(i.id))&&e.push("newCapabilities[].id must be PascalCase (example: SendEmail)."),(typeof i.title!="string"||!i.title.trim())&&e.push("newCapabilities[].title must be a non-empty string.")}for(const i of o.removedCapabilities||[])(typeof i!="string"||!i.trim())&&e.push("removedCapabilities[] must contain non-empty strings.");for(const i of o.updatedScenarios||[]){if(!i||typeof i!="object"){e.push('Each item in "updatedScenarios" must be an object.');continue}(typeof i.file!="string"||!i.file.endsWith(".json"))&&e.push("updatedScenarios[].file must be a .json filename."),typeof i.isNew!="boolean"&&e.push("updatedScenarios[].isNew must be boolean."),(!Array.isArray(i.capabilitiesCovered)||!Array.isArray(i.stepsToAdd))&&e.push("updatedScenarios[].capabilitiesCovered and stepsToAdd must be arrays.")}return e}function B(o,e){const i=[],p=new Set(o.capabilities||[]),g=new Set((e.newCapabilities||[]).map(s=>s.id)),d=new Set(e.removedCapabilities||[]);for(const s of g)d.has(s)&&i.push(`Capability "${s}" appears in both newCapabilities and removedCapabilities.`),p.has(s)&&i.push(`Capability "${s}" already exists in contract capabilities.`);for(const s of d)p.has(s)||i.push(`Capability "${s}" cannot be removed because it does not exist in contract.`);return i}function Q({cwd:o,contract:e,capabilities:i,suggestion:p,version:g,quiet:d=!1}){const s=l.join(o,"inferno"),f=l.join(s,"contract.json"),w=l.join(s,"capabilities.json"),y=l.join(s,"CHANGELOG.md"),h=l.join(s,"scenarios"),m=p.newCapabilities||[],S=p.removedCapabilities||[],k=p.updatedScenarios||[],O=p.changelogEntry||"";let x=!1;const P=[],j=(t,n)=>P.push({filePath:t,content:n});if(m.length>0||S.length>0){const t=[...e.capabilities.filter(b=>!S.includes(b)),...m.map(b=>b.id)],n=Number(e.policyVersion||1)+1,r={...e,capabilities:t,policyVersion:n};j(f,JSON.stringify(r,null,2)+`
56
+ `),d||N(`contract.json updated \u2192 policyVersion: v${n}`),x=!0}if(m.length>0||S.length>0){const t=i?{...i}:{schemaVersion:1,capabilities:[]};t.capabilities=(t.capabilities||[]).filter(n=>!S.includes(n.id));for(const n of m)t.capabilities.find(r=>r.id===n.id)||t.capabilities.push({id:n.id,title:n.title,since:g});j(w,JSON.stringify(t,null,2)+`
57
+ `),d||N("capabilities.json updated")}for(const t of k){const n=l.join(h,t.file);let r;if(t.isNew||!c.existsSync(n))r={scenarioId:t.file.replace(".json",""),description:p.summary||"",capabilitiesCovered:t.capabilitiesCovered||[],steps:t.stepsToAdd||[]},j(n,JSON.stringify(r,null,2)+`
58
+ `),d||N(`Created scenario: ${C(t.file)}`);else{r=$(n);const b=new Set(r.capabilitiesCovered||[]);(t.capabilitiesCovered||[]).forEach(I=>b.add(I)),r.capabilitiesCovered=[...b],r.steps=[...r.steps||[],...t.stepsToAdd||[]],j(n,JSON.stringify(r,null,2)+`
59
+ `),d||N(`Updated scenario: ${C(t.file)}`)}x=!0}if(O&&c.existsSync(y)){let t=c.readFileSync(y,"utf8");/##\s+Unreleased/i.test(t)&&(t=t.replace(/(##\s+Unreleased[^\n]*\n)/i,`$1
60
+ ${O}
61
+ `),j(y,t),d||N("CHANGELOG.md updated"),x=!0)}const u=new Map;try{for(const t of P){c.existsSync(t.filePath)?u.set(t.filePath,c.readFileSync(t.filePath,"utf8")):u.set(t.filePath,null);const n=`${t.filePath}.tmp`;c.writeFileSync(n,t.content),c.renameSync(n,t.filePath)}}catch(t){for(const[n,r]of u.entries())r===null?c.existsSync(n)&&c.unlinkSync(n):c.writeFileSync(n,r);throw new Error(`Failed applying changes. Rolled back. Details: ${t.message}`)}return x}function X(o){const e=String(o||"").trim().replace(/^```json?\n?/,"").replace(/\n?```$/,"");return JSON.parse(e)}function oe(o){const e=l.join(o,"inferno"),i=l.join(e,"contract.json"),p=l.join(e,"capabilities.json"),g=l.join(e,"scenarios"),d=$(i),s=$(p),f=[];if(c.existsSync(g))for(const h of c.readdirSync(g).filter(m=>m.endsWith(".json"))){const m=$(l.join(g,h));m&&f.push({...m,_file:h})}let w="0.1.0";const y=l.join(o,"package.json");if(c.existsSync(y)){const h=$(y);h?.version&&(w=h.version)}return{contract:d,capabilities:s,scenarios:f,version:w}}async function ne(o){const e=process.cwd(),i=l.join(e,"inferno");U("suggest"),c.existsSync(i)||E("inferno/ not found","Run: infernoflow init");const p=l.join(i,"contract.json"),g=l.join(i,"capabilities.json"),d=l.join(i,"scenarios"),s=$(p);s||E("contract.json not found or invalid");const f=$(g),w=[];if(c.existsSync(d))for(const a of c.readdirSync(d).filter(v=>v.endsWith(".json"))){const v=$(l.join(d,a));v&&w.push({...v,_file:a})}let y="0.1.0";const h=l.join(e,"package.json");if(c.existsSync(h)){const a=$(h);a?.version&&(y=a.version)}let S=o.filter(a=>!a.startsWith("-")).slice(1).join(" ");if(!S){const a=J.createInterface({input:process.stdin,output:process.stdout});console.log(A(" Describe what changed in your code (e.g. 'added email notifications'):")),S=await V(a,` ${C(">")} `),a.close(),console.log()}S||E("No description provided",'Usage: infernoflow suggest "what changed"');const k=Z({description:S,contract:s,capabilities:f,scenarios:w}),O=M(k,i);D("Generated Prompt"),console.log(),console.log(A("\u2500".repeat(50))),console.log(O),console.log(A("\u2500".repeat(50))),console.log(),R("Copy the prompt above and paste it into:"),console.log(` ${C("\u2022")} Claude \u2192 https://claude.ai`),console.log(` ${C("\u2022")} ChatGPT \u2192 https://chatgpt.com`),console.log(` ${C("\u2022")} Copilot, Cursor, or any AI you use`),console.log(),G("The AI will respond with a JSON object."),console.log();const x=J.createInterface({input:process.stdin,output:process.stdout});console.log(A(" Paste the AI's JSON response below, then press Enter twice:")),console.log();let P="",j=0;await new Promise(a=>{x.on("line",v=>{v.trim()===""?(j++,j>=2&&P.trim()&&a()):(j=0,P+=v+`
62
+ `)}),x.on("close",a)}),x.close();let u;try{u=X(P)}catch{E("Could not parse the AI response as JSON","Make sure you copied the full JSON response from the AI")}const t=K(u);t.length>0&&E("AI response schema is invalid",t[0]+(t.length>1?` (+${t.length-1} more)`:""));const n=B(s,u);n.length>0&&E("AI response contains conflicting capability operations",n[0]+(n.length>1?` (+${n.length-1} more)`:"")),D("Proposed Changes"),console.log(),u.summary&&(console.log(` ${Y("Summary:")} ${u.summary}`),console.log());const r=u.newCapabilities||[],b=u.removedCapabilities||[],I=u.updatedScenarios||[];r.length===0&&b.length===0&&I.length===0&&(N("No capability changes detected \u2014 nothing to apply."),console.log(),process.exit(0)),r.length>0&&(console.log(` ${L("+")} New capabilities:`),r.forEach(a=>console.log(` ${L(a.id)} \u2014 ${A(a.title)}`)),console.log()),b.length>0&&(console.log(` ${T("-")} Removed capabilities:`),b.forEach(a=>console.log(` ${T(a)}`)),console.log()),I.length>0&&(console.log(` ${C("~")} Scenario updates:`),I.forEach(a=>{const v=a.isNew?L("[new]"):C("[update]");console.log(` ${v} ${a.file}`)}),console.log()),u.changelogEntry&&(console.log(` ${z("\u{1F4DD}")} Changelog: ${A(u.changelogEntry)}`),console.log());const _=J.createInterface({input:process.stdin,output:process.stdout}),F=await V(_,` Apply these changes? ${A("(y/n)")} `);_.close(),console.log(),F.toLowerCase()!=="y"&&F.toLowerCase()!=="yes"&&(G("Cancelled \u2014 no changes made."),console.log(),process.exit(0)),D("Applying Changes"),console.log(),Q({cwd:e,contract:s,capabilities:f,suggestion:u,version:y}),W("suggest complete!"),H([C("infernoflow status")+" \u2014 verify the updated contract",C("infernoflow check")+" \u2014 validate everything"])}export{Q as applyChanges,Z as buildPrompt,B as detectSuggestionConflicts,oe as loadSuggestContext,X as parseSuggestionJson,$ as readJson,ne as suggestCommand,K as validateSuggestion};
@@ -0,0 +1,4 @@
1
+ import*as p from"node:fs";import*as m from"node:path";import{execSync as F}from"node:child_process";function k(i,o={}){const{sinceCommits:d=1,includeStagedOnly:l=!1}=o,r=new Set;try{const f=F("git diff --name-only HEAD",{cwd:i,encoding:"utf8",timeout:1e4});for(const c of f.split(`
2
+ `).map(n=>n.trim()).filter(Boolean))r.add(c)}catch{}try{const f=F(`git diff --name-only HEAD~${d} HEAD`,{cwd:i,encoding:"utf8",timeout:1e4});for(const c of f.split(`
3
+ `).map(n=>n.trim()).filter(Boolean))r.add(c)}catch{}try{const f=F("git ls-files --others --exclude-standard",{cwd:i,encoding:"utf8",timeout:1e4});for(const c of f.split(`
4
+ `).map(n=>n.trim()).filter(Boolean))r.add(c)}catch{}return Array.from(r).sort()}function C(i){const o=m.join(i,"capability-map.json");if(!p.existsSync(o))return null;try{return JSON.parse(p.readFileSync(o,"utf8"))}catch{return null}}function x(i){const o=m.join(i,"adoption_profile.json");if(!p.existsSync(o))return null;try{return JSON.parse(p.readFileSync(o,"utf8"))}catch{return null}}function j(i){const o=m.join(i,"capabilities.json");if(!p.existsSync(o))return[];try{return JSON.parse(p.readFileSync(o,"utf8")).capabilities||[]}catch{return[]}}function A(i,o={}){const d=m.join(i,"inferno"),l=k(i,o);if(!l.length)return{changedFiles:[],affectedCapabilities:[],unmappedFiles:[],hasCapabilityMap:!1};const r=C(d),f=x(d),c=j(d),n=new Map,y=(e,s)=>{n.has(e.id)||n.set(e.id,{id:e.id,title:e.title||e.id,matchedFiles:new Set}),n.get(e.id).matchedFiles.add(s)},h=new Set;if(r){for(const e of l)for(const[s,t]of Object.entries(r))if(e.startsWith(s.replace(/\\/g,"/")))for(const a of t){const u=c.find(b=>b.id===a)||{id:a,title:a};y(u,e),h.add(e)}}const g=[];if(f){const e=m.join(d,"capabilities.json");try{const s=JSON.parse(p.readFileSync(e,"utf8"));for(const t of s.capabilities||[])t.sourceFiles&&t.sourceFiles.length>0&&g.push(t)}catch{}}if(g.length>0)for(const e of g)for(const s of e.sourceFiles||[]){const t=s.replace(/\\/g,"/");for(const a of l){const u=a.replace(/\\/g,"/");(u===t||u.startsWith(m.dirname(t)+"/"))&&(y(e,a),h.add(a))}}const I=[{keywords:["search"],capId:"SearchItems"},{keywords:["filter"],capId:"FilterItems"},{keywords:["auth","login","logout","signin","signup"],capId:"Authentication"},{keywords:["create","add","new"],capId:"CreateItem"},{keywords:["update","edit"],capId:"UpdateItem"},{keywords:["delete","remove"],capId:"DeleteItem"},{keywords:["read","list","view"],capId:"ReadItems"},{keywords:["due","deadline","date"],capId:"SetDueDate"},{keywords:["priority"],capId:"SetPriority"},{keywords:["complete","done","toggle"],capId:"ToggleComplete"}];for(const e of l){if(h.has(e))continue;const s=e.toLowerCase();for(const t of I)if(t.keywords.some(a=>s.includes(a))){const a=c.find(u=>u.id===t.capId)||{id:t.capId,title:t.capId};y(a,e),h.add(e);break}}const S=l.filter(e=>!h.has(e)),w=Array.from(n.values()).map(e=>({id:e.id,title:e.title,matchedFiles:Array.from(e.matchedFiles),confidence:e.matchedFiles.size>=3?"high":e.matchedFiles.size>=1?"medium":"low"}));return{changedFiles:l,affectedCapabilities:w,unmappedFiles:S,hasCapabilityMap:!!r}}export{A as detectDrift,k as getChangedFiles,x as loadAdoptionProfile,j as loadCapabilities,C as loadCapabilityMap};
@@ -0,0 +1,9 @@
1
+ import{readProfile as r,summarizeProfile as i}from"./profile.mjs";function o(t){let e;try{e=r(t)}catch{return""}const n=[];if(!(e.namingStyle!=="unknown"||e.preferredVerbs.length>0||e.stack.framework!=="unknown"))return"";if(n.push("## Developer profile (personalise your response to match these preferences)"),e.namingStyle!=="unknown"&&n.push(`- Capability naming style: **${e.namingStyle}** \u2014 use this for all new capability IDs`),e.preferredVerbs.length>0&&n.push(`- Preferred action verbs: ${e.preferredVerbs.slice(0,5).join(", ")} \u2014 prefer these when naming new capabilities`),e.stack.framework!=="unknown"&&n.push(`- Stack: ${e.stack.framework} / ${e.stack.language} (${e.stack.projectType})`),e.changelogVerbosity!=="unknown"){const s=e.changelogVerbosity==="brief"?"Keep changelog entries short (one line, action-focused)":"Write detailed changelog entries (include context and impact)";n.push(`- Changelog style: ${s}`)}if(e.featureClusters.length>0){const s=e.featureClusters[0];s.length>=2&&n.push(`- Common capability cluster: [${s.slice(0,4).join(", ")}] \u2014 if the task touches one of these, consider whether others need updating too`)}return e.sessionCount>=10&&n.push(`- Experienced user (${e.sessionCount} sessions) \u2014 skip basic explanations, be direct`),n.join(`
2
+ `)}function c(t,e){const n=o(e);return n?t.includes("## Instructions")?t.replace("## Instructions",n+`
3
+
4
+ ## Instructions`):t.includes("Respond with ONLY")?t.replace("Respond with ONLY",n+`
5
+
6
+ ---
7
+ Respond with ONLY`):t+`
8
+
9
+ `+n:t}function u(t){try{const e=r(t);return i(e)||null}catch{return null}}export{o as buildPersonalisationBlock,c as personalisePrompt,u as profileStatusLine};
@@ -0,0 +1 @@
1
+ import"node:path";import{readProfile as s,writeProfile as c,recordCommandUse as l,detectNamingStyle as f,detectPreferredVerbs as a,recordCapabilityCluster as d}from"./profile.mjs";const m=1800*1e3;function h(o,t){try{const e=s(o);l(e,t);const r=Date.now(),n=e._lastCommandTs||0;r-n>m&&(e.sessionCount=(e.sessionCount||0)+1),e._lastCommandTs=r,c(o,e)}catch{}}function S(o,t){if(!(!t||t.length===0))try{const e=s(o),r=f(t);r!=="unknown"&&(e.sessionCount>=3||e.namingStyle==="unknown")&&(e.namingStyle=r);const n=a(t);if(n.length>0){const i=[...new Set([...e.preferredVerbs,...n])];e.preferredVerbs=i.slice(0,8)}t.length>=2&&d(e,t),c(o,e)}catch{}}function b(o,t){if(t)try{const e=s(o),n=String(t).trim().split(/\s+/).length>=15?"detailed":"brief";(e.changelogVerbosity==="unknown"||e.sessionCount>=5)&&(e.changelogVerbosity=n),c(o,e)}catch{}}export{S as observeCapabilitiesAdded,b as observeChangelogEntry,h as observeCommandStart};
@@ -0,0 +1,2 @@
1
+ import*as a from"node:fs";import*as f from"node:path";const i=1;function m(e){return f.join(e,"developer-profile.json")}function c(){const e=new Date().toISOString();return{schemaVersion:i,createdAt:e,updatedAt:e,sessionCount:0,namingStyle:"unknown",preferredVerbs:[],commandUsage:{},featureClusters:[],avgSessionLength:0,commitFrequency:"unknown",changelogVerbosity:"unknown",stack:{language:"unknown",framework:"unknown",projectType:"unknown"}}}function l(e){const n=m(e);if(!a.existsSync(n))return c();try{const o=JSON.parse(a.readFileSync(n,"utf8"));return{...c(),...o}}catch{return c()}}function d(e,n){n.updatedAt=new Date().toISOString(),n.schemaVersion=i,a.mkdirSync(e,{recursive:!0}),a.writeFileSync(m(e),JSON.stringify(n,null,2)+`
2
+ `,"utf8")}function S(e,n){return e.commandUsage||(e.commandUsage={}),e.commandUsage[n]=(e.commandUsage[n]||0)+1,e}function g(e){if(!e||e.length===0)return"unknown";let n=0,o=0,t=0;for(const s of e)/^[A-Z][a-z]/.test(s)?n++:/^[a-z].*[A-Z]/.test(s)?o++:s.includes("-")&&t++;const r=Math.max(n,o,t);return r===0?"unknown":n===r?"PascalCase":o===r?"camelCase":"kebab-case"}function w(e){const n={},o=/^(Create|Add|Update|Edit|Delete|Remove|Get|Read|List|Fetch|Search|Filter|Toggle|Set|Clear|Send|Upload|Download|Export|Import|Generate|Sync|Validate|Check|Run|Start|Stop|Enable|Disable|Show|Hide)/;for(const t of e||[]){const r=t.match(o);r&&(n[r[1]]=(n[r[1]]||0)+1)}return Object.entries(n).sort((t,r)=>r[1]-t[1]).slice(0,5).map(([t])=>t)}function h(e,n,o){const t=l(e);n?.developmentProfile&&(t.stack={language:n.developmentProfile.language||"unknown",framework:n.developmentProfile.framework||"unknown",projectType:n.developmentProfile.projectType||"unknown"});const r=(o||[]).map(s=>s.id);return r.length>0&&(t.namingStyle=g(r),t.preferredVerbs=w(r)),r.length>1&&(t.featureClusters=[r]),d(e,t),t}function p(e,n){if(!n||n.length<2)return e;e.featureClusters||(e.featureClusters=[]);for(let o=0;o<e.featureClusters.length;o++){const t=new Set(e.featureClusters[o]),r=new Set(n);if([...r].filter(u=>t.has(u)).length/Math.min(t.size,r.size)>.5){const u=Array.from(new Set([...t,...r]));return e.featureClusters[o]=u,e}}return e.featureClusters.push([...n]),e}function C(e){if(!e||e.sessionCount===0&&e.namingStyle==="unknown")return null;const n=[];return e.namingStyle!=="unknown"&&n.push(`naming: ${e.namingStyle}`),e.preferredVerbs.length>0&&n.push(`verbs: ${e.preferredVerbs.slice(0,3).join(", ")}`),e.stack.framework!=="unknown"&&n.push(`stack: ${e.stack.framework} (${e.stack.language})`),e.sessionCount>0&&n.push(`sessions: ${e.sessionCount}`),n.join(" \xB7 ")}export{i as PROFILE_SCHEMA_VERSION,c as blankProfile,g as detectNamingStyle,w as detectPreferredVerbs,m as profilePath,l as readProfile,p as recordCapabilityCluster,S as recordCommandUse,h as seedProfileFromAdoption,C as summarizeProfile,d as writeProfile};