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.
- package/README.md +52 -363
- package/dist/bin/infernoflow.mjs +37 -27
- package/dist/lib/adopters/angular.mjs +1 -0
- package/dist/lib/adopters/css.mjs +1 -0
- package/dist/lib/adopters/react.mjs +1 -0
- package/dist/lib/commands/adopt.mjs +11 -11
- package/dist/lib/commands/context.mjs +25 -18
- package/dist/lib/commands/generateSkills.mjs +114 -0
- package/dist/lib/commands/setup.mjs +4 -0
- package/dist/lib/commands/suggest.mjs +13 -13
- package/dist/lib/git/detect-drift.mjs +4 -0
- package/dist/lib/learning/adapt.mjs +9 -0
- package/dist/lib/learning/observe.mjs +1 -0
- package/dist/lib/learning/profile.mjs +2 -0
- package/dist/templates/cursor/inferno-mcp-server.mjs +378 -0
- package/package.json +1 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import*as
|
|
2
|
-
`),
|
|
3
|
-
`)}function
|
|
4
|
-
`);for(const
|
|
5
|
-
`)};return["Project Structure Signals","=".repeat(56),
|
|
6
|
-
`)}function
|
|
7
|
-
`);const
|
|
8
|
-
`);const
|
|
9
|
-
`),
|
|
10
|
-
`)}const
|
|
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
|
-
`;
|
|
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
|
|
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
|
|
3
|
-
`+
|
|
4
|
-
`),l.existsSync(
|
|
5
|
-
`)),process.exit(1));const
|
|
6
|
-
`)),process.exit(1));let
|
|
7
|
-
`))),s&&(
|
|
8
|
-
`),
|
|
9
|
-
`+e.items.map(
|
|
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_"
|
|
13
|
-
`):"_No decisions recorded_",
|
|
14
|
-
`);if(
|
|
15
|
-
\u2714 Context written \u2192 `+
|
|
16
|
-
`+
|
|
17
|
-
`),console.log(" "+
|
|
18
|
-
`),
|
|
19
|
-
`)):(console.log(" "+
|
|
20
|
-
`))}
|
|
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
|
|
2
|
-
`),s=
|
|
3
|
-
`);return` 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
|
-
${
|
|
19
|
+
${d||" (none)"}
|
|
20
20
|
|
|
21
21
|
## Current scenarios
|
|
22
22
|
${s||" (none)"}
|
|
23
23
|
|
|
24
24
|
## Developer's description of what changed
|
|
25
|
-
"${
|
|
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
|
|
56
|
-
`),
|
|
57
|
-
`),
|
|
58
|
-
`),
|
|
59
|
-
`),
|
|
60
|
-
${
|
|
61
|
-
`),
|
|
62
|
-
`)}),
|
|
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};
|