@vpxa/aikit 0.1.120 → 0.1.122

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vpxa/aikit",
3
- "version": "0.1.120",
3
+ "version": "0.1.122",
4
4
  "type": "module",
5
5
  "description": "Local-first AI developer toolkit — knowledge base, code analysis, context management, and developer tools for LLM agents",
6
6
  "license": "MIT",
@@ -1,4 +1,4 @@
1
- import{a as e,i as t,n,t as r}from"./user-DVWGiGOO.js";import{a as i,o as a,r as o}from"./templates-Bdyt_d_F.js";import{copyFileSync as s,existsSync as c,mkdirSync as l,readFileSync as u}from"node:fs";import{basename as d,dirname as f,join as p,resolve as m}from"node:path";import{fileURLToPath as h}from"node:url";import{addToWorkset as g,audit as _,check as v,checkpointLatest as y,checkpointList as b,checkpointLoad as x,checkpointSave as ee,codemod as te,compact as ne,dataTransform as re,delegate as ie,delegateListModels as ae,deleteWorkset as oe,diffParse as se,evaluate as ce,fileSummary as le,find as ue,findDeadSymbols as de,findExamples as fe,getWorkset as pe,gitContext as me,graphQuery as he,guide as ge,health as _e,laneCreate as ve,laneDiff as ye,laneDiscard as be,laneList as xe,laneMerge as Se,laneStatus as Ce,listWorksets as we,parseOutput as Te,processList as Ee,processLogs as De,processStart as S,processStatus as C,processStop as w,queueClear as T,queueCreate as E,queueDelete as Oe,queueDone as ke,queueFail as Ae,queueGet as je,queueList as Me,queueNext as Ne,queuePush as Pe,removeFromWorkset as Fe,rename as Ie,replayClear as Le,replayList as Re,replayTrim as ze,saveWorkset as Be,scopeMap as Ve,stashClear as He,stashDelete as Ue,stashGet as We,stashList as Ge,stashSet as Ke,symbol as qe,testRun as Je,trace as Ye,watchList as Xe,watchStart as Ze,watchStop as Qe}from"../../tools/dist/index.js";import{readFile as D}from"node:fs/promises";import{fork as $e}from"node:child_process";import{AIKIT_PATHS as O,EMBEDDING_DEFAULTS as k,getGlobalDataDir as et,getPartitionDir as A,isUserInstalled as tt,registerWorkspace as nt}from"../../core/dist/index.js";import{initializeWasm as rt}from"../../chunker/dist/index.js";import{OnnxEmbedder as it}from"../../embeddings/dist/index.js";import{IncrementalIndexer as at}from"../../indexer/dist/index.js";import{SqliteGraphStore as j,createSqliteAdapter as ot,createStore as st}from"../../store/dist/index.js";const ct=[{name:`analyze`,description:`Run analyzer output for a path`,usage:`aikit analyze <type> <path>`,run:async e=>{let t=e.shift()?.trim()??``,n=e.shift()?.trim()??``;(!t||!n)&&(console.error(`Usage: aikit analyze <type> <path>`),console.error(`Types: structure, deps, symbols, patterns, entry-points, blast-radius, diagram`),process.exit(1));let{BlastRadiusAnalyzer:r,DependencyAnalyzer:i,DiagramGenerator:a,EntryPointAnalyzer:o,PatternAnalyzer:s,StructureAnalyzer:c,SymbolAnalyzer:l}=await import(`../../analyzers/dist/index.js`),u=m(n),d;switch(t){case`structure`:d=await new c().analyze(u,{format:`markdown`});break;case`deps`:case`dependencies`:d=await new i().analyze(u,{format:`markdown`});break;case`symbols`:d=await new l().analyze(u,{format:`markdown`});break;case`patterns`:d=await new s().analyze(u,{format:`markdown`});break;case`entry-points`:d=await new o().analyze(u,{format:`markdown`});break;case`blast-radius`:d=await new r().analyze(process.cwd(),{files:[n],format:`markdown`});break;case`diagram`:d=await new a().analyze(u,{diagramType:`architecture`});break;default:console.error(`Unknown analyze type: ${t}`),console.error(`Types: structure, deps, symbols, patterns, entry-points, blast-radius, diagram`),process.exit(1)}console.log(d.output)}},{name:`onboard`,description:`Run all analyses for first-time codebase onboarding`,usage:`aikit onboard <path> [--generate] [--out-dir <dir>]`,run:async e=>{let{onboard:t}=await import(`../../tools/dist/index.js`),n=``,r=`memory`,i;for(let t=0;t<e.length;t++){let a=e[t].trim();a===`--generate`?r=`generate`:a===`--out-dir`&&t+1<e.length?i=e[++t].trim():a.startsWith(`--`)||(n=a)}n||=process.cwd();let a=m(n);console.log(`Onboarding: ${a} (mode: ${r})`),console.log(`Running analyses...
1
+ import{a as e,i as t,n,t as r}from"./user-Dz5HLUIS.js";import{a as i,o as a,r as o}from"./templates-BXxDSq-3.js";import{copyFileSync as s,existsSync as c,mkdirSync as l,readFileSync as u}from"node:fs";import{basename as d,dirname as f,join as p,resolve as m}from"node:path";import{fileURLToPath as h}from"node:url";import{addToWorkset as g,audit as _,check as v,checkpointLatest as y,checkpointList as b,checkpointLoad as x,checkpointSave as ee,codemod as te,compact as ne,dataTransform as re,delegate as ie,delegateListModels as ae,deleteWorkset as oe,diffParse as se,evaluate as ce,fileSummary as le,find as ue,findDeadSymbols as de,findExamples as fe,getWorkset as pe,gitContext as me,graphQuery as he,guide as ge,health as _e,laneCreate as ve,laneDiff as ye,laneDiscard as be,laneList as xe,laneMerge as Se,laneStatus as Ce,listWorksets as we,parseOutput as Te,processList as Ee,processLogs as De,processStart as S,processStatus as C,processStop as w,queueClear as T,queueCreate as E,queueDelete as Oe,queueDone as ke,queueFail as Ae,queueGet as je,queueList as Me,queueNext as Ne,queuePush as Pe,removeFromWorkset as Fe,rename as Ie,replayClear as Le,replayList as Re,replayTrim as ze,saveWorkset as Be,scopeMap as Ve,stashClear as He,stashDelete as Ue,stashGet as We,stashList as Ge,stashSet as Ke,symbol as qe,testRun as Je,trace as Ye,watchList as Xe,watchStart as Ze,watchStop as Qe}from"../../tools/dist/index.js";import{readFile as D}from"node:fs/promises";import{fork as $e}from"node:child_process";import{AIKIT_PATHS as O,EMBEDDING_DEFAULTS as k,getGlobalDataDir as et,getPartitionDir as A,isUserInstalled as tt,registerWorkspace as nt}from"../../core/dist/index.js";import{initializeWasm as rt}from"../../chunker/dist/index.js";import{OnnxEmbedder as it}from"../../embeddings/dist/index.js";import{IncrementalIndexer as at}from"../../indexer/dist/index.js";import{SqliteGraphStore as j,createSqliteAdapter as ot,createStore as st}from"../../store/dist/index.js";const ct=[{name:`analyze`,description:`Run analyzer output for a path`,usage:`aikit analyze <type> <path>`,run:async e=>{let t=e.shift()?.trim()??``,n=e.shift()?.trim()??``;(!t||!n)&&(console.error(`Usage: aikit analyze <type> <path>`),console.error(`Types: structure, deps, symbols, patterns, entry-points, blast-radius, diagram`),process.exit(1));let{BlastRadiusAnalyzer:r,DependencyAnalyzer:i,DiagramGenerator:a,EntryPointAnalyzer:o,PatternAnalyzer:s,StructureAnalyzer:c,SymbolAnalyzer:l}=await import(`../../analyzers/dist/index.js`),u=m(n),d;switch(t){case`structure`:d=await new c().analyze(u,{format:`markdown`});break;case`deps`:case`dependencies`:d=await new i().analyze(u,{format:`markdown`});break;case`symbols`:d=await new l().analyze(u,{format:`markdown`});break;case`patterns`:d=await new s().analyze(u,{format:`markdown`});break;case`entry-points`:d=await new o().analyze(u,{format:`markdown`});break;case`blast-radius`:d=await new r().analyze(process.cwd(),{files:[n],format:`markdown`});break;case`diagram`:d=await new a().analyze(u,{diagramType:`architecture`});break;default:console.error(`Unknown analyze type: ${t}`),console.error(`Types: structure, deps, symbols, patterns, entry-points, blast-radius, diagram`),process.exit(1)}console.log(d.output)}},{name:`onboard`,description:`Run all analyses for first-time codebase onboarding`,usage:`aikit onboard <path> [--generate] [--out-dir <dir>]`,run:async e=>{let{onboard:t}=await import(`../../tools/dist/index.js`),n=``,r=`memory`,i;for(let t=0;t<e.length;t++){let a=e[t].trim();a===`--generate`?r=`generate`:a===`--out-dir`&&t+1<e.length?i=e[++t].trim():a.startsWith(`--`)||(n=a)}n||=process.cwd();let a=m(n);console.log(`Onboarding: ${a} (mode: ${r})`),console.log(`Running analyses...
2
2
  `);let o=await t({path:a,mode:r,outDir:i});for(let e of o.steps){let t=e.status===`success`?`✓`:`✗`,n=e.status===`success`?`${e.durationMs}ms, ${e.output.length} chars`:e.error;console.log(` ${t} ${e.name} — ${n}`)}console.log(`\nTotal: ${o.totalDurationMs}ms`),o.outDir&&console.log(`Output written to: ${o.outDir}`)}}];function M(e,t,n){let r=e.indexOf(t);if(r===-1||r+1>=e.length)return n;let i=Number.parseInt(e.splice(r,2)[1],10);return Number.isNaN(i)?n:i}function N(e,t,n){let r=e.indexOf(t);return r===-1||r+1>=e.length?n:e.splice(r,2)[1]}function P(e,t){let n=e.indexOf(t);return n===-1?!1:(e.splice(n,1),!0)}async function F(){if(process.stdin.isTTY)return``;let e=[];for await(let t of process.stdin)e.push(t);return Buffer.concat(e).toString(`utf-8`)}function I(e){return e.split(`,`).map(e=>e.trim()).filter(Boolean)}function lt(e){return e.map(e=>{let t=e.heading?` ${e.heading}`:``;return`${e.start}-${e.end}${t}`}).join(`, `)}function ut(e){switch(e.tool){case`tsc`:case`biome`:console.log(`${e.tool} errors: ${e.errors.length}`);for(let t of e.errors){let e=[t.line,t.column].filter(e=>e!==void 0).join(`:`),n=e?`${t.file}:${e}`:t.file,r=t.code?` ${t.code}`:``;console.log(`- ${n} [${t.severity}${r}] ${t.message}`)}return;case`vitest`:console.log(`Vitest summary`),console.log(` Passed: ${e.summary.passed}`),console.log(` Failed: ${e.summary.failed}`),console.log(` Skipped: ${e.summary.skipped}`),e.summary.duration!==void 0&&console.log(` Duration: ${e.summary.duration}ms`);for(let t of e.summary.tests)t.status===`fail`&&(console.log(`- ${t.name}${t.file?` (${t.file})`:``}`),t.error&&console.log(` ${t.error}`));return;case`git-status`:console.log(`Branch: ${e.status.branch??`unknown`}`),console.log(`Staged: ${e.status.staged.length}`);for(let t of e.status.staged)console.log(` ${t.status} ${t.file}`);console.log(`Unstaged: ${e.status.unstaged.length}`);for(let t of e.status.unstaged)console.log(` ${t.status} ${t.file}`);console.log(`Untracked: ${e.status.untracked.length}`);for(let t of e.status.untracked)console.log(` ?? ${t}`);return}}function dt(e){console.log(`Overall: ${e.passed?`passed`:`failed`}`),L(`tsc`,e.tsc.passed,e.tsc.errors),L(`biome`,e.biome.passed,e.biome.errors)}function L(e,t,n){console.log(`${e}: ${t?`passed`:`${n.length} issue(s)`}`);for(let e of n){let t=[e.line,e.column].filter(e=>e!==void 0).join(`:`),n=t?`${e.file}:${t}`:e.file,r=e.code?` ${e.code}`:``;console.log(` - ${n} [${e.severity}${r}] ${e.message}`)}}function ft(e){console.log(`Vitest: ${e.passed?`passed`:`failed`}`),console.log(` Duration: ${e.durationMs}ms`),console.log(` Passed: ${e.summary.passed}`),console.log(` Failed: ${e.summary.failed}`),console.log(` Skipped: ${e.summary.skipped}`),e.summary.suites!==void 0&&console.log(` Suites: ${e.summary.suites}`);let t=e.summary.tests.filter(e=>e.status===`fail`);if(t.length!==0){console.log(`Failed tests:`);for(let e of t)console.log(` - ${e.name}${e.file?` (${e.file})`:``}`),e.error&&console.log(` ${e.error}`)}}function pt(e){console.log(`Branch: ${e.branch}`),console.log(`Staged: ${e.status.staged.length}`);for(let t of e.status.staged)console.log(` - ${t}`);console.log(`Modified: ${e.status.modified.length}`);for(let t of e.status.modified)console.log(` - ${t}`);console.log(`Untracked: ${e.status.untracked.length}`);for(let t of e.status.untracked)console.log(` - ${t}`);if(console.log(``),console.log(`Recent commits:`),e.recentCommits.length===0)console.log(` none`);else for(let t of e.recentCommits)console.log(` - ${t.hash} ${t.message}`),console.log(` ${t.author} @ ${t.date}`);e.diff&&(console.log(``),console.log(`Diff stat:`),console.log(e.diff))}function mt(e){if(e.length===0){console.log(`No diff files found.`);return}for(let t of e){let e=t.oldPath?` (from ${t.oldPath})`:``;console.log(`${t.path}${e}`),console.log(` Status: ${t.status}`),console.log(` Changes: +${t.additions} -${t.deletions}`),console.log(` Hunks: ${t.hunks.length}`);for(let e of t.hunks){let t=e.header?` ${e.header}`:``;console.log(` @@ -${e.oldStart},${e.oldLines} +${e.newStart},${e.newLines} @@${t}`)}}}function ht(e){if(console.log(`Start: ${e.start}`),console.log(`Direction: ${e.direction}`),console.log(`Depth reached: ${e.depth}`),console.log(`Nodes: ${e.nodes.length}`),e.nodes.length===0){console.log(`No trace nodes found.`);return}for(let t of e.nodes)console.log(` - [${t.relationship}] ${t.path}:${t.line} ${t.symbol}`)}function gt(e){if(console.log(`Query: ${e.query}`),console.log(`Examples: ${e.examples.length} shown (${e.totalFound} total)`),e.examples.length===0){console.log(`No matching examples found.`);return}for(let t of e.examples){console.log(``),console.log(`${t.path}:${t.startLine}-${t.endLine}`),console.log(` Context: ${t.context}`),console.log(` Relevance: ${(t.relevance*100).toFixed(1)}%`);for(let e of t.content.split(`
3
3
  `))console.log(` ${e}`)}}function R(e){console.log(e.id),console.log(` Command: ${e.command}${e.args.length>0?` ${e.args.join(` `)}`:``}`),console.log(` PID: ${e.pid??`unknown`}`),console.log(` Status: ${e.status}`),console.log(` Started: ${e.startedAt}`),e.exitCode!==void 0&&console.log(` Exit code: ${e.exitCode}`),console.log(` Logs: ${e.logs.length}`)}function _t(e){if(console.log(`Exports scanned: ${e.totalExports}`),console.log(`Dead in source: ${e.totalDeadSource} (actionable)`),console.log(`Dead in docs: ${e.totalDeadDocs} (informational)`),e.totalDeadSource===0&&e.totalDeadDocs===0){console.log(`No dead symbols found.`);return}if(e.deadInSource.length>0){console.log(`
4
4
  Dead in source (actionable):`);for(let t of e.deadInSource)console.log(` - ${t.path}:${t.line} ${t.kind} ${t.name}`)}if(e.deadInDocs.length>0){console.log(`
@@ -12,7 +12,7 @@ Issues found:`);for(let e of n.issues)console.log(` - ${e}`)}}}],Dt=[{name:`pro
12
12
  Actions: stats, find-nodes, find-edges, neighbors, traverse, delete, clear`),process.exit(1));let{graphStore:n}=await Y(),r=N(e,`--type`,``),i=N(e,`--name`,``),a=N(e,`--node-id`,``),o=N(e,`--edge-type`,``),s=N(e,`--direction`,`both`),c=M(e,`--depth`,2),l=M(e,`--limit`,50),u=N(e,`--source-path`,``),d={stats:`stats`,"find-nodes":`find_nodes`,"find-edges":`find_edges`,neighbors:`neighbors`,traverse:`traverse`,delete:`delete`,clear:`clear`}[t];d||(console.error(`Unknown graph action: ${t}`),console.error(`Actions: stats, find-nodes, find-edges, neighbors, traverse, delete, clear`),process.exit(1));let f=await he(n,{action:d,nodeType:r||void 0,namePattern:i||void 0,sourcePath:u||void 0,nodeId:a||void 0,edgeType:o||void 0,direction:s,maxDepth:c,limit:l});if(console.log(f.summary),f.nodes&&f.nodes.length>0){console.log(`
13
13
  Nodes:`);for(let e of f.nodes){let t=Object.keys(e.properties).length>0?` ${JSON.stringify(e.properties)}`:``;console.log(` ${e.name} (${e.type}, id: ${e.id})${t}`)}}if(f.edges&&f.edges.length>0){console.log(`
14
14
  Edges:`);for(let e of f.edges){let t=e.weight===1?``:` (weight: ${e.weight})`;console.log(` ${e.fromId} --[${e.type}]--> ${e.toId}${t}`)}}f.stats&&(console.log(`\nNode types: ${JSON.stringify(f.stats.nodeTypes)}`),console.log(`Edge types: ${JSON.stringify(f.stats.edgeTypes)}`)),f.deleted!==void 0&&console.log(`Deleted: ${f.deleted}`)}}],Ft=[{name:`remember`,description:`Store curated knowledge`,usage:`aikit remember <title> --category <cat> [--tags tag1,tag2]`,run:async e=>{let t=N(e,`--category`,``).trim(),n=I(N(e,`--tags`,``)),r=e.shift()?.trim()??``,i=await F(),a=i.trim().length>0?i:e.join(` `).trim();(!r||!t||!a.trim())&&(console.error(`Usage: aikit remember <title> --category <cat> [--tags tag1,tag2]`),process.exit(1));let{curated:o}=await Y(),s=await o.remember(r,a,t,n);console.log(`Stored curated entry`),console.log(` Path: ${s.path}`),console.log(` Category: ${t}`),n.length>0&&console.log(` Tags: ${n.join(`, `)}`)}},{name:`forget`,description:`Remove a curated entry`,usage:`aikit forget <path> --reason <reason>`,run:async e=>{let t=N(e,`--reason`,``).trim(),n=e.shift()?.trim()??``;(!n||!t)&&(console.error(`Usage: aikit forget <path> --reason <reason>`),process.exit(1));let{curated:r}=await Y(),i=await r.forget(n,t);console.log(`Removed curated entry: ${i.path}`)}},{name:`read`,description:`Read a curated entry`,usage:`aikit read <path>`,run:async e=>{let t=e.shift()?.trim()??``;t||(console.error(`Usage: aikit read <path>`),process.exit(1));let{curated:n}=await Y(),r=await n.read(t);console.log(r.title),console.log(`─`.repeat(60)),console.log(`Path: ${r.path}`),console.log(`Category: ${r.category}`),console.log(`Version: ${r.version}`),console.log(`Tags: ${r.tags.length>0?r.tags.join(`, `):`None`}`),console.log(``),console.log(r.content)}},{name:`list`,description:`List curated entries`,usage:`aikit list [--category <cat>] [--tag <tag>]`,run:async e=>{let t=N(e,`--category`,``).trim()||void 0,n=N(e,`--tag`,``).trim()||void 0,{curated:r}=await Y(),i=await r.list({category:t,tag:n});if(i.length===0){console.log(`No curated entries found.`);return}console.log(`Curated entries (${i.length})`),console.log(`─`.repeat(60));for(let e of i){console.log(e.path),console.log(` ${e.title}`),console.log(` Category: ${e.category} | Version: ${e.version}`),console.log(` Tags: ${e.tags.length>0?e.tags.join(`, `):`None`}`);let t=e.contentPreview.replace(/\s+/g,` `).trim();t&&console.log(` Preview: ${t}`),console.log(``)}}},{name:`update`,description:`Update a curated entry`,usage:`aikit update <path> --reason <reason>`,run:async e=>{let t=N(e,`--reason`,``).trim(),n=e.shift()?.trim()??``,r=await F();(!n||!t||!r.trim())&&(console.error(`Usage: aikit update <path> --reason <reason>`),process.exit(1));let{curated:i}=await Y(),a=await i.update(n,r,t);console.log(`Updated curated entry`),console.log(` Path: ${a.path}`),console.log(` Version: ${a.version}`)}},{name:`compact`,description:`Compress text for context`,usage:`aikit compact <query> [--path <file>] [--max-chars N] [--segmentation paragraph|sentence|line]`,run:async e=>{let t=M(e,`--max-chars`,3e3),n=N(e,`--path`,``).trim()||void 0,r=N(e,`--segmentation`,`paragraph`),i=e.join(` `).trim(),a=n?void 0:await F();(!i||!n&&!a?.trim())&&(console.error(`Usage: aikit compact <query> --path <file> OR cat file | aikit compact <query>`),process.exit(1));let{embedder:o}=await Y(),s=await ne(o,{text:a,path:n,query:i,maxChars:t,segmentation:r});console.log(`Compressed ${s.originalChars} chars to ${s.compressedChars} chars`),console.log(`Ratio: ${(s.ratio*100).toFixed(1)}% | Segments: ${s.segmentsKept}/${s.segmentsTotal}`),console.log(``),console.log(s.text)}}],It=[{name:`search`,description:`Search the AI Kit index`,usage:`aikit search <query> [--limit N] [--mode hybrid|semantic|keyword] [--graph-hops 0-3]`,run:async e=>{let t=M(e,`--limit`,5),n=N(e,`--mode`,`hybrid`),r=M(e,`--graph-hops`,0),i=e.join(` `).trim();i||(console.error(`Usage: aikit search <query>`),process.exit(1));let{embedder:a,store:o,graphStore:s}=await Y(),c=await a.embedQuery(i),l;if(n===`keyword`)l=await o.ftsSearch(i,{limit:t});else if(n===`semantic`)l=await o.search(c,{limit:t});else{let[e,n]=await Promise.all([o.search(c,{limit:t*2}),o.ftsSearch(i,{limit:t*2}).catch(()=>[])]);l=St(e,n).slice(0,t)}if(l.length===0){console.log(`No results found.`);return}for(let{record:e,score:t}of l){console.log(`\n${`─`.repeat(60)}`),console.log(`[${(t*100).toFixed(1)}%] ${e.sourcePath}:${e.startLine}-${e.endLine}`),console.log(` Type: ${e.contentType} | Origin: ${e.origin}`),e.tags.length>0&&console.log(` Tags: ${e.tags.join(`, `)}`),console.log(``);let n=e.content.length>500?`${e.content.slice(0,500)}...`:e.content;console.log(n)}if(console.log(`\n${`─`.repeat(60)}`),console.log(`${l.length} result(s) found.`),r>0&&l.length>0)try{let{graphAugmentSearch:e}=await import(`../../tools/dist/index.js`),t=(await e(s,l.map(e=>({recordId:e.record.id,score:e.score,sourcePath:e.record.sourcePath})),{hops:r,maxPerHit:5})).filter(e=>e.graphContext.nodes.length>0);if(t.length>0){console.log(`\nGraph context (${r} hop${r>1?`s`:``}):\n`);for(let e of t){console.log(` ${e.sourcePath}:`);for(let t of e.graphContext.nodes.slice(0,5))console.log(` → ${t.name} (${t.type})`);for(let t of e.graphContext.edges.slice(0,5))console.log(` → ${t.fromId} --[${t.type}]--> ${t.toId}`)}}}catch(e){console.error(`[graph] augmentation failed: ${e.message}`)}}},{name:`find`,description:`Run federated search across indexed content and files`,usage:`aikit find [query] [--glob <pattern>] [--pattern <regex>] [--limit N]`,run:async e=>{let t=M(e,`--limit`,10),n=N(e,`--glob`,``).trim()||void 0,r=N(e,`--pattern`,``).trim()||void 0,i=e.join(` `).trim()||void 0;!i&&!n&&!r&&(console.error(`Usage: aikit find [query] [--glob <pattern>] [--pattern <regex>] [--limit N]`),process.exit(1));let{embedder:a,store:o}=await Y(),s=await ue(a,o,{query:i,glob:n,pattern:r,limit:t});if(s.results.length===0){console.log(`No matches found.`);return}console.log(`Strategies: ${s.strategies.join(`, `)}`),console.log(`Results: ${s.results.length} shown (${s.totalFound} total)`);for(let e of s.results){let t=e.lineRange?`:${e.lineRange.start}-${e.lineRange.end}`:``;console.log(`\n[${e.source}] ${e.path}${t}`),console.log(` Score: ${(e.score*100).toFixed(1)}%`),e.preview&&console.log(` ${e.preview.replace(/\s+/g,` `).trim()}`)}}},{name:`scope-map`,description:`Generate a reading plan for a task`,usage:`aikit scope-map <task> [--max-files N]`,run:async e=>{let t=M(e,`--max-files`,15),n=e.join(` `).trim();n||(console.error(`Usage: aikit scope-map <task> [--max-files N]`),process.exit(1));let{embedder:r,store:i}=await Y(),a=await Ve(r,i,{task:n,maxFiles:t});console.log(`Task: ${a.task}`),console.log(`Files: ${a.files.length}`),console.log(`Estimated tokens: ${a.totalEstimatedTokens}`),console.log(``),console.log(`Reading order:`);for(let e of a.readingOrder)console.log(` ${e}`);for(let[e,t]of a.files.entries())console.log(`\n${e+1}. ${t.path}`),console.log(` Relevance: ${(t.relevance*100).toFixed(1)}% | Tokens: ${t.estimatedTokens}`),console.log(` Why: ${t.reason}`),t.focusRanges.length>0&&console.log(` Focus: ${lt(t.focusRanges)}`)}},{name:`symbol`,description:`Resolve a symbol definition, imports, and references`,usage:`aikit symbol <name> [--limit N]`,run:async e=>{let t=M(e,`--limit`,20),n=e.join(` `).trim();n||(console.error(`Usage: aikit symbol <name> [--limit N]`),process.exit(1));let{embedder:r,store:i}=await Y();yt(await qe(r,i,{name:n,limit:t}))}},{name:`trace`,description:`Trace forward/backward flow for a symbol or file location`,usage:`aikit trace <start> [--direction forward|backward|both] [--max-depth N]`,run:async e=>{let t=N(e,`--direction`,`both`).trim()||`both`,n=M(e,`--max-depth`,3),r=e.join(` `).trim();(!r||![`forward`,`backward`,`both`].includes(t))&&(console.error(`Usage: aikit trace <start> [--direction forward|backward|both] [--max-depth N]`),process.exit(1));let{embedder:i,store:a}=await Y();ht(await Ye(i,a,{start:r,direction:t,maxDepth:n}))}},{name:`examples`,description:`Find real code examples of a symbol or pattern`,usage:`aikit examples <query> [--limit N] [--content-type type]`,run:async e=>{let t=M(e,`--limit`,5),n=N(e,`--content-type`,``).trim()||void 0,r=e.join(` `).trim();r||(console.error(`Usage: aikit examples <query> [--limit N] [--content-type type]`),process.exit(1));let{embedder:i,store:a}=await Y();gt(await fe(i,a,{query:r,limit:t,contentType:n}))}},{name:`dead-symbols`,description:`Find exported symbols that appear to be unused`,usage:`aikit dead-symbols [--limit N]`,run:async e=>{let t=M(e,`--limit`,100),{embedder:n,store:r}=await Y();_t(await de(n,r,{limit:t}))}},{name:`lookup`,description:`Look up indexed content by record ID or source path`,usage:`aikit lookup <id>`,run:async e=>{let t=e.join(` `).trim();t||(console.error(`Usage: aikit lookup <id>`),process.exit(1));let{store:n}=await Y(),r=await n.getById(t);if(r){console.log(r.id),console.log(`─`.repeat(60)),console.log(`Path: ${r.sourcePath}`),console.log(`Chunk: ${r.chunkIndex+1}/${r.totalChunks}`),console.log(`Lines: ${r.startLine}-${r.endLine}`),console.log(`Type: ${r.contentType} | Origin: ${r.origin}`),r.tags.length>0&&console.log(`Tags: ${r.tags.join(`, `)}`),console.log(``),console.log(r.content);return}let i=await n.getBySourcePath(t);if(i.length===0){console.log(`No indexed content found for: ${t}`);return}i.sort((e,t)=>e.chunkIndex-t.chunkIndex),console.log(t),console.log(`─`.repeat(60)),console.log(`Chunks: ${i.length} | Type: ${i[0].contentType}`);for(let e of i){let t=e.startLine?` (lines ${e.startLine}-${e.endLine})`:``;console.log(`\nChunk ${e.chunkIndex+1}/${e.totalChunks}${t}`),console.log(e.content)}}}],X=f(h(import.meta.url));function Z(e){let t=e;for(let e=0;e<10;e++){try{let e=p(t,`package.json`);if(c(e)&&JSON.parse(u(e,`utf8`)).name===`@vpxa/aikit`)return t}catch{}let e=f(t);if(e===t)break;t=e}return m(e,`..`,`..`,`..`)}const Lt=[{name:`status`,description:`Show AI Kit index status and statistics`,run:async()=>{let{isUserInstalled:e,getGlobalDataDir:t,computePartitionKey:n,listWorkspaces:r}=await import(`../../core/dist/index.js`),{existsSync:i}=await import(`node:fs`),a=process.cwd(),o=e(),s=i(m(a,`.vscode`,`mcp.json`)),c,l;if(o&&s)c=`workspace (overrides user-level for this workspace)`,l=m(a,`.aikit-data`);else if(o){let e=n(a);c=i(m(a,`AGENTS.md`))?`user (workspace scaffolded)`:`user (workspace not scaffolded)`,l=m(t(),e)}else c=`workspace`,l=m(a,`.aikit-data`);if(console.log(`AI Kit Status`),console.log(`─`.repeat(40)),console.log(` Mode: ${c}`),console.log(` Data: ${l}`),o&&!s){let e=r();console.log(` Registry: ${e.length} workspace(s) enrolled`)}try{let{store:e}=await Y(),t=await e.getStats(),n=await e.listSourcePaths();console.log(` Records: ${t.totalRecords}`),console.log(` Files: ${t.totalFiles}`),console.log(` Indexed: ${t.lastIndexedAt??`Never`}`),console.log(` Backend: ${t.storeBackend}`),console.log(` Model: ${t.embeddingModel}`),console.log(``),console.log(`Content Types:`);for(let[e,n]of Object.entries(t.contentTypeBreakdown))console.log(` ${e}: ${n}`);if(n.length>0){console.log(``),console.log(`Files (${n.length} total):`);for(let e of n.slice(0,20))console.log(` ${e}`);n.length>20&&console.log(` ... and ${n.length-20} more`)}}catch{console.log(``),console.log(" Index not available — run `aikit reindex` to index this workspace.")}o&&!s&&!i(m(a,`AGENTS.md`))&&(console.log(``),console.log(" Action: Run `npx @vpxa/aikit init` to add AGENTS.md and copilot-instructions.md"))}},{name:`reindex`,description:`Re-index the AI Kit index from configured sources`,usage:`aikit reindex [--full]`,run:async e=>{let t=e.includes(`--full`),{store:n,indexer:r,curated:i,config:a}=await Y();console.log(`Indexing sources...`);let o=e=>{e.phase===`chunking`&&e.currentFile&&process.stdout.write(`\r [${e.filesProcessed+1}/${e.filesTotal}] ${e.currentFile}`),e.phase===`done`&&process.stdout.write(`
15
- `)},s;t?(console.log(`Dropping existing index for full reindex...`),s=await r.reindexAll(a,o)):s=await r.index(a,o),console.log(`Done: ${s.filesProcessed} files, ${s.chunksCreated} chunks in ${(s.durationMs/1e3).toFixed(1)}s`),console.log(`Building FTS index...`),await n.createFtsIndex(),console.log(`Re-indexing curated entries...`);let c=await i.reindexAll();console.log(`Curated: ${c.indexed} entries restored`)}},{name:`serve`,description:`Start the MCP server (stdio or HTTP)`,usage:`aikit serve [--transport stdio|http] [--port N]`,run:async e=>{let t=m(Z(X),`packages`,`server`,`dist`,`index.js`),n=N(e,`--transport`,`stdio`),r=N(e,`--port`,`3210`);try{await U({silent:!0})}catch{}let i=$e(t,[],{stdio:n===`stdio`?[`pipe`,`pipe`,`inherit`,`ipc`]:`inherit`,env:{...process.env,AIKIT_TRANSPORT:n,AIKIT_PORT:r}});n===`stdio`&&i.stdin&&i.stdout&&(process.stdin.pipe(i.stdin),i.stdout.pipe(process.stdout)),i.on(`exit`,e=>process.exit(e??0)),process.on(`SIGINT`,()=>i.kill(`SIGINT`)),process.on(`SIGTERM`,()=>i.kill(`SIGTERM`)),await new Promise(()=>{})}},{name:`init`,description:`Initialize AI Kit in the current directory`,usage:`aikit init [--workspace] [--smart] [--force] [--guide]`,run:async e=>{let t=e.includes(`--user`),n=e.includes(`--workspace`),r=e.includes(`--smart`),i=e.includes(`--guide`),a=e.includes(`--force`);if(t&&n&&(console.error(`Cannot use --user and --workspace together.`),process.exit(1)),i){let{guideProject:e}=await import(`./init-CEdDN3Lc.js`);await e();return}if(r){let{initSmart:e}=await import(`./init-CEdDN3Lc.js`);await e({force:a})}else if(t){let{initUser:e}=await import(`./user-DVWGiGOO.js`).then(e=>e.r);await e({force:a})}else if(n){let{initProject:e}=await import(`./init-CEdDN3Lc.js`);await e({force:a})}else{let{initUser:e}=await import(`./user-DVWGiGOO.js`).then(e=>e.r);await e({force:a})}}},{name:`check`,description:`Run incremental typecheck and lint`,usage:`aikit check [--cwd <dir>] [--files f1,f2] [--skip-types] [--skip-lint] [--detail efficient|normal|full]`,run:async e=>{let t=N(e,`--cwd`,``).trim()||void 0,n=N(e,`--files`,``),r=N(e,`--detail`,`full`)||`full`,i=n.split(`,`).map(e=>e.trim()).filter(Boolean),a=!1;e.includes(`--skip-types`)&&(e.splice(e.indexOf(`--skip-types`),1),a=!0);let o=!1;e.includes(`--skip-lint`)&&(e.splice(e.indexOf(`--skip-lint`),1),o=!0);let s=await v({cwd:t,files:i.length>0?i:void 0,skipTypes:a,skipLint:o,detail:r});dt(s),s.passed||(process.exitCode=1)}},{name:`health`,description:`Run project health checks on the current directory`,usage:`aikit health [path]`,run:async e=>{let t=_e(e.shift());console.log(`Project Health: ${t.path}`),console.log(`─`.repeat(50));for(let e of t.checks){let t=e.status===`pass`?`+`:e.status===`warn`?`~`:`X`;console.log(` [${t}] ${e.name}: ${e.message}`)}console.log(`─`.repeat(50)),console.log(`Score: ${t.score}% — ${t.summary}`)}},{name:`audit`,description:`Run a unified project audit (structure, deps, patterns, health, dead symbols, check)`,usage:`aikit audit [path] [--checks structure,dependencies,patterns,health,dead_symbols,check,entry_points] [--detail efficient|normal|full]`,run:async e=>{let{store:t,embedder:n}=await Y(),r=N(e,`--detail`,`efficient`)||`efficient`,i=N(e,`--checks`,``),a=i?i.split(`,`).map(e=>e.trim()):void 0,o=await _(t,n,{path:e.shift()||`.`,checks:a,detail:r});if(o.ok){if(console.log(o.summary),o.next&&o.next.length>0){console.log(`
16
- Suggested next steps:`);for(let e of o.next)console.log(` → ${e.tool}: ${e.reason}`)}}else console.error(o.error?.message??`Audit failed`),process.exitCode=1}},{name:`guide`,description:`Tool discovery — recommend AI Kit tools for a given goal`,usage:`aikit guide <goal> [--max N]`,run:async e=>{let t=e.indexOf(`--max`),n=5;t!==-1&&t+1<e.length&&(n=Number.parseInt(e.splice(t,2)[1],10)||5);let r=e.join(` `).trim();r||(console.error(`Usage: aikit guide <goal> [--max N]`),console.error(`Example: aikit guide "audit this project"`),process.exit(1));let i=ge(r,n);console.log(`Workflow: ${i.workflow}`),console.log(` ${i.description}\n`),console.log(`Recommended tools:`);for(let e of i.tools){let t=e.suggestedArgs?` ${JSON.stringify(e.suggestedArgs)}`:``;console.log(` ${e.order}. ${e.tool} — ${e.reason}${t}`)}i.alternativeWorkflows.length>0&&console.log(`\nAlternatives: ${i.alternativeWorkflows.join(`, `)}`)}},{name:`replay`,description:`Show recent tool invocation audit trail`,usage:`aikit replay [--last N] [--tool <name>] [--source mcp|cli]`,run:async e=>{let t=Re({last:Number.parseInt(e[e.indexOf(`--last`)+1],10)||20,tool:e.includes(`--tool`)?e[e.indexOf(`--tool`)+1]:void 0,source:e.includes(`--source`)?e[e.indexOf(`--source`)+1]:void 0});if(t.length===0){console.log(`No replay entries. Activity is logged when tools are invoked.`);return}console.log(`Replay Log (${t.length} entries)\n`);for(let e of t){let t=e.ts.split(`T`)[1]?.split(`.`)[0]??e.ts,n=e.status===`ok`?`✓`:`✗`;console.log(`${t} ${n} ${e.tool} (${e.durationMs}ms) [${e.source}]`),console.log(` in: ${e.input}`),console.log(` out: ${e.output}`)}ze().catch(()=>{})}},{name:`replay-clear`,description:`Clear the replay audit trail`,run:async()=>{Le(),console.log(`Replay log cleared.`)}},{name:`dashboard`,description:`Launch web dashboard for knowledge graph visualization`,usage:`aikit dashboard [--port <port>] [--no-open]`,run:async e=>{let t=e.indexOf(`--port`),n=t!==-1&&e[t+1]?Number.parseInt(e[t+1],10):3210,r=Number.isFinite(n)?n:3210,i=e.includes(`--no-open`);console.log(`Starting AI Kit server on port ${r}...`);let{spawn:a}=await import(`node:child_process`),{platform:o}=await import(`node:os`),s=m(Z(X),`packages`,`server`,`dist`,`index.js`),c=a(process.execPath,[s,`--transport`,`http`,`--port`,String(r)],{stdio:[`ignore`,`pipe`,`pipe`],env:{...process.env,AIKIT_TRANSPORT:`http`,AIKIT_PORT:String(r)}}),l=`http://localhost:${r}/_dashboard/`,u=`http://localhost:${r}/health`,d=!1;for(let e=0;e<30;e+=1){try{if((await fetch(u)).ok){d=!0;break}}catch{}await new Promise(e=>setTimeout(e,1e3))}if(d||(console.error(`Server failed to start within 30 seconds.`),c.kill(),process.exit(1)),console.log(`AI Kit Dashboard: ${l}`),console.log(`Press Ctrl+C to stop.`),!i){let e=o();e===`win32`?a(`cmd`,[`/c`,`start`,``,l],{stdio:`ignore`,detached:!0}).unref():a(e===`darwin`?`open`:`xdg-open`,[l],{stdio:`ignore`,detached:!0}).unref()}let f=()=>{c.kill(),process.exit(0)};process.on(`SIGINT`,f),process.on(`SIGTERM`,f),await new Promise(e=>{c.on(`exit`,()=>e())})}},{name:`settings`,description:`Launch web UI to manage AI Kit configuration and environment variables`,usage:`aikit settings [--port <port>] [--no-open]`,run:async e=>{let t=e.indexOf(`--port`),n=t!==-1&&e[t+1]?Number.parseInt(e[t+1],10):3210,r=Number.isFinite(n)?n:3210,i=e.includes(`--no-open`);console.log(`Starting AI Kit server on port ${r}...`);let{spawn:a}=await import(`node:child_process`),{platform:o}=await import(`node:os`),s=m(Z(X),`packages`,`server`,`dist`,`index.js`),c=a(process.execPath,[s,`--transport`,`http`,`--port`,String(r)],{stdio:[`ignore`,`pipe`,`pipe`],env:{...process.env,AIKIT_TRANSPORT:`http`,AIKIT_PORT:String(r)}}),l=`http://localhost:${r}/settings/`,u=`http://localhost:${r}/health`,d=!1;for(let e=0;e<30;e+=1){try{if((await fetch(u)).ok){d=!0;break}}catch{}await new Promise(e=>setTimeout(e,1e3))}if(d||(console.error(`Server failed to start within 30 seconds.`),c.kill(),process.exit(1)),console.log(`AI Kit Settings: ${l}`),console.log(`Press Ctrl+C to stop.`),!i){let e=o();e===`win32`?a(`cmd`,[`/c`,`start`,``,l],{stdio:`ignore`,detached:!0}).unref():a(e===`darwin`?`open`:`xdg-open`,[l],{stdio:`ignore`,detached:!0}).unref()}let f=()=>{c.kill(),process.exit(0)};process.on(`SIGINT`,f),process.on(`SIGTERM`,f),await new Promise(e=>{c.on(`exit`,()=>e())})}}];function Rt(e){let t=e;for(let e=0;e<10;e++){try{let e=p(t,`package.json`);if(c(e)&&JSON.parse(u(e,`utf8`)).name===`@vpxa/aikit`)return t}catch{}let e=f(t);if(e===t)break;t=e}return m(e,`..`,`..`,`..`)}const zt=[{name:`upgrade`,description:`Upgrade AI Kit agents, prompts, and skills to the latest version (user-level and workspace-level)`,usage:`aikit upgrade`,run:async()=>{let{initUser:e}=await import(`./user-DVWGiGOO.js`).then(e=>e.r);await e({force:!0});let t=process.cwd(),n=c(m(t,`.github`,`.aikit-scaffold.json`)),r=c(m(t,`.github`,`agents`)),i=c(m(t,`.github`,`prompts`)),s=c(m(t,`.claude`,`commands`));if(n||r||i||s){let{initScaffoldOnly:e}=await import(`./init-CEdDN3Lc.js`);await e({force:!0})}if(c(m(t,`.github`,`skills`))){let{smartCopySkills:e}=await import(`./scaffold--Z8D_CTu.js`).then(e=>e.a),n=Rt(f(h(import.meta.url))),r=JSON.parse(u(m(n,`package.json`),`utf-8`)).version;await e(t,n,[...a],r,!0);let{smartCopyFlows:i}=await import(`./scaffold--Z8D_CTu.js`).then(e=>e.a);await i(t,n,[...o],r,!0)}}}],Bt=[{name:`workset`,description:`Manage saved file sets`,usage:`aikit workset <action> [name] [--files f1,f2] [--description desc]`,run:async e=>{let t=e.shift()?.trim(),n=I(N(e,`--files`,``)),r=N(e,`--description`,``).trim()||void 0,i=e.shift()?.trim();switch(t||(console.error(`Usage: aikit workset <action> [name] [--files f1,f2] [--description desc]`),console.error(`Actions: save, get, list, delete, add, remove`),process.exit(1)),t){case`save`:{(!i||n.length===0)&&(console.error(`Usage: aikit workset save <name> --files f1,f2 [--description desc]`),process.exit(1));let e=Be(i,n,{description:r});console.log(`Saved workset: ${e.name}`),z(e);return}case`get`:{i||(console.error(`Usage: aikit workset get <name>`),process.exit(1));let e=pe(i);if(!e){console.log(`No workset found: ${i}`);return}z(e);return}case`list`:{let e=we();if(e.length===0){console.log(`No worksets saved.`);return}console.log(`Worksets (${e.length})`),console.log(`─`.repeat(60));for(let t of e)z(t),console.log(``);return}case`delete`:{i||(console.error(`Usage: aikit workset delete <name>`),process.exit(1));let e=oe(i);console.log(e?`Deleted workset: ${i}`:`No workset found: ${i}`);return}case`add`:{(!i||n.length===0)&&(console.error(`Usage: aikit workset add <name> --files f1,f2`),process.exit(1));let e=g(i,n);console.log(`Updated workset: ${e.name}`),z(e);return}case`remove`:{(!i||n.length===0)&&(console.error(`Usage: aikit workset remove <name> --files f1,f2`),process.exit(1));let e=Fe(i,n);if(!e){console.log(`No workset found: ${i}`);return}console.log(`Updated workset: ${e.name}`),z(e);return}default:console.error(`Unknown workset action: ${t}`),console.error(`Actions: save, get, list, delete, add, remove`),process.exit(1)}}},{name:`stash`,description:`Persist and retrieve named intermediate values`,usage:`aikit stash <set|get|list|delete|clear> [key] [value]`,run:async e=>{let t=e.shift()?.trim(),n=e.shift()?.trim();switch(t||(console.error(`Usage: aikit stash <set|get|list|delete|clear> [key] [value]`),process.exit(1)),t){case`set`:{n||(console.error(`Usage: aikit stash set <key> <value>`),process.exit(1));let t=e.join(` `),r=t.trim()?``:await F(),i=Ke(n,bt(t||r));console.log(`Stored stash entry: ${i.key}`),console.log(` Type: ${i.type}`),console.log(` Stored: ${i.storedAt}`);return}case`get`:{n||(console.error(`Usage: aikit stash get <key>`),process.exit(1));let e=We(n);if(!e){console.log(`No stash entry found: ${n}`);return}console.log(JSON.stringify(e,null,2));return}case`list`:{let e=Ge();if(e.length===0){console.log(`No stash entries saved.`);return}console.log(`Stash entries (${e.length})`),console.log(`─`.repeat(60));for(let t of e)console.log(`${t.key} (${t.type})`),console.log(` Stored: ${t.storedAt}`);return}case`delete`:{n||(console.error(`Usage: aikit stash delete <key>`),process.exit(1));let e=Ue(n);console.log(e?`Deleted stash entry: ${n}`:`No stash entry found: ${n}`);return}case`clear`:{let e=He();console.log(`Cleared ${e} stash entr${e===1?`y`:`ies`}.`);return}default:console.error(`Unknown stash action: ${t}`),console.error(`Actions: set, get, list, delete, clear`),process.exit(1)}}},{name:`lane`,description:`Manage verified lanes — isolated file copies for parallel exploration`,usage:`aikit lane <create|list|status|diff|merge|discard> [name] [--files f1,f2]`,run:async e=>{let t=e.shift();if((!t||![`create`,`list`,`status`,`diff`,`merge`,`discard`].includes(t))&&(console.error(`Usage: aikit lane <create|list|status|diff|merge|discard> [name] [--files f1,f2]`),process.exit(1)),t===`list`){let e=xe();if(e.length===0){console.log(`No active lanes.`);return}for(let t of e)console.log(`${t.name} (${t.sourceFiles.length} files, created ${t.createdAt})`);return}let n=e.shift();switch(n||(console.error(`Lane name is required for "${t}".`),process.exit(1)),t){case`create`:{let t=N(e,`--files`,``);t||(console.error(`Usage: aikit lane create <name> --files file1.ts,file2.ts`),process.exit(1));let r=ve(n,t.split(`,`).map(e=>e.trim()));console.log(`Lane "${r.name}" created with ${r.sourceFiles.length} files.`);break}case`status`:{let e=Ce(n);console.log(`Lane: ${e.name}`),console.log(`Modified: ${e.modified} | Added: ${e.added} | Deleted: ${e.deleted}`);for(let t of e.entries)console.log(` ${t.status.padEnd(10)} ${t.file}`);break}case`diff`:{let e=ye(n);console.log(`Lane: ${e.name} — ${e.modified} modified, ${e.added} added, ${e.deleted} deleted`);for(let t of e.entries)t.diff&&(console.log(`\n--- ${t.file} (${t.status})`),console.log(t.diff));break}case`merge`:{let e=Se(n);console.log(`Merged ${e.filesMerged} files from lane "${e.name}".`);for(let t of e.files)console.log(` ${t}`);break}case`discard`:{let e=be(n);console.log(e?`Lane "${n}" discarded.`:`Lane "${n}" not found.`);break}}}},{name:`queue`,description:`Manage task queues for sequential agent operations`,usage:`aikit queue <create|push|next|done|fail|get|list|clear|delete> [name] [args]`,run:async e=>{let t=e.shift();if((!t||![`create`,`push`,`next`,`done`,`fail`,`get`,`list`,`clear`,`delete`].includes(t))&&(console.error(`Usage: aikit queue <create|push|next|done|fail|get|list|clear|delete> [name] [args]`),process.exit(1)),t===`list`){let e=Me();if(e.length===0){console.log(`No queues.`);return}for(let t of e)console.log(`${t.name} pending:${t.pending} done:${t.done} failed:${t.failed} total:${t.total}`);return}let n=e.shift();switch(n||(console.error(`Queue name is required for "${t}".`),process.exit(1)),t){case`create`:{let e=E(n);console.log(`Queue "${e.name}" created.`);break}case`push`:{let t=Pe(n,e.join(` `)||`Untitled task`);console.log(`Pushed "${t.title}" (${t.id}) to queue "${n}".`);break}case`next`:{let e=Ne(n);console.log(e?`Next: ${e.title} (${e.id})`:`No pending items in queue "${n}".`);break}case`done`:{let t=e.shift();t||(console.error(`Usage: aikit queue done <name> <id>`),process.exit(1));let r=ke(n,t);console.log(`Marked "${r.item.title}" as done.`);break}case`fail`:{let t=e.shift(),r=e.join(` `)||`Unknown error`;t||(console.error(`Usage: aikit queue fail <name> <id> [error message]`),process.exit(1));let i=Ae(n,t,r);console.log(`Marked "${i.title}" as failed: ${r}`);break}case`get`:{let e=je(n);if(!e){console.log(`Queue "${n}" not found.`);return}console.log(`Queue: ${e.name} (${e.items.length} items)`);for(let t of e.items){let e=t.error?` — ${t.error}`:``;console.log(` ${t.status.padEnd(12)} ${t.id} ${t.title}${e}`)}break}case`clear`:{let e=T(n);console.log(`Cleared ${e} completed/failed items from queue "${n}".`);break}case`delete`:{let e=Oe(n);console.log(e?`Queue "${n}" deleted.`:`Queue "${n}" not found.`);break}}}}],Q=[...It,...Ft,...ct,...Pt,...Lt,...Ot,...Ct,...Bt,...Dt,...zt,...q,...Et];Q.push({name:`help`,description:`Show available commands`,run:async()=>{$()}});async function Vt(e){let t=[...e],n=t.shift();if(!n||n===`--help`||n===`-h`){$();return}if(n===`--version`||n===`-v`){let e=m(f(h(import.meta.url)),`..`,`..`,`..`,`package.json`),t=JSON.parse(u(e,`utf-8`));console.log(t.version);return}if(n&&new Set([`--user`,`--workspace`,`--guide`,`--smart`]).has(n)){let e=Q.find(e=>e.name===`init`);if(e){await e.run([n,...t]);return}}let r=Q.find(e=>e.name===n);r||(console.error(`Unknown command: ${n}`),$(),process.exit(1));try{await r.run(t)}finally{let e=Nt();e&&await e.store.close()}}function $(){console.log(`@vpxa/aikit — Local-first AI developer toolkit
15
+ `)},s;t?(console.log(`Dropping existing index for full reindex...`),s=await r.reindexAll(a,o)):s=await r.index(a,o),console.log(`Done: ${s.filesProcessed} files, ${s.chunksCreated} chunks in ${(s.durationMs/1e3).toFixed(1)}s`),console.log(`Building FTS index...`),await n.createFtsIndex(),console.log(`Re-indexing curated entries...`);let c=await i.reindexAll();console.log(`Curated: ${c.indexed} entries restored`)}},{name:`serve`,description:`Start the MCP server (stdio or HTTP)`,usage:`aikit serve [--transport stdio|http] [--port N]`,run:async e=>{let t=m(Z(X),`packages`,`server`,`dist`,`index.js`),n=N(e,`--transport`,`stdio`),r=N(e,`--port`,`3210`);try{await U({silent:!0})}catch{}let i=$e(t,[],{stdio:n===`stdio`?[`pipe`,`pipe`,`inherit`,`ipc`]:`inherit`,env:{...process.env,AIKIT_TRANSPORT:n,AIKIT_PORT:r}});n===`stdio`&&i.stdin&&i.stdout&&(process.stdin.pipe(i.stdin),i.stdout.pipe(process.stdout)),i.on(`exit`,e=>process.exit(e??0)),process.on(`SIGINT`,()=>i.kill(`SIGINT`)),process.on(`SIGTERM`,()=>i.kill(`SIGTERM`)),await new Promise(()=>{})}},{name:`init`,description:`Initialize AI Kit in the current directory`,usage:`aikit init [--workspace] [--smart] [--force] [--guide]`,run:async e=>{let t=e.includes(`--user`),n=e.includes(`--workspace`),r=e.includes(`--smart`),i=e.includes(`--guide`),a=e.includes(`--force`);if(t&&n&&(console.error(`Cannot use --user and --workspace together.`),process.exit(1)),i){let{guideProject:e}=await import(`./init-B8jFOU_T.js`);await e();return}if(r){let{initSmart:e}=await import(`./init-B8jFOU_T.js`);await e({force:a})}else if(t){let{initUser:e}=await import(`./user-Dz5HLUIS.js`).then(e=>e.r);await e({force:a})}else if(n){let{initProject:e}=await import(`./init-B8jFOU_T.js`);await e({force:a})}else{let{initUser:e}=await import(`./user-Dz5HLUIS.js`).then(e=>e.r);await e({force:a})}}},{name:`check`,description:`Run incremental typecheck and lint`,usage:`aikit check [--cwd <dir>] [--files f1,f2] [--skip-types] [--skip-lint] [--detail efficient|normal|full]`,run:async e=>{let t=N(e,`--cwd`,``).trim()||void 0,n=N(e,`--files`,``),r=N(e,`--detail`,`full`)||`full`,i=n.split(`,`).map(e=>e.trim()).filter(Boolean),a=!1;e.includes(`--skip-types`)&&(e.splice(e.indexOf(`--skip-types`),1),a=!0);let o=!1;e.includes(`--skip-lint`)&&(e.splice(e.indexOf(`--skip-lint`),1),o=!0);let s=await v({cwd:t,files:i.length>0?i:void 0,skipTypes:a,skipLint:o,detail:r});dt(s),s.passed||(process.exitCode=1)}},{name:`health`,description:`Run project health checks on the current directory`,usage:`aikit health [path]`,run:async e=>{let t=_e(e.shift());console.log(`Project Health: ${t.path}`),console.log(`─`.repeat(50));for(let e of t.checks){let t=e.status===`pass`?`+`:e.status===`warn`?`~`:`X`;console.log(` [${t}] ${e.name}: ${e.message}`)}console.log(`─`.repeat(50)),console.log(`Score: ${t.score}% — ${t.summary}`)}},{name:`audit`,description:`Run a unified project audit (structure, deps, patterns, health, dead symbols, check)`,usage:`aikit audit [path] [--checks structure,dependencies,patterns,health,dead_symbols,check,entry_points] [--detail efficient|normal|full]`,run:async e=>{let{store:t,embedder:n}=await Y(),r=N(e,`--detail`,`efficient`)||`efficient`,i=N(e,`--checks`,``),a=i?i.split(`,`).map(e=>e.trim()):void 0,o=await _(t,n,{path:e.shift()||`.`,checks:a,detail:r});if(o.ok){if(console.log(o.summary),o.next&&o.next.length>0){console.log(`
16
+ Suggested next steps:`);for(let e of o.next)console.log(` → ${e.tool}: ${e.reason}`)}}else console.error(o.error?.message??`Audit failed`),process.exitCode=1}},{name:`guide`,description:`Tool discovery — recommend AI Kit tools for a given goal`,usage:`aikit guide <goal> [--max N]`,run:async e=>{let t=e.indexOf(`--max`),n=5;t!==-1&&t+1<e.length&&(n=Number.parseInt(e.splice(t,2)[1],10)||5);let r=e.join(` `).trim();r||(console.error(`Usage: aikit guide <goal> [--max N]`),console.error(`Example: aikit guide "audit this project"`),process.exit(1));let i=ge(r,n);console.log(`Workflow: ${i.workflow}`),console.log(` ${i.description}\n`),console.log(`Recommended tools:`);for(let e of i.tools){let t=e.suggestedArgs?` ${JSON.stringify(e.suggestedArgs)}`:``;console.log(` ${e.order}. ${e.tool} — ${e.reason}${t}`)}i.alternativeWorkflows.length>0&&console.log(`\nAlternatives: ${i.alternativeWorkflows.join(`, `)}`)}},{name:`replay`,description:`Show recent tool invocation audit trail`,usage:`aikit replay [--last N] [--tool <name>] [--source mcp|cli]`,run:async e=>{let t=Re({last:Number.parseInt(e[e.indexOf(`--last`)+1],10)||20,tool:e.includes(`--tool`)?e[e.indexOf(`--tool`)+1]:void 0,source:e.includes(`--source`)?e[e.indexOf(`--source`)+1]:void 0});if(t.length===0){console.log(`No replay entries. Activity is logged when tools are invoked.`);return}console.log(`Replay Log (${t.length} entries)\n`);for(let e of t){let t=e.ts.split(`T`)[1]?.split(`.`)[0]??e.ts,n=e.status===`ok`?`✓`:`✗`;console.log(`${t} ${n} ${e.tool} (${e.durationMs}ms) [${e.source}]`),console.log(` in: ${e.input}`),console.log(` out: ${e.output}`)}ze().catch(()=>{})}},{name:`replay-clear`,description:`Clear the replay audit trail`,run:async()=>{Le(),console.log(`Replay log cleared.`)}},{name:`dashboard`,description:`Launch web dashboard for knowledge graph visualization`,usage:`aikit dashboard [--port <port>] [--no-open]`,run:async e=>{let t=e.indexOf(`--port`),n=t!==-1&&e[t+1]?Number.parseInt(e[t+1],10):3210,r=Number.isFinite(n)?n:3210,i=e.includes(`--no-open`);console.log(`Starting AI Kit server on port ${r}...`);let{spawn:a}=await import(`node:child_process`),{platform:o}=await import(`node:os`),s=m(Z(X),`packages`,`server`,`dist`,`index.js`),c=a(process.execPath,[s,`--transport`,`http`,`--port`,String(r)],{stdio:[`ignore`,`pipe`,`pipe`],env:{...process.env,AIKIT_TRANSPORT:`http`,AIKIT_PORT:String(r)}}),l=`http://localhost:${r}/_dashboard/`,u=`http://localhost:${r}/health`,d=!1;for(let e=0;e<30;e+=1){try{if((await fetch(u)).ok){d=!0;break}}catch{}await new Promise(e=>setTimeout(e,1e3))}if(d||(console.error(`Server failed to start within 30 seconds.`),c.kill(),process.exit(1)),console.log(`AI Kit Dashboard: ${l}`),console.log(`Press Ctrl+C to stop.`),!i){let e=o();e===`win32`?a(`cmd`,[`/c`,`start`,``,l],{stdio:`ignore`,detached:!0}).unref():a(e===`darwin`?`open`:`xdg-open`,[l],{stdio:`ignore`,detached:!0}).unref()}let f=()=>{c.kill(),process.exit(0)};process.on(`SIGINT`,f),process.on(`SIGTERM`,f),await new Promise(e=>{c.on(`exit`,()=>e())})}},{name:`settings`,description:`Launch web UI to manage AI Kit configuration and environment variables`,usage:`aikit settings [--port <port>] [--no-open]`,run:async e=>{let t=e.indexOf(`--port`),n=t!==-1&&e[t+1]?Number.parseInt(e[t+1],10):3210,r=Number.isFinite(n)?n:3210,i=e.includes(`--no-open`);console.log(`Starting AI Kit server on port ${r}...`);let{spawn:a}=await import(`node:child_process`),{platform:o}=await import(`node:os`),s=m(Z(X),`packages`,`server`,`dist`,`index.js`),c=a(process.execPath,[s,`--transport`,`http`,`--port`,String(r)],{stdio:[`ignore`,`pipe`,`pipe`],env:{...process.env,AIKIT_TRANSPORT:`http`,AIKIT_PORT:String(r)}}),l=`http://localhost:${r}/settings/`,u=`http://localhost:${r}/health`,d=!1;for(let e=0;e<30;e+=1){try{if((await fetch(u)).ok){d=!0;break}}catch{}await new Promise(e=>setTimeout(e,1e3))}if(d||(console.error(`Server failed to start within 30 seconds.`),c.kill(),process.exit(1)),console.log(`AI Kit Settings: ${l}`),console.log(`Press Ctrl+C to stop.`),!i){let e=o();e===`win32`?a(`cmd`,[`/c`,`start`,``,l],{stdio:`ignore`,detached:!0}).unref():a(e===`darwin`?`open`:`xdg-open`,[l],{stdio:`ignore`,detached:!0}).unref()}let f=()=>{c.kill(),process.exit(0)};process.on(`SIGINT`,f),process.on(`SIGTERM`,f),await new Promise(e=>{c.on(`exit`,()=>e())})}}];function Rt(e){let t=e;for(let e=0;e<10;e++){try{let e=p(t,`package.json`);if(c(e)&&JSON.parse(u(e,`utf8`)).name===`@vpxa/aikit`)return t}catch{}let e=f(t);if(e===t)break;t=e}return m(e,`..`,`..`,`..`)}const zt=[{name:`upgrade`,description:`Upgrade AI Kit agents, prompts, and skills to the latest version (user-level and workspace-level)`,usage:`aikit upgrade`,run:async()=>{let{initUser:e}=await import(`./user-Dz5HLUIS.js`).then(e=>e.r);await e({force:!0});let t=process.cwd(),n=c(m(t,`.github`,`.aikit-scaffold.json`)),r=c(m(t,`.github`,`agents`)),i=c(m(t,`.github`,`prompts`)),s=c(m(t,`.claude`,`commands`));if(n||r||i||s){let{initScaffoldOnly:e}=await import(`./init-B8jFOU_T.js`);await e({force:!0})}if(c(m(t,`.github`,`skills`))){let{smartCopySkills:e}=await import(`./scaffold--Z8D_CTu.js`).then(e=>e.a),n=Rt(f(h(import.meta.url))),r=JSON.parse(u(m(n,`package.json`),`utf-8`)).version;await e(t,n,[...a],r,!0);let{smartCopyFlows:i}=await import(`./scaffold--Z8D_CTu.js`).then(e=>e.a);await i(t,n,[...o],r,!0)}}}],Bt=[{name:`workset`,description:`Manage saved file sets`,usage:`aikit workset <action> [name] [--files f1,f2] [--description desc]`,run:async e=>{let t=e.shift()?.trim(),n=I(N(e,`--files`,``)),r=N(e,`--description`,``).trim()||void 0,i=e.shift()?.trim();switch(t||(console.error(`Usage: aikit workset <action> [name] [--files f1,f2] [--description desc]`),console.error(`Actions: save, get, list, delete, add, remove`),process.exit(1)),t){case`save`:{(!i||n.length===0)&&(console.error(`Usage: aikit workset save <name> --files f1,f2 [--description desc]`),process.exit(1));let e=Be(i,n,{description:r});console.log(`Saved workset: ${e.name}`),z(e);return}case`get`:{i||(console.error(`Usage: aikit workset get <name>`),process.exit(1));let e=pe(i);if(!e){console.log(`No workset found: ${i}`);return}z(e);return}case`list`:{let e=we();if(e.length===0){console.log(`No worksets saved.`);return}console.log(`Worksets (${e.length})`),console.log(`─`.repeat(60));for(let t of e)z(t),console.log(``);return}case`delete`:{i||(console.error(`Usage: aikit workset delete <name>`),process.exit(1));let e=oe(i);console.log(e?`Deleted workset: ${i}`:`No workset found: ${i}`);return}case`add`:{(!i||n.length===0)&&(console.error(`Usage: aikit workset add <name> --files f1,f2`),process.exit(1));let e=g(i,n);console.log(`Updated workset: ${e.name}`),z(e);return}case`remove`:{(!i||n.length===0)&&(console.error(`Usage: aikit workset remove <name> --files f1,f2`),process.exit(1));let e=Fe(i,n);if(!e){console.log(`No workset found: ${i}`);return}console.log(`Updated workset: ${e.name}`),z(e);return}default:console.error(`Unknown workset action: ${t}`),console.error(`Actions: save, get, list, delete, add, remove`),process.exit(1)}}},{name:`stash`,description:`Persist and retrieve named intermediate values`,usage:`aikit stash <set|get|list|delete|clear> [key] [value]`,run:async e=>{let t=e.shift()?.trim(),n=e.shift()?.trim();switch(t||(console.error(`Usage: aikit stash <set|get|list|delete|clear> [key] [value]`),process.exit(1)),t){case`set`:{n||(console.error(`Usage: aikit stash set <key> <value>`),process.exit(1));let t=e.join(` `),r=t.trim()?``:await F(),i=Ke(n,bt(t||r));console.log(`Stored stash entry: ${i.key}`),console.log(` Type: ${i.type}`),console.log(` Stored: ${i.storedAt}`);return}case`get`:{n||(console.error(`Usage: aikit stash get <key>`),process.exit(1));let e=We(n);if(!e){console.log(`No stash entry found: ${n}`);return}console.log(JSON.stringify(e,null,2));return}case`list`:{let e=Ge();if(e.length===0){console.log(`No stash entries saved.`);return}console.log(`Stash entries (${e.length})`),console.log(`─`.repeat(60));for(let t of e)console.log(`${t.key} (${t.type})`),console.log(` Stored: ${t.storedAt}`);return}case`delete`:{n||(console.error(`Usage: aikit stash delete <key>`),process.exit(1));let e=Ue(n);console.log(e?`Deleted stash entry: ${n}`:`No stash entry found: ${n}`);return}case`clear`:{let e=He();console.log(`Cleared ${e} stash entr${e===1?`y`:`ies`}.`);return}default:console.error(`Unknown stash action: ${t}`),console.error(`Actions: set, get, list, delete, clear`),process.exit(1)}}},{name:`lane`,description:`Manage verified lanes — isolated file copies for parallel exploration`,usage:`aikit lane <create|list|status|diff|merge|discard> [name] [--files f1,f2]`,run:async e=>{let t=e.shift();if((!t||![`create`,`list`,`status`,`diff`,`merge`,`discard`].includes(t))&&(console.error(`Usage: aikit lane <create|list|status|diff|merge|discard> [name] [--files f1,f2]`),process.exit(1)),t===`list`){let e=xe();if(e.length===0){console.log(`No active lanes.`);return}for(let t of e)console.log(`${t.name} (${t.sourceFiles.length} files, created ${t.createdAt})`);return}let n=e.shift();switch(n||(console.error(`Lane name is required for "${t}".`),process.exit(1)),t){case`create`:{let t=N(e,`--files`,``);t||(console.error(`Usage: aikit lane create <name> --files file1.ts,file2.ts`),process.exit(1));let r=ve(n,t.split(`,`).map(e=>e.trim()));console.log(`Lane "${r.name}" created with ${r.sourceFiles.length} files.`);break}case`status`:{let e=Ce(n);console.log(`Lane: ${e.name}`),console.log(`Modified: ${e.modified} | Added: ${e.added} | Deleted: ${e.deleted}`);for(let t of e.entries)console.log(` ${t.status.padEnd(10)} ${t.file}`);break}case`diff`:{let e=ye(n);console.log(`Lane: ${e.name} — ${e.modified} modified, ${e.added} added, ${e.deleted} deleted`);for(let t of e.entries)t.diff&&(console.log(`\n--- ${t.file} (${t.status})`),console.log(t.diff));break}case`merge`:{let e=Se(n);console.log(`Merged ${e.filesMerged} files from lane "${e.name}".`);for(let t of e.files)console.log(` ${t}`);break}case`discard`:{let e=be(n);console.log(e?`Lane "${n}" discarded.`:`Lane "${n}" not found.`);break}}}},{name:`queue`,description:`Manage task queues for sequential agent operations`,usage:`aikit queue <create|push|next|done|fail|get|list|clear|delete> [name] [args]`,run:async e=>{let t=e.shift();if((!t||![`create`,`push`,`next`,`done`,`fail`,`get`,`list`,`clear`,`delete`].includes(t))&&(console.error(`Usage: aikit queue <create|push|next|done|fail|get|list|clear|delete> [name] [args]`),process.exit(1)),t===`list`){let e=Me();if(e.length===0){console.log(`No queues.`);return}for(let t of e)console.log(`${t.name} pending:${t.pending} done:${t.done} failed:${t.failed} total:${t.total}`);return}let n=e.shift();switch(n||(console.error(`Queue name is required for "${t}".`),process.exit(1)),t){case`create`:{let e=E(n);console.log(`Queue "${e.name}" created.`);break}case`push`:{let t=Pe(n,e.join(` `)||`Untitled task`);console.log(`Pushed "${t.title}" (${t.id}) to queue "${n}".`);break}case`next`:{let e=Ne(n);console.log(e?`Next: ${e.title} (${e.id})`:`No pending items in queue "${n}".`);break}case`done`:{let t=e.shift();t||(console.error(`Usage: aikit queue done <name> <id>`),process.exit(1));let r=ke(n,t);console.log(`Marked "${r.item.title}" as done.`);break}case`fail`:{let t=e.shift(),r=e.join(` `)||`Unknown error`;t||(console.error(`Usage: aikit queue fail <name> <id> [error message]`),process.exit(1));let i=Ae(n,t,r);console.log(`Marked "${i.title}" as failed: ${r}`);break}case`get`:{let e=je(n);if(!e){console.log(`Queue "${n}" not found.`);return}console.log(`Queue: ${e.name} (${e.items.length} items)`);for(let t of e.items){let e=t.error?` — ${t.error}`:``;console.log(` ${t.status.padEnd(12)} ${t.id} ${t.title}${e}`)}break}case`clear`:{let e=T(n);console.log(`Cleared ${e} completed/failed items from queue "${n}".`);break}case`delete`:{let e=Oe(n);console.log(e?`Queue "${n}" deleted.`:`Queue "${n}" not found.`);break}}}}],Q=[...It,...Ft,...ct,...Pt,...Lt,...Ot,...Ct,...Bt,...Dt,...zt,...q,...Et];Q.push({name:`help`,description:`Show available commands`,run:async()=>{$()}});async function Vt(e){let t=[...e],n=t.shift();if(!n||n===`--help`||n===`-h`){$();return}if(n===`--version`||n===`-v`){let e=m(f(h(import.meta.url)),`..`,`..`,`..`,`package.json`),t=JSON.parse(u(e,`utf-8`));console.log(t.version);return}if(n&&new Set([`--user`,`--workspace`,`--guide`,`--smart`]).has(n)){let e=Q.find(e=>e.name===`init`);if(e){await e.run([n,...t]);return}}let r=Q.find(e=>e.name===n);r||(console.error(`Unknown command: ${n}`),$(),process.exit(1));try{await r.run(t)}finally{let e=Nt();e&&await e.store.close()}}function $(){console.log(`@vpxa/aikit — Local-first AI developer toolkit
17
17
  `),console.log(`Usage: aikit <command> [options]
18
18
  `),console.log(`Commands:`);let e=Math.max(...Q.map(e=>e.name.length));for(let t of Q)console.log(` ${t.name.padEnd(e+2)}${t.description}`);console.log(``),console.log(`Options:`),console.log(` --help, -h Show this help`),console.log(` --version, -v Show version`)}export{Vt as run};
@@ -1,4 +1,4 @@
1
- import{a as e,i as t,n,o as r,r as i,t as a}from"./templates-Bdyt_d_F.js";import{c as o,l as s,n as c,o as l,r as u,t as d}from"./scaffold--Z8D_CTu.js";import{appendFileSync as f,existsSync as p,mkdirSync as m,readFileSync as h,unlinkSync as g,writeFileSync as _}from"node:fs";import{basename as v,dirname as y,join as b,resolve as x}from"node:path";import{fileURLToPath as S}from"node:url";import{AIKIT_PATHS as C,EMBEDDING_DEFAULTS as w,isUserInstalled as T}from"../../core/dist/index.js";function E(e){return p(x(e,`.cursor`))?`cursor`:p(x(e,`.claude`))?`claude-code`:p(x(e,`.windsurf`))?`windsurf`:p(x(e,`.zed`))?`zed`:p(x(e,`.idea`))?`intellij`:`copilot`}function D(e){let t=[];return p(x(e,`.cursor`))&&t.push(`cursor`),(p(x(e,`.claude`))||p(x(e,`CLAUDE.md`)))&&t.push(`claude-code`),p(x(e,`.windsurf`))&&t.push(`windsurf`),p(x(e,`.zed`))&&t.push(`zed`),p(x(e,`.idea`))&&t.push(`intellij`),p(x(e,`.gemini`))&&t.push(`gemini-cli`),(p(x(e,`.codex`))||p(x(e,`codex.md`)))&&t.push(`codex-cli`),t.push(`copilot`),[...new Set(t)]}function O(e){return{servers:{[e]:{...t}}}}function k(e){let{type:n,...r}=t;return{mcpServers:{[e]:r}}}function A(e){let{type:n,...r}=t;return{context_servers:{[e]:{...r}}}}const j={scaffoldDir:`general`,writeMcpConfig(e,t){let n=x(e,`.vscode`),r=x(n,`mcp.json`);p(r)||(m(n,{recursive:!0}),_(r,`${JSON.stringify(O(t),null,2)}\n`,`utf-8`),console.log(` Created .vscode/mcp.json`))},writeInstructions(e,t){let r=x(e,`.github`),i=x(r,`copilot-instructions.md`);m(r,{recursive:!0}),_(i,n(v(e),t),`utf-8`),console.log(` Updated .github/copilot-instructions.md`)},writeAgentsMd(e,t){_(x(e,`AGENTS.md`),a(v(e),t),`utf-8`),console.log(` Updated AGENTS.md`)}},M={scaffoldDir:`general`,writeMcpConfig(e,t){let n=x(e,`.mcp.json`);p(n)||(_(n,`${JSON.stringify(k(t),null,2)}\n`,`utf-8`),console.log(` Created .mcp.json`))},writeInstructions(e,t){let r=x(e,`CLAUDE.md`),i=v(e);_(r,`${n(i,t)}\n---\n\n${a(i,t)}`,`utf-8`),console.log(` Updated CLAUDE.md`)},writeAgentsMd(e,t){}},N={scaffoldDir:`general`,writeMcpConfig(e,t){let n=x(e,`.cursor`),r=x(n,`mcp.json`);p(r)||(m(n,{recursive:!0}),_(r,`${JSON.stringify(k(t),null,2)}\n`,`utf-8`),console.log(` Created .cursor/mcp.json`))},writeInstructions(e,t){let r=x(e,`.cursor`,`rules`),i=x(r,`aikit.mdc`);m(r,{recursive:!0});let o=v(e);_(i,`${n(o,t)}\n---\n\n${a(o,t)}`,`utf-8`),console.log(` Updated .cursor/rules/aikit.mdc`);let s=x(r,`kb.mdc`);p(s)&&s!==i&&(g(s),console.log(` Removed legacy .cursor/rules/kb.mdc`))},writeAgentsMd(e,t){}},P={scaffoldDir:`general`,writeMcpConfig(e,t){let n=x(e,`.vscode`),r=x(n,`mcp.json`);p(r)||(m(n,{recursive:!0}),_(r,`${JSON.stringify(O(t),null,2)}\n`,`utf-8`),console.log(` Created .vscode/mcp.json (Windsurf-compatible)`))},writeInstructions(e,t){let r=x(e,`.windsurfrules`),i=v(e);_(r,`${n(i,t)}\n---\n\n${a(i,t)}`,`utf-8`),console.log(` Updated .windsurfrules`)},writeAgentsMd(e,t){}},F={scaffoldDir:`general`,writeMcpConfig(e,t){let n=x(e,`.zed`),r=x(n,`settings.json`);if(m(n,{recursive:!0}),!p(r))_(r,`${JSON.stringify(A(t),null,2)}\n`,`utf-8`),console.log(` Created .zed/settings.json`);else{let e;try{e=JSON.parse(h(r,`utf-8`))}catch{console.warn(` ⚠ .zed/settings.json contains invalid JSON — skipping MCP config merge`);return}e.context_servers?.[t]||(e.context_servers={...e.context_servers,...A(t).context_servers},_(r,`${JSON.stringify(e,null,2)}\n`,`utf-8`),console.log(` Updated .zed/settings.json with context_servers`))}},writeInstructions(e,t){let r=x(e,`.rules`),i=v(e);_(r,`${n(i,t)}\n---\n\n${a(i,t)}`,`utf-8`),console.log(` Updated .rules`)},writeAgentsMd(e,t){}},I={scaffoldDir:`general`,writeMcpConfig(e,t){let n=x(e,`mcp.json`);p(n)||(_(n,`${JSON.stringify(k(t),null,2)}\n`,`utf-8`),console.log(` Created mcp.json`))},writeInstructions(e,t){let r=x(e,`.aiassistant`,`rules`),i=x(r,`aikit.md`);m(r,{recursive:!0});let o=v(e);_(i,`${n(o,t)}\n---\n\n${a(o,t)}`,`utf-8`),console.log(` Updated .aiassistant/rules/aikit.md`)},writeAgentsMd(e,t){}};function L(e){switch(e){case`copilot`:return j;case`claude-code`:return M;case`cursor`:return N;case`windsurf`:return P;case`zed`:return F;case`intellij`:return I;case`gemini-cli`:case`codex-cli`:return j}}const R={serverName:e,sources:[{path:`.`,excludePatterns:[`**/node_modules/**`,`**/dist/**`,`**/build/**`,`**/.git/**`,`**/${C.data}/**`,`**/coverage/**`,`**/*.min.js`,`**/package-lock.json`,`**/pnpm-lock.yaml`]}],indexing:{chunkSize:1500,chunkOverlap:200,minChunkSize:100},embedding:{model:w.model,dimensions:w.dimensions},store:{backend:`sqlite-vec`,path:C.data},curated:{path:C.aiCurated}};function z(e,t){let n=x(e,`aikit.config.json`);return p(n)&&!t?(console.log(`aikit.config.json already exists. Use --force to overwrite.`),!1):(_(n,`${JSON.stringify(R,null,2)}\n`,`utf-8`),console.log(` Created aikit.config.json`),!0)}function B(e){let t=x(e,`.gitignore`),n=[{dir:`${C.data}/`,label:`AI Kit vector store`},{dir:`${C.state}/`,label:`AI Kit session state`},{dir:`${C.restorePoints}/`,label:`Restore points (codemod/rename undo snapshots)`},{dir:`${C.brainstorm}/`,label:`Brainstorming sessions`},{dir:`${C.handoffs}/`,label:`Handoff documents`}];if(p(t)){let e=h(t,`utf-8`),r=n.filter(t=>!e.includes(t.dir));r.length>0&&(f(t,`\n${r.map(e=>`# ${e.label}\n${e.dir}`).join(`
1
+ import{a as e,i as t,n,o as r,r as i,t as a}from"./templates-BXxDSq-3.js";import{c as o,l as s,n as c,o as l,r as u,t as d}from"./scaffold--Z8D_CTu.js";import{appendFileSync as f,existsSync as p,mkdirSync as m,readFileSync as h,unlinkSync as g,writeFileSync as _}from"node:fs";import{basename as v,dirname as y,join as b,resolve as x}from"node:path";import{fileURLToPath as S}from"node:url";import{AIKIT_PATHS as C,EMBEDDING_DEFAULTS as w,isUserInstalled as T}from"../../core/dist/index.js";function E(e){return p(x(e,`.cursor`))?`cursor`:p(x(e,`.claude`))?`claude-code`:p(x(e,`.windsurf`))?`windsurf`:p(x(e,`.zed`))?`zed`:p(x(e,`.idea`))?`intellij`:`copilot`}function D(e){let t=[];return p(x(e,`.cursor`))&&t.push(`cursor`),(p(x(e,`.claude`))||p(x(e,`CLAUDE.md`)))&&t.push(`claude-code`),p(x(e,`.windsurf`))&&t.push(`windsurf`),p(x(e,`.zed`))&&t.push(`zed`),p(x(e,`.idea`))&&t.push(`intellij`),p(x(e,`.gemini`))&&t.push(`gemini-cli`),(p(x(e,`.codex`))||p(x(e,`codex.md`)))&&t.push(`codex-cli`),t.push(`copilot`),[...new Set(t)]}function O(e){return{servers:{[e]:{...t}}}}function k(e){let{type:n,...r}=t;return{mcpServers:{[e]:r}}}function A(e){let{type:n,...r}=t;return{context_servers:{[e]:{...r}}}}const j={scaffoldDir:`general`,writeMcpConfig(e,t){let n=x(e,`.vscode`),r=x(n,`mcp.json`);p(r)||(m(n,{recursive:!0}),_(r,`${JSON.stringify(O(t),null,2)}\n`,`utf-8`),console.log(` Created .vscode/mcp.json`))},writeInstructions(e,t){let r=x(e,`.github`),i=x(r,`copilot-instructions.md`);m(r,{recursive:!0}),_(i,n(v(e),t),`utf-8`),console.log(` Updated .github/copilot-instructions.md`)},writeAgentsMd(e,t){_(x(e,`AGENTS.md`),a(v(e),t),`utf-8`),console.log(` Updated AGENTS.md`)}},M={scaffoldDir:`general`,writeMcpConfig(e,t){let n=x(e,`.mcp.json`);p(n)||(_(n,`${JSON.stringify(k(t),null,2)}\n`,`utf-8`),console.log(` Created .mcp.json`))},writeInstructions(e,t){let r=x(e,`CLAUDE.md`),i=v(e);_(r,`${n(i,t)}\n---\n\n${a(i,t)}`,`utf-8`),console.log(` Updated CLAUDE.md`)},writeAgentsMd(e,t){}},N={scaffoldDir:`general`,writeMcpConfig(e,t){let n=x(e,`.cursor`),r=x(n,`mcp.json`);p(r)||(m(n,{recursive:!0}),_(r,`${JSON.stringify(k(t),null,2)}\n`,`utf-8`),console.log(` Created .cursor/mcp.json`))},writeInstructions(e,t){let r=x(e,`.cursor`,`rules`),i=x(r,`aikit.mdc`);m(r,{recursive:!0});let o=v(e);_(i,`${n(o,t)}\n---\n\n${a(o,t)}`,`utf-8`),console.log(` Updated .cursor/rules/aikit.mdc`);let s=x(r,`kb.mdc`);p(s)&&s!==i&&(g(s),console.log(` Removed legacy .cursor/rules/kb.mdc`))},writeAgentsMd(e,t){}},P={scaffoldDir:`general`,writeMcpConfig(e,t){let n=x(e,`.vscode`),r=x(n,`mcp.json`);p(r)||(m(n,{recursive:!0}),_(r,`${JSON.stringify(O(t),null,2)}\n`,`utf-8`),console.log(` Created .vscode/mcp.json (Windsurf-compatible)`))},writeInstructions(e,t){let r=x(e,`.windsurfrules`),i=v(e);_(r,`${n(i,t)}\n---\n\n${a(i,t)}`,`utf-8`),console.log(` Updated .windsurfrules`)},writeAgentsMd(e,t){}},F={scaffoldDir:`general`,writeMcpConfig(e,t){let n=x(e,`.zed`),r=x(n,`settings.json`);if(m(n,{recursive:!0}),!p(r))_(r,`${JSON.stringify(A(t),null,2)}\n`,`utf-8`),console.log(` Created .zed/settings.json`);else{let e;try{e=JSON.parse(h(r,`utf-8`))}catch{console.warn(` ⚠ .zed/settings.json contains invalid JSON — skipping MCP config merge`);return}e.context_servers?.[t]||(e.context_servers={...e.context_servers,...A(t).context_servers},_(r,`${JSON.stringify(e,null,2)}\n`,`utf-8`),console.log(` Updated .zed/settings.json with context_servers`))}},writeInstructions(e,t){let r=x(e,`.rules`),i=v(e);_(r,`${n(i,t)}\n---\n\n${a(i,t)}`,`utf-8`),console.log(` Updated .rules`)},writeAgentsMd(e,t){}},I={scaffoldDir:`general`,writeMcpConfig(e,t){let n=x(e,`mcp.json`);p(n)||(_(n,`${JSON.stringify(k(t),null,2)}\n`,`utf-8`),console.log(` Created mcp.json`))},writeInstructions(e,t){let r=x(e,`.aiassistant`,`rules`),i=x(r,`aikit.md`);m(r,{recursive:!0});let o=v(e);_(i,`${n(o,t)}\n---\n\n${a(o,t)}`,`utf-8`),console.log(` Updated .aiassistant/rules/aikit.md`)},writeAgentsMd(e,t){}};function L(e){switch(e){case`copilot`:return j;case`claude-code`:return M;case`cursor`:return N;case`windsurf`:return P;case`zed`:return F;case`intellij`:return I;case`gemini-cli`:case`codex-cli`:return j}}const R={serverName:e,sources:[{path:`.`,excludePatterns:[`**/node_modules/**`,`**/dist/**`,`**/build/**`,`**/.git/**`,`**/${C.data}/**`,`**/coverage/**`,`**/*.min.js`,`**/package-lock.json`,`**/pnpm-lock.yaml`]}],indexing:{chunkSize:1500,chunkOverlap:200,minChunkSize:100},embedding:{model:w.model,dimensions:w.dimensions},store:{backend:`sqlite-vec`,path:C.data},curated:{path:C.aiCurated}};function z(e,t){let n=x(e,`aikit.config.json`);return p(n)&&!t?(console.log(`aikit.config.json already exists. Use --force to overwrite.`),!1):(_(n,`${JSON.stringify(R,null,2)}\n`,`utf-8`),console.log(` Created aikit.config.json`),!0)}function B(e){let t=x(e,`.gitignore`),n=[{dir:`${C.data}/`,label:`AI Kit vector store`},{dir:`${C.state}/`,label:`AI Kit session state`},{dir:`${C.restorePoints}/`,label:`Restore points (codemod/rename undo snapshots)`},{dir:`${C.brainstorm}/`,label:`Brainstorming sessions`},{dir:`${C.handoffs}/`,label:`Handoff documents`}];if(p(t)){let e=h(t,`utf-8`),r=n.filter(t=>!e.includes(t.dir));r.length>0&&(f(t,`\n${r.map(e=>`# ${e.label}\n${e.dir}`).join(`
2
2
  `)}\n`,`utf-8`),console.log(` Added ${r.map(e=>e.dir).join(`, `)} to .gitignore`))}else _(t,`${n.map(e=>`# ${e.label}\n${e.dir}`).join(`
3
3
  `)}\n`,`utf-8`),console.log(` Created .gitignore with AI Kit entries`)}function V(){return R.serverName}const H=[`decisions`,`patterns`,`conventions`,`troubleshooting`];function U(e){let t=x(e,`.ai`,`curated`);p(t)||(m(t,{recursive:!0}),console.log(` Created .ai/curated/`));for(let e of H){let n=x(t,e);p(n)||m(n,{recursive:!0})}console.log(` Created .ai/curated/{${H.join(`,`)}}/`)}function W(e){switch(e){case`zed`:return`zed`;case`intellij`:return`intellij`;case`claude-code`:return`claude-code`;case`gemini-cli`:return`gemini`;case`codex-cli`:return`codex`;default:return`copilot`}}function G(e){let t=e;for(let e=0;e<10;e++){try{let e=b(t,`package.json`);if(p(e)&&JSON.parse(h(e,`utf8`)).name===`@vpxa/aikit`)return t}catch{}let e=y(t);if(e===t)break;t=e}return x(e,`..`,`..`,`..`)}async function K(e){let t=process.cwd();if(!z(t,e.force))return;B(t);let n=V(),a=L(E(t));a.writeMcpConfig(t,n),a.writeInstructions(t,n),a.writeAgentsMd(t,n);let c=G(y(S(import.meta.url))),u=JSON.parse(h(x(c,`package.json`),`utf-8`)).version;await s(t,c,[...r],u,e.force),await l(t,c,[...i],u,e.force);let d=D(t),f=new Set;for(let n of d){let r=W(n);f.has(r)||(f.add(r),await o(t,c,r,u,e.force))}U(t),console.log(`
4
4
  AI Kit initialized! Next steps:`),console.log(` aikit reindex Index your codebase`),console.log(` aikit search Search indexed content`),console.log(` aikit serve Start MCP server for IDE integration`),T()&&console.log(`
@@ -1,4 +1,4 @@
1
- import{existsSync as e,readFileSync as t}from"node:fs";import{dirname as n,join as r,resolve as i}from"node:path";import{fileURLToPath as a}from"node:url";const o=[`const{execFileSync:x}=require('child_process')`,`const{renameSync:m}=require('fs')`,`const{join:j}=require('path')`,`const d=process.platform==='win32'?j(process.env.LOCALAPPDATA||'','npm-cache','_npx'):j(require('os').homedir(),'.npm','_npx')`,`const s={stdio:'inherit',shell:true}`,`try{x('npx',['-y','@vpxa/aikit','serve'],s)}catch(e){try{m(d,d+'_'+Date.now())}catch{};x('npx',['-y','@vpxa/aikit','serve'],s)}`].join(`;`),s=`aikit`,c={type:`stdio`,command:`node`,args:[`-e`,o]},l=[`aikit`,`brainstorming`,`multi-agents-development`,`session-handoff`,`requirements-clarity`,`lesson-learned`,`c4-architecture`,`adr-skill`,`present`,`frontend-design`,`react`,`typescript`,`docs`,`repo-access`],u=[`aikit-basic`,`aikit-advanced`,`_epilogue`],d={"chat.agentFilesLocations":{"~/.claude/agents":!1},"github.copilot.chat.copilotMemory.enabled":!0,"chat.customAgentInSubagent.enabled":!0,"chat.useNestedAgentsMdFiles":!0,"chat.useAgentSkills":!0,"github.copilot.chat.switchAgent.enabled":!0,"workbench.browser.enableChatTools":!0,"chat.mcp.apps.enabled":!0,"chat.instructionsFilesLocations":{"~/.copilot/instructions":!0,".github/instructions":!0}},f=/{\s*name:\s*'((?:\\.|[^'])*)',\s*rationale:\s*'((?:\\.|[^'])*)',\s*bitterLessonSafe:\s*(?:true|false),\s*}/g;let p=null;function m(a){let o=a;for(let i=0;i<10;i++){try{let n=r(o,`package.json`);if(e(n)&&JSON.parse(t(n,`utf8`)).name===`@vpxa/aikit`)return o}catch{}let i=n(o);if(i===o)break;o=i}return i(a,`..`,`..`,`..`,`..`)}function h(e){return e.replace(/\\'/g,`'`).replace(/\\n/g,`
1
+ import{existsSync as e,readFileSync as t}from"node:fs";import{dirname as n,join as r,resolve as i}from"node:path";import{fileURLToPath as a}from"node:url";const o=[`const{execSync:x}=require('child_process')`,`const{renameSync:m}=require('fs')`,`const{join:j}=require('path')`,`const d=process.platform==='win32'?j(process.env.LOCALAPPDATA||'','npm-cache','_npx'):j(require('os').homedir(),'.npm','_npx')`,`const c='npx -y @vpxa/aikit serve'`,`const s={stdio:'inherit'}`,`try{x(c,s)}catch(e){try{m(d,d+'_'+Date.now())}catch{};x(c,s)}`].join(`;`),s=`aikit`,c={type:`stdio`,command:`node`,args:[`-e`,o]},l=[`aikit`,`brainstorming`,`multi-agents-development`,`session-handoff`,`requirements-clarity`,`lesson-learned`,`c4-architecture`,`adr-skill`,`present`,`frontend-design`,`react`,`typescript`,`docs`,`repo-access`],u=[`aikit-basic`,`aikit-advanced`,`_epilogue`],d={"chat.agentFilesLocations":{"~/.claude/agents":!1},"github.copilot.chat.copilotMemory.enabled":!0,"chat.customAgentInSubagent.enabled":!0,"chat.useNestedAgentsMdFiles":!0,"chat.useAgentSkills":!0,"github.copilot.chat.switchAgent.enabled":!0,"workbench.browser.enableChatTools":!0,"chat.mcp.apps.enabled":!0,"chat.instructionsFilesLocations":{"~/.copilot/instructions":!0,".github/instructions":!0}},f=/{\s*name:\s*'((?:\\.|[^'])*)',\s*rationale:\s*'((?:\\.|[^'])*)',\s*bitterLessonSafe:\s*(?:true|false),\s*}/g;let p=null;function m(a){let o=a;for(let i=0;i<10;i++){try{let n=r(o,`package.json`);if(e(n)&&JSON.parse(t(n,`utf8`)).name===`@vpxa/aikit`)return o}catch{}let i=n(o);if(i===o)break;o=i}return i(a,`..`,`..`,`..`,`..`)}function h(e){return e.replace(/\\'/g,`'`).replace(/\\n/g,`
2
2
  `).replace(/\\r/g,`\r`).replace(/\\t/g,` `).replace(/\\\\/g,`\\`)}function g(){if(p)return p;let e=i(m(n(a(import.meta.url))),`scaffold`,`definitions`,`exclusions.mjs`),r=t(e,`utf8`),o=Array.from(r.matchAll(f),([,e,t])=>({name:h(e),rationale:h(t)}));if(o.length===0)throw Error(`Failed to parse permanent exclusions from ${e}`);return p=o,o}function _(e){return e.replace(/\|/g,`\\|`).replace(/\r?\n+/g,` `).trim()}function v(){return`## Permanent Exclusions
3
3
 
4
4
  These capabilities are intentionally NOT provided by aikit. Each decision follows the Bitter Lesson: leverage computation and configurable tools over hand-crafted features.
@@ -1,4 +1,4 @@
1
- import{t as e}from"./rolldown-runtime-BbypZo7q.js";import{a as t,i as n,n as r,s as i,t as a}from"./templates-Bdyt_d_F.js";import{d as o,f as s,i as c,s as l,u}from"./scaffold--Z8D_CTu.js";import{existsSync as d,mkdirSync as f,readFileSync as p,readdirSync as m,rmSync as ee,unlinkSync as h,writeFileSync as g}from"node:fs";import{dirname as _,join as v,posix as y,resolve as b,win32 as x}from"node:path";import{fileURLToPath as S}from"node:url";import{mkdir as C,readFile as w,rename as T,unlink as E,writeFile as D}from"node:fs/promises";import{randomUUID as O}from"node:crypto";import{homedir as k}from"node:os";import{execFileSync as A}from"node:child_process";import{getGlobalDataDir as j,saveRegistry as M}from"../../core/dist/index.js";function N(e){let t=``,n=0,r=e.length;for(;n<r;)if(e[n]===`"`){for(t+=`"`,n++;n<r&&e[n]!==`"`;)e[n]===`\\`&&(t+=e[n++]),n<r&&(t+=e[n++]);n<r&&(t+=e[n++])}else if(e[n]===`/`&&n+1<r&&e[n+1]===`/`)for(n+=2;n<r&&e[n]!==`
1
+ import{t as e}from"./rolldown-runtime-BbypZo7q.js";import{a as t,i as n,n as r,s as i,t as a}from"./templates-BXxDSq-3.js";import{d as o,f as s,i as c,s as l,u}from"./scaffold--Z8D_CTu.js";import{existsSync as d,mkdirSync as f,readFileSync as p,readdirSync as m,rmSync as ee,unlinkSync as h,writeFileSync as g}from"node:fs";import{dirname as _,join as v,posix as y,resolve as b,win32 as x}from"node:path";import{fileURLToPath as S}from"node:url";import{mkdir as C,readFile as w,rename as T,unlink as E,writeFile as D}from"node:fs/promises";import{randomUUID as O}from"node:crypto";import{homedir as k}from"node:os";import{execFileSync as A}from"node:child_process";import{getGlobalDataDir as j,saveRegistry as M}from"../../core/dist/index.js";function N(e){let t=``,n=0,r=e.length;for(;n<r;)if(e[n]===`"`){for(t+=`"`,n++;n<r&&e[n]!==`"`;)e[n]===`\\`&&(t+=e[n++]),n<r&&(t+=e[n++]);n<r&&(t+=e[n++])}else if(e[n]===`/`&&n+1<r&&e[n+1]===`/`)for(n+=2;n<r&&e[n]!==`
2
2
  `;)n++;else if(e[n]===`/`&&n+1<r&&e[n+1]===`*`){for(n+=2;n+1<r&&!(e[n]===`*`&&e[n+1]===`/`);)n++;n+=2}else t+=e[n++];return t.replace(/,(\s*[}\]])/g,`$1`)}var P=class{isPlatformSupported(){return this.platforms.includes(process.platform)}async readConfig(e){let t=this.getConfigPath(e);if(!d(t))return{};let n=await w(t,`utf-8`);try{return JSON.parse(N(n))}catch{throw Error(`Invalid JSON in ${t}. Please fix or remove the file before retrying.`)}}async writeConfig(e,t){let n=this.getConfigPath(t),r=_(n),i=v(r,`.aikit-tmp-${O()}.json`);await C(r,{recursive:!0});try{await D(i,`${JSON.stringify(e,null,2)}\n`,`utf-8`),await T(i,n)}catch(e){try{await E(i)}catch{}throw e}}async registerMcp(e,t,n){let r=await this.readConfig(n);r[this.configKey]||(r[this.configKey]={}),r[this.configKey][e]=t,await this.writeConfig(r,n)}async unregisterMcp(e,t){let n=await this.readConfig(t);n[this.configKey]&&(delete n[this.configKey][e],await this.writeConfig(n,t))}getScaffoldRoot(e){return null}getInstructionsRoot(e){return null}getScaffoldPaths(e){return{agents:null,skills:null,prompts:null,flows:null,commands:null,instructions:null,manifest:null}}buildInstructionContent(e,t){return`${e}\n---\n\n${t}`}},F=class extends P{id=`claude-code`;name=`Claude Code`;family=`claude`;platforms=[`win32`,`darwin`,`linux`];scope=`user`;configKey=`mcpServers`;async detect(){return this.isPlatformSupported()?d(this.getPathModule().resolve(k(),`.claude`)):!1}getConfigPath(){return this.getPathModule().resolve(k(),`.claude`,`mcp.json`)}getScaffoldRoot(){return this.getPathModule().resolve(k(),`.claude`)}getScaffoldPaths(){let e=this.getPathModule(),t=e.resolve(k(),`.claude`);return{agents:e.resolve(t,`agents`),skills:e.resolve(t,`skills`),prompts:null,flows:e.resolve(t,`flows`),commands:e.resolve(t,`commands`),instructions:e.resolve(t,`CLAUDE.md`),manifest:e.resolve(t,`.aikit-scaffold.json`)}}buildInstructionContent(e,t){return`${e}\n---\n\n${t}`}getPathModule(){return process.platform===`win32`?x:y}},I=class extends P{id=`codex-cli`;name=`Codex CLI`;family=`codex`;platforms=[`win32`,`darwin`,`linux`];scope=`user`;configKey=`mcpServers`;async detect(){return this.isPlatformSupported()?d(this.getPathModule().resolve(k(),`.codex`)):!1}getConfigPath(){return this.getPathModule().resolve(k(),`.codex`,`config.json`)}getScaffoldRoot(){return this.getPathModule().resolve(k(),`.codex`)}getScaffoldPaths(){let e=this.getPathModule(),t=e.resolve(k(),`.codex`);return{agents:e.resolve(t,`agents`),skills:e.resolve(t,`skills`),prompts:null,flows:e.resolve(t,`flows`),commands:null,instructions:e.resolve(t,`AGENTS.md`),manifest:e.resolve(t,`.aikit-scaffold.json`)}}getPathModule(){return process.platform===`win32`?x:y}},L=class extends P{family=`copilot`;platforms=[`win32`,`darwin`,`linux`];scope=`user`;hasInstructions=!1;async detect(){if(!this.isPlatformSupported())return!1;let e=this.getConfigDir();if(process.platform===`darwin`){let t=this.getMacAppPath();return d(e)||t!==null&&d(t)}return d(e)}getConfigPath(){return this.getPathModule().resolve(this.getConfigDir(),`mcp.json`)}getScaffoldRoot(){return this.getPathModule().resolve(k(),this.scaffoldBase)}getInstructionsRoot(){let e=this.getInstructionFilePath();return e?this.getPathModule().dirname(e):null}getScaffoldPaths(){let e=this.getPathModule(),t=e.resolve(k(),this.scaffoldBase);return{agents:e.resolve(t,`agents`),skills:e.resolve(t,`skills`),prompts:e.resolve(this.getConfigDir(),`prompts`),flows:e.resolve(t,`flows`),commands:null,instructions:this.getInstructionFilePath(),manifest:e.resolve(t,`.aikit-scaffold.json`)}}getConfigDir(){let e=this.getPathModule(),t=k();if(process.platform===`win32`){let n=process.env.APPDATA??e.resolve(t,`AppData`,`Roaming`);return e.resolve(n,this.configDirName,`User`)}if(process.platform===`darwin`)return e.resolve(t,`Library`,`Application Support`,this.configDirName,`User`);let n=process.env.XDG_CONFIG_HOME??e.resolve(t,`.config`);return e.resolve(n,this.configDirName,`User`)}getMacAppPath(){return null}getInstructionFilePath(){return null}getPathModule(){return process.platform===`win32`?x:y}},R=class extends P{id=`copilot-cli`;name=`Copilot CLI`;family=`copilot`;platforms=[`win32`,`darwin`,`linux`];scope=`user`;configKey=`mcpServers`;async detect(){return this.isPlatformSupported()?d(b(k(),`.copilot`)):!1}getConfigPath(){return b(k(),`.copilot`,`mcp-config.json`)}async registerMcp(e,t,n){let r={...t,tools:[`*`]},i=await this.readConfig(n);i[this.configKey]||(i[this.configKey]={}),i[this.configKey][e]=r,await this.writeConfig(i,n)}getScaffoldRoot(){return b(k(),`.copilot`)}getInstructionsRoot(){return null}getScaffoldPaths(){let e=b(k(),`.copilot`);return{agents:b(e,`agents`),skills:b(e,`skills`),prompts:null,flows:b(e,`flows`),commands:null,instructions:b(k(),`.github`,`copilot-instructions.md`),manifest:b(e,`.aikit-scaffold.json`)}}},z=class extends L{id=`cursor`;name=`Cursor`;configKey=`mcpServers`;configDirName=`Cursor`;scaffoldBase=`.cursor`;getMacAppPath(){return`/Applications/Cursor.app`}getInstructionFilePath(){return this.getPathModule().resolve(k(),this.scaffoldBase,`rules`,`aikit.mdc`)}},B=class extends L{id=`cursor-nightly`;name=`Cursor Nightly`;configKey=`mcpServers`;configDirName=`Cursor Nightly`;scaffoldBase=`.cursor`;getMacAppPath(){return`/Applications/Cursor Nightly.app`}getInstructionFilePath(){return this.getPathModule().resolve(k(),this.scaffoldBase,`rules`,`aikit.mdc`)}},V=class e extends P{id=`intellij`;name=`IntelliJ IDEA`;family=`copilot`;platforms=[`win32`,`darwin`,`linux`];scope=`user`;configKey=`servers`;static PRODUCTS=[`IntelliJIdea`,`IdeaIC`,`WebStorm`,`PyCharm`,`PyCharmCE`,`GoLand`,`PhpStorm`,`CLion`,`Rider`,`RubyMine`,`RustRover`,`DataGrip`];async detect(){if(!this.isPlatformSupported())return!1;if(d(this.getCopilotConfigDir()))return!0;let t=this.getJetBrainsBaseDir();if(!d(t))return!1;try{return m(t,{withFileTypes:!0}).some(t=>t.isDirectory()&&e.PRODUCTS.some(e=>t.name.startsWith(e)))}catch{return!1}}getConfigPath(){return b(this.getCopilotConfigDir(),`mcp.json`)}async registerMcp(e,t,n){let r=this.getCopilotConfigDir();await C(r,{recursive:!0});let i=t.args??[],a=i.indexOf(`-e`),o;if(a!==-1&&a+1<i.length){let t=i[a+1],n=b(r,`${e}-launcher.js`);await D(n,t,`utf-8`),o=[...i.slice(0,a),n,...i.slice(a+2)]}else o=[...i];let s={type:`stdio`,...t,args:o};await super.registerMcp(e,s,n)}getScaffoldRoot(e){return null}getInstructionsRoot(e){return null}getScaffoldPaths(e){return{agents:null,skills:null,prompts:null,flows:null,commands:null,instructions:null,manifest:null}}buildInstructionContent(e,t){return t}getCopilotConfigDir(){let e=k();return process.platform===`win32`?b(process.env.LOCALAPPDATA??b(e,`AppData`,`Local`),`github-copilot`,`intellij`):process.platform===`darwin`?b(e,`Library`,`Application Support`,`github-copilot`,`intellij`):b(process.env.XDG_CONFIG_HOME??b(e,`.config`),`github-copilot`,`intellij`)}getJetBrainsBaseDir(){let e=k();return process.platform===`win32`?b(process.env.APPDATA??b(e,`AppData`,`Roaming`),`JetBrains`):process.platform===`darwin`?b(e,`Library`,`Application Support`,`JetBrains`):b(process.env.XDG_CONFIG_HOME??b(e,`.config`),`JetBrains`)}},H=class extends L{id=`trae`;name=`Trae`;configKey=`servers`;configDirName=`Trae`;scaffoldBase=`.trae`;getMacAppPath(){return`/Applications/Trae.app`}getInstructionFilePath(){return this.getPathModule().resolve(k(),this.scaffoldBase,`rules`,`aikit.md`)}},U=class extends L{id=`vscode`;name=`VS Code`;configKey=`servers`;configDirName=`Code`;scaffoldBase=`.copilot`;hasInstructions=!0;getMacAppPath(){return`/Applications/Visual Studio Code.app`}getInstructionFilePath(){return this.hasInstructions?this.getPathModule().resolve(k(),this.scaffoldBase,`instructions`,`copilot-instructions.md`):null}buildInstructionContent(e,t){return`---\napplyTo: "**"\n---\n\n${e}\n---\n\n${t}`}},W=class extends L{id=`vscode-insiders`;name=`VS Code Insiders`;configKey=`servers`;configDirName=`Code - Insiders`;scaffoldBase=`.copilot`;hasInstructions=!0;getMacAppPath(){return`/Applications/Visual Studio Code - Insiders.app`}getInstructionFilePath(){return this.hasInstructions?this.getPathModule().resolve(k(),this.scaffoldBase,`instructions`,`copilot-instructions.md`):null}buildInstructionContent(e,t){return`---\napplyTo: "**"\n---\n\n${e}\n---\n\n${t}`}},G=class extends L{id=`vscodium`;name=`VSCodium`;configKey=`servers`;configDirName=`VSCodium`;scaffoldBase=`.copilot`;hasInstructions=!0;getMacAppPath(){return`/Applications/VSCodium.app`}getInstructionFilePath(){return this.hasInstructions?this.getPathModule().resolve(k(),this.scaffoldBase,`instructions`,`copilot-instructions.md`):null}buildInstructionContent(e,t){return`---\napplyTo: "**"\n---\n\n${e}\n---\n\n${t}`}},K=class extends L{id=`windsurf`;name=`Windsurf`;configKey=`servers`;configDirName=`Windsurf`;scaffoldBase=`.windsurf`;getMacAppPath(){return`/Applications/Windsurf.app`}getInstructionFilePath(){return this.getPathModule().resolve(k(),this.scaffoldBase,`rules`,`aikit.md`)}},q=class extends P{id=`gemini-cli`;name=`Gemini CLI`;family=`gemini`;platforms=[`win32`,`darwin`,`linux`];scope=`user`;configKey=`mcpServers`;async detect(){return this.isPlatformSupported()?d(this.getPathModule().resolve(k(),`.gemini`)):!1}getConfigPath(){return this.getPathModule().resolve(k(),`.gemini`,`settings.json`)}getScaffoldRoot(){return this.getPathModule().resolve(k(),`.gemini`)}getScaffoldPaths(){let e=this.getPathModule(),t=e.resolve(k(),`.gemini`);return{agents:e.resolve(t,`agents`),skills:e.resolve(t,`skills`),prompts:null,flows:e.resolve(t,`flows`),commands:null,instructions:e.resolve(t,`AGENTS.md`),manifest:e.resolve(t,`.aikit-scaffold.json`)}}getPathModule(){return process.platform===`win32`?x:y}},te=class extends P{id=`zed`;name=`Zed`;family=`zed`;platforms=[`win32`,`darwin`,`linux`];scope=`user`;configKey=`context_servers`;async detect(){return this.isPlatformSupported()?d(this.getConfigDir()):!1}getConfigPath(){return this.getPathModule().resolve(this.getConfigDir(),`settings.json`)}async registerMcp(e,t){let{type:n,...r}=t,i={...r,env:r.env??{}};await super.registerMcp(e,i)}getScaffoldRoot(){return this.getConfigDir()}getScaffoldPaths(){let e=this.getPathModule(),t=this.getConfigDir();return{agents:e.resolve(t,`prompts`),skills:null,prompts:e.resolve(t,`prompts`),flows:null,commands:null,instructions:null,manifest:e.resolve(t,`.aikit-scaffold.json`)}}getConfigDir(){let e=this.getPathModule();return process.platform===`win32`?e.resolve(process.env.APPDATA||k(),`Zed`):e.resolve(k(),`.config`,`zed`)}getPathModule(){return process.platform===`win32`?x:y}};const J=[new U,new W,new G,new z,new B,new K,new H,new R,new V,new F,new q,new I,new te];function ne(){return[...J]}async function Y(e){let t=e?.scope?J.filter(t=>t.scope===e.scope):J;return(await Promise.allSettled(t.map(async e=>await e.detect()?e:null))).flatMap(e=>e.status!==`fulfilled`||e.value===null?[]:[e.value])}var re=e({initUser:()=>oe,installGlobalScaffold:()=>$,resolveMcpServerEntry:()=>X,writeVscodeSettings:()=>Q});function ie(e){let t=e;for(let e=0;e<10;e++){try{let e=v(t,`package.json`);if(d(e)&&JSON.parse(p(e,`utf8`)).name===`@vpxa/aikit`)return t}catch{}let e=_(t);if(e===t)break;t=e}return b(e,`..`,`..`,`..`)}function X(){let e={command:n.command,args:n.args?[...n.args]:void 0,type:n.type};if(process.platform!==`win32`){let t=process.env.PATH;t&&(e.env={PATH:t});try{let t=A(`which`,[`node`],{encoding:`utf-8`,timeout:3e3}).trim();t&&d(t)&&(e.command=t)}catch{}}return e}const Z=new Set([`VS Code`,`VS Code Insiders`,`VSCodium`]);function Q(e,t=!1){if(!Z.has(e.name))return;let n=b(_(e.getConfigPath()),`settings.json`),r={};if(d(n))try{let e=p(n,`utf-8`);r=JSON.parse(e)}catch{console.log(` ${e.name}: skipped settings.json (invalid JSON)`);return}let a=!1;for(let[e,n]of Object.entries(i))if(typeof n==`object`&&n){let t=typeof r[e]==`object`&&r[e]!==null?r[e]:{},i={...t,...n};JSON.stringify(i)!==JSON.stringify(t)&&(r[e]=i,a=!0)}else (t||!(e in r))&&(r[e]=n,a=!0);a&&(g(n,`${JSON.stringify(r,null,2)}\n`,`utf-8`),console.log(` ${e.name}: updated settings.json`))}async function $(e,t,n,i,p=!1){let m=new Map;for(let e of t)e.getScaffoldRoot()!==null&&m.set(e.id,e);if(t.some(e=>Z.has(e.name))){let e=ne().find(e=>e.id===`copilot-cli`);e&&m.set(e.id,e)}if(m.size===0){console.log(` No IDEs with global scaffold support detected.`);return}let h=await c(e,`copilot`),v=new Map;for(let e of h){let t=e.path.indexOf(`/`);if(t===-1)continue;let n=e.path.substring(0,t);if(n!==`agents`&&n!==`prompts`)continue;let r=v.get(n)??[];r.push({path:e.path.substring(t+1),content:e.content}),v.set(n,r)}let y=await c(e,`skills`),x=new Set;for(let e of y){let t=e.path.indexOf(`/`);t!==-1&&x.add(e.path.substring(0,t))}let S=await c(e,`flows`),C=new Set;for(let e of S){let t=e.path.indexOf(`/`);t!==-1&&C.add(e.path.substring(0,t))}let w=await c(e,`claude-code`),T=new Set,E=new Set,D=new Set,O=(e,t,n)=>{let r=v.get(n);if(!e||!t||!r||r.length===0)return;let a=`${n}:${t}:${e}`;if(T.has(a))return;T.add(a);let c=o(t)??u(i);c.version=i,l(r,e,c,n,p),s(t,c),D.add(_(t))},k=(e,t,n,r)=>{if(!e||!t||r.length===0)return;let a=`${n}:${t}:${e}`;if(T.has(a))return;if(T.add(a),n===`flows`&&!E.has(e)){for(let n of C){let r=b(e,n,`skills`);d(r)&&(ee(r,{recursive:!0,force:!0}),console.log(` ${_(t)}: migrated ${n} flow to steps/ layout`))}E.add(e)}let c=o(t)??u(i);c.version=i,l(r,e,c,n,p),s(t,c),D.add(_(t))};for(let e of m.values()){let t=e.getScaffoldPaths();O(t.agents,t.manifest,`agents`),O(t.prompts,t.manifest,`prompts`),k(t.skills,t.manifest,`skills`,y),k(t.flows,t.manifest,`flows`,S),k(t.commands,t.manifest,`commands`,w)}for(let e of D)console.log(` ${e}: scaffold updated (${x.size} skills)`);let A=new Set,j=r(`aikit`,n),M=a(`aikit`,n);for(let e of m.values()){let t=e.getScaffoldPaths().instructions;!t||A.has(t)||(f(_(t),{recursive:!0}),g(t,e.buildInstructionContent(j,M),`utf-8`),A.add(t))}A.size>0&&console.log(` Instruction files: ${[...A].join(`, `)}`)}function ae(e){let t=[];for(let n of e){let e=n.getScaffoldRoot();if(e)if(Z.has(n.name)){let r=n.getInstructionsRoot()??e;t.push(b(r,`kb.instructions.md`)),t.push(b(r,`aikit.instructions.md`))}else n.name===`Cursor`||n.name===`Cursor Nightly`?t.push(b(e,`rules`,`kb.mdc`)):n.name===`Windsurf`&&t.push(b(e,`rules`,`kb.md`))}for(let e of t)d(e)&&(h(e),console.log(` Removed legacy file: ${e}`))}async function oe(e){let n=t,r=ie(_(S(import.meta.url))),i=JSON.parse(p(b(r,`package.json`),`utf-8`)).version;console.log(`Initializing @vpxa/aikit v${i}...\n`);let a=j();f(a,{recursive:!0}),console.log(` Global data store: ${a}`),M({version:1,workspaces:{}}),console.log(` Created registry.json`);let o=await Y({scope:`user`});if(o.length===0)console.log(`
3
3
  No supported IDEs detected. You can manually add the MCP server config.`);else{console.log(`\n Detected ${o.length} IDE(s):`);let t=X();for(let e of o)try{await e.registerMcp(n,t),console.log(` ${e.name}: configured ${n}`)}catch(t){console.log(` ${e.name}: failed to configure (${t.message})`)}for(let t of o)Q(t,e.force)}console.log(`
4
4
  Installing scaffold files:`),await $(r,o,n,i,e.force),ae(o),console.log(`
@@ -3,6 +3,6 @@
3
3
  "command": "node",
4
4
  "args": [
5
5
  "-e",
6
- "const{execFileSync:x}=require('child_process');const{renameSync:m}=require('fs');const{join:j}=require('path');const d=process.platform==='win32'?j(process.env.LOCALAPPDATA||'','npm-cache','_npx'):j(require('os').homedir(),'.npm','_npx');const s={stdio:'inherit',shell:true};try{x('npx',['-y','@vpxa/aikit','serve'],s)}catch(e){try{m(d,d+'_'+Date.now())}catch{};x('npx',['-y','@vpxa/aikit','serve'],s)}"
6
+ "const{execSync:x}=require('child_process');const{renameSync:m}=require('fs');const{join:j}=require('path');const d=process.platform==='win32'?j(process.env.LOCALAPPDATA||'','npm-cache','_npx'):j(require('os').homedir(),'.npm','_npx');const c='npx -y @vpxa/aikit serve';const s={stdio:'inherit'};try{x(c,s)}catch(e){try{m(d,d+'_'+Date.now())}catch{};x(c,s)}"
7
7
  ]
8
8
  }
@@ -3,6 +3,6 @@
3
3
  "command": "node",
4
4
  "args": [
5
5
  "-e",
6
- "const{execFileSync:x}=require('child_process');const{renameSync:m}=require('fs');const{join:j}=require('path');const d=process.platform==='win32'?j(process.env.LOCALAPPDATA||'','npm-cache','_npx'):j(require('os').homedir(),'.npm','_npx');const s={stdio:'inherit',shell:true};try{x('npx',['-y','@vpxa/aikit','serve'],s)}catch(e){try{m(d,d+'_'+Date.now())}catch{};x('npx',['-y','@vpxa/aikit','serve'],s)}"
6
+ "const{execSync:x}=require('child_process');const{renameSync:m}=require('fs');const{join:j}=require('path');const d=process.platform==='win32'?j(process.env.LOCALAPPDATA||'','npm-cache','_npx'):j(require('os').homedir(),'.npm','_npx');const c='npx -y @vpxa/aikit serve';const s={stdio:'inherit'};try{x(c,s)}catch(e){try{m(d,d+'_'+Date.now())}catch{};x(c,s)}"
7
7
  ]
8
8
  }
@@ -4082,6 +4082,93 @@ C4Deployment
4082
4082
  Rel(api, db, "Reads/writes", "JDBC")
4083
4083
  \`\`\`
4084
4084
 
4085
+ ## Layer Architecture Diagrams
4086
+
4087
+ Layer diagrams show how modules group into architectural layers and expose cross-layer dependencies - especially violations (upward or skip-layer calls).
4088
+
4089
+ ### Layer Map Diagram
4090
+
4091
+ Use Mermaid \`graph TD\` with subgraphs. Each layer is a subgraph, color-coded:
4092
+
4093
+ | Layer | Color | Stroke |
4094
+ |-------|-------|--------|
4095
+ | Presentation | \`#0e1a27\` fill | \`#22d3ee\` |
4096
+ | Business / Domain | \`#0e1a27\` fill | \`#34d399\` |
4097
+ | Data / Persistence | \`#0e1a27\` fill | \`#a78bfa\` |
4098
+ | Infrastructure | \`#0e1a27\` fill | \`#fbbf24\` |
4099
+ | Cross-cutting | \`#0e1a27\` fill | \`#fb923c\` |
4100
+
4101
+ **Example:**
4102
+
4103
+ \`\`\`mermaid
4104
+ graph TD
4105
+ subgraph Presentation["Presentation Layer"]
4106
+ style Presentation fill:#0e1a27,stroke:#22d3ee
4107
+ Pages["pages/"]
4108
+ Components["components/"]
4109
+ end
4110
+ subgraph Business["Business Layer"]
4111
+ style Business fill:#0e1a27,stroke:#34d399
4112
+ Services["services/"]
4113
+ UseCases["use-cases/"]
4114
+ end
4115
+ subgraph Data["Data Layer"]
4116
+ style Data fill:#0e1a27,stroke:#a78bfa
4117
+ Repos["repositories/"]
4118
+ Models["models/"]
4119
+ end
4120
+
4121
+ Pages --> Services
4122
+ Services --> Repos
4123
+ Components -.->|violation| Models
4124
+ \`\`\`
4125
+
4126
+ **Violation highlighting**: Use dotted lines with \`|violation|\` labels for cross-layer violations (e.g., Presentation accessing Data directly). Optionally use \`linkStyle\` with \`stroke:red,stroke-dasharray:5\` for emphasis.
4127
+
4128
+ ### Layer Detection Workflow
4129
+
4130
+ 1. \`analyze({ aspect: "structure", path: "." })\` - get directory tree
4131
+ 2. Detect framework (see Framework Layer Heuristics below)
4132
+ 3. Apply directory-to-layer mapping based on framework heuristics
4133
+ 4. \`analyze({ aspect: "dependencies", path: "." })\` - get import graph
4134
+ 5. Identify cross-layer dependencies - flag violations
4135
+ 6. Generate Mermaid diagram with subgraphs
4136
+
4137
+ ### Generic Layer Patterns
4138
+
4139
+ When no framework is detected, use these directory heuristics:
4140
+
4141
+ | Directory Pattern | Layer |
4142
+ |-------------------|-------|
4143
+ | \`pages/\`, \`views/\`, \`screens/\`, \`ui/\`, \`components/\` | Presentation |
4144
+ | \`services/\`, \`use-cases/\`, \`domain/\`, \`logic/\`, \`core/\` | Business |
4145
+ | \`models/\`, \`entities/\`, \`repositories/\`, \`db/\`, \`data/\` | Data |
4146
+ | \`infra/\`, \`config/\`, \`deploy/\`, \`docker/\`, \`k8s/\` | Infrastructure |
4147
+ | \`middleware/\`, \`utils/\`, \`shared/\`, \`common/\`, \`lib/\` | Cross-cutting |
4148
+
4149
+ ### Framework Layer Heuristics
4150
+
4151
+ Detect the framework from \`package.json\`, \`go.mod\`, \`requirements.txt\`, etc., then apply framework-specific layer assignments:
4152
+
4153
+ | Framework | Detection | Layer Assignments |
4154
+ |-----------|-----------|-------------------|
4155
+ | React / Next.js | \`react-dom\` or \`next\` in deps | \`pages/\` -> Presentation, \`components/\` -> Presentation, \`hooks/\` -> Business, \`api/\` -> Backend, \`lib/\` -> Shared |
4156
+ | Vue | \`vue\` in deps | \`views/\` -> Presentation, \`components/\` -> Presentation, \`composables/\` -> Business, \`stores/\` -> State |
4157
+ | Express | \`express\` in deps | \`routes/\` -> API, \`controllers/\` -> Business, \`middleware/\` -> Cross-cutting, \`models/\` -> Data, \`services/\` -> Business |
4158
+ | Go / Gin | \`gin-gonic\` in go.mod | \`cmd/\` -> Entry, \`internal/\` -> Business, \`pkg/\` -> Shared, \`handlers/\` -> API, \`middleware/\` -> Cross-cutting |
4159
+ | Django | \`django\` in requirements | \`views.py\` -> API, \`models.py\` -> Data, \`serializers.py\` -> Transform, \`urls.py\` -> Routing, \`services/\` -> Business |
4160
+ | Flask | \`flask\` in requirements | \`routes/\` -> API, \`models/\` -> Data, \`services/\` -> Business, \`blueprints/\` -> Modular |
4161
+ | Spring Boot | \`spring-boot\` in pom/gradle | \`controllers/\` -> API, \`services/\` -> Business, \`repositories/\` -> Data, \`entities/\` -> Domain |
4162
+ | Rails | \`rails\` in Gemfile | \`controllers/\` -> API, \`models/\` -> Data, \`services/\` -> Business, \`views/\` -> Presentation |
4163
+
4164
+ **Workflow for framework-aware layer diagrams:**
4165
+
4166
+ 1. Detect framework from manifest files (\`package.json\`, \`go.mod\`, etc.)
4167
+ 2. Look up the framework's layer assignment table above
4168
+ 3. Map each source directory to its assigned layer
4169
+ 4. Generate the Layer Map Diagram using the mapped subgraphs
4170
+ 5. Flag any imports that cross more than one layer boundary as violations
4171
+
4085
4172
  ## HTML/SVG Format
4086
4173
 
4087
4174
  For HTML/SVG output, use the design system and template from the references.
@@ -4283,6 +4370,313 @@ Write architecture documentation to \`docs/architecture/\` with naming conventio
4283
4370
  | Developers | All levels as needed |
4284
4371
  | DevOps | Container + Deployment |
4285
4372
 
4373
+ ## Interactive Architecture Diagrams (React Flow)
4374
+
4375
+ When HTML output is requested and the architecture is complex (>10 nodes, multiple layers, or interactive exploration needed), generate a standalone HTML file using React Flow via CDN. This produces zoomable, pannable, collapsible diagrams superior to static SVG.
4376
+
4377
+ ### Standalone HTML Template
4378
+
4379
+ \`\`\`html
4380
+ <!DOCTYPE html>
4381
+ <html lang="en">
4382
+ <head>
4383
+ <meta charset="UTF-8"/>
4384
+ <title>{{DIAGRAM_TITLE}} — C4 Architecture</title>
4385
+ <script src="https://unpkg.com/react@18/umd/react.production.min.js"><\/script>
4386
+ <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"><\/script>
4387
+ <script src="https://unpkg.com/@xyflow/react@12/dist/umd/index.js"><\/script>
4388
+ <script src="https://unpkg.com/elkjs@0.9/lib/elk.bundled.js"><\/script>
4389
+ <link rel="stylesheet" href="https://unpkg.com/@xyflow/react@12/dist/style.css"/>
4390
+ <style>
4391
+ body { margin: 0; font-family: system-ui, sans-serif; }
4392
+ .react-flow { width: 100vw; height: 100vh; }
4393
+ .c4-node { padding: 12px 16px; border-radius: 8px; border: 2px solid; min-width: 160px; }
4394
+ .c4-node-label { font-weight: 600; font-size: 14px; }
4395
+ .c4-node-tech { font-size: 11px; opacity: 0.7; margin-top: 4px; }
4396
+ .c4-node-desc { font-size: 12px; margin-top: 6px; max-width: 200px; }
4397
+ .c4-boundary { border: 2px dashed #64748b; border-radius: 12px; padding: 24px; background: rgba(100,116,139,0.05); }
4398
+ </style>
4399
+ </head>
4400
+ <body>
4401
+ <div id="root"></div>
4402
+ <script>
4403
+ // Node data — replace with generated architecture
4404
+ const nodes = {{NODES_JSON}};
4405
+ const edges = {{EDGES_JSON}};
4406
+ // ELK layout then render — see layout section below
4407
+ <\/script>
4408
+ </body>
4409
+ </html>
4410
+ \`\`\`
4411
+
4412
+ ### Custom Node Types by C4 Category
4413
+
4414
+ \`\`\`javascript
4415
+ const nodeTypes = {
4416
+ person: { shape: 'circle', color: '#60a5fa', icon: '👤' },
4417
+ system: { shape: 'rect-lg', color: '#a78bfa', border: 'solid' },
4418
+ container: { shape: 'rect-md', color: '#34d399', border: 'solid' },
4419
+ component: { shape: 'rect-sm', color: '#22d3ee', border: 'solid' },
4420
+ boundary: { shape: 'group', color: '#64748b', border: 'dashed' },
4421
+ database: { shape: 'cylinder', color: '#a78bfa', icon: '🗄️' },
4422
+ queue: { shape: 'rect-md', color: '#fb923c', icon: '📨' },
4423
+ external: { shape: 'rect-md', color: '#94a3b8', border: 'dotted' },
4424
+ };
4425
+ // Category colors match HTML/SVG design system above
4426
+ \`\`\`
4427
+
4428
+ ### ELK.js Hierarchical Layout
4429
+
4430
+ \`\`\`javascript
4431
+ const elkOptions = {
4432
+ 'elk.algorithm': 'layered',
4433
+ 'elk.direction': 'DOWN', // TOP → BOTTOM for C4
4434
+ 'elk.spacing.nodeNode': '50',
4435
+ 'elk.layered.spacing.nodeNodeBetweenLayers': '80',
4436
+ 'elk.edgeRouting': 'ORTHOGONAL',
4437
+ 'elk.layered.crossingMinimization.strategy': 'LAYER_SWEEP',
4438
+ 'elk.hierarchyHandling': 'INCLUDE_CHILDREN', // for boundaries/groups
4439
+ };
4440
+
4441
+ async function layoutWithElk(nodes, edges) {
4442
+ const elk = new ELK();
4443
+ const graph = {
4444
+ id: 'root',
4445
+ children: nodes.map(n => ({
4446
+ id: n.id, width: n.width || 200, height: n.height || 80,
4447
+ ...(n.parentNode && { parentId: n.parentNode }),
4448
+ })),
4449
+ edges: edges.map(e => ({ id: e.id, sources: [e.source], targets: [e.target] })),
4450
+ };
4451
+ const layout = await elk.layout(graph, { layoutOptions: elkOptions });
4452
+ return layout.children.map(child => ({
4453
+ ...nodes.find(n => n.id === child.id),
4454
+ position: { x: child.x, y: child.y },
4455
+ }));
4456
+ }
4457
+ \`\`\`
4458
+
4459
+ ### SVG Export from React Flow
4460
+
4461
+ When static SVG is needed from an interactive diagram (for embedding in markdown docs or printing):
4462
+
4463
+ \`\`\`javascript
4464
+ function exportToSVG(nodes, edges, options = {}) {
4465
+ const { padding = 20, fontSize = 12 } = options;
4466
+ const bounds = computeBoundingBox(nodes, padding);
4467
+ const svgParts = [
4468
+ \\\`<svg xmlns="http://www.w3.org/2000/svg" viewBox="\${bounds.viewBox}">\\\`,
4469
+ '<defs><marker id="arrow" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto"><polygon points="0 0, 10 3.5, 0 7" fill="#64748b"/></marker></defs>',
4470
+ ];
4471
+ // Render boundary groups first (back layer)
4472
+ nodes.filter(n => n.type === 'boundary').forEach(n => {
4473
+ svgParts.push(\\\`<rect x="\${n.position.x}" y="\${n.position.y}" width="\${n.width}" height="\${n.height}" rx="12" fill="none" stroke="#64748b" stroke-dasharray="8 4" stroke-width="2"/>\\\`);
4474
+ svgParts.push(\\\`<text x="\${n.position.x + 12}" y="\${n.position.y + 20}" font-size="13" fill="#64748b">\${n.data.label}</text>\\\`);
4475
+ });
4476
+ // Render edges
4477
+ edges.forEach(e => {
4478
+ const source = nodes.find(n => n.id === e.source);
4479
+ const target = nodes.find(n => n.id === e.target);
4480
+ svgParts.push(\\\`<line x1="\${source.position.x + source.width/2}" y1="\${source.position.y + source.height}" x2="\${target.position.x + target.width/2}" y2="\${target.position.y}" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrow)"/>\\\`);
4481
+ if (e.label) svgParts.push(\\\`<text x="\${(source.position.x + target.position.x)/2 + 60}" y="\${(source.position.y + target.position.y)/2 + 40}" font-size="11" fill="#94a3b8">\${e.label}</text>\\\`);
4482
+ });
4483
+ // Render nodes
4484
+ nodes.filter(n => n.type !== 'boundary').forEach(n => {
4485
+ const color = nodeTypes[n.type]?.color || '#64748b';
4486
+ svgParts.push(\\\`<rect x="\${n.position.x}" y="\${n.position.y}" width="\${n.width || 200}" height="\${n.height || 80}" rx="8" fill="\${color}22" stroke="\${color}" stroke-width="2"/>\\\`);
4487
+ svgParts.push(\\\`<text x="\${n.position.x + 12}" y="\${n.position.y + 24}" font-size="\${fontSize}" font-weight="600" fill="#e2e8f0">\${n.data.label}</text>\\\`);
4488
+ if (n.data.technology) svgParts.push(\\\`<text x="\${n.position.x + 12}" y="\${n.position.y + 42}" font-size="11" fill="#94a3b8">[\${n.data.technology}]</text>\\\`);
4489
+ if (n.data.description) svgParts.push(\\\`<text x="\${n.position.x + 12}" y="\${n.position.y + 60}" font-size="11" fill="#cbd5e1">\${n.data.description}</text>\\\`);
4490
+ });
4491
+ svgParts.push('</svg>');
4492
+ return svgParts.join('\\n');
4493
+ }
4494
+ \`\`\`
4495
+
4496
+ ### When to Use React Flow vs Static SVG
4497
+
4498
+ | Scenario | Output | Rationale |
4499
+ |----------|--------|-----------|
4500
+ | >10 components | React Flow HTML | Needs zoom/pan for readability |
4501
+ | Multi-layer (≥3 layers) | React Flow HTML | ELK layout handles crossing minimization |
4502
+ | Exploration/onboarding | React Flow HTML | Interactive collapse/expand aids learning |
4503
+ | Markdown embedding | Static SVG export | Inline in \`.md\` files |
4504
+ | Print/PDF | Static SVG export | Fixed layout, no JS needed |
4505
+ | Simple (≤5 components) | Static SVG directly | Overhead of React Flow unnecessary |
4506
+ | CI/automated docs | Static SVG export | Deterministic, no browser needed |
4507
+
4508
+ ## Architecture Generation Prompts
4509
+
4510
+ Structured LLM prompt templates for generating high-quality architecture documentation. Each prompt produces strict JSON output that feeds into diagram generation.
4511
+
4512
+ ### Layer Detection Prompt
4513
+
4514
+ \`\`\`
4515
+ You are a software architecture analyst. Given the following list of file paths from a codebase, identify the architectural layers present.
4516
+
4517
+ **File paths:**
4518
+ {{FILE_PATHS}}
4519
+
4520
+ **Framework context (if detected):**
4521
+ {{FRAMEWORK_CONTEXT}}
4522
+
4523
+ **Instructions:**
4524
+ - Identify 3-7 distinct architectural layers
4525
+ - Each layer must have a clear single responsibility
4526
+ - Assign file path glob patterns to each layer
4527
+ - If a file doesn't fit any layer, assign it to "Core" or "Shared"
4528
+
4529
+ **Respond ONLY with a JSON object in this exact format, no additional text:**
4530
+ {
4531
+ "layers": [
4532
+ {
4533
+ "id": "layer-id-slug",
4534
+ "name": "Human Readable Name",
4535
+ "description": "Single sentence describing layer responsibility",
4536
+ "filePatterns": ["src/api/**", "src/routes/**"],
4537
+ "color": "#hex-color"
4538
+ }
4539
+ ],
4540
+ "unmatched": ["paths/that/dont/fit/**"]
4541
+ }
4542
+ \`\`\`
4543
+
4544
+ ### Component Analysis Prompt
4545
+
4546
+ \`\`\`
4547
+ You are a software architecture analyst. Analyze this source file and extract its architectural role.
4548
+
4549
+ **File:** {{FILE_PATH}}
4550
+ **Content:**
4551
+ {{FILE_CONTENT}}
4552
+
4553
+ **Project context:**
4554
+ {{PROJECT_SUMMARY}}
4555
+
4556
+ **Respond ONLY with a JSON object:**
4557
+ {
4558
+ "summary": "One sentence: what this file does",
4559
+ "technology": "Primary technology/framework used",
4560
+ "category": "frontend|backend|data|infrastructure|messaging|security|external|monitoring",
4561
+ "layer": "Which architectural layer this belongs to",
4562
+ "dependencies": ["module-ids this file imports from"],
4563
+ "exposes": ["exported functions/classes/types"],
4564
+ "complexity": "low|medium|high",
4565
+ "c4Type": "container|component"
4566
+ }
4567
+ \`\`\`
4568
+
4569
+ ### Architecture Synthesis Prompt
4570
+
4571
+ \`\`\`
4572
+ You are a C4 architecture diagram generator. Given analyzed components, their relationships, and layer assignments, produce a complete C4 diagram specification.
4573
+
4574
+ **Components:**
4575
+ {{COMPONENTS_JSON}}
4576
+
4577
+ **Relationships (import edges):**
4578
+ {{EDGES_JSON}}
4579
+
4580
+ **Layers:**
4581
+ {{LAYERS_JSON}}
4582
+
4583
+ **Diagram level:** {{LEVEL}} (context|container|component)
4584
+
4585
+ **Instructions:**
4586
+ - Group components into their layers as boundaries
4587
+ - Identify the most important relationships (max 3 per component)
4588
+ - Label relationships with the interaction type (calls, subscribes, reads, writes)
4589
+ - External systems go outside all boundaries
4590
+ - Users/actors are Person type
4591
+
4592
+ **Respond ONLY with a JSON object:**
4593
+ {
4594
+ "title": "Diagram title",
4595
+ "nodes": [
4596
+ { "id": "unique-id", "type": "person|system|container|component|boundary|database|queue|external", "label": "Display Name", "technology": "Tech stack", "description": "Brief desc", "parent": "boundary-id-or-null", "layer": "layer-id" }
4597
+ ],
4598
+ "edges": [
4599
+ { "id": "edge-id", "source": "node-id", "target": "node-id", "label": "Relationship description", "style": "solid|dashed|dotted" }
4600
+ ],
4601
+ "layoutHints": { "direction": "DOWN|RIGHT", "groupByLayer": true }
4602
+ }
4603
+ \`\`\`
4604
+
4605
+ ### Framework Context Addendum Pattern
4606
+
4607
+ When a framework is detected, inject its addendum into the Layer Detection and Component Analysis prompts:
4608
+
4609
+ \`\`\`markdown
4610
+ ## {{FRAMEWORK}} Framework Addendum
4611
+ (Injected into architecture prompts when {{FRAMEWORK}} is detected)
4612
+
4613
+ ### Canonical File Roles
4614
+
4615
+ | Path Pattern | Role | C4 Type |
4616
+ |---|---|---|
4617
+ | {{pattern}} | {{role}} | {{type}} |
4618
+
4619
+ ### Architectural Layers
4620
+
4621
+ | Layer ID | Layer Name | What Goes Here |
4622
+ |---|---|---|
4623
+ | {{layer_id}} | {{name}} | {{description}} |
4624
+
4625
+ ### Edge Patterns
4626
+ - {{pattern 1: e.g., "pages/ → api/ via fetch/tRPC"}}
4627
+ - {{pattern 2: e.g., "services/ → repositories/ for data access"}}
4628
+ \`\`\`
4629
+
4630
+ **Addendum examples to create per framework:**
4631
+ - \`next.js\`: pages/app router → API routes → services → DB
4632
+ - \`express\`: routes → controllers → services → models
4633
+ - \`react-spa\`: pages → components → hooks → api-client → store
4634
+ - \`nestjs\`: modules → controllers → providers → entities
4635
+ - \`django\`: urls → views → serializers → models → managers
4636
+
4637
+ ## Architecture Documentation Pipeline
4638
+
4639
+ When generating comprehensive architecture documentation, orchestrate this phased approach:
4640
+
4641
+ ### Phase 1 — Scan (deterministic, no LLM)
4642
+ \`\`\`
4643
+ analyze({ aspect: "structure", path: "." }) # Directory tree
4644
+ analyze({ aspect: "dependencies", path: "." }) # Import graph
4645
+ analyze({ aspect: "entry_points", path: "." }) # Entry points (0 in-degree)
4646
+ graph({ action: "stats" }) # Graph size for complexity estimate
4647
+ \`\`\`
4648
+
4649
+ ### Phase 2 — Classify (LLM-assisted)
4650
+ 1. Apply **Layer Detection Prompt** with file paths from Phase 1
4651
+ 2. Detect framework from package.json/config files
4652
+ 3. Inject **Framework Addendum** for detected framework
4653
+ 4. Apply **Component Analysis Prompt** to key files (entry points + high fan-in nodes)
4654
+ 5. Output: \`layers.json\`, \`components.json\`
4655
+
4656
+ ### Phase 3 — Synthesize (LLM-assisted)
4657
+ 1. Apply **Architecture Synthesis Prompt** per C4 level:
4658
+ - System Context: external actors + top-level system boundary
4659
+ - Containers: runtime units (services, DBs, queues, frontends)
4660
+ - Components: internal modules within each container
4661
+ 2. Generate BOTH output formats:
4662
+ - **Mermaid** for \`docs/architecture/*.md\` files
4663
+ - **React Flow HTML** for interactive exploration
4664
+ 3. Output: diagram specs per level
4665
+
4666
+ ### Phase 4 — Layout & Render
4667
+ 1. Apply ELK.js layout to React Flow nodes
4668
+ 2. Generate static SVG exports for markdown embedding
4669
+ 3. Write files:
4670
+ - \`docs/architecture/c4-context.md\` (Mermaid)
4671
+ - \`docs/architecture/c4-containers.md\` (Mermaid)
4672
+ - \`docs/architecture/interactive/\` (HTML files)
4673
+
4674
+ ### Phase 5 — Validate
4675
+ \`\`\`
4676
+ check({}) # Typecheck (if TS)
4677
+ blast_radius({ files: ["docs/architecture/**"] }) # Verify scope
4678
+ \`\`\`
4679
+
4286
4680
  ## References
4287
4681
 
4288
4682
  - [references/c4-syntax.md](references/c4-syntax.md) - Complete Mermaid C4 syntax
@@ -5692,6 +6086,503 @@ Document implementation patterns for:
5692
6086
  - **Tools**: \`analyze({ aspect: "entry_points", ... })\` + \`analyze({ aspect: "patterns", ... })\`
5693
6087
  - **Output**: \`docs/guides/contributing.md\` or \`docs/architecture/overview.md\` extension section
5694
6088
 
6089
+ ## Guided Tour Generation
6090
+
6091
+ Generate dependency-ordered codebase walkthroughs that teach the system through its natural reading order. Tours live in \`docs/tours/\`.
6092
+
6093
+ ### Algorithm
6094
+
6095
+ 1. \`analyze({ aspect: "entry_points", path: "." })\` — find tour start points (0 in-degree modules)
6096
+ 2. \`graph({ action: "find_nodes", name_pattern: "src" })\` — get all module nodes
6097
+ 3. \`graph({ action: "neighbors", node_id: "<entry>", direction: "outgoing" })\` — build dependency graph
6098
+ 4. Topological sort on import edges — determine reading order
6099
+ 5. Group by depth/layer — create logical tour steps
6100
+ 6. Batch ≤ 3 related files per step — keep each step focused
6101
+ 7. Append concept/cross-cutting nodes as final steps
6102
+
6103
+ ### Tour Step Format
6104
+
6105
+ Each step in a tour:
6106
+
6107
+ \`\`\`markdown
6108
+ ## Step {order}: {title}
6109
+
6110
+ {description — what the reader will understand after this step}
6111
+
6112
+ **Files:**
6113
+ - \`{file1}\` — {role}
6114
+ - \`{file2}\` — {role}
6115
+
6116
+ **Key concepts:** {comma-separated concepts introduced here}
6117
+ **Depends on:** Steps {list of prerequisite step numbers}
6118
+ \`\`\`
6119
+
6120
+ ### Tour Generation Workflow
6121
+
6122
+ 1. Run the algorithm above to produce ordered step list
6123
+ 2. For each step, use \`compact({ path, query: "purpose and public API" })\` to extract key info
6124
+ 3. Write tour as \`docs/tours/{topic}.md\`
6125
+ 4. Generate \`docs/tours/index.md\` linking all tours with descriptions
6126
+
6127
+ ### Tour Index Template (\`docs/tours/index.md\`)
6128
+
6129
+ \`\`\`markdown
6130
+ # Codebase Tours
6131
+
6132
+ Guided walkthroughs ordered by dependency — read them in sequence to understand the system.
6133
+
6134
+ | Tour | Description | Steps | Audience |
6135
+ |------|-------------|-------|----------|
6136
+ | [Architecture](./architecture.md) | System structure and layers | N | New team members |
6137
+ | [Data Flow](./data-flow.md) | How data moves through the system | N | Backend developers |
6138
+ | [Feature: {name}](./feature-{name}.md) | End-to-end feature trace | N | Feature developers |
6139
+ \`\`\`
6140
+
6141
+ ### Directory Convention
6142
+
6143
+ \`\`\`
6144
+ docs/
6145
+ └── tours/
6146
+ ├── index.md # Tour listing with descriptions
6147
+ ├── architecture.md # System structure tour
6148
+ ├── data-flow.md # Data pipeline tour
6149
+ └── feature-{name}.md # Feature-specific tours
6150
+ \`\`\`
6151
+
6152
+ ## Business Domain Documentation
6153
+
6154
+ Document the business domain as a three-level hierarchy: **Domain → Flow → Step**. This captures what the system does in business terms, separate from its technical architecture.
6155
+
6156
+ ### Hierarchy
6157
+
6158
+ - **Domain**: A bounded business context (e.g., "Authentication", "Billing", "Inventory")
6159
+ - **Flow**: A business process within a domain (e.g., "User Login", "Invoice Generation")
6160
+ - **Step**: An atomic action within a flow (e.g., "Validate Credentials", "Generate PDF")
6161
+
6162
+ ### Domain Discovery Workflow
6163
+
6164
+ 1. \`analyze({ aspect: "structure", path: "." })\` — identify top-level domain boundaries (directories, modules)
6165
+ 2. \`analyze({ aspect: "entry_points", path: "." })\` — detect flow entry types (HTTP, CLI, event, cron, manual)
6166
+ 3. \`trace({ start: "<entry-point>", direction: "forward" })\` — map each flow's step sequence
6167
+ 4. \`graph({ action: "neighbors", node_id: "<domain-module>", direction: "both" })\` — find cross-domain interactions
6168
+
6169
+ ### Domain Template (\`docs/architecture/domains/{domain-name}.md\`)
6170
+
6171
+ \`\`\`markdown
6172
+ # Domain: {Name}
6173
+
6174
+ ## Overview
6175
+ {What this domain handles in business terms}
6176
+
6177
+ ## Entry Points
6178
+
6179
+ | Entry | Type | Trigger |
6180
+ |-------|------|---------|
6181
+ | \`{endpoint/command}\` | {http/cli/event/cron/manual} | {what triggers it} |
6182
+
6183
+ ## Entities
6184
+ - **{Entity}**: {description + key attributes}
6185
+
6186
+ ## Business Rules
6187
+ - {rule 1 — e.g., "Passwords must be hashed before storage"}
6188
+ - {rule 2}
6189
+
6190
+ ## Flows
6191
+
6192
+ ### {Flow Name}
6193
+ {What this flow accomplishes}
6194
+
6195
+ **Steps:**
6196
+ 1. {Step 1} — \`{file:function}\` — {what happens}
6197
+ 2. {Step 2} — \`{file:function}\` — {what happens}
6198
+ 3. {Step 3} — \`{file:function}\` — {what happens}
6199
+
6200
+ ### {Flow Name 2}
6201
+ ...
6202
+
6203
+ ## Cross-Domain Interactions
6204
+
6205
+ | This Domain | Direction | Other Domain | Mechanism |
6206
+ |-------------|-----------|--------------|-----------|
6207
+ | {current} | → calls | {other} | {API/event/import} |
6208
+ | {other} | → notifies | {current} | {event/callback} |
6209
+ \`\`\`
6210
+
6211
+ ### Directory Convention
6212
+
6213
+ \`\`\`
6214
+ docs/
6215
+ └── architecture/
6216
+ └── domains/
6217
+ ├── authentication.md
6218
+ ├── billing.md
6219
+ └── inventory.md
6220
+ \`\`\`
6221
+
6222
+ ## Interactive Tour Visualization (React Flow)
6223
+
6224
+ When HTML output is requested for tours or onboarding guides, generate a standalone React Flow HTML file showing the codebase tour as an interactive graph. Nodes represent files/modules, edges show dependencies AND tour progression order.
6225
+
6226
+ ### Tour Graph HTML Template
6227
+
6228
+ \`\`\`html
6229
+ <!DOCTYPE html>
6230
+ <html lang="en">
6231
+ <head>
6232
+ <meta charset="UTF-8"/>
6233
+ <title>{{TOUR_TITLE}} — Codebase Tour</title>
6234
+ <script src="https://unpkg.com/react@18/umd/react.production.min.js"><\/script>
6235
+ <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"><\/script>
6236
+ <script src="https://unpkg.com/@xyflow/react@12/dist/umd/index.js"><\/script>
6237
+ <script src="https://unpkg.com/elkjs@0.9/lib/elk.bundled.js"><\/script>
6238
+ <link rel="stylesheet" href="https://unpkg.com/@xyflow/react@12/dist/style.css"/>
6239
+ <style>
6240
+ body { margin: 0; font-family: system-ui, sans-serif; background: #0f172a; color: #e2e8f0; }
6241
+ .react-flow { width: 100vw; height: 100vh; }
6242
+ .tour-node { padding: 12px 16px; border-radius: 8px; border: 2px solid; min-width: 180px; cursor: pointer; transition: all 0.2s; }
6243
+ .tour-node:hover { transform: scale(1.05); box-shadow: 0 4px 12px rgba(0,0,0,0.3); }
6244
+ .tour-node.active { border-color: #22d3ee; box-shadow: 0 0 20px rgba(34,211,238,0.3); }
6245
+ .tour-node.visited { opacity: 0.7; }
6246
+ .tour-node.upcoming { opacity: 0.5; }
6247
+ .step-number { position: absolute; top: -10px; left: -10px; width: 24px; height: 24px; border-radius: 50%; background: #22d3ee; color: #0f172a; font-size: 12px; font-weight: 700; display: flex; align-items: center; justify-content: center; }
6248
+ .tour-panel { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: #1e293b; border: 1px solid #334155; border-radius: 12px; padding: 16px 24px; max-width: 600px; z-index: 10; }
6249
+ .tour-panel h3 { margin: 0 0 8px; color: #22d3ee; }
6250
+ .tour-panel p { margin: 0; font-size: 14px; line-height: 1.5; }
6251
+ .tour-nav { margin-top: 12px; display: flex; gap: 8px; }
6252
+ .tour-nav button { padding: 6px 16px; border-radius: 6px; border: 1px solid #475569; background: #334155; color: #e2e8f0; cursor: pointer; }
6253
+ .tour-nav button:hover { background: #475569; }
6254
+ .tour-nav button.primary { background: #22d3ee; color: #0f172a; border: none; }
6255
+ </style>
6256
+ </head>
6257
+ <body>
6258
+ <div id="root"></div>
6259
+ <script>
6260
+ const tourSteps = {{TOUR_STEPS_JSON}};
6261
+ const nodes = {{NODES_JSON}};
6262
+ const edges = {{EDGES_JSON}};
6263
+ // Tour logic: highlight active step, show explanation panel, navigate prev/next
6264
+ <\/script>
6265
+ </body>
6266
+ </html>
6267
+ \`\`\`
6268
+
6269
+ ### Tour Node Types
6270
+
6271
+ \`\`\`javascript
6272
+ const tourNodeTypes = {
6273
+ entryPoint: { color: '#22d3ee', icon: '🚀', label: 'Entry Point' },
6274
+ core: { color: '#34d399', icon: '⚙️', label: 'Core Module' },
6275
+ utility: { color: '#a78bfa', icon: '🔧', label: 'Utility' },
6276
+ config: { color: '#fbbf24', icon: '⚙️', label: 'Configuration' },
6277
+ test: { color: '#fb923c', icon: '🧪', label: 'Test' },
6278
+ external: { color: '#94a3b8', icon: '📦', label: 'External Dep' },
6279
+ };
6280
+ \`\`\`
6281
+
6282
+ ### Tour Edge Types
6283
+
6284
+ \`\`\`javascript
6285
+ const tourEdgeTypes = {
6286
+ dependency: { style: 'solid', color: '#475569', animated: false }, // import relationship
6287
+ tourPath: { style: 'solid', color: '#22d3ee', animated: true }, // tour progression
6288
+ dataFlow: { style: 'dashed', color: '#34d399', animated: false }, // data movement
6289
+ };
6290
+ \`\`\`
6291
+
6292
+ ### Tour Step Metadata
6293
+
6294
+ Each tour step node carries:
6295
+ \`\`\`json
6296
+ {
6297
+ "id": "step-1",
6298
+ "file": "src/index.ts",
6299
+ "title": "Application Entry Point",
6300
+ "explanation": "This is where the app bootstraps...",
6301
+ "learnsConcept": "Dependency injection setup",
6302
+ "stepNumber": 1,
6303
+ "layer": "entry",
6304
+ "linesOfInterest": [12, 45, 78],
6305
+ "duration": "2 min"
6306
+ }
6307
+ \`\`\`
6308
+
6309
+ ### SVG Export for Markdown Embedding
6310
+
6311
+ Same pattern as c4-architecture SVG export — iterate tour nodes and edges, generate static SVG showing tour path with numbered steps. Embed in \`docs/tours/*.md\` files.
6312
+
6313
+ ## Documentation Generation Prompts
6314
+
6315
+ Structured LLM prompt templates for generating high-quality documentation. Each produces strict JSON for consistent, parseable output.
6316
+
6317
+ ### Tour Generation Prompt (2-Phase)
6318
+
6319
+ **Phase 1** is deterministic (no LLM needed):
6320
+ \`\`\`
6321
+ # Deterministic Tour Ranking (run BEFORE LLM)
6322
+ 1. graph({ action: "find_nodes" }) → get all module nodes
6323
+ 2. Compute for each node:
6324
+ - in_degree (how many import it) → "importance"
6325
+ - out_degree (how many it imports) → "complexity"
6326
+ - centrality = in_degree / (in_degree + out_degree)
6327
+ 3. Rank by: entry_points FIRST, then by centrality DESC
6328
+ 4. Group by layer (from layer detection)
6329
+ 5. Topological sort within each layer
6330
+ 6. Output: ordered node list with rankings
6331
+ \`\`\`
6332
+
6333
+ **Phase 2** uses LLM with Phase 1 results as context:
6334
+ \`\`\`
6335
+ You are a developer onboarding guide. Given a ranked list of codebase modules with their relationships, generate a guided tour that teaches the system through progressive understanding.
6336
+
6337
+ **Module rankings (from static analysis):**
6338
+ {{RANKED_MODULES_JSON}}
6339
+
6340
+ **Dependency graph (edges):**
6341
+ {{EDGES_JSON}}
6342
+
6343
+ **Detected layers:**
6344
+ {{LAYERS_JSON}}
6345
+
6346
+ **Instructions:**
6347
+ - Create 8-15 tour steps (adjust to codebase size)
6348
+ - Start with entry points and high-level orchestration
6349
+ - Progress to increasingly detailed/specialized modules
6350
+ - Each step should teach ONE concept
6351
+ - Group related files into single steps (max 3 files per step)
6352
+ - End with cross-cutting concerns and extension points
6353
+ - Include estimated reading time per step
6354
+
6355
+ **Respond ONLY with a JSON object:**
6356
+ {
6357
+ "title": "Tour title",
6358
+ "description": "One sentence overview",
6359
+ "estimatedTime": "30 min",
6360
+ "steps": [
6361
+ {
6362
+ "stepNumber": 1,
6363
+ "title": "Step title — concept learned",
6364
+ "files": ["src/index.ts"],
6365
+ "explanation": "2-3 sentences explaining what this teaches",
6366
+ "learnsConcept": "Single concept name",
6367
+ "prerequisites": [],
6368
+ "linesOfInterest": { "src/index.ts": [12, 45] },
6369
+ "duration": "2 min"
6370
+ }
6371
+ ]
6372
+ }
6373
+ \`\`\`
6374
+
6375
+ ### File Documentation Prompt
6376
+
6377
+ \`\`\`
6378
+ You are a technical writer. Document this source file for developer reference.
6379
+
6380
+ **File:** {{FILE_PATH}}
6381
+ **Content:**
6382
+ {{FILE_CONTENT}}
6383
+
6384
+ **Module's role in architecture:** {{MODULE_ROLE}}
6385
+ **Layer:** {{LAYER}}
6386
+
6387
+ **Respond ONLY with a JSON object:**
6388
+ {
6389
+ "title": "Module name — one-line purpose",
6390
+ "overview": "2-3 sentence description of what this module does and why it exists",
6391
+ "publicAPI": [
6392
+ { "name": "functionName", "signature": "typed signature", "description": "what it does", "example": "brief usage" }
6393
+ ],
6394
+ "dependencies": [
6395
+ { "module": "import path", "purpose": "why this dependency" }
6396
+ ],
6397
+ "patterns": ["Pattern names used (e.g., Factory, Observer, Middleware)"],
6398
+ "gotchas": ["Non-obvious behaviors or edge cases"],
6399
+ "relatedModules": ["paths to related files for cross-reference"]
6400
+ }
6401
+ \`\`\`
6402
+
6403
+ ### Domain Documentation Prompt
6404
+
6405
+ \`\`\`
6406
+ You are a domain-driven design analyst. Given files belonging to a business domain, document the domain model.
6407
+
6408
+ **Domain:** {{DOMAIN_NAME}}
6409
+ **Files in this domain:**
6410
+ {{FILE_LIST}}
6411
+
6412
+ **Key file contents (summarized):**
6413
+ {{FILE_SUMMARIES}}
6414
+
6415
+ **Cross-domain relationships:**
6416
+ {{CROSS_DOMAIN_EDGES}}
6417
+
6418
+ **Respond ONLY with a JSON object:**
6419
+ {
6420
+ "domain": "Domain name",
6421
+ "description": "What business capability this domain handles",
6422
+ "ubiquitousLanguage": [
6423
+ { "term": "Term", "definition": "What it means in this context" }
6424
+ ],
6425
+ "entities": [
6426
+ { "name": "Entity", "description": "Role", "keyAttributes": ["attr1", "attr2"] }
6427
+ ],
6428
+ "businessRules": [
6429
+ "Rule 1 description",
6430
+ "Rule 2 description"
6431
+ ],
6432
+ "flows": [
6433
+ { "name": "Flow name", "steps": ["Step 1 → file:fn", "Step 2 → file:fn"], "trigger": "What initiates this" }
6434
+ ],
6435
+ "crossDomainInteractions": [
6436
+ { "direction": "outgoing|incoming", "otherDomain": "Name", "mechanism": "API/event/import", "purpose": "Why" }
6437
+ ]
6438
+ }
6439
+ \`\`\`
6440
+
6441
+ ## Documentation Generation Pipeline
6442
+
6443
+ Orchestrate comprehensive documentation generation using a phased approach (inspired by multi-agent pipeline patterns).
6444
+
6445
+ ### Phase 1 — Inventory (deterministic)
6446
+ \`\`\`
6447
+ # Scan existing docs and detect staleness
6448
+ find({ glob: "docs/**/*.md" }) # Current doc inventory
6449
+ git_context({ include_diff: true }) # Recent changes
6450
+ blast_radius({ files: changedFiles }) # What docs might be stale
6451
+ \`\`\`
6452
+
6453
+ ### Phase 2 — Analyze (deterministic + LLM)
6454
+ \`\`\`
6455
+ # Identify documentation gaps
6456
+ analyze({ aspect: "structure" }) # All modules
6457
+ analyze({ aspect: "entry_points" }) # Tour starting points
6458
+ graph({ action: "find_nodes" }) # Full module graph
6459
+ # For each undocumented module: apply File Documentation Prompt
6460
+ # For each domain cluster: apply Domain Documentation Prompt
6461
+ \`\`\`
6462
+
6463
+ ### Phase 3 — Detect Layers (LLM-assisted)
6464
+ \`\`\`
6465
+ # Classify architecture for tour and domain docs
6466
+ 1. Collect all file paths from structure analysis
6467
+ 2. Apply Layer Detection Prompt (from c4-architecture skill)
6468
+ 3. Inject Framework Addendum for detected framework
6469
+ 4. Output: layers.json used by tour generation
6470
+ \`\`\`
6471
+
6472
+ ### Phase 4 — Generate Tours (2-phase)
6473
+ \`\`\`
6474
+ # Phase 4a: Deterministic ranking
6475
+ 1. Compute node rankings (in-degree, centrality, layer)
6476
+ 2. Topological sort within layers
6477
+ 3. Identify tour start points (entry_points with 0 in-degree)
6478
+
6479
+ # Phase 4b: LLM narrative
6480
+ 1. Apply Tour Generation Prompt with rankings from 4a
6481
+ 2. Generate both Mermaid tour diagram AND React Flow HTML
6482
+ 3. Output: docs/tours/getting-started.md + docs/tours/interactive/
6483
+ \`\`\`
6484
+
6485
+ ### Phase 5 — Generate Domain Docs (LLM-assisted)
6486
+ \`\`\`
6487
+ # For each detected domain/bounded context:
6488
+ 1. Identify files belonging to domain (from layer/cluster analysis)
6489
+ 2. compact() key files for context
6490
+ 3. Apply Domain Documentation Prompt
6491
+ 4. Output: docs/architecture/domains/{domain}.md
6492
+ \`\`\`
6493
+
6494
+ ### Phase 6 — Validate & Cross-Reference
6495
+ \`\`\`
6496
+ # Ensure consistency
6497
+ 1. Check all internal doc links resolve
6498
+ 2. Verify code references still exist (file:line citations)
6499
+ 3. Cross-reference tour steps with actual files
6500
+ 4. check({}) to ensure no build breaks
6501
+ \`\`\`
6502
+
6503
+ ### Output Structure
6504
+ \`\`\`
6505
+ docs/
6506
+ ├── tours/
6507
+ │ ├── getting-started.md # Primary tour (Mermaid diagrams)
6508
+ │ ├── advanced-internals.md # Deep-dive tour
6509
+ │ └── interactive/
6510
+ │ ├── getting-started.html # React Flow interactive tour
6511
+ │ └── advanced-internals.html
6512
+ ├── architecture/
6513
+ │ ├── c4-context.md
6514
+ │ ├── c4-containers.md
6515
+ │ ├── interactive/
6516
+ │ │ ├── overview.html # React Flow architecture
6517
+ │ │ └── per-service.html
6518
+ │ └── domains/
6519
+ │ ├── authentication.md
6520
+ │ └── billing.md
6521
+ ├── reference/
6522
+ │ └── modules/ # Per-module API docs
6523
+ └── guides/
6524
+ └── contributing.md
6525
+ \`\`\`
6526
+
6527
+ ## Framework Context Injection
6528
+
6529
+ When generating documentation for a specific framework, inject framework-specific conventions into LLM prompts to improve generation quality:
6530
+
6531
+ ### Detection
6532
+ \`\`\`
6533
+ # Auto-detect framework from project files:
6534
+ - package.json → "next", "react", "express", "nestjs", "@angular/core"
6535
+ - requirements.txt → "django", "flask", "fastapi"
6536
+ - go.mod → "gin", "fiber", "echo"
6537
+ - Cargo.toml → "actix-web", "axum", "rocket"
6538
+ \`\`\`
6539
+
6540
+ ### Addendum Format
6541
+ \`\`\`markdown
6542
+ ## {{FRAMEWORK}} Documentation Addendum
6543
+ (Injected into documentation prompts when {{FRAMEWORK}} is detected)
6544
+
6545
+ ### Canonical Documentation Structure
6546
+
6547
+ | Path Pattern | Doc Type | Template |
6548
+ |---|---|---|
6549
+ | {{src_pattern}} | {{doc_type}} | {{which template to use}} |
6550
+
6551
+ ### Tour Conventions
6552
+
6553
+ - Tour starts at: {{entry_point_pattern}}
6554
+ - Key learning progression: {{progression}}
6555
+ - Framework-specific concepts to teach: {{concepts}}
6556
+
6557
+ ### Domain Conventions
6558
+
6559
+ - Where domains live: {{domain_path_pattern}}
6560
+ - How domains are bounded: {{boundary_mechanism}}
6561
+ \`\`\`
6562
+
6563
+ ### Example: Next.js Addendum
6564
+ \`\`\`
6565
+ ## Next.js Documentation Addendum
6566
+
6567
+ ### Canonical Documentation Structure
6568
+ | Path Pattern | Doc Type | Template |
6569
+ |---|---|---|
6570
+ | app/layout.tsx | Architecture | Root layout, providers, theme |
6571
+ | app/**/page.tsx | Feature docs | Page-level feature documentation |
6572
+ | app/api/** | API Reference | Endpoint documentation |
6573
+ | lib/** | Reference | Utility/service documentation |
6574
+ | components/** | Component docs | Props, usage examples, variants |
6575
+
6576
+ ### Tour Conventions
6577
+ - Tour starts at: app/layout.tsx (root) → app/page.tsx (home)
6578
+ - Key progression: Layout → Pages → API Routes → Lib → Components
6579
+ - Framework concepts: App Router, Server Components, Server Actions, Middleware
6580
+
6581
+ ### Domain Conventions
6582
+ - Domains live in: app/(domain)/ or features/(domain)/
6583
+ - Bounded by: Route groups or feature folders
6584
+ \`\`\`
6585
+
5695
6586
  ## Rules for the \`_docs-sync\` Epilogue Step
5696
6587
 
5697
6588
  When this skill is loaded during the \`_docs-sync\` epilogue: