<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Mir Mursalin Ankur</title>
    <description>The latest articles on Forem by Mir Mursalin Ankur (@mir_mursalin_ankur).</description>
    <link>https://forem.com/mir_mursalin_ankur</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1253577%2Fab3d1472-b6cf-4c16-994c-cf7c24c8104e.jpeg</url>
      <title>Forem: Mir Mursalin Ankur</title>
      <link>https://forem.com/mir_mursalin_ankur</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mir_mursalin_ankur"/>
    <language>en</language>
    <item>
      <title>Graphify + code-review-graph: Build a Self-Updating Knowledge Graph for Claude Code and other AI Coding Agent</title>
      <dc:creator>Mir Mursalin Ankur</dc:creator>
      <pubDate>Sun, 17 May 2026 15:31:39 +0000</pubDate>
      <link>https://forem.com/mir_mursalin_ankur/graphify-code-review-graph-build-a-self-updating-knowledge-graph-for-claude-code-and-other-ai-j1m</link>
      <guid>https://forem.com/mir_mursalin_ankur/graphify-code-review-graph-build-a-self-updating-knowledge-graph-for-claude-code-and-other-ai-j1m</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Every developer working with LLMs on a large codebase eventually hits the same wall: context windows are finite, but codebases are not.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You start a new AI coding session, ask about the payment flow — and your agent starts re-reading dozens of files just to get oriented. Twenty thousand tokens evaporated before a single line of code is written. Multiply that by every session, every team member, every day.&lt;/p&gt;

&lt;p&gt;Two open-source tools solve this in different but complementary ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Graphify&lt;/strong&gt; — converts your folder into a queryable knowledge graph with community detection, Obsidian-compatible reports, and cross-file traversal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;code-review-graph (CRG)&lt;/strong&gt; — builds a SQLite-backed AST graph with blast-radius analysis, embedding-based semantic search, ~25 MCP tools (allow-listable to a working set of 8), and sub-second incremental updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide walks through installing both tools, connecting them to any AI coding agent — Claude Code, Cursor, Gemini CLI, Windsurf, GitHub Copilot, and more — wiring auto-updates for code edited by humans, git commits, or the agent itself, and pairing everything with an Obsidian vault as a persistent memory layer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pick one, both, or none — every section degrades gracefully.&lt;/strong&gt; Each tool works standalone; the smart-grep-hook, session-start cheatsheet, and CLAUDE.md routing rules all detect what is present and adapt. Use just graphify if you want a pure CLI / zero-MCP setup. Use just CRG if you want embedding-aware semantic search and PR-grade impact tools. Use both for the full stack (CRG primary, graphify on miss). If neither is installed, the agent silently falls back to grep — no broken hooks, no errors.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All commands in this guide were tested on Ubuntu and macOS across multiple real pnpm monorepos of varying sizes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real Numbers from Two Test Projects
&lt;/h2&gt;

&lt;p&gt;Before diving in, here's what both tools produced across two real codebases — one a full-stack TypeScript monorepo with 5 packages, the other a lighter frontend-only repo:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Graphify (AST-only)&lt;/th&gt;
&lt;th&gt;code-review-graph&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Files indexed (large)&lt;/td&gt;
&lt;td&gt;1,020&lt;/td&gt;
&lt;td&gt;1,052&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nodes (large)&lt;/td&gt;
&lt;td&gt;3,815&lt;/td&gt;
&lt;td&gt;5,780&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edges (large)&lt;/td&gt;
&lt;td&gt;4,830&lt;/td&gt;
&lt;td&gt;30,611&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Files indexed (small)&lt;/td&gt;
&lt;td&gt;702&lt;/td&gt;
&lt;td&gt;711&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nodes (small)&lt;/td&gt;
&lt;td&gt;2,035&lt;/td&gt;
&lt;td&gt;2,773&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edges (small)&lt;/td&gt;
&lt;td&gt;2,357&lt;/td&gt;
&lt;td&gt;15,037&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Communities&lt;/td&gt;
&lt;td&gt;750 / 499&lt;/td&gt;
&lt;td&gt;28 wiki pages each&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Incremental update&lt;/td&gt;
&lt;td&gt;~10s (8 workers)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.425s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLM tokens used&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;graphify-out/&lt;/code&gt; (JSON)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.code-review-graph/&lt;/code&gt; (SQLite)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  How Each Tool Works
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyo9q8mfe5gtz74m1yllz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyo9q8mfe5gtz74m1yllz.png" alt="Pipeline Architecture: Graphify vs code-review-graph" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Graphify — Two-Pass Graph with Communities
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your Code
    │
    ▼
Pass 1: Tree-sitter AST   ← 0 tokens, 25 languages
(classes, functions, imports, call graphs)
    │
    ▼
Pass 2: AI Extraction     ← only for PDFs, images, markdown (optional)
(semantic relationships via Claude subagents)
    │
    ▼
NetworkX Graph + Leiden Clustering
    │
    ├── graphify-out/graph.json       (queryable)
    ├── graphify-out/GRAPH_REPORT.md  (750 communities, Obsidian links)
    ├── graphify-out/graph.html       (interactive visual)
    └── graphify-out/cache/           (SHA256 per file)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each edge has a confidence tag:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tag&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Confidence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EXTRACTED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Directly in AST&lt;/td&gt;
&lt;td&gt;1.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INFERRED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reasonable deduction&lt;/td&gt;
&lt;td&gt;0.7–0.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AMBIGUOUS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Needs review&lt;/td&gt;
&lt;td&gt;&amp;lt;0.7&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On the large monorepo: &lt;strong&gt;87% EXTRACTED · 13% INFERRED · 0% AMBIGUOUS&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  code-review-graph — Blast-Radius Graph with MCP
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your Code (git-tracked files)
    │
    ▼
Tree-sitter AST (23 languages, 0 tokens)
    │
    ▼
SQLite (.code-review-graph/graph.db)
    │
    ├── Nodes: functions, classes, files
    ├── Edges: imports, calls, inheritance
    └── Full-text search index (FTS5)
    │
    ▼
~25 MCP tools available (allow-list to ~8 via CRG_TOOLS env — see "Strip Unused CRG Tools")
(semantic_search_nodes, query_graph, get_impact_radius, list_communities, ...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Install one or both — the rest of the guide shows where each section applies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ubuntu
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Pick what you need:&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;graphifyy         &lt;span class="c"&gt;# graphify CLI (note: two y's on PyPI)&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;code-review-graph &lt;span class="c"&gt;# CRG (CLI + MCP server, includes embeddings extra)&lt;/span&gt;

&lt;span class="c"&gt;# Verify (only the lines for installed tools):&lt;/span&gt;
graphify &lt;span class="nt"&gt;--help&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt;
code-review-graph &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  macOS
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Via uv (fastest):&lt;/span&gt;
uv tool &lt;span class="nb"&gt;install &lt;/span&gt;graphifyy           &lt;span class="c"&gt;# graphify only&lt;/span&gt;
uv tool &lt;span class="nb"&gt;install &lt;/span&gt;code-review-graph   &lt;span class="c"&gt;# CRG only — or run both lines for both&lt;/span&gt;

&lt;span class="c"&gt;# Or via pipx:&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;pipx
pipx &lt;span class="nb"&gt;install &lt;/span&gt;graphifyy
pipx &lt;span class="nb"&gt;install &lt;/span&gt;code-review-graph
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;PyPI quirk&lt;/strong&gt;: The package is &lt;code&gt;graphifyy&lt;/code&gt; (two y's). The CLI command after install is &lt;code&gt;graphify&lt;/code&gt; (one y).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 1: Create Ignore Files
&lt;/h2&gt;

&lt;p&gt;Before building any graph, exclude noise from indexing. Place these at your project root.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;.graphifyignore&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules/
dist/
build/
.pnpm-store/
coverage/
*.min.js
*.min.css
*.map
pnpm-lock.yaml
yarn.lock
*.lock
*.log
.env*
graphify-out/
.code-review-graph/
*.example.*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;.code-review-graphignore&lt;/code&gt;&lt;/strong&gt; (same content)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules/
dist/
build/
.pnpm-store/
coverage/
*.min.js
*.min.css
*.map
pnpm-lock.yaml
yarn.lock
*.lock
*.log
.env*
graphify-out/
.code-review-graph/
*.example.*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Build the Graphs Manually
&lt;/h2&gt;

&lt;h3&gt;
  
  
  code-review-graph
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/your-project

&lt;span class="c"&gt;# Full build (first time) — parses all files&lt;/span&gt;
code-review-graph build

&lt;span class="c"&gt;# Output (large monorepo):&lt;/span&gt;
&lt;span class="c"&gt;# Full build: 1052 files, 5780 nodes, 30611 edges (postprocess=full)&lt;/span&gt;

&lt;span class="c"&gt;# Output (smaller frontend repo):&lt;/span&gt;
&lt;span class="c"&gt;# Full build: 711 files, 2773 nodes, 15037 edges (postprocess=full)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Graphify (AST-only, no LLM cost)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/your-project

&lt;span class="c"&gt;# AST-only update (no API key required)&lt;/span&gt;
graphify update &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Output (large monorepo):&lt;/span&gt;
&lt;span class="c"&gt;# Rebuilt: 3815 nodes, 4830 edges, 750 communities&lt;/span&gt;
&lt;span class="c"&gt;# graph.json, graph.html and GRAPH_REPORT.md updated in graphify-out&lt;/span&gt;

&lt;span class="c"&gt;# Output (smaller repo):&lt;/span&gt;
&lt;span class="c"&gt;# Rebuilt: 2035 nodes, 2357 edges, 499 communities&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the richer semantic graph (PDFs, images, markdown — uses LLM):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Full extraction with Claude subagents (requires ANTHROPIC_API_KEY)&lt;/span&gt;
graphify extract &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Register with Your AI Agent
&lt;/h2&gt;

&lt;h3&gt;
  
  
  code-review-graph (auto-configures 5 platforms)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code-review-graph &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writes &lt;code&gt;.mcp.json&lt;/code&gt; (Claude Code MCP server config)&lt;/li&gt;
&lt;li&gt;Writes &lt;code&gt;.cursor/mcp.json&lt;/code&gt;, &lt;code&gt;.opencode.json&lt;/code&gt;, Zed settings, &lt;code&gt;.cursorrules&lt;/code&gt;, &lt;code&gt;GEMINI.md&lt;/code&gt;, &lt;code&gt;AGENTS.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Creates &lt;code&gt;.claude/skills/&lt;/code&gt; for Claude Code tool integration&lt;/li&gt;
&lt;li&gt;Installs hooks in &lt;code&gt;.claude/settings.json&lt;/code&gt; (move these out — see below)&lt;/li&gt;
&lt;li&gt;Installs a &lt;strong&gt;git pre-commit hook&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Updates &lt;code&gt;.gitignore&lt;/code&gt; to exclude &lt;code&gt;.code-review-graph/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Post-install housekeeping:&lt;/strong&gt; &lt;code&gt;code-review-graph install&lt;/code&gt; is aggressive — it writes configs for every AI IDE it knows about. Most teams only use one. Add the noise to &lt;code&gt;.gitignore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .gitignore additions
AGENTS.md
GEMINI.md
.mcp.json          # keep only .mcp.example.json
.cursorrules
.windsurfrules
.opencode.json
.kiro/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Three-file hook pattern:&lt;/strong&gt; &lt;code&gt;settings.json&lt;/code&gt; stays clean — permissions only, no hooks. Hooks go into example/local:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.claude/settings.example.json&lt;/code&gt;&lt;/strong&gt; (committed) — documents the full hook structure for teammates. Contains all graph hooks (Stop, SessionStart, PreToolUse). Copy from here to activate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.claude/settings.local.json&lt;/code&gt;&lt;/strong&gt; (gitignored) — your actual personal hooks that fire at runtime. Copy from &lt;code&gt;settings.example.json&lt;/code&gt; on first setup, then customize with env vars or secrets. You can also copy individual hooks into your &lt;strong&gt;global &lt;code&gt;~/.claude/settings.json&lt;/code&gt;&lt;/strong&gt; if you want them active across all projects.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.claude/settings.example.json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;—&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;committed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;reference,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;shows&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;hook&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;structure&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Copy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.claude/settings.local.json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;customize&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Stop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"_comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CRG auto-update after AI finishes turn — once per turn, PID-guarded."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command -v code-review-graph &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;amp; [ -d .code-review-graph ] &amp;amp;&amp;amp; { PF=/tmp/crg-claude.pid; if [ -f &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$PF&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; ] &amp;amp;&amp;amp; kill -0 &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$(cat &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$PF&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; 2&amp;gt;/dev/null; then true; else { code-review-graph update --skip-flows 2&amp;gt;/dev/null &amp;amp;&amp;amp; nohup code-review-graph embed &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp; } &amp;amp; echo $! &amp;gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$PF&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; fi; } || true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"SessionStart"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"_comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CRG graph status on session open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command -v code-review-graph &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;amp; code-review-graph status 2&amp;gt;/dev/null || true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"_comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Setup nudge — prompt to initialize if CLI installed but no graph built yet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"if command -v code-review-graph &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;amp; git rev-parse --is-inside-work-tree &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;amp; [ ! -d .code-review-graph ]; then printf '{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;systemMessage&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Graph tool installed but not yet initialized. Ask me to set up: code-review-graph (code-review-graph install)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}'; fi || true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"_comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Graph query cheatsheet — injected once per session (~150 tokens)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"if [ -f .code-review-graph/graph.db ] || [ -f graphify-out/graph.json ]; then STATS=&lt;/span&gt;&lt;span class="se"&gt;\"\"&lt;/span&gt;&lt;span class="s2"&gt;; TOOL_LINES=&lt;/span&gt;&lt;span class="se"&gt;\"\"&lt;/span&gt;&lt;span class="s2"&gt;; if [ -f .code-review-graph/graph.db ]; then STATS=$(python3 -c &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;import sqlite3; c=sqlite3.connect('.code-review-graph/graph.db'); n=c.execute('SELECT COUNT(*) FROM nodes').fetchone()[0]; e=c.execute('SELECT COUNT(*) FROM edges').fetchone()[0]; print(f'{n} nodes, {e} edges'); c.close()&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; 2&amp;gt;/dev/null || echo &lt;/span&gt;&lt;span class="se"&gt;\"\"&lt;/span&gt;&lt;span class="s2"&gt;); TOOL_LINES=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;  where is X defined    → semantic_search_nodes_tool(query=X)&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n  who calls X           → query_graph_tool(pattern=callers_of, target=X)&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n  pre-refactor blast    → get_impact_radius_tool(changed_files=[...])&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n  community/cluster     → list_communities_tool()&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n  code review context   → get_review_context_tool(changed_files=[...])&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; fi; if [ -f graphify-out/graph.json ]; then GFY_STATS=$(python3 -c &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;import json; g=json.load(open('graphify-out/graph.json')); nodes=g.get('nodes',[]); comms=len(set(n.get('community','') for n in nodes if n.get('community',''))); print(f'{len(nodes)} nodes, {comms} communities')&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; 2&amp;gt;/dev/null || echo &lt;/span&gt;&lt;span class="se"&gt;\"\"&lt;/span&gt;&lt;span class="s2"&gt;); [ -n &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$GFY_STATS&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; ] &amp;amp;&amp;amp; STATS=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;${STATS:+$STATS | }graphify: $GFY_STATS&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; TOOL_LINES=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;${TOOL_LINES:+$TOOL_LINES&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n}  CRG miss / explore    → graphify query '&amp;lt;term&amp;gt;' --graph graphify-out/graph.json&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n  path A→B              → graphify path '&amp;lt;from&amp;gt;' '&amp;lt;to&amp;gt;' --graph graphify-out/graph.json&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; fi; printf '{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;hookSpecificOutput&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;hookEventName&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;SessionStart&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;additionalContext&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;GRAPH QUERY CHEATSHEET (%s) — use BEFORE Read/Grep/Bash-find on code:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n%s&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;nSkip graph for: .md .json .yml .log .jsonl configs cross-repo paths.&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;nOverride grep gate: append --graph-tried to any Bash command.&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}}' &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$STATS&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$TOOL_LINES&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; fi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .claude/settings.local.json — gitignored, actual runtime hooks&lt;/span&gt;
&lt;span class="c"&gt;# Copy from settings.example.json on first setup:&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; .claude/settings.example.json .claude/settings.local.json
&lt;span class="c"&gt;# Then add any personal env vars or secrets&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why Stop, not PostToolUse?&lt;/strong&gt; &lt;code&gt;PostToolUse&lt;/code&gt; fires after &lt;em&gt;every&lt;/em&gt; individual file edit — if Claude makes 10 edits in one response, it fires 10 times. The &lt;code&gt;pgrep&lt;/code&gt; guard that prevents double-spawning has a race window of a few milliseconds, which is not enough to block concurrent spawns reliably. The result: multiple CRG Python processes pile up, load average hits 12+, RAM saturates, and the machine slows to a crawl. &lt;code&gt;Stop&lt;/code&gt; fires &lt;strong&gt;once&lt;/strong&gt; when the AI finishes its entire turn — all edits batched, single update triggered. The PID-file guard then prevents overlap &lt;em&gt;across&lt;/em&gt; turns (skip if the previous turn's update is still running). Zero pile-up by design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why graphify update is NOT in any Claude hook:&lt;/strong&gt; &lt;code&gt;graphify update&lt;/code&gt; takes ~10s+ on a real monorepo (even with SHA256 caching). Running it in &lt;code&gt;Stop&lt;/code&gt; or &lt;code&gt;PostToolUse&lt;/code&gt; would block the session or cause the process to hang in the background, accumulating across turns. It belongs only in git hooks (&lt;code&gt;post-commit&lt;/code&gt;, &lt;code&gt;post-checkout&lt;/code&gt;) where it runs as a fully detached &lt;code&gt;nohup&lt;/code&gt; process after the commit returns — the developer has already moved on, so the latency is invisible.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .claude/settings.local.json — gitignored, actual runtime hooks (copy from example)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;.mcp.json&lt;/code&gt; it creates (keep as &lt;code&gt;.mcp.example.json&lt;/code&gt; only):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"code-review-graph"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uvx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"code-review-graph"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"serve"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stdio"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Graphify (AI Agent Integration)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Adds graphify section to CLAUDE.md + PreToolUse hook (Claude Code)&lt;/span&gt;
graphify claude &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Graphify's &lt;code&gt;claude install&lt;/code&gt; adds a &lt;code&gt;PreToolUse&lt;/code&gt; hook that intercepts &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;rg&lt;/code&gt;, &lt;code&gt;find&lt;/code&gt; commands and redirects the agent to &lt;code&gt;graphify query&lt;/code&gt; instead — turning search interception into graph navigation. For other agents: &lt;code&gt;code-review-graph install&lt;/code&gt; already writes equivalent config to &lt;code&gt;GEMINI.md&lt;/code&gt;, &lt;code&gt;AGENTS.md&lt;/code&gt;, &lt;code&gt;.cursorrules&lt;/code&gt;, and Zed settings.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;graphify claude install&lt;/code&gt; writes the &lt;code&gt;PreToolUse&lt;/code&gt; hook into &lt;code&gt;.claude/settings.json&lt;/code&gt;. Move it to &lt;code&gt;.claude/settings.example.json&lt;/code&gt; (committed as a reference) and copy to &lt;code&gt;.claude/settings.local.json&lt;/code&gt; to activate it locally — not every teammate will have graphify installed, so it shouldn't fire automatically for everyone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Auto-Update on Commit (the full picture)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjwt5sqmh30h4mejuc8fz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjwt5sqmh30h4mejuc8fz.png" alt="Auto-Update Triggers: PostToolUse, git commit, branch switch" width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Three independent triggers keep the graph fresh — install all of them so no edit path leaves it stale:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trigger&lt;/th&gt;
&lt;th&gt;Hook location&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Claude finishes a turn&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;settings.example.json&lt;/code&gt; → &lt;code&gt;settings.local.json&lt;/code&gt; Stop hook&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;CRG only&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~0.425s — fast enough to run after every AI turn; PID-guarded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Any commit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.git/hooks/post-commit&lt;/code&gt; (or &lt;code&gt;.husky/post-commit&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;graphify + CRG&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;terminal commits, IDE commits, other AI tools; graphify runs as background &lt;code&gt;nohup&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Branch switch&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.git/hooks/post-checkout&lt;/code&gt; (or &lt;code&gt;.husky/post-checkout&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;graphify + CRG&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;smart: ≤5 files diff → incremental update; &amp;gt;5 files or new branch → full rebuild; background &lt;code&gt;nohup&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;graphify is NOT in the Claude Stop hook.&lt;/strong&gt; &lt;code&gt;graphify update&lt;/code&gt; takes ~10s+ on large monorepos — too slow for an AI turn hook. It would pile up, hang in the background, and saturate CPU/RAM (observed: 3 stuck processes at 65–73% CPU each). Use git hooks instead — the developer has already moved on by the time graphify finishes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Graphify ships its own installer for the git side:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;graphify hook &lt;span class="nb"&gt;install&lt;/span&gt;   &lt;span class="c"&gt;# writes post-commit + post-checkout&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Code-review-graph does not.&lt;/strong&gt; Its &lt;code&gt;install&lt;/code&gt; command writes a &lt;code&gt;pre-commit&lt;/code&gt; hook that runs &lt;code&gt;detect-changes --brief&lt;/code&gt; (a status warning before the commit lands) but no &lt;code&gt;post-commit&lt;/code&gt; hook to update the SQLite graph after. Without one, &lt;code&gt;.code-review-graph/graph.db&lt;/code&gt; only refreshes when Claude touches a file — every terminal/IDE/other-tool commit drifts.&lt;/p&gt;

&lt;p&gt;Add the post-commit update yourself. Both forms are detached so &lt;code&gt;git commit&lt;/code&gt; returns immediately.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Resource guard required.&lt;/strong&gt; Graph rebuilds are CPU-intensive. Without guards, multiple rebuilds can pile up (observed: 3 concurrent graphify processes at 65–73% CPU each, load average 12+, RAM saturated). Every hook below includes a &lt;code&gt;_resources_ok&lt;/code&gt; check (CPU ≤ 50% of cores, memory ≥ 2 GB free) and a &lt;code&gt;pgrep&lt;/code&gt; process deduplication guard. If either check fails, the rebuild silently skips — the next commit retriggers a fresh attempt.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Plain git project — append to &lt;code&gt;.git/hooks/post-commit&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c"&gt;# Knowledge graph tools are optional — each section silently skips if tools are absent.&lt;/span&gt;

&lt;span class="c"&gt;# Returns 0 (ok) when CPU load and free memory are within acceptable limits.&lt;/span&gt;
&lt;span class="c"&gt;# CPU: 1-min load average must be &amp;lt;= 50% of logical core count (Linux/macOS)&lt;/span&gt;
&lt;span class="c"&gt;#      or aggregate load percentage &amp;lt;= 50% (Windows).&lt;/span&gt;
&lt;span class="c"&gt;# Memory: effectively available memory must be &amp;gt;= 2048 MB on all platforms.&lt;/span&gt;
_resources_ok&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;_os&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_os&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
    &lt;/span&gt;Linux&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nv"&gt;_nproc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;nproc &lt;/span&gt;2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'^processor'&lt;/span&gt; /proc/cpuinfo 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;1&lt;span class="si"&gt;)&lt;/span&gt;
      &lt;span class="nv"&gt;_cpu_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_nproc&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s1"&gt;'NR==1 { print ($1 / n &amp;lt;= 0.50) ? "1" : "0" }'&lt;/span&gt; /proc/loadavg 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;_cpu_ok&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[graph hook] Skipping rebuild — CPU load above 50% threshold"&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;1
      &lt;span class="k"&gt;fi
      &lt;/span&gt;&lt;span class="nv"&gt;_mem_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/^MemAvailable:/ { print ($2 / 1024 &amp;gt;= 2048) ? "1" : "0" }'&lt;/span&gt; /proc/meminfo 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;_mem_ok&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[graph hook] Skipping rebuild — available memory below 2 GB threshold"&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;1
      &lt;span class="k"&gt;fi&lt;/span&gt;
      &lt;span class="p"&gt;;;&lt;/span&gt;
    Darwin&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nv"&gt;_nproc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;sysctl &lt;span class="nt"&gt;-n&lt;/span&gt; hw.logicalcpu 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;1&lt;span class="si"&gt;)&lt;/span&gt;
      &lt;span class="nv"&gt;_cpu_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;sysctl &lt;span class="nt"&gt;-n&lt;/span&gt; vm.loadavg 2&amp;gt;/dev/null | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_nproc&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="s1"&gt;'{ gsub(/[{}]/, ""); print ($1 / n &amp;lt;= 0.50) ? "1" : "0" }'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;_cpu_ok&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[graph hook] Skipping rebuild — CPU load above 50% threshold"&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;1
      &lt;span class="k"&gt;fi
      &lt;/span&gt;&lt;span class="nv"&gt;_page_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;sysctl &lt;span class="nt"&gt;-n&lt;/span&gt; hw.pagesize 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;4096&lt;span class="si"&gt;)&lt;/span&gt;
      &lt;span class="nv"&gt;_mem_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;vm_stat 2&amp;gt;/dev/null | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;ps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_page_size&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s1"&gt;'
        /Pages free:/        { gsub(/\./, "", $3); free = $3 + 0 }
        /Pages inactive:/    { gsub(/\./, "", $3); inactive = $3 + 0 }
        /Pages speculative:/ { gsub(/\./, "", $3); spec = $3 + 0 }
        END { print ((free + inactive + spec) * ps / 1048576 &amp;gt;= 2048) ? "1" : "0" }
      '&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;_mem_ok&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[graph hook] Skipping rebuild — available memory below 2 GB threshold"&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;1
      &lt;span class="k"&gt;fi&lt;/span&gt;
      &lt;span class="p"&gt;;;&lt;/span&gt;
    MINGW&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;MSYS&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;CYGWIN&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; powershell.exe &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nv"&gt;_cpu_pct&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;powershell.exe &lt;span class="nt"&gt;-NoProfile&lt;/span&gt; &lt;span class="nt"&gt;-Command&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
          &lt;span class="s2"&gt;"(Get-CimInstance Win32_Processor | Measure-Object -Property LoadPercentage -Average).Average"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
          2&amp;gt;/dev/null | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'\r\n '&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
        &lt;span class="nv"&gt;_cpu_over&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;_cpu_pct&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print ($1 &amp;gt; 50) ? "1" : "0" }'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_cpu_over&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
          &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[graph hook] Skipping rebuild — CPU load above 50% threshold"&lt;/span&gt;
          &lt;span class="k"&gt;return &lt;/span&gt;1
        &lt;span class="k"&gt;fi
        &lt;/span&gt;&lt;span class="nv"&gt;_mem_mb&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;powershell.exe &lt;span class="nt"&gt;-NoProfile&lt;/span&gt; &lt;span class="nt"&gt;-Command&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
          &lt;span class="s2"&gt;"[math]::Round((Get-CimInstance Win32_OperatingSystem).FreePhysicalMemory / 1024)"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
          2&amp;gt;/dev/null | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'\r\n '&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
        &lt;span class="nv"&gt;_mem_low&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;_mem_mb&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;9999&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print ($1 &amp;lt; 2048) ? "1" : "0" }'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_mem_low&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
          &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[graph hook] Skipping rebuild — available memory below 2 GB threshold"&lt;/span&gt;
          &lt;span class="k"&gt;return &lt;/span&gt;1
        &lt;span class="k"&gt;fi
      fi&lt;/span&gt;
      &lt;span class="p"&gt;;;&lt;/span&gt;
  &lt;span class="k"&gt;esac&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;0
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Skip during rebase/merge/cherry-pick&lt;/span&gt;
&lt;span class="nv"&gt;GIT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--git-dir&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GIT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/rebase-merge"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GIT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/rebase-apply"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GIT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/MERGE_HEAD"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GIT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/CHERRY_PICK_HEAD"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0

&lt;span class="c"&gt;# ── graphify: rebuild graph after commit ─────────────────────────────────────&lt;/span&gt;
&lt;span class="c"&gt;# Entire block skips if graphify is not installed.&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; graphify &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;CHANGED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt; HEAD~1 HEAD 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt; HEAD 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CHANGED&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;GRAPHIFY_BIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; graphify&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAPHIFY_BIN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
      &lt;span class="k"&gt;*&lt;/span&gt;.exe&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;_SHEBANG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
      &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="nv"&gt;_SHEBANG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAPHIFY_BIN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/^#![[:space:]]*//'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;esac&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_SHEBANG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
      &lt;span class="k"&gt;*&lt;/span&gt;/env&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;GRAPHIFY_PYTHON&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;_SHEBANG&lt;/span&gt;&lt;span class="p"&gt;#*/env &lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
      &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="nv"&gt;GRAPHIFY_PYTHON&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_SHEBANG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;esac&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAPHIFY_PYTHON&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
      &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;[!&lt;/span&gt;a-zA-Z0-9/_.@-]&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;GRAPHIFY_PYTHON&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;esac&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAPHIFY_PYTHON&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAPHIFY_PYTHON&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import graphify"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nv"&gt;GRAPHIFY_PYTHON&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
      &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; python3 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import graphify"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;GRAPHIFY_PYTHON&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"python3"&lt;/span&gt;
      &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAPHIFY_PYTHON&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; python &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import graphify"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;GRAPHIFY_PYTHON&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"python"&lt;/span&gt;
    &lt;span class="k"&gt;fi
    if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAPHIFY_PYTHON&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; pgrep &lt;span class="nt"&gt;-qf&lt;/span&gt; &lt;span class="s1"&gt;'graphify'&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; _resources_ok&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GRAPHIFY_CHANGED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CHANGED&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="nv"&gt;_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.cache/graphify-rebuild.log"&lt;/span&gt;
      &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[graphify hook] launching background rebuild (log: &lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
      &lt;span class="nb"&gt;nohup timeout &lt;/span&gt;300 &lt;span class="nv"&gt;$GRAPHIFY_PYTHON&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
import os, sys
from pathlib import Path
changed_raw = os.environ.get('GRAPHIFY_CHANGED', '')
changed = [Path(f.strip()) for f in changed_raw.strip().splitlines() if f.strip()]
if not changed:
    sys.exit(0)
print(f'[graphify hook] {len(changed)} file(s) changed - rebuilding graph...')
try:
    from graphify.watch import _rebuild_code
    _force = os.environ.get('GRAPHIFY_FORCE', '').lower() in ('1', 'true', 'yes')
    _rebuild_code(Path('.'), force=_force)
except Exception as exc:
    print(f'[graphify hook] Rebuild failed: {exc}')
    sys.exit(1)
"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;&amp;amp;1 &amp;lt; /dev/null &amp;amp;
      &lt;span class="nb"&gt;disown &lt;/span&gt;2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
    &lt;/span&gt;&lt;span class="k"&gt;fi
  fi
fi&lt;/span&gt;

&lt;span class="c"&gt;# ── code-review-graph: incrementally update MCP graph index after commit ────&lt;/span&gt;
&lt;span class="c"&gt;# Uses `update --skip-flows` (incremental, sub-second) rather than `build`&lt;/span&gt;
&lt;span class="c"&gt;# (full rebuild — minutes on large repos).&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; code-review-graph &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; .code-review-graph &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; pgrep &lt;span class="nt"&gt;-qf&lt;/span&gt; &lt;span class="s1"&gt;'code-review-graph update'&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; _resources_ok&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.cache/code-review-graph-update.log"&lt;/span&gt;
  &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[code-review-graph] Commit detected - launching background update (log: &lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
  &lt;span class="nb"&gt;nohup &lt;/span&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'code-review-graph update --skip-flows &amp;amp;&amp;amp; code-review-graph embed'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;&amp;amp;1 &amp;lt; /dev/null &amp;amp;
  &lt;span class="nb"&gt;disown &lt;/span&gt;2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Husky project — create &lt;code&gt;.husky/post-commit&lt;/code&gt;&lt;/strong&gt; (Husky v9+ runs &lt;code&gt;.husky/&amp;lt;hookname&amp;gt;&lt;/code&gt; directly; no &lt;code&gt;.husky/_/&lt;/code&gt; shim editing needed). The content is identical to the plain git version above — copy the entire script. Always prefer &lt;code&gt;update --skip-flows&lt;/code&gt; (incremental, sub-second) over &lt;code&gt;build&lt;/code&gt; (full rebuild — minutes on large repos):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .husky/post-commit — same content as .git/hooks/post-commit above&lt;/span&gt;
&lt;span class="c"&gt;# (copy the full script from the plain git example)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify both graphs caught up to the latest commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;%ci                          &lt;span class="c"&gt;# last commit timestamp&lt;/span&gt;
&lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; %Sm graphify-out/graph.json              &lt;span class="c"&gt;# macOS&lt;/span&gt;
&lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; %Sm .code-review-graph/graph.db          &lt;span class="c"&gt;# macOS&lt;/span&gt;
&lt;span class="c"&gt;# stat -c %y graphify-out/graph.json             # Linux equivalent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both file mtimes should be at or after the commit timestamp. If &lt;code&gt;graph.db&lt;/code&gt; lags, the post-commit hook isn't firing — check &lt;code&gt;chmod +x&lt;/code&gt; on the hook file and that &lt;code&gt;pnpm install&lt;/code&gt; ran (so Husky's &lt;code&gt;prepare&lt;/code&gt; script wired up &lt;code&gt;.husky/&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent Config: trigger-list pattern (&lt;code&gt;CLAUDE.md&lt;/code&gt; / &lt;code&gt;AGENTS.md&lt;/code&gt; / &lt;code&gt;GEMINI.md&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Rather than pasting the full tool docs into your agent config file (which bloats every session's context), create a dedicated &lt;code&gt;docs/agent/knowledge-graph.md&lt;/code&gt; and add a trigger-list pointer. This works the same way across agents — Claude Code reads &lt;code&gt;CLAUDE.md&lt;/code&gt;, Gemini CLI reads &lt;code&gt;GEMINI.md&lt;/code&gt;, Codex and OpenAI agents read &lt;code&gt;AGENTS.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Knowledge Graph&lt;/span&gt;

&lt;span class="gs"&gt;**Read `docs/agent/knowledge-graph.md` whenever you:**&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Answer any architecture, cross-module, or "how does X work" question
&lt;span class="p"&gt;-&lt;/span&gt; Plan to grep, find, or glob through the codebase
&lt;span class="p"&gt;-&lt;/span&gt; Need to understand an unfamiliar module or trace a call chain
&lt;span class="p"&gt;-&lt;/span&gt; Are about to refactor or change something with unclear blast radius

The doc covers graphify (community detection, path tracing, GRAPH_REPORT.md), code-review-graph (MCP tools, impact analysis), when to use each, and the full auto-update pipeline.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Place this block in &lt;code&gt;CLAUDE.md&lt;/code&gt; for Claude Code, &lt;code&gt;GEMINI.md&lt;/code&gt; for Gemini CLI, or &lt;code&gt;AGENTS.md&lt;/code&gt; for Codex/OpenAI-compatible agents — the trigger-list pattern works the same across all of them. A vague "read before exploring unfamiliar code" pointer is easy for any agent to skip. Explicit trigger conditions — especially "plan to grep" — activate the graph habit reliably. The full reference lives in &lt;code&gt;docs/agent/knowledge-graph.md&lt;/code&gt; and is only loaded when actually needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enforcing Graph-First: The Smart Grep Hook
&lt;/h3&gt;

&lt;p&gt;The trigger-list in &lt;code&gt;CLAUDE.md&lt;/code&gt; is a soft nudge — in practice, agents still reach for grep by default even with both knowledge graphs initialized and the pointer in place. Soft warnings alone do not reliably change behaviour.&lt;/p&gt;

&lt;p&gt;The fix is a smarter &lt;code&gt;PreToolUse&lt;/code&gt; hook that doesn't just warn — it &lt;strong&gt;answers the question inside the rejection&lt;/strong&gt;, and handles every scenario gracefully: no graph, cross-repo grep, multi-repo registries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flee5t5fzr8d6z5pnhvf6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flee5t5fzr8d6z5pnhvf6.png" alt="Smart Grep Hook Decision Ladder" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decision ladder (first match wins, zero false positives):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Hook behaviour&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No graph in this repo, no registered siblings&lt;/td&gt;
&lt;td&gt;Silent pass — zero noise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Non-code target (&lt;code&gt;.md&lt;/code&gt;, &lt;code&gt;.json&lt;/code&gt;, &lt;code&gt;.log&lt;/code&gt;, &lt;code&gt;.jsonl&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Silent pass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;--graph-tried&lt;/code&gt; flag in command&lt;/td&gt;
&lt;td&gt;Silent pass — explicit override&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grep targets abs path outside CWD + that dir has graph&lt;/td&gt;
&lt;td&gt;Query that graph, show result as context, allow grep&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grep targets abs path outside CWD + no graph found&lt;/td&gt;
&lt;td&gt;Silent pass — grep is the right tool here&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No local graph + registered sibling repo matches pattern&lt;/td&gt;
&lt;td&gt;Show sibling result as context, allow grep&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First grep this session + local graph hit&lt;/td&gt;
&lt;td&gt;Show answer &lt;strong&gt;and&lt;/strong&gt; allow grep (one-shot lesson)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First grep this session + no graph hit&lt;/td&gt;
&lt;td&gt;Allow grep, suggest MCP tool for next time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subsequent grep + local graph hit&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Deny with answer inline&lt;/strong&gt; — zero retry loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subsequent grep + no graph hit&lt;/td&gt;
&lt;td&gt;Silent pass&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The key insight: &lt;strong&gt;the deny message IS the answer&lt;/strong&gt;. The costly retry loop (deny → model reads error → model tries graph → gets answer = 2 API calls) collapses to 1.&lt;/p&gt;

&lt;p&gt;Three graph sources, consulted in priority order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Local graph&lt;/strong&gt; — &lt;code&gt;.code-review-graph/graph.db&lt;/code&gt; in current working directory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-repo graph&lt;/strong&gt; — walk up from any absolute path in the command to find the nearest &lt;code&gt;.code-review-graph/&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-repo registry&lt;/strong&gt; — &lt;code&gt;code-review-graph repos&lt;/code&gt; for globally registered repos&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All lookups use the &lt;code&gt;nodes_fts&lt;/code&gt; FTS5 full-text index — sub-millisecond, no external process, no network. Falls back to a &lt;code&gt;LIKE&lt;/code&gt; scan if FTS returns empty.&lt;/p&gt;

&lt;p&gt;Save &lt;code&gt;~/.claude/scripts/smart-grep-hook.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# ~/.claude/scripts/smart-grep-hook.sh&lt;/span&gt;
&lt;span class="c"&gt;# Adaptive graph-first grep interceptor — CRG (SQLite FTS5) + graphify (JSON)&lt;/span&gt;
&lt;span class="c"&gt;# Decision ladder (first match wins):&lt;/span&gt;
&lt;span class="c"&gt;#   1. Not a search command              → pass silently&lt;/span&gt;
&lt;span class="c"&gt;#   2. --graph-tried override            → pass silently&lt;/span&gt;
&lt;span class="c"&gt;#   3. Non-code target (.md/.json/…)    → pass silently&lt;/span&gt;
&lt;span class="c"&gt;#   4. Cross-repo abs path:&lt;/span&gt;
&lt;span class="c"&gt;#      a. Target has CRG or graphify     → query both, show context, allow grep&lt;/span&gt;
&lt;span class="c"&gt;#      b. No graph in target             → pass silently&lt;/span&gt;
&lt;span class="c"&gt;#   5. No local graph of either type:&lt;/span&gt;
&lt;span class="c"&gt;#      a. CRG registered repo match     → show context, allow grep&lt;/span&gt;
&lt;span class="c"&gt;#      b. Global merged graphify match  → show context, allow grep&lt;/span&gt;
&lt;span class="c"&gt;#      c. No match                      → pass silently&lt;/span&gt;
&lt;span class="c"&gt;#   6. Local graph(s) present — session-aware gating:&lt;/span&gt;
&lt;span class="c"&gt;#      a. First grep + hit              → show result, allow (one-shot lesson)&lt;/span&gt;
&lt;span class="c"&gt;#      b. First grep + miss             → allow, suggest tool for next time&lt;/span&gt;
&lt;span class="c"&gt;#      c. Subsequent + hit              → answering deny (result inline, no retry)&lt;/span&gt;
&lt;span class="c"&gt;#      d. Subsequent + miss             → pass silently&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-uo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;INPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;CMD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"import json,sys; d=json.load(sys.stdin); print(d.get('tool_input',d).get('command',''))"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# ── Tier 1-3: fast exits ──────────────────────────────────────────────────────&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CMD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;" rg "&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;"   rg "&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;ripgrep&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;" fd "&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;" ack "&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;" ag "&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
  &lt;span class="k"&gt;*&lt;/span&gt;find&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0 &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CMD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="nt"&gt;--graph-tried&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;"# graph-checked"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;"GRAPH_TRIED=1"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0 &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CMD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
  &lt;span class="k"&gt;*&lt;/span&gt;.md&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;.json&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;.yml&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;.yaml&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;.log&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;.jsonl&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;.txt&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;.csv&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="k"&gt;*&lt;/span&gt;node_modules&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;"/.git/"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;/dist/&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;/build/&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;/.next/&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;/__pycache__/&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0 &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="k"&gt;esac&lt;/span&gt;

json_esc&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import json,sys; print(json.dumps(sys.stdin.read())[1:-1])"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# ── CRG: SQLite FTS5 query (sub-ms) ──────────────────────────────────────────&lt;/span&gt;
query_crg&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;db&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;pat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  python3 - &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pat&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;PYEOF&lt;/span&gt;&lt;span class="sh"&gt;' 2&amp;gt;/dev/null
import sqlite3, sys, os
pat, db = sys.argv[1], sys.argv[2]
if not os.path.exists(db): sys.exit(0)
try:
    c = sqlite3.connect(f"file:{db}?mode=ro", uri=True, timeout=3)
    rows = c.execute(
        "SELECT n.kind, n.name, n.file_path, n.line_start "
        "FROM nodes_fts f JOIN nodes n ON n.id=f.rowid WHERE nodes_fts MATCH ? LIMIT 5",
        (pat,)).fetchall()
    if not rows:
        rows = c.execute(
            "SELECT kind, name, file_path, line_start FROM nodes WHERE name LIKE ? LIMIT 5",
            (f'%{pat}%',)).fetchall()
    c.close()
    for kind, name, path, line in rows:
        print(f'[crg] {kind}  {name}  →  {path}:{line}')
except: pass
&lt;/span&gt;&lt;span class="no"&gt;PYEOF
&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# ── graphify: JSON in-memory search (~5ms for 4k nodes) ──────────────────────&lt;/span&gt;
query_graphify&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;pat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  python3 - &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pat&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$json&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;PYEOF&lt;/span&gt;&lt;span class="sh"&gt;' 2&amp;gt;/dev/null
import json, sys, os
pat_low, gfile = sys.argv[1].lower(), sys.argv[2]
if not os.path.exists(gfile): sys.exit(0)
try:
    g = json.load(open(gfile))
    results = []
    for n in g.get('nodes', []):
        label = n.get('label', '')
        if pat_low in label.lower() or pat_low in n.get('id', '').lower():
            src = n.get('source_file', '')
            loc = n.get('source_location', '') or n.get('line', '')
            kind = n.get('file_type', 'node')
            community = n.get('community', '')
            results.append(f'[graphify] {kind}  {label}  →  {src}:{loc}  (community {community})')
    for r in results[:5]: print(r)
except: pass
&lt;/span&gt;&lt;span class="no"&gt;PYEOF
&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# ── Query both graphs at a given root ─────────────────────────────────────────&lt;/span&gt;
query_at_root&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;root&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;pat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;crg_db&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.code-review-graph/graph.db"&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;gfy_json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;root&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/graphify-out/graph.json"&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$crg_db&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;   &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; out+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;query_crg &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$crg_db&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pat&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s1"&gt;$'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$gfy_json&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; out+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;query_graphify &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$gfy_json&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pat&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s1"&gt;$'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$out&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'/^[[:space:]]*$/d'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# ── Walk up to nearest repo with any graph ────────────────────────────────────&lt;/span&gt;
find_graph_root&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$d&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$d&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$d&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$d&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$d&lt;/span&gt;&lt;span class="s2"&gt;/.code-review-graph/graph.db"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$d&lt;/span&gt;&lt;span class="s2"&gt;/graphify-out/graph.json"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$d&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;return
    &lt;/span&gt;&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$d&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# ── Extract search pattern ────────────────────────────────────────────────────&lt;/span&gt;
&lt;span class="nv"&gt;PATTERN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CMD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
import sys, shlex
cmd = sys.stdin.read().strip()
try: parts = shlex.split(cmd)
except: parts = cmd.split()
bases = {'grep','rg','ripgrep','egrep','fgrep','ag','ack','fd'}
idx = next((i for i,p in enumerate(parts) if p.rsplit('/',1)[-1] in bases), -1)
if idx &amp;lt; 0: sys.exit(0)
for p in parts[idx+1:]:
    if not p.startswith('-') and '/' not in p and len(p) &amp;gt; 2: print(p[:60]); break
"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# ── Tier 4: cross-repo detection ──────────────────────────────────────────────&lt;/span&gt;
&lt;span class="nv"&gt;TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CMD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
import sys, shlex, os
try: parts = shlex.split(sys.stdin.read())
except: parts = sys.stdin.read().split()
cwd = os.getcwd()
for p in parts:
    if p.startswith('/') and not p.startswith('-') and not p.startswith(cwd): print(p); break
"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TARGET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find_graph_root &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TARGET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROOT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PATTERN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;RESULT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;query_at_root &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROOT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PATTERN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RESULT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nv"&gt;MSG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Cross-repo graph hit (&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ROOT&lt;/span&gt;&lt;span class="p"&gt;##*/&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) for '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PATTERN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;':&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RESULT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;Grep proceeding — result shown as context. Open a session in that repo for deeper analysis."&lt;/span&gt;
      &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'{"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":"%s"}}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MSG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | json_esc&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi
  fi
  &lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0  &lt;span class="c"&gt;# always allow cross-repo grep&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# ── Local graph detection ─────────────────────────────────────────────────────&lt;/span&gt;
&lt;span class="nv"&gt;HAVE_CRG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;HAVE_GFY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; .code-review-graph/graph.db &lt;span class="o"&gt;]&lt;/span&gt;   &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;HAVE_CRG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; graphify-out/graph.json &lt;span class="o"&gt;]&lt;/span&gt;       &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;HAVE_GFY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1

&lt;span class="c"&gt;# ── Tier 5: no local graph → registry + global merged graph ───────────────────&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HAVE_CRG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HAVE_GFY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;EXTRA_RESULT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;

  &lt;span class="c"&gt;# 5a: CRG multi-repo registry&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; code-review-graph &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PATTERN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;PATTERN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 2 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;REG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;code-review-graph repos 2&amp;gt;/dev/null | python3 - &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PATTERN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;PYEOF&lt;/span&gt;&lt;span class="sh"&gt;' 2&amp;gt;/dev/null
import sys, os, sqlite3
pat = sys.argv[1]; results = []
for line in sys.stdin.read().strip().splitlines():
    for token in line.split():
        if not token.startswith('/'): continue
        db = f"{token}/.code-review-graph/graph.db"
        if not os.path.exists(db): continue
        try:
            c = sqlite3.connect(f'file:{db}?mode=ro', uri=True, timeout=2)
            rows = c.execute(
                'SELECT n.kind, n.name, n.file_path, n.line_start '
                'FROM nodes_fts f JOIN nodes n ON n.id=f.rowid WHERE nodes_fts MATCH ? LIMIT 3',
                (pat,)).fetchall()
            c.close()
            for kind, name, fp, ln in rows:
                results.append(f'[crg:{token}] {kind}  {name}  →  {fp}:{ln}')
        except: pass
for r in results[:5]: print(r)
&lt;/span&gt;&lt;span class="no"&gt;PYEOF
&lt;/span&gt;    &lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; EXTRA_RESULT+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s1"&gt;$'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;

  &lt;span class="c"&gt;# 5b: global merged graphify (~/obsidian-vault/merged-graph.json)&lt;/span&gt;
  &lt;span class="nv"&gt;GLOBAL_GFY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/obsidian-vault/merged-graph.json"&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GLOBAL_GFY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PATTERN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;PATTERN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 2 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;GRESULT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;query_graphify &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GLOBAL_GFY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PATTERN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRESULT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; EXTRA_RESULT+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRESULT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s1"&gt;$'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
  &lt;span class="k"&gt;fi

  if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$EXTRA_RESULT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;CLEAN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$EXTRA_RESULT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'/^[[:space:]]*$/d'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;MSG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"No local graph. Sibling/global graph hit for '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PATTERN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;':&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CLEAN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;Grep proceeding. Open a session in the repo shown above for deeper analysis."&lt;/span&gt;
    &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'{"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":"%s"}}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MSG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | json_esc&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# ── Tier 6: local graph(s) — session-aware gating ────────────────────────────&lt;/span&gt;
&lt;span class="nv"&gt;KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PWD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | md5 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PWD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;md5sum &lt;/span&gt;2&amp;gt;/dev/null | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-c1-8&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.cache/claude-graph-hook"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;SLOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/first-grep-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KEY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y%m%d_%H&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;RESULT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PATTERN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;PATTERN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 2 &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;RESULT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;query_at_root &lt;span class="s2"&gt;"."&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PATTERN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Adaptive tool hint — shows MCP tool (CRG) or graphify CLI or both&lt;/span&gt;
&lt;span class="nv"&gt;TOOL_HINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HAVE_CRG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;TOOL_HINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"semantic_search_nodes_tool(query='&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PATTERN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;')"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HAVE_GFY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;GFY_HINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"graphify query '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PATTERN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' --graph graphify-out/graph.json"&lt;/span&gt;
  &lt;span class="nv"&gt;TOOL_HINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOOL_HINT&lt;/span&gt;:+&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOOL_HINT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="p"&gt; or &lt;/span&gt;&lt;span class="k"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;GFY_HINT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SLOT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SLOT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RESULT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;MSG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Graph pre-answer for '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PATTERN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;':&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RESULT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;If enough — skip the grep. Running this time (one-shot). Future code-path greps denied when graph answers. Override: --graph-tried."&lt;/span&gt;
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nv"&gt;MSG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"No graph hit for '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PATTERN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' — grep proceeding (one-shot). Next time try: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOOL_HINT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Append --graph-tried to bypass permanently."&lt;/span&gt;
  &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'{"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":"%s"}}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MSG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | json_esc&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Subsequent greps: answering deny if graph hits, else pass&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RESULT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;MSG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Graph has this — no retry needed:&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RESULT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;Use: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOOL_HINT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Append --graph-tried to override."&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"block","permissionDecisionReason":"%s"}}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MSG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | json_esc&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'[graph-hook] no result for "%s"\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PATTERN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/bypass.log"&lt;/span&gt;
&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wire it in &lt;code&gt;settings.example.json&lt;/code&gt; (then copy to &lt;code&gt;settings.local.json&lt;/code&gt; or &lt;code&gt;~/.claude/settings.json&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash ~/.claude/scripts/smart-grep-hook.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pair with a concrete SessionStart cheatsheet&lt;/strong&gt; — injects a query→tool map once per session (~150 tokens, prevents most reflexive greps before they happen). Fires only when a graph exists in the current project, silent otherwise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"SessionStart"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"if [ -f .code-review-graph/graph.db ] || [ -f graphify-out/graph.json ]; then STATS=''; TOOL_LINES=''; if [ -f .code-review-graph/graph.db ]; then STATS=$(python3 -c &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;import sqlite3; c=sqlite3.connect('.code-review-graph/graph.db'); n=c.execute('SELECT COUNT(*) FROM nodes').fetchone()[0]; e=c.execute('SELECT COUNT(*) FROM edges').fetchone()[0]; print(f'{n} nodes, {e} edges'); c.close()&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; 2&amp;gt;/dev/null || echo ''); TOOL_LINES=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;  where is X defined    → semantic_search_nodes_tool(query=X)&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n  who calls X           → query_graph_tool(pattern=callers_of, target=X)&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n  pre-refactor blast    → get_impact_radius_tool(changed_files=[...])&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n  community/cluster     → list_communities_tool()&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n  code review context   → get_review_context_tool(changed_files=[...])&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; fi; if [ -f graphify-out/graph.json ]; then GFY_STATS=$(python3 -c &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;import json; g=json.load(open('graphify-out/graph.json')); nodes=g.get('nodes',[]); comms=len(set(n.get('community','') for n in nodes if n.get('community',''))); print(f'{len(nodes)} nodes, {comms} communities')&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; 2&amp;gt;/dev/null || echo ''); [ -n &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$GFY_STATS&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; ] &amp;amp;&amp;amp; STATS=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;${STATS:+$STATS | }graphify: $GFY_STATS&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; TOOL_LINES=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;${TOOL_LINES:+$TOOL_LINES&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n}  CRG miss / explore    → graphify query '&amp;lt;term&amp;gt;' --graph graphify-out/graph.json&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n  path A→B              → graphify path '&amp;lt;from&amp;gt;' '&amp;lt;to&amp;gt;' --graph graphify-out/graph.json&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; fi; printf '{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;hookSpecificOutput&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;hookEventName&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;SessionStart&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;additionalContext&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;GRAPH QUERY CHEATSHEET (%s) — use BEFORE Read/Grep/Bash-find on code:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n%s&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;nSkip graph for: .md .json .yml .log .jsonl configs cross-repo paths.&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;nOverride grep gate: append --graph-tried to any Bash command.&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}}' &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$STATS&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$TOOL_LINES&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; fi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;--graph-tried&lt;/code&gt; beats a hard block:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;13 characters. The agent adds it when it has already consulted the graph or when the query targets content the AST graph cannot index (string literals, config values, log text, content in another language not indexed). The hook detects the flag and passes silently — legitimate grep is never more than one retry away.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-repo and no-graph: always allow, never penalise:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The hook is non-blocking in every case where the graph can't definitively help. Cross-repo greps get graph context shown as additional info, not a gate. Repos without any graph see zero hook overhead. The only hard deny is when the local graph has the exact answer — and even then, &lt;code&gt;--graph-tried&lt;/code&gt; is the immediate escape.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Session state (hourly bucket per repo):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.cache/claude-graph-hook/
├── first-grep-&amp;lt;repo-hash&amp;gt;-&amp;lt;YYYYMMDDHH&amp;gt;   ← one-shot allowance marker
└── bypass.log                             ← audit: greps that passed with no graph answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The allowance resets hourly. The &lt;code&gt;bypass.log&lt;/code&gt; is an audit trail — recurring patterns signal queries worth adding to a custom graph index or documentation.&lt;/p&gt;




&lt;h3&gt;
  
  
  Real Token Cost Comparison
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fju2gv2n98a8gq7eiol4k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fju2gv2n98a8gq7eiol4k.png" alt="Token Cost Per Lookup: grep vs graphify vs CRG" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All numbers measured live on a 1336-node TypeScript monorepo, embeddings enabled (&lt;code&gt;code-review-graph embed&lt;/code&gt; run once, then incremental after each edit).&lt;/p&gt;

&lt;h4&gt;
  
  
  Symbol and concept lookups
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Query&lt;/th&gt;
&lt;th&gt;grep&lt;/th&gt;
&lt;th&gt;graphify query&lt;/th&gt;
&lt;th&gt;CRG keyword&lt;/th&gt;
&lt;th&gt;CRG semantic&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"Where is &lt;code&gt;createThread&lt;/code&gt;?"&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;594 tokens&lt;/strong&gt; (2374 chars, 24 lines)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1532 tokens&lt;/strong&gt; (165 nodes, BFS depth=2)&lt;/td&gt;
&lt;td&gt;115 tokens&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;115 tokens&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"uiState persistence layer"&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1069 tokens&lt;/strong&gt; (4274 chars, 43 lines)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1552 tokens&lt;/strong&gt; (87 nodes)&lt;/td&gt;
&lt;td&gt;miss&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100 tokens&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"comment submit handler"&lt;/td&gt;
&lt;td&gt;114 tokens (narrow query)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1538 tokens&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;miss&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100 tokens&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"what imports api.ts?"&lt;/td&gt;
&lt;td&gt;437 tokens (filenames only)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1563 tokens&lt;/strong&gt; (378 nodes)&lt;/td&gt;
&lt;td&gt;125 tokens (41 results)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;125 tokens&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"who calls &lt;code&gt;createThread&lt;/code&gt;?"&lt;/td&gt;
&lt;td&gt;~1109 tokens + manual filter&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1532 tokens&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;80 tokens&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;80 tokens&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"error handling in server"&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;2220 chars&lt;/strong&gt; (noise: auto-generated .react-router types)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1538 tokens&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;477 chars (source only)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;477 chars&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Hidden grep cost:&lt;/strong&gt; after a grep hit the model usually reads the matched file. &lt;code&gt;comment-form.tsx&lt;/code&gt; = 909 tokens. &lt;code&gt;comment-thread.tsx&lt;/code&gt; = 1397 tokens. A typical grep + follow-up read costs &lt;strong&gt;1300–2500 tokens per question&lt;/strong&gt;. CRG returns &lt;code&gt;file:line&lt;/code&gt; — no follow-up read needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signal quality gap:&lt;/strong&gt; grep returns every file containing the pattern — including &lt;code&gt;dist/&lt;/code&gt;, generated &lt;code&gt;.react-router/types/&lt;/code&gt;, auto-compiled &lt;code&gt;.d.ts&lt;/code&gt;, and &lt;code&gt;node_modules&lt;/code&gt; leakage. CRG indexes only what Tree-sitter parses as source. For the "error handling" query: grep returned 20 lines from auto-generated type files before any real source. CRG returned 5 precise hits: &lt;code&gt;sendError&lt;/code&gt; and &lt;code&gt;withJsonBody&lt;/code&gt; in &lt;code&gt;http-utils.ts&lt;/code&gt;, &lt;code&gt;ErrorBoundary&lt;/code&gt; in &lt;code&gt;root.tsx&lt;/code&gt; and &lt;code&gt;diff.tsx&lt;/code&gt;. Zero noise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;graphify query — right tool, wrong use case:&lt;/strong&gt; BFS depth=2 returns 87–378 nodes (~1500 tokens) regardless of query specificity. For &lt;em&gt;symbol lookup&lt;/em&gt; ("where is createThread?"), this is 15× more expensive than CRG semantic. For &lt;em&gt;neighborhood exploration&lt;/em&gt; ("what connects to this module when I don't know the codebase?"), the BFS output is exactly what you want — a visual map of relationships. The smart-grep hook intercepts it for symbol queries and routes through CRG; if CRG misses, graphify query proceeds. Use &lt;code&gt;graphify query&lt;/code&gt; deliberately for exploration, not as a default lookup tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why graphify appears less in session logs than you'd expect:&lt;/strong&gt; In a measured multi-hour session on a 1336-node monorepo, graphify had 2 actual code lookups vs CRG's 36 graph tool calls. This is correct behaviour, not underuse. CRG with embeddings covers 90%+ of targeted symbol and concept queries at 100–350 tokens — graphify BFS for the same query returns 1,500 tokens. The session was targeted feature work on a &lt;em&gt;known&lt;/em&gt; codebase; both tools appropriately routed to CRG. Graphify's value surface is: unfamiliar codebase onboarding, cross-community architecture tracing, and path exploration between distant modules — none of which were needed. If the session had started with "I've never seen this codebase, orient me", graphify would have dominated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The CRG-miss → grep fallback gap:&lt;/strong&gt; When &lt;code&gt;semantic_search_nodes_tool&lt;/code&gt; returns 0 results (happened 3× for exact symbol names like &lt;code&gt;validateRef&lt;/code&gt;, &lt;code&gt;parseUnifiedDiff&lt;/code&gt;), the current cheatsheet doesn't tell the agent to try &lt;code&gt;graphify query&lt;/code&gt; next. The agent falls back to grep, which returned 6–29 chars of near-nothing. The correct fallback chain is: &lt;code&gt;CRG semantic (miss) → graphify query '&amp;lt;term&amp;gt;' → grep&lt;/code&gt;. Add this line to your CLAUDE.md trigger list or SessionStart cheatsheet to close the gap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CRG 0 results → graphify query '&amp;lt;term&amp;gt;' --graph graphify-out/graph.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  What grep cannot do at all
&lt;/h4&gt;

&lt;p&gt;Some questions require multi-hop graph traversal. Grep answers the &lt;em&gt;text search&lt;/em&gt; question; it cannot answer the &lt;em&gt;dependency&lt;/em&gt; question.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario: "What breaks if I change &lt;code&gt;server.ts&lt;/code&gt;?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With grep you would need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read &lt;code&gt;server.ts&lt;/code&gt; header to find what it exports (~828 chars)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;grep -rn "startServer" packages/&lt;/code&gt; to find 1-hop callers (~715 chars)&lt;/li&gt;
&lt;li&gt;For each caller found, grep again for their importers (~309+ chars)&lt;/li&gt;
&lt;li&gt;Aggregate manually — still missing transitive re-exports and inferred dependencies&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Total: &lt;strong&gt;3 separate operations, 1852+ chars, still incomplete.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;get_impact_radius_tool&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"summary": "Blast radius for server.ts:
  - 21 nodes directly changed
  - 500 nodes impacted (within 2 hops)
  - 135 additional files affected",
 "risk": "high",
 "key_entities": ["completer","promptBranch","fetchDefaultReviewers"]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;1 call, 274 chars, complete 2-hop picture, risk rating included.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Grep reaches one level of importers in one pass. To get two levels, you need to loop. To get re-exports of re-exports, you need to understand the module graph. &lt;code&gt;get_impact_radius_tool&lt;/code&gt; does all of this from the pre-built AST graph — sub-second.&lt;/p&gt;

&lt;p&gt;This matters most before refactoring: touching a widely-imported file without an impact check risks breaking 135 files silently.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;get_review_context_tool&lt;/code&gt; — best single call for code review
&lt;/h4&gt;

&lt;p&gt;Before any code review, this one call returns: risk level, impacted node count, impacted file count, and test gaps — all in ~111 tokens (session-measured average; range 80–270 depending on depth). No follow-up reads needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;get_review_context_tool(changed_files=["packages/git/src/threads.ts"], depth="minimal", include_source=false)

→ Risk: high
→ 359 impacted nodes in 126 files
→ test_gaps: 15
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare with manually: read file (828 tokens) + grep importers (715 tokens) + read callers to assess risk (1400+ tokens) = &lt;strong&gt;2943 tokens, incomplete&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Add it to your review workflow via the SessionStart cheatsheet — it fires once per session at ~150 tokens total, pointing the agent at this tool first for any PR or diff review.&lt;/p&gt;

&lt;h4&gt;
  
  
  The winner by query type
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────────────────────────┬────────────────────────────────┬──────────────────────────────┐
│ Query type                     │ Best tool                      │ Est. tokens                  │
├────────────────────────────────┼────────────────────────────────┼──────────────────────────────┤
│ Find function/class X          │ semantic_search_nodes_tool     │ ~100                         │
│ Who calls X?                   │ query_graph_tool(callers_of)   │ ~80                          │
│ What imports file X?           │ query_graph_tool(importers)    │ ~125 (41 results)            │
│ Concept graph traversal        │ traverse_graph_tool(query=X)   │ ~1000 (measured avg, depth=3) │
│ Pre-refactor impact check      │ get_impact_radius_tool         │ ~70, complete 2-hop          │
│ Code review / PR impact        │ get_review_context_tool        │ ~111 (measured avg), risk + test gaps │
│ Module/behavioral clusters     │ list_communities_tool          │ ~170 (12 communities)        │
│ CRG miss / neighborhood explore│ graphify query '&amp;lt;term&amp;gt;'        │ ~1500 (BFS — use sparingly)  │
│ Exact path A→B hop-by-hop      │ graphify path '&amp;lt;A&amp;gt;' '&amp;lt;B&amp;gt;'      │ ~200                         │
│ String/regex in code body      │ grep (# --graph-tried)         │ varies                       │
│ Config/JSON/log values         │ grep — graph can't index these │ varies                       │
└────────────────────────────────┴────────────────────────────────┴──────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Real session measurement: complete token accounting
&lt;/h4&gt;

&lt;p&gt;The per-query numbers above cover tool result sizes. A complete picture must also include the hidden context costs each approach carries: MCP schema overhead, required setup reads, and cache dynamics. These numbers were measured from a live multi-hour coding session on the same 1336-node monorepo (578 total API calls).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hidden context overhead per approach:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Schema/setup overhead&lt;/th&gt;
&lt;th&gt;Per-call result&lt;/th&gt;
&lt;th&gt;How it's paid&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;grep&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0 tokens&lt;/strong&gt; (none)&lt;/td&gt;
&lt;td&gt;~550 tok/call&lt;/td&gt;
&lt;td&gt;per call, direct&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;graphify CLI&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0 tokens&lt;/strong&gt; (it's a Bash call)&lt;/td&gt;
&lt;td&gt;~292 tok/call&lt;/td&gt;
&lt;td&gt;per call, direct&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+ GRAPH_REPORT.md&lt;/td&gt;
&lt;td&gt;+458 tok (partial read, measured)&lt;/td&gt;
&lt;td&gt;one-time&lt;/td&gt;
&lt;td&gt;Read into context once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CRG MCP tools&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;~6,000 tok always&lt;/strong&gt; (~25 tool schemas, default) — drops to ~1,800 tok with &lt;code&gt;CRG_TOOLS&lt;/code&gt; allow-list (8 tools)&lt;/td&gt;
&lt;td&gt;~69–1,359/call&lt;/td&gt;
&lt;td&gt;written to cache on cold start, then served from cache at 10× lower cost&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Honest full-session accounting (37 graph tool calls):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th&gt;With CRG&lt;/th&gt;
&lt;th&gt;With grep+Read&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Schema/setup overhead&lt;/td&gt;
&lt;td&gt;~6,000 tok (cached)&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tool result tokens&lt;/td&gt;
&lt;td&gt;14,167 tok (measured)&lt;/td&gt;
&lt;td&gt;110,335 tok (estimated)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~20,167 tok&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~110,335 tok&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Net saving&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~90,168 tokens — 5.5× cheaper after schema overhead&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;CRG schema tokens are written to the prompt cache on the first API call of a session (cold start: 59,310 tokens including system prompt + all tool schemas). After that they're served from &lt;code&gt;cache_read&lt;/code&gt; at $0.30/MTok — one-tenth the cost of new input ($3.00/MTok). The &lt;code&gt;get_architecture_overview_tool&lt;/code&gt; block saved 33,000 tokens from a single denied call; that alone exceeds the CRG schema overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cache economics for the full session:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cold-start cache_write (call 1)&lt;/td&gt;
&lt;td&gt;59,310 tokens — full context incl. all schemas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total cache_write across session&lt;/td&gt;
&lt;td&gt;3,056,502 tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold-start share of writes&lt;/td&gt;
&lt;td&gt;1.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total cache_read&lt;/td&gt;
&lt;td&gt;50,942,267 tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New input tokens&lt;/td&gt;
&lt;td&gt;7,694 tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Est. session cost (Sonnet 4.x)&lt;/td&gt;
&lt;td&gt;~$36 (cache_write $11.46 + cache_read $15.28 + output $9.49)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Estimated cost without CRG (same session, grep+Read instead): the additional 90K tool result tokens would each need to be cache-written then read — adding roughly $0.34 in cache_write + $2.70 in cache_read per 100K tokens. More importantly, a larger context window means cache blocks expire and must be re-created more often, compounding costs across 578 calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three-way comparison including all hidden costs (37 lookup calls, measured session):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                      grep          graphify CLI      CRG MCP
─────────────────────────────────────────────────────────────
Schema overhead       0 tok         0 tok             ~6,000 tok
                      (none)        (Bash call,       (~25 tool schemas,
                                     no MCP schema)    always in context)
Setup read            0 tok         +458 tok          0 tok
                                    (GRAPH_REPORT.md,
                                     one partial read,
                                     measured)
Per-call result       ~550 tok      ~292 tok          ~69–1,359 tok
Follow-up Read?       +~1,842 tok   not needed        not needed
                      (usually yes)

37 calls — total cost:
  Tool results        ~20,350 tok   ~10,804 tok       14,167 tok (measured)
  + overhead          0             +458 tok          +6,000 tok
  ─────────────────────────────────────────────────────────────
  TOTAL               ~20,350 tok   ~11,262 tok       ~20,167 tok

vs grep+Read equiv:   110,335 tok   110,335 tok       110,335 tok
Net saving            ~5.4×         ~9.8×             ~5.5×
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;graphify CLI is the most token-efficient per lookup&lt;/strong&gt; when full overhead is counted — zero MCP schema tax, no follow-up Read needed, compact output. Its limitation is output size at BFS scale (87–378 nodes, ~1,500 tokens) and needing GRAPH_REPORT.md for orientation. For targeted symbol lookup CRG wins; for schema-free cost-conscious exploration graphify wins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CRG's 6K schema overhead pays back fast&lt;/strong&gt;: the blocked &lt;code&gt;get_architecture_overview_tool&lt;/code&gt; call alone saved 33,000 tokens — 5× the entire schema overhead — from a single hook denial in this session. After cold-start, schemas cost $0.30/MTok (cache_read) not $3.00/MTok (new input).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Graphify vs CRG split:&lt;/strong&gt; graphify had 2 actual code lookups; CRG had 36 tool calls. This is the right ratio for targeted feature work on a known codebase — CRG semantic search (100–350 tok) dominated because the work was precise. Graphify BFS (~1,500 tok) is for orientation on unfamiliar ground. Same session on an unknown codebase would flip the ratio.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strip Unused CRG Tools (Cuts Schema Overhead 70%)
&lt;/h3&gt;

&lt;p&gt;CRG registers &lt;strong&gt;25 MCP tools by default&lt;/strong&gt; but a typical session uses ~8. Every unused tool still costs always-loaded schema tokens. CRG ships with a built-in allow-list filter — &lt;code&gt;_apply_tool_filter&lt;/code&gt; removes tools at server boot via &lt;code&gt;mcp.local_provider.remove_tool(name)&lt;/code&gt;. Filtered tools cannot be invoked at all, so the explicit PreToolUse block on &lt;code&gt;get_architecture_overview_tool&lt;/code&gt; becomes redundant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set &lt;code&gt;CRG_TOOLS&lt;/code&gt; in &lt;code&gt;~/.claude.json&lt;/code&gt; mcpServers env (or pass &lt;code&gt;serve --tools ...&lt;/code&gt;):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"code-review-graph"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stdio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uvx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"--with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"code-review-graph[embeddings]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"code-review-graph"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"serve"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"CRG_TOOLS"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"semantic_search_nodes_tool,query_graph_tool,get_impact_radius_tool,traverse_graph_tool,list_communities_tool,get_community_tool,get_review_context_tool,list_graph_stats_tool"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The 8-tool working set covers 95% of session usage:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;semantic_search_nodes_tool&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"where is X defined" — embedding match, ~100–350 tok&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;query_graph_tool&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;callers/callees/relations — &lt;code&gt;pattern=callers_of&lt;/code&gt;, ~80 tok&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_impact_radius_tool&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;pre-refactor blast radius&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;traverse_graph_tool&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;dependency walk (keep depth ≤ 3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_communities_tool&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;architecture overview, ~170 tok for 12 clusters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_community_tool&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;cluster drill-down&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_review_context_tool&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;code review with risk + test gaps, ~111 tok avg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_graph_stats_tool&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;sanity check / status&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Dropped (17 tools):&lt;/strong&gt; &lt;code&gt;get_architecture_overview_tool&lt;/code&gt; (33k tok output — never useful), &lt;code&gt;refactor_tool&lt;/code&gt; / &lt;code&gt;apply_refactor_tool&lt;/code&gt; (use git), &lt;code&gt;get_minimal_context_tool&lt;/code&gt;, &lt;code&gt;get_docs_section_tool&lt;/code&gt;, &lt;code&gt;get_wiki_page_tool&lt;/code&gt;, &lt;code&gt;find_large_functions_tool&lt;/code&gt;, &lt;code&gt;list_flows_tool&lt;/code&gt; / &lt;code&gt;get_flow_tool&lt;/code&gt; / &lt;code&gt;get_affected_flows_tool&lt;/code&gt; (flow analysis is rare), &lt;code&gt;get_hub_nodes_tool&lt;/code&gt; / &lt;code&gt;get_bridge_nodes_tool&lt;/code&gt; (covered by communities), &lt;code&gt;get_knowledge_gaps_tool&lt;/code&gt;, &lt;code&gt;get_surprising_connections_tool&lt;/code&gt;, &lt;code&gt;get_suggested_questions_tool&lt;/code&gt;, &lt;code&gt;list_repos_tool&lt;/code&gt; / &lt;code&gt;cross_repo_search_tool&lt;/code&gt; (single-repo work).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token math:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Before: ~6,000 tok schema overhead (25 tools, full descriptions + params, always loaded after cold-start)&lt;/li&gt;
&lt;li&gt;After: ~1,800 tok (8 tools)&lt;/li&gt;
&lt;li&gt;Savings: &lt;strong&gt;~4,200 tok per session, ~70% schema reduction&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Bonus: Claude Code's deferred-tool layer further lazy-loads param schemas via &lt;code&gt;ToolSearch&lt;/code&gt; — only the description metadata stays always-loaded, so real overhead is even lower&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lazy loading reality check:&lt;/strong&gt; MCP protocol itself does not support lazy-loading tool schemas — all are listed at startup. Claude Code adds a deferred-tool layer on top: tool &lt;em&gt;descriptions&lt;/em&gt; are exposed for matching, but full param schemas only load when &lt;code&gt;ToolSearch&lt;/code&gt; resolves a tool by name. So stripping at the CRG layer cuts the always-loaded description set; the Claude Code layer cuts the params. They compound.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;By case:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Workflow&lt;/th&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Targeted symbol lookup on known codebase&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;semantic_search_nodes_tool&lt;/code&gt; → fallback to &lt;code&gt;query_graph_tool&lt;/code&gt; → fallback to &lt;code&gt;graphify query&lt;/code&gt; → grep&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pre-refactor blast&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;get_impact_radius_tool&lt;/code&gt; + &lt;code&gt;traverse_graph_tool(depth=2)&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Architecture orientation&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;list_communities_tool&lt;/code&gt; → &lt;code&gt;get_community_tool&lt;/code&gt; (NEVER &lt;code&gt;get_architecture_overview_tool&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code review on a PR&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;get_review_context_tool(changed_files=[...])&lt;/code&gt; only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-repo work&lt;/td&gt;
&lt;td&gt;re-add &lt;code&gt;cross_repo_search_tool&lt;/code&gt;, &lt;code&gt;list_repos_tool&lt;/code&gt; to allow-list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flow / DAG analysis&lt;/td&gt;
&lt;td&gt;re-add &lt;code&gt;list_flows_tool&lt;/code&gt;, &lt;code&gt;get_flow_tool&lt;/code&gt;, &lt;code&gt;get_affected_flows_tool&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The concurrent session had zero graph tools:&lt;/strong&gt; a parallel session on the same codebase used only grep+Read (15 grep calls, 22 reads). Cold start: 60,610 tokens (same MCP schemas loaded — schemas are always present regardless of whether graph tools are used). Tool result tokens were proportionally higher per lookup, with no structured relationship data returned.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Bypassing the smart-grep hook:&lt;/strong&gt; append &lt;code&gt;# --graph-tried&lt;/code&gt; as a shell &lt;em&gt;comment&lt;/em&gt; (not a flag) to pass silently: &lt;code&gt;grep -rn "TODO" src/ # --graph-tried&lt;/code&gt;. On macOS, ugrep rejects &lt;code&gt;--graph-tried&lt;/code&gt; as an unknown flag — always use it as a comment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Expensive Calls to Avoid
&lt;/h3&gt;

&lt;p&gt;Some graph tool calls look useful but are token-budget traps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;get_architecture_overview_tool&lt;/code&gt;&lt;/strong&gt; — returns 131,000 chars (~33,000 tokens), exceeds any usable context budget. &lt;strong&gt;Best fix: strip it via &lt;code&gt;CRG_TOOLS&lt;/code&gt; allow-list&lt;/strong&gt; (see "Strip Unused CRG Tools" above) so it cannot be called at all. If you can't strip it, block via PreToolUse hook. Alternative: &lt;code&gt;list_communities_tool&lt;/code&gt; first (~170 tokens for 12 clusters), then &lt;code&gt;get_community_tool&lt;/code&gt; for any cluster you want to drill into.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;graphify query '&amp;lt;term&amp;gt;'&lt;/code&gt;&lt;/strong&gt; — BFS depth=2 returns 87–378 nodes (~1500 tokens) regardless of specificity. The smart-grep hook intercepts it: if CRG has a matching symbol it blocks with the CRG result inline (~100 tokens); if CRG misses it allows graphify to proceed but warns about cost. This is the right split — CRG for symbol lookup, graphify for neighborhood exploration when you genuinely want to see what's connected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;traverse_graph_tool&lt;/code&gt; on hub nodes without budget&lt;/strong&gt; — takes a &lt;code&gt;query&lt;/code&gt; string (not source/target nodes), does concept-based traversal, and at depth=5+ on hub nodes (&lt;code&gt;server.ts&lt;/code&gt;, &lt;code&gt;api.ts&lt;/code&gt;) returns thousands of nodes. Keep &lt;code&gt;depth ≤ 3&lt;/code&gt; and &lt;code&gt;token_budget ≤ 1500&lt;/code&gt;. For a specific A→B path, use &lt;code&gt;graphify path '&amp;lt;A&amp;gt;' '&amp;lt;B&amp;gt;'&lt;/code&gt; instead — it returns the shortest hop chain in ~200 tokens. For "who calls X", use &lt;code&gt;query_graph_tool(pattern=callers_of, target=X)&lt;/code&gt; (~80 tokens).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool stripping &amp;gt; hook blocking.&lt;/strong&gt; The cleanest enforcement is the &lt;code&gt;CRG_TOOLS&lt;/code&gt; allow-list shown earlier — stripped tools are removed from the MCP server entirely, so they cannot be invoked and their schemas never enter context. The PreToolUse hook block is only useful if you cannot edit the MCP server env (e.g., shared config).&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;graphify query&lt;/code&gt; interception lives inside &lt;code&gt;smart-grep-hook.sh&lt;/code&gt; and requires no extra config — it fires via the existing Bash PreToolUse hook.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making This Work for Any Project
&lt;/h3&gt;

&lt;p&gt;The hooks in &lt;code&gt;settings.example.json&lt;/code&gt; are designed to be portable — they silently no-op when the relevant tool or graph data isn't present. This means you can copy them to &lt;code&gt;settings.local.json&lt;/code&gt; per-project, or into &lt;code&gt;~/.claude/settings.json&lt;/code&gt; to activate across all projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the hooks do (each row is a no-op when the relevant tool/file is missing):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hook&lt;/th&gt;
&lt;th&gt;What fires&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;th&gt;Resource-safe?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Stop&lt;/td&gt;
&lt;td&gt;CRG &lt;code&gt;update + embed&lt;/code&gt; (background, PID-guarded — silent if not installed/initialized)&lt;/td&gt;
&lt;td&gt;Once after AI finishes each turn (all edits batched)&lt;/td&gt;
&lt;td&gt;PID file prevents overlap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PreToolUse&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;smart-grep-hook.sh&lt;/code&gt; (graph-first routing — adaptive: CRG only, graphify only, both, or silent fallback)&lt;/td&gt;
&lt;td&gt;Before every grep/find/graphify query&lt;/td&gt;
&lt;td&gt;Read-only, no resource concern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PreToolUse&lt;/td&gt;
&lt;td&gt;Linter config guard&lt;/td&gt;
&lt;td&gt;Before writing ESLint/Prettier/Biome&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SessionStart&lt;/td&gt;
&lt;td&gt;Graph cheatsheet (adaptive: shows CRG tools, graphify CLI, both, or nothing)&lt;/td&gt;
&lt;td&gt;Session open, only if graph exists&lt;/td&gt;
&lt;td&gt;Read-only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SessionStart&lt;/td&gt;
&lt;td&gt;Setup nudge (only when CLI installed but graph not initialized)&lt;/td&gt;
&lt;td&gt;Session open&lt;/td&gt;
&lt;td&gt;Read-only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What the git hooks do (resource-guarded, all platforms):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hook&lt;/th&gt;
&lt;th&gt;What fires&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;th&gt;Guards&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;post-commit&lt;/td&gt;
&lt;td&gt;graphify rebuild + CRG update + embed (both background nohup)&lt;/td&gt;
&lt;td&gt;Every commit&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;_resources_ok&lt;/code&gt; + &lt;code&gt;pgrep&lt;/code&gt; + &lt;code&gt;timeout 300&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;post-checkout&lt;/td&gt;
&lt;td&gt;graphify rebuild + CRG update/build (smart: ≤5 files incremental, &amp;gt;5 full)&lt;/td&gt;
&lt;td&gt;Branch switch only&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;_resources_ok&lt;/code&gt; + &lt;code&gt;pgrep&lt;/code&gt; + &lt;code&gt;timeout 300&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pre-commit&lt;/td&gt;
&lt;td&gt;Branch protection (blocks direct commits to master/development/release branches)&lt;/td&gt;
&lt;td&gt;Every commit attempt&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;graphify is not in the Stop hook.&lt;/strong&gt; Running &lt;code&gt;graphify update&lt;/code&gt; after every AI turn causes long-running background processes that accumulate, spike CPU, and exhaust swap. graphify update fires only via git hooks (&lt;code&gt;post-commit&lt;/code&gt;, &lt;code&gt;post-checkout&lt;/code&gt;) where it runs detached after the developer's action — invisible latency.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why no &lt;code&gt;get_architecture_overview_tool&lt;/code&gt; block hook?&lt;/strong&gt; The recommended setup uses the &lt;code&gt;CRG_TOOLS&lt;/code&gt; allow-list to strip that tool entirely (see "Strip Unused CRG Tools"). A stripped tool cannot be invoked, so the explicit block hook becomes redundant. If you cannot or do not want to strip, add the PreToolUse block as a fallback.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project &lt;code&gt;.claude/settings.json&lt;/code&gt;&lt;/strong&gt; is only for project-specific &lt;em&gt;permissions&lt;/em&gt; — which package-manager commands to allow, which config files to protect. Graph hooks go in &lt;code&gt;settings.example.json&lt;/code&gt; (committed reference) and &lt;code&gt;settings.local.json&lt;/code&gt; (live copy) — or your global &lt;code&gt;~/.claude/settings.json&lt;/code&gt; if you want them everywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One-time setup per project — pick what you have installed:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# CRG only (embedding-aware, MCP integration)&lt;/span&gt;
code-review-graph &lt;span class="nb"&gt;install
&lt;/span&gt;code-review-graph embed

&lt;span class="c"&gt;# Graphify only (pure CLI, zero MCP overhead)&lt;/span&gt;
graphify init &lt;span class="nb"&gt;.&lt;/span&gt;
graphify update &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Both — run all four; the smart-grep-hook routes CRG-first, graphify on miss&lt;/span&gt;

&lt;span class="c"&gt;# Git hooks — graphify ONLY updates via git hooks (too slow for Claude hooks)&lt;/span&gt;
code-review-graph hook &lt;span class="nb"&gt;install&lt;/span&gt;   &lt;span class="c"&gt;# post-commit: CRG update + embed (also runs in Stop hook)&lt;/span&gt;
graphify hook &lt;span class="nb"&gt;install&lt;/span&gt;            &lt;span class="c"&gt;# post-commit + post-checkout: graphify update (background nohup)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After setup: every time the AI finishes a turn, the Stop hook fires once — &lt;strong&gt;CRG only&lt;/strong&gt; updates in background (PID-guarded, ~0.425s, no pile-up). Every commit triggers both git hooks. graphify never runs from a Claude hook — only from git hooks. If only one tool is installed, only that one updates. If neither is installed, hooks are silent no-ops.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Verify Your Setup
&lt;/h2&gt;

&lt;p&gt;Run the checks for whichever tools you installed. Skip the rows for tools you did not install — there is no requirement to have both.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check CLI installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Either or both:&lt;/span&gt;
graphify &lt;span class="nt"&gt;--help&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-3&lt;/span&gt;            &lt;span class="c"&gt;# graphify only&lt;/span&gt;
code-review-graph &lt;span class="nt"&gt;--version&lt;/span&gt;          &lt;span class="c"&gt;# CRG only&lt;/span&gt;
&lt;span class="c"&gt;# code-review-graph 2.3.2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check graph outputs exist
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Graphify (skip if not installed):&lt;/span&gt;
&lt;span class="nb"&gt;ls &lt;/span&gt;graphify-out/
&lt;span class="c"&gt;# cache  graph.html  graph.json  GRAPH_REPORT.md  manifest.json&lt;/span&gt;

&lt;span class="c"&gt;# CRG (skip if not installed):&lt;/span&gt;
code-review-graph status
&lt;span class="c"&gt;# Nodes: 5780  Edges: 30611  Files: 1052&lt;/span&gt;
&lt;span class="c"&gt;# Languages: bash, javascript, vue, typescript&lt;/span&gt;
&lt;span class="c"&gt;# Last updated: 2026-05-05T18:29:51&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check GRAPH_REPORT.md freshness
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-15&lt;/span&gt; graphify-out/GRAPH_REPORT.md
&lt;span class="c"&gt;# ## Corpus Check&lt;/span&gt;
&lt;span class="c"&gt;# - 1020 files · ~1,587,186 words&lt;/span&gt;
&lt;span class="c"&gt;# - Verdict: corpus is large enough that graph structure adds value.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# ## Summary&lt;/span&gt;
&lt;span class="c"&gt;# - 3815 nodes · 4830 edges · 750 communities (533 shown, 217 thin omitted)&lt;/span&gt;
&lt;span class="c"&gt;# - Extraction: 87% EXTRACTED · 13% INFERRED · 0% AMBIGUOUS&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# ## Graph Freshness&lt;/span&gt;
&lt;span class="c"&gt;# - Built from commit: `4968d67a`&lt;/span&gt;
&lt;span class="c"&gt;# - Run `git rev-parse HEAD` and compare to check if the graph is stale.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check Stop hook is in settings.json (or settings.local.json)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; json.tool .claude/settings.json | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A5&lt;/span&gt; &lt;span class="s1"&gt;'"Stop"'&lt;/span&gt;
&lt;span class="c"&gt;# Should show the CRG PID-guard command&lt;/span&gt;
&lt;span class="c"&gt;# PostToolUse should be empty ([]) — graph updates moved to Stop hook&lt;/span&gt;

&lt;span class="c"&gt;# Verify PostToolUse is cleared (no graph update commands there):&lt;/span&gt;
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; json.tool .claude/settings.local.json | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A3&lt;/span&gt; &lt;span class="s1"&gt;'"PostToolUse"'&lt;/span&gt;
&lt;span class="c"&gt;# Should show: "PostToolUse": []&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check MCP server config
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; .mcp.example.json
&lt;span class="c"&gt;# Should show: code-review-graph serve entry&lt;/span&gt;
&lt;span class="c"&gt;# Copy to .mcp.json locally if it doesn't exist yet:&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; .mcp.example.json .mcp.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check git hooks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Husky projects (hooks live in .husky/, not .husky/_/)&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"graphify"&lt;/span&gt; .husky/post-commit
&lt;span class="c"&gt;# 1 (or more)&lt;/span&gt;

&lt;span class="c"&gt;# Plain git projects&lt;/span&gt;
&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt; .git/hooks/pre-commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test incremental update speed
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;time &lt;/span&gt;code-review-graph update &lt;span class="nt"&gt;--skip-flows&lt;/span&gt;
&lt;span class="c"&gt;# real  0m0.425s  ← CRG incremental speed (runs in Stop hook after each AI turn)&lt;/span&gt;

&lt;span class="nb"&gt;time &lt;/span&gt;graphify update &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;# SHA256 cache skips unchanged files — subsequent runs are near-instant&lt;/span&gt;
&lt;span class="c"&gt;# Note: graphify is slower (~10s on large repos) — this is why it moved to Stop&lt;/span&gt;
&lt;span class="c"&gt;# hook (once per turn) rather than PostToolUse (once per file edit)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test vault sync (if vault is configured)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp &lt;/span&gt;graphify-out/GRAPH_REPORT.md ai-vault/graphify/GRAPH_REPORT.md
&lt;span class="nb"&gt;ls &lt;/span&gt;ai-vault/graphify/
&lt;span class="c"&gt;# GRAPH_REPORT.md&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 5: Query the Graphs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Graphify CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# BFS traversal — find connections between concepts&lt;/span&gt;
graphify query &lt;span class="s2"&gt;"what connects payment to enrollment"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--graph&lt;/span&gt; graphify-out/graph.json &lt;span class="nt"&gt;--budget&lt;/span&gt; 1500

&lt;span class="c"&gt;# Depth-first traversal for flow tracing&lt;/span&gt;
graphify query &lt;span class="s2"&gt;"auth flow"&lt;/span&gt; &lt;span class="nt"&gt;--dfs&lt;/span&gt; &lt;span class="nt"&gt;--graph&lt;/span&gt; graphify-out/graph.json

&lt;span class="c"&gt;# Shortest path between two nodes&lt;/span&gt;
graphify path &lt;span class="s2"&gt;"UserDashboard.vue"&lt;/span&gt; &lt;span class="s2"&gt;"PaymentService"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--graph&lt;/span&gt; graphify-out/graph.json

&lt;span class="c"&gt;# Plain-language explanation of a node&lt;/span&gt;
graphify explain &lt;span class="s2"&gt;"UserDashboard.vue"&lt;/span&gt; &lt;span class="nt"&gt;--graph&lt;/span&gt; graphify-out/graph.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example output from &lt;code&gt;graphify explain&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Node: UserDashboard.vue
  Source:    packages/client/src/views/Dashboard
  Community: 239
  Degree:    3

Connections (3):
  --&amp;gt; UserDashboard()  [imports_from] [EXTRACTED]
  --&amp;gt; handleApiError() [contains]     [EXTRACTED]
  --&amp;gt; AuthService      [calls]        [INFERRED]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Inside Your AI Agent
&lt;/h3&gt;

&lt;p&gt;After &lt;code&gt;graphify install&lt;/code&gt; and &lt;code&gt;code-review-graph install&lt;/code&gt;, your AI agent gets graph tools automatically. For Claude Code, a &lt;code&gt;SessionStart&lt;/code&gt; hook shows the graph status on every session open. Cursor, Gemini CLI, and Windsurf pick up the MCP server from &lt;code&gt;.mcp.json&lt;/code&gt; instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# code-review-graph status on session open:
Nodes: 5780  Edges: 30611  Files: 1052
Languages: bash, javascript, vue, typescript
Last updated: 2026-05-05T18:29:51
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Auto-Update Strategies
&lt;/h2&gt;

&lt;p&gt;The graph is only valuable if it reflects current code. Here are the four strategies, from manual to fully automatic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy A: Manual Update (Always Available)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Incremental (only changed files — fast)&lt;/span&gt;
code-review-graph update        &lt;span class="c"&gt;# 0.425s on ~1000-file project&lt;/span&gt;
graphify update &lt;span class="nb"&gt;.&lt;/span&gt;               &lt;span class="c"&gt;# SHA256 cache, AST-only&lt;/span&gt;

&lt;span class="c"&gt;# Full rebuild (when switching branches or major refactors)&lt;/span&gt;
code-review-graph build
graphify update &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Strategy B: Auto-Update via Git Hooks (Recommended for graphify)
&lt;/h3&gt;

&lt;p&gt;graphify update belongs in git hooks, not in Claude Code hooks. The incremental rebuild takes ~10s+ on a real monorepo — too slow for a per-turn trigger. Git hooks fire at the right moment: after a commit or branch switch, when the developer has already moved on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;graphify hook &lt;span class="nb"&gt;install&lt;/span&gt;   &lt;span class="c"&gt;# installs post-commit + post-checkout&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The post-commit hook runs the rebuild &lt;strong&gt;in the background&lt;/strong&gt; (detached &lt;code&gt;nohup&lt;/code&gt; process) so &lt;code&gt;git commit&lt;/code&gt; returns immediately. Rebuild logs go to &lt;code&gt;~/.cache/graphify-rebuild.log&lt;/code&gt;. The post-checkout hook fires on every &lt;code&gt;git checkout &amp;lt;branch&amp;gt;&lt;/code&gt; and rebuilds for the new branch state.&lt;/p&gt;

&lt;p&gt;This is the &lt;strong&gt;only&lt;/strong&gt; recommended automatic trigger for &lt;code&gt;graphify update&lt;/code&gt; — do not add it to &lt;code&gt;Stop&lt;/code&gt;, &lt;code&gt;PostToolUse&lt;/code&gt;, or &lt;code&gt;SessionStart&lt;/code&gt; hooks.&lt;/p&gt;

&lt;p&gt;The hooks installed by &lt;code&gt;graphify hook install&lt;/code&gt; — both run as detached &lt;code&gt;nohup&lt;/code&gt; so the git operation returns immediately. &lt;strong&gt;Add the &lt;code&gt;_resources_ok&lt;/code&gt; function&lt;/strong&gt; (shown in the post-commit hook above) to the top of each hook file to prevent resource exhaustion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;.git/hooks/post-commit&lt;/code&gt;&lt;/strong&gt; — see the full resource-guarded version in the "Auto-Update on Commit" section above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;.git/hooks/post-checkout&lt;/code&gt;&lt;/strong&gt; (or &lt;code&gt;.husky/post-checkout&lt;/code&gt; for Husky projects):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c"&gt;# Knowledge graph tools are optional — each section silently skips if tools are absent.&lt;/span&gt;

&lt;span class="c"&gt;# _resources_ok function — copy from the post-commit hook above&lt;/span&gt;
&lt;span class="c"&gt;# (CPU ≤ 50% of logical cores, memory ≥ 2 GB free — same implementation)&lt;/span&gt;

&lt;span class="nv"&gt;PREV_HEAD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;NEW_HEAD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;span class="nv"&gt;BRANCH_SWITCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;

&lt;span class="c"&gt;# Only run on branch switches, not file checkouts&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BRANCH_SWITCH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0

&lt;span class="c"&gt;# Skip during rebase/merge/cherry-pick&lt;/span&gt;
&lt;span class="nv"&gt;GIT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--git-dir&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GIT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/rebase-merge"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GIT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/rebase-apply"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GIT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/MERGE_HEAD"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GIT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/CHERRY_PICK_HEAD"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0

&lt;span class="c"&gt;# Smart rebuild threshold — shared by graphify and CRG sections below&lt;/span&gt;
&lt;span class="nv"&gt;_ZERO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"0000000000000000000000000000000000000000"&lt;/span&gt;
&lt;span class="nv"&gt;_CHANGED_COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PREV_HEAD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NEW_HEAD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;_FULL_REBUILD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PREV_HEAD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_ZERO&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;_CHANGED_COUNT&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 5 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;_FULL_REBUILD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="c"&gt;# ── graphify: force-rebuild graph after branch switch ────────────────────────&lt;/span&gt;
&lt;span class="c"&gt;# Entire block skips if graphify is not installed or graph has never been built.&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; graphify &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"graphify-out"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
  &lt;span class="c"&gt;# (Python interpreter detection — same as post-commit hook)&lt;/span&gt;
  &lt;span class="nv"&gt;GRAPHIFY_BIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; graphify&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="c"&gt;# ... shebang detection omitted for brevity — copy from post-commit hook ...&lt;/span&gt;
  &lt;span class="nv"&gt;GRAPHIFY_PYTHON&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"python3"&lt;/span&gt;  &lt;span class="c"&gt;# fallback&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAPHIFY_PYTHON&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; pgrep &lt;span class="nt"&gt;-qf&lt;/span&gt; &lt;span class="s1"&gt;'graphify'&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; _resources_ok&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.cache/graphify-rebuild.log"&lt;/span&gt;
    &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_FULL_REBUILD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"true"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GRAPHIFY_FORCE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
    echo&lt;/span&gt; &lt;span class="s2"&gt;"[graphify] Branch switched (&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;_CHANGED_COUNT&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; files diff, full=&lt;/span&gt;&lt;span class="nv"&gt;$_FULL_REBUILD&lt;/span&gt;&lt;span class="s2"&gt;) - launching background rebuild (log: &lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
    &lt;span class="nb"&gt;nohup timeout &lt;/span&gt;300 &lt;span class="nv"&gt;$GRAPHIFY_PYTHON&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
from graphify.watch import _rebuild_code
from pathlib import Path
import os, sys
try:
    _force = os.environ.get('GRAPHIFY_FORCE', '').lower() in ('1', 'true', 'yes')
    _rebuild_code(Path('.'), force=_force)
except Exception as exc:
    print(f'[graphify] Rebuild failed: {exc}')
    sys.exit(1)
"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;&amp;amp;1 &amp;lt; /dev/null &amp;amp;
    &lt;span class="nb"&gt;disown &lt;/span&gt;2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
  &lt;/span&gt;&lt;span class="k"&gt;fi
fi&lt;/span&gt;

&lt;span class="c"&gt;# ── code-review-graph: update MCP graph index after branch switch ────────────&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; code-review-graph &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; .code-review-graph &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; pgrep &lt;span class="nt"&gt;-qf&lt;/span&gt; &lt;span class="s1"&gt;'code-review-graph update\|code-review-graph build'&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; _resources_ok&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.cache/code-review-graph-update.log"&lt;/span&gt;
  &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_FULL_REBUILD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"true"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[code-review-graph] Branch switched (full rebuild: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;_CHANGED_COUNT&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; files diff) - launching background build (log: &lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
    &lt;span class="nb"&gt;nohup &lt;/span&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'code-review-graph build'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;&amp;amp;1 &amp;lt; /dev/null &amp;amp;
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[code-review-graph] Branch switched (incremental: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;_CHANGED_COUNT&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; files diff) - launching background update (log: &lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
    &lt;span class="nb"&gt;nohup &lt;/span&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'code-review-graph update --skip-flows &amp;amp;&amp;amp; code-review-graph embed'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;&amp;amp;1 &amp;lt; /dev/null &amp;amp;
  &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;disown &lt;/span&gt;2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key differences from post-commit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only runs on actual branch switches (&lt;code&gt;BRANCH_SWITCH=1&lt;/code&gt; check)&lt;/li&gt;
&lt;li&gt;Smart rebuild: ≤5 files diff → incremental update; &amp;gt;5 files or new branch → full rebuild&lt;/li&gt;
&lt;li&gt;CRG uses &lt;code&gt;build&lt;/code&gt; (full SQLite rebuild) for large diffs, &lt;code&gt;update --skip-flows&lt;/code&gt; for small ones&lt;/li&gt;
&lt;li&gt;graphify sets &lt;code&gt;GRAPHIFY_FORCE=true&lt;/code&gt; for large diffs to force full AST reparse&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Strategy C: Git Hooks (On Commit + Branch Switch)
&lt;/h3&gt;

&lt;p&gt;Both tools install git hooks automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# code-review-graph: pre-commit hook in .git/hooks/pre-commit&lt;/span&gt;
code-review-graph &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# graphify: installs hooks in .git/hooks/ — for Husky projects, copy the hook logic to .husky/post-commit&lt;/span&gt;
graphify hook &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The graphify post-commit hook runs the rebuild &lt;strong&gt;in the background&lt;/strong&gt; (detached process) so &lt;code&gt;git commit&lt;/code&gt; returns immediately. Rebuild logs go to &lt;code&gt;~/.cache/graphify-rebuild.log&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy D: Claude Code Hooks (AI-Driven Updates)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Only CRG belongs in Claude hooks. graphify does not.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Claude hook?&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;code-review-graph&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Stop hook&lt;/td&gt;
&lt;td&gt;~0.425s incremental — safe after every AI turn&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;graphify&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No Claude hook&lt;/td&gt;
&lt;td&gt;~10s+ on large monorepos — causes hanging background processes, CPU spike, swap exhaustion&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When an AI agent edits files, the &lt;code&gt;Stop&lt;/code&gt; hook triggers a CRG incremental update. &lt;code&gt;code-review-graph install&lt;/code&gt; writes hooks into &lt;code&gt;.claude/settings.json&lt;/code&gt; initially — move them out. The right home is &lt;code&gt;.claude/settings.example.json&lt;/code&gt; (committed reference for teammates) and &lt;code&gt;.claude/settings.local.json&lt;/code&gt; (your live runtime copy, gitignored).&lt;/p&gt;

&lt;p&gt;Use the fallback-safe version that silently no-ops if the CLI isn't installed:&lt;/p&gt;

&lt;p&gt;At &lt;strong&gt;0.425s per update&lt;/strong&gt;, CRG runs after every AI turn without blocking the agent's workflow.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;&lt;code&gt;settings.example.json&lt;/code&gt; hooks&lt;/strong&gt; (copy to &lt;code&gt;settings.local.json&lt;/code&gt; for this project, or to &lt;code&gt;~/.claude/settings.json&lt;/code&gt; for all projects):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Stop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command -v code-review-graph &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;amp; [ -d .code-review-graph ] &amp;amp;&amp;amp; { PF=/tmp/crg-claude.pid; if [ -f &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$PF&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; ] &amp;amp;&amp;amp; kill -0 &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$(cat &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$PF&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; 2&amp;gt;/dev/null; then true; else { code-review-graph update --skip-flows 2&amp;gt;/dev/null &amp;amp;&amp;amp; nohup code-review-graph embed &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp; } &amp;amp; echo $! &amp;gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$PF&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; fi; } || true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash ~/.claude/scripts/smart-grep-hook.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"SessionStart"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command -v code-review-graph &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;amp; code-review-graph status 2&amp;gt;/dev/null || true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"if command -v code-review-graph &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;amp; git rev-parse --is-inside-work-tree &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;amp; [ ! -d .code-review-graph ]; then printf '{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;systemMessage&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Graph tool installed but not yet initialized. Ask me to set up: code-review-graph (code-review-graph install)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}'; fi || true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"if [ -f .code-review-graph/graph.db ] || [ -f graphify-out/graph.json ]; then STATS=&lt;/span&gt;&lt;span class="se"&gt;\"\"&lt;/span&gt;&lt;span class="s2"&gt;; TOOL_LINES=&lt;/span&gt;&lt;span class="se"&gt;\"\"&lt;/span&gt;&lt;span class="s2"&gt;; if [ -f .code-review-graph/graph.db ]; then STATS=$(python3 -c &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;import sqlite3; c=sqlite3.connect('.code-review-graph/graph.db'); n=c.execute('SELECT COUNT(*) FROM nodes').fetchone()[0]; e=c.execute('SELECT COUNT(*) FROM edges').fetchone()[0]; print(f'{n} nodes, {e} edges'); c.close()&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; 2&amp;gt;/dev/null || echo &lt;/span&gt;&lt;span class="se"&gt;\"\"&lt;/span&gt;&lt;span class="s2"&gt;); TOOL_LINES=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;  where is X defined    → semantic_search_nodes_tool(query=X)&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n  who calls X           → query_graph_tool(pattern=callers_of, target=X)&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n  pre-refactor blast    → get_impact_radius_tool(changed_files=[...])&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n  community/cluster     → list_communities_tool()&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n  code review context   → get_review_context_tool(changed_files=[...])&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; fi; if [ -f graphify-out/graph.json ]; then GFY_STATS=$(python3 -c &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;import json; g=json.load(open('graphify-out/graph.json')); nodes=g.get('nodes',[]); comms=len(set(n.get('community','') for n in nodes if n.get('community',''))); print(f'{len(nodes)} nodes, {comms} communities')&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; 2&amp;gt;/dev/null || echo &lt;/span&gt;&lt;span class="se"&gt;\"\"&lt;/span&gt;&lt;span class="s2"&gt;); [ -n &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$GFY_STATS&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; ] &amp;amp;&amp;amp; STATS=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;${STATS:+$STATS | }graphify: $GFY_STATS&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; TOOL_LINES=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;${TOOL_LINES:+$TOOL_LINES&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n}  CRG miss / explore    → graphify query '&amp;lt;term&amp;gt;' --graph graphify-out/graph.json&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n  path A→B              → graphify path '&amp;lt;from&amp;gt;' '&amp;lt;to&amp;gt;' --graph graphify-out/graph.json&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; fi; printf '{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;hookSpecificOutput&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;hookEventName&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;SessionStart&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;additionalContext&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;GRAPH QUERY CHEATSHEET (%s) — use BEFORE Read/Grep/Bash-find on code:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n%s&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;nSkip graph for: .md .json .yml .log .jsonl configs cross-repo paths.&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;nOverride grep gate: append --graph-tried to any Bash command.&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}}' &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$STATS&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$TOOL_LINES&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; fi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why Stop, not PostToolUse?&lt;/strong&gt; &lt;code&gt;PostToolUse&lt;/code&gt; fires after &lt;em&gt;every&lt;/em&gt; individual file edit — if Claude makes 10 edits in one response, it fires 10 times. The PID-file guard has a race window, so concurrent CRG Python processes pile up. &lt;code&gt;Stop&lt;/code&gt; fires &lt;strong&gt;once&lt;/strong&gt; when the AI finishes its entire turn — all edits batched, single update, PID-guarded. Zero pile-up by design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;PostToolUse: []&lt;/code&gt;?&lt;/strong&gt; Explicitly empty. Previous setups used PostToolUse for CRG updates. Clearing it prevents stale hook configs from double-triggering with the Stop hook.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 7: Generate Additional Outputs
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Resource exhaustion prevention&lt;/strong&gt; — all git hooks above include a &lt;code&gt;_resources_ok()&lt;/code&gt; guard that checks CPU load (≤ 50% of logical cores) and available memory (≥ 2 GB) before spawning any background rebuild. If either threshold is exceeded, the rebuild silently skips and logs why. This prevents a real problem: rapid-fire commits (amend chains, interactive rebase) spawning multiple concurrent graphify/CRG processes — observed at 3 stuck processes at 65–73% CPU each, load average 12+, RAM saturated, machine unresponsive. The &lt;code&gt;pgrep&lt;/code&gt; guard adds process-level deduplication (if a previous rebuild is still running, skip). The &lt;code&gt;timeout 300&lt;/code&gt; ensures no background process lives longer than 5 minutes. Together these three guards ensure graph rebuilds are always non-blocking and device-safe.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Wiki (Markdown pages per community)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code-review-graph wiki
&lt;span class="c"&gt;# Wiki: 28 new pages → .code-review-graph/wiki/&lt;/span&gt;

graphify tree &lt;span class="nt"&gt;--graph&lt;/span&gt; graphify-out/graph.json
&lt;span class="c"&gt;# D3 collapsible-tree HTML&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Interactive Visualization
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code-review-graph visualize
&lt;span class="c"&gt;# → .code-review-graph/graph.html&lt;/span&gt;

&lt;span class="c"&gt;# graphify-out/graph.html is generated automatically during build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Blast-Radius Analysis
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# What does this commit affect?&lt;/span&gt;
code-review-graph detect-changes &lt;span class="nt"&gt;--base&lt;/span&gt; HEAD~1 &lt;span class="nt"&gt;--brief&lt;/span&gt;

&lt;span class="c"&gt;# Risk-scored output:&lt;/span&gt;
&lt;span class="c"&gt;# Analyzed 3 changed file(s)&lt;/span&gt;
&lt;span class="c"&gt;# - 2 changed function(s)/class(es)&lt;/span&gt;
&lt;span class="c"&gt;# - 1 affected flow(s)&lt;/span&gt;
&lt;span class="c"&gt;# - Overall risk score: 0.42&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 8: Obsidian Vault (Visual Graph + Persistent Memory)
&lt;/h2&gt;

&lt;p&gt;The Obsidian layer adds a &lt;strong&gt;human-readable, visual knowledge graph&lt;/strong&gt; on top of your codebase. You've probably seen screenshots shared in the Claude Code / graphify community — a force-directed graph of hundreds of connected nodes. That's Obsidian's Graph View rendering one &lt;code&gt;.md&lt;/code&gt; file per symbol, linked by imports.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install Obsidian:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Ubuntu&lt;/span&gt;
snap &lt;span class="nb"&gt;install &lt;/span&gt;obsidian

&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; obsidian
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Three Vault Modes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Browser only&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;graphify-out/graph.html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One-off exploration, no Obsidian needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Project vault&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;project&amp;gt;/ai-vault/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single project, clean wikilinks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Global vault&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/obsidian-vault/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Multiple projects + cross-repo merged view&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Mode 1: Browser Only (Zero Setup)
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;graph.html&lt;/code&gt; file graphify generates is a self-contained interactive D3 force graph — drag nodes, zoom, click to inspect. No Obsidian required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xdg-open graphify-out/graph.html   &lt;span class="c"&gt;# Linux&lt;/span&gt;
open graphify-out/graph.html        &lt;span class="c"&gt;# macOS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Mode 2: Project Vault
&lt;/h3&gt;

&lt;p&gt;The project vault lives at &lt;code&gt;./ai-vault/&lt;/code&gt; inside the project (gitignored). It has two layers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1 — Basic (GRAPH_REPORT.md only):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ai-vault/&lt;span class="o"&gt;{&lt;/span&gt;graphify,permanent,logs,chats&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;cp &lt;/span&gt;graphify-out/GRAPH_REPORT.md ai-vault/graphify/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you the community report with wikilinks — useful for navigating clusters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2 — Rich vault (one .md per symbol):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The GRAPH_REPORT.md uses wikilinks like &lt;code&gt;[[_COMMUNITY_Community 0]]&lt;/code&gt;, but those target files don't exist yet. To make Obsidian's Graph View actually light up with a full network, generate individual note files for every node and community:&lt;/p&gt;

&lt;p&gt;Save this script to &lt;code&gt;~/.graphify/gen-obsidian-vault.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
gen-obsidian-vault.py — Generate Obsidian notes from graphify graph.json.

Usage:
  # Project vault
  python3 ~/.graphify/gen-obsidian-vault.py --project-root /path/to/project

  # Global vault (multiple projects)
  python3 ~/.graphify/gen-obsidian-vault.py &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s"&gt;    --project-root /path/to/project &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s"&gt;    --vault ~/obsidian-vault &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s"&gt;    --project-name my-project

  # Merged cross-repo graph
  python3 ~/.graphify/gen-obsidian-vault.py &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s"&gt;    --merged-graph ~/obsidian-vault/merged-graph.json &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s"&gt;    --vault ~/obsidian-vault &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s"&gt;    --project-name merged
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="n"&gt;MAX_LINKS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_build_adjacency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;links&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;link&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;links&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tgt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;target&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;relation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;tgt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;tgt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;inc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tgt&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inc&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_node_files&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nodes_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node_pfx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;comm_pfx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;nodes_dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;nid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;# &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;label&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source_file&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**File:** `&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;source_file&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;`  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source_location&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**Location:** &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;source_location&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;file_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**Type:** &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;file_type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**Community:** [[&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;comm_pfx&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/Community &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;community&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;?&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;|Community &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;community&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;?&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;]]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;nid&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;## Imports / Depends On&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tgt_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rel&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;nid&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="n"&gt;MAX_LINKS&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tgt_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- [[&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;node_pfx&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tgt_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tgt_id&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;label&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;]] `&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;`&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;inc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;nid&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;## Used By&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;src_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rel&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;inc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;nid&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="n"&gt;MAX_LINKS&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- [[&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;node_pfx&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;src_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;src_id&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;label&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;]] `&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;`&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nodes_dir&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;nid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;write_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_community_files&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;comm_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node_pfx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;comm_pfx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;comm_dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;nid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;community&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)].&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mids&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
        &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;# Community &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**Members:** &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;## Members&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;mid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;mids&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- [[&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;node_pfx&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;mid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;label&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;]]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;cross&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;community&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;mids&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;community&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;cross&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;community&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;mids&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;inc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;community&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cross&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;## Connected Communities&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- [[&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;comm_pfx&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/Community &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;|Community &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;]]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cross&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comm_dir&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Community &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;write_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project_root&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vault_root&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;project_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;graph_json_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;graph_json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;graph_json_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;graph_json_path&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project_root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;graphify-out&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;graph.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;graph_json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ERROR: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;graph_json&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; not found. Run &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;graphify update .&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; first.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;graph_json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nodes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
    &lt;span class="n"&gt;links&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;links&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_build_adjacency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;links&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;vault&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vault_root&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;expanduser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;vault_root&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project_root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ai-vault&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vault&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;project_name&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;project_name&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;vault&lt;/span&gt;
    &lt;span class="n"&gt;pfx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;project_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;project_name&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="nf"&gt;_node_files&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nodes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pfx&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;nodes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pfx&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;communities&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;n_comm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_community_files&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;communities&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pfx&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;nodes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pfx&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;communities&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;project_root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project_root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;graphify-out&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GRAPH_REPORT.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;gdir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;graphify&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;gdir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\[\[_COMMUNITY_Community (\d+)\|Community \1\]\]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[[&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pfx&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;communities/Community &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;|Community &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;]]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gdir&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GRAPH_REPORT.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;write_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  ✓ &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; nodes · &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;links&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; links · &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n_comm&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; communities → &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--project-root&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--vault&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--project-name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--merged-graph&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;project_root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vault&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;project_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;merged_graph&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.graphify
&lt;span class="c"&gt;# save the script above to ~/.graphify/gen-obsidian-vault.py, then:&lt;/span&gt;

python3 ~/.graphify/gen-obsidian-vault.py &lt;span class="nt"&gt;--project-root&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;# ✓ 3815 nodes · 4830 links · 749 communities → ./ai-vault&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates &lt;code&gt;ai-vault/nodes/&lt;/code&gt; (one &lt;code&gt;.md&lt;/code&gt; per symbol) and &lt;code&gt;ai-vault/communities/&lt;/code&gt; (one &lt;code&gt;.md&lt;/code&gt; per cluster). Each file contains wikilinks to its neighbors — Obsidian's Graph View draws the edges automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ai-vault/
├── .obsidian/               ← Obsidian config (auto-created)
├── nodes/                   ← one .md per function / class / file  ← NEW
├── communities/             ← one .md per module cluster           ← NEW
├── graphify/
│   └── GRAPH_REPORT.md      ← synced from graphify-out/
├── permanent/               ← Architecture decisions (human notes)
├── logs/                    ← Session records
└── CLAUDE.md                ← vault-level agent instructions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open in Obsidian → press &lt;code&gt;Ctrl+G&lt;/code&gt; → you get the full network.&lt;/p&gt;




&lt;h3&gt;
  
  
  Mode 3: Global Vault (Multiple Projects)
&lt;/h3&gt;

&lt;p&gt;One Obsidian window, all projects. Each project goes into its own subfolder with namespaced wikilinks — necessary because two projects often share node IDs (shared packages like &lt;code&gt;dto&lt;/code&gt;, &lt;code&gt;types&lt;/code&gt;, &lt;code&gt;utilities&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add each project to the global vault&lt;/span&gt;
python3 ~/.graphify/gen-obsidian-vault.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project-root&lt;/span&gt; /path/to/project-a &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vault&lt;/span&gt; ~/obsidian-vault &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project-name&lt;/span&gt; project-a

python3 ~/.graphify/gen-obsidian-vault.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project-root&lt;/span&gt; /path/to/project-b &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vault&lt;/span&gt; ~/obsidian-vault &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project-name&lt;/span&gt; project-b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/obsidian-vault/
├── 00-Welcome.md           ← optional index note
├── project-a/
│   ├── nodes/
│   ├── communities/
│   └── graphify/GRAPH_REPORT.md
├── project-b/
│   ├── nodes/
│   ├── communities/
│   └── graphify/GRAPH_REPORT.md
└── merged/                 ← optional cross-repo combined view
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Filter by project in Graph View:&lt;/strong&gt; Graph View sidebar → Filters → &lt;code&gt;path:project-a/nodes&lt;/code&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Cross-Repo Merged View
&lt;/h3&gt;

&lt;p&gt;When projects share types or utilities, merging their graphs reveals bridge nodes — code that affects both sides.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;graphify merge-graphs &lt;span class="se"&gt;\&lt;/span&gt;
  /path/to/project-a/graphify-out/graph.json &lt;span class="se"&gt;\&lt;/span&gt;
  /path/to/project-b/graphify-out/graph.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--out&lt;/span&gt; ~/obsidian-vault/merged-graph.json

python3 ~/.graphify/gen-obsidian-vault.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--merged-graph&lt;/span&gt; ~/obsidian-vault/merged-graph.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vault&lt;/span&gt; ~/obsidian-vault &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project-name&lt;/span&gt; merged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nodes in &lt;code&gt;merged/communities/&lt;/code&gt; that link to both projects are your highest-impact change points — changing them ripples across repos.&lt;/p&gt;




&lt;h3&gt;
  
  
  Switching Between Vaults
&lt;/h3&gt;

&lt;p&gt;This is a common pain point: clicking &lt;code&gt;X&lt;/code&gt; quits Obsidian entirely and the next launch reopens the same vault.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Switch inside Obsidian (always works):&lt;/strong&gt;&lt;br&gt;
Click the &lt;strong&gt;vault icon&lt;/strong&gt; at the very bottom-left of the sidebar (looks like a small building/safe). A panel lists all known vaults — click to switch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open two vaults simultaneously (Obsidian v1.x+):&lt;/strong&gt;&lt;br&gt;
Open the vault switcher → hold &lt;code&gt;Ctrl&lt;/code&gt; while clicking a vault → opens in a new window. Both are live at once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;From the terminal — Linux snap install:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;obsidian://&lt;/code&gt; URI scheme and &lt;code&gt;xdg-open&lt;/code&gt; do not work with snap-installed Obsidian because snap doesn't register the protocol with the system. Use a small helper script instead.&lt;/p&gt;

&lt;p&gt;Save &lt;code&gt;~/.graphify/obs.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Open a specific Obsidian vault from the terminal (snap-compatible).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="n"&gt;OBSIDIAN_CONFIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;~/snap/obsidian/current/.config/obsidian/obsidian.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;expanduser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_load&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OBSIDIAN_CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;OBSIDIAN_CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vaults&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;OBSIDIAN_CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;OBSIDIAN_CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;open_vault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vault_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vault_path&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;expanduser&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;vaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setdefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vaults&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="n"&gt;vid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;vaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;vid&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;vid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()[:&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;vaults&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;vid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Registered: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;vaults&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;vaults&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;vaults&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;vid&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="nf"&gt;_save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Opening: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/snap/bin/obsidian&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;start_new_session&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEVNULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEVNULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Usage: obs.py &amp;lt;vault-path&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;open_vault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This registers the vault in Obsidian's JSON config and launches Obsidian pointing to it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shell aliases (add to &lt;code&gt;~/.zshrc&lt;/code&gt; on macOS, &lt;code&gt;~/.bashrc&lt;/code&gt; on Linux):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;obs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'python3 ~/.graphify/obs.py ~/obsidian-vault'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;obs-project&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'python3 ~/.graphify/obs.py /path/to/your-project/ai-vault'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then: &lt;code&gt;source ~/.zshrc&lt;/code&gt; (or &lt;code&gt;source ~/.bashrc&lt;/code&gt; on Linux) and use &lt;code&gt;obs&lt;/code&gt; / &lt;code&gt;obs-project&lt;/code&gt; from any terminal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;macOS (non-snap):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;open &lt;span class="s2"&gt;"obsidian://open?vault=obsidian-vault"&lt;/span&gt;
&lt;span class="c"&gt;# or&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;obs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'open "obsidian://open?vault=obsidian-vault"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Practical tip — avoid switching entirely:&lt;/strong&gt;&lt;br&gt;
Keep the global vault as your default. Inside Graph View, use the path filter to scope to one project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Graph View sidebar → Filters → &lt;code&gt;path:my-project/nodes&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This shows only that project's symbols without switching vaults.&lt;/p&gt;


&lt;h3&gt;
  
  
  Graph View Filters
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Filter&lt;/th&gt;
&lt;th&gt;Shows&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;path:nodes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All code symbols&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;path:my-project/nodes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One project's symbols (global vault)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;path:communities&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Module clusters only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;path:permanent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Architecture decisions (human notes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;path:logs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Session records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-path:nodes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Human notes only, no code graph&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h3&gt;
  
  
  &lt;code&gt;ai-vault/CLAUDE.md&lt;/code&gt; (vault-level agent instructions — Claude Code example; use &lt;code&gt;AGENTS.md&lt;/code&gt; or &lt;code&gt;GEMINI.md&lt;/code&gt; for other agents)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Session Commands&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`/resume`&lt;/span&gt; — Read the latest log in &lt;span class="sb"&gt;`logs/`&lt;/span&gt; to restore context
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`/save`&lt;/span&gt; — Write a timestamped session summary to &lt;span class="sb"&gt;`logs/YYYY-MM-DD-HH-MM.md`&lt;/span&gt;

&lt;span class="gu"&gt;## Navigation&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`graphify/GRAPH_REPORT.md`&lt;/span&gt; — Codebase graph report
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`permanent/`&lt;/span&gt; — Architecture decisions and atomic notes
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`logs/`&lt;/span&gt; — Session records

&lt;span class="gu"&gt;## Graph Usage&lt;/span&gt;
Before answering architecture questions, read &lt;span class="sb"&gt;`graphify/GRAPH_REPORT.md`&lt;/span&gt;.
Use &lt;span class="sb"&gt;`graphify query`&lt;/span&gt;, &lt;span class="sb"&gt;`graphify path`&lt;/span&gt;, and &lt;span class="sb"&gt;`graphify explain`&lt;/span&gt; from the project root.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Keeping the Vault Fresh
&lt;/h3&gt;

&lt;p&gt;Git hooks keep &lt;code&gt;graph.json&lt;/code&gt; current automatically. Refresh the Obsidian notes after significant changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Project vault&lt;/span&gt;
python3 ~/.graphify/gen-obsidian-vault.py &lt;span class="nt"&gt;--project-root&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Global vault entry&lt;/span&gt;
python3 ~/.graphify/gen-obsidian-vault.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project-root&lt;/span&gt; /path/to/project &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vault&lt;/span&gt; ~/obsidian-vault &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project-name&lt;/span&gt; my-project

&lt;span class="c"&gt;# Rebuild merged view&lt;/span&gt;
graphify merge-graphs &lt;span class="se"&gt;\&lt;/span&gt;
  /path/to/a/graphify-out/graph.json &lt;span class="se"&gt;\&lt;/span&gt;
  /path/to/b/graphify-out/graph.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--out&lt;/span&gt; ~/obsidian-vault/merged-graph.json &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
python3 ~/.graphify/gen-obsidian-vault.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--merged-graph&lt;/span&gt; ~/obsidian-vault/merged-graph.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vault&lt;/span&gt; ~/obsidian-vault &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project-name&lt;/span&gt; merged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add vault regen to git hooks
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Husky projects:&lt;/strong&gt; create &lt;code&gt;.husky/post-commit&lt;/code&gt; (git-tracked; note &lt;code&gt;.husky/_/&lt;/code&gt; is gitignored and dead code — the &lt;code&gt;h&lt;/code&gt; wrapper always exits before any appended code there runs).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plain git projects:&lt;/strong&gt; use &lt;code&gt;.git/hooks/post-commit&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;The hook is structured in two independent sections so teammates without these tools see zero overhead — each section guards itself and silently no-ops if the tool isn't present:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c"&gt;# Knowledge graph tools are optional — each section silently skips if tools are absent.&lt;/span&gt;

&lt;span class="c"&gt;# _resources_ok function — copy from the post-commit hook above&lt;/span&gt;
&lt;span class="c"&gt;# (CPU ≤ 50% of logical cores, memory ≥ 2 GB free, all platforms)&lt;/span&gt;

&lt;span class="nv"&gt;GIT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--git-dir&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GIT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/rebase-merge"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GIT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/rebase-apply"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GIT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/MERGE_HEAD"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GIT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/CHERRY_PICK_HEAD"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0

&lt;span class="c"&gt;# ── graphify: rebuild graph after commit ─────────────────────────────────────&lt;/span&gt;
&lt;span class="c"&gt;# Entire block skips if graphify is not installed.&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; graphify &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;CHANGED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt; HEAD~1 HEAD 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt; HEAD 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CHANGED&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# (Python interpreter detection omitted for brevity — see full hook above)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAPHIFY_PYTHON&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; pgrep &lt;span class="nt"&gt;-qf&lt;/span&gt; &lt;span class="s1"&gt;'graphify'&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; _resources_ok&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GRAPHIFY_CHANGED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CHANGED&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="nv"&gt;_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.cache/graphify-rebuild.log"&lt;/span&gt;
      &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[graphify hook] launching background rebuild (log: &lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
      &lt;span class="nb"&gt;nohup timeout &lt;/span&gt;300 &lt;span class="nv"&gt;$GRAPHIFY_PYTHON&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
import os, sys
from pathlib import Path
changed_raw = os.environ.get('GRAPHIFY_CHANGED', '')
changed = [Path(f.strip()) for f in changed_raw.strip().splitlines() if f.strip()]
if not changed:
    sys.exit(0)
from graphify.watch import _rebuild_code
_rebuild_code(Path('.'))
"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;&amp;amp;1 &amp;lt; /dev/null &amp;amp;
      &lt;span class="nb"&gt;disown &lt;/span&gt;2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
    &lt;/span&gt;&lt;span class="k"&gt;fi
  fi
fi&lt;/span&gt;

&lt;span class="c"&gt;# ── code-review-graph: incrementally update MCP graph index after commit ────&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; code-review-graph &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; .code-review-graph &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; pgrep &lt;span class="nt"&gt;-qf&lt;/span&gt; &lt;span class="s1"&gt;'code-review-graph update'&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; _resources_ok&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.cache/code-review-graph-update.log"&lt;/span&gt;
  &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[code-review-graph] Commit detected - launching background update (log: &lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
  &lt;span class="nb"&gt;nohup &lt;/span&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'code-review-graph update --skip-flows &amp;amp;&amp;amp; code-review-graph embed'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;&amp;amp;1 &amp;lt; /dev/null &amp;amp;
  &lt;span class="nb"&gt;disown &lt;/span&gt;2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;timeout 300&lt;/code&gt;?&lt;/strong&gt; graphify &lt;code&gt;_rebuild_code&lt;/code&gt; has no internal deadline. On large repos it can hang indefinitely if it hits a file lock, encoding error, or memory pressure. &lt;code&gt;timeout 300&lt;/code&gt; auto-kills the background process after 5 minutes — the git commit returns immediately either way, and the next commit retriggers a fresh attempt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;_resources_ok&lt;/code&gt;?&lt;/strong&gt; Without it, rapid-fire commits (amend chains, rebase, interactive rebase) spawn multiple concurrent graphify processes. Observed: 3 stuck processes at 65–73% CPU each, load average 12+, RAM exhausted. The resource check ensures a rebuild only starts when the machine can handle it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Create the same file at &lt;code&gt;.husky/post-checkout&lt;/code&gt; with one change: wrap the graphify block with &lt;code&gt;if command -v graphify &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;amp; [ -d "graphify-out" ]&lt;/code&gt; (force-rebuild only when the graph has been built before). Mark both executable: &lt;code&gt;chmod +x .husky/post-commit .husky/post-checkout&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What "safe to commit" means here:&lt;/strong&gt; a developer with none of these tools runs exactly the 4 rebase guards and exits 0 — no Python spawned, no directories created, no errors.&lt;/p&gt;




&lt;h2&gt;
  
  
  Complete Auto-Update Flow
&lt;/h2&gt;

&lt;p&gt;After full setup, the pipeline looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI agent session opens (Claude Code shown — other agents use equivalent plugin hooks)
    → SessionStart: code-review-graph status
    → SessionStart: setup nudge (only if CLI installed but no graph built yet)
    → SessionStart: graph cheatsheet injected (query→tool map, ~150 tokens, once per session)

AI agent searches the codebase (grep / rg / find)
    → PreToolUse: smart-grep-hook.sh intercepts
        ├── no graph / non-code target / --graph-tried flag → pass silently
        ├── first grep this session
        │     ├── graph has answer → show answer in context, allow grep (one-shot lesson)
        │     └── graph has no answer → allow grep, suggest MCP tool
        └── subsequent grep
              ├── graph has answer → deny with answer inline (zero retry, saves 2000+ tokens)
              └── graph has no answer → pass silently

AI agent edits a file
    → Stop hook (once per full AI turn): code-review-graph update --skip-flows (0.425s)
    [graphify does NOT update here — too slow for per-turn trigger]

Code committed
    → post-commit: _resources_ok() check
        ├── CPU &amp;gt; 50% or memory &amp;lt; 2 GB → skip rebuild, log why
        ├── pgrep: graphify already running → skip
        └── resources OK → graphify update . (background nohup, timeout 300)
    → post-commit: _resources_ok() check
        ├── pgrep: CRG already running → skip
        └── resources OK → code-review-graph update + embed (background nohup)

Branch switched
    → post-checkout: diff count = git diff --name-only PREV NEW | wc -l
        ├── ≤5 files OR same-base branch switch
        │     ├── graphify: incremental update (SHA256 cached, fast) [resource-guarded]
        │     └── CRG: update --skip-flows + embed [resource-guarded]
        └── &amp;gt;5 files OR brand-new branch
              ├── graphify: force full rebuild (GRAPHIFY_FORCE=true) [resource-guarded]
              └── CRG: build (full SQLite rebuild) [resource-guarded]

Result: developer commits or switches branches → both graphs update in background (if device has resources).
        Agent edits files → CRG updates after each AI turn.
        Agent searches → graph answers before grep fires.
        Device overloaded → rebuilds silently skip, next commit retries.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Quick Reference
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Each block below is independent — install only the tools you want. Pure CLI setup → graphify only. Embedding + MCP setup → CRG only. Full stack → both.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Either, both, or pick one:&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;graphify              &lt;span class="c"&gt;# graphify (CLI only)&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;code-review-graph     &lt;span class="c"&gt;# CRG (CLI + MCP server)&lt;/span&gt;
&lt;span class="c"&gt;# Or via uvx / pipx — see the Installation section earlier in this guide&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Project Setup (run once per project, for each tool you installed)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create ignore files (only the ones you need)&lt;/span&gt;
&lt;span class="nb"&gt;touch&lt;/span&gt; .graphifyignore                 &lt;span class="c"&gt;# graphify only&lt;/span&gt;
&lt;span class="nb"&gt;touch&lt;/span&gt; .code-review-graphignore        &lt;span class="c"&gt;# CRG only&lt;/span&gt;

&lt;span class="c"&gt;# Build graphs manually&lt;/span&gt;
graphify update &lt;span class="nb"&gt;.&lt;/span&gt;           &lt;span class="c"&gt;# graphify: JSON graph, AST-only (no tokens)&lt;/span&gt;
code-review-graph build     &lt;span class="c"&gt;# CRG: SQLite graph (fast, no tokens)&lt;/span&gt;

&lt;span class="c"&gt;# Register with your AI agent (run only the lines for installed tools)&lt;/span&gt;
graphify claude &lt;span class="nb"&gt;install&lt;/span&gt;     &lt;span class="c"&gt;# graphify hooks + CLAUDE.md trigger&lt;/span&gt;
graphify hook &lt;span class="nb"&gt;install&lt;/span&gt;       &lt;span class="c"&gt;# graphify post-commit hook&lt;/span&gt;
code-review-graph &lt;span class="nb"&gt;install&lt;/span&gt;   &lt;span class="c"&gt;# CRG MCP server + skills + hooks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify (run only the rows for tools you installed)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code-review-graph status            &lt;span class="c"&gt;# CRG: node/edge counts&lt;/span&gt;
&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-15&lt;/span&gt; graphify-out/GRAPH_REPORT.md  &lt;span class="c"&gt;# graphify: corpus + freshness&lt;/span&gt;
&lt;span class="nb"&gt;time &lt;/span&gt;code-review-graph update &lt;span class="nt"&gt;--skip-flows&lt;/span&gt;   &lt;span class="c"&gt;# CRG: should be &amp;lt;1s&lt;/span&gt;
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; json.tool .claude/settings.local.json | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A3&lt;/span&gt; PostToolUse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Daily Commands (each line targets one tool — use what is relevant)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Manual incremental update&lt;/span&gt;
graphify update &lt;span class="nb"&gt;.&lt;/span&gt;           &lt;span class="c"&gt;# graphify: SHA256 incremental&lt;/span&gt;
code-review-graph update    &lt;span class="c"&gt;# CRG: 0.4s&lt;/span&gt;

&lt;span class="c"&gt;# Query (graphify CLI)&lt;/span&gt;
graphify query &lt;span class="s2"&gt;"auth flow"&lt;/span&gt; &lt;span class="nt"&gt;--graph&lt;/span&gt; graphify-out/graph.json
graphify path &lt;span class="s2"&gt;"ComponentA"&lt;/span&gt; &lt;span class="s2"&gt;"ServiceB"&lt;/span&gt; &lt;span class="nt"&gt;--graph&lt;/span&gt; graphify-out/graph.json
graphify explain &lt;span class="s2"&gt;"MyService"&lt;/span&gt; &lt;span class="nt"&gt;--graph&lt;/span&gt; graphify-out/graph.json

&lt;span class="c"&gt;# Blast-radius review (CRG)&lt;/span&gt;
code-review-graph detect-changes &lt;span class="nt"&gt;--base&lt;/span&gt; HEAD~1 &lt;span class="nt"&gt;--brief&lt;/span&gt;

&lt;span class="c"&gt;# Status (CRG)&lt;/span&gt;
code-review-graph status

&lt;span class="c"&gt;# Wiki / visualization (CRG)&lt;/span&gt;
code-review-graph wiki
code-review-graph visualize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Watch Mode
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;graphify watch &lt;span class="nb"&gt;.&lt;/span&gt;                  &lt;span class="c"&gt;# continuous auto-rebuild (graphify)&lt;/span&gt;
code-review-graph watch           &lt;span class="c"&gt;# continuous auto-rebuild (crg)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why Not a Vector Database?
&lt;/h2&gt;

&lt;p&gt;Code navigation is fundamentally relational — &lt;code&gt;UserController&lt;/code&gt; calls &lt;code&gt;AuthService&lt;/code&gt; which imports &lt;code&gt;TokenRepository&lt;/code&gt;. This is a directed graph, not a bag of vectors.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Knowledge Graph&lt;/th&gt;
&lt;th&gt;Vector DB&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Code structure&lt;/td&gt;
&lt;td&gt;Topology-exact&lt;/td&gt;
&lt;td&gt;Approximate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setup&lt;/td&gt;
&lt;td&gt;No embedding pipeline&lt;/td&gt;
&lt;td&gt;Embedding + chunking + sync&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hallucination&lt;/td&gt;
&lt;td&gt;None (AST is deterministic)&lt;/td&gt;
&lt;td&gt;Can return similar-but-wrong&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost (indexing)&lt;/td&gt;
&lt;td&gt;0 tokens (AST mode)&lt;/td&gt;
&lt;td&gt;Embedding cost per file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Incremental update&lt;/td&gt;
&lt;td&gt;0.4s (SQLite diff)&lt;/td&gt;
&lt;td&gt;Re-embed changed chunks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exact symbol lookup&lt;/td&gt;
&lt;td&gt;Use grep/LSP (still best)&lt;/td&gt;
&lt;td&gt;Often worse&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Knowledge graphs excel at &lt;strong&gt;"how does X relate to Y"&lt;/strong&gt; questions. For exact symbol lookup, grep and LSP still win — and both tools' &lt;code&gt;PreToolUse&lt;/code&gt; hooks (or equivalent agent config) redirect the agent toward the graph for structural questions while leaving grep for exact matches.&lt;/p&gt;




&lt;h2&gt;
  
  
  Files Created / Modified
&lt;/h2&gt;

&lt;p&gt;After completing this setup, here's what changes in each project:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Committed to repo:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;your-project/
├── .graphifyignore                    ← new
├── .code-review-graphignore           ← new
├── .mcp.example.json                  ← new (reference config for teammates)
├── CLAUDE.md                          ← 2-line pointer to docs/agent/knowledge-graph.md
├── .claude/
│   ├── settings.json                  ← permissions only, no hooks
│   ├── settings.example.json          ← new (hook reference: Stop CRG update + SessionStart cheatsheet + setup nudge)
│   └── skills/                        ← new (code-review-graph skills relevant to the repo)
├── .husky/                            ← Husky hooks (if using Husky)
│   ├── pre-commit                     ← branch protection (blocks direct commits to protected branches)
│   ├── post-commit                    ← graphify + CRG rebuild (resource-guarded, background nohup)
│   └── post-checkout                  ← graphify + CRG rebuild on branch switch (resource-guarded, smart incremental/full)
└── docs/agent/knowledge-graph.md      ← new (full tool reference)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Gitignored (local only, not committed):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;your-project/
├── .mcp.json                          ← personal MCP config (copy from .mcp.example.json)
├── .claude/settings.local.json        ← live personal hooks (copy from settings.example.json)
├── graphify-out/                      ← generated (graph.json, GRAPH_REPORT.md, graph.html)
├── .code-review-graph/                ← generated (SQLite db, wiki, visualization)
├── ai-vault/                          ← Obsidian vault (personal, lives inside the project)
├── AGENTS.md / GEMINI.md              ← tool-generated, IDE-specific
├── .cursorrules / .windsurfrules      ← IDE-specific
└── .kiro/ / .opencode.json            ← IDE-specific
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;New teammate setup&lt;/strong&gt; (copy example files and activate):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .mcp.example.json .mcp.json
&lt;span class="nb"&gt;cp&lt;/span&gt; .claude/settings.example.json .claude/settings.local.json
&lt;span class="c"&gt;# Or copy hooks into ~/.claude/settings.json to activate across all projects&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/safishamsi/graphify" rel="noopener noreferrer"&gt;Graphify&lt;/a&gt;&lt;/strong&gt; — AI coding assistant skill (Claude Code, Codex, OpenCode, Cursor, Gemini CLI, and more). Turn any folder of code, SQL schemas, R scripts, shell scripts, docs, papers, images, or videos into a queryable knowledge graph. App code + database schema + infrastructure in one graph.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/tirth8205/code-review-graph" rel="noopener noreferrer"&gt;code-review-graph&lt;/a&gt;&lt;/strong&gt; — Local knowledge graph for Claude Code. Builds a persistent map of your codebase so Claude reads only what matters — 6.8× fewer tokens on reviews and up to 49× on daily coding tasks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.analyticsvidhya.com/blog/2026/04/graphify-guide/" rel="noopener noreferrer"&gt;From Karpathy's LLM Wiki to Graphify: AI Memory Layers are Here&lt;/a&gt;&lt;/strong&gt; — Analytics Vidhya article on the evolution and implementation of knowledge graphs for AI systems.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Let's Connect
&lt;/h2&gt;

&lt;p&gt;I'm always excited to hear about what you're building! If you found this guide helpful, have questions, or just want to share your ai setup strategy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://encryptioner.github.io" rel="noopener noreferrer"&gt;encryptioner.github.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn&lt;/strong&gt;: &lt;a href="https://www.linkedin.com/in/mir-mursalin-ankur" rel="noopener noreferrer"&gt;Mir Mursalin Ankur&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Encryptioner" rel="noopener noreferrer"&gt;@Encryptioner&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;X (Twitter)&lt;/strong&gt;: &lt;a href="https://twitter.com/AnkurMursalin" rel="noopener noreferrer"&gt;@AnkurMursalin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical Writing&lt;/strong&gt;: &lt;a href="https://nerddevs.com/author/ankur/" rel="noopener noreferrer"&gt;Nerddevs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Support&lt;/strong&gt;: &lt;a href="https://www.supportkori.com/mirmursalinankur" rel="noopener noreferrer"&gt;SupportKori&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>llm</category>
      <category>tooling</category>
    </item>
    <item>
      <title>branchdiff + GitHub &amp; Bitbucket: A Local Lens for the Pull Request Workflow You Already Have</title>
      <dc:creator>Mir Mursalin Ankur</dc:creator>
      <pubDate>Sun, 10 May 2026 11:32:50 +0000</pubDate>
      <link>https://forem.com/mir_mursalin_ankur/branchdiff-github-bitbucket-a-local-lens-for-the-pull-request-workflow-you-already-have-20mm</link>
      <guid>https://forem.com/mir_mursalin_ankur/branchdiff-github-bitbucket-a-local-lens-for-the-pull-request-workflow-you-already-have-20mm</guid>
      <description>&lt;p&gt;Here is a number nobody tracks but everyone feels: the number of tab switches in a single code review.&lt;/p&gt;

&lt;p&gt;Open the PR. Switch to your editor to search for the function being changed. Switch back to the PR to find your place. Click a file in the sidebar. Scroll to the relevant hunk. The author pushes a fix commit — the diff resets to the top. Switch to Slack to tell them you were mid-read. Switch back. Find your place again. Copy a snippet into your AI assistant. Paste the AI's reply back as a comment. Tab-switch to the PR. Tab-switch back because you forgot which file you were on.&lt;/p&gt;

&lt;p&gt;By the time you post your review, you have made twenty tab switches to do ten minutes of actual reading. That friction is not a big problem. It is a hundred small ones. And small frictions are the exact things that cause engineers to put review off until Friday afternoon when they are too tired to read carefully.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;branchdiff is not a replacement for GitHub or Bitbucket.&lt;/strong&gt; It is a local browser app that sits beside your editor and handles the reading, marking, commenting, and lifecycle actions — while every click that matters (merge gate, approval, audit trail, CI status) still happens on the platform, through the same APIs you would use anyway. The PR on the platform is still the source of truth. branchdiff is just a better cockpit for the part of review that does not benefit from being a cloud round-trip.&lt;/p&gt;

&lt;p&gt;This post explains how those two layers fit together — what lives where, and why the seam is designed the way it is.&lt;/p&gt;




&lt;h2&gt;
  
  
  The mental model: source of truth vs. working surface
&lt;/h2&gt;

&lt;p&gt;Think of the PR on GitHub or Bitbucket as the &lt;strong&gt;source of truth&lt;/strong&gt;. The review history, approvals, merge state, CI status, branch protection, and audit trail all live there. Nothing in branchdiff changes any of that. Your team's review process, your release process, and your compliance posture keep working exactly the way they always have.&lt;/p&gt;

&lt;p&gt;branchdiff is the &lt;strong&gt;working surface&lt;/strong&gt; for the part of review that is fundamentally local — reading the diff, marking files as you go, drafting comments, navigating between files, and running an AI pass. When you are ready, you sync the comments to the PR with one click and the platform takes over again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fegcgpyirrqfmu1e7d7bs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fegcgpyirrqfmu1e7d7bs.png" alt="Source of Truth vs. Working Surface" width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The split in practice:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;GitHub / Bitbucket&lt;/th&gt;
&lt;th&gt;branchdiff&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Merge gating, branch protection&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI / status checks&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Approvals &amp;amp; required reviewers&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Final review comments&lt;/td&gt;
&lt;td&gt;✓ (after sync)&lt;/td&gt;
&lt;td&gt;✓ (drafted here)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reading the diff with full context&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Marking files reviewed / stale&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Running AI review or resolve&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PR lifecycle clicks (approve, merge)&lt;/td&gt;
&lt;td&gt;✓ (canonical)&lt;/td&gt;
&lt;td&gt;✓ (proxied via API)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Everything in the right column is a convenience layer. Everything in the left column is still authoritative. Uninstall branchdiff tomorrow and your team's review process keeps working — you go back to clicking on the PR page. That is the design constraint the whole tool is built around.&lt;/p&gt;




&lt;h2&gt;
  
  
  Opening any PR locally in seconds
&lt;/h2&gt;

&lt;p&gt;Point branchdiff at a PR URL on either platform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;branchdiff https://github.com/owner/repo/pull/123
branchdiff https://bitbucket.org/workspace/repo/pull-requests/45
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It resolves the base and head refs, runs &lt;code&gt;git fetch&lt;/code&gt; if needed, and opens a browser tab at &lt;code&gt;http://localhost:5391&lt;/code&gt; with the diff rendered — syntax highlighting for 150+ languages, split or unified view, a sidebar of changed files, keyboard navigation (&lt;code&gt;j&lt;/code&gt;/&lt;code&gt;k&lt;/code&gt; next/previous file, &lt;code&gt;n&lt;/code&gt;/&lt;code&gt;p&lt;/code&gt; next/previous hunk). The PR on the platform is untouched.&lt;/p&gt;

&lt;p&gt;Named-ref comparisons (&lt;code&gt;main..feature&lt;/code&gt;) get a &lt;strong&gt;persistent review session&lt;/strong&gt; backed by a local SQLite file in &lt;code&gt;~/.branchdiff/&lt;/code&gt;. Inline comments survive new commits — same idea as a GitHub PR thread, but stored on your machine, readable instantly, without a round-trip. If the author force-pushes mid-review, your view markers and drafts stay intact.&lt;/p&gt;

&lt;p&gt;Multiple sessions are the default, not a workaround. Each unique ref pair opens on its own port — the second session on &lt;code&gt;5392&lt;/code&gt;, the third on &lt;code&gt;5393&lt;/code&gt;, and so on. Reviewing a teammate's PR while working on your own branch in another tab is just two browser tabs. &lt;code&gt;branchdiff list&lt;/code&gt; shows everything running.&lt;/p&gt;




&lt;h2&gt;
  
  
  PR lifecycle from the toolbar — a remote control, not a parallel database
&lt;/h2&gt;

&lt;p&gt;The toolbar shows a dropdown for the open PR with the actions you would otherwise click on the platform. It is &lt;strong&gt;not&lt;/strong&gt; a separate database of PR state. It is a remote control. Each action calls &lt;code&gt;gh&lt;/code&gt; for GitHub or the Bitbucket REST API for Bitbucket, and the canonical PR records the action with the same timestamp it would have had if you had clicked on the website.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F130ovc40k04h23ga0d8h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F130ovc40k04h23ga0d8h.png" alt="PR Lifecycle Action Groups" width="800" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The dropdown groups four kinds of action:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Review actions&lt;/strong&gt; — Approve, Request Changes, Comment. The Comment action posts a regular PR comment via &lt;code&gt;gh pr comment&lt;/code&gt; (fixed in v1.5.1; the previous version mistakenly created a formal review object). Request Changes allows an optional comment; if you provide nothing, a default message is posted because both APIs require a review body.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Merge action&lt;/strong&gt; — with a strategy picker. GitHub offers &lt;em&gt;Merge commit&lt;/em&gt;, &lt;em&gt;Squash&lt;/em&gt;, and &lt;em&gt;Rebase&lt;/em&gt;; Bitbucket offers &lt;em&gt;Merge commit&lt;/em&gt;, &lt;em&gt;Squash&lt;/em&gt;, and &lt;em&gt;Fast-forward&lt;/em&gt; (added in v1.5.1). Branch protection rules and required status checks still apply — if the PR cannot merge, the platform refuses and branchdiff surfaces the error inline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State actions&lt;/strong&gt; — Close, Reopen (only when valid), Mark as Draft, Mark Ready for Review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metadata actions&lt;/strong&gt; — Edit title and description, Sync comments, Open in browser.&lt;/p&gt;

&lt;p&gt;The dropdown header carries a &lt;strong&gt;state dot&lt;/strong&gt; (green for open, purple for merged, red for closed) and a row of &lt;strong&gt;reviewer pills&lt;/strong&gt; showing each reviewer's latest state — approved, changes requested, commented, pending, or dismissed. Destructive actions (merge, close, request changes) show a confirmation dialog; errors stay inline in the dialog so a typed message is not lost when the network blips.&lt;/p&gt;




&lt;h2&gt;
  
  
  Comment sync — both directions, both opt-in
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Push local comments to the PR.&lt;/strong&gt; Draft comments in branchdiff (manually or via an AI pass), then click the PR badge in the toolbar and choose &lt;strong&gt;Push to PR&lt;/strong&gt;. Each single-comment thread is posted as an inline review comment. Duplicates (same file, line, body) are skipped. Multi-reply threads stay local because the platform APIs do not map cleanly to them.&lt;/p&gt;

&lt;p&gt;A status toast tells you what happened: &lt;code&gt;Pushed 3, skipped 1 duplicate, skipped 1 multi-reply thread&lt;/code&gt;. Failures keep the local thread intact so you can fix the anchor and retry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pull PR comments into branchdiff.&lt;/strong&gt; Same dialog, &lt;strong&gt;Pull from PR&lt;/strong&gt;. Existing review comments come down as local threads anchored to the same lines, with author and timestamp preserved. This is what makes branchdiff practical for re-review — open the PR locally on day three, pull the comments, see every thread inline, mark them resolved as the author addresses them, push the resolutions back.&lt;/p&gt;

&lt;p&gt;Sync requires your local HEAD to match the PR head and your working tree to be clean. branchdiff surfaces both constraints in the dialog with a one-line explanation (&lt;code&gt;git pull --rebase&lt;/code&gt;, &lt;code&gt;git stash&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;This is the only cloud round-trip in the whole tool. No telemetry, no remote diff service. Wipe &lt;code&gt;~/.branchdiff/&lt;/code&gt; and there is nothing in any backend with your data.&lt;/p&gt;




&lt;h2&gt;
  
  
  A concrete 90-second flow
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxlsdj6pp8ld5cjg4x487.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxlsdj6pp8ld5cjg4x487.png" alt="90-Second PR Review Flow" width="800" height="240"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Teammate posts: "PR is up — https://github.com/acme/api/pull/482"&lt;/span&gt;
branchdiff https://github.com/acme/api/pull/482
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browser opens in about three seconds. Toolbar shows &lt;code&gt;#482&lt;/code&gt; with a green dot. Reviewer pills: one teammate already approved, one pending. You scroll the diff, mark a few files viewed, drop two &lt;code&gt;[suggestion]&lt;/code&gt; comments and one &lt;code&gt;[must-fix]&lt;/code&gt;. Click &lt;code&gt;#482&lt;/code&gt; → &lt;strong&gt;Push to PR&lt;/strong&gt;. Toast: "Pushed 3 comments." Click &lt;strong&gt;Approve&lt;/strong&gt; in the dropdown. The toolbar updates to show your green pill.&lt;/p&gt;

&lt;p&gt;Total wall-clock time: 90 seconds from "teammate posts link" to "PR has your review on it." The work of &lt;em&gt;reading&lt;/em&gt; the diff is unchanged. Everything around it shrunk.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this matters for AI-assisted review
&lt;/h2&gt;

&lt;p&gt;AI is now part of almost every review, whether the team has formalised it or not. Engineers paste diffs into ChatGPT, run Copilot review, pipe &lt;code&gt;git diff&lt;/code&gt; into &lt;code&gt;claude&lt;/code&gt;. Done ad-hoc, that AI usage drifts — different people use different prompts, comments do not land on the right lines, and the context never makes it back to the PR.&lt;/p&gt;

&lt;p&gt;branchdiff gives that AI usage a controlled, repeatable surface: the same diff the human is looking at, the same line numbers, the same comment threads, with explicit &lt;code&gt;branchdiff agent&lt;/code&gt; commands so the AI cannot wander outside the review. Every AI comment lands on a real line, with a severity tag, in the same session as your manual comments. You can dismiss, resolve, or push selectively. The AI cannot post directly to the PR — it goes through the same command surface you use, and you stay the gate.&lt;/p&gt;

&lt;p&gt;The next two posts in this series go deeper: self-review on your own branch before the PR opens, and AI-assisted review of teammates' PRs that syncs back to the canonical thread.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it honestly saves you
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fewer tab switches.&lt;/strong&gt; Diff, comments, AI assistant, and PR lifecycle all live on one local page that loads in milliseconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A consistent UX across GitHub and Bitbucket.&lt;/strong&gt; Review keystrokes are the same on either side — engineers who straddle both platforms stop having to learn two UIs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No re-reading after a force-push.&lt;/strong&gt; Viewed / stale tracking remembers which files you have already looked at. You re-read only the parts that actually changed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A predictable AI surface.&lt;/strong&gt; Systematic, tagged, dismissable. Not an ad-hoc paste-into-ChatGPT that leaves no trace.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Where it stops
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No CI.&lt;/strong&gt; The platform's checks are still the gate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No cloud storage.&lt;/strong&gt; Wipe &lt;code&gt;~/.branchdiff/&lt;/code&gt; and you lose local drafts that have not been pushed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No multi-reply thread push.&lt;/strong&gt; Back-and-forth conversations belong on the PR.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No branch protection bypass.&lt;/strong&gt; Merge actions still respect required reviews and status checks on the platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No full PR page replacement.&lt;/strong&gt; The PR description, linked issues, and CI status block still live on the platform. &lt;em&gt;Open in browser&lt;/em&gt; in the toolbar is one click away.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tool is intentionally small. It does the part that benefits from being local, and gets out of the way of the part that benefits from being on the platform.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it on a PR you already have open
&lt;/h2&gt;

&lt;p&gt;Install from the &lt;a href="https://encryptioner.github.io/branchdiff-releases/" rel="noopener noreferrer"&gt;branchdiff releases page&lt;/a&gt; — install instructions, changelogs, and uninstall steps are all there. Quickstart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @encryptioner/branchdiff
&lt;span class="c"&gt;# or: pip install branchdiff&lt;/span&gt;
&lt;span class="c"&gt;# or: brew tap encryptioner/branchdiff https://github.com/encryptioner/branchdiff-releases \&lt;/span&gt;
&lt;span class="c"&gt;#          &amp;amp;&amp;amp; brew install branchdiff&lt;/span&gt;

branchdiff https://github.com/your-org/your-repo/pull/123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click the PR badge in the toolbar, watch the reviewer pills populate, push a comment back, check that it appears on the PR. The point is not "branchdiff vs. GitHub" — it is to see where your current review workflow keeps a tab open that does not need to be open.&lt;/p&gt;




&lt;h2&gt;
  
  
  Let's Connect
&lt;/h2&gt;

&lt;p&gt;I am always excited to hear what you are building. If you found this guide helpful, have questions, or just want to share your code review setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://encryptioner.github.io" rel="noopener noreferrer"&gt;encryptioner.github.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn&lt;/strong&gt;: &lt;a href="https://www.linkedin.com/in/mir-mursalin-ankur" rel="noopener noreferrer"&gt;Mir Mursalin Ankur&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Encryptioner" rel="noopener noreferrer"&gt;@Encryptioner&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;X (Twitter)&lt;/strong&gt;: &lt;a href="https://twitter.com/AnkurMursalin" rel="noopener noreferrer"&gt;@AnkurMursalin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical Writing&lt;/strong&gt;: &lt;a href="https://nerddevs.com/author/ankur/" rel="noopener noreferrer"&gt;Nerddevs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Support&lt;/strong&gt;: &lt;a href="https://www.supportkori.com/mirmursalinankur" rel="noopener noreferrer"&gt;SupportKori&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;branchdiff releases, changelog, install &amp;amp; uninstall guide: &lt;a href="https://encryptioner.github.io/branchdiff-releases/" rel="noopener noreferrer"&gt;encryptioner.github.io/branchdiff-releases&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>github</category>
      <category>productivity</category>
      <category>showdev</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Claude Code Configuration Blueprint: The Complete Guide for Production Teams</title>
      <dc:creator>Mir Mursalin Ankur</dc:creator>
      <pubDate>Sun, 15 Mar 2026 05:01:20 +0000</pubDate>
      <link>https://forem.com/mir_mursalin_ankur/claude-code-configuration-blueprint-the-complete-guide-for-production-teams-557p</link>
      <guid>https://forem.com/mir_mursalin_ankur/claude-code-configuration-blueprint-the-complete-guide-for-production-teams-557p</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Configure Claude Code once — get security, reusable workflows, specialized agents,&lt;br&gt;
and cross-session intelligence across every project.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Who This Is For
&lt;/h2&gt;

&lt;p&gt;You've used Claude Code on a project or two. You know the basics. Now you want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stop Claude from touching secrets, credentials, or system files&lt;/li&gt;
&lt;li&gt;Share team standards via git without leaking personal preferences&lt;/li&gt;
&lt;li&gt;Create reusable workflows (spec, plan, implement, review, ship)&lt;/li&gt;
&lt;li&gt;Make Claude remember lessons across sessions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide gives you copy-paste configs and explains the &lt;strong&gt;why&lt;/strong&gt; behind each decision.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Configuration Layers
&lt;/h2&gt;

&lt;p&gt;Claude Code loads configuration from multiple layers, each with a specific scope:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Personal (not in any repo)&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;~/.claude/settings.json        → Security (deny list for all projects)&lt;/span&gt;
  &lt;span class="s"&gt;~/.claude/CLAUDE.md            → Personal workflow preferences&lt;/span&gt;
  &lt;span class="s"&gt;~/.claude/skills/*/SKILL.md    → Reusable workflows&lt;/span&gt;
  &lt;span class="s"&gt;~/.claude/commands/*/SKILL.md  → Convenience shortcuts&lt;/span&gt;
  &lt;span class="s"&gt;~/.claude/rules/*.md           → File-specific patterns (glob matching)&lt;/span&gt;
  &lt;span class="s"&gt;~/.claude/agents/*.md          → Domain specialists&lt;/span&gt;

&lt;span class="na"&gt;Team-shared (committed to git)&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;.claude/settings.json          → Project permissions&lt;/span&gt;
  &lt;span class="s"&gt;CLAUDE.md                      → Coding standards, architecture&lt;/span&gt;
  &lt;span class="s"&gt;.claude/rules/*.md             → Team-specific patterns&lt;/span&gt;

&lt;span class="na"&gt;Personal overrides (gitignored)&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;.claude/settings.local.json    → Your project overrides&lt;/span&gt;
  &lt;span class="s"&gt;CLAUDE.local.md                → Your project preferences&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key rule:&lt;/strong&gt; Your project CLAUDE.md must be self-contained. Teammates don't have your global skills, agents, or personal CLAUDE.md — global files are your personal toolkit, project files are the team playbook.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 1: Security — The Permission System
&lt;/h2&gt;

&lt;p&gt;Permissions follow a strict hierarchy: &lt;strong&gt;Deny&lt;/strong&gt; (always blocks) → &lt;strong&gt;Ask&lt;/strong&gt; (prompts you) → &lt;strong&gt;Allow&lt;/strong&gt; (auto-approved). Deny always wins — this is what makes the system trustworthy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern Syntax
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bash(exact command)       → matches only that exact command
Bash(command *)           → matches command with any arguments (space before *)
Read(path/to/file)        → matches exact file
Read(path/**/*.json)      → matches glob pattern
Read(~/.ssh/**)           → ~ expands to home directory on any OS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Space matters:&lt;/strong&gt; &lt;code&gt;Bash(ls *)&lt;/code&gt; matches &lt;code&gt;ls -la&lt;/code&gt; but NOT &lt;code&gt;lsof&lt;/code&gt;. &lt;code&gt;Bash(ls*)&lt;/code&gt; matches both.&lt;/p&gt;

&lt;h3&gt;
  
  
  Global Settings (&lt;code&gt;~/.claude/settings.json&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Glob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Grep"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git status:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git diff:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(nvm use:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(node:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(ls:*)"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ask"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Bash(git push:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git commit:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(pnpm install:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(docker:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(curl:*)"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Bash(rm -rf:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(sudo:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git push --force:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(.env)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(.env.*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(~/.ssh/**)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(~/.aws/**)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(~/Library/Keychains/**)"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cross-OS coverage:&lt;/strong&gt; The deny list covers macOS (&lt;code&gt;~/Library/Keychains&lt;/code&gt;), Linux (&lt;code&gt;~/.local/share/keyrings&lt;/code&gt;), and Windows WSL (&lt;code&gt;/mnt/c/Users/*/AppData&lt;/code&gt;). One global config works on any machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Settings (&lt;code&gt;.claude/settings.example.json&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Glob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Grep"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(pnpm build:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(pnpm test:*)"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Read(.env)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(.env.*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(node_modules/**)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(dist/**)"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add to &lt;code&gt;.gitignore&lt;/code&gt;: &lt;code&gt;.claude/*&lt;/code&gt; and &lt;code&gt;!.claude/*.example*&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 2: CLAUDE.md — Teaching Claude Your Codebase
&lt;/h2&gt;

&lt;p&gt;A well-written CLAUDE.md turns Claude from a generic assistant into a team member who knows your conventions, pain points, and architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Instruction Budget Problem
&lt;/h3&gt;

&lt;p&gt;Frontier LLMs reliably follow roughly &lt;strong&gt;150–200 instructions&lt;/strong&gt; total. Claude Code's system prompt consumes ~50 slots before your CLAUDE.md loads. Instruction degradation is &lt;strong&gt;uniform&lt;/strong&gt; — every low-value rule you add actively makes your high-value rules less likely to be followed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Brevity isn't nice — it directly affects instruction compliance.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Template
&lt;/h3&gt;

&lt;p&gt;Write it for Claude, not humans. Don't duplicate your README.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# CLAUDE.md&lt;/span&gt;

&lt;span class="gu"&gt;## Commands&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`pnpm dev`&lt;/span&gt; — Start development server
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`pnpm build`&lt;/span&gt; — Production build
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`pnpm lint-fix`&lt;/span&gt; — Lint and fix all packages

&lt;span class="gu"&gt;## Architecture&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`packages/client`&lt;/span&gt; — Vue 3 frontend
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`packages/server`&lt;/span&gt; — Express API
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`packages/shared`&lt;/span&gt; — Types, DTOs, utilities

&lt;span class="gu"&gt;## Coding Standards&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; TypeScript strict mode, no &lt;span class="sb"&gt;`any`&lt;/span&gt; types
&lt;span class="p"&gt;-&lt;/span&gt; Absolute imports only (no &lt;span class="sb"&gt;`../../`&lt;/span&gt;)
&lt;span class="p"&gt;-&lt;/span&gt; API payloads: declare as typed constants

&lt;span class="gu"&gt;## PR Review Rules&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; &lt;span class="gs"&gt;**Type Safety**&lt;/span&gt; — Never use &lt;span class="sb"&gt;`any`&lt;/span&gt;. Typed constants for API payloads
&lt;span class="p"&gt;2.&lt;/span&gt; &lt;span class="gs"&gt;**Dead Code**&lt;/span&gt; — Remove unused imports, variables, i18n keys
&lt;span class="p"&gt;3.&lt;/span&gt; &lt;span class="gs"&gt;**Error Handling**&lt;/span&gt; — Wrap all API calls in try/catch

&lt;span class="gu"&gt;## Critical Gotchas&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Always use projection in DB queries — never fetch entire documents
&lt;span class="p"&gt;-&lt;/span&gt; Server error messages MUST come from constant files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Progressive Disclosure
&lt;/h3&gt;

&lt;p&gt;For larger projects, use a &lt;strong&gt;progressive disclosure&lt;/strong&gt; pattern — put detailed docs in a separate directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Detailed Documentation&lt;/span&gt;
When working on specific areas, read the relevant doc first:
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`docs/Agent/database_schema.md`&lt;/span&gt; — Data model and relationships
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`docs/Agent/payment_flow.md`&lt;/span&gt; — Payment gateway integration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Works vs. What Doesn't
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Do&lt;/th&gt;
&lt;th&gt;Don't&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Include commands Claude needs&lt;/td&gt;
&lt;td&gt;Include formatting rules (use linter + hooks)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Document non-obvious patterns&lt;/td&gt;
&lt;td&gt;Document obvious language features&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List real PR review pain points&lt;/td&gt;
&lt;td&gt;Duplicate your README&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keep it under 200 lines&lt;/td&gt;
&lt;td&gt;Include aspirational rules nobody follows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Part 3: Rules — Enforcing Patterns with Globs
&lt;/h2&gt;

&lt;p&gt;For patterns that must apply across specific file types, use &lt;strong&gt;rules&lt;/strong&gt; with glob matching. Place them in &lt;code&gt;~/.claude/rules/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Common coding mistakes — enforced across all projects&lt;/span&gt;
&lt;span class="na"&gt;globs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*.ts,*.tsx,*.vue,*.js,*.jsx"&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# Coding Gotchas&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Never use relative imports (&lt;span class="sb"&gt;`../../`&lt;/span&gt;) — always absolute paths
&lt;span class="p"&gt;-&lt;/span&gt; Never introduce &lt;span class="sb"&gt;`any`&lt;/span&gt; types — find or create the proper type
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When Claude edits files matching the glob, these rules apply automatically.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;CLAUDE.md&lt;/th&gt;
&lt;th&gt;Rules (&lt;code&gt;~/.claude/rules/&lt;/code&gt;)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scope&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Per-project&lt;/td&gt;
&lt;td&gt;Global (all projects)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Content&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Architecture, commands, standards&lt;/td&gt;
&lt;td&gt;File-specific patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trigger&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Loads when working in directory&lt;/td&gt;
&lt;td&gt;Loads when file matches glob&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Teaching your codebase&lt;/td&gt;
&lt;td&gt;Enforcing patterns across projects&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For teams: commit project-specific rules to &lt;code&gt;.claude/rules/&lt;/code&gt; in your repo.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 4: Plugins &amp;amp; Agents — Instant Capabilities
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Plugins
&lt;/h3&gt;

&lt;p&gt;Plugins are pre-built packages from the community. One install command gives you agents, MCP servers, and skills:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude plugins:install context7 superpowers code-review feature-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Recommended stack:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plugin&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;context7&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Live, version-specific documentation lookup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;feature-dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Codebase exploration + architecture design agents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;superpowers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TDD, debugging, brainstorming, parallel agents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;code-review&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Multi-agent PR review with confidence scoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;playwright&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Browser automation and E2E testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;commit-commands&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Streamlined git commit, push, and PR creation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;MCP Servers&lt;/strong&gt; — Many plugins include MCP (Model Context Protocol) servers that bridge Claude Code with external systems (databases, APIs, task trackers). No additional configuration needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom Marketplaces&lt;/strong&gt; — Add team-specific or community plugins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extraKnownMarketplaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"my-team"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"github"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"repo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-team/claude-plugins"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Agents
&lt;/h3&gt;

&lt;p&gt;Agents are pre-configured specialists you can call for specific tasks. Unlike plugins (which add tools), agents add expertise.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Example Agents&lt;/th&gt;
&lt;th&gt;When to Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Engineering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;backend-architect&lt;/code&gt;, &lt;code&gt;frontend-developer&lt;/code&gt;, &lt;code&gt;ai-engineer&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Domain-specific architecture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Testing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;test-writer-fixer&lt;/code&gt;, code-review agents&lt;/td&gt;
&lt;td&gt;Test strategy, bug fixing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Operations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;analytics-reporter&lt;/code&gt;, &lt;code&gt;finance-tracker&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Data analysis, reporting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Development&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;code-explorer&lt;/code&gt;, &lt;code&gt;code-architect&lt;/code&gt;, &lt;code&gt;rapid-prototyper&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Codebase understanding, MVPs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Instead of explaining React Server Components patterns, say &lt;em&gt;"Use the frontend-developer agent to build this with Server Components."&lt;/em&gt; The agent already knows the patterns, has React-specific tools, and follows frontend best practices automatically.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Plugins&lt;/th&gt;
&lt;th&gt;Agents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What they add&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tools, MCP servers, skills&lt;/td&gt;
&lt;td&gt;Domain expertise and focus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Adding capabilities&lt;/td&gt;
&lt;td&gt;Complex tasks requiring depth&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Part 5: Skills &amp;amp; Commands — Building Workflows
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Skills vs. Commands
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;
&lt;strong&gt;Commands&lt;/strong&gt; (&lt;code&gt;~/.claude/commands/&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;
&lt;strong&gt;Skills&lt;/strong&gt; (&lt;code&gt;~/.claude/skills/&lt;/code&gt;)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Convenience shortcuts&lt;/td&gt;
&lt;td&gt;Workflow orchestration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Examples&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/commit&lt;/code&gt;, &lt;code&gt;/loop&lt;/code&gt;, &lt;code&gt;/rewind&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/spec&lt;/code&gt;, &lt;code&gt;/implement&lt;/code&gt;, &lt;code&gt;/grill&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scope&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single operations&lt;/td&gt;
&lt;td&gt;Multi-step processes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Quick actions&lt;/td&gt;
&lt;td&gt;Structured workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use a &lt;strong&gt;command&lt;/strong&gt; for quick shortcuts. Use a &lt;strong&gt;skill&lt;/strong&gt; for multi-step workflows with decision points.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Spec-to-Ship Workflow
&lt;/h3&gt;

&lt;p&gt;The real power of skills comes from chaining them into a complete development workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/scope (optional) → /spec → /grill (auto) → /plan-work → /grill (auto) → /implement → /verify → /ship
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The ceremony scales with the risk:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task Type&lt;/th&gt;
&lt;th&gt;Workflow&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;New product&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/scope&lt;/code&gt; → full chain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full chain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Minor enhancement&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Light spec → plan → implement → verify → ship&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bug fix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bug spec or skip → plan → implement → verify → ship&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hotfix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Inline plan → implement → verify → ship&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Key Skills
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Skill&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/scope&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rapid product scoping — forces "ONE thing" question, maps user journey, identifies cost drivers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/spec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clarified technical spec with Change Log for tracking mid-implementation updates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/grill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hard-critic review through 7 lenses (security, architecture, testing, etc.) with severity levels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/plan-work&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Phase-by-phase implementation plan traced to spec requirements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/implement&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Executes plans with mid-implementation discovery handling (minor/moderate/significant tiers)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/verify&lt;/code&gt; → &lt;code&gt;/pre-review&lt;/code&gt; → &lt;code&gt;/ship&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Type check → spec requirement walkthrough → branch management + PR creation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Ticket Directory Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;ai/TICKET-146/&lt;/span&gt;
&lt;span class="s"&gt;├── requirements/&lt;/span&gt;
&lt;span class="s"&gt;│   ├── original-requirement.md&lt;/span&gt;    &lt;span class="c1"&gt;# Raw requirement (verbatim)&lt;/span&gt;
&lt;span class="s"&gt;│   ├── spec.md&lt;/span&gt;                    &lt;span class="c1"&gt;# Clarified technical spec&lt;/span&gt;
&lt;span class="s"&gt;│   └── grill-log.md&lt;/span&gt;              &lt;span class="c1"&gt;# Review findings&lt;/span&gt;
&lt;span class="s"&gt;├── plans/&lt;/span&gt;
&lt;span class="s"&gt;│   ├── overview.md&lt;/span&gt;               &lt;span class="c1"&gt;# High-level plan&lt;/span&gt;
&lt;span class="s"&gt;│   ├── phase-1-data-model.md&lt;/span&gt;     &lt;span class="c1"&gt;# Detailed phase plan&lt;/span&gt;
&lt;span class="s"&gt;│   └── grill-log.md&lt;/span&gt;              &lt;span class="c1"&gt;# Plan review findings&lt;/span&gt;
&lt;span class="s"&gt;└── tests/&lt;/span&gt;
    &lt;span class="s"&gt;└── manual-test-cases.csv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure makes work &lt;strong&gt;resumable across sessions&lt;/strong&gt; — future sessions read spec/plan files to pick up where you left off.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building Your Own Skills
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-skill&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Use when the user says "plan this" or presents a multi-file task&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# Skill Title&lt;/span&gt;
&lt;span class="gu"&gt;## When to Use&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [Trigger conditions]

&lt;span class="gu"&gt;## Process&lt;/span&gt;
&lt;span class="gu"&gt;### Step 1: [Name]&lt;/span&gt;
[Instructions Claude follows]

&lt;span class="gu"&gt;## Rules&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [Hard constraints]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Three tips:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write instructions TO Claude, not documentation ABOUT a process&lt;/li&gt;
&lt;li&gt;Include trigger phrases in the description&lt;/li&gt;
&lt;li&gt;Keep each skill focused — one workflow, one skill&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Part 6: Advanced Patterns — Hooks, Subagents, Worktrees
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Hooks — Automated Quality Gates
&lt;/h3&gt;

&lt;p&gt;Hooks run automatically at lifecycle events. Two types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;prompt&lt;/code&gt;&lt;/strong&gt; — Claude evaluates a condition and acts (block, remind, warn)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;command&lt;/code&gt;&lt;/strong&gt; — Runs a shell command directly (always executes)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"If this is a git commit, verify lint and type-check have been run. If not, block with: 'Run lint and type-check before committing.'"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write|Edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"If the file is TypeScript and more than 3 TS files have been edited since the last type-check, remind: 'Consider running type-check before continuing.'"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Notification"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"notify-send 'Claude Code' 'Awaiting your input'"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use hooks instead of CLAUDE.md for linting rules&lt;/strong&gt; — LLMs are expensive compared to linters. Hooks free up instruction budget for things only CLAUDE.md can teach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subagents — Keeping Context Clean
&lt;/h3&gt;

&lt;p&gt;For complex tasks, spawn subagents to handle research, exploration, or parallel work. Subagents run in separate contexts and return distilled findings — keeping your main conversation focused on implementation.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Research&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Investigating a library before using it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Exploration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Understanding a large codebase section&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Parallel work&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Running independent analyses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Heavy debugging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deep investigation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code review&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multiple reviewers checking different aspects&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Many skills use subagents automatically. &lt;code&gt;/grill&lt;/code&gt; spawns review agents for different aspects. &lt;code&gt;/implement&lt;/code&gt; may spawn research agents for unfamiliar libraries.&lt;/p&gt;

&lt;h3&gt;
  
  
  Worktrees — Isolated Development
&lt;/h3&gt;

&lt;p&gt;For feature work that needs isolation, use &lt;strong&gt;git worktrees&lt;/strong&gt;. Claude can create and manage worktrees, giving you a clean environment for each branch.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Benefit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multiple features&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Work on several branches simultaneously&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Parallel testing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Test approaches in isolated environments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code review&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Review PR branches while keeping main stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hotfixes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fix production without interrupting feature work&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Part 7: Automation — Auto-Invocation &amp;amp; Memory
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Auto-Invocation — Skills That Trigger Themselves
&lt;/h3&gt;

&lt;p&gt;Both plugins and skills support &lt;strong&gt;auto-invocation&lt;/strong&gt; — Claude reads the skill's description and triggers it automatically when your request matches. You don't need to remember slash commands.&lt;/p&gt;

&lt;p&gt;To make auto-invocation reliable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Be specific with trigger phrases&lt;/strong&gt; — vague descriptions lead to false activations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Include negative triggers&lt;/strong&gt; — "Do NOT use for TDD projects — use /plan-tdd instead"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test with natural language&lt;/strong&gt; — try different phrasings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example: With &lt;code&gt;/grill&lt;/code&gt; configured to trigger on "grill this", "poke holes", or "stress test this", simply saying &lt;em&gt;"poke holes in this spec"&lt;/em&gt; auto-triggers the workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Auto Memory — Cross-Session Intelligence
&lt;/h3&gt;

&lt;p&gt;Claude Code maintains a persistent memory directory per project at &lt;code&gt;~/.claude/projects/&amp;lt;project-path&amp;gt;/memory/&lt;/code&gt;. The &lt;code&gt;MEMORY.md&lt;/code&gt; file (first 200 lines) loads into every conversation automatically — no manual setup needed.&lt;/p&gt;

&lt;p&gt;Claude updates this file as it learns your project: architectural patterns, common mistakes, file paths that matter. The &lt;code&gt;/ship&lt;/code&gt; skill captures learnings at the end of each ticket automatically, so memory grows organically across sessions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 8: Putting It All Together
&lt;/h2&gt;

&lt;h3&gt;
  
  
  New Project Setup (5 Minutes)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create &lt;code&gt;CLAUDE.md&lt;/code&gt; — commands, architecture, coding standards, PR review rules, critical gotchas&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;.claude/settings.example.json&lt;/code&gt; — project-specific deny list&lt;/li&gt;
&lt;li&gt;Optionally create &lt;code&gt;.claude/rules/*.md&lt;/code&gt; — project-specific patterns with glob matching&lt;/li&gt;
&lt;li&gt;Add to &lt;code&gt;.gitignore&lt;/code&gt;: &lt;code&gt;.claude/*&lt;/code&gt; and &lt;code&gt;!.claude/*.example*&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Ensure &lt;code&gt;~/.claude/settings.json&lt;/code&gt; has the global security deny list&lt;/li&gt;
&lt;li&gt;Install plugins: &lt;code&gt;claude plugins:install context7 superpowers code-review feature-dev&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  How the Layers Work in Practice
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Read .env.local"             → Project deny  → BLOCKED
"pnpm add lodash"             → Ask rule      → Prompts you
"git push --force"            → Global deny   → BLOCKED (even with --dangerously-skip-permissions)
"Read src/components/App.vue" → Allow         → AUTO-APPROVED
"This test is failing"        → Auto-invoke   → debugging skill triggers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Quick Reference
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Shared?&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.claude/settings.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Global security (deny list)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Personal workflow preferences&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.claude/skills/*/SKILL.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Custom workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.claude/commands/*/SKILL.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Convenience shortcuts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.claude/rules/*.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;File-specific patterns (glob)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.claude/agents/*.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Domain specialists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plugins&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Pre-built agents + MCP + skills&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.claude/settings.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Team project permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.claude/rules/*.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Team-specific patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Team coding standards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.claude/projects/*/memory/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Cross-session memory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;Layer your configuration by scope and shareability:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Global settings&lt;/strong&gt; protect your system — secrets, credentials, destructive commands&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project settings&lt;/strong&gt; protect project resources — config files, build artifacts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLAUDE.md&lt;/strong&gt; encodes team standards (keep it under 200 lines — every low-value instruction degrades high-value ones)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rules&lt;/strong&gt; enforce file-specific patterns with glob matching — language conventions, gotchas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agents&lt;/strong&gt; provide domain expertise — backend, frontend, AI, testing, operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plugins&lt;/strong&gt; add instant capabilities — code review, testing, documentation, MCP servers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills&lt;/strong&gt; encode workflows (multi-step) — &lt;strong&gt;Commands&lt;/strong&gt; handle shortcuts (single actions)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hooks&lt;/strong&gt; automate quality gates — free up CLAUDE.md instruction slots&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subagents&lt;/strong&gt; keep context clean — offload research, exploration, parallel analysis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Worktrees&lt;/strong&gt; enable isolated development — multiple branches without switching&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory&lt;/strong&gt; captures lessons learned — no reteaching the same thing twice&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-invocation&lt;/strong&gt; makes skills trigger themselves — no slash commands needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The spec-to-ship workflow isn't bureaucracy. It's front-loading the thinking so implementation is mechanical. A grilled spec catches the bug that would have taken a day to debug. A phased plan prevents the "I changed 47 files and nothing type-checks" disaster.&lt;/p&gt;

&lt;p&gt;Configure once. Benefit on every session, every project, every team member.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Official:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.anthropic.com/en/docs/claude-code" rel="noopener noreferrer"&gt;Claude Code Documentation&lt;/a&gt; — Permissions, hooks, settings, skills&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/anthropics/claude-code/blob/main/plugins/README.md" rel="noopener noreferrer"&gt;Plugins README&lt;/a&gt; — Plugin marketplace&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Community:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.humanlayer.dev/blog/writing-a-good-claude-md" rel="noopener noreferrer"&gt;Writing a Good CLAUDE.md — HumanLayer&lt;/a&gt; — Instruction budgets research&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aiorg.dev/blog/claude-code-hooks" rel="noopener noreferrer"&gt;Claude Code Hooks: Complete Guide — aiorg.dev&lt;/a&gt; — 20+ hook examples&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.eesel.ai/blog/claude-code-permissions" rel="noopener noreferrer"&gt;Claude Code Permissions Guide — eesel.ai&lt;/a&gt; — Security setup&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Let's Connect
&lt;/h2&gt;

&lt;p&gt;I'm always excited to hear about what you're building! If you found this guide helpful, have questions, or just want to share your Claude Code journey:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://encryptioner.github.io" rel="noopener noreferrer"&gt;encryptioner.github.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn&lt;/strong&gt;: &lt;a href="https://www.linkedin.com/in/mir-mursalin-ankur" rel="noopener noreferrer"&gt;Mir Mursalin Ankur&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Encryptioner" rel="noopener noreferrer"&gt;@Encryptioner&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;X (Twitter)&lt;/strong&gt;: &lt;a href="https://twitter.com/AnkurMursalin" rel="noopener noreferrer"&gt;@AnkurMursalin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical Writing&lt;/strong&gt;: &lt;a href="https://nerddevs.com/author/ankur/" rel="noopener noreferrer"&gt;Nerddevs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>code</category>
      <category>development</category>
    </item>
    <item>
      <title>Navigating Bangladesh's Software Industry: A Practical Guide for Developers in 2026</title>
      <dc:creator>Mir Mursalin Ankur</dc:creator>
      <pubDate>Tue, 20 Jan 2026 15:39:57 +0000</pubDate>
      <link>https://forem.com/mir_mursalin_ankur/navigating-bangladeshs-software-industry-a-practical-guide-for-developers-in-2026-22ob</link>
      <guid>https://forem.com/mir_mursalin_ankur/navigating-bangladeshs-software-industry-a-practical-guide-for-developers-in-2026-22ob</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo6gvoqyf2h9teckk7njs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo6gvoqyf2h9teckk7njs.png" alt="avigating Bangladesh's Software Industry - Practical Guide for Developers" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bangladesh's software industry has grown rapidly over the past decade. From $1.3 billion in exports to &lt;strong&gt;2,650+ ICT companies&lt;/strong&gt; employing hundreds of thousands of developers, the sector has transformed from a niche outsourcing destination into a serious global technology hub. The country now boasts &lt;strong&gt;over 1 million active freelancers&lt;/strong&gt; (2026), making it a global freelancing powerhouse.&lt;/p&gt;

&lt;p&gt;But the landscape has changed dramatically. &lt;strong&gt;The entry-level market is saturated.&lt;/strong&gt; Senior talent is scarce. And the post-2024 reality—marked by internet shutdowns, tax policy uncertainty, and global competition—means the easy growth is over.&lt;/p&gt;

&lt;p&gt;This guide is about action—not analysis.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Brutal Truth: Too Many Developers, Not Enough Jobs
&lt;/h2&gt;

&lt;p&gt;Let's start with what's actually happening in Bangladesh.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We have a massive oversupply problem.&lt;/strong&gt; Every year, hundreds of universities and colleges pump out thousands of computer science graduates. Add to that the hundreds of online training institutes, bootcamps, and YouTube tutorials creating "developers" in 3-6 months.&lt;/p&gt;

&lt;p&gt;The result? A glut of low-skill junior developers fighting for the same entry-level positions.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Experience Level&lt;/th&gt;
&lt;th&gt;Jobs Available&lt;/th&gt;
&lt;th&gt;Candidates&lt;/th&gt;
&lt;th&gt;Competition&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Entry-level&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~12,000&lt;/td&gt;
&lt;td&gt;30,000+&lt;/td&gt;
&lt;td&gt;2.5x oversubscribed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mid-level&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~8,000&lt;/td&gt;
&lt;td&gt;20,000&lt;/td&gt;
&lt;td&gt;2.5x oversubscribed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Senior&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~3,000&lt;/td&gt;
&lt;td&gt;5,000&lt;/td&gt;
&lt;td&gt;1.7x oversubscribed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Specialized&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~1,000&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;0.5x (talent shortage)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; If you're doing what everyone else is doing (basic web development, tutorial-heavy learning, no specialization), you're competing with 2-3 other people for every job.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Is Happening: The Training Glut
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The University Problem
&lt;/h3&gt;

&lt;p&gt;Bangladesh has &lt;strong&gt;150+ universities&lt;/strong&gt; offering computer science and engineering programs. Many of these programs are outdated, teaching curriculums from 5-10 years ago. Students graduate knowing theory but unable to build real applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common issues:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Professors who haven't worked in the industry in decades&lt;/li&gt;
&lt;li&gt;Outdated technologies (teaching Java applets in 2026)&lt;/li&gt;
&lt;li&gt;Zero emphasis on practical projects&lt;/li&gt;
&lt;li&gt;No industry exposure or internships&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Online Course Explosion
&lt;/h3&gt;

&lt;p&gt;YouTube, Udemy, Coursera, and local platforms have made programming education accessible to everyone. But this has created a new problem: &lt;strong&gt;tutorial developers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Thousands of graduates complete 20+ tutorials but can't:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build an original project without following instructions&lt;/li&gt;
&lt;li&gt;Debug their own code&lt;/li&gt;
&lt;li&gt;Write clean, maintainable code&lt;/li&gt;
&lt;li&gt;Explain technical decisions&lt;/li&gt;
&lt;li&gt;Work in a team environment&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Bootcamp Industry
&lt;/h3&gt;

&lt;p&gt;Dozens of "become a full-stack developer in 3 months" bootcamps promise quick careers. They teach students to copy-paste code, pass interviews, and get jobs. But these developers struggle when faced with real-world complexity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The result:&lt;/strong&gt; Companies are flooded with resumes from graduates who look good on paper but can't deliver. They've stopped trusting fresh graduates and now require 2-3 years of experience—even for "entry-level" positions.&lt;/p&gt;




&lt;h2&gt;
  
  
  The New Reality: Global Competition + AI
&lt;/h2&gt;

&lt;p&gt;Just when Bangladesh's oversupply problem was peaking, two global shifts made things harder:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Remote Work Saturation
&lt;/h3&gt;

&lt;p&gt;During COVID, remote work exploded. Bangladeshi developers could finally access international jobs. But this also meant they were now competing directly with developers from India, Pakistan, Eastern Europe, and the Philippines—all of whom offer similar rates.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. AI Coding Tools
&lt;/h3&gt;

&lt;p&gt;AI tools like ChatGPT, GitHub Copilot, and Claude have changed the entry-level equation globally. A Stanford study found that &lt;strong&gt;employment for software developers aged 22-25 dropped nearly 20% between 2022-2025&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this hits Bangladesh hard:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Entry-level tech hiring globally decreased 25% in 2024&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;70% of hiring managers believe AI can do the work of interns&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Companies asking: "Why hire a junior for $90K when GitHub Copilot costs $10?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tasks that junior developers used to do—writing boilerplate code, fixing bugs, writing test scripts—are now what AI tools handle well.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three Career Paths That Actually Work
&lt;/h2&gt;

&lt;p&gt;Despite the challenges, there are still paths to a successful career. You just need to be strategic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Path 1: Specialization Premium&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pick a high-value niche (embedded systems, AI/ML, security, DevOps)&lt;/li&gt;
&lt;li&gt;Become exceptional in it&lt;/li&gt;
&lt;li&gt;Charge 2-3x what generalist developers charge&lt;/li&gt;
&lt;li&gt;Competition: Low&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it works:&lt;/strong&gt; Specialized work is harder to automate and outsources can't easily replicate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Path 2: International Remote&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build skills for global market&lt;/li&gt;
&lt;li&gt;Work directly for US/European companies&lt;/li&gt;
&lt;li&gt;Earn in USD (3-5x local rates)&lt;/li&gt;
&lt;li&gt;Competition: Medium (requires English and self-marketing)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it works:&lt;/strong&gt; Local market is saturated; global market has talent shortages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Path 3: Local Premium&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Join top outsourcing companies (Cefalo, Enosis, Optimizely, Cheq)&lt;/li&gt;
&lt;li&gt;Work your way up through established career ladders&lt;/li&gt;
&lt;li&gt;Earn local premium rates&lt;/li&gt;
&lt;li&gt;Competition: High (everyone wants these jobs)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it works:&lt;/strong&gt; These companies have established client relationships and can pay more&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The AI Reality: Don't Ignore It, Don't Overrate It
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;85% of developers globally use AI tools&lt;/strong&gt;, but here's what most don't realize:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The challenges:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;46% distrust AI accuracy&lt;/strong&gt; (only 3% "highly trust" it)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;66% are frustrated by "AI solutions that are almost right, but not quite"&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;45% say debugging AI-generated code takes longer&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;67% spend more time debugging AI code&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;68% spend more time fixing security issues in AI code&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The reality:&lt;/strong&gt; AI makes you faster at routine tasks but can slow you down overall when you account for verification and debugging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this means for you:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Learn AI tools&lt;/strong&gt; – GitHub Copilot, Cursor, ChatGPT, Claude, Gemini are industry standards&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;But don't depend on them&lt;/strong&gt; – Understand what the code is actually doing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus on what AI can't do&lt;/strong&gt; – Architecture, security, business logic, client communication&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Developers resist using AI for high-responsibility tasks:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deployment and monitoring:&lt;/strong&gt; 76% don't use AI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project planning:&lt;/strong&gt; 69% don't use AI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's where your value lives.&lt;/p&gt;




&lt;h2&gt;
  
  
  Action Plan: Your First 5 Years
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Year 1: Foundations + Specialization Choice&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Months 1-6:&lt;/strong&gt; Pick your lane (don't try to do everything)

&lt;ul&gt;
&lt;li&gt;Web/Mobile? Generalist path, more competition&lt;/li&gt;
&lt;li&gt;Embedded Systems? Fewer competitors, higher pay&lt;/li&gt;
&lt;li&gt;AI/ML? Growing field, severe talent shortage&lt;/li&gt;
&lt;li&gt;DevOps/Cloud? Critical infrastructure, hard to find&lt;/li&gt;
&lt;li&gt;Security/Biometrics? Trust-sensitive, premium pay&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Months 7-12:&lt;/strong&gt; Build portfolio while learning

&lt;ul&gt;
&lt;li&gt;3-5 substantial projects (not tutorials)&lt;/li&gt;
&lt;li&gt;Learn AI tools—but don't depend on them&lt;/li&gt;
&lt;li&gt;Start freelancing on Upwork for real experience&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Year 2-3: First Job + Skill Deepening&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Target companies based on your chosen path&lt;/li&gt;
&lt;li&gt;Build real-world experience&lt;/li&gt;
&lt;li&gt;Start specializing deeper&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Salary target:&lt;/strong&gt; 30,000-50,000 taka/month&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Year 3-5: Seniority or Pivot&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Option A:&lt;/strong&gt; Grow in current company toward senior roles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Option B:&lt;/strong&gt; Jump to higher-paying company&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Option C:&lt;/strong&gt; Go full remote international&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Salary target:&lt;/strong&gt; 80,000-150,000+ taka/month&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Specialization Deep Dives
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Embedded Systems &amp;amp; IoT&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why pays premium:&lt;/strong&gt; Specialized knowledge, fewer engineers, global talent shortage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Market demand:&lt;/strong&gt; ~2,921 embedded systems job openings on major job portals&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key employers:&lt;/strong&gt; Samsung R&amp;amp;D Institute Bangladesh, Teton Electronics, BJIT Embedded, SIMEC, Subra Systems, Ninos IT Solution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills needed:&lt;/strong&gt; C/C++, firmware development, microcontrollers, RTOS, hardware-software integration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Salary range:&lt;/strong&gt; 60,000-180,000+ taka/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time to expertise:&lt;/strong&gt; 12-18 months&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI/ML &amp;amp; Data Science&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why pays premium:&lt;/strong&gt; Global talent shortage, high value work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills needed:&lt;/strong&gt; Python, TensorFlow/PyTorch, SQL, statistics, MLOps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Salary range:&lt;/strong&gt; 100,000-200,000+ taka/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time to expertise:&lt;/strong&gt; 18-24 months&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;DevOps &amp;amp; Cloud&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why pays premium:&lt;/strong&gt; Critical infrastructure, hard to find qualified people&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills needed:&lt;/strong&gt; Linux, AWS/Azure/GCP, Docker, Kubernetes, CI/CD, scripting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Salary range:&lt;/strong&gt; 120,000-250,000+ taka/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time to expertise:&lt;/strong&gt; 12-18 months&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Security &amp;amp; Biometrics&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why pays premium:&lt;/strong&gt; Trust-sensitive roles, regulatory requirements&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key employers:&lt;/strong&gt; Tiger IT, security roles at banks/telecom&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills needed:&lt;/strong&gt; Cryptography, network security, ethical hacking, compliance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Salary range:&lt;/strong&gt; 90,000-150,000+ taka/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time to expertise:&lt;/strong&gt; 12-18 months&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Targeting the Right Companies
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tier 1: International Outsourcing (Premium Pay)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cefalo:&lt;/strong&gt; Norwegian market, 65,000-220,000 taka/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enosis:&lt;/strong&gt; US/Europe clients, 50,000-220,000 taka/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimizely BD:&lt;/strong&gt; US company, 70,000-300,000 taka/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cheq BD:&lt;/strong&gt; US cybersecurity, 70,000-300,000 taka/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tero Labs:&lt;/strong&gt; 120,000-150,000 taka/month&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tier 2: Top Local Companies&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Brain Station 23:&lt;/strong&gt; 60,000-220,000 taka/month (800+ employees)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BJIT:&lt;/strong&gt; Japanese market, 30,000-220,000 taka/month (80% work from Japan)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Selise:&lt;/strong&gt; Swiss-Bangladeshi operations (~$37.9M revenue)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tier 3: High-Growth Startups&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pathao:&lt;/strong&gt; 65,000-180,000 taka/month + equity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chaldal:&lt;/strong&gt; 70,000-180,000 taka/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ShopUp, Shikho, Arogga:&lt;/strong&gt; Competitive packages + equity upside&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Salary Breakdown: The Real Numbers (2024-2025)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Salary Range (BDT/month)&lt;/th&gt;
&lt;th&gt;Salary Range (USD/month)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trainee/Intern&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0-15,000&lt;/td&gt;
&lt;td&gt;$0-135&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Junior Developer&lt;/strong&gt; (0-2 years)&lt;/td&gt;
&lt;td&gt;20,000-45,000&lt;/td&gt;
&lt;td&gt;$180-405&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Software Engineer&lt;/strong&gt; (2-5 years)&lt;/td&gt;
&lt;td&gt;50,000-80,000&lt;/td&gt;
&lt;td&gt;$450-720&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Senior Engineer&lt;/strong&gt; (5+ years)&lt;/td&gt;
&lt;td&gt;100,000-220,000&lt;/td&gt;
&lt;td&gt;$900-1,980&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tech Lead/Principal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;165,000-300,000&lt;/td&gt;
&lt;td&gt;$1,485-2,700&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The Remote Work Premium:&lt;/strong&gt; Engineers working remotely for international companies earn substantially more (~345,000 taka/month average)—2-4x higher than local salaries.&lt;/p&gt;




&lt;h2&gt;
  
  
  Training: What Actually Works in 2026
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;For Foundations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;BITM (BASIS):&lt;/strong&gt; Industry-aligned, most credible for placement&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;University CS programs:&lt;/strong&gt; Top-tier only (BUET, DU, KUET, RUET, NSU, BRAC, AIUB)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Poridhi.io:&lt;/strong&gt; Bangla-language, AI/ML tracks, 250+ hands-on labs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For Practical Skills:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Build real projects, not tutorials&lt;/strong&gt; – This is the #1 differentiator&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub portfolio&lt;/strong&gt; – Quality over quantity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Freelancing on Upwork&lt;/strong&gt; – Real client experience&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open source contributions&lt;/strong&gt; – Demonstrates collaboration skills&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI Tools (Learn, Don't Depend):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Copilot, Claude, Gemini, or Cursor&lt;/strong&gt; – Industry standard tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Practice validation&lt;/strong&gt; – Review AI code for security and correctness&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understand first, then automate&lt;/strong&gt; – Strong fundamentals matter more than AI skills&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Red Flags:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Courses that only teach syntax without projects&lt;/li&gt;
&lt;li&gt;"Become a full-stack developer in 3 months" claims&lt;/li&gt;
&lt;li&gt;No portfolio/real-world application&lt;/li&gt;
&lt;li&gt;Instructors who haven't worked in the industry&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"AI will replace developers"&lt;/strong&gt; – No, it won't. But developers who use AI will replace those who don't.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Common Mistakes to Avoid
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Mistake 1:&lt;/strong&gt; Tutorial addiction&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; You've completed 20 tutorials but can't build anything original&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; After learning basics, stop tutorials and build original projects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Mistake 2:&lt;/strong&gt; Jack of all trades&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; You know a little of everything, expert in nothing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Pick one stack and go deep&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Mistake 3:&lt;/strong&gt; Ignoring the market reality&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; "If I learn MERN stack, I'll get a job"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Look at what companies actually need. 30,000+ people know MERN. Fewer know embedded systems or DevOps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Mistake 4:&lt;/strong&gt; Relying on AI too much&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; Copy-pasting AI code without understanding it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Always validate. Security vulnerabilities in AI code are common.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Mistake 5:&lt;/strong&gt; Waiting for the "perfect job"&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; 6 months unemployed, waiting for top company&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Take the good-enough job, build skills, move up later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Mistake 6:&lt;/strong&gt; No specialization&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; Doing what everyone else is doing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Pick a niche. Embedded systems, security, DevOps, AI/ML—anything but generic web development.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Where the Industry Is Headed
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Short-term (2026-2027):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Post-2024 stabilization (after internet shutdown, white paper, tax changes)&lt;/li&gt;
&lt;li&gt;Entry-level jobs remain tough; senior/specialized roles in high demand&lt;/li&gt;
&lt;li&gt;AI adoption accelerates—teams that use AI effectively will win&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Medium-term (2027-2029):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developer role transforms from "coder" to "problem solver"&lt;/li&gt;
&lt;li&gt;Companies that don't hire juniors will eventually have no seniors&lt;/li&gt;
&lt;li&gt;New career pathways emerge for AI-augmented developers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Long-term (2029-2031):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Either: Software becomes a top export category ($5B+)&lt;/li&gt;
&lt;li&gt;Or: Stagnation at current levels ($1-2B)&lt;/li&gt;
&lt;li&gt;Difference depends on: Investment + Education quality + Policy stability&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Bangladesh's software industry is at an inflection point. The easy growth is over. The market is saturated with junior developers. AI has disrupted entry-level jobs globally. But this isn't the end—it's a transformation.&lt;/p&gt;

&lt;p&gt;The developers who thrive in 2026 and beyond will be the ones who:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Specialize&lt;/strong&gt; in areas where there's actual demand&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build genuine skills&lt;/strong&gt;—not tutorial certificates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use AI tools&lt;/strong&gt; without depending on them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Think architecturally&lt;/strong&gt;, not just syntactically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Communicate effectively&lt;/strong&gt; (English + technical explanation)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The free ride ended in 2024. What comes next depends on the choices you make.&lt;/p&gt;

&lt;p&gt;For those who build genuine skills and create real value: The opportunity is substantial.&lt;/p&gt;

&lt;p&gt;For those who want shortcuts: The door is closing.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Choose wisely.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Learning Platforms:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://poridhi.io" rel="noopener noreferrer"&gt;Poridhi.io&lt;/a&gt; - Bangla Software Engineering Courses&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bitm.org.bd" rel="noopener noreferrer"&gt;BITM - BASIS Institute of Technology &amp;amp; Management&lt;/a&gt; - Industry-aligned training&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.coursera.org" rel="noopener noreferrer"&gt;Coursera&lt;/a&gt; - University-level courses&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.edx.org" rel="noopener noreferrer"&gt;edX&lt;/a&gt; - MIT, Harvard courses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI Coding Tools (Learn, Don't Depend):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/features/copilot" rel="noopener noreferrer"&gt;GitHub Copilot&lt;/a&gt; - Industry standard&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cursor.sh" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt; - AI-native IDE&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://chat.openai.com" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt; - Code review and explanation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://claude.ai" rel="noopener noreferrer"&gt;Claude&lt;/a&gt; - Advanced coding assistant with long context&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gemini.google.com/" rel="noopener noreferrer"&gt;Gemini&lt;/a&gt; - Google's AI assistant&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Job Platforms:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.upwork.com" rel="noopener noreferrer"&gt;Upwork&lt;/a&gt; - Freelance work&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.toptal.com" rel="noopener noreferrer"&gt;Toptal&lt;/a&gt; - Elite freelance network&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.turing.com" rel="noopener noreferrer"&gt;Turing&lt;/a&gt; - Remote US jobs&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.facebook.com/groups/1336177993759909/" rel="noopener noreferrer"&gt;CSE Job Bangladesh Facebook Group&lt;/a&gt; - Local jobs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Career Development:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.linkedin.com" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; - Build your professional profile&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; - Showcase your code&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://stackoverflow.com" rel="noopener noreferrer"&gt;Stack Overflow&lt;/a&gt; - Learn and help others&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Bangladesh Industry Data (2026):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://vivasoftltd.com/best-software-companies-in-bangladesh/" rel="noopener noreferrer"&gt;Vivasoft: Top 20 Best Software Companies in Bangladesh 2026&lt;/a&gt; (January 6, 2026)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.ontiktechnology.com/blog/top-software-companies-in-bangladesh" rel="noopener noreferrer"&gt;Ontik Technology: Top 25 Software Companies in Bangladesh 2026&lt;/a&gt; (January 7, 2026)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.nextzen.com.bd/blog/top-10-best-software-companies-in-bangladesh-2026" rel="noopener noreferrer"&gt;Nextzen: Top 10 Best Software Companies in Bangladesh 2026&lt;/a&gt; (January 10, 2026)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.kaz.com.bd/blog/top-ai-developers-in-bangladesh-a-practical-guide-for-global-businesses-2026" rel="noopener noreferrer"&gt;Kaz Software: Top AI Developers in Bangladesh 2026&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://vocal.media/journal/the-rise-of-freelancing-in-bangladesh-shaping-the-digital-economy-in-2026" rel="noopener noreferrer"&gt;Vocal Media: The Rise of Freelancing in Bangladesh 2026&lt;/a&gt; - Over 1 million active freelancers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI Impact &amp;amp; Job Market Data:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://digitaleconomy.stanford.edu/app/uploads/2025/11/CanariesintheCoalMine_Nov25.pdf" rel="noopener noreferrer"&gt;Stanford Digital Economy Study: Canaries in the Coal Mine (2025)&lt;/a&gt; - 20% decline for devs aged 22-25&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cio.com/article/4062024/demand-for-junior-developers-softens-as-ai-takes-over.html" rel="noopener noreferrer"&gt;CIO: Demand for Junior Developers Softens as AI Takes Over&lt;/a&gt; - Entry-level hiring down 25% YoY&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://survey.stackoverflow.co/2025/ai" rel="noopener noreferrer"&gt;Stack Overflow Developer Survey 2025 - AI Section&lt;/a&gt; - 84% adoption, sentiment data&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.weforum.org/stories/2026/01/software-developers-ai-work/" rel="noopener noreferrer"&gt;World Economic Forum: Software Developers and AI Work (January 2026)&lt;/a&gt; - Learning priorities and trends&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://devops.com/survey-sees-wider-adoption-of-ai-coding-tools-creating-more-devops-challenges/" rel="noopener noreferrer"&gt;DevOps.com: AI Coding Tools Survey (January 2026)&lt;/a&gt; - Daily usage statistics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Salary &amp;amp; Company Data:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tahanima.github.io/salary-ranges-offered-by-bangladeshi-software-companies-for-different-positions/" rel="noopener noreferrer"&gt;Salary ranges offered by Bangladeshi Software Companies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ibos.io/best-software-companies-in-bangladesh-2026/" rel="noopener noreferrer"&gt;Best Software Companies in Bangladesh 2026 | iBOS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This guide is based on publicly available information and industry analysis as of January 2026. Specific situations vary by individual skills, location, and market conditions.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;p&gt;That's all!&lt;/p&gt;

&lt;p&gt;I hope you've found the article useful. I'll write more about the software engineering, career opportunities and overall ecosystem. Feel free to share your thoughts.&lt;/p&gt;

&lt;p&gt;Check more on&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://encryptioner.github.io" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/mir-mursalin-ankur" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Encryptioner" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/AnkurMursalin" rel="noopener noreferrer"&gt;X (Twitter)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nerddevs.com/author/ankur/" rel="noopener noreferrer"&gt;Nerddevs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>career</category>
      <category>softwaredevelopment</category>
      <category>bangladesh</category>
      <category>developers</category>
    </item>
    <item>
      <title>Inside Bangladesh's Software Industry: Companies, Models, and Opportunities</title>
      <dc:creator>Mir Mursalin Ankur</dc:creator>
      <pubDate>Thu, 15 Jan 2026 19:19:47 +0000</pubDate>
      <link>https://forem.com/mir_mursalin_ankur/inside-bangladeshs-software-industry-companies-models-and-opportunities-5a2c</link>
      <guid>https://forem.com/mir_mursalin_ankur/inside-bangladeshs-software-industry-companies-models-and-opportunities-5a2c</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjd7m2q1k91if6b4lnvl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjd7m2q1k91if6b4lnvl.png" alt="Inside Bangladesh's Software Industry: Companies, Models, and Opportunities" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you think about software outsourcing destinations, Bangladesh probably isn't the first name that comes to mind. But it should be.&lt;/p&gt;

&lt;p&gt;Beside the &lt;strong&gt;Bay of Bengal&lt;/strong&gt;, this country of 170 million people has built a diverse software ecosystem with companies doing remarkable work—from government systems that serve millions to products used by British Council and ACCA for global examinations.&lt;/p&gt;

&lt;p&gt;Let me walk you through the companies, what they actually do, and the business models that work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Industry at a Glance
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Current State (2024-2025):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;$1.3 billion+&lt;/strong&gt; in annual ICT exports&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;$840 million&lt;/strong&gt; in official software export revenue (FY 2023-24)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4,500+&lt;/strong&gt; registered IT/software companies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;400,000+&lt;/strong&gt; professionals employed&lt;/li&gt;
&lt;li&gt;Exports to &lt;strong&gt;80+ countries&lt;/strong&gt; worldwide&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Growth:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From $26 million in 2008 to over $1 billion today—a &lt;strong&gt;40x increase&lt;/strong&gt; in 16 years&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Bangladesh's Software Industry Exists
&lt;/h2&gt;

&lt;p&gt;Bangladesh has pulled off something remarkable over the past fifteen years. The government made a deliberate bet on IT services as an export industry, offering tax breaks, building infrastructure, and pushing digitization across public services. The result? An industry that went from essentially nothing to generating over a billion dollars annually.&lt;/p&gt;

&lt;p&gt;Here's what makes the model work: Bangladesh offers highly skilled English-speaking developers at competitive rates that deliver exceptional value. A senior software engineer in Dhaka might earn 100,000+ taka monthly (roughly $900+)—professional compensation that enables access to world-class engineering talent at reasonable investment levels.&lt;/p&gt;

&lt;p&gt;For American or European companies, the value proposition is compelling. You're not "saving money"—you're accessing talented engineers who bring strong technical skills, problem-solving abilities, and dedication to building quality products. The time zone works perfectly too—Bangladesh is well-positioned for both European and US clients, enabling real-time collaboration when needed.&lt;/p&gt;

&lt;p&gt;This created multiple distinct types of software businesses beyond just traditional outsourcing. Let's break them down.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Complete Business Model Breakdown
&lt;/h2&gt;

&lt;p&gt;Looking across Bangladesh's software landscape, you can categorize companies into &lt;strong&gt;seven main business models&lt;/strong&gt;—not just the two or three most people talk about:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Business Model&lt;/th&gt;
&lt;th&gt;% of Industry&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Top Companies&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pure Outsourcing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;40%&lt;/td&gt;
&lt;td&gt;Provide developers/teams to international clients on time-and-materials or fixed-price projects&lt;/td&gt;
&lt;td&gt;Brain Station 23, BJIT, Cefalo, Enosis, Vivasoft&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Government Contracts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;15%&lt;/td&gt;
&lt;td&gt;Specialize in public sector projects, national infrastructure, digitization initiatives&lt;/td&gt;
&lt;td&gt;Datasoft, Tiger IT, OrangeBD, Tappware, SoftBD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Product Companies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;12%&lt;/td&gt;
&lt;td&gt;Build and sell own software (SaaS, licenses, marketplace products)&lt;/td&gt;
&lt;td&gt;BDTask, Ollyo, REVE Systems, NerdDevs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hybrid Models&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8%&lt;/td&gt;
&lt;td&gt;Combine client work with own products for diversification&lt;/td&gt;
&lt;td&gt;Brain Station 23, NerdDevs, Kaz Software&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tech Startups&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;td&gt;VC-backed growth companies building consumer/enterprise platforms&lt;/td&gt;
&lt;td&gt;bKash, Pathao, ShopUp, Chaldal, Shikho&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Embedded Systems&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5%&lt;/td&gt;
&lt;td&gt;Firmware, IoT, hardware-software integration for electronics/devices&lt;/td&gt;
&lt;td&gt;Samsung R&amp;amp;D, BJIT Embedded, Teton Electronics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Freelance-to-Company&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6%&lt;/td&gt;
&lt;td&gt;Companies that started as freelancers on Upwork/Fiverr and grew into agencies&lt;/td&gt;
&lt;td&gt;Hundreds of small agencies across Dhaka/Chittagong&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Digital Services&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4%&lt;/td&gt;
&lt;td&gt;Software + SEO, content marketing, social media management&lt;/td&gt;
&lt;td&gt;Bizcope, Red Sparrow Digital, HYPE DHAKA&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Let's dive into each category with the major players you need to know.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Government Contract Giants
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Datasoft Systems Bangladesh Limited
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://datasoftbd.com" rel="noopener noreferrer"&gt;https://datasoftbd.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Founded:&lt;/strong&gt; 1998&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Employees:&lt;/strong&gt; 300+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Certification:&lt;/strong&gt; CMMI Level 5 (first in Bangladesh)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Datasoft was the first mover in Bangladesh's software industry, and they focused on something many others avoided—government contracts. That turned out to be prescient.&lt;/p&gt;

&lt;p&gt;Walk into any government office in Bangladesh and there's a decent chance you'll encounter Datasoft's work. They built the national ID system, the e-passport system, biometric registration systems for multiple agencies, voter registration platforms, and border control infrastructure. These aren't small projects—they're nation-building scale, often worth tens or hundreds of millions of taka.&lt;/p&gt;

&lt;p&gt;Their flagship commercial product, &lt;strong&gt;Microfin 360&lt;/strong&gt; (launched 2008), targets microfinance institutions and NGOs. Given Bangladesh's massive microfinance sector (pioneered by Grameen Bank), this was smart positioning. The product has since expanded across the region to Nepal, Bhutan, Canada, and India.&lt;/p&gt;

&lt;p&gt;What Datasoft understood early was that government contracts, while bureaucratic and sometimes slow-paying, provide stable long-term revenue. Once you're embedded in a ministry's infrastructure, replacement becomes politically and technically difficult. That stickiness matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Salary Range:&lt;/strong&gt; 30,000-180,000 taka/month+ for software engineers&lt;/p&gt;

&lt;h3&gt;
  
  
  Tiger IT Bangladesh Limited
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://www.tigerit.com" rel="noopener noreferrer"&gt;https://www.tigerit.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Founded:&lt;/strong&gt; 2006&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Employees:&lt;/strong&gt; 100+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specialization:&lt;/strong&gt; Biometric and identity management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If Datasoft is the generalist in government contracts, Tiger IT is the specialist. They focus almost exclusively on security, identity management, and biometric systems—the stuff that requires security clearances and deep technical expertise.&lt;/p&gt;

&lt;p&gt;This is the company government agencies call when they need fingerprint identification systems, automated fingerprint identification systems (AFIS), facial recognition, or secure document management. Their client list includes defense and security agencies, which means many of their projects aren't publicly discussed.&lt;/p&gt;

&lt;p&gt;Tiger IT has carved out a defensible niche. Biometric systems aren't something you can easily commodity outsource—they require specialized knowledge, security protocols, and trust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Salary Range:&lt;/strong&gt; 90,000-180,000 taka/month+ for software engineers&lt;/p&gt;

&lt;h3&gt;
  
  
  The Government Contract Controversy
&lt;/h3&gt;

&lt;p&gt;No discussion of Bangladesh's software industry is complete without addressing the elephant in the room: government procurement controversies.&lt;/p&gt;

&lt;p&gt;Between 2019 and 2024, the a2i (Aspire to Innovate) project—a major government digitalization initiative supported by UNDP—procured software through 46 separate contracts. An investigation found that just four companies received 23 of those 46 contracts (50%).&lt;/p&gt;

&lt;p&gt;The companies were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tappware Solutions:&lt;/strong&gt; 6 contracts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SoftBD:&lt;/strong&gt; 6 contracts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business Automation Limited:&lt;/strong&gt; 6 contracts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OrangeBD&lt;/strong&gt; (Orange IT): 5 contracts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Allegations emerged of a "syndicate" where procurement conditions were allegedly tailor-made to benefit specific companies. This isn't unique to Bangladesh—government tech procurement is notoriously prone to corruption globally. But it illustrates why some companies focus intensely on government contracts while others avoid them entirely.&lt;/p&gt;




&lt;h2&gt;
  
  
  The International Outsourcing Leaders
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Brain Station 23
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://brainstation-23.com" rel="noopener noreferrer"&gt;https://brainstation-23.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Founded:&lt;/strong&gt; 2006&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Employees:&lt;/strong&gt; 800+ (700+ software engineers)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Certifications:&lt;/strong&gt; CMMI Level 3, ISO 9001, ISO 27001
&lt;strong&gt;Projects Completed:&lt;/strong&gt; 2,000+&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you had to pick one company that represents Bangladesh's software ambitions, it's probably Brain Station 23. They're the largest by headcount, the most diverse in services, and have the strongest international presence with offices in the USA, UK, Australia, Nigeria, and across Europe and the Middle East.&lt;/p&gt;

&lt;p&gt;Brain Station 23 does everything. Banking and fintech (they built CityTouch for City Bank, which processes over $3 billion in transactions), e-commerce platforms (the Shwapno supermarket system), ERP implementations (official Odoo partner), learning management systems (Moodle partner with their own Proctoring Pro product), AI and machine learning solutions, AR/VR, mobile apps—the list goes on.&lt;/p&gt;

&lt;p&gt;Their client roster reads like a who's who: PayPal, Nissan, British Telecom, Unilever, City Bank, AB Bank, HSBC Bangladesh, MetLife, Grameenphone, Robi, BAT Bangladesh, and multiple pharmaceutical companies.&lt;/p&gt;

&lt;p&gt;What makes Brain Station 23 interesting is their &lt;strong&gt;hybrid model&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;~50% international outsourcing&lt;/li&gt;
&lt;li&gt;~30% own product development (like Intellifriend, their AI time-tracking app)&lt;/li&gt;
&lt;li&gt;~20% local enterprise solutions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Salary Range:&lt;/strong&gt; 60,000-220,000 taka/month+ for software engineers&lt;/p&gt;

&lt;h3&gt;
  
  
  BJIT Group
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://bjitgroup.com" rel="noopener noreferrer"&gt;https://bjitgroup.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Founded:&lt;/strong&gt; 2007&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Employees:&lt;/strong&gt; 800+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specialization:&lt;/strong&gt; Japanese market&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;BJIT took a different approach—instead of trying to serve everyone, they became the go-to partner for Japanese companies. About 80% of their work comes from Japan, with the rest split between Singapore and other markets.&lt;/p&gt;

&lt;p&gt;This specialization makes sense. The Japanese market is huge, culturally specific, and historically underserved by South Asian outsourcing. Companies like BJIT that invest in understanding Japanese business culture, communication styles, and quality expectations can command premium rates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Salary Range:&lt;/strong&gt; 30,000-220,000 taka/month+ for software engineers&lt;/p&gt;

&lt;h3&gt;
  
  
  Cefalo Bangladesh Ltd.
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://www.cefalo.com" rel="noopener noreferrer"&gt;https://www.cefalo.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specialization:&lt;/strong&gt; Norwegian market&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Like BJIT with Japan, Cefalo focused almost exclusively on Norway. They provide full-time capable developers to Norwegian businesses and have built a reputation as one of the best workplaces in Bangladesh.&lt;/p&gt;

&lt;p&gt;The salaries reflect Norwegian client budgets:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Salary Range:&lt;/strong&gt; 65,000-220,000 taka/month+ (senior engineers can earn 185,000-220,000 taka)&lt;/p&gt;

&lt;p&gt;That senior engineer salary is exceptional for Bangladesh—roughly $1,700-$2,000/month. Competitive value for Norwegian clients while representing top-tier local compensation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enosis Solutions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://enosisbd.com" rel="noopener noreferrer"&gt;https://enosisbd.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Founded:&lt;/strong&gt; 2010&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Employees:&lt;/strong&gt; 200+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus:&lt;/strong&gt; US and European markets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enosis went the American and European route, focusing on web and mobile application development, cloud solutions, and IT consulting using agile methodologies. About 70% of revenue comes from US clients, 20% from Europe, 10% elsewhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Salary Range:&lt;/strong&gt; 50,000-220,000 taka/month+ (well-structured career ladder with clear levels)&lt;/p&gt;

&lt;h3&gt;
  
  
  Vivasoft Limited
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://vivasoftltd.com" rel="noopener noreferrer"&gt;https://vivasoftltd.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Founded:&lt;/strong&gt; 2010&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Employees:&lt;/strong&gt; 300+ (180+ dedicated engineers)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus:&lt;/strong&gt; Team augmentation and offshore office setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vivasoft positioned themselves uniquely—instead of just doing custom development, they focus on team augmentation (providing dedicated developers to extend existing teams) and helping companies set up full offshore offices in Bangladesh.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Salary Range:&lt;/strong&gt; 25,000-220,000 taka/month+ for software engineers&lt;/p&gt;

&lt;h3&gt;
  
  
  Selise Digital Platforms
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://selisegroup.com" rel="noopener noreferrer"&gt;https://selisegroup.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revenue:&lt;/strong&gt; ~$37.9 million&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Headquarters:&lt;/strong&gt; Zürich, Switzerland (with Bangladesh operations)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ranking:&lt;/strong&gt; #2 Top Software Company in Bangladesh 2025&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Selise is a global IT delivery organization with significant Bangladesh operations. They specialize in advanced computing, AI, data science, and digital platform creation. As a "Swiss tech partner for digital transformation," they bring European quality standards to Bangladeshi talent.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Product Companies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  NerdDevs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://nerddevs.com" rel="noopener noreferrer"&gt;https://nerddevs.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Product Website:&lt;/strong&gt; &lt;a href="https://biddaan.com" rel="noopener noreferrer"&gt;https://biddaan.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Founded:&lt;/strong&gt; 2015&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Employees:&lt;/strong&gt; 20+ (small but impactful)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specialization:&lt;/strong&gt; EdTech and messaging solutions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NerdDevs represents a different model—staying small, focusing intensely on specific problems, and building both client work and own products simultaneously.&lt;/p&gt;

&lt;p&gt;Their flagship partnership achievement is &lt;strong&gt;TestReach&lt;/strong&gt;, an online assessment platform that ACCA (Association of Chartered Certified Accountants), British Council, CIPS, and iSQI use for global examinations. If you've taken a professional certification exam in the UK, there's a decent chance NerdDevs wrote part of the software.&lt;/p&gt;

&lt;p&gt;They also built &lt;strong&gt;Genius&lt;/strong&gt; and other messaging apps, which are mostly used in the US market that handles 4-5 million messages monthly—managing that volume reliably requires serious engineering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For the Bangladesh market, their major products include:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Biddaan&lt;/strong&gt; (&lt;a href="https://biddaan.com" rel="noopener noreferrer"&gt;https://biddaan.com&lt;/a&gt;) - Their flagship Learning Management System specifically designed for Bangladeshi &amp;amp; International educational institutions. It handles student tracking, progress notes, institution management, and the full administrative stack educational organizations need. The interface is multilingual, which matters—most LMS solutions are English-only, creating barriers for local schools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Salary Range:&lt;/strong&gt; 30,000-220,000 taka/month+ for software engineers&lt;/p&gt;

&lt;h3&gt;
  
  
  BDTask Limited
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://www.bdtask.com" rel="noopener noreferrer"&gt;https://www.bdtask.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus:&lt;/strong&gt; Ready-made software products&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While most Bangladeshi companies do client work, BDTask went the opposite direction—they build ready-made software and sell it globally through marketplaces like Envato.&lt;/p&gt;

&lt;p&gt;Their product catalog includes accounting software, inventory management, POS systems, school management, hospital management, hotel management, restaurant management, HR and payroll systems—basically every business management need you can think of.&lt;/p&gt;

&lt;p&gt;This is a different business model entirely. Instead of getting paid for hours worked or projects completed, they sell licenses—think $50-$500 per copy, with customers in Europe, North America, and Southeast Asia buying directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ollyo (MuktoSoft)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://ollyo.com" rel="noopener noreferrer"&gt;https://ollyo.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Founded:&lt;/strong&gt; 2010&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus:&lt;/strong&gt; WordPress and Joomla ecosystem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ollyo took a similar product approach but focused narrowly on WordPress plugins, themes, Joomla extensions, and open-source tools. They're an Envato partner, meaning their products sell through Envato's massive marketplace.&lt;/p&gt;

&lt;p&gt;Products include WordPress plugins, Joomla templates, no-code website builders, e-learning platforms, e-commerce solutions, and a free open-source icon library. Some of their work is used by thousands of websites globally.&lt;/p&gt;

&lt;h3&gt;
  
  
  REVE Systems Ltd.
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://www.revechat.com" rel="noopener noreferrer"&gt;https://www.revechat.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus:&lt;/strong&gt; VoIP and communications platform software&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;REVE Systems built telecommunications software—VoIP platforms, communication tools, and enterprise communications solutions used globally. This is highly specialized work requiring understanding of telecommunications protocols, real-time communication, and handling large-scale voice/video traffic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Salary Range:&lt;/strong&gt; 50,000-150,000 taka/month+ for software engineers&lt;/p&gt;




&lt;h2&gt;
  
  
  The Embedded Systems &amp;amp; IoT Specialists
&lt;/h2&gt;

&lt;p&gt;This is a specialized but critical segment of Bangladesh's software industry—companies working on firmware, embedded systems, IoT solutions, and hardware-software integration. These engineers bridge the gap between software code and physical devices.&lt;/p&gt;

&lt;h3&gt;
  
  
  Samsung R&amp;amp;D Institute Bangladesh (SRBD)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://research.samsung.com/srbd" rel="noopener noreferrer"&gt;https://research.samsung.com/srbd&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Founded:&lt;/strong&gt; February 2011&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus:&lt;/strong&gt; Embedded systems, IoT, cellular modem technology&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Samsung chose Bangladesh for its first-ever R&amp;amp;D hub in the country—a strong endorsement of the local engineering talent. SRBD works on cutting-edge embedded systems including cellular modem systems, IoT hardware, and intelligent embedded devices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What they work on:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Embedded software and systems engineering&lt;/li&gt;
&lt;li&gt;Cellular modem systems development&lt;/li&gt;
&lt;li&gt;IoT hardware and connectivity systems&lt;/li&gt;
&lt;li&gt;Firmware for Samsung's global product line&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; When Samsung—one of the world's largest electronics companies—sets up an R&amp;amp;D center in your country, it's a validation of your engineering talent. SRBD employs some of Bangladesh's most skilled embedded systems engineers working on products used globally.&lt;/p&gt;

&lt;h3&gt;
  
  
  Teton Electronics / Teton Private Ltd.
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://tetonelectronics.com" rel="noopener noreferrer"&gt;https://tetonelectronics.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus:&lt;/strong&gt; Firmware development and embedded systems services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Teton Electronics provides comprehensive firmware development services, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Programming and updating embedded firmware&lt;/li&gt;
&lt;li&gt;Integration for different types of microcontrollers and processors&lt;/li&gt;
&lt;li&gt;Hardware-software integration solutions&lt;/li&gt;
&lt;li&gt;Custom embedded systems design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They work across various industries requiring specialized firmware solutions—from consumer electronics to industrial applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  BJIT Embedded Systems Division
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://www.bjit.co.jp" rel="noopener noreferrer"&gt;https://www.bjit.co.jp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus:&lt;/strong&gt; IoT &amp;amp; Embedded Engineering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;BJIT, known for its Japanese market specialization, has a dedicated embedded systems division offering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IoT &amp;amp; Embedded Engineering&lt;/li&gt;
&lt;li&gt;Embedded Application Development&lt;/li&gt;
&lt;li&gt;Mechanical Design&lt;/li&gt;
&lt;li&gt;Hardware-software integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Their Japanese clients demand precision and reliability—qualities that have driven BJIT to develop strong embedded systems capabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subra Systems Limited
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Specialization:&lt;/strong&gt; Embedded systems and industrial automation&lt;/p&gt;

&lt;p&gt;Subra Systems focuses on embedded solutions for industrial applications, where reliability and real-time performance are critical. They work on automation systems, control systems, and industrial IoT solutions.&lt;/p&gt;

&lt;h3&gt;
  
  
  SIMEC System Ltd.
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Specialization:&lt;/strong&gt; Embedded systems and firmware development&lt;/p&gt;

&lt;p&gt;SIMEC provides embedded systems services across various sectors, working on firmware design, implementation, and optimization for different hardware platforms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ninos IT Solution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://www.ninositsolution.com" rel="noopener noreferrer"&gt;https://www.ninositsolution.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specialization:&lt;/strong&gt; Cutting-edge embedded systems design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ninos IT Solution specializes in designing and developing embedded systems with seamless integration capabilities. They work on projects requiring deep hardware and software knowledge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Embedded Systems Job Market:&lt;/strong&gt;&lt;br&gt;
The demand for embedded systems engineers in Bangladesh is substantial, with approximately 2,921 embedded systems job openings listed on major job portals. Skills in high demand include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Embedded software engineering&lt;/li&gt;
&lt;li&gt;IoT development&lt;/li&gt;
&lt;li&gt;LoRaWAN device development&lt;/li&gt;
&lt;li&gt;Firmware design and implementation&lt;/li&gt;
&lt;li&gt;Hardware-software integration&lt;/li&gt;
&lt;li&gt;Robotics and automation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Salary Ranges:&lt;/strong&gt; 60,000-180,000 taka/month+ for embedded systems engineers (higher due to specialized skills)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Embedded Systems Matter:&lt;/strong&gt;&lt;br&gt;
As the world becomes more connected with smart devices, IoT sensors, and intelligent electronics, embedded systems expertise becomes increasingly valuable. Bangladeshi companies are positioning themselves to serve this growing global market with skilled engineers who understand both hardware and software.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Tech Startup Scene (The Real Growth Engine)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  bKash
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Industry:&lt;/strong&gt; Mobile Financial Services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ranking:&lt;/strong&gt; #3 Top Startup in Bangladesh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;bKash isn't just a startup—it's practically a utility. As Bangladesh's leading mobile financial services provider, bKash has transformed how millions of Bangladeshis send, receive, and store money. With international investment and attention, bKash has attracted significant funding and become one of Bangladesh's most successful tech exports.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pathao
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Founded:&lt;/strong&gt; 2015&lt;br&gt;
&lt;strong&gt;Founders:&lt;/strong&gt; Hussain Elius, Shifat Adnan, Fahad&lt;br&gt;
&lt;strong&gt;Ranking:&lt;/strong&gt; #6 Top Startup in Bangladesh&lt;/p&gt;

&lt;p&gt;Pathao is Bangladesh's "super-app" offering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ride-sharing services (bikes, cars, cycles)&lt;/li&gt;
&lt;li&gt;Food delivery&lt;/li&gt;
&lt;li&gt;Logistics and parcel delivery&lt;/li&gt;
&lt;li&gt;Created over &lt;strong&gt;300,000 jobs&lt;/strong&gt; in eight years&lt;/li&gt;
&lt;li&gt;Serving &lt;strong&gt;60,000 small businesses&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Over &lt;strong&gt;8 million users&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Salary Range:&lt;/strong&gt; 65,000-180,000 taka/month+ for software engineers&lt;/p&gt;

&lt;h3&gt;
  
  
  ShopUp
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Ranking:&lt;/strong&gt; #1 Startup in Bangladesh&lt;/p&gt;

&lt;p&gt;ShopUp focuses on B2B commerce platform, helping small retailers and businesses source inventory. As Bangladesh's top-ranked startup, ShopUp has attracted significant investor attention and represents the maturation of Bangladesh's startup ecosystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chaldal
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Industry:&lt;/strong&gt; E-commerce grocery&lt;/p&gt;

&lt;p&gt;Chaldal has revolutionized grocery shopping in Bangladesh's major cities with its online grocery platform. They've built sophisticated logistics technology and inventory management systems that compete with international players.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Salary Range:&lt;/strong&gt; 70,000-180,000 taka/month+ for software engineers&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Notable Startups:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shikho&lt;/strong&gt; - EdTech platform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Arogga&lt;/strong&gt; - Healthcare/medicine delivery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PriyoShop Retail&lt;/strong&gt; - B2B retail platform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shohoz&lt;/strong&gt; - Transportation and logistics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Startup Ecosystem Growth:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bangladesh's startup investment grew &lt;strong&gt;1,116% year-over-year&lt;/strong&gt; to &lt;strong&gt;$119.9 million in H1 2025&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;698+ top startups currently operating&lt;/li&gt;
&lt;li&gt;Middle class growing at 10% per annum to reach 34 million by 2025&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2024 Startup Investments That Matter:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sombhab&lt;/strong&gt; (tech startup): $1 million investment from Singapore's Cocoon Capital&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blue-Tech&lt;/strong&gt;: $100,000+ foreign investment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bangladesh Startup Summit 2024&lt;/strong&gt;: July 27-28 at Bangladesh Computer Council, theme "Smart Bangladesh, Limitless Possibilities"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AdTech Recognition&lt;/strong&gt;: 7 Bangladeshi EdTech startups ranked in South Asia's top 100&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Freelance-to-Company Model (Hidden Giants)
&lt;/h2&gt;

&lt;p&gt;This is the category nobody talks about but it's MASSIVE. Hundreds of software companies in Bangladesh started as individual freelancers on &lt;strong&gt;Upwork&lt;/strong&gt;, &lt;strong&gt;Fiverr&lt;/strong&gt;, &lt;strong&gt;Freelancer.com&lt;/strong&gt;, and &lt;strong&gt;oDesk&lt;/strong&gt; (now Upwork).&lt;/p&gt;

&lt;p&gt;Here's how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Developer starts freelancing on Upwork/Fiverr while in university or early career&lt;/li&gt;
&lt;li&gt;Builds reputation, gets 5-star reviews, starts earning $2,000-$5,000/month&lt;/li&gt;
&lt;li&gt;Gets more work than they can handle alone&lt;/li&gt;
&lt;li&gt;Hires friends/junior developers&lt;/li&gt;
&lt;li&gt;Registers a company&lt;/li&gt;
&lt;li&gt;Continues taking contracts through platforms + direct clients&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Characteristics of these companies:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5-50 employees typically&lt;/li&gt;
&lt;li&gt;Heavy focus on web development (WordPress, Laravel, React, Node.js)&lt;/li&gt;
&lt;li&gt;Strong presence on Upwork, Fiverr, Freelancer.com&lt;/li&gt;
&lt;li&gt;Often specialize in specific niches (e-commerce sites, real estate portals, booking systems)&lt;/li&gt;
&lt;li&gt;Salaries: 20,000-150,000 taka/month+ typically&lt;/li&gt;
&lt;li&gt;Revenue: $50,000-$500,000 annually for established agencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why this model works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Low barrier to entry (no sales team needed—platforms bring clients)&lt;/li&gt;
&lt;li&gt;International clients from day one&lt;/li&gt;
&lt;li&gt;Payment security through platforms&lt;/li&gt;
&lt;li&gt;Can build reputation faster than cold outreach&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Notable platforms used:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upwork (most popular for software development)&lt;/li&gt;
&lt;li&gt;Fiverr (quick projects, WordPress fixes, small websites)&lt;/li&gt;
&lt;li&gt;Freelancer.com (competitive bidding model)&lt;/li&gt;
&lt;li&gt;PeoplePerHour, Guru (alternative platforms)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Digital Marketing + Software Companies
&lt;/h2&gt;

&lt;p&gt;A growing category of companies combines software development with digital marketing services. These companies often build websites, then handle SEO, content creation, social media marketing, and paid advertising for clients.&lt;/p&gt;

&lt;h3&gt;
  
  
  Leading Digital Marketing Agencies:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Bizcope&lt;/strong&gt; (&lt;a href="https://www.bizcope.com/" rel="noopener noreferrer"&gt;https://www.bizcope.com/&lt;/a&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Global SEO and digital marketing agency&lt;/li&gt;
&lt;li&gt;Services: SEO, web development, content production, 360-degree digital marketing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Red Sparrow Digital&lt;/strong&gt; (&lt;a href="https://www.redsparrowdigital.com/" rel="noopener noreferrer"&gt;https://www.redsparrowdigital.com/&lt;/a&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full-stack digital marketing agency&lt;/li&gt;
&lt;li&gt;Web design &amp;amp; development, SEO, comprehensive marketing solutions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;HYPE DHAKA&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Communications agency&lt;/li&gt;
&lt;li&gt;Services: SEO, email marketing, social media marketing, website development&lt;/li&gt;
&lt;li&gt;100% positive client reviews according to Clutch.co&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Marketorr&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One of the fastest-growing digital marketing agencies&lt;/li&gt;
&lt;li&gt;Deep SEO expertise and full-funnel marketing strategies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Implevista&lt;/strong&gt; (&lt;a href="https://digital.implevista.com/" rel="noopener noreferrer"&gt;https://digital.implevista.com/&lt;/a&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expert SEO, PPC, and social media marketing (SMM) solutions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why this model matters:&lt;/strong&gt;&lt;br&gt;
Many Bangladeshi businesses don't just need software—they need the complete digital package. These companies provide that integrated solution, often at competitive rates compared to Western agencies.&lt;/p&gt;




&lt;h2&gt;
  
  
  Salary Comparison: The Real Numbers
&lt;/h2&gt;

&lt;p&gt;Let me give you the unvarnished truth about salaries in Bangladesh's software industry. These are ranges based on current market data (2024-2025):&lt;/p&gt;

&lt;h3&gt;
  
  
  By Experience Level:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Salary Range (BDT/month)&lt;/th&gt;
&lt;th&gt;Salary Range (USD/month)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trainee/Intern&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0-15,000&lt;/td&gt;
&lt;td&gt;$0-135&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Junior Developer&lt;/strong&gt; (0-2 years)&lt;/td&gt;
&lt;td&gt;20,000-45,000&lt;/td&gt;
&lt;td&gt;$180-405&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Software Engineer&lt;/strong&gt; (2-5 years)&lt;/td&gt;
&lt;td&gt;50,000-80,000&lt;/td&gt;
&lt;td&gt;$450-720&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Senior Engineer&lt;/strong&gt; (5+ years)&lt;/td&gt;
&lt;td&gt;100,000-220,000&lt;/td&gt;
&lt;td&gt;$900-1,980&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tech Lead/Principal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;165,000-300,000&lt;/td&gt;
&lt;td&gt;$1,485-2,700&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  By Company Type:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Company Type&lt;/th&gt;
&lt;th&gt;Junior&lt;/th&gt;
&lt;th&gt;Mid-Level&lt;/th&gt;
&lt;th&gt;Senior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Local Outsourcing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;25,000-35,000&lt;/td&gt;
&lt;td&gt;50,000-70,000&lt;/td&gt;
&lt;td&gt;100,000-150,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Western Outsourcing&lt;/strong&gt; (Cefalo, Enosis)&lt;/td&gt;
&lt;td&gt;50,000-80,000&lt;/td&gt;
&lt;td&gt;70,000-130,000&lt;/td&gt;
&lt;td&gt;185,000-220,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;International Companies&lt;/strong&gt; (Optimizely, Cheq)&lt;/td&gt;
&lt;td&gt;70,000+&lt;/td&gt;
&lt;td&gt;120,000+&lt;/td&gt;
&lt;td&gt;250,000-300,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Tech Startups&lt;/strong&gt; (Pathao, Chaldal)&lt;/td&gt;
&lt;td&gt;65,000+&lt;/td&gt;
&lt;td&gt;70,000-100,000&lt;/td&gt;
&lt;td&gt;155,000-220,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Product Companies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;30,000-50,000&lt;/td&gt;
&lt;td&gt;60,000-90,000&lt;/td&gt;
&lt;td&gt;100,000-220,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Key Observations:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The Entry-Level Reality:&lt;/strong&gt;&lt;br&gt;
Many companies pay trainee and intern positions 0-10,000 taka monthly (effectively free or near-free labor). It's controversial but common—companies view it as paid training rather than productive work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The International Company Premium:&lt;/strong&gt;&lt;br&gt;
Companies with international headquarters pay significantly more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Optimizely: Senior Software Engineer at 250,000-300,000 taka/month+&lt;/li&gt;
&lt;li&gt;Cheq Inc.: Senior QA Automation Engineer at 180,000-300,000 taka/month+&lt;/li&gt;
&lt;li&gt;Tero Labs: Engineer at 120,000-150,000 taka/month+&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. The Startup Competitiveness:&lt;/strong&gt;&lt;br&gt;
Local tech companies (Pathao, Chaldal) pay competitively because they compete directly with international employers for talent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Remote Work Premium:&lt;/strong&gt;&lt;br&gt;
Engineers working remotely for international companies earn substantially more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;~$41,465 (average for remote software developers in Bangladesh)&lt;/li&gt;
&lt;li&gt;~$46,135 (median annual salary for remote software engineers)&lt;/li&gt;
&lt;li&gt;That's 2-4x higher than local salaries&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Complete Company Directory
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Government &amp;amp; Enterprise Solutions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Datasoft Systems Bangladesh:&lt;/strong&gt; &lt;a href="https://datasoftbd.com" rel="noopener noreferrer"&gt;https://datasoftbd.com&lt;/a&gt; - CMMI Level 5, government contracts specialist&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tiger IT Bangladesh:&lt;/strong&gt; &lt;a href="https://www.tigerit.com" rel="noopener noreferrer"&gt;https://www.tigerit.com&lt;/a&gt; - Biometric and security systems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Southtech Limited:&lt;/strong&gt; &lt;a href="https://www.southtech.com.bd" rel="noopener noreferrer"&gt;https://www.southtech.com.bd&lt;/a&gt; - Banking and ERP solutions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LeadSoft Bangladesh:&lt;/strong&gt; Financial and insurance platforms&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  International Outsourcing Leaders
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Brain Station 23:&lt;/strong&gt; &lt;a href="https://brainstation-23.com" rel="noopener noreferrer"&gt;https://brainstation-23.com&lt;/a&gt; - Largest diversified player, 800+ employees&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Selise Digital Platforms:&lt;/strong&gt; &lt;a href="https://selisegroup.com" rel="noopener noreferrer"&gt;https://selisegroup.com&lt;/a&gt; - #2 ranked, Swiss-Bangladeshi operations, $37.9M revenue&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BJIT Group:&lt;/strong&gt; &lt;a href="https://bjitgroup.com" rel="noopener noreferrer"&gt;https://bjitgroup.com&lt;/a&gt; - Japan market specialist&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cefalo Bangladesh:&lt;/strong&gt; &lt;a href="https://www.cefalo.com" rel="noopener noreferrer"&gt;https://www.cefalo.com&lt;/a&gt; - Norway market specialist&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enosis Solutions:&lt;/strong&gt; &lt;a href="https://enosisbd.com" rel="noopener noreferrer"&gt;https://enosisbd.com&lt;/a&gt; - US/Europe agile development&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vivasoft Limited:&lt;/strong&gt; &lt;a href="https://vivasoftltd.com" rel="noopener noreferrer"&gt;https://vivasoftltd.com&lt;/a&gt; - Team augmentation and offshore setup&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Product Companies
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NerdDevs:&lt;/strong&gt; &lt;a href="https://nerddevs.com" rel="noopener noreferrer"&gt;https://nerddevs.com&lt;/a&gt; - EdTech and messaging solutions

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Biddaan LMS:&lt;/strong&gt; &lt;a href="https://biddaan.com" rel="noopener noreferrer"&gt;https://biddaan.com&lt;/a&gt; - Learning management system for Bangladesh&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;BDTask Limited:&lt;/strong&gt; &lt;a href="https://www.bdtask.com" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.bdtask.com" rel="noopener noreferrer"&gt;https://www.bdtask.com&lt;/a&gt; - Ready-made software products&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Ollyo (MuktoSoft):&lt;/strong&gt; &lt;a href="https://ollyo.com" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://ollyo.com" rel="noopener noreferrer"&gt;https://ollyo.com&lt;/a&gt; - WordPress/Joomla ecosystem&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;REVE Systems:&lt;/strong&gt; &lt;a href="https://www.revechat.com" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.revechat.com" rel="noopener noreferrer"&gt;https://www.revechat.com&lt;/a&gt; - VoIP and communications platforms&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Embedded Systems &amp;amp; IoT Companies
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Samsung R&amp;amp;D Institute Bangladesh:&lt;/strong&gt; &lt;a href="https://research.samsung.com/srbd" rel="noopener noreferrer"&gt;https://research.samsung.com/srbd&lt;/a&gt; - Embedded systems, IoT, cellular modem (founded 2011)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teton Electronics:&lt;/strong&gt; &lt;a href="https://tetonelectronics.com" rel="noopener noreferrer"&gt;https://tetonelectronics.com&lt;/a&gt; - Firmware development and embedded systems services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BJIT Embedded Systems:&lt;/strong&gt; &lt;a href="https://www.bjit.co.jp" rel="noopener noreferrer"&gt;https://www.bjit.co.jp&lt;/a&gt; - IoT &amp;amp; Embedded Engineering&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subra Systems Limited:&lt;/strong&gt; Embedded systems and industrial automation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SIMEC System Ltd.:&lt;/strong&gt; Embedded systems and firmware development&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ninos IT Solution:&lt;/strong&gt; &lt;a href="https://www.ninositsolution.com" rel="noopener noreferrer"&gt;https://www.ninositsolution.com&lt;/a&gt; - Cutting-edge embedded systems design&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tech Startups (Growth Stage)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;bKash:&lt;/strong&gt; &lt;a href="https://www.bkash.com" rel="noopener noreferrer"&gt;https://www.bkash.com&lt;/a&gt; - Mobile financial services (#3 startup)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pathao:&lt;/strong&gt; &lt;a href="https://pathao.com" rel="noopener noreferrer"&gt;https://pathao.com&lt;/a&gt; - Super-app: ride-sharing, food delivery, logistics (#6 startup)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ShopUp:&lt;/strong&gt; #1 ranked startup - B2B commerce platform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chaldal:&lt;/strong&gt; &lt;a href="https://chaldal.com" rel="noopener noreferrer"&gt;https://chaldal.com&lt;/a&gt; - E-commerce grocery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shikho:&lt;/strong&gt; EdTech platform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Arogga:&lt;/strong&gt; Healthcare/medicine delivery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PriyoShop Retail:&lt;/strong&gt; B2B retail platform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shohoz:&lt;/strong&gt; Transportation and logistics&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Digital Marketing + Software Agencies
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bizcope:&lt;/strong&gt; &lt;a href="https://www.bizcope.com" rel="noopener noreferrer"&gt;https://www.bizcope.com&lt;/a&gt; - SEO, web development, content production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Red Sparrow Digital:&lt;/strong&gt; &lt;a href="https://www.redsparrowdigital.com" rel="noopener noreferrer"&gt;https://www.redsparrowdigital.com&lt;/a&gt; - Full-stack digital marketing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HYPE DHAKA:&lt;/strong&gt; SEO, email marketing, social media marketing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Marketorr:&lt;/strong&gt; Fastest-growing digital marketing agency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implevista:&lt;/strong&gt; &lt;a href="https://digital.implevista.com" rel="noopener noreferrer"&gt;https://digital.implevista.com&lt;/a&gt; - SEO, PPC, SMM solutions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Smaller Specialists
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kaz Software:&lt;/strong&gt; &lt;a href="https://www.kaz.com.bd" rel="noopener noreferrer"&gt;https://www.kaz.com.bd&lt;/a&gt; - Enterprise custom development&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nascenia Limited:&lt;/strong&gt; &lt;a href="https://www.nascenia.com" rel="noopener noreferrer"&gt;https://www.nascenia.com&lt;/a&gt; - SaaS product engineering&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Solution Innovators:&lt;/strong&gt; &lt;a href="https://www.dsibd.com" rel="noopener noreferrer"&gt;https://www.dsibd.com&lt;/a&gt; - E-commerce and ERP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dream71 Bangladesh:&lt;/strong&gt; Mobile games and creative apps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PipilikaSoft:&lt;/strong&gt; Education management systems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dcastalia Limited:&lt;/strong&gt; Top 10 ranked software company&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Government Contractors (Under Investigation)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OrangeBD (Orange IT):&lt;/strong&gt; Infrastructure and cybersecurity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tappware Solutions:&lt;/strong&gt; Software development and maintenance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SoftBD:&lt;/strong&gt; Software development services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business Automation Limited:&lt;/strong&gt; Process automation solutions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  International Companies with Bangladesh Operations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Optimizely Bangladesh:&lt;/strong&gt; Global optimization platform office&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Samsung R&amp;amp;D Bangladesh:&lt;/strong&gt; Samsung research and development&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cheq Inc.:&lt;/strong&gt; Cybersecurity platform&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  IT Training Institutes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;BASIS Institute of Technology &amp;amp; Management (BITM):&lt;/strong&gt; bitm.org.bd&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creative IT Institute:&lt;/strong&gt; Web development, MERN stack, digital marketing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dewan ICT Institute:&lt;/strong&gt; #1 ranked IT training institute&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;European IT Institute:&lt;/strong&gt; Professional IT training, Dhanmondi&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Softopark IT Institute:&lt;/strong&gt; Career-boosting IT programs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;BASIS&lt;/strong&gt; (Bangladesh Association of Software &amp;amp; Information Services): &lt;a href="https://basis.org.bd" rel="noopener noreferrer"&gt;https://basis.org.bd&lt;/a&gt; - Industry association&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DoICT&lt;/strong&gt; (Directorate of ICT): &lt;a href="https://doict.gov.bd" rel="noopener noreferrer"&gt;https://doict.gov.bd&lt;/a&gt; - Government ICT initiatives&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Salary Data:&lt;/strong&gt; &lt;a href="https://tahanima.github.io/salary-ranges-offered-by-bangladeshi-software-companies-for-different-positions/" rel="noopener noreferrer"&gt;https://tahanima.github.io/salary-ranges-offered-by-bangladeshi-software-companies-for-different-positions/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What This All Means
&lt;/h2&gt;

&lt;p&gt;Bangladesh's software industry has reached an interesting inflection point. The early stage of just doing outsourcing for international clients is maturing into something more sophisticated.&lt;/p&gt;

&lt;p&gt;Companies like Brain Station 23, NerdDevs, and Selise show it's possible to move up the value chain—not just writing code someone else specifies, but building products, solving complex problems, and working with prestigious international clients. TestReach (NerdDevs' platform) being used by British Council and ACCA isn't just outsourcing—it's being the technical partner for critical global infrastructure.&lt;/p&gt;

&lt;p&gt;The government digitalization push, despite procurement controversies, created a domestic market for software. Companies like Datasoft and Tiger IT built genuinely hard technology (national ID systems, biometric platforms) that required serious engineering.&lt;/p&gt;

&lt;p&gt;The product companies (BDTask, Ollyo, Biddaan) demonstrate that Bangladeshi developers can compete globally in marketplaces and create solutions used by thousands of businesses and schools.&lt;/p&gt;

&lt;p&gt;The startup ecosystem (bKash, Pathao, ShopUp, Chaldal) shows that world-class consumer platforms can be built from Bangladesh for Bangladesh—and eventually for emerging markets globally.&lt;/p&gt;

&lt;p&gt;But challenges remain real. Salaries lag far behind international standards. A "senior" engineer in Bangladesh earning $2,000/month would be entry-level pay in the US or Europe. This creates brain drain—talented developers leave for opportunities abroad. Companies struggle to retain their best engineers once they get two-three years experience and realize their market value elsewhere.&lt;/p&gt;

&lt;p&gt;Infrastructure issues persist. Power grid losses, internet reliability in secondary cities, and reliance on Dhaka for most operations limit growth. The skills gap in advanced technologies (AI, machine learning, blockchain) means many companies can't compete for cutting-edge work.&lt;/p&gt;

&lt;p&gt;The procurement corruption allegations around government contracts raise questions about whether that sector is sustainable long-term without serious reform.&lt;/p&gt;

&lt;p&gt;Still, the trajectory is impressive. From essentially nothing twenty years ago to a billion-dollar industry employing hundreds of thousands, with serious companies working on real problems for real international clients—that's meaningful progress.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future: Where Bangladesh's Software Industry Is Headed
&lt;/h2&gt;

&lt;p&gt;The software industry Bangladesh built over fifteen years is real, substantial, and growing. It's not Silicon Valley, and it's not trying to be. It's something different—a developing country using its advantages (cost, talent, English proficiency) to build an export industry while simultaneously digitizing its own economy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to watch in 2026-2030:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AI and ML Adoption:&lt;/strong&gt; As global demand for AI talent explodes, Bangladesh's value proposition could drive growth—if the skills gap can be addressed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Startup Exits:&lt;/strong&gt; Bangladesh has yet to see a major IPO or acquisition. When bKash, Pathao, or ShopUp exit, it will unlock capital and validate the ecosystem&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Government Reform:&lt;/strong&gt; If procurement becomes transparent and merit-based, government contracts could drive sustainable growth rather than creating dependencies&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Regional Expansion:&lt;/strong&gt; Bangladeshi companies are well-positioned to serve other emerging markets in South Asia, Southeast Asia, and Africa&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Remote Work:&lt;/strong&gt; As remote work becomes normalized, Bangladeshi developers can work directly for international companies at international rates—reducing brain drain&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Product Success:&lt;/strong&gt; BDTask, Ollyo, and NerdDevs are showing that products can scale. More success stories here could shift the industry from services to products&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Freelance Professionalization:&lt;/strong&gt; The freelance-to-company pipeline will likely formalize, creating more sustainable agencies rather than individual freelancer outfits&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The story of Bangladesh's software industry isn't just about affordable services. It's about a country leveraging its human capital to participate in the global digital economy. It's about companies like Datasoft building national infrastructure, NerdDevs powering global examinations, and Pathao transforming transportation.&lt;/p&gt;

&lt;p&gt;It's about 400,000 professionals building their futures one line of code at a time.&lt;/p&gt;

&lt;p&gt;That's a story worth paying attention to.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Industry Statistics &amp;amp; Market Data
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.statista.com/outlook/tmo/software/bangladesh" rel="noopener noreferrer"&gt;Software - Bangladesh | Statista Market Forecast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.mordorintelligence.com/industry-reports/bangladesh-ict-market" rel="noopener noreferrer"&gt;Bangladesh ICT Market Analysis | Mordor Intelligence&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://basis.org.bd/public/files/publication/60d707ec45811__Software%2520&amp;amp;%2520IT%2520Services%2520Catalog%25202021-min.pdf" rel="noopener noreferrer"&gt;BASIT Software and IT Services Catalog 2021&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.tbsnews.net/bangladesh/export-earnings-technology-sector-exceeds-13-billion-palak-370579" rel="noopener noreferrer"&gt;Export earnings in technology sector exceeds $1.3 billion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.tbsnews.net/economy/5b-ict-export-target-missed-huge-margin-heres-why-1281591" rel="noopener noreferrer"&gt;$5b ICT export target missed by huge margin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.daily-sun.com/5/787382" rel="noopener noreferrer"&gt;Software companies earn $840m in export revenue in FY24&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://basis.org.bd/public/files/publication/5e123f2d5a6c6ba96136a3b168568073f9800e5b0f5b9.pdf" rel="noopener noreferrer"&gt;IT AND ITES INDUSTRY OVERVIEW - BASIS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Salary &amp;amp; Compensation Data
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tahanima.github.io/salary-ranges-offered-by-bangladeshi-software-companies-for-different-positions/" rel="noopener noreferrer"&gt;Salary ranges offered by Bangladeshi Software Companies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.levels.fyi/t/software-engineer/levels/senior/locations/dhaka-bgd" rel="noopener noreferrer"&gt;Senior Software Engineer Salary in Dhaka | Levels.fyi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.glassdoor.com/Salaries/bangladesh-senior-software-engineer-salary-SRCH_IL.0,10_IN27_KO11,35.htm" rel="noopener noreferrer"&gt;Senior Software Engineer Salary | Glassdoor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.payscale.com/research/BD/Job=Senior_Software_Engineer/Salary" rel="noopener noreferrer"&gt;Average Senior Software Engineer Salary in Bangladesh | PayScale&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://arc.dev/salaries/software-engineers-in-bangladesh" rel="noopener noreferrer"&gt;Remote Software Engineer Salary in Bangladesh | Arc.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://geniusee.com/single-blog/cost-of-outsourcing-software-development" rel="noopener noreferrer"&gt;Cost of outsourcing software development by country 2025&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Company Rankings &amp;amp; Lists
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ibos.io/best-software-companies-in-bangladesh-2026/" rel="noopener noreferrer"&gt;Best Software Companies in Bangladesh 2026 | iBOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.kaz.com.bd/blog/best-software-companies-in-bangladesh" rel="noopener noreferrer"&gt;Top 25 Best Software Companies in Bangladesh 2026 | Kaz Software&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vivasoftltd.com/best-software-companies-in-bangladesh/" rel="noopener noreferrer"&gt;Top 20 Best Software Companies in Bangladesh 2026 | Vivasoft&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dcastalia.com/blog/top-10-software-company-in-bangladesh/" rel="noopener noreferrer"&gt;Top 10 Software Companies in Bangladesh 2025 | Dcastalia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/pulse/100-bangladeshi-software-companies-profile-sagore-sarker" rel="noopener noreferrer"&gt;100 Bangladeshi Software Companies | LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Startup Ecosystem
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.startupblink.com/top-startups/bangladesh" rel="noopener noreferrer"&gt;Top Startups in Bangladesh | StartupBlink&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tipsoi.pro/top-10-tech-startups-in-bangladesh/" rel="noopener noreferrer"&gt;Top 10 Tech Startups In Bangladesh 2026 | TipsOI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nucamp.co/blog/coding-bootcamp-bangladesh-bgd-bangladeshs-top-10-startups-that-tech-professionals-should-watch-out-for-in-2024" rel="noopener noreferrer"&gt;Bangladesh's Top 10 Startups | Nucamp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lightcastlepartners.com/wp-content/uploads/2020/07/Bangladesh-Startup-Ecosystem-Report-2020-1.pdf" rel="noopener noreferrer"&gt;Bangladesh Startup Ecosystem Report | LightCastle Partners&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Digital Marketing Agencies
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://clutch.co/bd/agencies/content-marketing" rel="noopener noreferrer"&gt;Top Content Marketing Companies in Bangladesh | Clutch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://superbcompanies.com/categories/seo-companies-in-bangladesh/" rel="noopener noreferrer"&gt;Top 20 SEO Companies in Bangladesh 2026 | SuperbCompanies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techbehemoths.com/companies/content-marketing/bangladesh" rel="noopener noreferrer"&gt;Top 20+ Content Marketing Agencies in Bangladesh | TechBehemoths&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Outsourcing Comparisons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://proquantic.com/blog/software-development-outsourcing-countries" rel="noopener noreferrer"&gt;Top 10 Southeast Asian Countries for Software Outsourcing | Proquantic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://geniusee.com/single-blog/cost-of-outsourcing-software-development" rel="noopener noreferrer"&gt;Cost of outsourcing software development by country 2025 | Geniusee&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vivasoftltd.com/south-asian-countries-to-hire-software-development-team/" rel="noopener noreferrer"&gt;Top South Asian Countries to Hire Software Development | Vivasoft&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://innovatureinc.com/top-outsourcing-countries-to-watch-in-2025/" rel="noopener noreferrer"&gt;Top 10 Outsourcing Countries to Watch in 2025 | Innovature&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Freelance &amp;amp; Marketplace Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://viserx.com/blog/web-development/freelancing-websites-bangladesh" rel="noopener noreferrer"&gt;Top 15 Freelancing Websites in Bangladesh For 2026 | ViserX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.elevatepay.co/blog/freelancing-websites-in-bangladesh" rel="noopener noreferrer"&gt;Top 7 Freelancing Websites in Bangladesh | ElevatePay&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.truelancer.com/freelancers-in-bangladesh" rel="noopener noreferrer"&gt;Best Freelancers in Bangladesh | Truelancer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://graphicdesigneye.com/best-upwork-alternatives/" rel="noopener noreferrer"&gt;Best Upwork Alternatives | Graphic Design Eye&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Embedded Systems &amp;amp; IoT
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ensun.io/search/embedded-systems/bangladesh" rel="noopener noreferrer"&gt;Top Embedded Systems Companies in Bangladesh | Ensun&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://research.samsung.com/srbd" rel="noopener noreferrer"&gt;Samsung R&amp;amp;D Institute Bangladesh | Samsung Research&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tetonelectronics.com/firmware-development-services/" rel="noopener noreferrer"&gt;Firmware Development Services | Teton Electronics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.bjit.co.jp/en/service/iot-embedded/" rel="noopener noreferrer"&gt;IoT &amp;amp; Embedded Engineering | BJIT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ninositsolution.com/bangladesh/embedded-systems-company" rel="noopener noreferrer"&gt;Embedded Systems Company in Bangladesh | Ninos IT Solution&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tetonelectronics.com/top-10-it-company/" rel="noopener noreferrer"&gt;Top 10 IT Company in Bangladesh | Teton Electronics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Government &amp;amp; Policy
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bida.gov.bd/investment-sector/it-it-enabled-services" rel="noopener noreferrer"&gt;The IT &amp;amp; ITES Goldmine – Bangladesh is Open for Business | BIDA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://doict.gov.bd" rel="noopener noreferrer"&gt;DoICT | Directorate of ICT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://file-rangpur.portal.gov.bd/files/pbs2.dinajpur.gov.bd/files/1885c0a0_28a4_4fcc_8d4d_dcd7ce23fd8b/7b684f19a15dfcd0f542382764572486.pdf" rel="noopener noreferrer"&gt;National Strategy for Artificial Intelligence Bangladesh&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Company Reviews &amp;amp; Salary Data (Bangla Sources)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://deshimula.com" rel="noopener noreferrer"&gt;Deshi Mula - Company Review Platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/arman_aru/"&gt;বাংলাদেশে সফটওয়্যার ও ওয়েব ডেভেলপমেন্টের চাহিদা, বেতন ও ... | Dev.to&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Academic &amp;amp; Research
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://journalwjarr.com/sites/default/files/fulltext_pdf/WJARR-2025-3840.pdf" rel="noopener noreferrer"&gt;Absent of software regulation and regulator in Bangladesh | Journal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.researchgate.net/publication/364315458_Bangladesh_and_USA_ICT_frontier_to_explore" rel="noopener noreferrer"&gt;Bangladesh and USA: ICT frontier to explore | ResearchGate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ifc.org/content/dam/ifc/doc/2025/bangladesh-country-private-sector-diagnostic-en.pdf" rel="noopener noreferrer"&gt;Bangladesh: Country Private Sector Diagnostic | IFC&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This blog is based on publicly available information as of January 2026. Company details, salaries, and situations may change. All salary figures are approximate based on employee reports and job postings.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;p&gt;That's all!&lt;/p&gt;

&lt;p&gt;I hope you've found the article useful. I'll write more about the software engineering and it's business ecosystem. Feel free to share your thoughts.&lt;/p&gt;

&lt;p&gt;Check more on&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://encryptioner.github.io" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/mir-mursalin-ankur" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Encryptioner" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/AnkurMursalin" rel="noopener noreferrer"&gt;X (Twitter)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nerddevs.com/author/ankur/" rel="noopener noreferrer"&gt;Nerddevs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>software</category>
      <category>businesss</category>
      <category>bangladesh</category>
      <category>news</category>
    </item>
    <item>
      <title>The Complete Guide to Software Business Models: How Tech Companies Actually Make Money</title>
      <dc:creator>Mir Mursalin Ankur</dc:creator>
      <pubDate>Sat, 10 Jan 2026 15:03:06 +0000</pubDate>
      <link>https://forem.com/mir_mursalin_ankur/the-complete-guide-to-software-business-models-how-tech-companies-actually-make-money-20dg</link>
      <guid>https://forem.com/mir_mursalin_ankur/the-complete-guide-to-software-business-models-how-tech-companies-actually-make-money-20dg</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftiuhwehruocdxi88aedn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftiuhwehruocdxi88aedn.png" alt="How Tech Companies Actually Make Money" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you've ever wondered how your favorite apps stay free, why some software charges monthly while others are one-time purchases, or how tech giants like Google and Apple structure their businesses, you're in the right place.&lt;/p&gt;

&lt;p&gt;The software industry has evolved far beyond simple "buy once, use forever" models. Today's tech landscape features dozens of sophisticated business models, each designed to solve specific problems and create value in unique ways. Understanding these models isn't just for entrepreneurs—it helps you make smarter decisions as a consumer, investor, or anyone navigating the digital economy.&lt;/p&gt;

&lt;p&gt;Let's break down the most important business models shaping the software world today.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Foundation: Core Business Model Families
&lt;/h2&gt;

&lt;p&gt;Think of these as the main categories that most tech businesses fall into. While companies often mix and match elements, understanding these fundamentals helps you see the bigger picture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subscription: The Predictable Revenue Machine
&lt;/h3&gt;

&lt;p&gt;Remember when you bought Microsoft Office once and used it for years? Those days are largely gone. The subscription model has become the dominant force in software, and for good reason—it works incredibly well for both companies and customers.&lt;/p&gt;

&lt;p&gt;Here's how it works: instead of paying a large upfront fee, you pay a smaller amount regularly—usually monthly or annually. Netflix pioneered this for entertainment, but it's everywhere now. Adobe Creative Cloud, Spotify, Microsoft 365, and thousands of business software tools all use subscriptions.&lt;/p&gt;

&lt;p&gt;Why did this model take over? For companies, it creates predictable, recurring revenue. Instead of hunting for new customers every month, they can focus on keeping existing ones happy. A company with 10,000 subscribers paying $10 monthly knows it has roughly $100,000 coming in next month, making planning and investment much easier.&lt;/p&gt;

&lt;p&gt;For customers, the benefits are real too. Lower upfront costs mean less risk when trying new software. You're not committing $500 upfront—maybe just $10 for the first month. Plus, subscriptions typically include automatic updates, cloud storage, and customer support. The software company stays invested in keeping you happy because if you cancel, their revenue drops.&lt;/p&gt;

&lt;p&gt;The numbers tell the story. A well-run subscription business might see 90-95% of customers renew each year. That compounds powerfully over time. After three years, a company might still have 75% of its original customers, all while adding new ones. This creates what investors call an "annuity stream"—revenue that keeps flowing with less effort over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Freemium: The Generous Hook
&lt;/h3&gt;

&lt;p&gt;Freemium is everywhere, even if you don't realize it. The name combines "free" and "premium"—give away the basic product for free, then charge for advanced features.&lt;/p&gt;

&lt;p&gt;Think about Spotify, Canva, or Slack. All offer genuinely useful free tiers that millions of people use daily without ever paying. But eventually, enough users hit limitations that matter to them—whether it's offline listening, premium templates, or message history—and convert to paid plans.&lt;/p&gt;

&lt;p&gt;The economics seem counterintuitive at first. How can you make money giving away your product? The key is that digital products have near-zero marginal cost. Serving one more free user costs almost nothing—no manufacturing, shipping, or materials. So if you can convert even 2-5% of free users to paying customers, the math works beautifully.&lt;/p&gt;

&lt;p&gt;Dropbox famously grew this way. They gave everyone 2GB of free storage—enough to be useful, but eventually, most people needed more. Some users upgraded immediately, others took years, but the free tier kept them engaged the whole time. Today, Dropbox has millions of paying customers who started with that free 2GB.&lt;/p&gt;

&lt;p&gt;The challenge is finding the right balance. Make the free tier too generous, and no one upgrades. Make it too restrictive, and people never experience the value. The best freemium products hook users with the free tier, then create natural "aha moments" that drive upgrades.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transaction and Commission: Taking a Cut
&lt;/h3&gt;

&lt;p&gt;Some of the world's largest tech companies don't sell software at all—they connect buyers and sellers and take a percentage of each transaction.&lt;/p&gt;

&lt;p&gt;Airbnb doesn't own hotels. Uber doesn't own cars. Etsy doesn't make crafts. But they've built billion-dollar businesses by creating marketplaces where transactions happen, then taking a slice of each one.&lt;/p&gt;

&lt;p&gt;The magic of this model is that it scales with customer success. When an Airbnb host makes more money, Airbnb makes more money. When Stripe processes more payments for businesses, Stripe earns more. The incentives align perfectly.&lt;/p&gt;

&lt;p&gt;Transaction rates vary widely. Stripe and PayPal charge around 2.9% plus 30 cents per transaction. Airbnb takes about 3% from hosts and 14% from guests. The App Store and Google Play take 15-30% of app sales. These percentages might seem small, but when you're processing billions in transactions, they add up fast.&lt;/p&gt;

&lt;p&gt;The tricky part is reaching critical mass. A marketplace with few buyers won't attract sellers, and vice versa. This "chicken and egg problem" means transaction-based businesses often operate at a loss initially, subsidizing one or both sides to build momentum. Once they reach critical mass, though, network effects create powerful moats—the more users join, the more valuable the platform becomes, attracting even more users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advertising: Free for Users, Paid by Brands
&lt;/h3&gt;

&lt;p&gt;If you're not paying for the product, you are the product. This saying captures the advertising model perfectly.&lt;/p&gt;

&lt;p&gt;Google Search, Facebook, Instagram, YouTube, and most of the internet you use daily are free because advertisers pay for your attention. These companies have gotten incredibly sophisticated at targeting ads based on your behavior, demographics, and interests, making those ads more valuable to advertisers.&lt;/p&gt;

&lt;p&gt;The numbers are staggering. Google made over $200 billion from ads in 2023. Meta (Facebook/Instagram) made another $130 billion. These aren't small businesses with ads on the side—advertising IS their business.&lt;/p&gt;

&lt;p&gt;For this model to work, you need massive scale. A blog with 1,000 readers might make a few dollars from ads. A platform with 100 million users makes millions. That's why ad-supported businesses focus obsessively on growth and engagement—more eyeballs and more time spent equals more ad revenue.&lt;/p&gt;

&lt;p&gt;The model has its critics. Privacy concerns have grown as tracking becomes more sophisticated. Regulations like GDPR in Europe are forcing changes. And users increasingly turn to ad blockers or paid ad-free alternatives. Still, for many services—particularly social media—advertising remains the dominant model because users won't pay directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pay-As-You-Go: Usage-Based Pricing
&lt;/h3&gt;

&lt;p&gt;Some software charges based on exactly what you use, like your electricity bill. This usage-based model has exploded with cloud computing.&lt;/p&gt;

&lt;p&gt;Amazon Web Services (AWS), the backbone of much of the internet, bills by the computing power, storage, and bandwidth you consume. Use more, pay more. Use less, pay less. The same applies to Twilio (charged per text message or phone call), Stripe (per transaction), and many developer tools.&lt;/p&gt;

&lt;p&gt;This model aligns costs with value better than almost anything else. A startup spending $100 monthly on AWS can scale to $100,000 when they grow, without renegotiating contracts or switching providers. The pricing grows naturally with their business.&lt;/p&gt;

&lt;p&gt;For customers, it removes the fear of overpaying. You're never paying for capacity you don't need. For companies, it means revenue automatically scales with customer success—a win-win.&lt;/p&gt;

&lt;p&gt;The downside is unpredictability. Companies can't forecast usage-based revenue as easily as subscriptions. A customer might spend $1,000 one month and $5,000 the next. This variability makes planning harder. Many companies now combine fixed subscriptions with usage-based pricing to balance predictability and flexibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  Marketplaces: Connecting Supply and Demand
&lt;/h3&gt;

&lt;p&gt;Marketplaces are fascinating because they create value without creating products. eBay doesn't manufacture goods. Upwork doesn't do the freelance work. Airbnb doesn't provide the accommodations.&lt;/p&gt;

&lt;p&gt;Instead, they build platforms that reduce friction between buyers and sellers. They handle payments, build trust through reviews, provide insurance, and create discovery mechanisms. This infrastructure is valuable enough that both sides willingly pay fees.&lt;/p&gt;

&lt;p&gt;The best marketplaces solve real pain points. Before Airbnb, renting your apartment to strangers was risky and complicated. Before Uber, hailing a cab in many cities was frustrating. The marketplace didn't just connect supply and demand—it made transactions trustworthy and convenient.&lt;/p&gt;

&lt;p&gt;Two-sided marketplaces face unique challenges. You need enough supply to attract demand and enough demand to attract supply. Successful marketplaces often subsidize one side initially. Uber spent billions on driver incentives to ensure riders could always find a car. Once established, network effects made the platform nearly impossible to displace.&lt;/p&gt;

&lt;h3&gt;
  
  
  Open Source and Open Core: The Generosity Paradox
&lt;/h3&gt;

&lt;p&gt;Some of the most valuable software companies give away their core product for free—completely free, not freemium. How does that work?&lt;/p&gt;

&lt;p&gt;The open-core model releases the basic software as open source (anyone can use, modify, and see the code for free) but charges for enterprise features, support, or managed services.&lt;/p&gt;

&lt;p&gt;GitLab is a perfect example. Their core product is free and open source. Millions of developers use it daily without paying a cent. But large companies pay substantial fees for advanced features like enterprise security, compliance tools, and dedicated support.&lt;/p&gt;

&lt;p&gt;This model builds massive adoption and community goodwill while still generating revenue from those who need enterprise capabilities. It's particularly effective for developer tools because developers love open source and will advocate for tools they already use when their companies need to make purchasing decisions.&lt;/p&gt;

&lt;p&gt;Red Hat pioneered this model with Linux, eventually selling to IBM for $34 billion. MongoDB, Elastic, and many others have followed similar paths. The key is offering enough value in the free version to drive adoption while reserving genuinely valuable enterprise features for paying customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference: Business Model Comparison
&lt;/h2&gt;

&lt;p&gt;Here's a comprehensive comparison of the major software business models to help you quickly understand their key differences:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Revenue Structure&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;th&gt;Key Metrics&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subscription&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Recurring monthly/annual fees&lt;/td&gt;
&lt;td&gt;Products with ongoing value, sticky workflows&lt;/td&gt;
&lt;td&gt;Predictable revenue, customer retention, compounding growth&lt;/td&gt;
&lt;td&gt;Churn management, subscription fatigue&lt;/td&gt;
&lt;td&gt;Netflix, Spotify, Adobe CC, Microsoft 365&lt;/td&gt;
&lt;td&gt;MRR, ARR, Churn, LTV, NRR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Freemium&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free tier + paid upgrades&lt;/td&gt;
&lt;td&gt;Products that scale with usage/team size&lt;/td&gt;
&lt;td&gt;Low friction acquisition, viral growth, large user base&lt;/td&gt;
&lt;td&gt;Low conversion (2-5%), high support costs for free users&lt;/td&gt;
&lt;td&gt;Spotify, Canva, Slack, Dropbox&lt;/td&gt;
&lt;td&gt;Free-to-paid conversion, activation rate, time to convert&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Transaction/Commission&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;% of each transaction&lt;/td&gt;
&lt;td&gt;Marketplaces, payment processing, two-sided platforms&lt;/td&gt;
&lt;td&gt;Scales with customer success, aligned incentives&lt;/td&gt;
&lt;td&gt;Chicken-egg problem, needs critical mass&lt;/td&gt;
&lt;td&gt;Uber, Airbnb, Stripe, Etsy, App Store&lt;/td&gt;
&lt;td&gt;GMV, take rate, liquidity, active buyers/sellers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Advertising&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CPM/CPC from brands&lt;/td&gt;
&lt;td&gt;Mass-market consumer products&lt;/td&gt;
&lt;td&gt;Free for users drives scale, massive revenue potential&lt;/td&gt;
&lt;td&gt;Privacy concerns, ad blockers, regulatory risk&lt;/td&gt;
&lt;td&gt;Google, Facebook, YouTube, Instagram&lt;/td&gt;
&lt;td&gt;DAU/MAU, engagement time, CPC/CPM, revenue per user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Usage-Based&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pay per unit consumed&lt;/td&gt;
&lt;td&gt;Cloud infrastructure, APIs, developer tools&lt;/td&gt;
&lt;td&gt;Fair pricing, scales with customer growth&lt;/td&gt;
&lt;td&gt;Unpredictable revenue, harder forecasting&lt;/td&gt;
&lt;td&gt;AWS, Twilio, Zapier, Stripe&lt;/td&gt;
&lt;td&gt;Usage volume, consumption trends, expansion revenue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Marketplace&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fees from buyers/sellers or both&lt;/td&gt;
&lt;td&gt;Supply-demand matching, peer-to-peer transactions&lt;/td&gt;
&lt;td&gt;Network effects, strong moat at scale&lt;/td&gt;
&lt;td&gt;Critical mass challenge, complex trust-building&lt;/td&gt;
&lt;td&gt;eBay, Upwork, Airbnb, Uber, Etsy&lt;/td&gt;
&lt;td&gt;GMV, take rate, buyer/seller ratio, transaction frequency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Open Core&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free OSS + paid enterprise features&lt;/td&gt;
&lt;td&gt;Developer tools, infrastructure software&lt;/td&gt;
&lt;td&gt;Community trust, rapid adoption, developer advocacy&lt;/td&gt;
&lt;td&gt;Converting OSS users to paid is challenging&lt;/td&gt;
&lt;td&gt;GitLab, MongoDB, Red Hat, Elastic&lt;/td&gt;
&lt;td&gt;Enterprise conversion rate, community growth, support revenue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API-First&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Per-call or % of transaction value&lt;/td&gt;
&lt;td&gt;Technical infrastructure, developer platforms&lt;/td&gt;
&lt;td&gt;Deep integration, switching costs, usage scales automatically&lt;/td&gt;
&lt;td&gt;Technical audience, requires developer trust&lt;/td&gt;
&lt;td&gt;Stripe, Twilio, Plaid, SendGrid&lt;/td&gt;
&lt;td&gt;API calls, transaction volume, developer adoption&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Platform Ecosystem&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multiple revenue streams (hardware + services + subscriptions)&lt;/td&gt;
&lt;td&gt;Companies with product breadth&lt;/td&gt;
&lt;td&gt;Lock-in, multiple monetization touchpoints, high LTV&lt;/td&gt;
&lt;td&gt;Requires huge capital, slow to build, complex coordination&lt;/td&gt;
&lt;td&gt;Apple, Google, Microsoft, Amazon&lt;/td&gt;
&lt;td&gt;Cross-product adoption, ecosystem retention, ARPU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Creator Economy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;% of creator earnings or subscription&lt;/td&gt;
&lt;td&gt;Content creator monetization platforms&lt;/td&gt;
&lt;td&gt;Aligns with creator success, viral growth&lt;/td&gt;
&lt;td&gt;Content moderation, payment processing complexity&lt;/td&gt;
&lt;td&gt;Patreon, Substack, YouTube, OnlyFans&lt;/td&gt;
&lt;td&gt;Active creators, creator earnings, fan spend per creator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;White-Label&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Wholesale licensing fees&lt;/td&gt;
&lt;td&gt;B2B infrastructure, platform tech&lt;/td&gt;
&lt;td&gt;Rapid scaling through partners, reduced CAC&lt;/td&gt;
&lt;td&gt;Lower margins, less customer control, brand dilution&lt;/td&gt;
&lt;td&gt;WordPress, white-label SaaS&lt;/td&gt;
&lt;td&gt;Partner count, reseller revenue, partner retention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data as Service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Subscription for data access&lt;/td&gt;
&lt;td&gt;Companies with unique, valuable data&lt;/td&gt;
&lt;td&gt;High margins, recurring revenue, defensible&lt;/td&gt;
&lt;td&gt;Privacy/ethics concerns, data quality maintenance&lt;/td&gt;
&lt;td&gt;Bloomberg, weather services, analytics platforms&lt;/td&gt;
&lt;td&gt;Data accuracy, subscriber count, data refresh rate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Choosing Based on Your Situation
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your Situation&lt;/th&gt;
&lt;th&gt;Recommended Model(s)&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Consumer app, viral potential&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Freemium + Ads&lt;/td&gt;
&lt;td&gt;Low barrier, mass scale required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;B2B software, clear ROI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Subscription (tiered)&lt;/td&gt;
&lt;td&gt;Predictable revenue, willingness to pay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Marketplace idea&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Transaction/Commission&lt;/td&gt;
&lt;td&gt;Scales with transaction volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Developer tool&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Open Core + API/Usage&lt;/td&gt;
&lt;td&gt;Developers prefer OSS, enterprises pay for support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Infrastructure/technical&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Usage-based/API&lt;/td&gt;
&lt;td&gt;Fair pricing, scales naturally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Content/media&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ads + Creator Economy split&lt;/td&gt;
&lt;td&gt;Users won't pay directly, creators need monetization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Enterprise focus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Subscription + White-label&lt;/td&gt;
&lt;td&gt;High contract values, predictable revenue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Platform vision&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ecosystem approach&lt;/td&gt;
&lt;td&gt;Maximize LTV through multiple touchpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Revenue Predictability Comparison
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;High Predictability:  Subscription │────────────────────────────────│ 95%+
                        SaaS Tiered │─────────────────────────│ 90%+

Medium Predictability: Freemium→Sub │──────────────│ 70-80%
                        Usage-Based (large customers) │────────│ 60-70%

Low Predictability:   Usage-Based (small customers) │────│ 50-60%
                        Transaction/Commission │──│ 40-50%
                        Advertising │─│ 30-40%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Customer Acquisition Cost (CAC) Recovery Time
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Typical CAC Recovery&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Freemium&lt;/td&gt;
&lt;td&gt;12-24+ months&lt;/td&gt;
&lt;td&gt;Most users never pay, long conversion cycles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Advertising&lt;/td&gt;
&lt;td&gt;Immediate (but per-user value is low)&lt;/td&gt;
&lt;td&gt;Revenue starts immediately but margins thin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subscription&lt;/td&gt;
&lt;td&gt;6-18 months&lt;/td&gt;
&lt;td&gt;Predictable monthly payments accelerate recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Usage-Based&lt;/td&gt;
&lt;td&gt;3-12 months&lt;/td&gt;
&lt;td&gt;Depends on customer adoption speed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transaction&lt;/td&gt;
&lt;td&gt;Varies wildly&lt;/td&gt;
&lt;td&gt;Scales with customer transaction volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise Subscription&lt;/td&gt;
&lt;td&gt;12-36 months&lt;/td&gt;
&lt;td&gt;High CAC but large contracts justify it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Specialized Software Business Models
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcmme507mgzul87n9w7j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcmme507mgzul87n9w7j.png" alt="Specialized, Emerging and Creative Models" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Beyond the main categories, several specialized models have emerged for specific contexts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Software as a Service (SaaS) Variations
&lt;/h3&gt;

&lt;p&gt;SaaS deserves its own deep dive because it's become the default for business software. But not all SaaS is created equal. Let's look at the main pricing variations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flat-rate SaaS&lt;/strong&gt; charges everyone the same amount—simple but limiting. Basecamp famously charges $99 monthly regardless of team size. This simplicity is refreshing in a world of complex pricing tiers, though it means large companies and small teams pay the same.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tiered pricing&lt;/strong&gt; offers multiple plans at different prices. Think Slack's Free, Pro, Business, and Enterprise tiers. This captures more value from customers with different needs and budgets. A startup might pay $8 per user monthly while an enterprise pays $15+.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-user pricing&lt;/strong&gt; scales costs with team size. Most SaaS tools use this—Zoom, Microsoft 365, and thousands of others charge per user per month. It's straightforward and aligns costs with value, though teams sometimes share accounts to save money.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usage-based SaaS&lt;/strong&gt; charges for consumption, not seats. Zapier charges based on tasks automated. AWS charges for computing resources used. This works beautifully when value correlates directly with usage.&lt;/p&gt;

&lt;p&gt;Many modern SaaS companies use &lt;strong&gt;hybrid models&lt;/strong&gt;, combining elements. You might have a base platform fee, per-user charges, and usage fees for certain features. Complexity increases but so does revenue optimization.&lt;/p&gt;

&lt;h3&gt;
  
  
  API-First Business Models
&lt;/h3&gt;

&lt;p&gt;APIs (Application Programming Interfaces) have become products themselves. Companies like Stripe, Twilio, and Plaid built billion-dollar businesses by offering APIs that developers integrate into their applications.&lt;/p&gt;

&lt;p&gt;Stripe's API makes it incredibly easy to accept payments. Instead of building payment infrastructure from scratch—dealing with banks, fraud prevention, and compliance—developers add a few lines of code and start processing payments immediately. Stripe handles the complexity and charges per transaction.&lt;/p&gt;

&lt;p&gt;This model works because it solves hard technical problems and packages them as simple APIs. Developers pay for convenience, reliability, and not having to become experts in payments, communications, or banking infrastructure.&lt;/p&gt;

&lt;p&gt;The economics are compelling. Once you build the API infrastructure, serving additional customers costs very little. A company processing $1 million in payments pays the same percentage as one processing $100 million, but Stripe's costs don't increase proportionally. At scale, margins are excellent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data as a Service
&lt;/h3&gt;

&lt;p&gt;Some companies have realized their data itself is valuable. Bloomberg built an empire selling financial data. Weather companies sell forecasts to businesses. Analytics platforms sell aggregated insights.&lt;/p&gt;

&lt;p&gt;This model requires unique data that others don't have and customers who will pay for it. Bloomberg can charge thousands monthly because traders need real-time market data to make billion-dollar decisions. The value is clear.&lt;/p&gt;

&lt;p&gt;The challenge is often privacy and ethics. Companies must aggregate and anonymize data carefully. Selling individual user data creates backlash, but selling anonymized trends and insights can be legitimate and valuable.&lt;/p&gt;

&lt;h3&gt;
  
  
  White-Label and Licensing
&lt;/h3&gt;

&lt;p&gt;Some software companies don't sell to end users at all—they license their technology to other companies who rebrand and resell it.&lt;/p&gt;

&lt;p&gt;WordPress powers 40%+ of the internet, but most users don't know or care. Web hosting companies like Bluehost and GoDaddy offer "WordPress hosting," adding their branding and support. WordPress benefits through ecosystem growth.&lt;/p&gt;

&lt;p&gt;White-label SaaS lets companies offer software products without building them. A marketing agency might white-label email marketing software, offering it to clients under their own brand. The underlying software company charges a wholesale rate, and the agency marks it up.&lt;/p&gt;

&lt;p&gt;This model scales quickly because each licensing partner becomes a sales channel. The tradeoff is lower margins and less direct customer relationships.&lt;/p&gt;

&lt;h2&gt;
  
  
  Emerging and Creative Models
&lt;/h2&gt;

&lt;p&gt;Innovation in business models continues. Here are some newer patterns worth understanding.&lt;/p&gt;

&lt;h3&gt;
  
  
  Platform Ecosystems: Building Worlds, Not Products
&lt;/h3&gt;

&lt;p&gt;The most valuable tech companies don't sell products—they create ecosystems that lock users in through interconnectivity and network effects.&lt;/p&gt;

&lt;p&gt;Apple's ecosystem is the classic example. An iPhone works beautifully alone, but add an iPad, MacBook, Apple Watch, and AirPods, and you're deeply embedded. Messages sync across devices. AirPods switch seamlessly between them. Files move effortlessly through AirDrop. Leaving means losing all that integration.&lt;/p&gt;

&lt;p&gt;Apple doesn't just sell hardware—they sell the App Store (30% commission), Apple Music (subscription), iCloud storage (subscription), Apple TV+ (subscription), and more. Each product makes the others more valuable. This is ecosystem thinking at its finest.&lt;/p&gt;

&lt;p&gt;Google does the same with Android, Chrome, Search, Gmail, Drive, and YouTube. Once you're using several Google services, switching becomes increasingly difficult. Your data, preferences, and workflows are all integrated.&lt;/p&gt;

&lt;p&gt;Building ecosystems requires patience and capital. You need multiple products that genuinely work better together. But once established, ecosystems create powerful moats. Customers stay not because they can't leave but because leaving would mean losing substantial value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Platform Cooperatives: The Democratic Alternative
&lt;/h3&gt;

&lt;p&gt;While most platforms are owned by shareholders seeking maximum profit, a small but growing movement advocates for platform cooperatives—platforms owned by users or workers.&lt;/p&gt;

&lt;p&gt;Imagine if Uber were owned by drivers. Or if a social network were owned by its users. Instead of optimizing for investor returns, these platforms could optimize for user welfare.&lt;/p&gt;

&lt;p&gt;Examples are emerging. Stocksy (stock photography) is owned by contributing artists. Resonate (music streaming) is owned by musicians and listeners. These cooperatives still need to generate revenue and cover costs, but profits are distributed among stakeholders rather than external shareholders.&lt;/p&gt;

&lt;p&gt;The model addresses concerns about platform power and wealth concentration. Whether it can scale to compete with venture-backed giants remains uncertain, but it represents an interesting alternative approach to platform building.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creator Economy Models
&lt;/h3&gt;

&lt;p&gt;The rise of creators—YouTubers, podcasters, newsletter writers, course creators—has spawned new business models.&lt;/p&gt;

&lt;p&gt;Platforms like Patreon let creators charge fans directly for access to exclusive content. Substack enables writers to run paid newsletters, keeping 90% of revenue. YouTube shares ad revenue with creators. OnlyFans takes a percentage of creator earnings.&lt;/p&gt;

&lt;p&gt;These platforms succeeded by recognizing that creators needed infrastructure for payments, hosting, and fan management. By providing these tools, they could capture a percentage of the growing creator economy without creating content themselves.&lt;/p&gt;

&lt;p&gt;The model works because it aligns incentives. When creators succeed, the platform succeeds. Unlike traditional media where companies employed creators, these platforms enable independent creators while taking a cut.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bundling and Cross-Selling
&lt;/h3&gt;

&lt;p&gt;Some companies win not through individual products but through packaging multiple offerings together.&lt;/p&gt;

&lt;p&gt;Microsoft 365 bundles Word, Excel, PowerPoint, Teams, OneDrive, and more for one price. Individually, these might cost $15-30 monthly each. Bundled, they're $12.50 per user. Customers get better value, Microsoft gets stickier relationships and higher total revenue per customer.&lt;/p&gt;

&lt;p&gt;Adobe Creative Cloud similarly bundles Photoshop, Illustrator, Premiere Pro, and 20+ other tools. Professional designers might use several daily. Buying individually would cost hundreds monthly; bundled, it's $55.&lt;/p&gt;

&lt;p&gt;Bundling increases switching costs. A customer might consider alternatives to Photoshop, but finding replacements for their entire Adobe workflow is much harder.&lt;/p&gt;

&lt;p&gt;The risk is that customers only value one or two bundle components. If they're paying for 10 tools but only using 2, competitors offering just those 2 tools for less might win.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Choose the Right Model
&lt;/h2&gt;

&lt;p&gt;With so many options, how should a company choose? Several factors matter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Audience and market:&lt;/strong&gt; Consumer apps rarely work with direct payments—users expect free or very cheap. Business software can command higher prices because the ROI is clear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Product nature:&lt;/strong&gt; Some products fit certain models naturally. Developer tools work well with usage-based pricing. Creative tools suit subscriptions. Marketplaces obviously need transaction models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Competition:&lt;/strong&gt; In crowded markets, freemium helps acquire users despite competition. In blue ocean markets, you might charge from day one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Growth goals:&lt;/strong&gt; Venture-backed startups often prioritize growth over revenue, making freemium attractive. Bootstrapped companies need revenue sooner, favoring paid models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Margins and economics:&lt;/strong&gt; High-margin products (pure software) support freemium better than low-margin ones (with human services or heavy infrastructure costs).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customer acquisition costs:&lt;/strong&gt; If it costs $1,000 to acquire a customer, you need a business model that generates substantially more than $1,000 per customer over time. Subscriptions work well here; one-time purchases might not.&lt;/p&gt;

&lt;p&gt;Most successful companies eventually use hybrid models. Slack is freemium with tiered subscriptions. AWS has subscriptions for some services, usage-based pricing for others. Apple combines hardware sales, commissions, subscriptions, and ecosystem lock-in.&lt;/p&gt;

&lt;p&gt;The key is starting simple, learning from customers, and evolving thoughtfully rather than adopting every trendy model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategic Considerations
&lt;/h2&gt;

&lt;p&gt;Beyond choosing a model, several strategic factors determine success.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding Your Key Players
&lt;/h3&gt;

&lt;p&gt;Every market has dominant players whose strategies shape the landscape. In cloud computing, AWS, Google Cloud, and Microsoft Azure set pricing and features. In social media, Meta and Google define the advertising model.&lt;/p&gt;

&lt;p&gt;Understanding these players helps you identify opportunities. Are they ignoring a segment? Is there a feature gap? Could you serve customers they're overpricing?&lt;/p&gt;

&lt;p&gt;Sometimes the best strategy is being acquired by a major player. Instagram and WhatsApp became worth billions because Facebook (now Meta) needed to acquire their users and eliminate competition.&lt;/p&gt;

&lt;h3&gt;
  
  
  Opportunities in the Current Market
&lt;/h3&gt;

&lt;p&gt;Several trends create opportunities for new entrants:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remote work&lt;/strong&gt; has exploded demand for collaboration software, virtual events, and distributed team management. Companies that solve remote work pain points well have grown dramatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI and automation&lt;/strong&gt; are transforming every industry. Tools that help businesses implement AI without deep expertise are proliferating. The companies building the best AI wrappers and integrations will capture substantial value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Privacy and data ownership&lt;/strong&gt; concerns are creating demand for alternatives to ad-supported services. Paid search engines, encrypted messaging, and privacy-focused analytics are emerging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vertical SaaS&lt;/strong&gt; targets specific industries with tailored solutions rather than horizontal tools for everyone. Software for restaurants, medical practices, or law firms can command premium prices by solving industry-specific problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No-code and low-code&lt;/strong&gt; platforms democratize software creation. As these mature, new types of businesses become possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Weaknesses and Risks to Navigate
&lt;/h3&gt;

&lt;p&gt;No business model is perfect. Understanding weaknesses helps you mitigate them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subscription fatigue&lt;/strong&gt; is real. Consumers and businesses are pushback against dozens of monthly charges. Some users are choosing one-time purchases or reducing subscriptions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Platform dependence&lt;/strong&gt; means your business can change overnight if Apple, Google, or Amazon changes policies. Many businesses have seen revenue disappear when App Store rules changed or Amazon adjusted search algorithms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Competition and commoditization&lt;/strong&gt; affect every model. As markets mature, differentiation becomes harder and prices compress. What once commanded premium prices becomes a race to the bottom.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retention challenges&lt;/strong&gt; plague subscription businesses. Losing 5% of customers monthly compounds into losing more than half your base in a year. Customer success and continuous value delivery are critical.&lt;/p&gt;

&lt;h3&gt;
  
  
  Threats on the Horizon
&lt;/h3&gt;

&lt;p&gt;Several external factors could disrupt current models:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regulation&lt;/strong&gt; is increasing globally. Europe's Digital Markets Act forces changes to App Store fees and default browser settings. Privacy regulations restrict advertising practices. Future regulations could reshape entire business models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Economic downturns&lt;/strong&gt; hit different models differently. Subscription businesses often see churn spikes in recessions. Ad-supported businesses see revenue drop as marketing budgets shrink. Transaction-based businesses decline with overall economic activity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technology shifts&lt;/strong&gt; can make entire categories obsolete. Mobile apps disrupted desktop software. Cloud computing disrupted on-premise software. AI might disrupt both. Companies must continuously evolve.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security and privacy breaches&lt;/strong&gt; destroy trust and can kill companies. As data becomes more central to business models, protecting it becomes existential.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tax and Legal Considerations
&lt;/h3&gt;

&lt;p&gt;Business models have different tax and legal implications that matter enormously.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Digital services taxation&lt;/strong&gt; varies globally. Selling software to customers in Europe requires understanding VAT (typically 17-27%). Some US states tax SaaS, others don't. Compliance complexity grows with geographic expansion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Revenue recognition&lt;/strong&gt; rules differ by model. Subscriptions recognize revenue over time as services are delivered. One-time sales recognize revenue immediately. Marketplaces have specific rules for gross versus net revenue recognition. Getting this wrong can have serious accounting and legal consequences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data privacy compliance&lt;/strong&gt; affects advertising and data businesses particularly. GDPR in Europe, CCPA in California, and similar laws globally create compliance obligations. Violations bring substantial fines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contractor versus employee classification&lt;/strong&gt; matters for platform businesses. Uber, Lyft, and others face ongoing legal battles about whether drivers are contractors or employees. The distinction affects costs massively and has business model implications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Metrics That Matter
&lt;/h3&gt;

&lt;p&gt;Different business models require tracking different metrics.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;subscription businesses&lt;/strong&gt;, the critical metrics are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monthly Recurring Revenue (MRR) and Annual Recurring Revenue (ARR)&lt;/li&gt;
&lt;li&gt;Customer Acquisition Cost (CAC)&lt;/li&gt;
&lt;li&gt;Lifetime Value (LTV)&lt;/li&gt;
&lt;li&gt;Churn rate (monthly percentage of customers lost)&lt;/li&gt;
&lt;li&gt;Net Revenue Retention (revenue from existing customers including expansions minus churn)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For &lt;strong&gt;marketplace businesses&lt;/strong&gt;, track:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gross Merchandise Value (GMV) - total transaction volume&lt;/li&gt;
&lt;li&gt;Take rate (percentage you keep)&lt;/li&gt;
&lt;li&gt;Active buyers and sellers&lt;/li&gt;
&lt;li&gt;Liquidity (how quickly supply meets demand)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For &lt;strong&gt;advertising businesses&lt;/strong&gt;, watch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Daily/Monthly Active Users (DAU/MAU)&lt;/li&gt;
&lt;li&gt;Engagement metrics (time spent, actions taken)&lt;/li&gt;
&lt;li&gt;Cost Per Click (CPC) or Cost Per Thousand Impressions (CPM)&lt;/li&gt;
&lt;li&gt;Revenue per user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For &lt;strong&gt;freemium businesses&lt;/strong&gt;, monitor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free to paid conversion rate&lt;/li&gt;
&lt;li&gt;Activation rate (percentage of signups who experience core value)&lt;/li&gt;
&lt;li&gt;Time to conversion&lt;/li&gt;
&lt;li&gt;Feature adoption by tier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding which metrics matter for your model helps you optimize the right things.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Examples and Lessons
&lt;/h2&gt;

&lt;p&gt;Theory is important, but let's look at how this plays out in practice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zoom&lt;/strong&gt; started as a better video conferencing tool in a crowded market. They offered generous free tiers (40-minute meetings for up to 100 participants) while charging businesses for unlimited meeting length, more participants, and features like recording.&lt;/p&gt;

&lt;p&gt;The freemium model drove explosive growth. Millions tried Zoom for free during the pandemic. Many upgraded when remote work became permanent. By 2023, Zoom had over 467,000 customers with more than 10 employees, most paying substantial subscriptions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stripe&lt;/strong&gt; could have sold their payment processing API as a monthly subscription. Instead, they chose transaction-based pricing—2.9% plus 30 cents per charge. This aligned perfectly with customer success. Startups pay almost nothing initially, but as they grow, Stripe grows with them. Some of Stripe's largest customers process billions in payments annually, generating millions in fees.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notion&lt;/strong&gt; brilliantly combined freemium, subscription tiers, and viral growth. The product is genuinely useful for free, encouraging personal adoption. As teams form and need collaboration features, they upgrade. Users love Notion enough to tell others, driving organic growth. The result: a $10 billion valuation built largely through product-led growth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Epic Games&lt;/strong&gt; challenged the App Store model directly. They bypassed Apple's 30% commission by offering direct payment options in Fortnite. Apple banned Fortnite, leading to lawsuits and regulatory scrutiny. While Epic hasn't fully won, they've sparked global debate about fair platform fees and helped drive regulatory changes.&lt;/p&gt;

&lt;p&gt;These examples show that business models aren't just theoretical—they're strategic decisions with massive implications.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future of Software Business Models
&lt;/h2&gt;

&lt;p&gt;Where is this all heading? Several trends seem clear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI integration&lt;/strong&gt; will become table stakes. Every software category will incorporate AI capabilities. The question becomes: do you charge extra for AI features, include them in existing tiers, or use AI as a moat? Different companies will choose different approaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unbundling and rebundling&lt;/strong&gt; will continue. Markets oscillate between integrated suites and best-of-breed point solutions. Currently, we're seeing both: some companies bundle more features (Notion adding docs, wikis, databases), while others unbundle (focused tools that do one thing excellently).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Privacy-first models&lt;/strong&gt; may gain traction as users tire of ad-supported services that monetize attention. Paid alternatives to free, ad-supported tools are emerging. Whether consumers actually pay remains to be seen, but the shift is beginning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consumption-based pricing&lt;/strong&gt; will likely expand beyond cloud infrastructure. As companies seek to align costs with value more precisely, usage-based pricing makes sense for more categories.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Platform regulation&lt;/strong&gt; will reshape marketplace and ecosystem models. Regulators globally are scrutinizing platform power, app store fees, and default settings. Changes seem inevitable, though specifics remain uncertain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Web3 and blockchain&lt;/strong&gt; introduce new possibilities. Decentralized platforms owned by token holders rather than shareholders, creator tokens that let fans invest in creators directly, and NFTs that enable new digital ownership models are all experiments in progress. Most will fail, but some innovations may stick.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: It's About Value Creation, Not Just Revenue
&lt;/h2&gt;

&lt;p&gt;After exploring dozens of business models, one truth becomes clear: the best model is the one that creates and captures value most effectively for your specific situation.&lt;/p&gt;

&lt;p&gt;Subscription models succeed when they continuously deliver value, keeping customers engaged month after month. Freemium works when the free tier genuinely helps users while premium features solve real pain points. Marketplaces thrive when they reduce friction and enable transactions that wouldn't happen otherwise.&lt;/p&gt;

&lt;p&gt;The companies that win long-term don't just choose a model—they obsess over delivering value, then find business models that capture some portion of that value sustainably.&lt;/p&gt;

&lt;p&gt;Understanding these models helps you make better decisions whether you're building software, investing in tech companies, or simply using digital products. You'll recognize when you're in a freemium acquisition funnel, understand why that SaaS company is pushing annual plans so hard, and see the strategic logic behind platform ecosystem moves.&lt;/p&gt;

&lt;p&gt;The software industry continues evolving rapidly. New models emerge, existing ones adapt, and what works today might not work tomorrow. But the fundamental principle remains: create real value for real people, then build a business model that captures enough value to sustain and grow while keeping customers happy.&lt;/p&gt;

&lt;p&gt;That's the real art of software business models—finding that sustainable balance where everyone wins.&lt;/p&gt;

&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;p&gt;That's all!&lt;/p&gt;

&lt;p&gt;I hope you've found the article useful. I'll write more about the software business &amp;amp; related organizations &amp;amp; ecosystem soon. Feel free to share your thoughts.&lt;/p&gt;

&lt;p&gt;Check more on&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://encryptioner.github.io" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/mir-mursalin-ankur" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Encryptioner" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/AnkurMursalin" rel="noopener noreferrer"&gt;X (Twitter)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nerddevs.com/author/ankur/" rel="noopener noreferrer"&gt;Nerddevs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>softwarebusiness</category>
      <category>startup</category>
      <category>product</category>
      <category>freemium</category>
    </item>
    <item>
      <title>Publishing Your First NPM Package: A Real-World Guide That Actually Helps</title>
      <dc:creator>Mir Mursalin Ankur</dc:creator>
      <pubDate>Sat, 22 Nov 2025 02:54:55 +0000</pubDate>
      <link>https://forem.com/mir_mursalin_ankur/publishing-your-first-npm-package-a-real-world-guide-that-actually-helps-4l4</link>
      <guid>https://forem.com/mir_mursalin_ankur/publishing-your-first-npm-package-a-real-world-guide-that-actually-helps-4l4</guid>
      <description>&lt;p&gt;You know that moment when you’ve written the &lt;em&gt;same utility function&lt;/em&gt; for the third time across different projects and suddenly think: &lt;strong&gt;“Wait… why am I doing this again?”&lt;/strong&gt;&lt;br&gt;
Yeah — that’s exactly how NPM packages quietly sneak into your life.&lt;/p&gt;

&lt;p&gt;I recently went through the process of turning a simple HTML-to-PDF generator into a proper NPM package. What started as a quick utility became a small adventure — a mix of excitement, challenges, and learning-by-doing. A little confusing at times, but absolutely worth it.&lt;/p&gt;

&lt;p&gt;And here’s the twist: NPM has updated quite a few things lately — &lt;strong&gt;deprecated classic tokens, changed their UI, and even updated how org-scoped packages behave&lt;/strong&gt;. So if you’ve followed older tutorials, you’ve probably felt that “why isn’t this working?” frustration.&lt;/p&gt;

&lt;p&gt;This guide is different.&lt;br&gt;
It’s based on real steps, real mistakes, and the little details nobody tells you about until you’re already stuck.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why Even Bother Publishing a Package?
&lt;/h2&gt;

&lt;p&gt;Publishing a package does three magical things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reusability&lt;/strong&gt;: Stop copy-pasting code between projects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Helping others&lt;/strong&gt;: Someone out there is fighting the same problem right now&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning&lt;/strong&gt;: You'll dive into open-source workflow, versioning, CI/CD, and documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plus, there's something genuinely satisfying about typing &lt;code&gt;npm install your-package-name&lt;/code&gt; and seeing your own work install. It's a tiny piece of software craftsmanship that feels really good.&lt;/p&gt;


&lt;h2&gt;
  
  
  Understanding Scopes and Naming (Before You Start)
&lt;/h2&gt;

&lt;p&gt;NPM packages can be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unscoped&lt;/strong&gt;: &lt;code&gt;your-package-name&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scoped&lt;/strong&gt;: &lt;code&gt;@yourname/your-package-name&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's what matters: if your *NPM username *is &lt;code&gt;cooldev&lt;/code&gt;, then &lt;code&gt;@cooldev/anything&lt;/code&gt; is automatically your namespace. You don't need to create an organization unless your scope differs from your username.&lt;/p&gt;

&lt;p&gt;Check if a name is available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm view @yourname/your-package-name
&lt;span class="c"&gt;# 404 means it's free to use&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 1: Set Up the Project Structure
&lt;/h2&gt;

&lt;p&gt;Start fresh. Create a clean directory and initialize it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;your-package-name
&lt;span class="nb"&gt;cd &lt;/span&gt;your-package-name
git init
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep things organized with a &lt;code&gt;src/&lt;/code&gt; folder for your code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Bring in Your Source Files
&lt;/h2&gt;

&lt;p&gt;Copy your working code into the new package structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; src
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /path/to/your-project/src/lib/your-library/&lt;span class="k"&gt;*&lt;/span&gt; src/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isolation keeps your package clean and focused.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Configure Your package.json
&lt;/h2&gt;

&lt;p&gt;This is the heart of your package. Update these critical fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@yourname/your-package-name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/index.d.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"repository"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/yourname/your-package-name.git"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bugs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/yourname/your-package-name/issues"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"homepage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/yourname/your-package-name#readme"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; is unique (check NPM first)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;repository.url&lt;/code&gt; points to your GitHub repo&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bugs.url&lt;/code&gt; gives people a place to report issues&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;homepage&lt;/code&gt; links to documentation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 4: Connect to GitHub
&lt;/h2&gt;

&lt;p&gt;Get your code into version control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial commit: My Library to NPM Package"&lt;/span&gt;
git branch &lt;span class="nt"&gt;-M&lt;/span&gt; master
git remote add origin https://github.com/yourname/your-package-name.git
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 5: Test Your Build Locally
&lt;/h2&gt;

&lt;p&gt;Before publishing anything, make sure it actually works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the secret weapon most beginners don't know about — test your package locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm pack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a &lt;code&gt;.tgz&lt;/code&gt; file — the exact same file that would go to NPM.&lt;/p&gt;

&lt;p&gt;Now test it in another project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; /path/to/your-package-1.0.0.tgz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This catches so many issues. Seriously, multiple problems only showed up after testing in a fresh environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Login to NPM Account
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fivqhro4sryihvx38gm33.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fivqhro4sryihvx38gm33.png" alt="NPM Profile Page" width="299" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can login to npm from any browser by going to &lt;a href="https://www.npmjs.com/login" rel="noopener noreferrer"&gt;npmjs.com&lt;/a&gt;. Login is also straightforward by command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll be prompted for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Username&lt;/li&gt;
&lt;li&gt;Password&lt;/li&gt;
&lt;li&gt;Email&lt;/li&gt;
&lt;li&gt;One-time password (if you have 2FA enabled)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 7: Choose Your Publishing Strategy
&lt;/h2&gt;

&lt;p&gt;You have two ways to publish your package:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Manual Publishing&lt;/strong&gt; - Quick start, publish from command line&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated CI/CD&lt;/strong&gt; - Professional, publish on git tag (recommended)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's start with manual to get you published quickly, then set up automation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Manual Publishing (Quick Start)
&lt;/h3&gt;

&lt;p&gt;Login to NPM from your terminal then publish:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm login
npm publish &lt;span class="nt"&gt;--access&lt;/span&gt; public
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--access public&lt;/code&gt; flag is required for scoped packages (like &lt;code&gt;@yourname/package-name&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;That's it. Your package is live on NPM!&lt;/p&gt;

&lt;p&gt;But here's the thing: manual publishing is error-prone. You might forget to build, or push the wrong version, or skip tests. Let's set up automation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: Set Up Automated Publishing (Recommended)
&lt;/h2&gt;

&lt;p&gt;Here's where it gets interesting. &lt;strong&gt;Classic NPM tokens are deprecated&lt;/strong&gt; — many old tutorials still reference them, which is confusing. You need to create a &lt;strong&gt;Granular Access Token&lt;/strong&gt; instead. Then use github &lt;code&gt;CI/CD&lt;/code&gt; workflow to publish the changes to &lt;code&gt;npm&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnxwe12gt48mbng7u9tjy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnxwe12gt48mbng7u9tjy.png" alt="NPM Classic Token Deprecation Notice" width="312" height="119"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a Granular Access Token
&lt;/h3&gt;

&lt;p&gt;This token lets GitHub Actions publish your package automatically. Here's how:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Go to &lt;a href="https://www.npmjs.com/login" rel="noopener noreferrer"&gt;npmjs.com&lt;/a&gt; and login&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Navigate to your profile → &lt;strong&gt;Access Tokens&lt;/strong&gt; → &lt;strong&gt;Generate New Token&lt;/strong&gt; → Select &lt;strong&gt;Granular Access Token&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Configure the token:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Token Name: GitHub Actions CI/CD
Expiration: 90 days (or your preference)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Set permissions:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Under "Packages and scopes":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select &lt;strong&gt;Read and write&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Choose either:

&lt;ul&gt;
&lt;li&gt;"All packages" (easier), OR&lt;/li&gt;
&lt;li&gt;Select your specific package &lt;code&gt;@yourname/your-package-name&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Copy the token immediately&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You won't be able to see it again! It starts with &lt;code&gt;npm_...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Add to GitHub Secrets:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go to your GitHub repository:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Settings&lt;/code&gt; → &lt;code&gt;Secrets and variables&lt;/code&gt; → &lt;code&gt;Actions&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click "New repository secret"&lt;/li&gt;
&lt;li&gt;Name: &lt;code&gt;NPM_TOKEN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Value: Paste your token&lt;/li&gt;
&lt;li&gt;Click "Add secret"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now GitHub Actions can publish on your behalf.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 9: Understanding Semantic Versioning
&lt;/h2&gt;

&lt;p&gt;Before we automate, let's talk about versioning. It's not just numbers — it's a promise to your users:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;When to Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm version patch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1.0.0 → 1.0.1&lt;/td&gt;
&lt;td&gt;Bug fixes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm version minor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1.0.0 → 1.1.0&lt;/td&gt;
&lt;td&gt;New features&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm version major&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1.0.0 → 2.0.0&lt;/td&gt;
&lt;td&gt;Breaking changes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here's the magic: each &lt;code&gt;npm version&lt;/code&gt; command does three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Updates version in &lt;code&gt;package.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Creates a git commit&lt;/li&gt;
&lt;li&gt;Creates a git tag (like &lt;code&gt;v1.0.1&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That git tag is what triggers automated publishing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 10: Create the GitHub Actions Workflow
&lt;/h2&gt;

&lt;p&gt;Now let's set up the automation. This workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Triggers when you push a version tag (like &lt;code&gt;v1.2.3&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Runs your tests and builds&lt;/li&gt;
&lt;li&gt;Verifies the version matches&lt;/li&gt;
&lt;li&gt;Publishes to NPM with provenance (security feature)&lt;/li&gt;
&lt;li&gt;Creates a GitHub release&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create &lt;code&gt;.github/workflows/publish.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish to NPM&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;v*'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;publish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
      &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pnpm/action-setup@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;9.0.0&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;18.20.0'&lt;/span&gt;
          &lt;span class="na"&gt;registry-url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://registry.npmjs.org'&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pnpm&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pnpm install --frozen-lockfile&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pnpm run typecheck&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pnpm run build&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Verify version matches tag&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;TAG="${GITHUB_REF#refs/tags/}"&lt;/span&gt;
          &lt;span class="s"&gt;VERSION="${TAG#v}"&lt;/span&gt;
          &lt;span class="s"&gt;FILE_VERSION=$(node -p "require('./package.json').version")&lt;/span&gt;
          &lt;span class="s"&gt;if [ "$VERSION" != "$FILE_VERSION" ]; then&lt;/span&gt;
            &lt;span class="s"&gt;echo "Version mismatch: tag=$VERSION, package.json=$FILE_VERSION"&lt;/span&gt;
            &lt;span class="s"&gt;exit 1&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish to NPM&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;NODE_AUTH_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.NPM_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pnpm publish --access public --provenance&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important notes about this workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;permissions: id-token: write&lt;/code&gt; enables NPM provenance (proves your package came from your GitHub repo)&lt;/li&gt;
&lt;li&gt;The version verification step prevents accidental publishes with mismatched versions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--frozen-lockfile&lt;/code&gt; ensures consistent installs&lt;/li&gt;
&lt;li&gt;Adjust &lt;code&gt;pnpm&lt;/code&gt; to &lt;code&gt;npm&lt;/code&gt; if you're not using pnpm&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 11: Publishing with the Automated Workflow
&lt;/h2&gt;

&lt;p&gt;Now that automation is set up, here's your new publishing workflow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Make sure all changes are committed:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"feat: add new feature"&lt;/span&gt;
git push origin master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Create and push a version tag:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# For a patch release (bug fixes)&lt;/span&gt;
npm version patch
git push origin master &lt;span class="nt"&gt;--tags&lt;/span&gt;

&lt;span class="c"&gt;# For a minor release (new features)&lt;/span&gt;
npm version minor
git push origin master &lt;span class="nt"&gt;--tags&lt;/span&gt;

&lt;span class="c"&gt;# For a major release (breaking changes)&lt;/span&gt;
npm version major
git push origin master &lt;span class="nt"&gt;--tags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Watch it happen:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to your GitHub repository → "Actions" tab&lt;/li&gt;
&lt;li&gt;You'll see the workflow running&lt;/li&gt;
&lt;li&gt;Within a few minutes, your package is published!&lt;/li&gt;
&lt;li&gt;GitHub automatically creates a release too&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; The moment you push the tag, GitHub Actions takes over. No manual &lt;code&gt;npm publish&lt;/code&gt; needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 12: Control What Gets Published
&lt;/h2&gt;

&lt;p&gt;Want to see exactly what goes into your package?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm pack &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typically you want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dist/&lt;/code&gt; (your compiled code)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LICENSE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use &lt;code&gt;.npmignore&lt;/code&gt; to exclude:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source files (&lt;code&gt;src/&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Tests&lt;/li&gt;
&lt;li&gt;Config files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.github/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Development dependencies&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 13: Write Documentation That Actually Helps
&lt;/h2&gt;

&lt;p&gt;Your README is the first thing people see. Make it count:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Must-haves:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quick installation command&lt;/li&gt;
&lt;li&gt;Basic usage example (copy-paste ready)&lt;/li&gt;
&lt;li&gt;API reference&lt;/li&gt;
&lt;li&gt;Common problems and solutions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Nice-to-haves:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Advanced examples&lt;/li&gt;
&lt;li&gt;Browser vs Node.js differences&lt;/li&gt;
&lt;li&gt;Performance tips&lt;/li&gt;
&lt;li&gt;Contribution guidelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good documentation increases adoption way more than perfect code does.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 14: Licensing and Collaboration
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Choose a license:&lt;/strong&gt; MIT is the most permissive and common for open-source libraries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enable collaboration:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; with guidelines&lt;/li&gt;
&lt;li&gt;Use GitHub Issues for bug reports&lt;/li&gt;
&lt;li&gt;Consider GitHub Discussions for questions&lt;/li&gt;
&lt;li&gt;Tag releases properly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note on releases:&lt;/strong&gt;&lt;br&gt;
With GitHub Actions set up, you don't need to manually create releases. The workflow automatically creates a GitHub release when you push a version tag. But you can still edit the release notes afterward to add detailed changelogs.&lt;/p&gt;

&lt;p&gt;If it is important to create &lt;code&gt;Releases&lt;/code&gt; manually, follow this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to your repo's "Releases" tab&lt;/li&gt;
&lt;li&gt;Click "Create new release"&lt;/li&gt;
&lt;li&gt;Tag it as &lt;code&gt;v1.x.x&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add a changelog describing what changed&lt;/li&gt;
&lt;li&gt;Publish&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once the tag is pushed, GitHub Actions takes over and handles publishing.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 15: When Things Go Wrong
&lt;/h2&gt;

&lt;p&gt;Yes, mistakes happen. Accidentally published version 1.1.0 instead of 1.0.3? Been there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Within 24 hours:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm unpublish @scope/package@version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After 24 hours:&lt;/strong&gt;&lt;br&gt;
NPM won't let you unpublish for safety reasons. Instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm deprecate @scope/package@version &lt;span class="s2"&gt;"Broken release, use 1.0.4 instead"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then publish the correct version and update your changelog.&lt;/p&gt;

&lt;p&gt;Wait a minute or two after unpublishing — NPM's cache takes time to update.&lt;/p&gt;

&lt;p&gt;Lesson learned: always double-check before tagging and pushing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus: One-Command Publishing Scripts
&lt;/h2&gt;

&lt;p&gt;Want to make publishing even easier? Create these helper scripts that combine versioning and pushing in one command:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;publish-patch.sh:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
npm version patch
git push origin master &lt;span class="nt"&gt;--tags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;publish-minor.sh:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
npm version minor
git push origin master &lt;span class="nt"&gt;--tags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;publish-major.sh:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
npm version major
git push origin master &lt;span class="nt"&gt;--tags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make them executable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x publish-&lt;span class="k"&gt;*&lt;/span&gt;.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now publishing is as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./publish-patch.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script bumps the version, creates the tag, and pushes it. GitHub Actions sees the tag and handles the rest — testing, building, and publishing to NPM automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best Practices Checklist
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before Publishing:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Test with &lt;code&gt;npm pack&lt;/code&gt; and install locally&lt;/li&gt;
&lt;li&gt;✅ Run all tests and type checks&lt;/li&gt;
&lt;li&gt;✅ Update README with latest features&lt;/li&gt;
&lt;li&gt;✅ Add or update CHANGELOG&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Package Quality:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Include TypeScript types (if applicable)&lt;/li&gt;
&lt;li&gt;✅ Add a LICENSE file&lt;/li&gt;
&lt;li&gt;✅ Set up CI/CD for automated testing&lt;/li&gt;
&lt;li&gt;✅ Use &lt;code&gt;.npmignore&lt;/code&gt; to keep package lean&lt;/li&gt;
&lt;li&gt;✅ Include usage examples in README&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Maintenance:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Respond to issues promptly&lt;/li&gt;
&lt;li&gt;✅ Tag releases with semantic versions&lt;/li&gt;
&lt;li&gt;✅ Keep dependencies updated&lt;/li&gt;
&lt;li&gt;✅ Document breaking changes clearly&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real-World Example: From Hobby Project to Published Package
&lt;/h2&gt;

&lt;p&gt;This guide is based on a real-world transformation: turning an HTML-to-PDF generator from a single-application solution into a framework-agnostic package that any developer can use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The package:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/package/@encryptioner/html-to-pdf-generator" rel="noopener noreferrer"&gt;&lt;code&gt;@encryptioner/html-to-pdf-generator&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Modern multi-page PDF generator from HTML content with smart pagination and styling support.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The transformation journey:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Started as tightly coupled code in a single project&lt;/li&gt;
&lt;li&gt;Extracted the core logic into a standalone library&lt;/li&gt;
&lt;li&gt;Made it framework-agnostic (works with React, Vue, vanilla JS, Node.js)&lt;/li&gt;
&lt;li&gt;Added proper TypeScript types&lt;/li&gt;
&lt;li&gt;Set up automated publishing with GitHub Actions&lt;/li&gt;
&lt;li&gt;Documented everything in a useful README&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The outcome:&lt;/strong&gt;&lt;br&gt;
A package that solves a real problem for developers who need to generate clean, multi-page PDFs from HTML without fighting with browser print dialogs or complex PDF libraries.&lt;/p&gt;

&lt;p&gt;Installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @encryptioner/html-to-pdf-generator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This transition from "code that works for one project" to "code that works for everyone" demonstrates what successful package publishing looks like. The technical steps matter, but the mindset shift matters more — thinking about edge cases, documentation, and user experience.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Publishing your first NPM package is a rite of passage. It's messy, confusing, and occasionally frustrating — but incredibly rewarding.&lt;/p&gt;

&lt;p&gt;The moment you see someone install your package, report a bug, or (even better) contribute a feature — it all clicks. You're not just writing code anymore. You're building something that helps others build things.&lt;/p&gt;

&lt;p&gt;Don't wait for the perfect moment or the perfect code. Start with something useful, document it well, and iterate. The tools are better than ever, and the community is welcoming.&lt;/p&gt;

&lt;p&gt;And when something breaks at 2 AM (it will), don't panic. Unpublish within 24 hours, fix it, document what happened, and keep going.&lt;/p&gt;

&lt;p&gt;The open-source world needs more builders. So go ahead — publish that package you've been thinking about.&lt;/p&gt;

&lt;p&gt;Happy publishing! 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  Let's Connect
&lt;/h2&gt;

&lt;p&gt;I'm always excited to hear about what you're building! If you found this guide helpful, have questions, or just want to share your NPM publishing journey:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://encryptioner.github.io" rel="noopener noreferrer"&gt;encryptioner.github.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn&lt;/strong&gt;: &lt;a href="https://www.linkedin.com/in/mir-mursalin-ankur" rel="noopener noreferrer"&gt;Mir Mursalin Ankur&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Encryptioner" rel="noopener noreferrer"&gt;@Encryptioner&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;X (Twitter)&lt;/strong&gt;: &lt;a href="https://twitter.com/AnkurMursalin" rel="noopener noreferrer"&gt;@AnkurMursalin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical Writing&lt;/strong&gt;: &lt;a href="https://nerddevs.com/author/ankur/" rel="noopener noreferrer"&gt;Nerddevs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drop a message if you publish your first package using this guide — I'd love to check it out!&lt;/p&gt;

</description>
      <category>node</category>
      <category>npm</category>
      <category>development</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I Built My Own Shell for Fun. You Can Too</title>
      <dc:creator>Mir Mursalin Ankur</dc:creator>
      <pubDate>Fri, 01 Aug 2025 14:07:27 +0000</pubDate>
      <link>https://forem.com/mir_mursalin_ankur/i-built-my-own-shell-for-fun-you-can-too-56j3</link>
      <guid>https://forem.com/mir_mursalin_ankur/i-built-my-own-shell-for-fun-you-can-too-56j3</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fueqeskye4zs3fqh5yiiu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fueqeskye4zs3fqh5yiiu.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Ever wondered what happens when you type &lt;code&gt;ls&lt;/code&gt; and hit Enter? I did too. So I built my own shell to find out.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Why" Behind the Madness
&lt;/h2&gt;

&lt;p&gt;As a lead software engineer, I've spent years working with shells—Bash, Zsh—but I never really understood what was happening under the hood. Sure, I knew about processes, file descriptors, and system calls, but the actual mechanics of how a shell works? That was a black box.&lt;/p&gt;

&lt;p&gt;Then I stumbled upon &lt;a href="https://codingchallenges.fyi/challenges/intro" rel="noopener noreferrer"&gt;codingchallenges.fyi&lt;/a&gt; and their &lt;a href="https://codingchallenges.fyi/challenges/challenge-shell" rel="noopener noreferrer"&gt;shell challenge&lt;/a&gt;. It gave motivation to uncover the invisible scene of most common functionalities. It was my chance to peel back the layers and understand the fundamentals that power every terminal session.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Shell, Really?
&lt;/h2&gt;

&lt;p&gt;When you type &lt;code&gt;ls -la&lt;/code&gt;, here's what happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read&lt;/strong&gt;: The shell reads your input from stdin&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parse&lt;/strong&gt;: It breaks your command into tokens (&lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;-la&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execute&lt;/strong&gt;: It finds the &lt;code&gt;ls&lt;/code&gt; program and runs it with &lt;code&gt;-la&lt;/code&gt; as arguments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wait&lt;/strong&gt;: It waits for the program to finish&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repeat&lt;/strong&gt;: It shows a new prompt, ready for your next command&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simple, right? But the devil is in the details.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Journey Begins: &lt;a href="https://github.com/Encryptioner/ccsh-shell" rel="noopener noreferrer"&gt;ccsh&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I decided to build &lt;strong&gt;ccsh&lt;/strong&gt; (Compact C Shell)—a minimal but functional Unix-like shell. Here's what I learned along the way:&lt;/p&gt;

&lt;h2&gt;
  
  
  Why C?
&lt;/h2&gt;

&lt;p&gt;C is the lingua franca of Unix systems. Every Linux distribution, macOS, and BSD variant comes with a C compiler. This means my shell can run anywhere without dependencies.&lt;/p&gt;

&lt;p&gt;But more importantly, C gives me direct access to the system calls that power everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fork()&lt;/code&gt; and &lt;code&gt;exec()&lt;/code&gt; for process creation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pipe()&lt;/code&gt; for inter-process communication
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dup2()&lt;/code&gt; for file descriptor manipulation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;waitpid()&lt;/code&gt; for process synchronization&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;signal()&lt;/code&gt; for handling interrupts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the same system calls that Bash, Zsh, and every other shell use under the hood.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI as My Learning Partner
&lt;/h2&gt;

&lt;p&gt;Here's the honest truth: &lt;strong&gt;AI generated most of my code, but I understood every line.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How AI Helped Me:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Generated boilerplate code structure&lt;/strong&gt; - I didn't start from scratch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explained complex system calls&lt;/strong&gt; - When I was stuck on &lt;code&gt;fork()&lt;/code&gt; vs &lt;code&gt;exec()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugged memory management issues&lt;/strong&gt; - C's manual memory management is tricky&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Suggested best practices&lt;/strong&gt; - Error handling, code organization, security considerations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Key Insight:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;AI is a tool, not a replacement.&lt;/strong&gt; Understanding the fundamentals is crucial. Here's what I learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;80% understanding is enough&lt;/strong&gt; - If this isn't your core job, you don't need to memorize every system call&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI accelerates learning&lt;/strong&gt; - When you know the basics, AI can help you build faster&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fundamentals matter&lt;/strong&gt; - You still need to understand what &lt;code&gt;fork()&lt;/code&gt; does, even if AI wrote the code&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  My Approach:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start with understanding&lt;/strong&gt; - Learn what each system call does conceptually&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use AI for implementation&lt;/strong&gt; - Let AI handle the boilerplate and edge cases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review and modify&lt;/strong&gt; - Understand AI generated lines. If necessary, get clarification from alternate source&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test and debug&lt;/strong&gt; - Break things to understand them better&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;"AI generated the code, but I understood most of the lines. That's the sweet spot."&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Shell
&lt;/h2&gt;

&lt;p&gt;I chose to implement everything in a single &lt;code&gt;main.c&lt;/code&gt; file for simplicity. Here's how to build it:&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a Makefile?
&lt;/h3&gt;

&lt;p&gt;A Makefile is a configuration file that tells the &lt;a href="https://faculty.cs.niu.edu/~hutchins/csci480/make.htm" rel="noopener noreferrer"&gt;make&lt;/a&gt; utility how to build your project. It's like a recipe that automates the compilation process. Instead of typing long gcc commands, you just run &lt;code&gt;make&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simple Build
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcc &lt;span class="nt"&gt;-Wall&lt;/span&gt; &lt;span class="nt"&gt;-Wextra&lt;/span&gt; &lt;span class="nt"&gt;-std&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;c99 &lt;span class="nt"&gt;-g&lt;/span&gt; main.c &lt;span class="nt"&gt;-o&lt;/span&gt; ccsh &lt;span class="nt"&gt;-lreadline&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What each flag does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-Wall -Wextra&lt;/code&gt;: Enable all warning messages&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-std=c99&lt;/code&gt;: Use C99 standard&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-g&lt;/code&gt;: Include debug symbols&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-lreadline&lt;/code&gt;: Link against the readline library (for command history)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  With Makefile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="c"&gt;# Compiler and flags
&lt;/span&gt;&lt;span class="nv"&gt;CC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; gcc
&lt;span class="nv"&gt;CFLAGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-Wall&lt;/span&gt; &lt;span class="nt"&gt;-Wextra&lt;/span&gt; &lt;span class="nt"&gt;-std&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;c99 &lt;span class="nt"&gt;-g&lt;/span&gt;
&lt;span class="nv"&gt;LDFLAGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-lreadline&lt;/span&gt;

&lt;span class="c"&gt;# Build target: ccsh depends on main.c
&lt;/span&gt;&lt;span class="nl"&gt;ccsh&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;main.c&lt;/span&gt;
    &lt;span class="p"&gt;$(&lt;/span&gt;CC&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;CFLAGS&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; ccsh main.c &lt;span class="p"&gt;$(&lt;/span&gt;LDFLAGS&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Clean up build artifacts
&lt;/span&gt;&lt;span class="nl"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ccsh

&lt;span class="c"&gt;# Install to system (optional)
&lt;/span&gt;&lt;span class="nl"&gt;install&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;ccsh&lt;/span&gt;
    &lt;span class="nb"&gt;cp &lt;/span&gt;ccsh ~/.local/bin/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CC = gcc&lt;/code&gt;: Define the compiler&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CFLAGS&lt;/code&gt;: Compiler flags for warnings and standards&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LDFLAGS&lt;/code&gt;: Linker flags for external libraries&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ccsh: main.c&lt;/code&gt;: Target &lt;code&gt;ccsh&lt;/code&gt; depends on &lt;code&gt;main.c&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;clean&lt;/code&gt;: Remove built files&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Building and Running
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build the shell&lt;/span&gt;
make

&lt;span class="c"&gt;# Run the shell&lt;/span&gt;
./ccsh

&lt;span class="c"&gt;# Clean up&lt;/span&gt;
make clean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Cool Features I Added
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Built-in Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/directory    &lt;span class="c"&gt;# Change directory&lt;/span&gt;
&lt;span class="nb"&gt;pwd&lt;/span&gt;                      &lt;span class="c"&gt;# Print working directory&lt;/span&gt;
&lt;span class="nb"&gt;exit&lt;/span&gt;                     &lt;span class="c"&gt;# Exit shell&lt;/span&gt;
&lt;span class="nb"&gt;jobs&lt;/span&gt;                     &lt;span class="c"&gt;# List background jobs&lt;/span&gt;
&lt;span class="nb"&gt;fg&lt;/span&gt; %1                    &lt;span class="c"&gt;# Bring job to foreground&lt;/span&gt;
&lt;span class="nb"&gt;help&lt;/span&gt;                     &lt;span class="c"&gt;# Show help information&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Alias System
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;ll&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ls -lah"&lt;/span&gt;       &lt;span class="c"&gt;# Create alias&lt;/span&gt;
&lt;span class="nb"&gt;alias&lt;/span&gt;                    &lt;span class="c"&gt;# List all aliases&lt;/span&gt;
&lt;span class="nb"&gt;unalias &lt;/span&gt;ll               &lt;span class="c"&gt;# Remove alias&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Background Job Management
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sleep &lt;/span&gt;10 &amp;amp;               &lt;span class="c"&gt;# Run in background&lt;/span&gt;
&lt;span class="nb"&gt;jobs&lt;/span&gt;                     &lt;span class="c"&gt;# List background jobs&lt;/span&gt;
&lt;span class="nb"&gt;fg&lt;/span&gt; %1                    &lt;span class="c"&gt;# Bring to foreground&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. I/O Redirection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; output.txt          &lt;span class="c"&gt;# Redirect output&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &amp;lt; input.txt          &lt;span class="c"&gt;# Redirect input&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"hello"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; log.txt  &lt;span class="c"&gt;# Append output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Globbing Support
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.txt                 &lt;span class="c"&gt;# List all .txt files&lt;/span&gt;
&lt;span class="nb"&gt;cp &lt;/span&gt;file?.txt backup/     &lt;span class="c"&gt;# Copy files matching pattern&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The "Aha!" Moments
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Process Groups and Sessions
&lt;/h3&gt;

&lt;p&gt;I never fully understood how &lt;code&gt;Ctrl+C&lt;/code&gt; works until I implemented it. When you press &lt;code&gt;Ctrl+C&lt;/code&gt;, the kernel sends a SIGINT to the entire process group. The shell needs to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new process group for each command&lt;/li&gt;
&lt;li&gt;Handle signals appropriately&lt;/li&gt;
&lt;li&gt;Restore the shell's process group when the command finishes&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Environment Variables
&lt;/h3&gt;

&lt;p&gt;I always knew about &lt;code&gt;PATH&lt;/code&gt;, but seeing how the shell searches for executables was enlightening. It's just a simple loop through directories, checking if the file exists and is executable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenges I Faced
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Memory Management Nightmares
&lt;/h3&gt;

&lt;p&gt;C doesn't have garbage collection, so every &lt;code&gt;malloc()&lt;/code&gt; needs a corresponding &lt;code&gt;free()&lt;/code&gt;. With complex command parsing and multiple processes, memory leaks were a constant threat.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signal Handling Complexity
&lt;/h3&gt;

&lt;p&gt;Getting &lt;code&gt;Ctrl+C&lt;/code&gt; to work correctly was surprisingly tricky. The shell needs to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ignore SIGINT for itself&lt;/li&gt;
&lt;li&gt;Forward SIGINT to child processes&lt;/li&gt;
&lt;li&gt;Restore signal handlers after child completion&lt;/li&gt;
&lt;li&gt;Handle signals during pipeline execution&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cross-Platform Quirks
&lt;/h3&gt;

&lt;p&gt;While C is portable, Unix systems have subtle differences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Different signal behavior between Linux and macOS&lt;/li&gt;
&lt;li&gt;Varying limits on file descriptors&lt;/li&gt;
&lt;li&gt;Different implementations of &lt;code&gt;readline&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deep Dive: How C Talks to the Operating System
&lt;/h2&gt;

&lt;p&gt;Let's trace through what happens when you type &lt;code&gt;ls -la&lt;/code&gt; in my shell:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Fork-Exec Dance
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;pid_t&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fork&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Child process&lt;/span&gt;
    &lt;span class="n"&gt;execvp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expanded&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;expanded&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;perror&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"execvp"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Parent process&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Background job&lt;/span&gt;
        &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[%d] %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;job_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;add_job&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Foreground job - wait for completion&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;waitpid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What's happening:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fork()&lt;/code&gt; creates an exact copy of the current process&lt;/li&gt;
&lt;li&gt;The child gets a new process ID but identical memory&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;execvp()&lt;/code&gt; replaces the child's memory with the &lt;code&gt;ls&lt;/code&gt; program&lt;/li&gt;
&lt;li&gt;The parent waits for the child to finish&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. How Redirection Works
&lt;/h3&gt;

&lt;p&gt;When you type &lt;code&gt;ls &amp;gt; output.txt&lt;/code&gt;, here's what happens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Handle output redirection */&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outfile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;O_WRONLY&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;O_CREAT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="n"&gt;O_APPEND&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="n"&gt;O_TRUNC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;fd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outfile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mo"&gt;0644&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;perror&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;dup2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;STDOUT_FILENO&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The magic is &lt;code&gt;dup2(fd, STDOUT_FILENO)&lt;/code&gt;. This makes file descriptor 1 (stdout) point to the same place as our file descriptor.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Globbing Implementation
&lt;/h3&gt;

&lt;p&gt;For &lt;code&gt;ls *.txt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;expand_globs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;expanded_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;expanded_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;expanded_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;glob_t&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gl_offs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gl_pathc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gl_pathv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GLOB_TILDE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="cm"&gt;/* Expand ~ to home directory */&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strchr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sc"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;strchr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sc"&gt;'?'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="cm"&gt;/* Pattern contains glob characters */&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;expanded_count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="n"&gt;GLOB_APPEND&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="cm"&gt;/* Glob failed, use original argument */&lt;/span&gt;
                &lt;span class="n"&gt;expanded_args&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;expanded_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="cm"&gt;/* Add all matched files */&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gl_pathc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;expanded_args&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;expanded_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gl_pathv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="cm"&gt;/* No glob characters, use as-is */&lt;/span&gt;
            &lt;span class="n"&gt;expanded_args&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;expanded_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;expanded_args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;expanded_count&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/* Free the glob structure */&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gl_pathv&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;globfree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What I Learned About Real Shells
&lt;/h2&gt;

&lt;p&gt;Building my own shell gave me a new appreciation for tools like Bash and Zsh. They handle edge cases I never considered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complex quoting rules&lt;/li&gt;
&lt;li&gt;Job control with multiple background processes&lt;/li&gt;
&lt;li&gt;Signal handling in pipelines&lt;/li&gt;
&lt;li&gt;Performance optimizations&lt;/li&gt;
&lt;li&gt;Cross-platform compatibility&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;Building your own shell isn't just an academic exercise. It's a practical way to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Process management&lt;/strong&gt;: How the OS handles processes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I/O systems&lt;/strong&gt;: File descriptors, pipes, redirection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signal handling&lt;/strong&gt;: How programs communicate with the OS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System calls&lt;/strong&gt;: The interface between user and kernel space&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build systems&lt;/strong&gt;: How to create robust, portable software&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;If you're interested in low-level programming, I highly recommend building a shell. Start simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Basic command execution&lt;/li&gt;
&lt;li&gt;Add argument parsing&lt;/li&gt;
&lt;li&gt;Implement I/O redirection&lt;/li&gt;
&lt;li&gt;Add pipes&lt;/li&gt;
&lt;li&gt;Build in job control&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;a href="https://codingchallenges.fyi/challenges/challenge-shell" rel="noopener noreferrer"&gt;codingchallenges.fyi shell challenge&lt;/a&gt; is an excellent starting point. you can also check&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Encryptioner/ccsh-shell" rel="noopener noreferrer"&gt;My Shell Implementation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/" rel="noopener noreferrer"&gt;Unix Programming Manual&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;I'm thinking about adding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tab completion&lt;/li&gt;
&lt;li&gt;Better error handling&lt;/li&gt;
&lt;li&gt;More built-in commands&lt;/li&gt;
&lt;li&gt;Scripting capabilities&lt;/li&gt;
&lt;li&gt;Performance optimizations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But honestly, the learning experience was the real reward. Understanding how the tools I use daily actually work has made me a better engineer.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The next time you type a command, take a moment to appreciate the complexity hidden behind that simple prompt. And maybe, just maybe, consider building your own shell to see what's really happening under the hood.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy coding! 🐚&lt;/em&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  End:
&lt;/h2&gt;

&lt;p&gt;That's all!&lt;/p&gt;

&lt;p&gt;I hope you've found the article useful. You should try building your own shell if you haven't already. Feel free to share your thoughts in the comments below.&lt;/p&gt;

&lt;p&gt;Check more on&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://encryptioner.github.io" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/mir-mursalin-ankur" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Encryptioner" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/AnkurMursalin" rel="noopener noreferrer"&gt;X (Twitter)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nerddevs.com/author/ankur/" rel="noopener noreferrer"&gt;Nerddevs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>c</category>
      <category>shell</category>
      <category>ai</category>
    </item>
    <item>
      <title>Integrating Bitbucket MCP with Cursor: A Practical Guide for Developers</title>
      <dc:creator>Mir Mursalin Ankur</dc:creator>
      <pubDate>Mon, 21 Jul 2025 15:44:40 +0000</pubDate>
      <link>https://forem.com/mir_mursalin_ankur/integrating-bitbucket-mcp-with-cursor-a-practical-guide-for-developers-1e5b</link>
      <guid>https://forem.com/mir_mursalin_ankur/integrating-bitbucket-mcp-with-cursor-a-practical-guide-for-developers-1e5b</guid>
      <description>&lt;p&gt;As developers, we all want our workflows to be smooth and efficient. Managing repositories, reviewing pull requests, and keeping track of changes can get tedious—especially when it means switching between your editor and browser. Recently, I set up the Bitbucket MCP (Model Context Protocol) server with Cursor IDE, and it’s made these tasks much more convenient. &lt;/p&gt;

&lt;p&gt;While Cursor supports &lt;a href="https://docs.cursor.com/bugbot" rel="noopener noreferrer"&gt;bugbot&lt;/a&gt; for GitHub PR reviews, there’s nothing similar out-of-the-box for &lt;a href="https://bitbucket.org/" rel="noopener noreferrer"&gt;bitbucket&lt;/a&gt; users. Setting up Bitbucket MCP with Cursor changed that for me—and made my dev life a lot smoother.&lt;/p&gt;

&lt;p&gt;Here’s a practical guide based on my experience, with tips that apply even if you use other tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Integrate Bitbucket MCP with Cursor?
&lt;/h2&gt;

&lt;p&gt;The main benefit is reducing context switching. With Bitbucket MCP, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List and manage repositories&lt;/li&gt;
&lt;li&gt;Create, review, and approve pull requests&lt;/li&gt;
&lt;li&gt;Access commit history and diffs&lt;/li&gt;
&lt;li&gt;Adjust repository settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All from within Cursor, so you spend more time coding and less time navigating web UIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You’ll Need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cursor IDE&lt;/li&gt;
&lt;li&gt;Node.js v16 or higher&lt;/li&gt;
&lt;li&gt;Bitbucket account with repository access&lt;/li&gt;
&lt;li&gt;Bitbucket Repository Access Token (not an app password)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step-by-Step Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Install the &lt;a href="https://github.com/MatanYemini/bitbucket-mcp" rel="noopener noreferrer"&gt;Bitbucket MCP Server&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;In your project directory, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; bitbucket-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use &lt;code&gt;npm&lt;/code&gt; if you prefer.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Configure MCP
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;.cursor/mcp.json&lt;/code&gt; file in your project root. Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bitbucket"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"BITBUCKET_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.bitbucket.org/2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"BITBUCKET_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-repository-access-token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"BITBUCKET_WORKSPACE"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-workspace"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"bitbucket-mcp"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the placeholders with your actual token and workspace name (not the URL).&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Get a Bitbucket Access Token
&lt;/h3&gt;

&lt;p&gt;Follow &lt;a href="https://support.atlassian.com/bitbucket-cloud/docs/create-a-repository-access-token/" rel="noopener noreferrer"&gt;this guide&lt;/a&gt; to generate a token. Make sure it has &lt;strong&gt;Read/Write&lt;/strong&gt; permissions for repositories and pull requests. Use a repository access token, not an app password.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Test Your Credentials
&lt;/h3&gt;

&lt;p&gt;Before moving on, it’s a good idea to verify your token. Here’s a simple Node.js script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokenCredentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.bitbucket.org/2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your-workspace&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your-repository-access-token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tokenCredentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/workspaces/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tokenCredentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tokenCredentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Token is valid! Workspace:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Token test failed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;statusText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save it as &lt;code&gt;scripts/test-bitbucket-credentials.js&lt;/code&gt; and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node scripts/test-bitbucket-credentials.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see your workspace name, you’re set.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Restart Cursor
&lt;/h3&gt;

&lt;p&gt;Close and reopen Cursor to load the new MCP server. You should see Bitbucket MCP under Settings → Tools &amp;amp; Integrations → MCP Tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everyday Usage
&lt;/h2&gt;

&lt;p&gt;Once set up, you can run Bitbucket commands from Cursor’s agent chat or command palette. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List repos: &lt;code&gt;/bitbucket listRepositories&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Get repo details: &lt;code&gt;/bitbucket getRepository --workspace your-workspace --repo-slug your-repo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Manage pull requests: &lt;code&gt;/bitbucket getPullRequests --workspace your-workspace --repo-slug your-repo&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also create or approve PRs, check commit diffs, and manage branching models—all from your editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP server not loading?&lt;/strong&gt; Double-check your &lt;code&gt;.cursor/mcp.json&lt;/code&gt; and environment variables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth errors?&lt;/strong&gt; Make sure you’re using a repository access token (not an app password) and that it hasn’t expired.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commands not recognized?&lt;/strong&gt; Restart Cursor and verify the MCP server is loaded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to avoid confirmation prompts for MCP tools, enable “Auto-run MCP Tools” in Cursor’s chat settings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep Dive: How MCP Works (and How the Cursor and Bitbucket MCP Server Uses It)
&lt;/h2&gt;

&lt;p&gt;To really understand the power of this integration, it helps to know what’s happening under the hood.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is MCP?
&lt;/h3&gt;

&lt;p&gt;MCP stands for Model Context Protocol. It’s an open protocol designed to let tools (like editors, IDEs, or AI agents) communicate with code hosts (like GitHub, GitLab, Bitbucket, etc.) in a standardized way. Instead of every tool building its own custom integration for each code host, MCP defines a common set of commands and data structures for things like repositories, pull requests, branches, and commits.&lt;/p&gt;

&lt;p&gt;Think of MCP as a universal translator for developer tools. It lets your editor ask questions (“What pull requests are open?”) or take actions (“Create a new branch”) in a way that works across different platforms, as long as there’s an MCP server for that platform.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Does the Bitbucket MCP Server Leverage MCP?
&lt;/h3&gt;

&lt;p&gt;The Bitbucket MCP server is an implementation of this protocol, acting as a bridge between Cursor and Bitbucket’s API. Here’s how it works in practice:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Command Flow&lt;/strong&gt;: When you run a Bitbucket command in Cursor (like listing repositories or creating a pull request), Cursor sends a standardized MCP request to the Bitbucket MCP server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Translation Layer&lt;/strong&gt;: The MCP server receives this request and translates it into the appropriate Bitbucket API call, handling authentication and formatting as needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Exchange&lt;/strong&gt;: The server fetches the data from Bitbucket, then converts the response back into the MCP format so Cursor can display it in a consistent way, regardless of the underlying code host.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt;: The MCP server uses your Bitbucket repository access token to authenticate API requests securely, so you never have to expose your credentials directly to Cursor or other tools.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This architecture means you get a seamless, unified experience in your editor, while the heavy lifting of talking to Bitbucket’s API is handled by the MCP server. It also makes it easier for new tools and platforms to integrate in the future—just implement the MCP protocol, and you’re good to go.&lt;/p&gt;

&lt;p&gt;In summary, MCP is the glue that connects your editor to your code host, and the Bitbucket MCP server is the translator that makes it all work smoothly for Bitbucket users.&lt;/p&gt;

&lt;h3&gt;
  
  
  How AI Chat Agents of Cursor Leverage MCP
&lt;/h3&gt;

&lt;p&gt;One of the most powerful aspects of MCP is how it enables AI chat agents (like those in Cursor) to interact with your repositories in a smart, automated way. Here’s how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User Request&lt;/strong&gt;: You ask the AI agent to do something—like review a pull request, summarize recent changes, or list open issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP Command Generation&lt;/strong&gt;: The agent formulates a standardized MCP command based on your request (for example, "get pull request details").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Retrieval&lt;/strong&gt;: The MCP server fetches the relevant data from Bitbucket, translates it into the MCP format, and returns it to the agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Processing&lt;/strong&gt;: The agent analyzes the structured data—such as code diffs, comments, or commit history—and generates a human-friendly summary, review, or action.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action or Insight&lt;/strong&gt;: The agent presents its findings to you (e.g., a PR review summary, a list of suggested changes, or even automates a merge if you approve).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because MCP provides a consistent, structured interface, the AI agent doesn’t need to know the details of Bitbucket’s API. This makes it much easier to build powerful, context-aware automation and insights directly into your development workflow, regardless of which code host you use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Broader Lessons
&lt;/h2&gt;

&lt;p&gt;Even if you use GitHub, GitLab, or another platform, the principle is the same: integrating your tools saves time and helps you stay focused. Investing a little effort in setup pays off in smoother workflows and fewer distractions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Integrating Bitbucket MCP with Cursor has made my daily work more efficient and less fragmented. If you’re looking to streamline your repo management, I recommend giving it a try—or finding similar integrations for your stack. The right tools should help you focus on what matters: building great software.&lt;/p&gt;

&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;p&gt;That's all!&lt;/p&gt;

&lt;p&gt;I hope you've found the article useful. You should try to use &lt;code&gt;mcp server&lt;/code&gt; if you haven't already. It has many interesting use cases. Feel free to share your thoughts and experiences.&lt;/p&gt;

&lt;p&gt;Check more on&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://encryptioner.github.io" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/mir-mursalin-ankur" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Encryptioner" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/AnkurMursalin" rel="noopener noreferrer"&gt;X (Twitter)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nerddevs.com/author/ankur/" rel="noopener noreferrer"&gt;Nerddevs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>automation</category>
      <category>productivity</category>
      <category>devchallenge</category>
      <category>learning</category>
    </item>
    <item>
      <title>Give Google Sheet Access Only to Form Submitters — With Apps Script</title>
      <dc:creator>Mir Mursalin Ankur</dc:creator>
      <pubDate>Sun, 20 Jul 2025 16:23:40 +0000</pubDate>
      <link>https://forem.com/mir_mursalin_ankur/give-google-sheet-access-only-to-form-submitters-with-apps-script-1anh</link>
      <guid>https://forem.com/mir_mursalin_ankur/give-google-sheet-access-only-to-form-submitters-with-apps-script-1anh</guid>
      <description>&lt;p&gt;As a lead software engineer, I've seen teams across companies rely on &lt;strong&gt;Google Forms + Sheets&lt;/strong&gt; for quick data collection — feedback surveys, attendance tracking, event registration, you name it.&lt;/p&gt;

&lt;p&gt;But here’s a common issue that comes up:&lt;br&gt;
&lt;strong&gt;How do you restrict access to the Google Sheet so only the people who submitted the form can view it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Google Forms doesn't offer this natively (which is honestly surprising given how common this need is). But with a little Apps Script magic, you can make it happen.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Let's say you're running a private beta program. You collect sign-ups via Google Form. Now you want only those who signed up (and no one else) to see the list.&lt;/p&gt;

&lt;p&gt;Manually adding viewers to the Sheet? Not scalable.&lt;br&gt;
Leaving it public? Not secure.&lt;br&gt;
The goal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Give &lt;strong&gt;view access only&lt;/strong&gt; to people who submit the form.&lt;/li&gt;
&lt;li&gt;Remove access from people who are no longer in the list.&lt;/li&gt;
&lt;li&gt;Keep certain admin/owner access always intact.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  The Approach
&lt;/h2&gt;

&lt;p&gt;We hook into &lt;a href="https://developers.google.com/apps-script" rel="noopener noreferrer"&gt;Google Apps Script&lt;/a&gt; and:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read the email column from the sheet.&lt;/li&gt;
&lt;li&gt;Add new emails as viewers.&lt;/li&gt;
&lt;li&gt;Remove emails who shouldn't have access anymore.&lt;/li&gt;
&lt;li&gt;Always preserve admin users (like yourself).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's the battle-tested script I've used in my projects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateSheetSharing&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;spreadsheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveSpreadsheet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;spreadsheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSheets&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// First sheet&lt;/span&gt;

  &lt;span class="c1"&gt;// update this column according to your sheet&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailColumnIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Column B = 2&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;startRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Header on row 1&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLastRow&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lastRow&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;startRow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ownerEmails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin@gmail.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// Add your admin email(s)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;startRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;emailColumnIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastRow&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;startRow&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValues&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uniqueEmails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()))];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentViewers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;spreadsheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getViewers&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEmail&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="c1"&gt;// Add new viewers&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;uniqueEmails&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ownerEmails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;currentViewers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;spreadsheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addViewer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Failed to add &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Remove viewers not on the list&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;currentViewers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ownerEmails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;uniqueEmails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;spreadsheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeViewer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Failed to remove &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Deployment Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Trigger it on Form Submit&lt;/strong&gt;:&lt;br&gt;
Go to &lt;strong&gt;Triggers → Add Trigger&lt;/strong&gt; → Choose &lt;code&gt;updateSheetSharing&lt;/code&gt; → Event: &lt;code&gt;On form submit&lt;/code&gt;. See &lt;a href="https://developers.google.com/apps-script/guides/triggers" rel="noopener noreferrer"&gt;Google's trigger documentation&lt;/a&gt; for more details.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Avoid Too Many Writes&lt;/strong&gt;:&lt;br&gt;
If your form gets hundreds of submissions daily, consider adding debounce logic or batching updates periodically.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error Handling&lt;/strong&gt;:&lt;br&gt;
Some domains may block sharing or throw quota errors — log and monitor failures via &lt;code&gt;Logger&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Security Reminder&lt;/strong&gt;:&lt;br&gt;
Never share sheet access based on user input &lt;em&gt;unless you validate the email&lt;/em&gt; (i.e., restrict form to logged-in users and auto-capture email).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;"Google hasn't verified this app" Warning&lt;/strong&gt;:&lt;br&gt;
You'll likely see this warning when first running the script. This happens because Apps Script is accessing sensitive Google services (Sheets, Drive) and requesting OAuth scopes that Google considers sensitive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Solution for Personal/Internal Use:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
When you see the warning screen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click "Advanced" at the bottom&lt;/li&gt;
&lt;li&gt;Click "Go to &lt;a href="https://dev.tounsafe"&gt;your project name&lt;/a&gt;"&lt;/li&gt;
&lt;li&gt;Accept the permissions manually for your account&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is safe for personal scripts or internal organizational use. The warning appears because Google hasn't reviewed your specific script, but you can authorize it yourself.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Domain Restrictions&lt;/strong&gt;:&lt;br&gt;
Some organizations block external sharing entirely. If you're in a corporate environment, check with your IT team about Google Workspace sharing policies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quota Limits&lt;/strong&gt;:&lt;br&gt;
Apps Script has daily quotas for API calls. For high-volume forms, monitor your usage in the Apps Script dashboard under "Quotas" to avoid hitting limits. Check &lt;a href="https://developers.google.com/apps-script/guides/services/quotas" rel="noopener noreferrer"&gt;Google's quota limits&lt;/a&gt; for current limits.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Testing in Development&lt;/strong&gt;:&lt;br&gt;
Test the script on a copy of your sheet first. Create a test form and sheet to verify everything works before deploying to production.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Execution Time Limits&lt;/strong&gt;:&lt;br&gt;
Apps Script has a maximum execution time of 6 minutes per invocation. If your script processes a large number of emails, it might timeout. For high-volume scenarios, consider batching operations or using time-based triggers instead of form submission triggers. See &lt;a href="https://developers.google.com/apps-script/guides/services/quotas#current_limitations" rel="noopener noreferrer"&gt;Google's execution limits&lt;/a&gt; for detailed information.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Monitoring Executions&lt;/strong&gt;:&lt;br&gt;
After setting up triggers, you can monitor their execution in the Apps Script dashboard:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;strong&gt;Executions&lt;/strong&gt; in the left sidebar&lt;/li&gt;
&lt;li&gt;See a list of all recent trigger runs with timestamps&lt;/li&gt;
&lt;li&gt;Check execution status (success/failed)&lt;/li&gt;
&lt;li&gt;Click on any execution to view detailed logs&lt;/li&gt;
&lt;li&gt;Failed executions show error messages and stack traces&lt;/li&gt;
&lt;li&gt;Successful runs show "Execution completed" status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is crucial for debugging. If your script isn't working as expected, check the Executions menu first to see if triggers are firing and what errors might be occurring.&lt;/p&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Deep Dive: How Google Handles Millions of Triggers
&lt;/h2&gt;

&lt;p&gt;Ever wonder how Google manages Apps Script triggers for millions of users worldwide? Here's what's happening behind the scenes:&lt;/p&gt;

&lt;h3&gt;
  
  
  Google's Trigger Infrastructure
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Event-Driven Architecture&lt;/strong&gt;: Google uses a sophisticated event-driven system where every Google Workspace action (form submission, sheet edit, time-based events) generates events that flow through their infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distributed Processing&lt;/strong&gt;: When you submit a form, Google's systems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Detect the form submission event&lt;/li&gt;
&lt;li&gt;Route it to the appropriate Apps Script project&lt;/li&gt;
&lt;li&gt;Execute your script in a sandboxed environment&lt;/li&gt;
&lt;li&gt;Handle the response and any side effects (like sharing permissions)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Scalability Challenges&lt;/strong&gt;: Google processes billions of events daily across:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Form submissions&lt;/li&gt;
&lt;li&gt;Sheet modifications
&lt;/li&gt;
&lt;li&gt;Time-based triggers&lt;/li&gt;
&lt;li&gt;Calendar events&lt;/li&gt;
&lt;li&gt;Email triggers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why the 6-Minute Limit?
&lt;/h3&gt;

&lt;p&gt;The execution time limit isn't arbitrary. Google's infrastructure needs to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resource Management&lt;/strong&gt;: Prevent runaway scripts from consuming excessive resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fair Usage&lt;/strong&gt;: Ensure all users get reasonable access to computing resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Control&lt;/strong&gt;: Apps Script runs on Google's infrastructure, and they need to manage operational costs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliability&lt;/strong&gt;: Shorter execution times mean faster recovery from failures&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Event Processing Pipeline
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Form Submission → Event Queue → Trigger Router → Script Execution → Result Handler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Event Queue&lt;/strong&gt;: Google maintains massive queues of events waiting to be processed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trigger Router&lt;/strong&gt;: Routes events to the correct Apps Script projects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Script Execution&lt;/strong&gt;: Runs your code in isolated containers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result Handler&lt;/strong&gt;: Processes the output and applies changes (like sharing permissions)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why Triggers Sometimes Fail
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Rate Limiting&lt;/strong&gt;: Google implements sophisticated rate limiting to prevent abuse and ensure fair resource distribution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resource Contention&lt;/strong&gt;: During peak hours, the system might be under higher load, causing occasional delays or failures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Network Issues&lt;/strong&gt;: Between Google's internal services (Forms → Sheets → Apps Script), network hiccups can cause temporary failures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quota Management&lt;/strong&gt;: Google tracks usage across all services to prevent any single user from overwhelming the system.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Magic of "Eventually Consistent"
&lt;/h3&gt;

&lt;p&gt;Google's systems are designed for "eventual consistency" - meaning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your trigger might not fire immediately&lt;/li&gt;
&lt;li&gt;There might be a few seconds delay&lt;/li&gt;
&lt;li&gt;But the system guarantees it will eventually process your event&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This design allows Google to handle massive scale while maintaining reliability.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I Use This
&lt;/h2&gt;

&lt;p&gt;I’ve used this in social group's data collection, closed feedback loops, and classroom setups. It works beautifully when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want lightweight access control without complex infrastructure&lt;/li&gt;
&lt;li&gt;You trust the form submitters (or validate their identity)&lt;/li&gt;
&lt;li&gt;You don't want to set up a whole database + auth system&lt;/li&gt;
&lt;li&gt;You need something that "just works" without ongoing maintenance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's not enterprise-grade security — but for small teams and quick workflows, it's a surprisingly robust solution that I've seen work reliably in production.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Apps Script has a &lt;em&gt;quiet superpower&lt;/em&gt;: It brings automation to everyday Google tools that teams already use daily.&lt;/p&gt;

&lt;p&gt;What I love about this approach is its simplicity. With just ~50 lines of code, you can build a simple access control system that feels like it was always meant to be there — tailored to your specific workflow.&lt;/p&gt;

&lt;p&gt;And that's why I still reach for Apps Script for glue code like this — it's the Swiss Army knife of Google Workspace automation.&lt;/p&gt;




&lt;p&gt;Feel free to adapt this script for your own use case. If you're dealing with something more complex (like edit permissions, or syncing to multiple sheets), you'll want to add further checks — but this core pattern will get you a long way.&lt;/p&gt;

&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;p&gt;That's all!&lt;/p&gt;

&lt;p&gt;I hope you've found the article useful. You should try to use &lt;code&gt;Apps Script&lt;/code&gt; if you haven't already. It has many interesting use cases. Feel free to share your thoughts and experiences.&lt;/p&gt;

&lt;p&gt;Check more on&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://encryptioner.github.io" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/mir-mursalin-ankur" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Encryptioner" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/AnkurMursalin" rel="noopener noreferrer"&gt;X (Twitter)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nerddevs.com/author/ankur/" rel="noopener noreferrer"&gt;Nerddevs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>automation</category>
      <category>tooling</category>
      <category>google</category>
      <category>productivity</category>
    </item>
    <item>
      <title>📬 My Go-To Engineering Newsletters (30+ Summarized)</title>
      <dc:creator>Mir Mursalin Ankur</dc:creator>
      <pubDate>Thu, 22 May 2025 02:19:25 +0000</pubDate>
      <link>https://forem.com/mir_mursalin_ankur/my-go-to-engineering-newsletters-30-summarized-19na</link>
      <guid>https://forem.com/mir_mursalin_ankur/my-go-to-engineering-newsletters-30-summarized-19na</guid>
      <description>&lt;p&gt;Engineering newsletters offer a streamlined way for professionals to stay informed about industry trends, learn new skills, and connect with a community. They are a convenient way to access curated information and expert insights, keeping you ahead of the curve in a rapidly evolving field.&lt;/p&gt;

&lt;p&gt;Here’s a quick breakdown of the newsletters I read regularly, their key topics, and why they’re valuable for software engineers.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 A. Software Engineering &amp;amp; System Design
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. ByteByteGo&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: System design, scalability, and backend architecture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Offers visual explanations of complex system design concepts, beneficial for technical interviews and architectural understanding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://bytebytego.com" rel="noopener noreferrer"&gt;bytebytego.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Quastor&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: System design, big tech engineering blog summaries, technical deep dives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Summaries of big tech engineering blogs and guides on API design, database paradigms, and performance optimization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://blog.quastor.org/" rel="noopener noreferrer"&gt;blog.quastor.org&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. System Design One by Neo Kim&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: In-depth system design case studies and architectural patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Provides practical insights into designing robust systems, enhancing your architectural skills.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://systemdesign.one" rel="noopener noreferrer"&gt;systemdesign.one&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Michael Thiessen&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Vue.js development, component design, and frontend architecture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Delivers actionable tips and patterns for building scalable Vue applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://michaelnthiessen.com" rel="noopener noreferrer"&gt;michaelnthiessen.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧑‍💼 B. Engineering Leadership &amp;amp; Management
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;5. Engineering Leadership by Gregor Ojstersek&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Engineering management, career growth, industry insights, career development, and personal journey.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Covers team scaling, stakeholder management, and overcoming imposter syndrome to improve leadership skills for tech leads/EMs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://newsletter.eng-leadership.com/" rel="noopener noreferrer"&gt;newsletter.eng-leadership.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;6. The Pragmatic Engineer by Gergely Orosz&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Software engineering leadership, industry insights, and career development.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Offers deep dives into engineering practices and management strategies from a seasoned leader.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://newsletter.pragmaticengineer.com" rel="noopener noreferrer"&gt;newsletter.pragmaticengineer.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;7. Pointer by Suraj&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Curated articles for engineering leaders, covering management and technical topics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Provides a concise selection of high-quality reads to stay updated on leadership trends.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://pointer.io" rel="noopener noreferrer"&gt;pointer.io&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;8. Refactoring by Luca Rossi&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Engineering management, team dynamics, and productivity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Delivers practical advice on improving team performance and engineering processes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://refactoring.fm" rel="noopener noreferrer"&gt;refactoring.fm&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;9. Manager.dev by Anton&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Resources and insights for engineering managers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Offers tools and articles to enhance management skills and team leadership.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://manager.dev" rel="noopener noreferrer"&gt;manager.dev&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;10. The Caring Techie by Irina Stanescu&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Empathetic leadership and team well-being.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Emphasizes the human aspect of tech leadership, promoting a caring work culture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://thecaringtechie.substack.com" rel="noopener noreferrer"&gt;thecaringtechie.substack.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;11. Engineer’s Codex&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Engineering principles and leadership insights.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Explores the intersection of technical excellence and effective leadership.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://engineercodex.substack.com" rel="noopener noreferrer"&gt;engineercodex.substack.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;12. Thrive Engineering&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Engineering team growth and productivity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Provides strategies to build thriving engineering teams and foster innovation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://thriveengineering.substack.com" rel="noopener noreferrer"&gt;thriveengineering.substack.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📈 C. Product, Growth &amp;amp; Career Development
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;13. Lenny's Newsletter by Lenny Rachitsky&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Product management, growth strategies, and career advice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Offers actionable insights from industry experts to accelerate your career.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://www.lennyrachitsky.com" rel="noopener noreferrer"&gt;lennyrachitsky.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;14. Sarah Tavel's Newsletter&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Investment perspectives and product-market fit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Provides valuable insights into building products that resonate with users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://sarahtavel.substack.com" rel="noopener noreferrer"&gt;sarahtavel.substack.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;15. One Useful Thing by Ethan Mollick&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Practical applications of research in work and life.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Translates academic findings into actionable advice for professionals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://oneusefulthing.substack.com" rel="noopener noreferrer"&gt;oneusefulthing.substack.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;16. Nathan Baugh from World Builders&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Storytelling and content creation strategies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Enhances your ability to communicate and build a personal brand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://nathanbaugh.substack.com" rel="noopener noreferrer"&gt;nathanbaugh.substack.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;17. The Developing Dev by Ryan Peterman&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Weekly insights to accelerate your software engineering career, written by a Staff Software Engineer at Instagram.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Offers actionable advice and mentorship for engineers aiming to progress from junior to senior roles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://read.developing.dev/" rel="noopener noreferrer"&gt;read.developing.dev&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧪 D. Data, AI &amp;amp; Analytics
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;18. Chartr&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Data storytelling and visualizations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Simplifies complex data into engaging visuals, aiding in data literacy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://chartr.co" rel="noopener noreferrer"&gt;chartr.co&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;19. SeattleDataGuy&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Data engineering and analytics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Shares practical advice and industry trends in data engineering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://seattledataguy.substack.com" rel="noopener noreferrer"&gt;seattledataguy.substack.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;20. DataEngineer.io by Zach Wilson&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Data engineering education and career development.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Offers courses and resources to advance in data engineering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://dataengineer.io" rel="noopener noreferrer"&gt;dataengineer.io&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧰 E. Frontend &amp;amp; Web Development
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;21. Addy Osmani from Elevate&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Web performance and frontend engineering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Provides deep dives into optimizing web applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://addyosmani.com" rel="noopener noreferrer"&gt;addyosmani.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;22. Hybrid Hacker&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Full-stack development and tech tutorials.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Shares hands-on guides and coding tips for developers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://hybridhacker.substack.com" rel="noopener noreferrer"&gt;hybridhacker.substack.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;23. Tech World With Milan by Dr. Milan Milanovic&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Technology trends and software development.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Explores emerging technologies and their applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://techworldwithmilan.substack.com" rel="noopener noreferrer"&gt;techworldwithmilan.substack.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧑‍💻 F. Coding Challenges &amp;amp; Skill Development
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;24. Coding Challenges by John Crickett&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Programming challenges and problem-solving.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Enhances coding skills through regular challenges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://codingchallenges.substack.com" rel="noopener noreferrer"&gt;codingchallenges.substack.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;25. Developing Skills by John Crickett&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Career growth and skill enhancement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Provides strategies for continuous professional development.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://developingskills.substack.com" rel="noopener noreferrer"&gt;developingskills.substack.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;26. Craft Better Software by Daniel Moka&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Software craftsmanship and best practices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Offers insights into writing clean and maintainable code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://danielmoka.substack.com" rel="noopener noreferrer"&gt;danielmoka.substack.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;27. Biweekly Engineering&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Engineering updates and industry news.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Keeps you informed about the latest in software engineering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://biweekly-engineering.beehiiv.com" rel="noopener noreferrer"&gt;biweekly-engineering.beehiiv.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;28. Level Up Coding by Trey Huffine&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Simplifying complex programming concepts, system design, and AI through engaging visuals and tutorials.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Ideal for developers seeking to deepen their understanding of coding and system design with accessible content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://levelupcoding.substack.com]" rel="noopener noreferrer"&gt;levelupcoding.substack.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📰 G. General Tech &amp;amp; Industry News
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;29. The Engineer Newsletter&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Cutting-edge engineering trends (semiconductors, space tech, clean energy) and industry breakthroughs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Quick updates on tech milestones (e.g., Intel’s 18A chips, SpaceX Starship launches) and premium deep dives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://theengineernews.substack.com/" rel="noopener noreferrer"&gt;theengineernews.substack.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;30. The Medium Newsletter&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus&lt;/strong&gt;: Curated articles across various topics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Read&lt;/strong&gt;: Discover diverse perspectives and stories from the Medium community.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt;: &lt;a href="https://medium.com/newsletter" rel="noopener noreferrer"&gt;medium.com/newsletter&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;By subscribing to these newsletters, you can stay updated on various aspects of software engineering, from technical skills to leadership and industry trends.&lt;/p&gt;

&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;p&gt;That's all!&lt;/p&gt;

&lt;p&gt;I hope you’ve found the article useful. If you haven’t already, you should read the newsletters. They are full of knowledge and experience. Reading newsletters could be part of your career growth and personal improvement.&lt;/p&gt;

&lt;p&gt;Check more on&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://encryptioner.github.io" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/mir-mursalin-ankur" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Encryptioner" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/AnkurMursalin" rel="noopener noreferrer"&gt;X (Twitter)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nerddevs.com/author/ankur/" rel="noopener noreferrer"&gt;Nerddevs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>career</category>
      <category>softwareengineering</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Apps Script - List all the newsletters filling up your Gmail. Selective unsubscribing got easy</title>
      <dc:creator>Mir Mursalin Ankur</dc:creator>
      <pubDate>Sun, 26 Jan 2025 14:57:46 +0000</pubDate>
      <link>https://forem.com/mir_mursalin_ankur/apps-script-list-all-the-newsletters-filling-up-your-gmail-selective-unsubscribing-got-easy-2gic</link>
      <guid>https://forem.com/mir_mursalin_ankur/apps-script-list-all-the-newsletters-filling-up-your-gmail-selective-unsubscribing-got-easy-2gic</guid>
      <description>

&lt;p&gt;Are you subscribed to many newsletters that are filling up your Gmail? You can clean up all newsletters by filtering Gmail's &lt;code&gt;unsubscribe&lt;/code&gt; text. You may have known that already. And I am not writing to describe that procedure. You are intelligent enough to do that with a little googling.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;I am writing to tell you about a special case that happened to me. I was subscribed to many newsletters I felt interesting. I used to read newsletters almost daily. However, little by little I got overwhelmed with the increasing number of subscribed newsletters.&lt;/p&gt;

&lt;p&gt;So, I decided to filter my subscribed newsletter. I didn't want to delete old newsletters. I just wanted to list my current subscribed newsletters (which are unique). Check them a bit and unsubscribe which feels unnecessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;Turns out, there is no easy way to do it from &lt;code&gt;Gmail&lt;/code&gt;. So, I had to do a little code. Google has a service called &lt;a href="https://developers.google.com/apps-script" rel="noopener noreferrer"&gt;Apps Script&lt;/a&gt;. It lets you run some code. You can fetch and manipulate data from your used Google products. So, you can automate many boring manual tasks. Believe it or not, I had subscriptions to around 100 newsletters from different websites, services, and tech newsletters. I reduced my subscribed newsletters to only those that seemed appropriate.&lt;/p&gt;

&lt;p&gt;So, Here's the instructional summary of what I did:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new &lt;a href="https://workspace.google.com/products/sheets/" rel="noopener noreferrer"&gt;Google Sheet&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Go to &lt;code&gt;Extensions &amp;gt; Apps Script&lt;/code&gt; of my new Google sheet&lt;/li&gt;
&lt;li&gt;Write the necessary code in the code editor of Apps Script which does the following:

&lt;ul&gt;
&lt;li&gt;Get unique mail list senders with other necessary info like name&lt;/li&gt;
&lt;li&gt;Wrote that data in an existing Google sheet&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Save the script and give it a name&lt;/li&gt;
&lt;li&gt;Go to &lt;code&gt;Run &amp;gt; main&lt;/code&gt;, then grant permissions&lt;/li&gt;
&lt;li&gt;The unique email addresses will populate your spreadsheet&lt;/li&gt;
&lt;li&gt;After that, from Gmail you can unsubscribe from unwanted senders&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  My code had the following parts:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; A &lt;code&gt;main&lt;/code&gt; function that calls &lt;code&gt;_getUniqueMailingListSenders&lt;/code&gt; function&lt;/li&gt;
&lt;li&gt; The &lt;code&gt;_getUniqueMailingListSenders&lt;/code&gt; takes the following parameters:

&lt;ul&gt;
&lt;li&gt;applicable filter which will be applied to filter the Gmail&lt;/li&gt;
&lt;li&gt;The column title of the Google sheet&lt;/li&gt;
&lt;li&gt;A boolean parameter &lt;code&gt;clearSheet&lt;/code&gt;, which declares if I want to clear my existing sheet or not&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Inside the &lt;code&gt;_getUniqueMailingListSenders&lt;/code&gt; function, it filters all the emails of my &lt;code&gt;Gmail&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Later, it loops through the filtered emails and adds the &lt;code&gt;From&lt;/code&gt; email to a &lt;code&gt;javascript set&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;from&lt;/code&gt; field in Gmail filter also keeps the name of the email sender&lt;/li&gt;
&lt;li&gt;So, A loop runs on &lt;code&gt;javascript set&lt;/code&gt;, and regular expression gets applied to separate the name and email and save in a map&lt;/li&gt;
&lt;li&gt;This could also be done in an earlier loop, but I kept it in a separate loop for simplicity.&lt;/li&gt;
&lt;li&gt;The result can be checked via &lt;code&gt;Execution log&lt;/code&gt; using the &lt;code&gt;console.log() of javascript&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;However, adding that result in Google sheet is more convenient&lt;/li&gt;
&lt;li&gt;So, the active sheet gets fetched (From where &lt;code&gt;Extensions &amp;gt; Apps Script&lt;/code&gt; was called) and cleared (depending on &lt;code&gt;clearSheet&lt;/code&gt; boolean parameter)&lt;/li&gt;
&lt;li&gt;Then, it finds the last used column and sets the columns for emails and names dynamically after the last used column&lt;/li&gt;
&lt;li&gt;It sets the column title of Google sheet (with current date info for checking later)&lt;/li&gt;
&lt;li&gt;And writes each unique email address and corresponding name to the new columns&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Full Code:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_getUniqueMailingListSenders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;searchOption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;clearSheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// Search in Gmail to check all threads&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;GmailApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchOption&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Use a Set to store unique senders&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;senders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Loop through each thread&lt;/span&gt;
  &lt;span class="nx"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thread&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMessages&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Loop through each message in the thread&lt;/span&gt;
    &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;senders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFrom&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Find email and sender name and save it in a map&lt;/span&gt;
  &lt;span class="nx"&gt;senders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="se"&gt;([^&lt;/span&gt;&lt;span class="sr"&gt;&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;&amp;gt;/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;emailMatch&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;emailMatch&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;emailData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;emailData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Get the active sheet&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveSpreadsheet&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getActiveSheet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clearSheet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Find the last used column&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastColumn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLastColumn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Set the columns for emails and names dynamically after the last used column&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailColumn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lastColumn&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nameColumn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lastColumn&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLocaleDateString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Add headers with the current date in the new columns&lt;/span&gt;
  &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;emailColumn&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; - Email - &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currentDate&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nameColumn&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; - Name - &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currentDate&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Start writing below the header&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Write each unique email address and corresponding name to the new columns&lt;/span&gt;
  &lt;span class="nx"&gt;emailData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Adding data on column (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;emailColumn&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nameColumn&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) and row &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;emailColumn&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nameColumn&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Search for emails that belong to a mailing list&lt;/span&gt;
  &lt;span class="nf"&gt;_getUniqueMailingListSenders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;list:(&amp;lt;*&amp;gt;)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unique Email Sender&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Search for emails that belong to a substack mailing list&lt;/span&gt;
  &lt;span class="c1"&gt;// _getUniqueMailingListSenders(&lt;/span&gt;
  &lt;span class="c1"&gt;//   // 'list:(&amp;lt;*techworldwithmilan@substack.com&amp;gt;)',&lt;/span&gt;
  &lt;span class="c1"&gt;//   'list:(&amp;lt;*.substack.com&amp;gt;)',&lt;/span&gt;
  &lt;span class="c1"&gt;//   'Unique Substack Email Sender',&lt;/span&gt;
  &lt;span class="c1"&gt;//   false,&lt;/span&gt;
  &lt;span class="c1"&gt;// );&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  End
&lt;/h2&gt;

&lt;p&gt;That's all!&lt;/p&gt;

&lt;p&gt;I hope you've found the article useful. You should try to use &lt;code&gt;Apps Script&lt;/code&gt; if you haven't already. It has many interesting use cases. Feel free to share your thoughts and experiences.&lt;/p&gt;

&lt;p&gt;Check more on&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://encryptioner.github.io" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/mir-mursalin-ankur" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Encryptioner" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/AnkurMursalin" rel="noopener noreferrer"&gt;X (Twitter)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nerddevs.com/author/ankur/" rel="noopener noreferrer"&gt;Nerddevs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>automaton</category>
      <category>javascript</category>
      <category>google</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
