From be3dcfca083f58b9cf8392c6ddeb6ac66f477e92 Mon Sep 17 00:00:00 2001 From: Safi Date: Tue, 16 Jun 2026 03:32:26 +0100 Subject: [PATCH] Unify query skill: ship expansion + inline fallback to every platform The query skill was split across two fragments so no platform got both capabilities: Claude had the vocab/IDF query-expansion step but no fallback if the CLI was unavailable; every other platform had the inline NetworkX fallback but the weaker raw-question matcher. Merge into one unified query reference + stub (Step 0 expansion -> CLI traversal -> inline NetworkX fallback, plus path/explain inline) shipped to all hosts. Remove the query_variant enum, its toml field, and the _CLI_ONLY_QUERY_HEADINGS coverage-audit exemption. Re-render all skill artifacts and re-bless expected/. skillgen check/audit-coverage/ monolith-roundtrip/schema-singleton all pass. Refs #1325. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 1 + graphify/skill-amp.md | 2 +- graphify/skill-claw.md | 2 +- graphify/skill-codex.md | 2 +- graphify/skill-copilot.md | 2 +- graphify/skill-droid.md | 2 +- graphify/skill-kilo.md | 2 +- graphify/skill-kiro.md | 2 +- graphify/skill-opencode.md | 2 +- graphify/skill-pi.md | 2 +- graphify/skill-trae.md | 2 +- graphify/skill-vscode.md | 2 +- graphify/skill-windows.md | 2 +- graphify/skill.md | 4 +- graphify/skills/amp/references/query.md | 68 +++++- graphify/skills/claude/references/query.md | 212 +++++++++++++++++- graphify/skills/claw/references/query.md | 68 +++++- graphify/skills/codex/references/query.md | 68 +++++- graphify/skills/copilot/references/query.md | 68 +++++- graphify/skills/droid/references/query.md | 68 +++++- graphify/skills/kilo/references/query.md | 68 +++++- graphify/skills/kiro/references/query.md | 68 +++++- graphify/skills/opencode/references/query.md | 68 +++++- graphify/skills/pi/references/query.md | 68 +++++- graphify/skills/trae/references/query.md | 68 +++++- graphify/skills/vscode/references/query.md | 68 +++++- graphify/skills/windows/references/query.md | 68 +++++- tests/test_skillgen.py | 18 +- .../skillgen/expected/graphify__skill-amp.md | 2 +- .../skillgen/expected/graphify__skill-claw.md | 2 +- .../expected/graphify__skill-codex.md | 2 +- .../expected/graphify__skill-copilot.md | 2 +- .../expected/graphify__skill-droid.md | 2 +- .../skillgen/expected/graphify__skill-kilo.md | 2 +- .../skillgen/expected/graphify__skill-kiro.md | 2 +- .../expected/graphify__skill-opencode.md | 2 +- tools/skillgen/expected/graphify__skill-pi.md | 2 +- .../skillgen/expected/graphify__skill-trae.md | 2 +- .../expected/graphify__skill-vscode.md | 2 +- .../expected/graphify__skill-windows.md | 2 +- tools/skillgen/expected/graphify__skill.md | 4 +- ...raphify__skills__amp__references__query.md | 68 +++++- ...hify__skills__claude__references__query.md | 212 +++++++++++++++++- ...aphify__skills__claw__references__query.md | 68 +++++- ...phify__skills__codex__references__query.md | 68 +++++- ...ify__skills__copilot__references__query.md | 68 +++++- ...phify__skills__droid__references__query.md | 68 +++++- ...aphify__skills__kilo__references__query.md | 68 +++++- ...aphify__skills__kiro__references__query.md | 68 +++++- ...fy__skills__opencode__references__query.md | 68 +++++- ...graphify__skills__pi__references__query.md | 68 +++++- ...aphify__skills__trae__references__query.md | 68 +++++- ...hify__skills__vscode__references__query.md | 68 +++++- ...ify__skills__windows__references__query.md | 68 +++++- .../fragments/query-stub/cli-inline.md | 7 - tools/skillgen/fragments/query-stub/cli.md | 7 - .../skillgen/fragments/query-stub/default.md | 7 + .../fragments/references/query/cli.md | 103 --------- .../query/{cli-inline.md => default.md} | 68 +++++- tools/skillgen/gen.py | 43 ++-- tools/skillgen/platforms.toml | 15 -- 61 files changed, 1996 insertions(+), 385 deletions(-) delete mode 100644 tools/skillgen/fragments/query-stub/cli-inline.md delete mode 100644 tools/skillgen/fragments/query-stub/cli.md create mode 100644 tools/skillgen/fragments/query-stub/default.md delete mode 100644 tools/skillgen/fragments/references/query/cli.md rename tools/skillgen/fragments/references/query/{cli-inline.md => default.md} (68%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 382109369..2259f7063 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Full release notes with details on each version: [GitHub Releases](https://githu ## Unreleased +- Fix: every platform's query skill now ships **both** the vocab/IDF query-expansion step and the inline NetworkX fallback. Previously the two capabilities were split across `cli.md` / `cli-inline.md` so no platform got both — Claude had the superior expansion but no CLI-down fallback, while all other platforms had the fallback but the weaker raw-question matcher. The two fragments are merged into one unified `query` reference (and stub) shipped to all hosts; the `query_variant` enum and its coverage-audit exemption are removed (#1325; thanks @LeanderBlume). - Fix: cross-file Java `implements`/`inherits`/`imports` edges no longer orphan onto bare "shadow" nodes when two packages define a same-named type. The referencing file's `import` statement now disambiguates by exact package (FQN) and re-points the edge to the real definition, dropping the orphan stub. Previously `_rewire_unique_stub_nodes` could only repair the globally-unique case, so same-named interfaces (common in large Java codebases — `Handler`, `Service`, interface+impl pairs) left the real definition isolated in its own community (#1318). - Fix: Swift imports of the same module from multiple files now collapse to a single shared `type=module` node instead of N path-qualified duplicates. The import target is tagged `type=module` and exempted from id-disambiguation, so reverse traversal ("what imports CoreKit?") works; the `--no-cluster` writer also now dedupes nodes by id (and edges) to match the clustered `build_from_json` path. Builds on the v0.8.40 Swift-import fix (#1327, #1330; thanks @duncan-daydream). diff --git a/graphify/skill-amp.md b/graphify/skill-amp.md index 56f7b8692..af9702105 100644 --- a/graphify/skill-amp.md +++ b/graphify/skill-amp.md @@ -589,7 +589,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/graphify/skill-claw.md b/graphify/skill-claw.md index db3905b10..847e07eca 100644 --- a/graphify/skill-claw.md +++ b/graphify/skill-claw.md @@ -592,7 +592,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/graphify/skill-codex.md b/graphify/skill-codex.md index d63892df0..07c69ab5f 100644 --- a/graphify/skill-codex.md +++ b/graphify/skill-codex.md @@ -589,7 +589,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/graphify/skill-copilot.md b/graphify/skill-copilot.md index db3905b10..847e07eca 100644 --- a/graphify/skill-copilot.md +++ b/graphify/skill-copilot.md @@ -592,7 +592,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/graphify/skill-droid.md b/graphify/skill-droid.md index 0238913d6..362f1c5c5 100644 --- a/graphify/skill-droid.md +++ b/graphify/skill-droid.md @@ -589,7 +589,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/graphify/skill-kilo.md b/graphify/skill-kilo.md index 2cb0e1f6b..39c5835da 100644 --- a/graphify/skill-kilo.md +++ b/graphify/skill-kilo.md @@ -592,7 +592,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/graphify/skill-kiro.md b/graphify/skill-kiro.md index db3905b10..847e07eca 100644 --- a/graphify/skill-kiro.md +++ b/graphify/skill-kiro.md @@ -592,7 +592,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/graphify/skill-opencode.md b/graphify/skill-opencode.md index 31ef311b7..66cea9db4 100644 --- a/graphify/skill-opencode.md +++ b/graphify/skill-opencode.md @@ -584,7 +584,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/graphify/skill-pi.md b/graphify/skill-pi.md index db3905b10..847e07eca 100644 --- a/graphify/skill-pi.md +++ b/graphify/skill-pi.md @@ -592,7 +592,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/graphify/skill-trae.md b/graphify/skill-trae.md index f27e2fc20..e441ed1b9 100644 --- a/graphify/skill-trae.md +++ b/graphify/skill-trae.md @@ -590,7 +590,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/graphify/skill-vscode.md b/graphify/skill-vscode.md index 764fe51d6..28c8a18af 100644 --- a/graphify/skill-vscode.md +++ b/graphify/skill-vscode.md @@ -588,7 +588,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/graphify/skill-windows.md b/graphify/skill-windows.md index f943feaab..9137adf1a 100644 --- a/graphify/skill-windows.md +++ b/graphify/skill-windows.md @@ -614,7 +614,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/graphify/skill.md b/graphify/skill.md index 1ad666729..847e07eca 100644 --- a/graphify/skill.md +++ b/graphify/skill.md @@ -586,13 +586,13 @@ Both are non-default subcommands. `--update` re-extracts only new or changed fil ## For /graphify query -When `graphify-out/graph.json` already exists and the user asks a question about the corpus, run the query directly: +When `graphify-out/graph.json` already exists and the user asks a question about the corpus, answer from the graph rather than rebuilding it: ```bash graphify query "" ``` -Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. For that vocab-expansion step, the `--dfs` / `--budget` modes, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/graphify/skills/amp/references/query.md b/graphify/skills/amp/references/query.md index 25b826f8e..cb93304a2 100644 --- a/graphify/skills/amp/references/query.md +++ b/graphify/skills/amp/references/query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/graphify/skills/claude/references/query.md b/graphify/skills/claude/references/query.md index b08289e1b..cb93304a2 100644 --- a/graphify/skills/claude/references/query.md +++ b/graphify/skills/claude/references/query.md @@ -1,6 +1,6 @@ # graphify reference: query, path, explain -Load this when the user asks a question against an existing graph, or runs `/graphify path` or `/graphify explain`. The core's query stub points here for the full traversal flow. +Load this when the user asks a question against an existing graph, or runs `/graphify path` or `/graphify explain`. The core's query stub points here for the full traversal flow. These flows use the `graphify query` CLI when it is available and fall back to an inline NetworkX traversal otherwise. Two traversal modes - choose based on the question: @@ -9,9 +9,20 @@ Two traversal modes - choose based on the question: | BFS (default) | _(none)_ | "What is X connected to?" - broad context, nearest neighbors first | | DFS | `--dfs` | "How does X reach Y?" - trace a specific chain or dependency path | +First check the graph exists: +```bash +$(cat graphify-out/.graphify_python) -c " +from pathlib import Path +if not Path('graphify-out/graph.json').exists(): + print('ERROR: No graph found. Run /graphify first to build the graph.') + raise SystemExit(1) +" +``` +If it fails, stop and tell the user to run `/graphify ` first. + ### Step 0 — Constrained query expansion (REQUIRED before traversal) -graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: @@ -51,12 +62,108 @@ If the list is empty, say so plainly and stop — do not proceed to traversal. Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) +Prefer the CLI when it is installed: ```bash graphify query "QUESTION" # or: graphify query "QUESTION" --dfs --budget 3000 ``` -Answer using **only** what the graph output contains. Quote `source_location` when citing a specific fact. If the graph lacks enough information, say so - do not hallucinate edges. +If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: + +1. Find the 1-3 nodes whose label best matches the expanded tokens. +2. Run the appropriate traversal from each starting node. +3. Read the subgraph - node labels, edge relations, confidence tags, source locations. +4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. +5. If the graph lacks enough information, say so - do not hallucinate edges. + +```bash +$(cat graphify-out/.graphify_python) -c " +import sys, json +from networkx.readwrite import json_graph +import networkx as nx +from pathlib import Path + +data = json.loads(Path('graphify-out/graph.json').read_text()) +G = json_graph.node_link_graph(data, edges='links') + +question = 'QUESTION' +mode = 'MODE' # 'bfs' or 'dfs' +terms = [t.lower() for t in question.split() if len(t) > 3] + +# Find best-matching start nodes +scored = [] +for nid, ndata in G.nodes(data=True): + label = ndata.get('label', '').lower() + score = sum(1 for t in terms if t in label) + if score > 0: + scored.append((score, nid)) +scored.sort(reverse=True) +start_nodes = [nid for _, nid in scored[:3]] + +if not start_nodes: + print('No matching nodes found for query terms:', terms) + sys.exit(0) + +subgraph_nodes = set() +subgraph_edges = [] + +if mode == 'dfs': + # DFS: follow one path as deep as possible before backtracking. + # Depth-limited to 6 to avoid traversing the whole graph. + visited = set() + stack = [(n, 0) for n in reversed(start_nodes)] + while stack: + node, depth = stack.pop() + if node in visited or depth > 6: + continue + visited.add(node) + subgraph_nodes.add(node) + for neighbor in G.neighbors(node): + if neighbor not in visited: + stack.append((neighbor, depth + 1)) + subgraph_edges.append((node, neighbor)) +else: + # BFS: explore all neighbors layer by layer up to depth 3. + frontier = set(start_nodes) + subgraph_nodes = set(start_nodes) + for _ in range(3): + next_frontier = set() + for n in frontier: + for neighbor in G.neighbors(n): + if neighbor not in subgraph_nodes: + next_frontier.add(neighbor) + subgraph_edges.append((n, neighbor)) + subgraph_nodes.update(next_frontier) + frontier = next_frontier + +# Token-budget aware output: rank by relevance, cut at budget (~4 chars/token) +token_budget = BUDGET # default 2000 +char_budget = token_budget * 4 + +# Score each node by term overlap for ranked output +def relevance(nid): + label = G.nodes[nid].get('label', '').lower() + return sum(1 for t in terms if t in label) + +ranked_nodes = sorted(subgraph_nodes, key=relevance, reverse=True) + +lines = [f'Traversal: {mode.upper()} | Start: {[G.nodes[n].get(\"label\",n) for n in start_nodes]} | {len(subgraph_nodes)} nodes'] +for nid in ranked_nodes: + d = G.nodes[nid] + lines.append(f' NODE {d.get(\"label\", nid)} [src={d.get(\"source_file\",\"\")} loc={d.get(\"source_location\",\"\")}]') +for u, v in subgraph_edges: + if u in subgraph_nodes and v in subgraph_nodes: + _raw = G[u][v]; d = next(iter(_raw.values()), {}) if isinstance(G, nx.MultiGraph) else _raw + lines.append(f' EDGE {G.nodes[u].get(\"label\",u)} --{d.get(\"relation\",\"\")} [{d.get(\"confidence\",\"\")}]--> {G.nodes[v].get(\"label\",v)}') + +output = '\n'.join(lines) +if len(output) > char_budget: + output = output[:char_budget] + f'\n... (truncated at ~{token_budget} token budget - use --budget N for more)' +print(output) +" +``` + +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: @@ -70,13 +177,63 @@ Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with you ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: ```bash graphify path "NODE_A" "NODE_B" ``` -Replace `NODE_A` and `NODE_B` with the actual concept names. Then explain the path in plain language - what each hop means, why it's significant. +If the CLI is unavailable, run it inline: + +```bash +$(cat graphify-out/.graphify_python) -c " +import json, sys +import networkx as nx +from networkx.readwrite import json_graph +from pathlib import Path + +data = json.loads(Path('graphify-out/graph.json').read_text()) +G = json_graph.node_link_graph(data, edges='links') + +a_term = 'NODE_A' +b_term = 'NODE_B' + +def find_node(term): + term = term.lower() + scored = sorted( + [(sum(1 for w in term.split() if w in G.nodes[n].get('label','').lower()), n) + for n in G.nodes()], + reverse=True + ) + return scored[0][1] if scored and scored[0][0] > 0 else None + +src = find_node(a_term) +tgt = find_node(b_term) + +if not src or not tgt: + print(f'Could not find nodes matching: {a_term!r} or {b_term!r}') + sys.exit(0) + +try: + path = nx.shortest_path(G, src, tgt) + print(f'Shortest path ({len(path)-1} hops):') + for i, nid in enumerate(path): + label = G.nodes[nid].get('label', nid) + if i < len(path) - 1: + _raw = G[nid][path[i+1]]; edge = next(iter(_raw.values()), {}) if isinstance(G, nx.MultiGraph) else _raw + rel = edge.get('relation', '') + conf = edge.get('confidence', '') + print(f' {label} --{rel}--> [{conf}]') + else: + print(f' {label}') +except nx.NetworkXNoPath: + print(f'No path found between {a_term!r} and {b_term!r}') +except nx.NodeNotFound as e: + print(f'Node not found: {e}') +" +``` + +Replace `NODE_A` and `NODE_B` with the actual concept names from the user. Then explain the path in plain language - what each hop means, why it's significant. After writing the explanation, save it back: @@ -88,12 +245,55 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: ```bash graphify explain "NODE_NAME" ``` +If the CLI is unavailable, run it inline: + +```bash +$(cat graphify-out/.graphify_python) -c " +import json, sys +import networkx as nx +from networkx.readwrite import json_graph +from pathlib import Path + +data = json.loads(Path('graphify-out/graph.json').read_text()) +G = json_graph.node_link_graph(data, edges='links') + +term = 'NODE_NAME' +term_lower = term.lower() + +# Find best matching node +scored = sorted( + [(sum(1 for w in term_lower.split() if w in G.nodes[n].get('label','').lower()), n) + for n in G.nodes()], + reverse=True +) +if not scored or scored[0][0] == 0: + print(f'No node matching {term!r}') + sys.exit(0) + +nid = scored[0][1] +data_n = G.nodes[nid] +print(f'NODE: {data_n.get(\"label\", nid)}') +print(f' source: {data_n.get(\"source_file\",\"unknown\")}') +print(f' type: {data_n.get(\"file_type\",\"unknown\")}') +print(f' degree: {G.degree(nid)}') +print() +print('CONNECTIONS:') +for neighbor in G.neighbors(nid): + _raw = G[nid][neighbor]; edge = next(iter(_raw.values()), {}) if isinstance(G, nx.MultiGraph) else _raw + nlabel = G.nodes[neighbor].get('label', neighbor) + rel = edge.get('relation', '') + conf = edge.get('confidence', '') + src_file = G.nodes[neighbor].get('source_file', '') + print(f' --{rel}--> {nlabel} [{conf}] ({src_file})') +" +``` + Replace `NODE_NAME` with the concept the user asked about. Then write a 3-5 sentence explanation of what this node is, what it connects to, and why those connections are significant. Use the source locations as citations. After writing the explanation, save it back: diff --git a/graphify/skills/claw/references/query.md b/graphify/skills/claw/references/query.md index 25b826f8e..cb93304a2 100644 --- a/graphify/skills/claw/references/query.md +++ b/graphify/skills/claw/references/query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/graphify/skills/codex/references/query.md b/graphify/skills/codex/references/query.md index 25b826f8e..cb93304a2 100644 --- a/graphify/skills/codex/references/query.md +++ b/graphify/skills/codex/references/query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/graphify/skills/copilot/references/query.md b/graphify/skills/copilot/references/query.md index 25b826f8e..cb93304a2 100644 --- a/graphify/skills/copilot/references/query.md +++ b/graphify/skills/copilot/references/query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/graphify/skills/droid/references/query.md b/graphify/skills/droid/references/query.md index 25b826f8e..cb93304a2 100644 --- a/graphify/skills/droid/references/query.md +++ b/graphify/skills/droid/references/query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/graphify/skills/kilo/references/query.md b/graphify/skills/kilo/references/query.md index 25b826f8e..cb93304a2 100644 --- a/graphify/skills/kilo/references/query.md +++ b/graphify/skills/kilo/references/query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/graphify/skills/kiro/references/query.md b/graphify/skills/kiro/references/query.md index 25b826f8e..cb93304a2 100644 --- a/graphify/skills/kiro/references/query.md +++ b/graphify/skills/kiro/references/query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/graphify/skills/opencode/references/query.md b/graphify/skills/opencode/references/query.md index 25b826f8e..cb93304a2 100644 --- a/graphify/skills/opencode/references/query.md +++ b/graphify/skills/opencode/references/query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/graphify/skills/pi/references/query.md b/graphify/skills/pi/references/query.md index 25b826f8e..cb93304a2 100644 --- a/graphify/skills/pi/references/query.md +++ b/graphify/skills/pi/references/query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/graphify/skills/trae/references/query.md b/graphify/skills/trae/references/query.md index 25b826f8e..cb93304a2 100644 --- a/graphify/skills/trae/references/query.md +++ b/graphify/skills/trae/references/query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/graphify/skills/vscode/references/query.md b/graphify/skills/vscode/references/query.md index 25b826f8e..cb93304a2 100644 --- a/graphify/skills/vscode/references/query.md +++ b/graphify/skills/vscode/references/query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/graphify/skills/windows/references/query.md b/graphify/skills/windows/references/query.md index 25b826f8e..cb93304a2 100644 --- a/graphify/skills/windows/references/query.md +++ b/graphify/skills/windows/references/query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/tests/test_skillgen.py b/tests/test_skillgen.py index 73e9884c0..da6e28f24 100644 --- a/tests/test_skillgen.py +++ b/tests/test_skillgen.py @@ -305,19 +305,21 @@ def test_codex_uses_compact_extraction_windows_uses_verbose(): assert "(compact)" not in windows_refs["extraction-spec.md"] -def test_cli_inline_query_stub_has_no_vocab_expansion(): - """cli-inline hosts get the NetworkX-fallback stub, not vocab-expansion.""" - for key in ("codex", "windows"): +def test_every_platform_query_has_expansion_and_fallback(): + """#1325: the unified query reference ships BOTH the vocab-expansion step and + the inline NetworkX fallback to every platform (previously split so no host + got both — Claude had expansion but no fallback; the rest the reverse).""" + for key in ("claude", "codex", "windows", "opencode"): core, refs = _platform_artifacts(key) - # The core stub points at the query reference without the vocab step. - assert "expand the question against the graph's own vocabulary" not in core + # Core stub mentions both the vocab-expansion step and the inline fallback. + assert "expand the question against the graph's own vocabulary" in core assert "NetworkX traversal" in core - # The query reference carries the path/explain headings but not the - # claude-only vocab-expansion sub-headings. + # The query reference carries expansion, fallback, and path/explain. q = refs["query.md"] + assert "Constrained query expansion" in q + assert "If the CLI is unavailable" in q assert "## For /graphify path" in q assert "## For /graphify explain" in q - assert "Constrained query expansion" not in q def test_schema_singleton_passes_across_all_platforms(): diff --git a/tools/skillgen/expected/graphify__skill-amp.md b/tools/skillgen/expected/graphify__skill-amp.md index 56f7b8692..af9702105 100644 --- a/tools/skillgen/expected/graphify__skill-amp.md +++ b/tools/skillgen/expected/graphify__skill-amp.md @@ -589,7 +589,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/tools/skillgen/expected/graphify__skill-claw.md b/tools/skillgen/expected/graphify__skill-claw.md index db3905b10..847e07eca 100644 --- a/tools/skillgen/expected/graphify__skill-claw.md +++ b/tools/skillgen/expected/graphify__skill-claw.md @@ -592,7 +592,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/tools/skillgen/expected/graphify__skill-codex.md b/tools/skillgen/expected/graphify__skill-codex.md index d63892df0..07c69ab5f 100644 --- a/tools/skillgen/expected/graphify__skill-codex.md +++ b/tools/skillgen/expected/graphify__skill-codex.md @@ -589,7 +589,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/tools/skillgen/expected/graphify__skill-copilot.md b/tools/skillgen/expected/graphify__skill-copilot.md index db3905b10..847e07eca 100644 --- a/tools/skillgen/expected/graphify__skill-copilot.md +++ b/tools/skillgen/expected/graphify__skill-copilot.md @@ -592,7 +592,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/tools/skillgen/expected/graphify__skill-droid.md b/tools/skillgen/expected/graphify__skill-droid.md index 0238913d6..362f1c5c5 100644 --- a/tools/skillgen/expected/graphify__skill-droid.md +++ b/tools/skillgen/expected/graphify__skill-droid.md @@ -589,7 +589,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/tools/skillgen/expected/graphify__skill-kilo.md b/tools/skillgen/expected/graphify__skill-kilo.md index 2cb0e1f6b..39c5835da 100644 --- a/tools/skillgen/expected/graphify__skill-kilo.md +++ b/tools/skillgen/expected/graphify__skill-kilo.md @@ -592,7 +592,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/tools/skillgen/expected/graphify__skill-kiro.md b/tools/skillgen/expected/graphify__skill-kiro.md index db3905b10..847e07eca 100644 --- a/tools/skillgen/expected/graphify__skill-kiro.md +++ b/tools/skillgen/expected/graphify__skill-kiro.md @@ -592,7 +592,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/tools/skillgen/expected/graphify__skill-opencode.md b/tools/skillgen/expected/graphify__skill-opencode.md index 31ef311b7..66cea9db4 100644 --- a/tools/skillgen/expected/graphify__skill-opencode.md +++ b/tools/skillgen/expected/graphify__skill-opencode.md @@ -584,7 +584,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/tools/skillgen/expected/graphify__skill-pi.md b/tools/skillgen/expected/graphify__skill-pi.md index db3905b10..847e07eca 100644 --- a/tools/skillgen/expected/graphify__skill-pi.md +++ b/tools/skillgen/expected/graphify__skill-pi.md @@ -592,7 +592,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/tools/skillgen/expected/graphify__skill-trae.md b/tools/skillgen/expected/graphify__skill-trae.md index f27e2fc20..e441ed1b9 100644 --- a/tools/skillgen/expected/graphify__skill-trae.md +++ b/tools/skillgen/expected/graphify__skill-trae.md @@ -590,7 +590,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/tools/skillgen/expected/graphify__skill-vscode.md b/tools/skillgen/expected/graphify__skill-vscode.md index 764fe51d6..28c8a18af 100644 --- a/tools/skillgen/expected/graphify__skill-vscode.md +++ b/tools/skillgen/expected/graphify__skill-vscode.md @@ -588,7 +588,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/tools/skillgen/expected/graphify__skill-windows.md b/tools/skillgen/expected/graphify__skill-windows.md index f943feaab..9137adf1a 100644 --- a/tools/skillgen/expected/graphify__skill-windows.md +++ b/tools/skillgen/expected/graphify__skill-windows.md @@ -614,7 +614,7 @@ When `graphify-out/graph.json` already exists and the user asks a question about graphify query "" ``` -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/tools/skillgen/expected/graphify__skill.md b/tools/skillgen/expected/graphify__skill.md index 1ad666729..847e07eca 100644 --- a/tools/skillgen/expected/graphify__skill.md +++ b/tools/skillgen/expected/graphify__skill.md @@ -586,13 +586,13 @@ Both are non-default subcommands. `--update` re-extracts only new or changed fil ## For /graphify query -When `graphify-out/graph.json` already exists and the user asks a question about the corpus, run the query directly: +When `graphify-out/graph.json` already exists and the user asks a question about the corpus, answer from the graph rather than rebuilding it: ```bash graphify query "" ``` -Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. For that vocab-expansion step, the `--dfs` / `--budget` modes, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. --- diff --git a/tools/skillgen/expected/graphify__skills__amp__references__query.md b/tools/skillgen/expected/graphify__skills__amp__references__query.md index 25b826f8e..cb93304a2 100644 --- a/tools/skillgen/expected/graphify__skills__amp__references__query.md +++ b/tools/skillgen/expected/graphify__skills__amp__references__query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/tools/skillgen/expected/graphify__skills__claude__references__query.md b/tools/skillgen/expected/graphify__skills__claude__references__query.md index b08289e1b..cb93304a2 100644 --- a/tools/skillgen/expected/graphify__skills__claude__references__query.md +++ b/tools/skillgen/expected/graphify__skills__claude__references__query.md @@ -1,6 +1,6 @@ # graphify reference: query, path, explain -Load this when the user asks a question against an existing graph, or runs `/graphify path` or `/graphify explain`. The core's query stub points here for the full traversal flow. +Load this when the user asks a question against an existing graph, or runs `/graphify path` or `/graphify explain`. The core's query stub points here for the full traversal flow. These flows use the `graphify query` CLI when it is available and fall back to an inline NetworkX traversal otherwise. Two traversal modes - choose based on the question: @@ -9,9 +9,20 @@ Two traversal modes - choose based on the question: | BFS (default) | _(none)_ | "What is X connected to?" - broad context, nearest neighbors first | | DFS | `--dfs` | "How does X reach Y?" - trace a specific chain or dependency path | +First check the graph exists: +```bash +$(cat graphify-out/.graphify_python) -c " +from pathlib import Path +if not Path('graphify-out/graph.json').exists(): + print('ERROR: No graph found. Run /graphify first to build the graph.') + raise SystemExit(1) +" +``` +If it fails, stop and tell the user to run `/graphify ` first. + ### Step 0 — Constrained query expansion (REQUIRED before traversal) -graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: @@ -51,12 +62,108 @@ If the list is empty, say so plainly and stop — do not proceed to traversal. Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) +Prefer the CLI when it is installed: ```bash graphify query "QUESTION" # or: graphify query "QUESTION" --dfs --budget 3000 ``` -Answer using **only** what the graph output contains. Quote `source_location` when citing a specific fact. If the graph lacks enough information, say so - do not hallucinate edges. +If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: + +1. Find the 1-3 nodes whose label best matches the expanded tokens. +2. Run the appropriate traversal from each starting node. +3. Read the subgraph - node labels, edge relations, confidence tags, source locations. +4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. +5. If the graph lacks enough information, say so - do not hallucinate edges. + +```bash +$(cat graphify-out/.graphify_python) -c " +import sys, json +from networkx.readwrite import json_graph +import networkx as nx +from pathlib import Path + +data = json.loads(Path('graphify-out/graph.json').read_text()) +G = json_graph.node_link_graph(data, edges='links') + +question = 'QUESTION' +mode = 'MODE' # 'bfs' or 'dfs' +terms = [t.lower() for t in question.split() if len(t) > 3] + +# Find best-matching start nodes +scored = [] +for nid, ndata in G.nodes(data=True): + label = ndata.get('label', '').lower() + score = sum(1 for t in terms if t in label) + if score > 0: + scored.append((score, nid)) +scored.sort(reverse=True) +start_nodes = [nid for _, nid in scored[:3]] + +if not start_nodes: + print('No matching nodes found for query terms:', terms) + sys.exit(0) + +subgraph_nodes = set() +subgraph_edges = [] + +if mode == 'dfs': + # DFS: follow one path as deep as possible before backtracking. + # Depth-limited to 6 to avoid traversing the whole graph. + visited = set() + stack = [(n, 0) for n in reversed(start_nodes)] + while stack: + node, depth = stack.pop() + if node in visited or depth > 6: + continue + visited.add(node) + subgraph_nodes.add(node) + for neighbor in G.neighbors(node): + if neighbor not in visited: + stack.append((neighbor, depth + 1)) + subgraph_edges.append((node, neighbor)) +else: + # BFS: explore all neighbors layer by layer up to depth 3. + frontier = set(start_nodes) + subgraph_nodes = set(start_nodes) + for _ in range(3): + next_frontier = set() + for n in frontier: + for neighbor in G.neighbors(n): + if neighbor not in subgraph_nodes: + next_frontier.add(neighbor) + subgraph_edges.append((n, neighbor)) + subgraph_nodes.update(next_frontier) + frontier = next_frontier + +# Token-budget aware output: rank by relevance, cut at budget (~4 chars/token) +token_budget = BUDGET # default 2000 +char_budget = token_budget * 4 + +# Score each node by term overlap for ranked output +def relevance(nid): + label = G.nodes[nid].get('label', '').lower() + return sum(1 for t in terms if t in label) + +ranked_nodes = sorted(subgraph_nodes, key=relevance, reverse=True) + +lines = [f'Traversal: {mode.upper()} | Start: {[G.nodes[n].get(\"label\",n) for n in start_nodes]} | {len(subgraph_nodes)} nodes'] +for nid in ranked_nodes: + d = G.nodes[nid] + lines.append(f' NODE {d.get(\"label\", nid)} [src={d.get(\"source_file\",\"\")} loc={d.get(\"source_location\",\"\")}]') +for u, v in subgraph_edges: + if u in subgraph_nodes and v in subgraph_nodes: + _raw = G[u][v]; d = next(iter(_raw.values()), {}) if isinstance(G, nx.MultiGraph) else _raw + lines.append(f' EDGE {G.nodes[u].get(\"label\",u)} --{d.get(\"relation\",\"\")} [{d.get(\"confidence\",\"\")}]--> {G.nodes[v].get(\"label\",v)}') + +output = '\n'.join(lines) +if len(output) > char_budget: + output = output[:char_budget] + f'\n... (truncated at ~{token_budget} token budget - use --budget N for more)' +print(output) +" +``` + +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: @@ -70,13 +177,63 @@ Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with you ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: ```bash graphify path "NODE_A" "NODE_B" ``` -Replace `NODE_A` and `NODE_B` with the actual concept names. Then explain the path in plain language - what each hop means, why it's significant. +If the CLI is unavailable, run it inline: + +```bash +$(cat graphify-out/.graphify_python) -c " +import json, sys +import networkx as nx +from networkx.readwrite import json_graph +from pathlib import Path + +data = json.loads(Path('graphify-out/graph.json').read_text()) +G = json_graph.node_link_graph(data, edges='links') + +a_term = 'NODE_A' +b_term = 'NODE_B' + +def find_node(term): + term = term.lower() + scored = sorted( + [(sum(1 for w in term.split() if w in G.nodes[n].get('label','').lower()), n) + for n in G.nodes()], + reverse=True + ) + return scored[0][1] if scored and scored[0][0] > 0 else None + +src = find_node(a_term) +tgt = find_node(b_term) + +if not src or not tgt: + print(f'Could not find nodes matching: {a_term!r} or {b_term!r}') + sys.exit(0) + +try: + path = nx.shortest_path(G, src, tgt) + print(f'Shortest path ({len(path)-1} hops):') + for i, nid in enumerate(path): + label = G.nodes[nid].get('label', nid) + if i < len(path) - 1: + _raw = G[nid][path[i+1]]; edge = next(iter(_raw.values()), {}) if isinstance(G, nx.MultiGraph) else _raw + rel = edge.get('relation', '') + conf = edge.get('confidence', '') + print(f' {label} --{rel}--> [{conf}]') + else: + print(f' {label}') +except nx.NetworkXNoPath: + print(f'No path found between {a_term!r} and {b_term!r}') +except nx.NodeNotFound as e: + print(f'Node not found: {e}') +" +``` + +Replace `NODE_A` and `NODE_B` with the actual concept names from the user. Then explain the path in plain language - what each hop means, why it's significant. After writing the explanation, save it back: @@ -88,12 +245,55 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: ```bash graphify explain "NODE_NAME" ``` +If the CLI is unavailable, run it inline: + +```bash +$(cat graphify-out/.graphify_python) -c " +import json, sys +import networkx as nx +from networkx.readwrite import json_graph +from pathlib import Path + +data = json.loads(Path('graphify-out/graph.json').read_text()) +G = json_graph.node_link_graph(data, edges='links') + +term = 'NODE_NAME' +term_lower = term.lower() + +# Find best matching node +scored = sorted( + [(sum(1 for w in term_lower.split() if w in G.nodes[n].get('label','').lower()), n) + for n in G.nodes()], + reverse=True +) +if not scored or scored[0][0] == 0: + print(f'No node matching {term!r}') + sys.exit(0) + +nid = scored[0][1] +data_n = G.nodes[nid] +print(f'NODE: {data_n.get(\"label\", nid)}') +print(f' source: {data_n.get(\"source_file\",\"unknown\")}') +print(f' type: {data_n.get(\"file_type\",\"unknown\")}') +print(f' degree: {G.degree(nid)}') +print() +print('CONNECTIONS:') +for neighbor in G.neighbors(nid): + _raw = G[nid][neighbor]; edge = next(iter(_raw.values()), {}) if isinstance(G, nx.MultiGraph) else _raw + nlabel = G.nodes[neighbor].get('label', neighbor) + rel = edge.get('relation', '') + conf = edge.get('confidence', '') + src_file = G.nodes[neighbor].get('source_file', '') + print(f' --{rel}--> {nlabel} [{conf}] ({src_file})') +" +``` + Replace `NODE_NAME` with the concept the user asked about. Then write a 3-5 sentence explanation of what this node is, what it connects to, and why those connections are significant. Use the source locations as citations. After writing the explanation, save it back: diff --git a/tools/skillgen/expected/graphify__skills__claw__references__query.md b/tools/skillgen/expected/graphify__skills__claw__references__query.md index 25b826f8e..cb93304a2 100644 --- a/tools/skillgen/expected/graphify__skills__claw__references__query.md +++ b/tools/skillgen/expected/graphify__skills__claw__references__query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/tools/skillgen/expected/graphify__skills__codex__references__query.md b/tools/skillgen/expected/graphify__skills__codex__references__query.md index 25b826f8e..cb93304a2 100644 --- a/tools/skillgen/expected/graphify__skills__codex__references__query.md +++ b/tools/skillgen/expected/graphify__skills__codex__references__query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/tools/skillgen/expected/graphify__skills__copilot__references__query.md b/tools/skillgen/expected/graphify__skills__copilot__references__query.md index 25b826f8e..cb93304a2 100644 --- a/tools/skillgen/expected/graphify__skills__copilot__references__query.md +++ b/tools/skillgen/expected/graphify__skills__copilot__references__query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/tools/skillgen/expected/graphify__skills__droid__references__query.md b/tools/skillgen/expected/graphify__skills__droid__references__query.md index 25b826f8e..cb93304a2 100644 --- a/tools/skillgen/expected/graphify__skills__droid__references__query.md +++ b/tools/skillgen/expected/graphify__skills__droid__references__query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/tools/skillgen/expected/graphify__skills__kilo__references__query.md b/tools/skillgen/expected/graphify__skills__kilo__references__query.md index 25b826f8e..cb93304a2 100644 --- a/tools/skillgen/expected/graphify__skills__kilo__references__query.md +++ b/tools/skillgen/expected/graphify__skills__kilo__references__query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/tools/skillgen/expected/graphify__skills__kiro__references__query.md b/tools/skillgen/expected/graphify__skills__kiro__references__query.md index 25b826f8e..cb93304a2 100644 --- a/tools/skillgen/expected/graphify__skills__kiro__references__query.md +++ b/tools/skillgen/expected/graphify__skills__kiro__references__query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/tools/skillgen/expected/graphify__skills__opencode__references__query.md b/tools/skillgen/expected/graphify__skills__opencode__references__query.md index 25b826f8e..cb93304a2 100644 --- a/tools/skillgen/expected/graphify__skills__opencode__references__query.md +++ b/tools/skillgen/expected/graphify__skills__opencode__references__query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/tools/skillgen/expected/graphify__skills__pi__references__query.md b/tools/skillgen/expected/graphify__skills__pi__references__query.md index 25b826f8e..cb93304a2 100644 --- a/tools/skillgen/expected/graphify__skills__pi__references__query.md +++ b/tools/skillgen/expected/graphify__skills__pi__references__query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/tools/skillgen/expected/graphify__skills__trae__references__query.md b/tools/skillgen/expected/graphify__skills__trae__references__query.md index 25b826f8e..cb93304a2 100644 --- a/tools/skillgen/expected/graphify__skills__trae__references__query.md +++ b/tools/skillgen/expected/graphify__skills__trae__references__query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/tools/skillgen/expected/graphify__skills__vscode__references__query.md b/tools/skillgen/expected/graphify__skills__vscode__references__query.md index 25b826f8e..cb93304a2 100644 --- a/tools/skillgen/expected/graphify__skills__vscode__references__query.md +++ b/tools/skillgen/expected/graphify__skills__vscode__references__query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/tools/skillgen/expected/graphify__skills__windows__references__query.md b/tools/skillgen/expected/graphify__skills__windows__references__query.md index 25b826f8e..cb93304a2 100644 --- a/tools/skillgen/expected/graphify__skills__windows__references__query.md +++ b/tools/skillgen/expected/graphify__skills__windows__references__query.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/tools/skillgen/fragments/query-stub/cli-inline.md b/tools/skillgen/fragments/query-stub/cli-inline.md deleted file mode 100644 index cd080864e..000000000 --- a/tools/skillgen/fragments/query-stub/cli-inline.md +++ /dev/null @@ -1,7 +0,0 @@ -When `graphify-out/graph.json` already exists and the user asks a question about the corpus, answer from the graph rather than rebuilding it: - -```bash -graphify query "" -``` - -If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. diff --git a/tools/skillgen/fragments/query-stub/cli.md b/tools/skillgen/fragments/query-stub/cli.md deleted file mode 100644 index 3690f43c1..000000000 --- a/tools/skillgen/fragments/query-stub/cli.md +++ /dev/null @@ -1,7 +0,0 @@ -When `graphify-out/graph.json` already exists and the user asks a question about the corpus, run the query directly: - -```bash -graphify query "" -``` - -Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. For that vocab-expansion step, the `--dfs` / `--budget` modes, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. diff --git a/tools/skillgen/fragments/query-stub/default.md b/tools/skillgen/fragments/query-stub/default.md new file mode 100644 index 000000000..696796ec5 --- /dev/null +++ b/tools/skillgen/fragments/query-stub/default.md @@ -0,0 +1,7 @@ +When `graphify-out/graph.json` already exists and the user asks a question about the corpus, answer from the graph rather than rebuilding it: + +```bash +graphify query "" +``` + +Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`. diff --git a/tools/skillgen/fragments/references/query/cli.md b/tools/skillgen/fragments/references/query/cli.md deleted file mode 100644 index b08289e1b..000000000 --- a/tools/skillgen/fragments/references/query/cli.md +++ /dev/null @@ -1,103 +0,0 @@ -# graphify reference: query, path, explain - -Load this when the user asks a question against an existing graph, or runs `/graphify path` or `/graphify explain`. The core's query stub points here for the full traversal flow. - -Two traversal modes - choose based on the question: - -| Mode | Flag | Best for | -|------|------|----------| -| BFS (default) | _(none)_ | "What is X connected to?" - broad context, nearest neighbors first | -| DFS | `--dfs` | "How does X reach Y?" - trace a specific chain or dependency path | - -### Step 0 — Constrained query expansion (REQUIRED before traversal) - -graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. - -Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: - -1. Extract the token vocabulary from node labels: -```bash -$(cat graphify-out/.graphify_python) -c " -import json, re -from pathlib import Path -data = json.loads(Path('graphify-out/graph.json').read_text()) -vocab = set() -for n in data['nodes']: - for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): - parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] - for p in parts: - t = p.lower() - if 3 <= len(t) <= 30: - vocab.add(t) -Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) -print(f'vocab: {len(vocab)} tokens') -" -``` - -2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: - - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. - - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. - - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. - - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. - - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. - -3. Print the selection explicitly to the user before running the query, so the expansion is auditable: -``` -Query expanded to (from graph vocab, N tokens): [token1, token2, ...] -``` -If the list is empty, say so plainly and stop — do not proceed to traversal. - -### Step 1 — Traversal - -Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) - -```bash -graphify query "QUESTION" -# or: graphify query "QUESTION" --dfs --budget 3000 -``` - -Answer using **only** what the graph output contains. Quote `source_location` when citing a specific fact. If the graph lacks enough information, say so - do not hallucinate edges. - -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: - -```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 -``` - -Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. - ---- - -## For /graphify path - -Find the shortest path between two named concepts in the graph. - -```bash -graphify path "NODE_A" "NODE_B" -``` - -Replace `NODE_A` and `NODE_B` with the actual concept names. Then explain the path in plain language - what each hop means, why it's significant. - -After writing the explanation, save it back: - -```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Path from NODE_A to NODE_B" --answer "ANSWER" --type path_query --nodes NODE_A NODE_B -``` - ---- - -## For /graphify explain - -Give a plain-language explanation of a single node - everything connected to it. - -```bash -graphify explain "NODE_NAME" -``` - -Replace `NODE_NAME` with the concept the user asked about. Then write a 3-5 sentence explanation of what this node is, what it connects to, and why those connections are significant. Use the source locations as citations. - -After writing the explanation, save it back: - -```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME -``` diff --git a/tools/skillgen/fragments/references/query/cli-inline.md b/tools/skillgen/fragments/references/query/default.md similarity index 68% rename from tools/skillgen/fragments/references/query/cli-inline.md rename to tools/skillgen/fragments/references/query/default.md index 25b826f8e..cb93304a2 100644 --- a/tools/skillgen/fragments/references/query/cli-inline.md +++ b/tools/skillgen/fragments/references/query/default.md @@ -20,6 +20,48 @@ if not Path('graphify-out/graph.json').exists(): ``` If it fails, stop and tell the user to run `/graphify ` first. +### Step 0 — Constrained query expansion (REQUIRED before traversal) + +graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise. + +Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first: + +1. Extract the token vocabulary from node labels: +```bash +$(cat graphify-out/.graphify_python) -c " +import json, re +from pathlib import Path +data = json.loads(Path('graphify-out/graph.json').read_text()) +vocab = set() +for n in data['nodes']: + for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE): + parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c] + for p in parts: + t = p.lower() + if 3 <= len(t) <= 30: + vocab.add(t) +Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab))) +print(f'vocab: {len(vocab)} tokens') +" +``` + +2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints: + - You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens. + - If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory. + - If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search. + - Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab. + - Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present. + +3. Print the selection explicitly to the user before running the query, so the expansion is auditable: +``` +Query expanded to (from graph vocab, N tokens): [token1, token2, ...] +``` +If the list is empty, say so plainly and stop — do not proceed to traversal. + +### Step 1 — Traversal + +Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.) + Prefer the CLI when it is installed: ```bash graphify query "QUESTION" @@ -28,7 +70,7 @@ graphify query "QUESTION" If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline: -1. Find the 1-3 nodes whose label best matches key terms in the question. +1. Find the 1-3 nodes whose label best matches the expanded tokens. 2. Run the appropriate traversal from each starting node. 3. Read the subgraph - node labels, edge relations, confidence tags, source locations. 4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact. @@ -121,21 +163,27 @@ print(output) " ``` -Replace `QUESTION` with the user's actual question, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above. +Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries: +After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 ``` -Replace `QUESTION` with the user's verbatim question, `ANSWER` with your full answer text, and the node list with the labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. +Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. --- ## For /graphify path -Find the shortest path between two named concepts in the graph. +Find the shortest path between two named concepts in the graph. Prefer the CLI when installed: + +```bash +graphify path "NODE_A" "NODE_B" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " @@ -197,7 +245,13 @@ $(cat graphify-out/.graphify_python) -m graphify save-result --question "Path fr ## For /graphify explain -Give a plain-language explanation of a single node - everything connected to it. +Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed: + +```bash +graphify explain "NODE_NAME" +``` + +If the CLI is unavailable, run it inline: ```bash $(cat graphify-out/.graphify_python) -c " diff --git a/tools/skillgen/gen.py b/tools/skillgen/gen.py index 245ae0d06..32e0c6e56 100644 --- a/tools/skillgen/gen.py +++ b/tools/skillgen/gen.py @@ -91,10 +91,9 @@ ALWAYS_ON_BLOCKS = { ENUM_VALUES = "code|document|paper|image|rationale|concept" ENUM_PROSE = "`code`, `document`, `paper`, `image`, `rationale`, `concept`" -# The eight on-demand references every split platform renders. Five are -# shared-verbatim; three (extraction-spec, query, hooks) are variant-selected and -# their source is resolved per platform from the extraction/query_variant/ -# hooks_variant fields. +# The eight on-demand references every split platform renders. Six are +# shared-verbatim; two (extraction-spec, hooks) are variant-selected and resolved +# per platform from the extraction/hooks_variant fields. _SHARED_REFERENCES = { "update": "references/shared/update.md", "exports": "references/shared/exports.md", @@ -106,10 +105,13 @@ _EXTRACTION_SOURCE = { "verbose": "references/shared/extraction-spec.md", "compact": "references/shared/extraction-spec-compact.md", } -_QUERY_SOURCE = { - "cli": "references/query/cli.md", - "cli-inline": "references/query/cli-inline.md", -} +# Single unified query reference + stub: superior vocab-expansion (Step 0) plus +# CLI traversal plus inline NetworkX fallback, shipped to every platform. The +# capabilities used to be split across cli.md / cli-inline.md so no platform got +# both — Claude had expansion but no fallback, the rest had the fallback but the +# weaker matcher (#1325). +_QUERY_REFERENCE = "references/query/default.md" +_QUERY_STUB = "query-stub/default.md" # The hooks reference is host-flavored. Most hosts read CLAUDE.md and wire # always-on via `graphify claude install` (the shared body). The agents-md hosts # (trae, trae-cn, amp) read AGENTS.md and wire it via `graphify install`. @@ -157,17 +159,6 @@ _HOOKS_TARGET = { "agents-md": "AGENTS.md", } -# The v8 claude monolith (the coverage baseline) carries claude's CLI + vocab- -# expansion query design. These two sub-headings are private to that design -# (Decision C). A cli-inline platform's query reference uses the NetworkX- -# fallback traversal instead and has no vocab-expansion step, so these headings -# are legitimately absent there and must not count as a coverage hole. The -# top-level query/path/explain headings are still required everywhere. -_CLI_ONLY_QUERY_HEADINGS = { - "### Step 0 — Constrained query expansion (REQUIRED before traversal)", - "### Step 1 — Traversal", -} - # Allowlist for the per-host coverage audit (waves 2-3 consolidations). # # The lean core is one shared template across every split host, so a few v8 @@ -246,7 +237,6 @@ class Platform: description: str | None = None trigger: str | None = None # removed — not part of Agent Skills spec (#1180) dispatch: str | None = None - query_variant: str = "cli-inline" extraction: str = "verbose" shell: str = "posix" claude_md: bool = False @@ -260,7 +250,7 @@ class Platform: """Resolve the rendered-name -> source-fragment map for this split platform.""" refs = dict(_SHARED_REFERENCES) refs["extraction-spec"] = _EXTRACTION_SOURCE[self.extraction] - refs["query"] = _QUERY_SOURCE[self.query_variant] + refs["query"] = _QUERY_REFERENCE refs["hooks"] = _HOOKS_SOURCE[self.hooks_variant] return refs @@ -285,7 +275,6 @@ def load_platforms() -> dict[str, Platform]: description=cfg.get("description"), trigger=cfg.get("trigger"), dispatch=cfg.get("dispatch"), - query_variant=cfg.get("query_variant", "cli-inline"), extraction=cfg.get("extraction", "verbose"), shell=cfg.get("shell", "posix"), claude_md=bool(cfg.get("claude_md", False)), @@ -339,7 +328,7 @@ def _render_core(platform: Platform) -> str: install = _read_fragment(f"shell/{platform.shell}.md").rstrip("\n") dispatch = _read_fragment(f"dispatch/{platform.dispatch}.md").rstrip("\n") - query_stub = _read_fragment(f"query-stub/{platform.query_variant}.md").rstrip("\n") + query_stub = _read_fragment(_QUERY_STUB).rstrip("\n") if platform.extra_sections: extra = "".join( @@ -590,9 +579,7 @@ def audit_coverage(platform: Platform) -> list[str]: every host is checked against claude's monolith, so each host is checked against itself. - Three classes of v8 heading are exempt and documented as deltas, not holes: - - the lean core's query stub re-homes claude's CLI vocab-expansion - sub-headings into the query reference (_CLI_ONLY_QUERY_HEADINGS); + v8 headings are exempt and documented as deltas, not holes: - waves 2-3 consolidations (the lean "## What graphify is for" intro and the per-host re-homed step/part headings on the minimal kilo/vscode bodies), tracked in the audit allowlist. @@ -619,10 +606,6 @@ def audit_coverage(platform: Platform) -> list[str]: # Allowlisted consolidations + the lean intro are intentional deltas. if h in allowlist: continue - # Query sub-headings that are private to the CLI + vocab-expansion design - # do not appear in a cli-inline platform's query reference (Decision C). - if platform.query_variant != "cli" and h in _CLI_ONLY_QUERY_HEADINGS: - continue homes = [] if h in core_headings: homes.append("core") diff --git a/tools/skillgen/platforms.toml b/tools/skillgen/platforms.toml index 49f702dc6..1090a58a9 100644 --- a/tools/skillgen/platforms.toml +++ b/tools/skillgen/platforms.toml @@ -19,8 +19,6 @@ # name frontmatter name (default "graphify"; graphify-windows for windows). # description frontmatter description, PRESERVED VERBATIM per platform. Required. # dispatch Part-B dispatch fragment basename under fragments/dispatch/. -# query_variant cli | cli-inline. Selects both the core query stub and the -# query reference body. # extraction verbose | compact. Selects the extraction-spec reference body. # shell posix | powershell. Selects the Step 1 install fragment. # claude_md bool. Whether the host gets an always-on block (installer-side). @@ -41,7 +39,6 @@ skill_dst = "graphify/skill.md" refs_dst = "graphify/skills/claude/references" description = "Use for any question about a codebase, its architecture, file relationships, or project content — especially when graphify-out/ exists, where the question should be treated as a graphify query first. Turns any input (code, docs, papers, images, videos) into a persistent knowledge graph with god nodes, community detection, and query/path/explain tools." dispatch = "agent-tool-disk" -query_variant = "cli" extraction = "verbose" shell = "posix" claude_md = true @@ -53,7 +50,6 @@ skill_dst = "graphify/skill-codex.md" refs_dst = "graphify/skills/codex/references" description = "Use for any question about a codebase, its architecture, file relationships, or project content — especially when graphify-out/ exists, where the question should be treated as a graphify query first. Turns any input (code, docs, papers, images, videos) into a persistent knowledge graph with god nodes, community detection, and query/path/explain tools." dispatch = "codex-agenttask" -query_variant = "cli-inline" extraction = "compact" shell = "posix" @@ -65,7 +61,6 @@ refs_dst = "graphify/skills/windows/references" name = "graphify-windows" description = "Use for any question about a codebase, its architecture, file relationships, or project content — especially when graphify-out/ exists, where the question should be treated as a graphify query first. Turns any input (code, docs, papers, images, videos) into a persistent knowledge graph with god nodes, community detection, and query/path/explain tools." dispatch = "agent-tool-disk-powershell" -query_variant = "cli-inline" extraction = "verbose" shell = "powershell" claude_md = true @@ -78,7 +73,6 @@ skill_dst = "graphify/skill-opencode.md" refs_dst = "graphify/skills/opencode/references" description = "Use for any question about a codebase, its architecture, file relationships, or project content — especially when graphify-out/ exists, where the question should be treated as a graphify query first. Turns any input (code, docs, papers, images, videos) into a persistent knowledge graph with god nodes, community detection, and query/path/explain tools." dispatch = "opencode-mention" -query_variant = "cli-inline" extraction = "verbose" [platform.kilo] @@ -88,7 +82,6 @@ skill_dst = "graphify/skill-kilo.md" refs_dst = "graphify/skills/kilo/references" description = "Use for any question about a codebase, its architecture, file relationships, or project content — especially when graphify-out/ exists, where the question should be treated as a graphify query first. Turns any input (code, docs, papers, images, videos) into a persistent knowledge graph with god nodes, community detection, and query/path/explain tools." dispatch = "agent-tool-disk" -query_variant = "cli-inline" extraction = "verbose" extra_sections = ["kilo-rules"] @@ -99,7 +92,6 @@ skill_dst = "graphify/skill-copilot.md" refs_dst = "graphify/skills/copilot/references" description = "Use for any question about a codebase, its architecture, file relationships, or project content — especially when graphify-out/ exists, where the question should be treated as a graphify query first. Turns any input (code, docs, papers, images, videos) into a persistent knowledge graph with god nodes, community detection, and query/path/explain tools." dispatch = "agent-tool-disk" -query_variant = "cli-inline" extraction = "verbose" [platform.claw] @@ -109,7 +101,6 @@ skill_dst = "graphify/skill-claw.md" refs_dst = "graphify/skills/claw/references" description = "Use for any question about a codebase, its architecture, file relationships, or project content — especially when graphify-out/ exists, where the question should be treated as a graphify query first. Turns any input (code, docs, papers, images, videos) into a persistent knowledge graph with god nodes, community detection, and query/path/explain tools." dispatch = "agent-tool-disk" -query_variant = "cli-inline" extraction = "compact" [platform.droid] @@ -119,7 +110,6 @@ skill_dst = "graphify/skill-droid.md" refs_dst = "graphify/skills/droid/references" description = "Use for any question about a codebase, its architecture, file relationships, or project content — especially when graphify-out/ exists, where the question should be treated as a graphify query first. Turns any input (code, docs, papers, images, videos) into a persistent knowledge graph with god nodes, community detection, and query/path/explain tools." dispatch = "task-tool-disk" -query_variant = "cli-inline" extraction = "verbose" [platform.amp] @@ -133,7 +123,6 @@ description = "Use for any question about a codebase, its architecture, file rel # caveat). It reads AGENTS.md and wires always-on via `graphify amp install`, so # it uses the agents-md hooks variant with amp's own (caveat-free) wording. dispatch = "task-tool-disk" -query_variant = "cli-inline" extraction = "verbose" hooks_variant = "agents-md" @@ -147,7 +136,6 @@ description = "Use for any question about a codebase, its architecture, file rel # block carries the no-PreToolUse caveat and the hooks reference wires # `graphify trae install` -> AGENTS.md. trae-cn reuses this same bundle. dispatch = "task-tool-disk-trae" -query_variant = "cli-inline" extraction = "verbose" hooks_variant = "agents-md" @@ -158,7 +146,6 @@ skill_dst = "graphify/skill-kiro.md" refs_dst = "graphify/skills/kiro/references" description = "Use for any question about a codebase, its architecture, file relationships, or project content — especially when graphify-out/ exists, where the question should be treated as a graphify query first. Turns any input (code, docs, papers, images, videos) into a persistent knowledge graph with god nodes, community detection, and query/path/explain tools." dispatch = "agent-tool-disk" -query_variant = "cli-inline" extraction = "compact" [platform.pi] @@ -168,7 +155,6 @@ skill_dst = "graphify/skill-pi.md" refs_dst = "graphify/skills/pi/references" description = "Use for any question about a codebase, its architecture, file relationships, or project content — especially when graphify-out/ exists, where the question should be treated as a graphify query first. Turns any input (code, docs, papers, images, videos) into a persistent knowledge graph with god nodes, community detection, and query/path/explain tools." dispatch = "agent-tool-disk" -query_variant = "cli-inline" extraction = "compact" [platform.vscode] @@ -178,7 +164,6 @@ skill_dst = "graphify/skill-vscode.md" refs_dst = "graphify/skills/vscode/references" description = "Use for any question about a codebase, its architecture, file relationships, or project content — especially when graphify-out/ exists, where the question should be treated as a graphify query first. Turns any input (code, docs, papers, images, videos) into a persistent knowledge graph with god nodes, community detection, and query/path/explain tools." dispatch = "manual-paste" -query_variant = "cli-inline" extraction = "verbose" # Monoliths: rendered inline as a single body, no split, no references/. The