graph-scope
Skill2 starsChanged 3 months agoMITView on GitHub
Cross-cutting skill for ACF. Decomposes a project into a dependency graph so the agent loads context only for the affected subgraph, not the entire project. Builds the graph using grep/find (no external dependencies), stores it as a markdown file under .slim/acf/, and provides query patterns for blast-radius (forward) and context-tracing (backward). Use before context-load to narrow scope, when the user asks "what's affected by changing X", "map the dependencies", "scope this change", or when ACF detects the project is large enough that full context-load would waste tokens.
In this repository10 other files it gives its agents.
- AGENTS.mdAGENTS.md
- context-loadSkill
- stack-auditSkill
- issue-craftSkill
- pr-contextSkill
- frontend-previewSkill
- label-metadataSkill
- compactionSkill
- cavemanSkill
- acfSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
25 rules. About 0 are common, 16 are distinctive, and 9 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈16
Distinctive · estimate
This author's own choices. Worth a look.
9
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 29
Before context-load, when the project has more than ~20 source files
- Distinctive: line 30
When the user asks "what's affected by changing X?"
- Distinctive: line 31
When the user asks to "map the dependencies" or "build the graph"
- Distinctive: line 32
When the user asks to "scope this change" or "narrow the context"
- Distinctive: line 33
When ACF detects a large project where full context-load would waste tokens
- Distinctive: line 34
When the user explicitly requests "full scope" (skip graph-scope, load everything)
- Distinctive: line 55
Compute a content hash: after each build — hash file mtimes and file count:
- Distinctive: line 66
If hashes match → reuse the cached graph (skip rebuild)
- Distinctive: line 67
If hashes differ → rebuild the graph and update the hash
- Very specific: line 196
After building the adjacency list, run a cycle detection pass (DFS with a visited stack).
- Distinctive: line 218
When computing the forward (blast radius) traversal, skip cyclic edges to prevent infinite loops:
- Very specific: line 222
If a node is already in visited, skip it (do not recurse further)
- Very specific: line 232
When a change is requested (e.g., "fix the flashlight bug in EntityController.luau"), graph-scope computes the affected subgraph:
- Very specific: line 270
Read their exports/types/interfaces
- Distinctive: line 275
After computing both traversals, graph-scope writes a directive to the progress file:
- Very specific: line 331
Check if .slim/acf/graph.json exists and is fresh
- Distinctive: line 332
If not, run graph-scope build phase
- Distinctive: line 333
Run graph-scope query phase with the change target
- Distinctive: line 379
When the token budget is tight:
- Distinctive: line 387
After graph-scope runs, the progress file contains:
- Very specific: line 404
.slim/acf/directory doesn't exist: create it before writing graph files. - Very specific: line 411
ghnot authenticated: not applicable to graph-scope. - Distinctive: line 412
GitHub API rate limits: not applicable to graph-scope.
- Very specific: line 414
If .slim/acf/ doesn't exist, create it.
- Very specific: line 416
Commands fail: if grep or find fails (permissions, binary files), skip the affected files and note them: "[file]: scan error — skipped." Do …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Graph-Scope
Decomposes a project into a dependency graph so the agent navigates only the relevant subgraph for each change. Instead of reading the entire project (default context-load behavior), graph-scope identifies which files matter for the current change and limits context-load to those files.
This is the difference between a flashlight (context-load: read all MDs) and a laser (graph-scope: read only what the change touches).
When to Trigger
- Before context-load, when the project has more than ~20 source files
- When the user asks "what's affected by changing X?"
- When the user asks to "map the dependencies" or "build the graph"
- When the user asks to "scope this change" or "narrow the context"
- When ACF detects a large project where full context-load would waste tokens
- When the user explicitly requests "full scope" (skip graph-scope, load everything)
What It Produces
Two files under .slim/acf/:
graph.md— human/agent-readable graph summary (hubs, leaves, layers)graph.json— machine-readable adjacency list (for programmatic traversal by the agent)
The graph is rebuilt when:
- The project is scanned for the first time
- The user asks to "rebuild the graph"
- Files have been added/removed since the last build (check with
git status)
Cache Invalidation
To avoid rebuilding the graph on every run, graph-scope caches the graph and rebuilds only when the project has changed:
- Compute a content hash after each build — hash file mtimes and file
count:
# Hash: file count + sum of mtimes for all source files find . -type f \( -name "*.luau" -o -name "*.lua" -o -name "*.py" \ -o -name "*.ts" -o -name "*.js" -o -name "*.go" -o -name "*.rs" \) \ -not -path './.git/*' -not -path './node_modules/*' \ -exec stat -c '%Y' {} \; | sort | sha256sum - Store the hash in
.slim/acf/graph.jsonunder a"content_hash"field. - On next run, recompute the hash and compare:
- If hashes match → reuse the cached graph (skip rebuild)
- If hashes differ → rebuild the graph and update the hash
- When to rebuild (hash changes):
- Files added or removed (file count changes)
- Files modified (mtime changes)
- User explicitly requests "rebuild the graph"
.slim/acf/graph.jsonis missing or corrupted
Build Phase: Constructing the Graph
Step 1: Detect language(s)
Identify the project's primary language(s) by file extension frequency:
find . -type f -not -path './.git/*' -not -path './node_modules/*' \
| sed 's/.*\.//' | sort | uniq -c | sort -rn | head -10Map extensions to import patterns:
| Language | Extension(s) | Import pattern (regex) | |
| Luau | .luau | require\(.*\) | |
| Lua | .lua | require\(.*\) | |
| Python | .py | ^import |^from .+ import | |
| JavaScript | .js, .jsx | `require( | ^import .+ from` |
| TypeScript | .ts, .tsx | `^import .+ from | require(` |
| Go | .go | ^import \( | |
| Rust | .rs | ^use | |
| Java | .java | ^import | |
| C# | .cs | ^using | |
| C/C++ | .c, .cpp, .h | ^#include |
Step 2: Extract imports per file
For each source file, extract its imports using grep:
# Example: Luau
grep -rEn "require\(" --include="*.luau" . \
| sed 's/:\s*require(\(.*\))/\1/' \
| sort -uThe agent should adapt the pattern to the detected language(s). For multi-language projects, run each pattern and merge results.
Step 3: Resolve import paths
Map import strings to actual file paths. This is language-specific:
- Luau/Lua:
require(game:GetService("ReplicatedStorage").Packages.Knit)→src/shared/+ module name +.luau - Python:
from src.shared.constants import X→src/shared/constants.py - JS/TS:
import X from './utils'→./utils.tsor./utils/index.ts - Go:
import "github.com/user/repo/pkg"→pkg/directory
The agent resolves paths using find and grep — no external parser needed.
Step 4: Build adjacency list
For each file, record:
id: the file path (relative to project root)imports: list of file paths this file imports (resolved in step 3)imported_by: list of file paths that import this file (reverse edges)
Write to .slim/acf/graph.json:
{
"version": 1,
"project": "project-name",
"languages": ["luau"],
"built_at": "2026-08-04T12:00:00Z",
"content_hash": "a1b2c3d4...",
"node_count": 53,
"edge_count": 127,
"cycles": ["src/server/Services/A.luau ↔ src/server/Services/B.luau"],
"nodes": {
"src/shared/Constants.luau": {
"imports": [],
"imported_by": [
"src/server/Services/CycleManager.luau",
"src/server/Services/EconomyService.luau"
]
}
}
}Step 5: Build human-readable summary
Write to .slim/acf/graph.md:
# Dependency Graph
## Stats
- Nodes: 53
- Edges: 127
- Languages: luau
- Built: 2026-08-04
## Hub nodes (most depended-on, top 10)
1. src/shared/Constants.luau (26 dependents)
2. src/shared/Types.luau (22 dependents)
3. src/shared/Utils.luau (18 dependents)
...
## Leaf nodes (no dependents — safe to change without blast radius)
- src/client/Controllers/UIController.luau
...
## Isolated nodes (no imports, no dependents)
- scripts/test_core.luau
...
## Layers (by dependency depth)
- Layer 0 (no imports): Constants, Types, Utils
- Layer 1 (imports Layer 0): Services, SignalRefs
- Layer 2 (imports Layer 1): ControllersCircular Dependency Handling
During graph build, detect circular dependencies (import cycles):
Detection
After building the adjacency list, run a cycle detection pass (DFS with a
visited stack). For each node, traverse its imports edges recursively. If a
node is revisited on the current DFS path, a cycle exists.
A → B → C → A (cycle detected)Reporting
- Report cycles as warnings, not errors — do not crash the graph build
- Record detected cycles in
graph.jsonunder a"cycles"field (list of cycle descriptions) - Record cycles in
graph.mdunder a## Circular Dependenciessection:## Circular Dependencies (warnings) - src/server/Services/A.luau ↔ src/server/Services/B.luau (mutual import) - src/client/Controllers/X.luau → Y.luau → Z.luau → X.luau (3-node cycle)
Blast-radius traversal
When computing the forward (blast radius) traversal, skip cyclic edges to prevent infinite loops:
- Maintain a
visitedset during traversal - If a node is already in
visited, skip it (do not recurse further) - Note in the scoped directive if cycles were encountered and skipped:
Cycles skipped in blast-radius: A↔B (mutual import)
This ensures the blast-radius computation always terminates, even with cycles.
Query Phase: Scoping a Change
When a change is requested (e.g., "fix the flashlight bug in
EntityController.luau"), graph-scope computes the affected subgraph:
Forward traversal (blast radius — what might break)
Starting from the changed file, traverse imported_by edges outward:
Changed: src/server/Services/EntityController.luau
→ imported_by: src/server/Services/CycleManager.luau
→ imported_by: src/server/init.luau
→ imported_by: src/client/Controllers/EntityController.luau
→ imported_by: src/client/init.luauResult: the blast radius is [CycleManager, init.luau (server), EntityController (client), init.luau (client)].
The agent should:
- List all files in the blast radius
- Flag any hub nodes in the radius (high-risk changes)
- Flag any test files in the radius (must re-run those tests)
Backward traversal (context — what to load)
Starting from the changed file, traverse imports edges inward:
Changed: src/server/Services/EntityController.luau
→ imports: src/shared/Constants.luau
→ imports: src/shared/Types.luau
→ imports: src/shared/SignalRefs.luau
→ imports: src/shared/Utils.luauResult: the context scope is [Constants, Types, SignalRefs, Utils].
The agent should:
- Load context for these files (via context-load, but scoped)
- Read their exports/types/interfaces
- Skip files not in this subgraph
Output: Scoped Context Directive
After computing both traversals, graph-scope writes a directive to the progress file:
## Graph Scope
Change target: src/server/Services/EntityController.luau
Blast radius (forward, 3 hops):
- src/server/Services/CycleManager.luau
- src/client/Controllers/EntityController.luau
- src/server/init.luau
- src/client/init.luau
Context scope (backward, full depth):
- src/shared/Constants.luau
- src/shared/Types.luau
- src/shared/SignalRefs.luau
- src/shared/Utils.luau
Test files in scope:
- scripts/test_services.luau (covers EntityController)
- scripts/test_controllers.luau (covers EntityController client)
Hub nodes in blast radius: none (all leaves/low-degree)
Scope mode: scoped (9 files of 53 total — 83% reduction)Scope Modes
| Mode | Behavior | When to use |
scoped | Load context only for the affected subgraph | Default, large projects |
full | Load context for the entire project | Small projects, or when user says "load everything" |
manual | User specifies which files to scope to | When the user knows the exact scope |
The orchestrator selects the mode:
- Default:
scopedif project has >20 source files,fullotherwise - Override: user can say "full scope" or "scope to [file list]"
Integration with ACF Pipeline
Graph-scope runs before context-load when in scoped mode:
graph-scope → context-load (scoped) → stack-audit → issue-craft → ...In full mode, graph-scope is skipped and context-load reads everything:
context-load (full) → stack-audit → issue-craft → ...The orchestrator should:
- Check if
.slim/acf/graph.jsonexists and is fresh - If not, run graph-scope build phase
- Run graph-scope query phase with the change target
- Pass the scoped file list to context-load
- Context-load reads only those files + all project MDs (MDs are always loaded — they contain conventions and AC patterns)
What Graph-Scope Does NOT Do
- Static type analysis — it traces imports, not type flows. For type-level impact, use a language-specific tool (tsc, mypy, luau-analyze).
- Runtime dependency detection — it scans source code, not runtime behavior. Dynamic imports, reflection, or dependency injection are invisible.
- Replace tests — it identifies which tests to run, but doesn't run them. The agent should use the test list from graph-scope to run the right tests.
- Require external tools — no tree-sitter, no AST parser, no Python. The agent uses grep, find, and its own language understanding to build the graph.
Comparison to Existing Tools
| Tool | Approach | Dependencies | ACF integration | Compaction |
| change-impact-analysis-skill | AST + BFS | Python, PyYAML | None | No |
| project-understanding (evil-skills) | Tree-sitter | Node, tree-sitter | None | Token budgeting |
| codespaces (diskd-ai) | Tree-sitter, belief-map | Python, tree-sitter | None | No |
| Hawkeye | AST + graph | Python | None | Compact JSON |
| Constrictor | AST | Python | None | No |
| ACF graph-scope | grep + agent reasoning | None | Yes (pipeline) | Yes (phase 7/8) |
ACF graph-scope is the only one that:
- Requires zero external dependencies (agent uses grep/find)
- Integrates with an issue/PR crafting pipeline
- Can be compacted by phase 7 and caveman phase 8
- Works on any language the agent understands (not limited to Python/TS)
- Produces agent-readable markdown (not just JSON for tools)
The trade-off: ACF graph-scope is less precise than AST-based tools (it uses regex, not parsing). For projects that need type-level analysis, use an AST tool alongside ACF. For projects that need fast scope narrowing without dependencies, ACF graph-scope is sufficient.
Token Budget
The graph itself is compact:
graph.md: ~200-500 tokens (summary, top hubs, layers)graph.json: ~500-2000 tokens (full adjacency list)- Scoped directive: ~50-200 tokens (just the affected subgraph)
When the token budget is tight:
- Phase 7 (compaction) compresses the graph to hub/leaf counts + the scoped directive only
- Phase 8 (caveman) compresses to
SCOPE: [file list] | BLAST: [count] | TESTS: [file list]
Output
After graph-scope runs, the progress file contains:
- The scoped file list (context scope)
- The blast radius (forward traversal)
- The test files in scope
- The scope mode used
- The reduction percentage (files scoped vs total)
Empty State Handling
- Project has <5 files: graph-scope is not useful for tiny projects. Skip
graph-scope entirely and use
fullmode for context-load. Report "Project has [N] files — too small for graph-scope. Using full context-load." - Project has no source files (only docs/config): report "No source files detected — graph-scope skipped. Context-load will read MDs only."
- No imports detected (all files are isolated): build the graph with all nodes as isolated. Report "No import relationships found — all files are isolated. Blast radius is limited to the changed file only."
.slim/acf/directory doesn't exist: create it before writing graph files. This is expected on first run.
Error Handling
ghCLI not installed: graph-scope does not requiregh— it usesgrep,find, andstatonly. Proceed normally.ghnot authenticated: not applicable to graph-scope. Proceed normally.- GitHub API rate limits: not applicable to graph-scope. Proceed normally.
- Required files/directories don't exist: see Empty State Handling above.
If
.slim/acf/doesn't exist, create it. If no source files are found, skip graph-scope. - Commands fail: if
greporfindfails (permissions, binary files), skip the affected files and note them: "[file]: scan error — skipped." Do not abort the graph build. Ifgraph.jsonis corrupted, delete it and rebuild from scratch.
Example
graph.json (excerpt)
{
"version": 1,
"project": "flashlight-game",
"languages": ["luau"],
"built_at": "2026-08-04T12:00:00Z",
"content_hash": "a1b2c3d4e5f6...",
"node_count": 53,
"edge_count": 127,
"cycles": [],
"nodes": {
"src/shared/Constants.luau": {
"imports": [],
"imported_by": [
"src/server/Services/CycleManager.luau",
"src/server/Services/EconomyService.luau",
"src/client/Controllers/ToolController.luau"
]
},
"src/server/Services/EntityController.luau": {
"imports": [
"src/shared/Constants.luau",
"src/shared/Types.luau",
"src/shared/SignalRefs.luau"
],
"imported_by": [
"src/server/Services/CycleManager.luau",
"src/client/Controllers/EntityController.luau"
]
}
}
}graph.md (excerpt)
# Dependency Graph
## Stats
- Nodes: 53
- Edges: 127
- Languages: luau
- Built: 2026-08-04
- Cycles: none
## Hub nodes (most depended-on, top 10)
1. src/shared/Constants.luau (26 dependents)
2. src/shared/Types.luau (22 dependents)
3. src/shared/Utils.luau (18 dependents)
## Leaf nodes (no dependents — safe to change without blast radius)
- src/client/Controllers/UIController.luau
- src/client/Controllers/ToolController.luau
## Layers (by dependency depth)
- Layer 0 (no imports): Constants, Types, Utils
- Layer 1 (imports Layer 0): Services, SignalRefs
- Layer 2 (imports Layer 1): ControllersDoes it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

