skills/forge-graph/SKILL.md
[writes] Manage the Neo4j knowledge graph. Subcommands: init, rebuild (writes); status, query <cypher>, debug (read-only). Requires Docker. No default — an explicit subcommand is required. Use when setting up the graph for the first time, rebuilding after major refactors, checking graph health, or running ad-hoc Cypher diagnostics.
npx skillsauth add quantumbitcz/dev-pipeline forge-graphInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
One skill, five subcommands. Each subcommand preserves the behavior of the corresponding Phase 1 skill verbatim.
Follow shared/skill-subcommand-pattern.md. This skill uses positional subcommands, NOT flags.
Dispatch rules:
$ARGUMENTS.SUB="$1"; shift; REST="$*".$SUB is empty OR matches -* (bare invocation or flags-only): print the usage block and exit 2 (No subcommand provided. Valid: init | status | query | rebuild | debug.).$SUB == --help OR $SUB == help: print usage and exit 0.$SUB is in {init, status, query, rebuild, debug}: dispatch to the matching ### Subcommand: <SUB> section with $REST as its arguments.Unknown subcommand '<SUB>'. Valid: init | status | query | rebuild | debug. Try /forge-graph --help. and exit 2.No default subcommand. This is intentional — rebuild is destructive, so a bare /forge-graph must not silently rebuild.
init, rebuild)status, debug)Subcommand-specific flags are documented under each subcommand section.
See shared/skill-contract.md §3.
Before any subcommand:
.claude/forge.local.md exists. If not: "Pipeline not initialized. Run /forge-init first." STOP.graph.enabled: true in forge.local.md. If false/absent: "Graph integration is disabled. Set graph.enabled: true to use this feature." STOP.docker info. If fails: "Docker is not available. Cannot run graph operations." STOP.Read graph.neo4j_container_name from .claude/forge.local.md. If not set, default: forge-neo4j. Use the resolved name in ALL docker commands below.
You are the graph initializer. Your job is to start the Neo4j container, import the plugin seed data, and build the project codebase graph. Be idempotent — detect what is already done and skip those steps.
Check that .claude/forge.local.md exists in the project root.
/forge-init first." Abort.Read .claude/forge.local.md and check graph.enabled.
graph.enabled: false or the graph: section is absent: inform the user — "Graph integration is disabled in forge.local.md. Set graph.enabled: true to use this feature." Exit.Check Docker availability: docker info
.forge/state.json integrations: "neo4j": {"available": false}Copy the Docker Compose template to the pipeline working directory:
cp "${CLAUDE_PLUGIN_ROOT}/shared/graph/docker-compose.neo4j.yml" .forge/docker-compose.neo4j.yml
Substitute port variables from config (read graph.neo4j_port and graph.neo4j_bolt_port from forge.local.md, defaulting to 7474 and 7687 respectively). Edit the copied file to replace placeholder values with the resolved ports.
Check if the container is already running:
docker ps --filter "name=forge-neo4j" --format "{{.Names}}"
forge-neo4j appears in output: skip this step — container is already running.docker image inspect neo4j:5-community >/dev/null 2>&1
docker pull neo4j:5-community
docker compose -f .forge/docker-compose.neo4j.yml up -d
Important: The image tag neo4j:5-community uses a major-version floating tag, which always resolves to the latest 5.x release. This is intentional — Neo4j 5.x is backward-compatible within the major version. Do NOT pin to a specific patch version as it would require manual updates.
Poll the health check script until Neo4j is ready, up to 60 seconds:
"${CLAUDE_PLUGIN_ROOT}/shared/graph/neo4j-health.sh"
Run this in a loop (every 3 seconds) until it exits 0 or 60 seconds have elapsed.
docker logs forge-neo4j" Abort.Check for the seed marker node to determine if the seed has already been imported:
echo "MATCH (n:_SeedMarker {id: 'forge-seed-v2'}) RETURN count(n)" | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local --format plain
cat "${CLAUDE_PLUGIN_ROOT}/shared/graph/seed.cypher" | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local
Check .forge/graph/.last-build-sha — if it exists and matches the current git rev-parse HEAD, the graph is already up to date for this commit; skip rebuild and note this to the user.
After container startup, derive project_id:
PROJECT_ID=$(derive_project_id "$PROJECT_ROOT")
Pass to build-project-graph.sh:
./shared/graph/build-project-graph.sh --project-root "$PROJECT_ROOT" --project-id "$PROJECT_ID"
For monorepo with components, iterate each component:
for component in $(read_components "$PROJECT_ROOT"); do
./shared/graph/build-project-graph.sh --project-root "$PROJECT_ROOT" --project-id "$PROJECT_ID" --component "$component"
done
Otherwise, build the project graph:
"${CLAUDE_PLUGIN_ROOT}/shared/graph/build-project-graph.sh" --project-root . | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local
After success, write the current commit SHA to .forge/graph/.last-build-sha.
Create .forge/graph/ directory if it does not exist.
Update .forge/state.json integrations block:
"neo4j": {
"available": true
}
If .forge/state.json does not exist or has no integrations key, create/add the key. Do not overwrite unrelated fields.
Query and display node counts:
echo "MATCH (n) RETURN labels(n)[0] AS label, count(*) AS count ORDER BY count DESC" | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local --format plain
Present a summary:
Graph initialized successfully.
Container: forge-neo4j (running)
Seed: imported
Build SHA: <sha>
Node counts:
ProjectFile 142
ProjectClass 38
ProjectFunction 215
...
Run /forge-graph query to explore the graph.
Run /forge-graph status for health and coverage details.
Note any steps that were skipped (idempotency).
Key behavior preserved:
.forge/graph/.last-build-sha on success..forge/state.json.integrations.neo4j.available = true.neo4j:5-community if image not present locally.You are the graph status reporter. Your job is to display the current state of the Neo4j knowledge graph: container health, node and relationship counts, last build SHA, and enrichment coverage.
Read-only. Honors --json flag per skill-contract §2.
git rev-parse --show-toplevel 2>/dev/null. If fails: report "Not a git repository. Navigate to a project directory." and STOP./forge-graph init first." and STOP.Run the health check script:
"${CLAUDE_PLUGIN_ROOT}/shared/graph/neo4j-health.sh"
Also check the container's running state:
docker ps --filter "name=forge-neo4j" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
If Docker itself is unavailable, report: "Docker is not available. Cannot check graph status."
Show node counts grouped by project:
MATCH (n) WHERE n.project_id IS NOT NULL
RETURN n.project_id, labels(n)[0] AS label, count(n) AS count
ORDER BY n.project_id, label
If Neo4j is healthy, query node counts by label:
echo "MATCH (n) RETURN labels(n)[0] AS label, count(*) AS count ORDER BY count DESC" | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local --format plain
Display all results in a table.
Read .forge/graph/.last-build-sha and display its contents.
git rev-parse HEAD and indicate whether the graph is up to date or stale (HEAD has moved since last build).Read .forge/graph/.enriched-files if it exists.
git ls-files | wc -l).If Neo4j is healthy, query relationship counts:
echo "MATCH ()-[r]->() RETURN type(r) AS type, count(*) AS count ORDER BY count DESC" | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local --format plain
Display all results in a table.
Present a consolidated status summary:
Knowledge Graph Status
Container: HEALTHY (forge-neo4j)
Ports: 7474 (HTTP), 7687 (Bolt)
Last build: abc1234 (up to date)
Node counts:
ProjectFile 142
ProjectClass 38
ProjectFunction 215
ProjectPackage 12
ProjectDependency 27
_SeedMarker 1
Relationship counts:
CONTAINS 180
CALLS 94
IMPORTS 61
DEPENDS_ON 27
Enrichment coverage: 89/142 files (63%)
Run /forge-graph init to rebuild if stale.
Run /forge-graph query <cypher> to explore.
If Neo4j is unavailable, show what can be determined from local files (last build SHA, enriched files) and suggest running /forge-graph init.
You are the graph query executor. Your job is to accept a Cypher query (everything after query on the command line), validate that the graph is available, execute the query, and display formatted results.
Takes the Cypher query as a positional argument. If no argument: prompts the user. Read-only.
git rev-parse --show-toplevel 2>/dev/null. If fails: report "Not a git repository. Navigate to a project directory." and STOP./forge-graph init first." and STOP.Run the health check script:
"${CLAUDE_PLUGIN_ROOT}/shared/graph/neo4j-health.sh"
/forge-graph init to start the graph." Abort.Inject project_id automatically into all queries:
PROJECT_ID=$(derive_project_id "$PROJECT_ROOT")
User can override by specifying their own :param project_id in the query, or omit project_id for cross-project queries.
Accept the Cypher query from the skill argument (the text following query on the command line).
Store the query in $QUERY.
Run the query against Neo4j:
echo "$QUERY" | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local --format plain
Capture both stdout and stderr.
Present the raw output from cypher-shell. If the output is empty (no rows returned), show: "Query returned no results."
Also show the query that was executed, so the user can reference or modify it:
Query:
MATCH (n:ProjectClass) RETURN n.name LIMIT 10
Results:
n.name
------
UserService
OrderRepository
PaymentGateway
...
(3 rows)
If the output is large (more than 50 rows), truncate display to 50 rows and note: "Showing first 50 of N rows. Add a LIMIT clause to restrict results."
After displaying results, offer useful next steps based on the query type:
MATCH ... RETURN with no LIMIT: suggest adding LIMIT for large graphs.MATCH (n) RETURN DISTINCT labels(n)./forge-graph status to see all available labels and relationship types.You are the graph rebuilder. Your job is to wipe all project-derived nodes from the knowledge graph and rebuild them from the current codebase. The plugin seed graph (framework conventions, patterns, rules) is preserved.
Honors --component <name>, --clear-enrichment, and --dry-run flags. Uses AskUserQuestion for the confirmation step. Destructive — deletes project-scoped nodes (preserves plugin seed).
git rev-parse --is-inside-work-tree. If not: report "Not a git repository." and STOP./forge-graph init to start the graph first." and STOP.Run git rev-parse --is-inside-work-tree. If not a git repository: ERROR — "Not a git repository." Abort.
Run the health check script:
"${CLAUDE_PLUGIN_ROOT}/shared/graph/neo4j-health.sh"
/forge-graph init to start the graph first." Abort.Inform the user what will happen:
"This will delete all project nodes (ProjectFile, ProjectClass, ProjectFunction, ProjectPackage, ProjectDependency) and rebuild them from the current codebase. The plugin seed graph will not be affected. Bugfix enrichment data (bug_fix_count, last_bug_fix_date) is preserved by default."
Use AskUserQuestion to confirm:
Accept optional --component <name> argument:
--component: rebuild all components for current project--component api: rebuild only the api componentDeletion is always scoped to current project — never touches other projects' nodes.
By default, ProjectFile enrichment properties (bug_fix_count, last_bug_fix_date) are preserved across rebuilds. The deletion step saves enrichment data before deleting, and the rebuild step restores it.
Accept optional --clear-enrichment flag to wipe all enrichment data. Useful when enrichment is stale or after significant codebase restructuring.
Derive the project_id for scoping all queries:
PROJECT_ID=$(git remote get-url origin 2>/dev/null | sed 's|.*github.com[:/]||; s|\.git$||')
# Fallback for repos without a remote:
[ -z "$PROJECT_ID" ] && PROJECT_ID=$(basename "$(git rev-parse --show-toplevel)")
All Cypher queries in this step MUST include n.project_id = '$PROJECT_ID' to avoid affecting other projects sharing the same Neo4j instance.
--clear-enrichment)echo "MATCH (n:ProjectFile {project_id: '$PROJECT_ID'}) WHERE n.bug_fix_count > 0 RETURN n.path AS path, n.bug_fix_count AS count, n.last_bug_fix_date AS date" | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local --format csv > /tmp/forge-enrichment-backup.csv
Delete project-derived nodes scoped to current project only:
echo "MATCH (n) WHERE (n:ProjectFile OR n:ProjectClass OR n:ProjectFunction OR n:ProjectPackage OR n:ProjectDependency) AND n.project_id = '$PROJECT_ID' DETACH DELETE n" | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local
/forge-graph init to fully reinitialize.Deleted N nodes, deleted M relationships).Also clear the stale build marker so the next step always runs:
rm -f .forge/graph/.last-build-sha
Re-run the build script and pipe output to Neo4j:
"${CLAUDE_PLUGIN_ROOT}/shared/graph/build-project-graph.sh" --project-root . | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local
build-project-graph.sh is executable and that the project root is correct..forge/graph/.last-build-sha:git rev-parse HEAD > .forge/graph/.last-build-sha
--clear-enrichment)If enrichment data was saved in Step 3a and the backup file is non-empty, restore it:
# Parse the CSV and apply enrichment via MERGE
while IFS=',' read -r path count date; do
[ -z "$path" ] && continue
# Escape single quotes in path to prevent Cypher injection
safe_path=$(printf '%s' "$path" | sed "s/'/''/g")
echo "MATCH (n:ProjectFile {project_id: '$PROJECT_ID', path: '$safe_path'}) SET n.bug_fix_count = $count, n.last_bug_fix_date = '$date';"
done < /tmp/forge-enrichment-backup.csv | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local
rm -f /tmp/forge-enrichment-backup.csvQuery and display the updated node counts:
echo "MATCH (n) RETURN labels(n)[0] AS label, count(*) AS count ORDER BY count DESC" | \
docker exec -i forge-neo4j cypher-shell -u neo4j -p forge-local --format plain
Present a summary:
Graph rebuilt successfully.
Deleted: 142 project nodes, 350 relationships
Build SHA: <sha>
Node counts after rebuild:
ProjectFile 138
ProjectClass 41
ProjectFunction 228
ProjectPackage 13
ProjectDependency 27
_SeedMarker 1 (seed preserved)
Run /forge-graph status for enrichment coverage details.
Run /forge-graph query to explore the graph.
If any step failed partway through, clearly indicate the graph may be in an inconsistent state and suggest running /forge-graph init to fully reinitialize.
Targeted diagnostic skill for the Neo4j knowledge graph. Provides structured diagnostic recipes without requiring raw Cypher knowledge.
Read-only. Enforces LIMIT 50 on every query. All queries scoped to project_id.
git rev-parse --show-toplevel 2>/dev/null. If fails: report "Not a git repository. Navigate to a project directory." and STOP.shared/graph/neo4j-health.sh. If unhealthy: report "Neo4j is not available. Run /forge-graph init first." and STOP./forge-graph init to build the project graph." and STOP.Nodes with no relationships (potential data quality issue):
MATCH (n {project_id: $project_id})
WHERE NOT (n)--()
RETURN labels(n) AS type, count(n) AS count
LIMIT 50
Nodes not updated since the current HEAD:
MATCH (n {project_id: $project_id})
WHERE n.last_updated_sha <> $current_sha
RETURN labels(n)[0] AS type, n.name AS name, n.last_updated_sha AS stale_sha
LIMIT 50
Expected enrichment properties absent on node types:
MATCH (n:Function {project_id: $project_id})
WHERE n.complexity IS NULL OR n.test_coverage IS NULL
RETURN n.name AS function, n.file_path AS file
LIMIT 50
Check for expected relationship types:
MATCH (n {project_id: $project_id})
WHERE NOT (n)-[:DEFINED_IN]->()
RETURN labels(n)[0] AS type, n.name AS name
LIMIT 50
Quick health overview by label:
MATCH (n {project_id: $project_id})
RETURN labels(n)[0] AS label, count(n) AS count
ORDER BY count DESC
LIMIT 50
shared/graph/neo4j-health.sh/forge-graph init or Docker troubleshootingproject_id from git remote origin URL/forge-graph rebuild for widespread staleness, manual fixes for isolated issuesInherits the error-handling tables from each of the five Phase-1 source skills. Consolidated matrix:
| Condition | Action |
|---|---|
| Shared prerequisites fail | Report specific error and STOP |
| Docker image pull fails (init) | "Failed to pull Neo4j image. Check internet + Docker Hub access." STOP |
| Neo4j health timeout (60s) | "Neo4j did not become healthy within 60 seconds. Check docker logs forge-neo4j." STOP |
| Container not running (status/query/rebuild/debug) | "Neo4j not running. Run /forge-graph init first." STOP (or show local file data for status) |
| Seed import fails (init) | "Container is running but seed is missing. Retry /forge-graph init." |
| Query returns no results (query) | "Query returned no results. Check labels with MATCH (n) RETURN DISTINCT labels(n)." |
| User cancels rebuild | "Rebuild cancelled. Graph unchanged." STOP |
| Deletion fails mid-rebuild | "Graph may be in partial state. Run /forge-graph init to fully reinitialize." STOP |
| Enrichment restore fails | WARNING "Bugfix telemetry will restart from zero." Continue |
/forge-ask — Natural-language queries over the graph/forge-init — Full project setup (may invoke /forge-graph init as a step)development
[writes] Build, fix, deploy, review, or modify code in this project. Universal entry for the forge pipeline. Auto-bootstraps on first run; brainstorms before planning when given a feature description. Use when you want to take any productive action: implementing features, fixing bugs, reviewing branches, deploying, committing, running migrations.
tools
[writes] Manage forge state and configuration: recovery, abort, config edits, session handoff, automations, playbooks, output compression, knowledge graph maintenance. Use when you need to recover from broken pipeline state, edit settings, or manage long-lived state.
development
[writes] Create, list, show, resume, or search forge session handoffs. Use when context is getting heavy and you want to transfer a forge run or conversation into a fresh Claude Code session, or to resume from a prior handoff artefact. Subcommands - no args (write), list, show, resume, search.
testing
[writes] Diagnose or fix pipeline state — read-only diagnose (default), repair counters/locks, reset clearing state while preserving caches, resume from checkpoint, rollback worktree commits, rewind to any prior checkpoint (time-travel), or list the checkpoint DAG. Use when pipeline stuck, failed with state errors, or you need to explore alternate execution paths. Trigger: /forge-recover, diagnose state, repair pipeline, reset state, resume from checkpoint, rollback commits, rewind checkpoint, time travel, list checkpoints