name: kg-merge description: "This skill should be used when the user asks to merge graphs across multiple projects, build a cross-repo knowledge graph, says '여러 프로젝트 통합 그래프', 'cross-project KG', 'merge graphs', or invokes /kg-merge. Reads each project's graphify-out/graph.json and produces a unified graphify-out/merged-graph.json via graphify v0.8.x merge-graphs. Read-only on source projects." trigger: /kg-merge
/kg-merge — Cross-Project Knowledge Graph
Combine multiple projects' graphify-out/ artifacts into one unified graph for cross-repo query, exploration, and visualization. Hard-depends on graphify v0.8.x (graphify merge-graphs).
Activate When
- User asks "여러 프로젝트 통합 그래프", "cross-project KG", "cross-repo graph", "merge graphs"
- User invokes
/kg-merge <project1> <project2> [...] [--out <path>] - Multiple
graphify-out/directories exist locally and user wants unified view - Before cross-project
/kg-query(which would otherwise need to query each separately)
Do Not Activate When
- Single project →
/kg-updateis enough; no merge needed - Project has wiki/ but no graphify-out/ → not applicable (this skill operates on graphs only, not wiki pages)
- User wants to query existing merged graph →
/kg-query(which detects merged-graph.json automatically once present)
Preconditions
graphifyCLI v0.8.x installed (which graphify;uv tool install graphifyy)- ≥2 source projects each containing
graphify-out/graph.json - Write access to chosen
--outlocation (default:<cwd>/graphify-out/merged-graph.json)
Workflow
Resolve sources
- Each positional argument is either a project root (auto-locate
graphify-out/graph.json) or a directgraph.jsonpath. - Verify every source exists. Report missing ones and stop.
- Each positional argument is either a project root (auto-locate
Freshness check (per source, per
~/.claude/skills/kg/references/architecture.mdfreshness gate)- For each source, check
graph.json mtime. - Any-stale (1+ source ≥7d): warn, list the stale paths in Caveats, and proceed. Suggest
/kg-updatefor those sources as a Next command. - All-stale (every source ≥7d): STOP. Require explicit user confirmation ("merge anyway") in a fresh command before proceeding. Do not auto-proceed on a "yes" response — user must re-state intent with the source list.
- For each source, check
Run merge
graphify merge-graphs <g1> <g2> [...] --out <output-path>- Default output:
<cwd>/graphify-out/merged-graph.json - Custom output via
--out
- Default output:
Post-merge stats
- Total nodes, total edges, total communities (after re-clustering — Leiden if the optional
graspologicextra is installed (Python<3.13), else Louvain fallback) - Per-source contribution: nodes from each source, overlap (label collision)
- Cross-source bridges: edges connecting nodes from different sources (most interesting signal)
- Total nodes, total edges, total communities (after re-clustering — Leiden if the optional
Optional: regenerate cross-repo report
- If user wants a re-clustered report, run
graphify cluster-only --graph graphify-out/merged-graph.json merged(v0.8.x accepts--graph <file>directly; writes a NEW graph atmerged/graphify-out/graph.json— the merged-graph.json input is left untouched; see Exceptions for caveats). Use a distinct out-dir likemerged, NOTgraphify-out(nests) or.(overwrites primary graph). Community naming needs an LLM backend; without one it keepsCommunity Nplaceholders.
- If user wants a re-clustered report, run
graphify CLI integration
# Inspect input graphs
graphify explain "<node-label>" --graph <project>/graphify-out/graph.json
# Merge
graphify merge-graphs <g1> <g2> <g3> --out graphify-out/merged-graph.json
# Re-cluster the merged result if needed (v0.8.x: --graph reads the file read-only,
# writes a NEW graph under the distinct out-dir 'merged/' — input merged-graph.json untouched)
graphify cluster-only --graph graphify-out/merged-graph.json merged
# Query the merged graph
graphify query "<question>" --graph graphify-out/merged-graph.json --budget 2000
Output Contract
Merge result: PASS | PARTIAL | FAIL
Sources merged: <N>
- [<source-1>] graph.json — <nodes>n / <edges>e (mtime: <date>, FRESH | STALE)
- [<source-2>] graph.json — ...
Output: <merged-graph-path>
Total nodes: <N>
Total edges: <M>
Total communities (post-clustering): <K>
Cross-source bridges: <N> edges
Top bridges (by degree on each side):
- [<src-A>:node-x] ↔ [<src-B>:node-y] (predicate: <type>)
- ...
Label overlaps (same label across sources): <N>
Top overlaps:
- "<label>" appears in <N> sources
Re-clustered: yes | no | not requested
MERGED_GRAPH_REPORT.md: regenerated | skipped | not requested
Confidence: high | medium | low
Caveats:
- <N stale source(s) | label-collision count high | none>
Next command:
- graphify query "<question>" --graph <merged-path>
- /kg-canvas community <N> (visualize merged communities)
- /kg-update <stale-source> (refresh stale source then re-merge)
Exceptions and Escalation
- graphify CLI not found → stop, suggest
uv tool install graphifyy(recommended overpip). - Fewer than 2 sources → stop; merge needs ≥2 inputs.
- Source
graph.jsonmissing for any specified project → report exact path, stop. Do not silently skip. - All sources stale (≥7d) → require explicit user confirmation to merge stale data; default behavior is to suggest refreshing via
/kg-updatefirst. - Output path overwrites a non-merged
graph.json→ refuse. Resolve the proposed--outpath to its canonical absolute form (Path.resolve()), then refuse if it equals the canonical path ofgraphify-out/graph.jsonfor the cwd OR for any input source. Symlinks resolve to their target; relative paths resolve against cwd. The output must bemerged-graph.jsonor any path that does not collide with an inputgraph.json. - Never modify input project sources, their
wiki/, or theirgraphify-out/graph.json. This skill is read-only on inputs and write-only to the merged output. - Re-clustering a merged graph (v0.8.x — limitation resolved). The old v0.5.0 restriction (
cluster-onlyrejected arbitrary file paths) no longer applies. v0.8.39 accepts--graph <file>directly — verified:graphify cluster-only --graph graphify-out/merged-graph.json <out-dir>reads the merged JSON via--graph(read-only — the source file is NOT modified; md5 unchanged after the run) and writes a NEW<out-dir>/graphify-out/graph.jsonplusGRAPH_REPORT.md+graph.html. So the re-clustered graph is a different file — point downstreamquery//kg-canvasat it, or copy it overmerged-graph.jsonto fold the communities back. Pick a distinct<out-dir>(e.g.merged): usinggraphify-outnests output atgraphify-out/graphify-out/graph.json, and using.would overwrite the project's primarygraph.json(violates the no-overwrite rule above). Community naming needs an LLM backend (an API key, or--backend/--model) — without one it keepsCommunity Nplaceholders (--no-labelto skip naming). For a pure-offline alternative, load the merged JSON (linkskey) and runnetworkx.community.louvain_communities(G). merge-graphsdoes NOT auto-detect cross-source bridges — it produces a UNION graph (concatenation). Bridges between conceptually-related nodes with different identifiers (e.g., your reimplementation'sEMLclass vs paper'scompile_to_emlfunction) require either:- Manual annotation: post-process the merged JSON to add explicit cross-edges
- LLM-based semantic matching: out of CLI scope; use
/kg-connectworkflow on the merged graph after Claude Code restart with MCP server activated Empirically (2026-04-26): merging eml-net (162n/303e) + SymbolicRegressionPackage (427n/1076e) → 589n/1379e, 0 cross-source edges, 0 label collisions (entirely disjoint identifiers). Cross-source bridges come from semantic understanding, not graphify's union operation.
Quality Gates
Before final answer:
- All input source paths verified before merge starts
- Output path does not overwrite any input
graph.json - Per-source freshness reported in Output Contract
- Cross-source bridge count surfaced (this is the merge's main value-add)
- At least one
Next commandactionable on the merged graph