diff --git a/README.md b/README.md index 0c14d207c9..33337be703 100644 --- a/README.md +++ b/README.md @@ -390,7 +390,7 @@ graphify export callflow-html # Mermaid architecture/call-flow HTML (auto-r /graphify add https://arxiv.org/abs/1706.03762 # fetch a paper and add it /graphify add # transcribe and add a video -graphify hook install # auto-rebuild on git commit +graphify hook install # auto-rebuild on git commit (AST; optional semantics) graphify merge-graphs a.json b.json # combine two graphs graphify prs # PR dashboard: CI state, review status, worktree mapping @@ -440,8 +440,24 @@ graphify-out/cost.json # local only **Workflow:** 1. One person runs `/graphify .` and commits `graphify-out/`. 2. Everyone pulls — their assistant reads the graph immediately. -3. Run `graphify hook install` to auto-rebuild after each commit (AST only, no API cost). This also sets up a git merge driver so `graph.json` is never left with conflict markers — two devs committing in parallel get their graphs union-merged automatically. -4. When docs or papers change, run `/graphify --update` to refresh those nodes. +3. Run `graphify hook install` to auto-rebuild after each commit (AST only, no API cost by default). This also sets up a git merge driver so `graph.json` is never left with conflict markers — two devs committing in parallel get their graphs union-merged automatically. +4. To keep documents, papers, and images current without a manual step, opt the project into semantic updates: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker runs detached after a commit, shares the graph rebuild lock, +and coalesces overlapping commits. Failed or interrupted work stays queued for a +later commit. The env file is parsed as data, never sourced as shell code; only +credential variables for the selected backend are loaded, existing process +credentials win, and endpoint variables are ignored. Keep that file untracked. +Without `semantic_update=on_commit`, hooks remain AST-only and incur no LLM cost. --- @@ -778,6 +794,7 @@ graphify --version # print installed version graphify watch ./src graphify check-update ./src graphify update ./src +graphify update ./my-workspace --semantic --backend kimi # refresh changed code + documents, then regenerate report/community outputs graphify update ./src --no-cluster # skip reclustering, write raw AST graph only graphify update ./src --force # overwrite even if new graph has fewer nodes graphify cluster-only ./my-project @@ -792,6 +809,14 @@ graphify label ./my-project # (re)name commun graphify label ./my-project --backend=openai --model gpt-4o # force a specific backend and model ``` +Use `graphify update --semantic` for a mixed code/document corpus when +you want one incremental command to finish the whole pipeline. It reuses the +semantic cache, prunes replaced or deleted sources, and regenerates +`graph.json`, community labels, and `GRAPH_REPORT.md`. Changed documents use +the selected LLM backend and may incur API cost; plain `graphify update` stays +AST-only and makes no LLM calls. `--semantic` cannot be combined with +`--code-only` or `--no-cluster`. + > **Community names:** inside an agent (Claude Code, Gemini CLI) the agent names communities itself. When you run the bare CLI, `cluster-only` auto-names them with the configured backend (built-in or custom OpenAI-compatible provider) — pass `--no-label` to keep `Community N`, or run `graphify label` to (re)generate names on demand. --- diff --git a/graphify/__main__.py b/graphify/__main__.py index 155501a98d..49039d316b 100644 --- a/graphify/__main__.py +++ b/graphify/__main__.py @@ -535,6 +535,9 @@ def _run_cli() -> None: print(" --dir target directory (default: ./raw)") print(" watch watch a folder and rebuild the graph on code changes") print(" update re-extract code files and update the graph (no LLM needed)") + print(" --semantic also refresh changed documents with an LLM, then regenerate the report") + print(" --backend= semantic extraction and community-labeling backend") + print(" --model= model for semantic extraction and community labeling") print(" --force overwrite graph.json even if the rebuild has fewer nodes") print(" (also: GRAPHIFY_FORCE=1 env var; use after refactors that delete code)") print(" --no-cluster skip clustering, write raw extraction only") diff --git a/graphify/cli.py b/graphify/cli.py index 02e55b9447..082d84d108 100644 --- a/graphify/cli.py +++ b/graphify/cli.py @@ -829,7 +829,138 @@ def _reenter_main() -> None: main() +def _semantic_update_extract_argv(update_argv: list[str]) -> list[str]: + """Normalize update's flag-anywhere syntax for the extract parser.""" + value_options = { + "--backend", + "--model", + "--mode", + "--out", + "--output", + "--as", + "--max-workers", + "--token-budget", + "--max-concurrency", + "--api-timeout", + "--resolution", + "--exclude-hubs", + "--exclude", + "--postgres", + "--batch-size", + "--min-community-size", + } + flag_options = { + "--dedup-llm", + "--no-dedup", + "--google-workspace", + "--no-gitignore", + "--global", + "--cargo", + "--force", + "--allow-partial", + "--timing", + "--no-viz", + "--no-label", + "--missing-only", + } + options: list[str] = [] + paths: list[str] = [] + args = update_argv[2:] + i = 0 + while i < len(args): + arg = args[i] + if arg == "--semantic": + i += 1 + elif arg in value_options: + if i + 1 >= len(args): + raise ValueError(f"semantic update option requires a value: {arg}") + options.extend((arg, args[i + 1])) + i += 2 + elif arg in flag_options or any( + arg.startswith(f"{option}=") for option in value_options + ): + options.append(arg) + i += 1 + elif arg.startswith("-"): + raise ValueError(f"unknown semantic update option: {arg}") + else: + paths.append(arg) + i += 1 + + if len(paths) > 1: + raise ValueError("update accepts at most one path argument") + if paths: + update_root = paths[0] + else: + saved_root = Path(_GRAPHIFY_OUT) / ".graphify_root" + update_root = ( + saved_root.read_text(encoding="utf-8").strip() + if saved_root.exists() + else "." + ) + return [update_argv[0], "extract", update_root, *options] + + +def _semantic_update_cluster_argv(extract_argv: list[str], out_root: Path) -> list[str]: + """Build the report-stage argv for a semantic update. + + Extraction and ``cluster-only`` intentionally have separate parsers. Keep + only their shared/report-specific options here so extraction-only flags do + not leak into the final stage. + """ + value_options = { + "--backend", + "--model", + "--resolution", + "--exclude-hubs", + "--max-concurrency", + "--batch-size", + "--min-community-size", + } + flag_options = {"--no-viz", "--no-label", "--missing-only", "--timing"} + cluster_args: list[str] = [] + args = extract_argv[2:] + i = 0 + while i < len(args): + arg = args[i] + if arg in value_options and i + 1 < len(args): + cluster_args.extend((arg, args[i + 1])) + i += 2 + elif arg in flag_options or any( + arg.startswith(f"{option}=") for option in value_options + ): + cluster_args.append(arg) + i += 1 + else: + i += 1 + return [extract_argv[0], "cluster-only", str(out_root), *cluster_args] + + def dispatch_command(cmd: str) -> None: + semantic_update = cmd == "update" and "--semantic" in sys.argv[2:] + if semantic_update: + incompatible = next( + (flag for flag in ("--no-cluster", "--code-only") if flag in sys.argv[2:]), + None, + ) + if incompatible: + print( + f"error: --semantic and {incompatible} cannot be combined; " + "a semantic update refreshes documents and completes the report " + "and community outputs", + file=sys.stderr, + ) + sys.exit(2) + # Reuse the native incremental extractor instead of maintaining a + # second mixed-corpus update pipeline. The report stage is completed at + # the end of the extract branch below. + try: + sys.argv = _semantic_update_extract_argv(sys.argv) + except ValueError as exc: + print(f"error: {exc}", file=sys.stderr) + sys.exit(2) + cmd = "extract" + if cmd == "provider": from graphify.llm import _custom_providers_path, BACKENDS import json as _json @@ -4121,14 +4252,29 @@ def _invalidate_file_manifest_for_db_graph() -> None: f"{merged['output_tokens']:,} out, " f"est. cost (~{backend}): ${cost:.4f}" ) - # extract intentionally stops at graph.json + analysis; the report and - # community labels are produced by `cluster-only` (or an agent's Step 5). - # Point standalone users at it so communities get named (#1097). - print( - "[graphify extract] next: run " - f"`graphify cluster-only {graphify_out.parent}` " - "to generate GRAPH_REPORT.md and name communities" - ) + if semantic_update: + # Complete the native update in-process so extraction monkeypatches, + # custom providers, and exit handling stay consistent with the + # command that produced graph.json. + extract_argv = sys.argv + try: + sys.argv = _semantic_update_cluster_argv(extract_argv, out_root) + dispatch_command("cluster-only") + finally: + sys.argv = extract_argv + print( + "[graphify update] semantic update complete: " + "graph, communities, and report are current" + ) + else: + # extract intentionally stops at graph.json + analysis; the report + # and community labels are produced by `cluster-only` (or an agent's + # Step 5). Point standalone users at it so communities get named. + print( + "[graphify extract] next: run " + f"`graphify cluster-only {graphify_out.parent}` " + "to generate GRAPH_REPORT.md and name communities" + ) stages.total() elif cmd == "cache-check": diff --git a/graphify/hooks.py b/graphify/hooks.py index d284903e5e..78b7338036 100644 --- a/graphify/hooks.py +++ b/graphify/hooks.py @@ -3,6 +3,7 @@ import os import re import sys +import uuid from pathlib import Path _HOOK_MARKER = "# graphify-hook-start" @@ -139,6 +140,14 @@ def _bail(): if _txt: _root = Path(_txt) _rebuild_code(_root, changed_paths=changed, force=_force) + # The AST timeout has served its purpose. Semantic extraction has its own + # subprocess timeout and can legitimately run much longer than an AST pass. + if sys.platform != 'win32': + signal.alarm(0) + elif '_watchdog' in globals(): + _watchdog.cancel() + from graphify.hooks import _run_auto_semantic_update + _run_auto_semantic_update(_root, changed) # Refresh the work-memory lessons doc when saved Q&A outcomes exist # (best-effort; never fails the hook). try: @@ -276,7 +285,7 @@ def _detached_launch(rebuild_body: str) -> str: _HOOK_SCRIPT = """\ # graphify-hook-start -# Auto-rebuilds the knowledge graph after each commit (code files only, no LLM needed). +# Auto-rebuilds the knowledge graph after each commit (AST by default; semantic by opt-in). # Installed by: graphify hook install # Deterministic clustering: networkx louvain iterates string-keyed sets whose @@ -386,17 +395,69 @@ def _detached_launch(rebuild_body: str) -> str: """ -def _load_graphifyrc(root: Path) -> dict[str, str | int]: +_SEMANTIC_UPDATE_MODES = frozenset({"off", "on_commit"}) +_SEMANTIC_BACKENDS = frozenset( + {"azure", "bedrock", "claude", "claude-cli", "deepseek", "gemini", "kimi", "ollama", "openai"} +) +_SEMANTIC_ENV_KEYS: dict[str, frozenset[str]] = { + "azure": frozenset( + { + "AZURE_OPENAI_API_KEY", + } + ), + "bedrock": frozenset( + { + "AWS_ACCESS_KEY_ID", + "AWS_SECRET_ACCESS_KEY", + "AWS_SESSION_TOKEN", + "AWS_PROFILE", + "AWS_REGION", + "AWS_DEFAULT_REGION", + } + ), + "claude": frozenset({"ANTHROPIC_API_KEY"}), + "claude-cli": frozenset(), + "deepseek": frozenset({"DEEPSEEK_API_KEY"}), + "gemini": frozenset({"GEMINI_API_KEY", "GOOGLE_API_KEY"}), + "kimi": frozenset({"MOONSHOT_API_KEY"}), + "ollama": frozenset({"OLLAMA_API_KEY"}), + "openai": frozenset({"OPENAI_API_KEY"}), +} +_SEMANTIC_PENDING_DIR = ".semantic_pending" + + +def _validate_relative_config_path(value: str, *, option: str, rc_path: Path) -> str: + """Validate a portable, repo-relative config path without touching disk.""" + from graphify.paths import is_absolute_any_platform + + if not value or is_absolute_any_platform(value): + raise ValueError(f"Invalid {option} in {rc_path}: {value!r}. Must be a relative path.") + path = Path(value) + if ".." in path.parts or "\\" in value or "\x00" in value: + raise ValueError( + f"Invalid {option} in {rc_path}: {value!r}. " + "Must stay within the repository and use forward slashes." + ) + return value + + +def _load_graphifyrc(root: Path) -> dict[str, str | int | bool]: """Load key/value options from /.graphifyrc if present. Supported options: viz_node_limit: integer >= 0 (e.g. viz_node_limit=0) + semantic_update: off | on_commit (default: off) + semantic_backend: explicit LLM backend required for on_commit + semantic_model: optional model override + semantic_env_file: optional repo-relative dotenv file; only credentials + for semantic_backend are loaded and shell syntax is never evaluated + semantic_google_workspace: true | false (default: false) """ rc_path = root / ".graphifyrc" if not rc_path.is_file(): return {} - cfg: dict[str, str | int] = {} + cfg: dict[str, str | int | bool] = {} content = rc_path.read_text(encoding="utf-8") for line_num, raw in enumerate(content.splitlines(), 1): line = raw.strip() @@ -418,9 +479,282 @@ def _load_graphifyrc(root: Path) -> dict[str, str | int]: f"Invalid viz_node_limit in {rc_path} at line {line_num}: {val!r}. " f"Must be a non-negative integer." ) from exc + elif key == "semantic_update": + if val not in _SEMANTIC_UPDATE_MODES: + raise ValueError( + f"Invalid semantic_update in {rc_path} at line {line_num}: {val!r}. " + "Must be 'off' or 'on_commit'." + ) + cfg[key] = val + elif key == "semantic_backend": + if val not in _SEMANTIC_BACKENDS: + raise ValueError( + f"Invalid semantic_backend in {rc_path} at line {line_num}: {val!r}. " + f"Available: {', '.join(sorted(_SEMANTIC_BACKENDS))}." + ) + cfg[key] = val + elif key == "semantic_model": + if not val or any(ord(char) < 32 for char in val): + raise ValueError( + f"Invalid semantic_model in {rc_path} at line {line_num}: {val!r}." + ) + cfg[key] = val + elif key == "semantic_env_file": + cfg[key] = _validate_relative_config_path( + val, option="semantic_env_file", rc_path=rc_path + ) + elif key == "semantic_google_workspace": + if val not in {"true", "false"}: + raise ValueError( + f"Invalid semantic_google_workspace in {rc_path} at line " + f"{line_num}: {val!r}. Must be 'true' or 'false'." + ) + cfg[key] = val == "true" + + if cfg.get("semantic_update") == "on_commit" and not cfg.get("semantic_backend"): + raise ValueError( + f"Invalid semantic automation in {rc_path}: " + "semantic_backend is required when semantic_update=on_commit." + ) return cfg +def _semantic_out_dir(root: Path) -> Path: + out = Path(os.environ.get("GRAPHIFY_OUT", "graphify-out")) + return out if out.is_absolute() else root / out + + +def _semantic_pending_requests(out_dir: Path) -> list[Path]: + pending_dir = out_dir / _SEMANTIC_PENDING_DIR + if not pending_dir.is_dir(): + return [] + try: + return sorted(path for path in pending_dir.iterdir() if path.is_file()) + except OSError: + return [] + + +def _queue_semantic_request(root: Path) -> Path: + """Create one durable request token for the coalescing semantic worker.""" + root = Path(root).resolve() + pending_dir = _semantic_out_dir(root) / _SEMANTIC_PENDING_DIR + pending_dir.mkdir(parents=True, exist_ok=True) + token = pending_dir / f"{os.getpid()}-{uuid.uuid4().hex}" + token.write_text("1\n", encoding="utf-8") + return token + + +def _semantic_change_present(root: Path, changed_paths: list[Path]) -> bool: + """Return whether a commit includes a live semantic-corpus change. + + Classification and ignore checks intentionally reuse the extraction layer, + so an ignored workbook cannot trigger a paid run and package manifests that + look like YAML remain on the free AST path. + """ + from graphify.detect import FileType, classify_file, ignored_predicate + from graphify.watch import _read_build_excludes, _read_build_gitignore + + root = Path(root).resolve() + out = _semantic_out_dir(root) + gitignore_enabled = _read_build_gitignore(out) + ignored = ignored_predicate( + root, + extra_excludes=_read_build_excludes(out) or None, + gitignore=gitignore_enabled, + ) + cwd = Path.cwd().resolve() + semantic_types = {FileType.DOCUMENT, FileType.PAPER, FileType.IMAGE} + for raw in changed_paths: + path = Path(raw) + path = path if path.is_absolute() else cwd / path + try: + path = path.resolve() + path.relative_to(root) + except (OSError, RuntimeError, ValueError): + continue + if path.name == ".graphifyignore" or ( + path.name == ".gitignore" and gitignore_enabled + ): + return True + if not path.is_file() or ignored(path): + continue + if classify_file(path) in semantic_types: + return True + return False + + +def _dotenv_value(raw: str) -> str: + value = raw.strip() + if len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}: + return value[1:-1] + return value + + +def _semantic_environment( + root: Path, + cfg: dict[str, str | int | bool], + base_env: dict[str, str] | None = None, +) -> dict[str, str]: + """Build the semantic child's environment without evaluating dotenv code. + + Only variables used by the selected backend are admitted. Existing process + values win, matching python-dotenv's safe default and preventing a project + file from overriding credentials explicitly supplied for one run. + """ + env = dict(os.environ if base_env is None else base_env) + env_file = cfg.get("semantic_env_file") + if not isinstance(env_file, str): + return env + + root = Path(root).resolve() + rc_path = root / ".graphifyrc" + relative = _validate_relative_config_path( + env_file, option="semantic_env_file", rc_path=rc_path + ) + path = (root / relative).resolve() + try: + path.relative_to(root) + except ValueError as exc: + raise ValueError(f"semantic_env_file resolves outside the repository: {relative!r}") from exc + if not path.is_file(): + raise FileNotFoundError(f"semantic_env_file not found: {path}") + + backend = str(cfg.get("semantic_backend", "")) + allowed = _SEMANTIC_ENV_KEYS.get(backend, frozenset()) + for raw in path.read_text(encoding="utf-8").splitlines(): + line = raw.strip() + if not line or line.startswith("#"): + continue + if line.startswith("export "): + line = line[7:].lstrip() + if "=" not in line: + continue + key, value = line.split("=", 1) + key = key.strip() + if key not in allowed: + continue + env.setdefault(key, _dotenv_value(value)) + return env + + +def _run_auto_semantic_update(root: Path, changed_paths: list[Path]) -> bool: + """Run a configured semantic update after a commit, coalescing overlap. + + Git already detached the caller, so blocking on the graph rebuild lock here + never blocks ``git commit``. One token file is created per semantic commit. + A successful run removes only the tokens present when it started; tokens + created mid-run remain for the next pass, and failed/killed runs remain + durable for a later commit to retry. + """ + import subprocess + + root = Path(root).resolve() + try: + cfg = _load_graphifyrc(root) + except (OSError, ValueError) as exc: + print(f"[graphify hook] semantic automation disabled: {exc}") + return False + if cfg.get("semantic_update") != "on_commit": + return False + + out = _semantic_out_dir(root) + existing_requests = _semantic_pending_requests(out) + if _semantic_change_present(root, changed_paths): + try: + _queue_semantic_request(root) + except OSError as exc: + print(f"[graphify hook] could not queue semantic update: {exc}") + return False + elif not existing_requests: + return False + + from graphify.watch import ( + _PENDING_DRAIN_MAX_PASSES, + _drain_pending, + _rebuild_code, + _rebuild_lock, + ) + + ran = False + with _rebuild_lock(out, blocking=True) as acquired: + if not acquired: # Windows fallback currently always acquires; defensive. + return False + for _ in range(_PENDING_DRAIN_MAX_PASSES): + queued_ast = _drain_pending(out) + if queued_ast: + _rebuild_code(root, changed_paths=queued_ast, acquire_lock=False) + + requests = _semantic_pending_requests(out) + if not requests: + return ran + try: + current_cfg = _load_graphifyrc(root) + if current_cfg.get("semantic_update") != "on_commit": + return ran + env = _semantic_environment(root, current_cfg) + except (OSError, ValueError) as exc: + print(f"[graphify hook] semantic update remains queued: {exc}") + return False + + command = [ + sys.executable, + "-m", + "graphify", + "update", + str(root), + "--semantic", + "--backend", + str(current_cfg["semantic_backend"]), + ] + model = current_cfg.get("semantic_model") + if isinstance(model, str): + command.extend(("--model", model)) + if current_cfg.get("semantic_google_workspace") is True: + command.append("--google-workspace") + + print( + f"[graphify hook] running queued semantic update via " + f"{current_cfg['semantic_backend']}..." + ) + try: + semantic_timeout = int(os.environ.get("GRAPHIFY_SEMANTIC_TIMEOUT", "3600")) + except ValueError: + semantic_timeout = 3600 + if semantic_timeout <= 0: + semantic_timeout = 3600 + try: + result = subprocess.run( + command, + cwd=str(root), + env=env, + check=False, + timeout=semantic_timeout, + ) + except subprocess.TimeoutExpired: + print( + f"[graphify hook] semantic update exceeded {semantic_timeout}s; " + "request remains queued" + ) + return False + if result.returncode != 0: + print( + f"[graphify hook] semantic update failed with exit " + f"{result.returncode}; request remains queued" + ) + return False + + ran = True + for request in requests: + try: + request.unlink() + except FileNotFoundError: + pass + + if _semantic_pending_requests(out): + print("[graphify hook] semantic update burst remains queued for the next commit") + return ran + + def _git_root(path: Path) -> Path | None: """Walk up to find .git directory.""" current = path.resolve() @@ -772,6 +1106,8 @@ def status(path: Path = Path(".")) -> str: cfg = {} print(f" warning: {exc}") cfg_limit = cfg.get("viz_node_limit") + cfg_semantic = cfg.get("semantic_update") + cfg_backend = cfg.get("semantic_backend") def _check(name: str, marker: str) -> str: p = hooks_dir / name @@ -780,6 +1116,12 @@ def _check(name: str, marker: str) -> str: text = p.read_text(encoding="utf-8") if marker not in text: return "not installed (hook exists but graphify not found)" + if ( + name == "post-commit" + and cfg_semantic == "on_commit" + and "_run_auto_semantic_update(_root, changed)" not in text + ): + return "installed (out of date: semantic automation missing)" if cfg_limit is not None: # Baked as `"${GRAPHIFY_VIZ_NODE_LIMIT:-}"` so a per-run override # wins; match the default , and still accept the older bare @@ -804,4 +1146,6 @@ def _check(name: str, marker: str) -> str: res = f"post-commit: {commit}\npost-checkout: {checkout}\nmerge driver: {merge}" if cfg_limit is not None: res += f"\nviz node limit: {cfg_limit}" + if cfg_semantic == "on_commit": + res += f"\nsemantic update: on_commit ({cfg_backend})" return res diff --git a/graphify/skill-aider.md b/graphify/skill-aider.md index 4996beb787..792d04d36f 100644 --- a/graphify/skill-aider.md +++ b/graphify/skill-aider.md @@ -1252,7 +1252,9 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +To update docs, papers, and images automatically, set `semantic_update=on_commit` and an explicit `semantic_backend` in `.graphifyrc` before installing the hook. Optionally set `semantic_env_file` to a repo-relative, untracked env file. Graphify parses it without executing shell code and only admits credentials for the selected backend. Failed semantic work stays queued for retry. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/graphify/skill-devin.md b/graphify/skill-devin.md index f9be846cbf..6c2698411e 100644 --- a/graphify/skill-devin.md +++ b/graphify/skill-devin.md @@ -1382,7 +1382,9 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed, re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed, re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +To update docs, papers, and images automatically, set `semantic_update=on_commit` and an explicit `semantic_backend` in `.graphifyrc` before installing the hook. Optionally set `semantic_env_file` to a repo-relative, untracked env file. Graphify parses it without executing shell code and only admits credentials for the selected backend. Failed semantic work stays queued for retry. --- diff --git a/graphify/skills/agents/references/hooks.md b/graphify/skills/agents/references/hooks.md index 3fb74d1545..f8abfa6e95 100644 --- a/graphify/skills/agents/references/hooks.md +++ b/graphify/skills/agents/references/hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/graphify/skills/agents/references/update.md b/graphify/skills/agents/references/update.md index 3632fd4126..d3fe51d465 100644 --- a/graphify/skills/agents/references/update.md +++ b/graphify/skills/agents/references/update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/graphify/skills/amp/references/hooks.md b/graphify/skills/amp/references/hooks.md index af1ac7e720..6505b1cba4 100644 --- a/graphify/skills/amp/references/hooks.md +++ b/graphify/skills/amp/references/hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/graphify/skills/amp/references/update.md b/graphify/skills/amp/references/update.md index 3632fd4126..d3fe51d465 100644 --- a/graphify/skills/amp/references/update.md +++ b/graphify/skills/amp/references/update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/graphify/skills/claude/references/hooks.md b/graphify/skills/claude/references/hooks.md index 438b8b16be..83b4125261 100644 --- a/graphify/skills/claude/references/hooks.md +++ b/graphify/skills/claude/references/hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/graphify/skills/claude/references/update.md b/graphify/skills/claude/references/update.md index 3632fd4126..d3fe51d465 100644 --- a/graphify/skills/claude/references/update.md +++ b/graphify/skills/claude/references/update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/graphify/skills/claw/references/hooks.md b/graphify/skills/claw/references/hooks.md index 438b8b16be..83b4125261 100644 --- a/graphify/skills/claw/references/hooks.md +++ b/graphify/skills/claw/references/hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/graphify/skills/claw/references/update.md b/graphify/skills/claw/references/update.md index 3632fd4126..d3fe51d465 100644 --- a/graphify/skills/claw/references/update.md +++ b/graphify/skills/claw/references/update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/graphify/skills/codex/references/hooks.md b/graphify/skills/codex/references/hooks.md index 438b8b16be..83b4125261 100644 --- a/graphify/skills/codex/references/hooks.md +++ b/graphify/skills/codex/references/hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/graphify/skills/codex/references/update.md b/graphify/skills/codex/references/update.md index 3632fd4126..d3fe51d465 100644 --- a/graphify/skills/codex/references/update.md +++ b/graphify/skills/codex/references/update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/graphify/skills/copilot/references/hooks.md b/graphify/skills/copilot/references/hooks.md index 438b8b16be..83b4125261 100644 --- a/graphify/skills/copilot/references/hooks.md +++ b/graphify/skills/copilot/references/hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/graphify/skills/copilot/references/update.md b/graphify/skills/copilot/references/update.md index 3632fd4126..d3fe51d465 100644 --- a/graphify/skills/copilot/references/update.md +++ b/graphify/skills/copilot/references/update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/graphify/skills/droid/references/hooks.md b/graphify/skills/droid/references/hooks.md index 438b8b16be..83b4125261 100644 --- a/graphify/skills/droid/references/hooks.md +++ b/graphify/skills/droid/references/hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/graphify/skills/droid/references/update.md b/graphify/skills/droid/references/update.md index 3632fd4126..d3fe51d465 100644 --- a/graphify/skills/droid/references/update.md +++ b/graphify/skills/droid/references/update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/graphify/skills/kilo/references/hooks.md b/graphify/skills/kilo/references/hooks.md index 438b8b16be..83b4125261 100644 --- a/graphify/skills/kilo/references/hooks.md +++ b/graphify/skills/kilo/references/hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/graphify/skills/kilo/references/update.md b/graphify/skills/kilo/references/update.md index 3632fd4126..d3fe51d465 100644 --- a/graphify/skills/kilo/references/update.md +++ b/graphify/skills/kilo/references/update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/graphify/skills/kiro/references/hooks.md b/graphify/skills/kiro/references/hooks.md index 438b8b16be..83b4125261 100644 --- a/graphify/skills/kiro/references/hooks.md +++ b/graphify/skills/kiro/references/hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/graphify/skills/kiro/references/update.md b/graphify/skills/kiro/references/update.md index 3632fd4126..d3fe51d465 100644 --- a/graphify/skills/kiro/references/update.md +++ b/graphify/skills/kiro/references/update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/graphify/skills/opencode/references/hooks.md b/graphify/skills/opencode/references/hooks.md index 438b8b16be..83b4125261 100644 --- a/graphify/skills/opencode/references/hooks.md +++ b/graphify/skills/opencode/references/hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/graphify/skills/opencode/references/update.md b/graphify/skills/opencode/references/update.md index 3632fd4126..d3fe51d465 100644 --- a/graphify/skills/opencode/references/update.md +++ b/graphify/skills/opencode/references/update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/graphify/skills/pi/references/hooks.md b/graphify/skills/pi/references/hooks.md index 438b8b16be..83b4125261 100644 --- a/graphify/skills/pi/references/hooks.md +++ b/graphify/skills/pi/references/hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/graphify/skills/pi/references/update.md b/graphify/skills/pi/references/update.md index 3632fd4126..d3fe51d465 100644 --- a/graphify/skills/pi/references/update.md +++ b/graphify/skills/pi/references/update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/graphify/skills/trae/references/hooks.md b/graphify/skills/trae/references/hooks.md index 7c04d5b0b8..57ae19a2de 100644 --- a/graphify/skills/trae/references/hooks.md +++ b/graphify/skills/trae/references/hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/graphify/skills/trae/references/update.md b/graphify/skills/trae/references/update.md index 3632fd4126..d3fe51d465 100644 --- a/graphify/skills/trae/references/update.md +++ b/graphify/skills/trae/references/update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/graphify/skills/vscode/references/hooks.md b/graphify/skills/vscode/references/hooks.md index 438b8b16be..83b4125261 100644 --- a/graphify/skills/vscode/references/hooks.md +++ b/graphify/skills/vscode/references/hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/graphify/skills/vscode/references/update.md b/graphify/skills/vscode/references/update.md index 3632fd4126..d3fe51d465 100644 --- a/graphify/skills/vscode/references/update.md +++ b/graphify/skills/vscode/references/update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/graphify/skills/windows/references/hooks.md b/graphify/skills/windows/references/hooks.md index 438b8b16be..83b4125261 100644 --- a/graphify/skills/windows/references/hooks.md +++ b/graphify/skills/windows/references/hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/graphify/skills/windows/references/update.md b/graphify/skills/windows/references/update.md index 3632fd4126..d3fe51d465 100644 --- a/graphify/skills/windows/references/update.md +++ b/graphify/skills/windows/references/update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/tests/test_hooks.py b/tests/test_hooks.py index 3d8bcfc2ac..403d648d46 100644 --- a/tests/test_hooks.py +++ b/tests/test_hooks.py @@ -1002,3 +1002,325 @@ def test_both_hooks_configured(tmp_path): for name in ("post-commit", "post-checkout"): hook_text = (repo / ".git" / "hooks" / name).read_text() assert 'export GRAPHIFY_VIZ_NODE_LIMIT="${GRAPHIFY_VIZ_NODE_LIMIT:-42}"' in hook_text + + +# ── automatic semantic updates on commit ──────────────────────────────────── + +def test_graphifyrc_parses_semantic_commit_automation(tmp_path): + from graphify.hooks import _load_graphifyrc + + (tmp_path / ".graphifyrc").write_text( + "semantic_update=on_commit\n" + "semantic_backend=kimi\n" + "semantic_model=kimi-k2.6\n" + "semantic_env_file=.env.local\n" + "semantic_google_workspace=true\n", + encoding="utf-8", + ) + + cfg = _load_graphifyrc(tmp_path) + + assert cfg["semantic_update"] == "on_commit" + assert cfg["semantic_backend"] == "kimi" + assert cfg["semantic_model"] == "kimi-k2.6" + assert cfg["semantic_env_file"] == ".env.local" + assert cfg["semantic_google_workspace"] is True + + +@pytest.mark.parametrize( + "content,match", + [ + ("semantic_update=always\nsemantic_backend=kimi\n", "semantic_update"), + ("semantic_update=on_commit\n", "semantic_backend"), + ("semantic_update=on_commit\nsemantic_backend=unknown\n", "semantic_backend"), + ( + "semantic_update=on_commit\nsemantic_backend=kimi\nsemantic_env_file=../keys.env\n", + "semantic_env_file", + ), + ( + "semantic_update=on_commit\nsemantic_backend=kimi\nsemantic_google_workspace=yes\n", + "semantic_google_workspace", + ), + ], +) +def test_graphifyrc_rejects_unsafe_semantic_automation(content, match, tmp_path): + from graphify.hooks import _load_graphifyrc + + (tmp_path / ".graphifyrc").write_text(content, encoding="utf-8") + + with pytest.raises(ValueError, match=match): + _load_graphifyrc(tmp_path) + + +def test_semantic_env_loader_only_admits_selected_backend_keys(tmp_path): + from graphify.hooks import _semantic_environment + + (tmp_path / ".env.local").write_text( + "MOONSHOT_API_KEY=file-key\n" + "KIMI_BASE_URL=https://attacker.invalid/v1\n" + "OPENAI_API_KEY=must-not-load\n" + "PYTHONPATH=/tmp/hostile\n" + "PATH=/tmp/hostile\n", + encoding="utf-8", + ) + cfg = { + "semantic_backend": "kimi", + "semantic_env_file": ".env.local", + } + + env = _semantic_environment(tmp_path, cfg, {"PATH": "/usr/bin"}) + + assert env["MOONSHOT_API_KEY"] == "file-key" + assert "KIMI_BASE_URL" not in env + assert "OPENAI_API_KEY" not in env + assert "PYTHONPATH" not in env + assert env["PATH"] == "/usr/bin" + + +def test_semantic_env_loader_preserves_explicit_process_credentials(tmp_path): + from graphify.hooks import _semantic_environment + + (tmp_path / ".env.local").write_text( + "export MOONSHOT_API_KEY=file-key\n", + encoding="utf-8", + ) + + env = _semantic_environment( + tmp_path, + {"semantic_backend": "kimi", "semantic_env_file": ".env.local"}, + {"MOONSHOT_API_KEY": "process-key"}, + ) + + assert env["MOONSHOT_API_KEY"] == "process-key" + + +def test_auto_semantic_update_ignores_code_only_commits(tmp_path, monkeypatch): + from graphify.hooks import _run_auto_semantic_update + + (tmp_path / ".graphifyrc").write_text( + "semantic_update=on_commit\nsemantic_backend=kimi\n", + encoding="utf-8", + ) + source = tmp_path / "app.py" + source.write_text("x = 1\n", encoding="utf-8") + calls = [] + monkeypatch.setattr("subprocess.run", lambda *a, **kw: calls.append((a, kw))) + + assert _run_auto_semantic_update(tmp_path, [source]) is False + assert calls == [] + assert not (tmp_path / "graphify-out" / ".semantic_pending").exists() + + +def test_auto_semantic_update_honors_graphifyignore(tmp_path, monkeypatch): + from graphify.hooks import _run_auto_semantic_update + + (tmp_path / ".graphifyrc").write_text( + "semantic_update=on_commit\nsemantic_backend=kimi\n", + encoding="utf-8", + ) + (tmp_path / ".graphifyignore").write_text("private/\n", encoding="utf-8") + private = tmp_path / "private" + private.mkdir() + doc = private / "customer.md" + doc.write_text("restricted\n", encoding="utf-8") + calls = [] + monkeypatch.setattr("subprocess.run", lambda *a, **kw: calls.append((a, kw))) + + assert _run_auto_semantic_update(tmp_path, [doc]) is False + assert calls == [] + + +def test_gitignore_change_does_not_trigger_when_build_disabled_it(tmp_path, monkeypatch): + from graphify.hooks import _run_auto_semantic_update + + (tmp_path / ".graphifyrc").write_text( + "semantic_update=on_commit\nsemantic_backend=kimi\n", + encoding="utf-8", + ) + out = tmp_path / "graphify-out" + out.mkdir() + (out / ".graphify_build.json").write_text( + '{"gitignore": false}', encoding="utf-8" + ) + gitignore = tmp_path / ".gitignore" + gitignore.write_text("private/\n", encoding="utf-8") + calls = [] + monkeypatch.setattr("subprocess.run", lambda *a, **kw: calls.append((a, kw))) + + assert _run_auto_semantic_update(tmp_path, [gitignore]) is False + assert calls == [] + + +def test_auto_semantic_update_runs_native_command_with_safe_env(tmp_path, monkeypatch): + from graphify.hooks import _run_auto_semantic_update + + (tmp_path / ".graphifyrc").write_text( + "semantic_update=on_commit\n" + "semantic_backend=kimi\n" + "semantic_model=kimi-k2.6\n" + "semantic_env_file=.env.local\n" + "semantic_google_workspace=true\n", + encoding="utf-8", + ) + (tmp_path / ".env.local").write_text( + "MOONSHOT_API_KEY=secret\nPYTHONPATH=/tmp/hostile\n", + encoding="utf-8", + ) + doc = tmp_path / "notes.md" + doc.write_text("# changed\n", encoding="utf-8") + calls = [] + + def fake_run(command, **kwargs): + calls.append((command, kwargs)) + return SimpleNamespace(returncode=0) + + monkeypatch.setattr("subprocess.run", fake_run) + + assert _run_auto_semantic_update(tmp_path, [doc]) is True + assert len(calls) == 1 + command, kwargs = calls[0] + assert command == [ + os.sys.executable, + "-m", + "graphify", + "update", + str(tmp_path.resolve()), + "--semantic", + "--backend", + "kimi", + "--model", + "kimi-k2.6", + "--google-workspace", + ] + assert kwargs["cwd"] == str(tmp_path.resolve()) + assert kwargs["check"] is False + assert kwargs["timeout"] == 3600 + assert kwargs["env"]["MOONSHOT_API_KEY"] == "secret" + assert kwargs["env"].get("PYTHONPATH") != "/tmp/hostile" + pending = tmp_path / "graphify-out" / ".semantic_pending" + assert not pending.exists() or not any(pending.iterdir()) + + +def test_failed_auto_semantic_update_stays_queued_for_retry(tmp_path, monkeypatch): + from graphify.hooks import _run_auto_semantic_update + + (tmp_path / ".graphifyrc").write_text( + "semantic_update=on_commit\nsemantic_backend=claude-cli\n", + encoding="utf-8", + ) + doc = tmp_path / "notes.md" + doc.write_text("# changed\n", encoding="utf-8") + code = tmp_path / "app.py" + code.write_text("x = 1\n", encoding="utf-8") + results = iter((1, 0)) + monkeypatch.setattr( + "subprocess.run", + lambda *a, **kw: SimpleNamespace(returncode=next(results)), + ) + + assert _run_auto_semantic_update(tmp_path, [doc]) is False + pending = tmp_path / "graphify-out" / ".semantic_pending" + assert pending.is_dir() and any(pending.iterdir()) + + # Any later commit retries durable semantic work; no person has to notice + # the failed background run or repeat the semantic command manually. + assert _run_auto_semantic_update(tmp_path, [code]) is True + assert not any(pending.iterdir()) + + +def test_timed_out_auto_semantic_update_stays_queued(tmp_path, monkeypatch): + from graphify.hooks import _run_auto_semantic_update + + (tmp_path / ".graphifyrc").write_text( + "semantic_update=on_commit\nsemantic_backend=claude-cli\n", + encoding="utf-8", + ) + doc = tmp_path / "notes.md" + doc.write_text("# changed\n", encoding="utf-8") + + def time_out(*args, **kwargs): + raise subprocess.TimeoutExpired(args[0], kwargs["timeout"]) + + monkeypatch.setattr("subprocess.run", time_out) + + assert _run_auto_semantic_update(tmp_path, [doc]) is False + pending = tmp_path / "graphify-out" / ".semantic_pending" + assert pending.is_dir() and any(pending.iterdir()) + + +def test_auto_semantic_worker_coalesces_new_request_arriving_mid_run(tmp_path, monkeypatch): + from graphify.hooks import _run_auto_semantic_update + + (tmp_path / ".graphifyrc").write_text( + "semantic_update=on_commit\nsemantic_backend=claude-cli\n", + encoding="utf-8", + ) + first = tmp_path / "first.md" + second = tmp_path / "second.md" + first.write_text("first\n", encoding="utf-8") + second.write_text("second\n", encoding="utf-8") + calls = [] + + def fake_run(*args, **kwargs): + calls.append(args) + if len(calls) == 1: + # A second hook can safely add work while this worker holds the + # graph rebuild lock. Model that arrival without recursively + # trying to acquire the same lock in this test process. + from graphify.hooks import _queue_semantic_request + _queue_semantic_request(tmp_path) + return SimpleNamespace(returncode=0) + + monkeypatch.setattr("subprocess.run", fake_run) + + assert _run_auto_semantic_update(tmp_path, [first]) is True + assert len(calls) == 2 + pending = tmp_path / "graphify-out" / ".semantic_pending" + assert not any(pending.iterdir()) + + +def test_installed_post_commit_hook_invokes_auto_semantic_worker(tmp_path): + repo = _make_git_repo(tmp_path) + install(repo) + + commit_hook = (repo / ".git" / "hooks" / "post-commit").read_text() + checkout_hook = (repo / ".git" / "hooks" / "post-checkout").read_text() + + assert "_run_auto_semantic_update(_root, changed)" in commit_hook + assert "_run_auto_semantic_update" not in checkout_hook + + +def test_hook_status_reports_semantic_commit_automation(tmp_path): + repo = _make_git_repo(tmp_path) + (repo / ".graphifyrc").write_text( + "semantic_update=on_commit\nsemantic_backend=kimi\n", + encoding="utf-8", + ) + install(repo) + + result = status(repo) + + assert "semantic update: on_commit (kimi)" in result + assert "out of date" not in result + + +def test_hook_status_marks_pre_automation_post_commit_hook_out_of_date(tmp_path): + repo = _make_git_repo(tmp_path) + install(repo) + hook = repo / ".git" / "hooks" / "post-commit" + hook.write_text( + hook.read_text(encoding="utf-8").replace( + " from graphify.hooks import _run_auto_semantic_update\n" + " _run_auto_semantic_update(_root, changed)\n", + "", + ), + encoding="utf-8", + ) + (repo / ".graphifyrc").write_text( + "semantic_update=on_commit\nsemantic_backend=kimi\n", + encoding="utf-8", + ) + + result = status(repo) + + assert "post-commit: installed (out of date: semantic automation missing)" in result diff --git a/tests/test_skillgen.py b/tests/test_skillgen.py index cf116869f2..edc8bc23fa 100644 --- a/tests/test_skillgen.py +++ b/tests/test_skillgen.py @@ -687,6 +687,22 @@ def test_generated_runbooks_pass_root_to_save_manifest(): assert checked >= 4, f"expected save_manifest calls across the runbooks, found {checked}" +def test_split_skills_prefer_native_semantic_update_conductor(): + """Every split-skill host reuses the CLI semantic-update conductor. + + Keeping this assertion at the rendered-artifact boundary prevents one of + the shared-reference hosts from silently drifting back to the duplicated + manual runbook. + """ + platforms = gen.load_platforms() + for key, platform in platforms.items(): + if platform.bucket != "split": + continue + body = "\n".join(artifact.content for artifact in gen.render(platform)) + assert "graphify update INPUT_PATH --semantic" in body, key + assert "Compatibility fallback" in body, key + + def test_devin_keeps_its_multi_field_frontmatter(): """devin renders inline, so its 4+-field frontmatter is preserved verbatim.""" platforms = gen.load_platforms() diff --git a/tests/test_update_semantic_cli.py b/tests/test_update_semantic_cli.py new file mode 100644 index 0000000000..460bea4ccc --- /dev/null +++ b/tests/test_update_semantic_cli.py @@ -0,0 +1,181 @@ +"""Behavioral tests for native mixed-corpus ``graphify update``.""" +from __future__ import annotations + +import json + +import pytest + +import graphify.__main__ as mainmod + + +def _run_main(monkeypatch, argv: list[str]) -> None: + monkeypatch.setattr(mainmod.sys, "argv", argv) + try: + mainmod.main() + except SystemExit as exc: + assert exc.code in (None, 0), f"unexpected exit code {exc.code}" + + +def test_help_distinguishes_semantic_and_code_only_updates(monkeypatch, capsys): + monkeypatch.setattr(mainmod.sys, "argv", ["graphify", "--help"]) + + mainmod.main() + + help_text = capsys.readouterr().out + assert "update " in help_text + assert "--semantic" in help_text + assert "documents" in help_text + assert "LLM" in help_text + + +@pytest.mark.parametrize("incompatible", ["--no-cluster", "--code-only"]) +def test_update_semantic_rejects_incomplete_modes( + monkeypatch, tmp_path, capsys, incompatible +): + (tmp_path / "app.py").write_text("value = 1\n") + monkeypatch.setattr(mainmod, "_check_skill_version", lambda _: None) + monkeypatch.setattr( + mainmod.sys, + "argv", + ["graphify", "update", str(tmp_path), "--semantic", incompatible], + ) + + with pytest.raises(SystemExit) as exc_info: + mainmod.main() + + assert exc_info.value.code == 2 + assert "cannot be combined" in capsys.readouterr().err + + +def test_update_semantic_defaults_to_current_directory(monkeypatch, tmp_path): + (tmp_path / "app.py").write_text("value = 1\n") + monkeypatch.chdir(tmp_path) + monkeypatch.setattr(mainmod, "_check_skill_version", lambda _: None) + + _run_main( + monkeypatch, + ["graphify", "update", "--semantic", "--no-label", "--no-viz"], + ) + + assert (tmp_path / "graphify-out" / "GRAPH_REPORT.md").is_file() + + +def test_update_semantic_accepts_path_after_flags(monkeypatch, tmp_path): + corpus = tmp_path / "corpus" + corpus.mkdir() + (corpus / "app.py").write_text("value = 1\n") + monkeypatch.chdir(tmp_path) + monkeypatch.setattr(mainmod, "_check_skill_version", lambda _: None) + + _run_main( + monkeypatch, + [ + "graphify", + "update", + "--semantic", + "--no-label", + "--no-viz", + str(corpus), + ], + ) + + assert (corpus / "graphify-out" / "GRAPH_REPORT.md").is_file() + + +def test_update_semantic_rejects_unknown_options(monkeypatch, tmp_path, capsys): + monkeypatch.setattr(mainmod, "_check_skill_version", lambda _: None) + monkeypatch.setattr( + mainmod.sys, + "argv", + ["graphify", "update", str(tmp_path), "--semantic", "--typo"], + ) + + with pytest.raises(SystemExit) as exc_info: + mainmod.main() + + assert exc_info.value.code == 2 + assert "unknown semantic update option: --typo" in capsys.readouterr().err + + +def test_update_semantic_refreshes_changed_docs_and_final_outputs( + monkeypatch, tmp_path, capsys +): + """One command refreshes mixed code/docs through the report stage. + + The third unchanged run also proves that the native update path retains + extract's incremental cache instead of paying to re-extract the document. + """ + corpus = tmp_path / "corpus" + corpus.mkdir() + (corpus / "app.py").write_text("def answer():\n return 42\n") + guide = corpus / "guide.md" + guide.write_text("# Guide\nVersion one explains the answer.\n") + + monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-test-fake-key") + monkeypatch.setattr(mainmod, "_check_skill_version", lambda _: None) + calls: list[tuple[str, str]] = [] + + def _extract_docs(paths, **kwargs): + text = paths[0].read_text() + version = "v2" if "Version two" in text else "v1" + calls.append((version, kwargs["backend"])) + chunk = { + "nodes": [ + { + "id": f"guide_{version}", + "label": f"Guide {version}", + "type": "concept", + "source_file": "guide.md", + "file_type": "document", + } + ], + "edges": [], + "hyperedges": [], + } + on_chunk = kwargs.get("on_chunk_done") + if on_chunk: + on_chunk(0, 1, chunk) + return {**chunk, "input_tokens": 10, "output_tokens": 5} + + monkeypatch.setattr("graphify.llm.extract_corpus_parallel", _extract_docs) + command = [ + "graphify", + "update", + str(corpus), + "--semantic", + "--backend", + "claude", + "--no-label", + "--no-viz", + "--no-dedup", + "--min-community-size=1", + ] + + _run_main(monkeypatch, command) + + out = corpus / "graphify-out" + graph = json.loads((out / "graph.json").read_text()) + first_nodes = graph["nodes"] + assert "guide_v1" in {node["id"] for node in first_nodes} + assert any(node.get("source_file") == "app.py" for node in first_nodes) + assert (out / "GRAPH_REPORT.md").is_file() + assert "Guide v1" in (out / "GRAPH_REPORT.md").read_text() + assert not (out / ".graphify_labels.json").exists() + assert not (out / "graph.html").exists() + + guide.write_text("# Guide\nVersion two explains the answer.\n") + _run_main(monkeypatch, command) + + graph = json.loads((out / "graph.json").read_text()) + node_ids = {node["id"] for node in graph["nodes"]} + assert "guide_v2" in node_ids + assert "guide_v1" not in node_ids + report = (out / "GRAPH_REPORT.md").read_text() + assert "Guide v2" in report + assert "Guide v1" not in report + + _run_main(monkeypatch, command) + + assert calls == [("v1", "claude"), ("v2", "claude")] + assert not (out / "graph.html").exists() + assert "semantic update complete" in capsys.readouterr().out.lower() diff --git a/tools/skillgen/expected/graphify__skill-aider.md b/tools/skillgen/expected/graphify__skill-aider.md index 4996beb787..792d04d36f 100644 --- a/tools/skillgen/expected/graphify__skill-aider.md +++ b/tools/skillgen/expected/graphify__skill-aider.md @@ -1252,7 +1252,9 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +To update docs, papers, and images automatically, set `semantic_update=on_commit` and an explicit `semantic_backend` in `.graphifyrc` before installing the hook. Optionally set `semantic_env_file` to a repo-relative, untracked env file. Graphify parses it without executing shell code and only admits credentials for the selected backend. Failed semantic work stays queued for retry. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/tools/skillgen/expected/graphify__skill-devin.md b/tools/skillgen/expected/graphify__skill-devin.md index f9be846cbf..6c2698411e 100644 --- a/tools/skillgen/expected/graphify__skill-devin.md +++ b/tools/skillgen/expected/graphify__skill-devin.md @@ -1382,7 +1382,9 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed, re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed, re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +To update docs, papers, and images automatically, set `semantic_update=on_commit` and an explicit `semantic_backend` in `.graphifyrc` before installing the hook. Optionally set `semantic_env_file` to a repo-relative, untracked env file. Graphify parses it without executing shell code and only admits credentials for the selected backend. Failed semantic work stays queued for retry. --- diff --git a/tools/skillgen/expected/graphify__skills__agents__references__hooks.md b/tools/skillgen/expected/graphify__skills__agents__references__hooks.md index 3fb74d1545..f8abfa6e95 100644 --- a/tools/skillgen/expected/graphify__skills__agents__references__hooks.md +++ b/tools/skillgen/expected/graphify__skills__agents__references__hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/tools/skillgen/expected/graphify__skills__agents__references__update.md b/tools/skillgen/expected/graphify__skills__agents__references__update.md index 3632fd4126..d3fe51d465 100644 --- a/tools/skillgen/expected/graphify__skills__agents__references__update.md +++ b/tools/skillgen/expected/graphify__skills__agents__references__update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/tools/skillgen/expected/graphify__skills__amp__references__hooks.md b/tools/skillgen/expected/graphify__skills__amp__references__hooks.md index af1ac7e720..6505b1cba4 100644 --- a/tools/skillgen/expected/graphify__skills__amp__references__hooks.md +++ b/tools/skillgen/expected/graphify__skills__amp__references__hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/tools/skillgen/expected/graphify__skills__amp__references__update.md b/tools/skillgen/expected/graphify__skills__amp__references__update.md index 3632fd4126..d3fe51d465 100644 --- a/tools/skillgen/expected/graphify__skills__amp__references__update.md +++ b/tools/skillgen/expected/graphify__skills__amp__references__update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/tools/skillgen/expected/graphify__skills__claude__references__hooks.md b/tools/skillgen/expected/graphify__skills__claude__references__hooks.md index 438b8b16be..83b4125261 100644 --- a/tools/skillgen/expected/graphify__skills__claude__references__hooks.md +++ b/tools/skillgen/expected/graphify__skills__claude__references__hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/tools/skillgen/expected/graphify__skills__claude__references__update.md b/tools/skillgen/expected/graphify__skills__claude__references__update.md index 3632fd4126..d3fe51d465 100644 --- a/tools/skillgen/expected/graphify__skills__claude__references__update.md +++ b/tools/skillgen/expected/graphify__skills__claude__references__update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/tools/skillgen/expected/graphify__skills__claw__references__hooks.md b/tools/skillgen/expected/graphify__skills__claw__references__hooks.md index 438b8b16be..83b4125261 100644 --- a/tools/skillgen/expected/graphify__skills__claw__references__hooks.md +++ b/tools/skillgen/expected/graphify__skills__claw__references__hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/tools/skillgen/expected/graphify__skills__claw__references__update.md b/tools/skillgen/expected/graphify__skills__claw__references__update.md index 3632fd4126..d3fe51d465 100644 --- a/tools/skillgen/expected/graphify__skills__claw__references__update.md +++ b/tools/skillgen/expected/graphify__skills__claw__references__update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/tools/skillgen/expected/graphify__skills__codex__references__hooks.md b/tools/skillgen/expected/graphify__skills__codex__references__hooks.md index 438b8b16be..83b4125261 100644 --- a/tools/skillgen/expected/graphify__skills__codex__references__hooks.md +++ b/tools/skillgen/expected/graphify__skills__codex__references__hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/tools/skillgen/expected/graphify__skills__codex__references__update.md b/tools/skillgen/expected/graphify__skills__codex__references__update.md index 3632fd4126..d3fe51d465 100644 --- a/tools/skillgen/expected/graphify__skills__codex__references__update.md +++ b/tools/skillgen/expected/graphify__skills__codex__references__update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/tools/skillgen/expected/graphify__skills__copilot__references__hooks.md b/tools/skillgen/expected/graphify__skills__copilot__references__hooks.md index 438b8b16be..83b4125261 100644 --- a/tools/skillgen/expected/graphify__skills__copilot__references__hooks.md +++ b/tools/skillgen/expected/graphify__skills__copilot__references__hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/tools/skillgen/expected/graphify__skills__copilot__references__update.md b/tools/skillgen/expected/graphify__skills__copilot__references__update.md index 3632fd4126..d3fe51d465 100644 --- a/tools/skillgen/expected/graphify__skills__copilot__references__update.md +++ b/tools/skillgen/expected/graphify__skills__copilot__references__update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/tools/skillgen/expected/graphify__skills__droid__references__hooks.md b/tools/skillgen/expected/graphify__skills__droid__references__hooks.md index 438b8b16be..83b4125261 100644 --- a/tools/skillgen/expected/graphify__skills__droid__references__hooks.md +++ b/tools/skillgen/expected/graphify__skills__droid__references__hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/tools/skillgen/expected/graphify__skills__droid__references__update.md b/tools/skillgen/expected/graphify__skills__droid__references__update.md index 3632fd4126..d3fe51d465 100644 --- a/tools/skillgen/expected/graphify__skills__droid__references__update.md +++ b/tools/skillgen/expected/graphify__skills__droid__references__update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/tools/skillgen/expected/graphify__skills__kilo__references__hooks.md b/tools/skillgen/expected/graphify__skills__kilo__references__hooks.md index 438b8b16be..83b4125261 100644 --- a/tools/skillgen/expected/graphify__skills__kilo__references__hooks.md +++ b/tools/skillgen/expected/graphify__skills__kilo__references__hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/tools/skillgen/expected/graphify__skills__kilo__references__update.md b/tools/skillgen/expected/graphify__skills__kilo__references__update.md index 3632fd4126..d3fe51d465 100644 --- a/tools/skillgen/expected/graphify__skills__kilo__references__update.md +++ b/tools/skillgen/expected/graphify__skills__kilo__references__update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/tools/skillgen/expected/graphify__skills__kiro__references__hooks.md b/tools/skillgen/expected/graphify__skills__kiro__references__hooks.md index 438b8b16be..83b4125261 100644 --- a/tools/skillgen/expected/graphify__skills__kiro__references__hooks.md +++ b/tools/skillgen/expected/graphify__skills__kiro__references__hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/tools/skillgen/expected/graphify__skills__kiro__references__update.md b/tools/skillgen/expected/graphify__skills__kiro__references__update.md index 3632fd4126..d3fe51d465 100644 --- a/tools/skillgen/expected/graphify__skills__kiro__references__update.md +++ b/tools/skillgen/expected/graphify__skills__kiro__references__update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/tools/skillgen/expected/graphify__skills__opencode__references__hooks.md b/tools/skillgen/expected/graphify__skills__opencode__references__hooks.md index 438b8b16be..83b4125261 100644 --- a/tools/skillgen/expected/graphify__skills__opencode__references__hooks.md +++ b/tools/skillgen/expected/graphify__skills__opencode__references__hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/tools/skillgen/expected/graphify__skills__opencode__references__update.md b/tools/skillgen/expected/graphify__skills__opencode__references__update.md index 3632fd4126..d3fe51d465 100644 --- a/tools/skillgen/expected/graphify__skills__opencode__references__update.md +++ b/tools/skillgen/expected/graphify__skills__opencode__references__update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/tools/skillgen/expected/graphify__skills__pi__references__hooks.md b/tools/skillgen/expected/graphify__skills__pi__references__hooks.md index 438b8b16be..83b4125261 100644 --- a/tools/skillgen/expected/graphify__skills__pi__references__hooks.md +++ b/tools/skillgen/expected/graphify__skills__pi__references__hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/tools/skillgen/expected/graphify__skills__pi__references__update.md b/tools/skillgen/expected/graphify__skills__pi__references__update.md index 3632fd4126..d3fe51d465 100644 --- a/tools/skillgen/expected/graphify__skills__pi__references__update.md +++ b/tools/skillgen/expected/graphify__skills__pi__references__update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/tools/skillgen/expected/graphify__skills__trae__references__hooks.md b/tools/skillgen/expected/graphify__skills__trae__references__hooks.md index 7c04d5b0b8..57ae19a2de 100644 --- a/tools/skillgen/expected/graphify__skills__trae__references__hooks.md +++ b/tools/skillgen/expected/graphify__skills__trae__references__hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/tools/skillgen/expected/graphify__skills__trae__references__update.md b/tools/skillgen/expected/graphify__skills__trae__references__update.md index 3632fd4126..d3fe51d465 100644 --- a/tools/skillgen/expected/graphify__skills__trae__references__update.md +++ b/tools/skillgen/expected/graphify__skills__trae__references__update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/tools/skillgen/expected/graphify__skills__vscode__references__hooks.md b/tools/skillgen/expected/graphify__skills__vscode__references__hooks.md index 438b8b16be..83b4125261 100644 --- a/tools/skillgen/expected/graphify__skills__vscode__references__hooks.md +++ b/tools/skillgen/expected/graphify__skills__vscode__references__hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/tools/skillgen/expected/graphify__skills__vscode__references__update.md b/tools/skillgen/expected/graphify__skills__vscode__references__update.md index 3632fd4126..d3fe51d465 100644 --- a/tools/skillgen/expected/graphify__skills__vscode__references__update.md +++ b/tools/skillgen/expected/graphify__skills__vscode__references__update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/tools/skillgen/expected/graphify__skills__windows__references__hooks.md b/tools/skillgen/expected/graphify__skills__windows__references__hooks.md index 438b8b16be..83b4125261 100644 --- a/tools/skillgen/expected/graphify__skills__windows__references__hooks.md +++ b/tools/skillgen/expected/graphify__skills__windows__references__hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/tools/skillgen/expected/graphify__skills__windows__references__update.md b/tools/skillgen/expected/graphify__skills__windows__references__update.md index 3632fd4126..d3fe51d465 100644 --- a/tools/skillgen/expected/graphify__skills__windows__references__update.md +++ b/tools/skillgen/expected/graphify__skills__windows__references__update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/tools/skillgen/fragments/core/aider.md b/tools/skillgen/fragments/core/aider.md index 4996beb787..792d04d36f 100644 --- a/tools/skillgen/fragments/core/aider.md +++ b/tools/skillgen/fragments/core/aider.md @@ -1252,7 +1252,9 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +To update docs, papers, and images automatically, set `semantic_update=on_commit` and an explicit `semantic_backend` in `.graphifyrc` before installing the hook. Optionally set `semantic_env_file` to a repo-relative, untracked env file. Graphify parses it without executing shell code and only admits credentials for the selected backend. Failed semantic work stays queued for retry. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/tools/skillgen/fragments/core/devin.md b/tools/skillgen/fragments/core/devin.md index f9be846cbf..6c2698411e 100644 --- a/tools/skillgen/fragments/core/devin.md +++ b/tools/skillgen/fragments/core/devin.md @@ -1382,7 +1382,9 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed, re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed, re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +To update docs, papers, and images automatically, set `semantic_update=on_commit` and an explicit `semantic_backend` in `.graphifyrc` before installing the hook. Optionally set `semantic_env_file` to a repo-relative, untracked env file. Graphify parses it without executing shell code and only admits credentials for the selected backend. Failed semantic work stays queued for retry. --- diff --git a/tools/skillgen/fragments/references/host/hooks-agents-md.md b/tools/skillgen/fragments/references/host/hooks-agents-md.md index 26b9a8f749..b2b29401a0 100644 --- a/tools/skillgen/fragments/references/host/hooks-agents-md.md +++ b/tools/skillgen/fragments/references/host/hooks-agents-md.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/tools/skillgen/fragments/references/shared/hooks.md b/tools/skillgen/fragments/references/shared/hooks.md index 438b8b16be..83b4125261 100644 --- a/tools/skillgen/fragments/references/shared/hooks.md +++ b/tools/skillgen/fragments/references/shared/hooks.md @@ -12,7 +12,20 @@ graphify hook uninstall # remove graphify hook status # check ``` -After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those. +After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. This default path uses no LLM and has no API cost. + +For a project where docs, papers, or images must stay current without a manual step, add an explicit opt-in before installing the hook: + +```ini +# .graphifyrc +semantic_update=on_commit +semantic_backend=kimi +semantic_env_file=.env.local +# semantic_model=kimi-k2.6 # optional +# semantic_google_workspace=true # optional +``` + +The semantic worker is detached, coalesces overlapping commits, honors ignore rules, and leaves failed work queued for retry. The env file is never executed; Graphify only reads credential variables for the selected backend, preserves credentials already in the process environment, and ignores endpoint variables. Keep the env file untracked. Without this config, the hook remains AST-only. If a post-commit hook already exists, graphify appends to it rather than replacing it. diff --git a/tools/skillgen/fragments/references/shared/update.md b/tools/skillgen/fragments/references/shared/update.md index 3632fd4126..d3fe51d465 100644 --- a/tools/skillgen/fragments/references/shared/update.md +++ b/tools/skillgen/fragments/references/shared/update.md @@ -6,6 +6,22 @@ Load this only when the user passed `--update` or `--cluster-only`. A first-time Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time. +### Native conductor (preferred) + +Run the complete incremental pipeline through Graphify's tested CLI conductor first: + +```bash +graphify update INPUT_PATH --semantic +``` + +Replace `INPUT_PATH` with the path being updated. Append any supported flags the user supplied, such as `--backend`, `--model`, `--no-label`, or `--no-viz`. Keep `--semantic`: bare `graphify update` intentionally remains the AST-only fast path. The conductor detects changes, reuses the semantic cache, prunes stale sources, refreshes code and content, and regenerates communities and reports. A code-only change needs no LLM backend. + +If the command exits successfully, do not replay the compatibility flow below. Use that flow only when the installed binary rejects `--semantic`, or when it reports that changed semantic files need an unconfigured backend and the current host can perform semantic extraction itself. For any other failure, stop and report the error instead of mutating the graph through a second path. + +### Compatibility fallback + +The remaining steps preserve host-agent extraction for older Graphify binaries or environments without a configured native semantic backend. + ```bash $(cat graphify-out/.graphify_python) -c " import sys, json diff --git a/tools/skillgen/gen.py b/tools/skillgen/gen.py index 09e19ede00..f585939f31 100644 --- a/tools/skillgen/gen.py +++ b/tools/skillgen/gen.py @@ -1142,6 +1142,25 @@ def _is_community_label_export_fix_line(line: str) -> bool: ) +def _is_semantic_commit_automation_doc_line(line: str) -> bool: + """Whether a monolith line documents opt-in semantic commit automation. + + The old hook guidance required a manual doc/image refresh. The native + semantic update command now has an explicitly configured post-commit worker, + so the two legacy lines and the replacement safety/queueing guidance are an + intentional divergence from the frozen v8 monoliths. + """ + return any( + marker in line + for marker in ( + "Doc/image changes are ignored by the hook", + "This default path uses no LLM and has no API cost.", + "To update docs, papers, and images automatically", + "Optionally set `semantic_env_file`", + ) + ) + + # Every line that may differ between a rendered monolith and its pristine v8 # baseline. Each predicate documents one sanctioned change-class; a blank line is # allowed because the multi-line fix blocks insert spacing. Anything else failing @@ -1163,6 +1182,7 @@ def _is_community_label_export_fix_line(line: str) -> bool: _is_uv_from_interpreter_fix_line, _is_semantic_cache_scope_fix_line, _is_community_label_export_fix_line, + _is_semantic_commit_automation_doc_line, )