원클릭으로
vibemap
// Use when the user wants to visualize, diagram, or explore the architecture of a repository. Triggers on requests like "show me the architecture", "diagram this repo", "visualize the codebase", or direct invocation via /vibemap.
// Use when the user wants to visualize, diagram, or explore the architecture of a repository. Triggers on requests like "show me the architecture", "diagram this repo", "visualize the codebase", or direct invocation via /vibemap.
| name | vibemap |
| description | Use when the user wants to visualize, diagram, or explore the architecture of a repository. Triggers on requests like "show me the architecture", "diagram this repo", "visualize the codebase", or direct invocation via /vibemap. |
Generate an interactive architecture diagram of the current repository and open it in the browser.
All shell commands below use node -e so they work in any shell (bash, PowerShell, cmd).
Read the file .vibemap/state.json in the project root. If it exists and contains tmpDir, port, and repoRoot:
repoRoot in the state file to the current working directory. If they don't match, delete the state file and continue with "Fresh setup" below.node -e "const fs=require('fs');console.log(fs.existsSync('<tmpDir>/data.json')&&fs.existsSync('<tmpDir>/index.html')?'DATA_EXISTS':'NO_DATA')"
node -e "const r=require('http').get('http://127.0.0.1:<port>/',()=>{console.log('RUNNING');r.destroy()}).on('error',()=>console.log('STOPPED'))"
node -e "const{spawn:s}=require('child_process'),fs=require('fs'),f='<tmpDir>/.port';try{fs.unlinkSync(f)}catch{};const p=s(process.execPath,['<tmpDir>/server.mjs','<tmpDir>','<repo_root>','<port>'],{detached:true,stdio:'ignore'});p.unref();(function w(){fs.existsSync(f)?console.log(fs.readFileSync(f,'utf-8').trim()):setTimeout(w,200)})()"
Compare the data.json timestamp against the latest source file change:
node -e "
const fs=require('fs'),path=require('path');
const skip=new Set(['node_modules','.git','dist','build','__pycache__','.vibemap','.claude','.worktrees']);
const dm=fs.statSync('<tmpDir>/data.json').mtimeMs;
let newest=0;
(function walk(d){try{for(const e of fs.readdirSync(d,{withFileTypes:true})){
if(skip.has(e.name))continue;const p=path.join(d,e.name);
if(e.isDirectory())walk(p);else{const t=fs.statSync(p).mtimeMs;if(t>newest)newest=t}
}}catch{}})('<repo_root>');
console.log(newest>dm?'STALE':'FRESH');
"
<tmpDir>/data.json with the new JSON.node -e "require('child_process').exec(process.platform==='win32'?'start http://127.0.0.1:<port>':process.platform==='darwin'?'open http://127.0.0.1:<port>':'xdg-open http://127.0.0.1:<port>')"
Explore the project thoroughly — do not stop at top-level directories:
**/* to get the full file tree (ignore node_modules/, .git/, dist/, build/, __pycache__/, lock files)package.json, tsconfig.json, README.md, etc.)src/components/** to ensure you see the full tree.Analyze the architecture and produce a JSON object (DO NOT output it to the user):
{
"title": "repo-name",
"groups": [{ "id": "g1", "label": "Group Label" }],
"nodes": [{ "id": "n1", "label": "Name", "shape": "box", "path": "relative/path", "group": "g1", "description": "What it does" }],
"edges": [{ "from": "n1", "to": "n2", "label": "relationship" }]
}
Rules:
id must match [a-zA-Z][a-zA-Z0-9_]*shape: one of box, database, queue, document, circle, hexagonpath: must be a real file/directory — verify with Globgroupdescription: 1-2 sentence summaryrenderable (optional boolean): Set true to force-show the Render tab for this node, or false to hide it. When omitted, renderability is auto-detected: nodes with a route, or with a frontend file extension (.tsx, .jsx, .vue, .svelte, .html, .astro, .mdx) are renderable. Nodes with database or queue shape are not. Backend services, configs, and non-visual items should NOT be marked renderable.BarSide/PlayerCard, BarSide/TeamEconomy), each child gets its own node with an edge from the parent. Do NOT flatten a parent with children into a single node.vite.config.js or webpack.config.js for port settingspackage.json scripts for dev or start commands with port numbers"devServerUrl": "http://localhost:<port>" to the top-level JSON objectdevServerUrl"route": "/path" to the node. Check the router config (React Router, Vue Router, etc.) to find route paths. For example, if HudMain maps to /hudmain, set "route": "/hudmain" on that node. This enables the Render tab to show the correct page when clicking that node or its children.node -e "const fs=require('fs'),os=require('os'),p=require('path');const d=fs.mkdtempSync(p.join(os.tmpdir(),'vibemap-'));[['template.html','index.html'],['server.mjs','server.mjs'],['bridge.js','bridge.js']].forEach(([s,t])=>fs.copyFileSync(p.join('__VIBEMAP_SKILL_ROOT__',s),p.join(d,t)));console.log(d)"
Use the printed path as <temp_dir> for all subsequent steps.<temp_dir>/data.json — the template loads it automatically via fetch, no replacement needed. Use the exact path printed above (e.g. C:\Users\...\vibemap-xxxxx\data.json on Windows)..port file, and prints the port:
node -e "const{spawn:s}=require('child_process'),fs=require('fs'),f='<temp_dir>/.port';const p=s(process.execPath,['<temp_dir>/server.mjs','<temp_dir>','<repo_root>','0'],{detached:true,stdio:'ignore'});p.unref();(function w(){fs.existsSync(f)?console.log(fs.readFileSync(f,'utf-8').trim()):setTimeout(w,200)})()"
Use the printed number as <port>..vibemap/state.json:
{ "tmpDir": "<temp_dir>", "port": <port>, "repoRoot": "<repo_root>" }
node -e "require('child_process').exec(process.platform==='win32'?'start http://127.0.0.1:<port>':process.platform==='darwin'?'open http://127.0.0.1:<port>':'xdg-open http://127.0.0.1:<port>')"
When the user says "read my selections", "go", etc.:
node -e "const http=require('http');http.get('http://127.0.0.1:<port>/selections',r=>{let d='';r.on('data',c=>d+=c);r.on('end',()=>console.log(d))})"
Parse the JSON response. Extract the paths array. For each path, use the Read tool to load the file contents. Present organized context.
If subSelections exists in the response, present each sub-selection as targeted context:
path/to/file:" followed by the selected text<tag> element matching selector with text: text"Ask what the user would like to do with this context.