vibemap

star 1

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.

raulvidis By raulvidis schedule Updated 4/12/2026

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.

vibemap: Interactive Architecture Diagram

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).

Before starting — check for existing session

Read the file .vibemap/state.json in the project root. If it exists and contains tmpDir, port, and repoRoot:

  1. Verify the repo matches: compare 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.
  2. Check if the temp dir and data still exist:
    node -e "const fs=require('fs');console.log(fs.existsSync('<tmpDir>/data.json')&&fs.existsSync('<tmpDir>/index.html')?'DATA_EXISTS':'NO_DATA')"
    
  3. If NO_DATA: delete the state file and continue with "Fresh setup" below.
  4. Check if the server is still running:
    node -e "const r=require('http').get('http://127.0.0.1:<port>/',()=>{console.log('RUNNING');r.destroy()}).on('error',()=>console.log('STOPPED'))"
    
  5. If RUNNING: skip to "Check for changes" below.
  6. If STOPPED but data exists: restart the server, then check for changes:
    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)})()"
    

Check for changes

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');
"
  • If FRESH: just open browser and tell the user Diagram is open in your browser. Stop here.
  • If STALE: continue to "Update existing diagram" below.

Update existing diagram

  1. Run Step 1 and Step 2 below to gather structure and produce new JSON.
  2. Use the Write tool to overwrite <tmpDir>/data.json with the new JSON.
  3. Open browser (refresh will pick up the new data):
    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>')"
    
  4. Tell the user: Diagram updated and opened in your browser.
  5. Stop here — do not continue to Fresh setup.

Fresh setup

Step 1 — Gather repo structure

Explore the project thoroughly — do not stop at top-level directories:

  1. Use Glob with **/* to get the full file tree (ignore node_modules/, .git/, dist/, build/, __pycache__/, lock files)
  2. Read key config files (package.json, tsconfig.json, README.md, etc.)
  3. For component-based projects (React, Vue, Angular, etc.): list ALL subdirectories inside the components folder, including nested children. Every leaf component directory should be a candidate node. Use Glob src/components/** to ensure you see the full tree.
  4. Read the entry point and router files to understand the component hierarchy

Step 2 — Produce graph JSON

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:

  • Max 10 groups, 30 nodes, 40 edges
  • id must match [a-zA-Z][a-zA-Z0-9_]*
  • shape: one of box, database, queue, document, circle, hexagon
  • path: must be a real file/directory — verify with Glob
  • Every node should have a group
  • No double quotes in labels
  • description: 1-2 sentence summary
  • renderable (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.
  • Include child components as nodes — if a parent component contains subcomponents (e.g. 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.
  • Use groups to organize by feature area (e.g. "Sidebar", "Top Bar", "Overlays")
  • Prefer more nodes over fewer — use the full budget of 30 nodes
  • Dev server detection: Check if the project has a dev server configured:
    • Read vite.config.js or webpack.config.js for port settings
    • Check package.json scripts for dev or start commands with port numbers
    • If a dev server port is found, add "devServerUrl": "http://localhost:<port>" to the top-level JSON object
    • If no dev server is detected, omit devServerUrl
  • Route detection: For nodes that correspond to routes/pages, add "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.

Step 3 — Build and launch

  1. Create a temp directory and copy files:
    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.
  2. Use the Write tool to write your JSON object to <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).
  3. Start the server (port 0 = auto-pick). This spawns it as a detached background process, polls for the .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>.
  4. Save state — use the Write tool to write .vibemap/state.json:
    { "tmpDir": "<temp_dir>", "port": <port>, "repoRoot": "<repo_root>" }
    
  5. Open browser:
    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>')"
    
  6. Tell the user: Diagram is open in your browser. Select components by clicking them, then click "Send to Agent".

Phase 2: Receive Selections

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:

  • Code type: "The user selected lines N-M of path/to/file:" followed by the selected text
  • Element type: "The user pointed at <tag> element matching selector with text: text"

Ask what the user would like to do with this context.

Install via CLI
npx skills add https://github.com/raulvidis/vibemap --skill vibemap
Repository Details
star Stars 1
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator