diff --git a/graphify/skills/agents/references/add-watch.md b/graphify/skills/agents/references/add-watch.md index 77844343e1..6da691efd0 100644 --- a/graphify/skills/agents/references/add-watch.md +++ b/graphify/skills/agents/references/add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/graphify/skills/agents/references/exports.md b/graphify/skills/agents/references/exports.md index 242ff868e0..27b031e58c 100644 --- a/graphify/skills/agents/references/exports.md +++ b/graphify/skills/agents/references/exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/graphify/skills/agents/references/query.md b/graphify/skills/agents/references/query.md index 56565eb782..a13b53bde4 100644 --- a/graphify/skills/agents/references/query.md +++ b/graphify/skills/agents/references/query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(cat graphify-out/.graphify_python)" -m graphify save-result --question 'Explain NODE_NAME' --answer 'ANSWER' --type explain --nodes 'NODE_NAME' ``` diff --git a/graphify/skills/agents/references/transcribe.md b/graphify/skills/agents/references/transcribe.md index b967f83799..5fb6296821 100644 --- a/graphify/skills/agents/references/transcribe.md +++ b/graphify/skills/agents/references/transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/graphify/skills/agents/references/update.md b/graphify/skills/agents/references/update.md index 3632fd4126..493323c3fe 100644 --- a/graphify/skills/agents/references/update.md +++ b/graphify/skills/agents/references/update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/graphify/skills/amp/references/add-watch.md b/graphify/skills/amp/references/add-watch.md index 77844343e1..6da691efd0 100644 --- a/graphify/skills/amp/references/add-watch.md +++ b/graphify/skills/amp/references/add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/graphify/skills/amp/references/exports.md b/graphify/skills/amp/references/exports.md index 242ff868e0..27b031e58c 100644 --- a/graphify/skills/amp/references/exports.md +++ b/graphify/skills/amp/references/exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/graphify/skills/amp/references/query.md b/graphify/skills/amp/references/query.md index 56565eb782..a13b53bde4 100644 --- a/graphify/skills/amp/references/query.md +++ b/graphify/skills/amp/references/query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(cat graphify-out/.graphify_python)" -m graphify save-result --question 'Explain NODE_NAME' --answer 'ANSWER' --type explain --nodes 'NODE_NAME' ``` diff --git a/graphify/skills/amp/references/transcribe.md b/graphify/skills/amp/references/transcribe.md index b967f83799..5fb6296821 100644 --- a/graphify/skills/amp/references/transcribe.md +++ b/graphify/skills/amp/references/transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/graphify/skills/amp/references/update.md b/graphify/skills/amp/references/update.md index 3632fd4126..493323c3fe 100644 --- a/graphify/skills/amp/references/update.md +++ b/graphify/skills/amp/references/update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/graphify/skills/claude/references/add-watch.md b/graphify/skills/claude/references/add-watch.md index 77844343e1..6da691efd0 100644 --- a/graphify/skills/claude/references/add-watch.md +++ b/graphify/skills/claude/references/add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/graphify/skills/claude/references/exports.md b/graphify/skills/claude/references/exports.md index 242ff868e0..27b031e58c 100644 --- a/graphify/skills/claude/references/exports.md +++ b/graphify/skills/claude/references/exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/graphify/skills/claude/references/query.md b/graphify/skills/claude/references/query.md index 56565eb782..a13b53bde4 100644 --- a/graphify/skills/claude/references/query.md +++ b/graphify/skills/claude/references/query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(cat graphify-out/.graphify_python)" -m graphify save-result --question 'Explain NODE_NAME' --answer 'ANSWER' --type explain --nodes 'NODE_NAME' ``` diff --git a/graphify/skills/claude/references/transcribe.md b/graphify/skills/claude/references/transcribe.md index b967f83799..5fb6296821 100644 --- a/graphify/skills/claude/references/transcribe.md +++ b/graphify/skills/claude/references/transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/graphify/skills/claude/references/update.md b/graphify/skills/claude/references/update.md index 3632fd4126..493323c3fe 100644 --- a/graphify/skills/claude/references/update.md +++ b/graphify/skills/claude/references/update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/graphify/skills/claw/references/add-watch.md b/graphify/skills/claw/references/add-watch.md index 77844343e1..6da691efd0 100644 --- a/graphify/skills/claw/references/add-watch.md +++ b/graphify/skills/claw/references/add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/graphify/skills/claw/references/exports.md b/graphify/skills/claw/references/exports.md index 242ff868e0..27b031e58c 100644 --- a/graphify/skills/claw/references/exports.md +++ b/graphify/skills/claw/references/exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/graphify/skills/claw/references/query.md b/graphify/skills/claw/references/query.md index 56565eb782..a13b53bde4 100644 --- a/graphify/skills/claw/references/query.md +++ b/graphify/skills/claw/references/query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(cat graphify-out/.graphify_python)" -m graphify save-result --question 'Explain NODE_NAME' --answer 'ANSWER' --type explain --nodes 'NODE_NAME' ``` diff --git a/graphify/skills/claw/references/transcribe.md b/graphify/skills/claw/references/transcribe.md index b967f83799..5fb6296821 100644 --- a/graphify/skills/claw/references/transcribe.md +++ b/graphify/skills/claw/references/transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/graphify/skills/claw/references/update.md b/graphify/skills/claw/references/update.md index 3632fd4126..493323c3fe 100644 --- a/graphify/skills/claw/references/update.md +++ b/graphify/skills/claw/references/update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/graphify/skills/codex/references/add-watch.md b/graphify/skills/codex/references/add-watch.md index 77844343e1..6da691efd0 100644 --- a/graphify/skills/codex/references/add-watch.md +++ b/graphify/skills/codex/references/add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/graphify/skills/codex/references/exports.md b/graphify/skills/codex/references/exports.md index 242ff868e0..27b031e58c 100644 --- a/graphify/skills/codex/references/exports.md +++ b/graphify/skills/codex/references/exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/graphify/skills/codex/references/query.md b/graphify/skills/codex/references/query.md index 56565eb782..a13b53bde4 100644 --- a/graphify/skills/codex/references/query.md +++ b/graphify/skills/codex/references/query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(cat graphify-out/.graphify_python)" -m graphify save-result --question 'Explain NODE_NAME' --answer 'ANSWER' --type explain --nodes 'NODE_NAME' ``` diff --git a/graphify/skills/codex/references/transcribe.md b/graphify/skills/codex/references/transcribe.md index b967f83799..5fb6296821 100644 --- a/graphify/skills/codex/references/transcribe.md +++ b/graphify/skills/codex/references/transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/graphify/skills/codex/references/update.md b/graphify/skills/codex/references/update.md index 3632fd4126..493323c3fe 100644 --- a/graphify/skills/codex/references/update.md +++ b/graphify/skills/codex/references/update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/graphify/skills/copilot/references/add-watch.md b/graphify/skills/copilot/references/add-watch.md index 77844343e1..6da691efd0 100644 --- a/graphify/skills/copilot/references/add-watch.md +++ b/graphify/skills/copilot/references/add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/graphify/skills/copilot/references/exports.md b/graphify/skills/copilot/references/exports.md index 242ff868e0..27b031e58c 100644 --- a/graphify/skills/copilot/references/exports.md +++ b/graphify/skills/copilot/references/exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/graphify/skills/copilot/references/query.md b/graphify/skills/copilot/references/query.md index 56565eb782..a13b53bde4 100644 --- a/graphify/skills/copilot/references/query.md +++ b/graphify/skills/copilot/references/query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(cat graphify-out/.graphify_python)" -m graphify save-result --question 'Explain NODE_NAME' --answer 'ANSWER' --type explain --nodes 'NODE_NAME' ``` diff --git a/graphify/skills/copilot/references/transcribe.md b/graphify/skills/copilot/references/transcribe.md index b967f83799..5fb6296821 100644 --- a/graphify/skills/copilot/references/transcribe.md +++ b/graphify/skills/copilot/references/transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/graphify/skills/copilot/references/update.md b/graphify/skills/copilot/references/update.md index 3632fd4126..493323c3fe 100644 --- a/graphify/skills/copilot/references/update.md +++ b/graphify/skills/copilot/references/update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/graphify/skills/droid/references/add-watch.md b/graphify/skills/droid/references/add-watch.md index 77844343e1..6da691efd0 100644 --- a/graphify/skills/droid/references/add-watch.md +++ b/graphify/skills/droid/references/add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/graphify/skills/droid/references/exports.md b/graphify/skills/droid/references/exports.md index 242ff868e0..27b031e58c 100644 --- a/graphify/skills/droid/references/exports.md +++ b/graphify/skills/droid/references/exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/graphify/skills/droid/references/query.md b/graphify/skills/droid/references/query.md index 56565eb782..a13b53bde4 100644 --- a/graphify/skills/droid/references/query.md +++ b/graphify/skills/droid/references/query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(cat graphify-out/.graphify_python)" -m graphify save-result --question 'Explain NODE_NAME' --answer 'ANSWER' --type explain --nodes 'NODE_NAME' ``` diff --git a/graphify/skills/droid/references/transcribe.md b/graphify/skills/droid/references/transcribe.md index b967f83799..5fb6296821 100644 --- a/graphify/skills/droid/references/transcribe.md +++ b/graphify/skills/droid/references/transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/graphify/skills/droid/references/update.md b/graphify/skills/droid/references/update.md index 3632fd4126..493323c3fe 100644 --- a/graphify/skills/droid/references/update.md +++ b/graphify/skills/droid/references/update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/graphify/skills/kilo/references/add-watch.md b/graphify/skills/kilo/references/add-watch.md index 77844343e1..6da691efd0 100644 --- a/graphify/skills/kilo/references/add-watch.md +++ b/graphify/skills/kilo/references/add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/graphify/skills/kilo/references/exports.md b/graphify/skills/kilo/references/exports.md index 242ff868e0..27b031e58c 100644 --- a/graphify/skills/kilo/references/exports.md +++ b/graphify/skills/kilo/references/exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/graphify/skills/kilo/references/query.md b/graphify/skills/kilo/references/query.md index 56565eb782..a13b53bde4 100644 --- a/graphify/skills/kilo/references/query.md +++ b/graphify/skills/kilo/references/query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(cat graphify-out/.graphify_python)" -m graphify save-result --question 'Explain NODE_NAME' --answer 'ANSWER' --type explain --nodes 'NODE_NAME' ``` diff --git a/graphify/skills/kilo/references/transcribe.md b/graphify/skills/kilo/references/transcribe.md index b967f83799..5fb6296821 100644 --- a/graphify/skills/kilo/references/transcribe.md +++ b/graphify/skills/kilo/references/transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/graphify/skills/kilo/references/update.md b/graphify/skills/kilo/references/update.md index 3632fd4126..493323c3fe 100644 --- a/graphify/skills/kilo/references/update.md +++ b/graphify/skills/kilo/references/update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/graphify/skills/kiro/references/add-watch.md b/graphify/skills/kiro/references/add-watch.md index 77844343e1..6da691efd0 100644 --- a/graphify/skills/kiro/references/add-watch.md +++ b/graphify/skills/kiro/references/add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/graphify/skills/kiro/references/exports.md b/graphify/skills/kiro/references/exports.md index 242ff868e0..27b031e58c 100644 --- a/graphify/skills/kiro/references/exports.md +++ b/graphify/skills/kiro/references/exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/graphify/skills/kiro/references/query.md b/graphify/skills/kiro/references/query.md index 56565eb782..a13b53bde4 100644 --- a/graphify/skills/kiro/references/query.md +++ b/graphify/skills/kiro/references/query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(cat graphify-out/.graphify_python)" -m graphify save-result --question 'Explain NODE_NAME' --answer 'ANSWER' --type explain --nodes 'NODE_NAME' ``` diff --git a/graphify/skills/kiro/references/transcribe.md b/graphify/skills/kiro/references/transcribe.md index b967f83799..5fb6296821 100644 --- a/graphify/skills/kiro/references/transcribe.md +++ b/graphify/skills/kiro/references/transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/graphify/skills/kiro/references/update.md b/graphify/skills/kiro/references/update.md index 3632fd4126..493323c3fe 100644 --- a/graphify/skills/kiro/references/update.md +++ b/graphify/skills/kiro/references/update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/graphify/skills/opencode/references/add-watch.md b/graphify/skills/opencode/references/add-watch.md index 77844343e1..6da691efd0 100644 --- a/graphify/skills/opencode/references/add-watch.md +++ b/graphify/skills/opencode/references/add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/graphify/skills/opencode/references/exports.md b/graphify/skills/opencode/references/exports.md index 242ff868e0..27b031e58c 100644 --- a/graphify/skills/opencode/references/exports.md +++ b/graphify/skills/opencode/references/exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/graphify/skills/opencode/references/query.md b/graphify/skills/opencode/references/query.md index 56565eb782..a13b53bde4 100644 --- a/graphify/skills/opencode/references/query.md +++ b/graphify/skills/opencode/references/query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(cat graphify-out/.graphify_python)" -m graphify save-result --question 'Explain NODE_NAME' --answer 'ANSWER' --type explain --nodes 'NODE_NAME' ``` diff --git a/graphify/skills/opencode/references/transcribe.md b/graphify/skills/opencode/references/transcribe.md index b967f83799..5fb6296821 100644 --- a/graphify/skills/opencode/references/transcribe.md +++ b/graphify/skills/opencode/references/transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/graphify/skills/opencode/references/update.md b/graphify/skills/opencode/references/update.md index 3632fd4126..493323c3fe 100644 --- a/graphify/skills/opencode/references/update.md +++ b/graphify/skills/opencode/references/update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/graphify/skills/pi/references/add-watch.md b/graphify/skills/pi/references/add-watch.md index 77844343e1..6da691efd0 100644 --- a/graphify/skills/pi/references/add-watch.md +++ b/graphify/skills/pi/references/add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/graphify/skills/pi/references/exports.md b/graphify/skills/pi/references/exports.md index 242ff868e0..27b031e58c 100644 --- a/graphify/skills/pi/references/exports.md +++ b/graphify/skills/pi/references/exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/graphify/skills/pi/references/query.md b/graphify/skills/pi/references/query.md index 56565eb782..a13b53bde4 100644 --- a/graphify/skills/pi/references/query.md +++ b/graphify/skills/pi/references/query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(cat graphify-out/.graphify_python)" -m graphify save-result --question 'Explain NODE_NAME' --answer 'ANSWER' --type explain --nodes 'NODE_NAME' ``` diff --git a/graphify/skills/pi/references/transcribe.md b/graphify/skills/pi/references/transcribe.md index b967f83799..5fb6296821 100644 --- a/graphify/skills/pi/references/transcribe.md +++ b/graphify/skills/pi/references/transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/graphify/skills/pi/references/update.md b/graphify/skills/pi/references/update.md index 3632fd4126..493323c3fe 100644 --- a/graphify/skills/pi/references/update.md +++ b/graphify/skills/pi/references/update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/graphify/skills/trae/references/add-watch.md b/graphify/skills/trae/references/add-watch.md index 77844343e1..6da691efd0 100644 --- a/graphify/skills/trae/references/add-watch.md +++ b/graphify/skills/trae/references/add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/graphify/skills/trae/references/exports.md b/graphify/skills/trae/references/exports.md index 242ff868e0..27b031e58c 100644 --- a/graphify/skills/trae/references/exports.md +++ b/graphify/skills/trae/references/exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/graphify/skills/trae/references/query.md b/graphify/skills/trae/references/query.md index 56565eb782..a13b53bde4 100644 --- a/graphify/skills/trae/references/query.md +++ b/graphify/skills/trae/references/query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(cat graphify-out/.graphify_python)" -m graphify save-result --question 'Explain NODE_NAME' --answer 'ANSWER' --type explain --nodes 'NODE_NAME' ``` diff --git a/graphify/skills/trae/references/transcribe.md b/graphify/skills/trae/references/transcribe.md index b967f83799..5fb6296821 100644 --- a/graphify/skills/trae/references/transcribe.md +++ b/graphify/skills/trae/references/transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/graphify/skills/trae/references/update.md b/graphify/skills/trae/references/update.md index 3632fd4126..493323c3fe 100644 --- a/graphify/skills/trae/references/update.md +++ b/graphify/skills/trae/references/update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/graphify/skills/vscode/references/add-watch.md b/graphify/skills/vscode/references/add-watch.md index 77844343e1..6da691efd0 100644 --- a/graphify/skills/vscode/references/add-watch.md +++ b/graphify/skills/vscode/references/add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/graphify/skills/vscode/references/exports.md b/graphify/skills/vscode/references/exports.md index 242ff868e0..27b031e58c 100644 --- a/graphify/skills/vscode/references/exports.md +++ b/graphify/skills/vscode/references/exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/graphify/skills/vscode/references/query.md b/graphify/skills/vscode/references/query.md index 56565eb782..a13b53bde4 100644 --- a/graphify/skills/vscode/references/query.md +++ b/graphify/skills/vscode/references/query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(cat graphify-out/.graphify_python)" -m graphify save-result --question 'Explain NODE_NAME' --answer 'ANSWER' --type explain --nodes 'NODE_NAME' ``` diff --git a/graphify/skills/vscode/references/transcribe.md b/graphify/skills/vscode/references/transcribe.md index b967f83799..5fb6296821 100644 --- a/graphify/skills/vscode/references/transcribe.md +++ b/graphify/skills/vscode/references/transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/graphify/skills/vscode/references/update.md b/graphify/skills/vscode/references/update.md index 3632fd4126..493323c3fe 100644 --- a/graphify/skills/vscode/references/update.md +++ b/graphify/skills/vscode/references/update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/graphify/skills/windows/references/add-watch.md b/graphify/skills/windows/references/add-watch.md index 77844343e1..6da691efd0 100644 --- a/graphify/skills/windows/references/add-watch.md +++ b/graphify/skills/windows/references/add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/graphify/skills/windows/references/exports.md b/graphify/skills/windows/references/exports.md index 242ff868e0..27b031e58c 100644 --- a/graphify/skills/windows/references/exports.md +++ b/graphify/skills/windows/references/exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/graphify/skills/windows/references/query.md b/graphify/skills/windows/references/query.md index 56565eb782..a13b53bde4 100644 --- a/graphify/skills/windows/references/query.md +++ b/graphify/skills/windows/references/query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(cat graphify-out/.graphify_python)" -m graphify save-result --question 'Explain NODE_NAME' --answer 'ANSWER' --type explain --nodes 'NODE_NAME' ``` diff --git a/graphify/skills/windows/references/transcribe.md b/graphify/skills/windows/references/transcribe.md index b967f83799..5fb6296821 100644 --- a/graphify/skills/windows/references/transcribe.md +++ b/graphify/skills/windows/references/transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/graphify/skills/windows/references/update.md b/graphify/skills/windows/references/update.md index 3632fd4126..493323c3fe 100644 --- a/graphify/skills/windows/references/update.md +++ b/graphify/skills/windows/references/update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/tools/skillgen/expected/graphify__skills__agents__references__add-watch.md b/tools/skillgen/expected/graphify__skills__agents__references__add-watch.md index 77844343e1..6da691efd0 100644 --- a/tools/skillgen/expected/graphify__skills__agents__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__agents__references__add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/tools/skillgen/expected/graphify__skills__agents__references__exports.md b/tools/skillgen/expected/graphify__skills__agents__references__exports.md index 242ff868e0..27b031e58c 100644 --- a/tools/skillgen/expected/graphify__skills__agents__references__exports.md +++ b/tools/skillgen/expected/graphify__skills__agents__references__exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/tools/skillgen/expected/graphify__skills__agents__references__query.md b/tools/skillgen/expected/graphify__skills__agents__references__query.md index 56565eb782..a13b53bde4 100644 --- a/tools/skillgen/expected/graphify__skills__agents__references__query.md +++ b/tools/skillgen/expected/graphify__skills__agents__references__query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(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/expected/graphify__skills__agents__references__transcribe.md b/tools/skillgen/expected/graphify__skills__agents__references__transcribe.md index b967f83799..5fb6296821 100644 --- a/tools/skillgen/expected/graphify__skills__agents__references__transcribe.md +++ b/tools/skillgen/expected/graphify__skills__agents__references__transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/tools/skillgen/expected/graphify__skills__agents__references__update.md b/tools/skillgen/expected/graphify__skills__agents__references__update.md index 3632fd4126..493323c3fe 100644 --- a/tools/skillgen/expected/graphify__skills__agents__references__update.md +++ b/tools/skillgen/expected/graphify__skills__agents__references__update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/tools/skillgen/expected/graphify__skills__amp__references__add-watch.md b/tools/skillgen/expected/graphify__skills__amp__references__add-watch.md index 77844343e1..6da691efd0 100644 --- a/tools/skillgen/expected/graphify__skills__amp__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__amp__references__add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/tools/skillgen/expected/graphify__skills__amp__references__exports.md b/tools/skillgen/expected/graphify__skills__amp__references__exports.md index 242ff868e0..27b031e58c 100644 --- a/tools/skillgen/expected/graphify__skills__amp__references__exports.md +++ b/tools/skillgen/expected/graphify__skills__amp__references__exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/tools/skillgen/expected/graphify__skills__amp__references__query.md b/tools/skillgen/expected/graphify__skills__amp__references__query.md index 56565eb782..a13b53bde4 100644 --- a/tools/skillgen/expected/graphify__skills__amp__references__query.md +++ b/tools/skillgen/expected/graphify__skills__amp__references__query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(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/expected/graphify__skills__amp__references__transcribe.md b/tools/skillgen/expected/graphify__skills__amp__references__transcribe.md index b967f83799..5fb6296821 100644 --- a/tools/skillgen/expected/graphify__skills__amp__references__transcribe.md +++ b/tools/skillgen/expected/graphify__skills__amp__references__transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/tools/skillgen/expected/graphify__skills__amp__references__update.md b/tools/skillgen/expected/graphify__skills__amp__references__update.md index 3632fd4126..493323c3fe 100644 --- a/tools/skillgen/expected/graphify__skills__amp__references__update.md +++ b/tools/skillgen/expected/graphify__skills__amp__references__update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/tools/skillgen/expected/graphify__skills__claude__references__add-watch.md b/tools/skillgen/expected/graphify__skills__claude__references__add-watch.md index 77844343e1..6da691efd0 100644 --- a/tools/skillgen/expected/graphify__skills__claude__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__claude__references__add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/tools/skillgen/expected/graphify__skills__claude__references__exports.md b/tools/skillgen/expected/graphify__skills__claude__references__exports.md index 242ff868e0..27b031e58c 100644 --- a/tools/skillgen/expected/graphify__skills__claude__references__exports.md +++ b/tools/skillgen/expected/graphify__skills__claude__references__exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/tools/skillgen/expected/graphify__skills__claude__references__query.md b/tools/skillgen/expected/graphify__skills__claude__references__query.md index 56565eb782..a13b53bde4 100644 --- a/tools/skillgen/expected/graphify__skills__claude__references__query.md +++ b/tools/skillgen/expected/graphify__skills__claude__references__query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(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/expected/graphify__skills__claude__references__transcribe.md b/tools/skillgen/expected/graphify__skills__claude__references__transcribe.md index b967f83799..5fb6296821 100644 --- a/tools/skillgen/expected/graphify__skills__claude__references__transcribe.md +++ b/tools/skillgen/expected/graphify__skills__claude__references__transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/tools/skillgen/expected/graphify__skills__claude__references__update.md b/tools/skillgen/expected/graphify__skills__claude__references__update.md index 3632fd4126..493323c3fe 100644 --- a/tools/skillgen/expected/graphify__skills__claude__references__update.md +++ b/tools/skillgen/expected/graphify__skills__claude__references__update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/tools/skillgen/expected/graphify__skills__claw__references__add-watch.md b/tools/skillgen/expected/graphify__skills__claw__references__add-watch.md index 77844343e1..6da691efd0 100644 --- a/tools/skillgen/expected/graphify__skills__claw__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__claw__references__add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/tools/skillgen/expected/graphify__skills__claw__references__exports.md b/tools/skillgen/expected/graphify__skills__claw__references__exports.md index 242ff868e0..27b031e58c 100644 --- a/tools/skillgen/expected/graphify__skills__claw__references__exports.md +++ b/tools/skillgen/expected/graphify__skills__claw__references__exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/tools/skillgen/expected/graphify__skills__claw__references__query.md b/tools/skillgen/expected/graphify__skills__claw__references__query.md index 56565eb782..a13b53bde4 100644 --- a/tools/skillgen/expected/graphify__skills__claw__references__query.md +++ b/tools/skillgen/expected/graphify__skills__claw__references__query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(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/expected/graphify__skills__claw__references__transcribe.md b/tools/skillgen/expected/graphify__skills__claw__references__transcribe.md index b967f83799..5fb6296821 100644 --- a/tools/skillgen/expected/graphify__skills__claw__references__transcribe.md +++ b/tools/skillgen/expected/graphify__skills__claw__references__transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/tools/skillgen/expected/graphify__skills__claw__references__update.md b/tools/skillgen/expected/graphify__skills__claw__references__update.md index 3632fd4126..493323c3fe 100644 --- a/tools/skillgen/expected/graphify__skills__claw__references__update.md +++ b/tools/skillgen/expected/graphify__skills__claw__references__update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/tools/skillgen/expected/graphify__skills__codex__references__add-watch.md b/tools/skillgen/expected/graphify__skills__codex__references__add-watch.md index 77844343e1..6da691efd0 100644 --- a/tools/skillgen/expected/graphify__skills__codex__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__codex__references__add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/tools/skillgen/expected/graphify__skills__codex__references__exports.md b/tools/skillgen/expected/graphify__skills__codex__references__exports.md index 242ff868e0..27b031e58c 100644 --- a/tools/skillgen/expected/graphify__skills__codex__references__exports.md +++ b/tools/skillgen/expected/graphify__skills__codex__references__exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/tools/skillgen/expected/graphify__skills__codex__references__query.md b/tools/skillgen/expected/graphify__skills__codex__references__query.md index 56565eb782..a13b53bde4 100644 --- a/tools/skillgen/expected/graphify__skills__codex__references__query.md +++ b/tools/skillgen/expected/graphify__skills__codex__references__query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(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/expected/graphify__skills__codex__references__transcribe.md b/tools/skillgen/expected/graphify__skills__codex__references__transcribe.md index b967f83799..5fb6296821 100644 --- a/tools/skillgen/expected/graphify__skills__codex__references__transcribe.md +++ b/tools/skillgen/expected/graphify__skills__codex__references__transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/tools/skillgen/expected/graphify__skills__codex__references__update.md b/tools/skillgen/expected/graphify__skills__codex__references__update.md index 3632fd4126..493323c3fe 100644 --- a/tools/skillgen/expected/graphify__skills__codex__references__update.md +++ b/tools/skillgen/expected/graphify__skills__codex__references__update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/tools/skillgen/expected/graphify__skills__copilot__references__add-watch.md b/tools/skillgen/expected/graphify__skills__copilot__references__add-watch.md index 77844343e1..6da691efd0 100644 --- a/tools/skillgen/expected/graphify__skills__copilot__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__copilot__references__add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/tools/skillgen/expected/graphify__skills__copilot__references__exports.md b/tools/skillgen/expected/graphify__skills__copilot__references__exports.md index 242ff868e0..27b031e58c 100644 --- a/tools/skillgen/expected/graphify__skills__copilot__references__exports.md +++ b/tools/skillgen/expected/graphify__skills__copilot__references__exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/tools/skillgen/expected/graphify__skills__copilot__references__query.md b/tools/skillgen/expected/graphify__skills__copilot__references__query.md index 56565eb782..a13b53bde4 100644 --- a/tools/skillgen/expected/graphify__skills__copilot__references__query.md +++ b/tools/skillgen/expected/graphify__skills__copilot__references__query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(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/expected/graphify__skills__copilot__references__transcribe.md b/tools/skillgen/expected/graphify__skills__copilot__references__transcribe.md index b967f83799..5fb6296821 100644 --- a/tools/skillgen/expected/graphify__skills__copilot__references__transcribe.md +++ b/tools/skillgen/expected/graphify__skills__copilot__references__transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/tools/skillgen/expected/graphify__skills__copilot__references__update.md b/tools/skillgen/expected/graphify__skills__copilot__references__update.md index 3632fd4126..493323c3fe 100644 --- a/tools/skillgen/expected/graphify__skills__copilot__references__update.md +++ b/tools/skillgen/expected/graphify__skills__copilot__references__update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/tools/skillgen/expected/graphify__skills__droid__references__add-watch.md b/tools/skillgen/expected/graphify__skills__droid__references__add-watch.md index 77844343e1..6da691efd0 100644 --- a/tools/skillgen/expected/graphify__skills__droid__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__droid__references__add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/tools/skillgen/expected/graphify__skills__droid__references__exports.md b/tools/skillgen/expected/graphify__skills__droid__references__exports.md index 242ff868e0..27b031e58c 100644 --- a/tools/skillgen/expected/graphify__skills__droid__references__exports.md +++ b/tools/skillgen/expected/graphify__skills__droid__references__exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/tools/skillgen/expected/graphify__skills__droid__references__query.md b/tools/skillgen/expected/graphify__skills__droid__references__query.md index 56565eb782..a13b53bde4 100644 --- a/tools/skillgen/expected/graphify__skills__droid__references__query.md +++ b/tools/skillgen/expected/graphify__skills__droid__references__query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(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/expected/graphify__skills__droid__references__transcribe.md b/tools/skillgen/expected/graphify__skills__droid__references__transcribe.md index b967f83799..5fb6296821 100644 --- a/tools/skillgen/expected/graphify__skills__droid__references__transcribe.md +++ b/tools/skillgen/expected/graphify__skills__droid__references__transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/tools/skillgen/expected/graphify__skills__droid__references__update.md b/tools/skillgen/expected/graphify__skills__droid__references__update.md index 3632fd4126..493323c3fe 100644 --- a/tools/skillgen/expected/graphify__skills__droid__references__update.md +++ b/tools/skillgen/expected/graphify__skills__droid__references__update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/tools/skillgen/expected/graphify__skills__kilo__references__add-watch.md b/tools/skillgen/expected/graphify__skills__kilo__references__add-watch.md index 77844343e1..6da691efd0 100644 --- a/tools/skillgen/expected/graphify__skills__kilo__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__kilo__references__add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/tools/skillgen/expected/graphify__skills__kilo__references__exports.md b/tools/skillgen/expected/graphify__skills__kilo__references__exports.md index 242ff868e0..27b031e58c 100644 --- a/tools/skillgen/expected/graphify__skills__kilo__references__exports.md +++ b/tools/skillgen/expected/graphify__skills__kilo__references__exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/tools/skillgen/expected/graphify__skills__kilo__references__query.md b/tools/skillgen/expected/graphify__skills__kilo__references__query.md index 56565eb782..a13b53bde4 100644 --- a/tools/skillgen/expected/graphify__skills__kilo__references__query.md +++ b/tools/skillgen/expected/graphify__skills__kilo__references__query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(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/expected/graphify__skills__kilo__references__transcribe.md b/tools/skillgen/expected/graphify__skills__kilo__references__transcribe.md index b967f83799..5fb6296821 100644 --- a/tools/skillgen/expected/graphify__skills__kilo__references__transcribe.md +++ b/tools/skillgen/expected/graphify__skills__kilo__references__transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/tools/skillgen/expected/graphify__skills__kilo__references__update.md b/tools/skillgen/expected/graphify__skills__kilo__references__update.md index 3632fd4126..493323c3fe 100644 --- a/tools/skillgen/expected/graphify__skills__kilo__references__update.md +++ b/tools/skillgen/expected/graphify__skills__kilo__references__update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/tools/skillgen/expected/graphify__skills__kiro__references__add-watch.md b/tools/skillgen/expected/graphify__skills__kiro__references__add-watch.md index 77844343e1..6da691efd0 100644 --- a/tools/skillgen/expected/graphify__skills__kiro__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__kiro__references__add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/tools/skillgen/expected/graphify__skills__kiro__references__exports.md b/tools/skillgen/expected/graphify__skills__kiro__references__exports.md index 242ff868e0..27b031e58c 100644 --- a/tools/skillgen/expected/graphify__skills__kiro__references__exports.md +++ b/tools/skillgen/expected/graphify__skills__kiro__references__exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/tools/skillgen/expected/graphify__skills__kiro__references__query.md b/tools/skillgen/expected/graphify__skills__kiro__references__query.md index 56565eb782..a13b53bde4 100644 --- a/tools/skillgen/expected/graphify__skills__kiro__references__query.md +++ b/tools/skillgen/expected/graphify__skills__kiro__references__query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(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/expected/graphify__skills__kiro__references__transcribe.md b/tools/skillgen/expected/graphify__skills__kiro__references__transcribe.md index b967f83799..5fb6296821 100644 --- a/tools/skillgen/expected/graphify__skills__kiro__references__transcribe.md +++ b/tools/skillgen/expected/graphify__skills__kiro__references__transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/tools/skillgen/expected/graphify__skills__kiro__references__update.md b/tools/skillgen/expected/graphify__skills__kiro__references__update.md index 3632fd4126..493323c3fe 100644 --- a/tools/skillgen/expected/graphify__skills__kiro__references__update.md +++ b/tools/skillgen/expected/graphify__skills__kiro__references__update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/tools/skillgen/expected/graphify__skills__opencode__references__add-watch.md b/tools/skillgen/expected/graphify__skills__opencode__references__add-watch.md index 77844343e1..6da691efd0 100644 --- a/tools/skillgen/expected/graphify__skills__opencode__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__opencode__references__add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/tools/skillgen/expected/graphify__skills__opencode__references__exports.md b/tools/skillgen/expected/graphify__skills__opencode__references__exports.md index 242ff868e0..27b031e58c 100644 --- a/tools/skillgen/expected/graphify__skills__opencode__references__exports.md +++ b/tools/skillgen/expected/graphify__skills__opencode__references__exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/tools/skillgen/expected/graphify__skills__opencode__references__query.md b/tools/skillgen/expected/graphify__skills__opencode__references__query.md index 56565eb782..a13b53bde4 100644 --- a/tools/skillgen/expected/graphify__skills__opencode__references__query.md +++ b/tools/skillgen/expected/graphify__skills__opencode__references__query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(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/expected/graphify__skills__opencode__references__transcribe.md b/tools/skillgen/expected/graphify__skills__opencode__references__transcribe.md index b967f83799..5fb6296821 100644 --- a/tools/skillgen/expected/graphify__skills__opencode__references__transcribe.md +++ b/tools/skillgen/expected/graphify__skills__opencode__references__transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/tools/skillgen/expected/graphify__skills__opencode__references__update.md b/tools/skillgen/expected/graphify__skills__opencode__references__update.md index 3632fd4126..493323c3fe 100644 --- a/tools/skillgen/expected/graphify__skills__opencode__references__update.md +++ b/tools/skillgen/expected/graphify__skills__opencode__references__update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/tools/skillgen/expected/graphify__skills__pi__references__add-watch.md b/tools/skillgen/expected/graphify__skills__pi__references__add-watch.md index 77844343e1..6da691efd0 100644 --- a/tools/skillgen/expected/graphify__skills__pi__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__pi__references__add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/tools/skillgen/expected/graphify__skills__pi__references__exports.md b/tools/skillgen/expected/graphify__skills__pi__references__exports.md index 242ff868e0..27b031e58c 100644 --- a/tools/skillgen/expected/graphify__skills__pi__references__exports.md +++ b/tools/skillgen/expected/graphify__skills__pi__references__exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/tools/skillgen/expected/graphify__skills__pi__references__query.md b/tools/skillgen/expected/graphify__skills__pi__references__query.md index 56565eb782..a13b53bde4 100644 --- a/tools/skillgen/expected/graphify__skills__pi__references__query.md +++ b/tools/skillgen/expected/graphify__skills__pi__references__query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(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/expected/graphify__skills__pi__references__transcribe.md b/tools/skillgen/expected/graphify__skills__pi__references__transcribe.md index b967f83799..5fb6296821 100644 --- a/tools/skillgen/expected/graphify__skills__pi__references__transcribe.md +++ b/tools/skillgen/expected/graphify__skills__pi__references__transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/tools/skillgen/expected/graphify__skills__pi__references__update.md b/tools/skillgen/expected/graphify__skills__pi__references__update.md index 3632fd4126..493323c3fe 100644 --- a/tools/skillgen/expected/graphify__skills__pi__references__update.md +++ b/tools/skillgen/expected/graphify__skills__pi__references__update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/tools/skillgen/expected/graphify__skills__trae__references__add-watch.md b/tools/skillgen/expected/graphify__skills__trae__references__add-watch.md index 77844343e1..6da691efd0 100644 --- a/tools/skillgen/expected/graphify__skills__trae__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__trae__references__add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/tools/skillgen/expected/graphify__skills__trae__references__exports.md b/tools/skillgen/expected/graphify__skills__trae__references__exports.md index 242ff868e0..27b031e58c 100644 --- a/tools/skillgen/expected/graphify__skills__trae__references__exports.md +++ b/tools/skillgen/expected/graphify__skills__trae__references__exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/tools/skillgen/expected/graphify__skills__trae__references__query.md b/tools/skillgen/expected/graphify__skills__trae__references__query.md index 56565eb782..a13b53bde4 100644 --- a/tools/skillgen/expected/graphify__skills__trae__references__query.md +++ b/tools/skillgen/expected/graphify__skills__trae__references__query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(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/expected/graphify__skills__trae__references__transcribe.md b/tools/skillgen/expected/graphify__skills__trae__references__transcribe.md index b967f83799..5fb6296821 100644 --- a/tools/skillgen/expected/graphify__skills__trae__references__transcribe.md +++ b/tools/skillgen/expected/graphify__skills__trae__references__transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/tools/skillgen/expected/graphify__skills__trae__references__update.md b/tools/skillgen/expected/graphify__skills__trae__references__update.md index 3632fd4126..493323c3fe 100644 --- a/tools/skillgen/expected/graphify__skills__trae__references__update.md +++ b/tools/skillgen/expected/graphify__skills__trae__references__update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/tools/skillgen/expected/graphify__skills__vscode__references__add-watch.md b/tools/skillgen/expected/graphify__skills__vscode__references__add-watch.md index 77844343e1..6da691efd0 100644 --- a/tools/skillgen/expected/graphify__skills__vscode__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__vscode__references__add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/tools/skillgen/expected/graphify__skills__vscode__references__exports.md b/tools/skillgen/expected/graphify__skills__vscode__references__exports.md index 242ff868e0..27b031e58c 100644 --- a/tools/skillgen/expected/graphify__skills__vscode__references__exports.md +++ b/tools/skillgen/expected/graphify__skills__vscode__references__exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/tools/skillgen/expected/graphify__skills__vscode__references__query.md b/tools/skillgen/expected/graphify__skills__vscode__references__query.md index 56565eb782..a13b53bde4 100644 --- a/tools/skillgen/expected/graphify__skills__vscode__references__query.md +++ b/tools/skillgen/expected/graphify__skills__vscode__references__query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(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/expected/graphify__skills__vscode__references__transcribe.md b/tools/skillgen/expected/graphify__skills__vscode__references__transcribe.md index b967f83799..5fb6296821 100644 --- a/tools/skillgen/expected/graphify__skills__vscode__references__transcribe.md +++ b/tools/skillgen/expected/graphify__skills__vscode__references__transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/tools/skillgen/expected/graphify__skills__vscode__references__update.md b/tools/skillgen/expected/graphify__skills__vscode__references__update.md index 3632fd4126..493323c3fe 100644 --- a/tools/skillgen/expected/graphify__skills__vscode__references__update.md +++ b/tools/skillgen/expected/graphify__skills__vscode__references__update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/tools/skillgen/expected/graphify__skills__windows__references__add-watch.md b/tools/skillgen/expected/graphify__skills__windows__references__add-watch.md index 77844343e1..6da691efd0 100644 --- a/tools/skillgen/expected/graphify__skills__windows__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__windows__references__add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/tools/skillgen/expected/graphify__skills__windows__references__exports.md b/tools/skillgen/expected/graphify__skills__windows__references__exports.md index 242ff868e0..27b031e58c 100644 --- a/tools/skillgen/expected/graphify__skills__windows__references__exports.md +++ b/tools/skillgen/expected/graphify__skills__windows__references__exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/tools/skillgen/expected/graphify__skills__windows__references__query.md b/tools/skillgen/expected/graphify__skills__windows__references__query.md index 56565eb782..a13b53bde4 100644 --- a/tools/skillgen/expected/graphify__skills__windows__references__query.md +++ b/tools/skillgen/expected/graphify__skills__windows__references__query.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(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/expected/graphify__skills__windows__references__transcribe.md b/tools/skillgen/expected/graphify__skills__windows__references__transcribe.md index b967f83799..5fb6296821 100644 --- a/tools/skillgen/expected/graphify__skills__windows__references__transcribe.md +++ b/tools/skillgen/expected/graphify__skills__windows__references__transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/tools/skillgen/expected/graphify__skills__windows__references__update.md b/tools/skillgen/expected/graphify__skills__windows__references__update.md index 3632fd4126..493323c3fe 100644 --- a/tools/skillgen/expected/graphify__skills__windows__references__update.md +++ b/tools/skillgen/expected/graphify__skills__windows__references__update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json diff --git a/tools/skillgen/fragments/references/query/default.md b/tools/skillgen/fragments/references/query/default.md index 56565eb782..a13b53bde4 100644 --- a/tools/skillgen/fragments/references/query/default.md +++ b/tools/skillgen/fragments/references/query/default.md @@ -11,7 +11,7 @@ Two traversal modes - choose based on the question: First check the graph exists: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(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.') @@ -28,7 +28,7 @@ Fix this **without inventing tokens** by expanding the query against the actual 1. Extract the token vocabulary from node labels: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, re from pathlib import Path data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')) @@ -77,7 +77,7 @@ If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal 5. If the graph lacks enough information, say so - do not hallucinate edges. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from networkx.readwrite import json_graph import networkx as nx @@ -168,10 +168,10 @@ Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs 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 +"$(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. +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. The single quotes around every user-supplied value (including each `--nodes` argument) make the substitution injection-safe: a question like `hello"; rm -rf /` or a node label containing whitespace, single quotes, or shell metacharacters cannot escape the argument. If `ORIGINAL_QUESTION`, `ANSWER`, or any node label contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote) - the escape applies to every substituted value, not just node labels. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph. **Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting): @@ -194,7 +194,7 @@ graphify path "NODE_A" "NODE_B" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -243,10 +243,10 @@ except nx.NodeNotFound as 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```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 +"$(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' ``` --- @@ -262,7 +262,7 @@ graphify explain "NODE_NAME" If the CLI is unavailable, run it inline: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json, sys import networkx as nx from networkx.readwrite import json_graph @@ -304,8 +304,8 @@ for neighbor in G.neighbors(nid): 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: +After writing the explanation, save it back (single quotes around the user-supplied values keep the substitution injection-safe; replace any literal `'` in the substituted text with `'\''`): ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME +"$(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/shared/add-watch.md b/tools/skillgen/fragments/references/shared/add-watch.md index 77844343e1..6da691efd0 100644 --- a/tools/skillgen/fragments/references/shared/add-watch.md +++ b/tools/skillgen/fragments/references/shared/add-watch.md @@ -7,13 +7,17 @@ Load this when the user ran `/graphify add ` or passed `--watch`. Neither i Fetch a URL and add it to the corpus, then update the graph. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys from graphify.ingest import ingest from pathlib import Path +url = sys.argv[1] +author = sys.argv[2] if len(sys.argv) > 2 else '' +contributor = sys.argv[3] if len(sys.argv) > 3 else '' + try: - out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR') + out = ingest(url, Path('./raw'), author=author, contributor=contributor) print(f'Saved to {out}') except ValueError as e: print(f'error: {e}', file=sys.stderr) @@ -21,10 +25,10 @@ except ValueError as e: except RuntimeError as e: print(f'error: {e}', file=sys.stderr) sys.exit(1) -" +" 'URL' 'AUTHOR' 'CONTRIBUTOR' ``` -Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. +Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. Each value is passed as a separate shell argument and read in Python via `sys.argv`, so user-supplied text never lands inside the evaluated Python source - even a URL containing single quotes, double quotes, backticks, or shell metacharacters is treated as a string value, not code. Single-quote each argument in the shell command; if a value contains a literal single quote, replace it with `'\''` (close-quote, escaped-quote, open-quote). If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph. Supported URL types (auto-detected): - YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`) @@ -41,7 +45,7 @@ Supported URL types (auto-detected): Start a background watcher that monitors a folder and auto-updates the graph when files change. ```bash -$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3 +"$(cat graphify-out/.graphify_python)" -m graphify.watch INPUT_PATH --debounce 3 ``` Replace INPUT_PATH with the folder to watch. Behavior depends on what changed: diff --git a/tools/skillgen/fragments/references/shared/exports.md b/tools/skillgen/fragments/references/shared/exports.md index 242ff868e0..27b031e58c 100644 --- a/tools/skillgen/fragments/references/shared/exports.md +++ b/tools/skillgen/fragments/references/shared/exports.md @@ -59,7 +59,7 @@ graphify export graphml ### Step 7d - MCP server (only if --mcp flag) ```bash -$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json +"$(cat graphify-out/.graphify_python)" -m graphify.serve graphify-out/graph.json ``` This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live. diff --git a/tools/skillgen/fragments/references/shared/transcribe.md b/tools/skillgen/fragments/references/shared/transcribe.md index b967f83799..5fb6296821 100644 --- a/tools/skillgen/fragments/references/shared/transcribe.md +++ b/tools/skillgen/fragments/references/shared/transcribe.md @@ -25,8 +25,12 @@ Read the top god node labels from detect output or analysis, then compose a shor ```bash export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported) -export GRAPHIFY_WHISPER_PROMPT="" -$(cat graphify-out/.graphify_python) -c " +# Single quotes keep the prompt literal — no expansion of $, `, or \ inside it. +# If your domain hint itself contains a single quote, replace it with '\'' (close-quote, escaped-quote, open-quote) +# or use a here-doc / printf %q pattern; the export must still happen for the child Python process to inherit it. +GRAPHIFY_WHISPER_PROMPT='' +export GRAPHIFY_WHISPER_PROMPT +"$(cat graphify-out/.graphify_python)" -c " import json, os, sys from pathlib import Path from graphify.transcribe import transcribe_all diff --git a/tools/skillgen/fragments/references/shared/update.md b/tools/skillgen/fragments/references/shared/update.md index 3632fd4126..493323c3fe 100644 --- a/tools/skillgen/fragments/references/shared/update.md +++ b/tools/skillgen/fragments/references/shared/update.md @@ -7,7 +7,7 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import sys, json from graphify.detect import detect_incremental, save_manifest from pathlib import Path @@ -30,7 +30,7 @@ if new_total > 0: Then populate `.graphify_detect.json` so Steps 3A–6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\")) @@ -48,7 +48,7 @@ Path('graphify-out/.graphify_detect.json').write_text(json.dumps({ If new files exist, first check whether all changed files are code files: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path @@ -71,7 +71,7 @@ If no new files exist (only deletions), create an empty extraction so the merge ```bash if [ ! -f graphify-out/.graphify_extract.json ]; then echo '[graphify update] Only deletions -- creating empty extraction for merge.' - $(cat graphify-out/.graphify_python) -c " + "$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8') @@ -83,7 +83,7 @@ fi Then: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from pathlib import Path from graphify.build import build_merge @@ -170,7 +170,7 @@ Then run Steps 4–8 on the merged graph as normal. After Step 4, show the graph diff: ```bash -$(cat graphify-out/.graphify_python) -c " +"$(cat graphify-out/.graphify_python)" -c " import json from graphify.analyze import graph_diff from graphify.build import build_from_json