diff --git a/graphify/cli.py b/graphify/cli.py index 400cf463d8..cc7c7c755f 100644 --- a/graphify/cli.py +++ b/graphify/cli.py @@ -1461,22 +1461,39 @@ def dispatch_command(cmd: str) -> None: elif cmd == "save-result": # graphify save-result --question Q --answer A [--type T] [--nodes N1 N2 ...] # [--outcome useful|dead_end|corrected] [--correction TEXT] + # + # --question-file/--answer-file/--correction-file read the value from a + # file instead of a command-line argument: skill instructions that build + # this command from free text (the user's verbatim question, an LLM's + # generated answer, of unbounded length and content) must not substitute + # that text directly into a shell command string, since embedded quotes, + # backticks, or $() would corrupt or escape the command entirely + # (#3439). Writing the value to a file first has no such injection + # surface at all. import argparse as _ap p = _ap.ArgumentParser(prog="graphify save-result") - p.add_argument("--question", required=True) + p.add_argument("--question", default=None) + p.add_argument("--question-file", dest="question_file", default=None) p.add_argument("--answer", default=None) p.add_argument("--answer-file", dest="answer_file", default=None) p.add_argument("--type", dest="query_type", default="query") p.add_argument("--nodes", nargs="*", default=[]) p.add_argument("--outcome", choices=("useful", "dead_end", "corrected"), default=None) p.add_argument("--correction", default=None) + p.add_argument("--correction-file", dest="correction_file", default=None) p.add_argument("--memory-dir", default=str(Path(_GRAPHIFY_OUT) / "memory")) opts = p.parse_args(sys.argv[2:]) + if opts.question_file: + opts.question = Path(opts.question_file).read_text(encoding="utf-8").strip() + elif not opts.question: + p.error("--question or --question-file is required") if opts.answer_file: opts.answer = Path(opts.answer_file).read_text(encoding="utf-8").strip() elif not opts.answer: p.error("--answer or --answer-file is required") + if opts.correction_file: + opts.correction = Path(opts.correction_file).read_text(encoding="utf-8").strip() from graphify.ingest import save_query_result as _sqr out = _sqr( @@ -1942,32 +1959,76 @@ def dispatch_command(cmd: str) -> None: print(format_diagnostic_report(summary)) elif cmd == "add": - if len(sys.argv) < 3: - print( - "Usage: graphify add [--author Name] [--contributor Name] [--dir ./raw]", - file=sys.stderr, - ) + # --from-file reads {"url": ..., "author": ..., "contributor": ..., "dir": ...} + # (author/contributor/dir optional) instead of taking url/--author/ + # --contributor as command-line text: skill instructions that build this + # command from a user-supplied URL and name have no way to shell-quote + # values they cannot predict the content of, so substituting them + # directly into a command string risks corrupting or escaping it + # (#3439). Writing the payload to a file first has no such injection + # surface -- only the file's own (agent-controlled) path is a shell + # argument. + args = sys.argv[2:] + from_file = None + from_file_requested = False + for i, a in enumerate(args): + if a == "--from-file": + from_file_requested = True + if i + 1 < len(args): + from_file = args[i + 1] + break + if from_file_requested and not from_file: + print("error: --from-file requires a path argument", file=sys.stderr) sys.exit(1) + if from_file: + try: + payload = json.loads(Path(from_file).read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError) as exc: + print(f"error: could not read --from-file payload: {exc}", file=sys.stderr) + sys.exit(1) + if not isinstance(payload, dict): + print( + "error: --from-file payload must be a JSON object with a " + "'url' key, not " + type(payload).__name__, + file=sys.stderr, + ) + sys.exit(1) + try: + url = payload["url"] + except KeyError: + print("error: --from-file payload is missing required key 'url'", file=sys.stderr) + sys.exit(1) + author = payload.get("author") + contributor = payload.get("contributor") + target_dir = Path(payload.get("dir") or "raw") + else: + if len(sys.argv) < 3: + print( + "Usage: graphify add [--author Name] [--contributor Name] " + "[--dir ./raw] | graphify add --from-file payload.json", + file=sys.stderr, + ) + sys.exit(1) + url = sys.argv[2] + author = None + contributor = None + target_dir = Path("raw") + args = sys.argv[3:] + i = 0 + while i < len(args): + if args[i] == "--author" and i + 1 < len(args): + author = args[i + 1] + i += 2 + elif args[i] == "--contributor" and i + 1 < len(args): + contributor = args[i + 1] + i += 2 + elif args[i] == "--dir" and i + 1 < len(args): + target_dir = Path(args[i + 1]) + i += 2 + else: + i += 1 from graphify.ingest import ingest as _ingest - url = sys.argv[2] - author: str | None = None - contributor: str | None = None - target_dir = Path("raw") - args = sys.argv[3:] - i = 0 - while i < len(args): - if args[i] == "--author" and i + 1 < len(args): - author = args[i + 1] - i += 2 - elif args[i] == "--contributor" and i + 1 < len(args): - contributor = args[i + 1] - i += 2 - elif args[i] == "--dir" and i + 1 < len(args): - target_dir = Path(args[i + 1]) - i += 2 - else: - i += 1 try: saved = _ingest(url, target_dir, author=author, contributor=contributor) print(f"Saved to {saved}") diff --git a/graphify/skills/agents/references/add-watch.md b/graphify/skills/agents/references/add-watch.md index 77844343e1..8eb3d19d91 100644 --- a/graphify/skills/agents/references/add-watch.md +++ b/graphify/skills/agents/references/add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/graphify/skills/agents/references/query.md b/graphify/skills/agents/references/query.md index 56565eb782..f859ddc374 100644 --- a/graphify/skills/agents/references/query.md +++ b/graphify/skills/agents/references/query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` diff --git a/graphify/skills/amp/references/add-watch.md b/graphify/skills/amp/references/add-watch.md index 77844343e1..8eb3d19d91 100644 --- a/graphify/skills/amp/references/add-watch.md +++ b/graphify/skills/amp/references/add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/graphify/skills/amp/references/query.md b/graphify/skills/amp/references/query.md index 56565eb782..f859ddc374 100644 --- a/graphify/skills/amp/references/query.md +++ b/graphify/skills/amp/references/query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` diff --git a/graphify/skills/claude/references/add-watch.md b/graphify/skills/claude/references/add-watch.md index 77844343e1..8eb3d19d91 100644 --- a/graphify/skills/claude/references/add-watch.md +++ b/graphify/skills/claude/references/add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/graphify/skills/claude/references/query.md b/graphify/skills/claude/references/query.md index 56565eb782..f859ddc374 100644 --- a/graphify/skills/claude/references/query.md +++ b/graphify/skills/claude/references/query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` diff --git a/graphify/skills/claw/references/add-watch.md b/graphify/skills/claw/references/add-watch.md index 77844343e1..8eb3d19d91 100644 --- a/graphify/skills/claw/references/add-watch.md +++ b/graphify/skills/claw/references/add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/graphify/skills/claw/references/query.md b/graphify/skills/claw/references/query.md index 56565eb782..f859ddc374 100644 --- a/graphify/skills/claw/references/query.md +++ b/graphify/skills/claw/references/query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` diff --git a/graphify/skills/codex/references/add-watch.md b/graphify/skills/codex/references/add-watch.md index 77844343e1..8eb3d19d91 100644 --- a/graphify/skills/codex/references/add-watch.md +++ b/graphify/skills/codex/references/add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/graphify/skills/codex/references/query.md b/graphify/skills/codex/references/query.md index 56565eb782..f859ddc374 100644 --- a/graphify/skills/codex/references/query.md +++ b/graphify/skills/codex/references/query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` diff --git a/graphify/skills/copilot/references/add-watch.md b/graphify/skills/copilot/references/add-watch.md index 77844343e1..8eb3d19d91 100644 --- a/graphify/skills/copilot/references/add-watch.md +++ b/graphify/skills/copilot/references/add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/graphify/skills/copilot/references/query.md b/graphify/skills/copilot/references/query.md index 56565eb782..f859ddc374 100644 --- a/graphify/skills/copilot/references/query.md +++ b/graphify/skills/copilot/references/query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` diff --git a/graphify/skills/droid/references/add-watch.md b/graphify/skills/droid/references/add-watch.md index 77844343e1..8eb3d19d91 100644 --- a/graphify/skills/droid/references/add-watch.md +++ b/graphify/skills/droid/references/add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/graphify/skills/droid/references/query.md b/graphify/skills/droid/references/query.md index 56565eb782..f859ddc374 100644 --- a/graphify/skills/droid/references/query.md +++ b/graphify/skills/droid/references/query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` diff --git a/graphify/skills/kilo/references/add-watch.md b/graphify/skills/kilo/references/add-watch.md index 77844343e1..8eb3d19d91 100644 --- a/graphify/skills/kilo/references/add-watch.md +++ b/graphify/skills/kilo/references/add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/graphify/skills/kilo/references/query.md b/graphify/skills/kilo/references/query.md index 56565eb782..f859ddc374 100644 --- a/graphify/skills/kilo/references/query.md +++ b/graphify/skills/kilo/references/query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` diff --git a/graphify/skills/kiro/references/add-watch.md b/graphify/skills/kiro/references/add-watch.md index 77844343e1..8eb3d19d91 100644 --- a/graphify/skills/kiro/references/add-watch.md +++ b/graphify/skills/kiro/references/add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/graphify/skills/kiro/references/query.md b/graphify/skills/kiro/references/query.md index 56565eb782..f859ddc374 100644 --- a/graphify/skills/kiro/references/query.md +++ b/graphify/skills/kiro/references/query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` diff --git a/graphify/skills/opencode/references/add-watch.md b/graphify/skills/opencode/references/add-watch.md index 77844343e1..8eb3d19d91 100644 --- a/graphify/skills/opencode/references/add-watch.md +++ b/graphify/skills/opencode/references/add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/graphify/skills/opencode/references/query.md b/graphify/skills/opencode/references/query.md index 56565eb782..f859ddc374 100644 --- a/graphify/skills/opencode/references/query.md +++ b/graphify/skills/opencode/references/query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` diff --git a/graphify/skills/pi/references/add-watch.md b/graphify/skills/pi/references/add-watch.md index 77844343e1..8eb3d19d91 100644 --- a/graphify/skills/pi/references/add-watch.md +++ b/graphify/skills/pi/references/add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/graphify/skills/pi/references/query.md b/graphify/skills/pi/references/query.md index 56565eb782..f859ddc374 100644 --- a/graphify/skills/pi/references/query.md +++ b/graphify/skills/pi/references/query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` diff --git a/graphify/skills/trae/references/add-watch.md b/graphify/skills/trae/references/add-watch.md index 77844343e1..8eb3d19d91 100644 --- a/graphify/skills/trae/references/add-watch.md +++ b/graphify/skills/trae/references/add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/graphify/skills/trae/references/query.md b/graphify/skills/trae/references/query.md index 56565eb782..f859ddc374 100644 --- a/graphify/skills/trae/references/query.md +++ b/graphify/skills/trae/references/query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` diff --git a/graphify/skills/vscode/references/add-watch.md b/graphify/skills/vscode/references/add-watch.md index 77844343e1..8eb3d19d91 100644 --- a/graphify/skills/vscode/references/add-watch.md +++ b/graphify/skills/vscode/references/add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/graphify/skills/vscode/references/query.md b/graphify/skills/vscode/references/query.md index 56565eb782..f859ddc374 100644 --- a/graphify/skills/vscode/references/query.md +++ b/graphify/skills/vscode/references/query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` diff --git a/graphify/skills/windows/references/add-watch.md b/graphify/skills/windows/references/add-watch.md index 77844343e1..8eb3d19d91 100644 --- a/graphify/skills/windows/references/add-watch.md +++ b/graphify/skills/windows/references/add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/graphify/skills/windows/references/query.md b/graphify/skills/windows/references/query.md index 56565eb782..f859ddc374 100644 --- a/graphify/skills/windows/references/query.md +++ b/graphify/skills/windows/references/query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` diff --git a/tests/test_ingest.py b/tests/test_ingest.py index 6b7d1fb034..8f2467e987 100644 --- a/tests/test_ingest.py +++ b/tests/test_ingest.py @@ -110,3 +110,140 @@ def test_concurrent_saves_of_the_same_question_do_not_overwrite(tmp_path): paths = list(ex.map(lambda _: save_query_result("how does auth work", "a", mem), range(20))) assert len({p.name for p in paths}) == 20 assert len(list(mem.glob("*.md"))) == 20 + + +def test_cli_add_from_file_passes_payload_through_unexecuted(tmp_path, monkeypatch): + """#3439: the skill instructions for `graphify add` used to have the + agent substitute a URL and free-text author/contributor names directly + into a `python -c "..."` heredoc calling ingest(...) as Python source -- + an embedded quote breaks it, and crafted input executes arbitrary + Python. --from-file reads the same values from a JSON payload instead, + which has no such injection surface: only the file's own path (agent- + controlled, not user content) becomes a shell argument. Mocks ingest + itself so this never touches the network; the point here is that the + adversarial author/contributor text reaches ingest() as plain string + values, verbatim, never as code.""" + import json + import sys + from pathlib import Path + from graphify.cli import dispatch_command + + captured = {} + + def fake_ingest(url, target_dir, author=None, contributor=None): + captured["url"] = url + captured["target_dir"] = target_dir + captured["author"] = author + captured["contributor"] = contributor + return Path(target_dir) / "page.md" + + monkeypatch.setattr("graphify.ingest.ingest", fake_ingest) + + payload = tmp_path / "payload.json" + payload.write_text(json.dumps({ + "url": "https://example.com/page", + "author": "O'Brien", + "contributor": "test`whoami`; rm -rf / #", + "dir": str(tmp_path / "raw"), + }), encoding="utf-8") + + monkeypatch.setattr(sys, "argv", ["graphify", "add", "--from-file", str(payload)]) + dispatch_command("add") + + assert captured["url"] == "https://example.com/page" + assert captured["author"] == "O'Brien" + assert captured["contributor"] == "test`whoami`; rm -rf / #" + assert captured["target_dir"] == Path(str(tmp_path / "raw")) + + +def test_cli_add_still_accepts_positional_url_and_flags(tmp_path, monkeypatch): + """--from-file is additive: the original `graphify add --author X + --contributor Y` form (a human typing the command directly, not a skill + substituting untrusted text into it) must keep working unchanged.""" + import sys + from pathlib import Path + from graphify.cli import dispatch_command + + captured = {} + + def fake_ingest(url, target_dir, author=None, contributor=None): + captured["url"] = url + captured["author"] = author + captured["contributor"] = contributor + return Path(target_dir) / "page.md" + + monkeypatch.setattr("graphify.ingest.ingest", fake_ingest) + monkeypatch.setattr(sys, "argv", [ + "graphify", "add", "https://example.com/x", + "--author", "Jane", "--contributor", "Jo", + ]) + dispatch_command("add") + + assert captured["url"] == "https://example.com/x" + assert captured["author"] == "Jane" + assert captured["contributor"] == "Jo" + + +def test_cli_add_from_file_without_a_path_is_a_clean_error(capsys, monkeypatch): + """`--from-file` with no path operand used to fall through to the + positional-url branch and treat the literal string "--from-file" itself + as the URL (a confusing "Blocked URL scheme" error instead of pointing + at the actual mistake).""" + import sys + from graphify.cli import dispatch_command + + monkeypatch.setattr(sys, "argv", ["graphify", "add", "--from-file"]) + with pytest.raises(SystemExit) as exc_info: + dispatch_command("add") + assert exc_info.value.code != 0 + assert "--from-file" in capsys.readouterr().err + + +def test_cli_add_from_file_missing_url_key_is_a_clean_error(tmp_path, capsys, monkeypatch): + """A payload missing the required "url" key used to raise a raw, + unhandled KeyError instead of the same clean "error: ..." message + every other failure in this command produces.""" + import json + import sys + from graphify.cli import dispatch_command + + payload = tmp_path / "payload.json" + payload.write_text(json.dumps({"author": "Jane"}), encoding="utf-8") + monkeypatch.setattr(sys, "argv", ["graphify", "add", "--from-file", str(payload)]) + with pytest.raises(SystemExit) as exc_info: + dispatch_command("add") + assert exc_info.value.code != 0 + assert "url" in capsys.readouterr().err + + +def test_cli_add_from_file_malformed_json_is_a_clean_error(tmp_path, capsys, monkeypatch): + """Malformed JSON or a missing file must also produce a clean error, + not a raw traceback.""" + import sys + from graphify.cli import dispatch_command + + payload = tmp_path / "payload.json" + payload.write_text("not json", encoding="utf-8") + monkeypatch.setattr(sys, "argv", ["graphify", "add", "--from-file", str(payload)]) + with pytest.raises(SystemExit) as exc_info: + dispatch_command("add") + assert exc_info.value.code != 0 + assert "error:" in capsys.readouterr().err + + +@pytest.mark.parametrize("body", ["[1, 2, 3]", '"just a string"', "42"]) +def test_cli_add_from_file_non_object_json_is_a_clean_error(body, tmp_path, capsys, monkeypatch): + """A payload that is syntactically valid JSON but not an object (a + list, a bare string, a number) used to raise a raw, unhandled + TypeError from subscripting it with "url" -- json.JSONDecodeError + alone does not cover this, since the JSON itself parses fine.""" + import sys + from graphify.cli import dispatch_command + + payload = tmp_path / "payload.json" + payload.write_text(body, encoding="utf-8") + monkeypatch.setattr(sys, "argv", ["graphify", "add", "--from-file", str(payload)]) + with pytest.raises(SystemExit) as exc_info: + dispatch_command("add") + assert exc_info.value.code != 0 + assert "error:" in capsys.readouterr().err diff --git a/tests/test_reflect.py b/tests/test_reflect.py index c24cacefd1..5f15954d36 100644 --- a/tests/test_reflect.py +++ b/tests/test_reflect.py @@ -499,6 +499,36 @@ def test_cli_save_result_requires_answer_or_answer_file(tmp_path): assert "--answer" in (r.stderr + r.stdout) +def test_cli_save_result_reads_question_and_correction_from_file(tmp_path): + """#3439: --question-file/--correction-file mirror --answer-file for the + same reason -- a value a skill's instructions build this command from + (the user's verbatim question, an LLM-generated correction) is free + text of unpredictable content, and substituting it directly into a + shell command string risks corruption or injection via an embedded + quote, backtick, or $(). A file has no such surface. Round-trips + adversarial content (backticks, $(), both quote styles) through both + fields at once, unexecuted and uncorrupted.""" + q = tmp_path / "question.txt" + q.write_text("what about $(whoami) and `id` and 'quotes' and \"more\"?\n", encoding="utf-8") + c = tmp_path / "correction.txt" + c.write_text("actually it's `AuthMiddleware`, not $(OldAuth); check 'both'.\n", encoding="utf-8") + r = _run(["save-result", "--question-file", str(q), "--answer", "a", + "--outcome", "corrected", "--correction-file", str(c)], tmp_path) + assert r.returncode == 0, r.stderr + docs = list((tmp_path / "graphify-out" / "memory").glob("*.md")) + assert docs, "save-result wrote no memory doc" + body = docs[0].read_text(encoding="utf-8") + assert "$(whoami)" in body and "`id`" in body + assert "`AuthMiddleware`" in body and "$(OldAuth)" in body + + +def test_cli_save_result_requires_question_or_question_file(tmp_path): + """Neither --question nor --question-file -> clean argparse error, not a crash.""" + r = _run(["save-result", "--answer", "a", "--outcome", "useful"], tmp_path) + assert r.returncode != 0 + assert "--question" in (r.stderr + r.stdout) + + def test_cli_reflect_cold_start_writes_empty_lessons(tmp_path): """First run with no graphify-out/memory/ still succeeds and writes a valid doc.""" r = _run(["reflect"], tmp_path) 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..8eb3d19d91 100644 --- a/tools/skillgen/expected/graphify__skills__agents__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__agents__references__add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/tools/skillgen/expected/graphify__skills__agents__references__query.md b/tools/skillgen/expected/graphify__skills__agents__references__query.md index 56565eb782..f859ddc374 100644 --- a/tools/skillgen/expected/graphify__skills__agents__references__query.md +++ b/tools/skillgen/expected/graphify__skills__agents__references__query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` 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..8eb3d19d91 100644 --- a/tools/skillgen/expected/graphify__skills__amp__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__amp__references__add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/tools/skillgen/expected/graphify__skills__amp__references__query.md b/tools/skillgen/expected/graphify__skills__amp__references__query.md index 56565eb782..f859ddc374 100644 --- a/tools/skillgen/expected/graphify__skills__amp__references__query.md +++ b/tools/skillgen/expected/graphify__skills__amp__references__query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` 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..8eb3d19d91 100644 --- a/tools/skillgen/expected/graphify__skills__claude__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__claude__references__add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/tools/skillgen/expected/graphify__skills__claude__references__query.md b/tools/skillgen/expected/graphify__skills__claude__references__query.md index 56565eb782..f859ddc374 100644 --- a/tools/skillgen/expected/graphify__skills__claude__references__query.md +++ b/tools/skillgen/expected/graphify__skills__claude__references__query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` 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..8eb3d19d91 100644 --- a/tools/skillgen/expected/graphify__skills__claw__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__claw__references__add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/tools/skillgen/expected/graphify__skills__claw__references__query.md b/tools/skillgen/expected/graphify__skills__claw__references__query.md index 56565eb782..f859ddc374 100644 --- a/tools/skillgen/expected/graphify__skills__claw__references__query.md +++ b/tools/skillgen/expected/graphify__skills__claw__references__query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` 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..8eb3d19d91 100644 --- a/tools/skillgen/expected/graphify__skills__codex__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__codex__references__add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/tools/skillgen/expected/graphify__skills__codex__references__query.md b/tools/skillgen/expected/graphify__skills__codex__references__query.md index 56565eb782..f859ddc374 100644 --- a/tools/skillgen/expected/graphify__skills__codex__references__query.md +++ b/tools/skillgen/expected/graphify__skills__codex__references__query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` 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..8eb3d19d91 100644 --- a/tools/skillgen/expected/graphify__skills__copilot__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__copilot__references__add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/tools/skillgen/expected/graphify__skills__copilot__references__query.md b/tools/skillgen/expected/graphify__skills__copilot__references__query.md index 56565eb782..f859ddc374 100644 --- a/tools/skillgen/expected/graphify__skills__copilot__references__query.md +++ b/tools/skillgen/expected/graphify__skills__copilot__references__query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` 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..8eb3d19d91 100644 --- a/tools/skillgen/expected/graphify__skills__droid__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__droid__references__add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/tools/skillgen/expected/graphify__skills__droid__references__query.md b/tools/skillgen/expected/graphify__skills__droid__references__query.md index 56565eb782..f859ddc374 100644 --- a/tools/skillgen/expected/graphify__skills__droid__references__query.md +++ b/tools/skillgen/expected/graphify__skills__droid__references__query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` 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..8eb3d19d91 100644 --- a/tools/skillgen/expected/graphify__skills__kilo__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__kilo__references__add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/tools/skillgen/expected/graphify__skills__kilo__references__query.md b/tools/skillgen/expected/graphify__skills__kilo__references__query.md index 56565eb782..f859ddc374 100644 --- a/tools/skillgen/expected/graphify__skills__kilo__references__query.md +++ b/tools/skillgen/expected/graphify__skills__kilo__references__query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` 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..8eb3d19d91 100644 --- a/tools/skillgen/expected/graphify__skills__kiro__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__kiro__references__add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/tools/skillgen/expected/graphify__skills__kiro__references__query.md b/tools/skillgen/expected/graphify__skills__kiro__references__query.md index 56565eb782..f859ddc374 100644 --- a/tools/skillgen/expected/graphify__skills__kiro__references__query.md +++ b/tools/skillgen/expected/graphify__skills__kiro__references__query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` 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..8eb3d19d91 100644 --- a/tools/skillgen/expected/graphify__skills__opencode__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__opencode__references__add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/tools/skillgen/expected/graphify__skills__opencode__references__query.md b/tools/skillgen/expected/graphify__skills__opencode__references__query.md index 56565eb782..f859ddc374 100644 --- a/tools/skillgen/expected/graphify__skills__opencode__references__query.md +++ b/tools/skillgen/expected/graphify__skills__opencode__references__query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` 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..8eb3d19d91 100644 --- a/tools/skillgen/expected/graphify__skills__pi__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__pi__references__add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/tools/skillgen/expected/graphify__skills__pi__references__query.md b/tools/skillgen/expected/graphify__skills__pi__references__query.md index 56565eb782..f859ddc374 100644 --- a/tools/skillgen/expected/graphify__skills__pi__references__query.md +++ b/tools/skillgen/expected/graphify__skills__pi__references__query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` 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..8eb3d19d91 100644 --- a/tools/skillgen/expected/graphify__skills__trae__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__trae__references__add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/tools/skillgen/expected/graphify__skills__trae__references__query.md b/tools/skillgen/expected/graphify__skills__trae__references__query.md index 56565eb782..f859ddc374 100644 --- a/tools/skillgen/expected/graphify__skills__trae__references__query.md +++ b/tools/skillgen/expected/graphify__skills__trae__references__query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` 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..8eb3d19d91 100644 --- a/tools/skillgen/expected/graphify__skills__vscode__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__vscode__references__add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/tools/skillgen/expected/graphify__skills__vscode__references__query.md b/tools/skillgen/expected/graphify__skills__vscode__references__query.md index 56565eb782..f859ddc374 100644 --- a/tools/skillgen/expected/graphify__skills__vscode__references__query.md +++ b/tools/skillgen/expected/graphify__skills__vscode__references__query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` 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..8eb3d19d91 100644 --- a/tools/skillgen/expected/graphify__skills__windows__references__add-watch.md +++ b/tools/skillgen/expected/graphify__skills__windows__references__add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`) diff --git a/tools/skillgen/expected/graphify__skills__windows__references__query.md b/tools/skillgen/expected/graphify__skills__windows__references__query.md index 56565eb782..f859ddc374 100644 --- a/tools/skillgen/expected/graphify__skills__windows__references__query.md +++ b/tools/skillgen/expected/graphify__skills__windows__references__query.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --type explain --nodes NODE_NAME ``` diff --git a/tools/skillgen/fragments/references/query/default.md b/tools/skillgen/fragments/references/query/default.md index 56565eb782..f859ddc374 100644 --- a/tools/skillgen/fragments/references/query/default.md +++ b/tools/skillgen/fragments/references/query/default.md @@ -165,15 +165,31 @@ print(output) Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains. -After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node: +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. + +The question and answer are free text you do not control the content of - a +quote, backtick, or `$()` embedded in either one corrupts or escapes a +command it's substituted into. Reserve two unique file paths first - a +fixed, shared filename risks a concurrent graphify session overwriting or +reading a stale value: ```bash -$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX ``` -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. +Using your file-write tool, write the user's verbatim question to the path +the first command printed and your full answer text (containing the +expanded-token trace) to the path the second one printed, then pass those +exact paths - not their content - on the command line: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type query --nodes NODE1 NODE2 +``` -**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): +Replace `QUESTION_PATH`/`ANSWER_PATH` with the paths `mktemp` printed and `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. + +**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, when correcting, reserve one more unique path with `mktemp`, write what was right to it, and pass `--correction-file CORRECTION_PATH` the same way): - `useful` — the cited nodes answered the question well (they become *preferred sources*). - `dead_end` — the question/path led nowhere; don't re-derive it next time. @@ -243,10 +259,24 @@ 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. `NODE_A`/`NODE_B` are node +labels, which can come from extracted document content and so are not +guaranteed free of shell characters either - treat the question the same +as the explanation. Reserve two unique file paths first (a fixed, shared +filename risks a concurrent graphify session overwriting or reading a +stale value): ```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 +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Path from NODE_A to NODE_B` (with the +actual node names) to the first path and the explanation to the second, +then pass only those paths, the same way as for `/graphify query` above: + +```bash +$(cat graphify-out/.graphify_python) -m graphify save-result --question-file QUESTION_PATH --answer-file ANSWER_PATH --type path_query --nodes NODE_A NODE_B ``` --- @@ -304,8 +334,21 @@ 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. `NODE_NAME` is a node label, +which can come from extracted document content and so is not guaranteed +free of shell characters either - treat the question the same as the +explanation. Reserve two unique file paths first (a fixed, shared filename +risks a concurrent graphify session overwriting or reading a stale value): + +```bash +mktemp /tmp/graphify_question.XXXXXX +mktemp /tmp/graphify_answer.XXXXXX +``` + +Using your file-write tool, write `Explain NODE_NAME` (with the actual +node name) to the first path and the explanation to the second, then pass +only those paths, the same way as for `/graphify query` above: ```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-file QUESTION_PATH --answer-file ANSWER_PATH --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..8eb3d19d91 100644 --- a/tools/skillgen/fragments/references/shared/add-watch.md +++ b/tools/skillgen/fragments/references/shared/add-watch.md @@ -6,25 +6,35 @@ 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. +The URL and any author/contributor name are free text you do not control the +content of - do not build a command or inline script by substituting them +into a string; an embedded quote or shell character corrupts or escapes it. +Reserve a unique file path first - a fixed, shared filename risks a +concurrent graphify session overwriting or reading a stale payload: + +```bash +mktemp /tmp/graphify_add_payload.XXXXXX.json +``` + +Using your file-write tool (not a shell heredoc, which has the same +quoting problem one level down), write a JSON file with those values to +the path that command printed, then pass only that path - not its content +- to `graphify add`: + +```json +{"url": "URL", "author": "AUTHOR", "contributor": "CONTRIBUTOR", "dir": "./raw"} +``` + +Replace `URL` with the actual URL, `AUTHOR` with the user's name if +provided (omit the key entirely if not), `CONTRIBUTOR` likewise, then run: + ```bash -$(cat graphify-out/.graphify_python) -c " -import sys -from graphify.ingest import ingest -from pathlib import Path - -try: - 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) - sys.exit(1) -except RuntimeError as e: - print(f'error: {e}', file=sys.stderr) - sys.exit(1) -" +$(cat graphify-out/.graphify_python) -m graphify add --from-file PAYLOAD_PATH ``` -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 `PAYLOAD_PATH` with the path `mktemp` printed. 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]'`)