@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 +1 -1
- package/packages/cli/dist/index.js +3 -3
- package/packages/cli/dist/{init-CEdDN3Lc.js → init-B8jFOU_T.js} +1 -1
- package/packages/cli/dist/{templates-Bdyt_d_F.js → templates-BXxDSq-3.js} +1 -1
- package/packages/cli/dist/{user-DVWGiGOO.js → user-Dz5HLUIS.js} +1 -1
- package/scaffold/definitions/mcp-entry.json +1 -1
- package/scaffold/dist/definitions/mcp-entry.json +1 -1
- package/scaffold/dist/definitions/skills.mjs +891 -0
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{a as e,i as t,n,t as r}from"./user-
|
|
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-
|
|
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-
|
|
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{
|
|
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-
|
|
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{
|
|
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{
|
|
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:
|