diff --git a/graphify/__main__.py b/graphify/__main__.py index ef1dc72..3805728 100644 --- a/graphify/__main__.py +++ b/graphify/__main__.py @@ -1118,7 +1118,7 @@ def _agents_install(project_dir: Path, platform: str) -> None: if platform not in ("codex", "opencode"): print() print("Note: unlike Claude Code, there is no PreToolUse hook equivalent for") - print(f"{platform.capitalize()} — the AGENTS.md rules are the always-on mechanism.") + print(f"{platform.capitalize()} - the AGENTS.md rules are the always-on mechanism.") def _project_install(platform_name: str, project_dir: Path | None = None) -> None: @@ -1399,7 +1399,7 @@ def _clone_repo(url: str, branch: str | None = None, out_dir: Path | None = None sys.exit(1) if dest.exists(): - print(f"Repo already cloned at {dest} — pulling latest...", flush=True) + print(f"Repo already cloned at {dest} - pulling latest...", flush=True) cmd = ["git", "-C", str(dest), "pull"] if branch: cmd += ["origin", "--", branch] @@ -1423,6 +1423,12 @@ def _clone_repo(url: str, branch: str | None = None, out_dir: Path | None = None def main() -> None: + for _stream in (sys.stdout, sys.stderr): + if _stream is not None and hasattr(_stream, "reconfigure"): + try: + _stream.reconfigure(encoding="utf-8", errors="replace") + except Exception: + pass # Check all known skill install locations for a stale version stamp. # Skip during install/uninstall (hook writes trigger a fresh check anyway). # Skip during hook-check — it runs on every editor tool use and must be silent. @@ -2364,7 +2370,7 @@ def main() -> None: watch_path = Path(".") graph_json = graph_override if graph_override is not None else watch_path / "graphify-out" / "graph.json" if not graph_json.exists(): - print(f"error: no graph found at {graph_json} — run /graphify first", file=sys.stderr) + print(f"error: no graph found at {graph_json} - run /graphify first", file=sys.stderr) sys.exit(1) from networkx.readwrite import json_graph as _jg from graphify.build import build_from_json @@ -2427,16 +2433,16 @@ def main() -> None: if no_viz: if html_target.exists(): html_target.unlink() - print(f"Done — {len(communities)} communities. GRAPH_REPORT.md and graph.json updated (--no-viz; graph.html removed).") + print(f"Done - {len(communities)} communities. GRAPH_REPORT.md and graph.json updated (--no-viz; graph.html removed).") else: try: to_html(G, communities, str(html_target), community_labels=labels or None) - print(f"Done — {len(communities)} communities. GRAPH_REPORT.md, graph.json and graph.html updated.") + print(f"Done - {len(communities)} communities. GRAPH_REPORT.md, graph.json and graph.html updated.") except ValueError as viz_err: if html_target.exists(): html_target.unlink() print(f"Skipped graph.html: {viz_err}") - print(f"Done — {len(communities)} communities. GRAPH_REPORT.md and graph.json updated.") + print(f"Done - {len(communities)} communities. GRAPH_REPORT.md and graph.json updated.") elif cmd == "update": force = os.environ.get("GRAPHIFY_FORCE", "").lower() in ("1", "true", "yes") @@ -2487,7 +2493,7 @@ def main() -> None: ): print("Tip: set GEMINI_API_KEY or GOOGLE_API_KEY to use Gemini for semantic extraction.") else: - print("Nothing to update or rebuild failed — check output above.", file=sys.stderr) + print("Nothing to update or rebuild failed - check output above.", file=sys.stderr) sys.exit(1) elif cmd == "hook-check": @@ -2981,7 +2987,7 @@ def main() -> None: try: result = _global_add(source, tag) if result["skipped"]: - print(f"'{tag}' unchanged since last add — global graph not modified.") + print(f"'{tag}' unchanged since last add - global graph not modified.") else: print(f"Added '{tag}' to global graph: +{result['nodes_added']} nodes, " f"-{result['nodes_removed']} pruned. Global: {_global_path()}") @@ -3458,7 +3464,7 @@ def main() -> None: try: result = _global_add(graphify_out / "graph.json", _tag) if result["skipped"]: - print(f"[graphify global] '{_tag}' unchanged since last add — skipped.") + print(f"[graphify global] '{_tag}' unchanged since last add - skipped.") else: print(f"[graphify global] '{_tag}' merged into global graph " f"(+{result['nodes_added']} nodes, -{result['nodes_removed']} pruned).") @@ -3520,7 +3526,7 @@ def main() -> None: try: result = _global_add(graphify_out / "graph.json", _tag) if result["skipped"]: - print(f"[graphify global] '{_tag}' unchanged since last add — skipped.") + print(f"[graphify global] '{_tag}' unchanged since last add - skipped.") else: print(f"[graphify global] '{_tag}' merged into global graph " f"(+{result['nodes_added']} nodes, -{result['nodes_removed']} pruned).") diff --git a/graphify/export.py b/graphify/export.py index f685e4c..ff127c0 100644 --- a/graphify/export.py +++ b/graphify/export.py @@ -86,11 +86,11 @@ def backup_if_protected(out_dir: Path) -> "Path | None": except Exception: pass if copied: - print(f"[graphify] backed up {reason} graph ({copied} files) → {backup_dir.name}/") + print(f"[graphify] backed up {reason} graph ({copied} files) -> {backup_dir.name}/") return backup_dir except Exception as exc: import sys - print(f"[graphify] warning: backup failed ({exc}) — continuing with overwrite", file=sys.stderr) + print(f"[graphify] warning: backup failed ({exc}) - continuing with overwrite", file=sys.stderr) return None def _obsidian_tag(name: str) -> str: diff --git a/graphify/prs.py b/graphify/prs.py index 7ddb43b..319892e 100644 --- a/graphify/prs.py +++ b/graphify/prs.py @@ -471,7 +471,7 @@ def render_worktrees(prs: list[PRInfo], worktrees: dict[str, str]) -> None: if pr: status = _status_color(pr.status) print(f" {cyan(path)}") - print(f" {dim('branch:')} {branch} → PR {bold(f'#{pr.number}')} [{status}] {_truncate(pr.title, 50)}") + print(f" {dim('branch:')} {branch} -> PR {bold(f'#{pr.number}')} [{status}] {_truncate(pr.title, 50)}") else: print(f" {cyan(path)}") print(f" {dim('branch:')} {branch} {dim('(no open PR)')}") @@ -485,7 +485,7 @@ def render_conflicts( ) -> None: actionable = [p for p in prs if p.base_branch == base and p.communities_touched] if not actionable: - print(dim("\n No graph impact data — run with a valid graph.json to detect conflicts.\n")) + print(dim("\n No graph impact data - run with a valid graph.json to detect conflicts.\n")) return # Build community → [PRs] map @@ -496,7 +496,7 @@ def render_conflicts( conflicts = {c: ps for c, ps in comm_to_prs.items() if len(ps) > 1} if not conflicts: - print(green("\n No community overlap between open PRs — safe to merge in any order.\n")) + print(green("\n No community overlap between open PRs - safe to merge in any order.\n")) return print() @@ -518,7 +518,7 @@ def render_pr_detail(pr: PRInfo, repo: str | None = None) -> None: print(bold(f" PR #{pr.number} · {_status_color(pr.status)}")) print(f" {pr.title}") print() - print(f" {dim('branch:')} {pr.branch} → {pr.base_branch}") + print(f" {dim('branch:')} {pr.branch} -> {pr.base_branch}") print(f" {dim('author:')} {pr.author}") print(f" {dim('updated:')} {pr.days_old}d ago") print(f" {dim('CI:')} {_ci_icon(pr.ci_status)} {pr.ci_status}") @@ -579,7 +579,7 @@ def triage_with_opus(prs: list[PRInfo], base: str) -> None: try: from graphify.llm import BACKENDS, _get_backend_api_key except ImportError: - print(red(" graphify.llm not available — cannot run triage."), file=sys.stderr) + print(red(" graphify.llm not available - cannot run triage."), file=sys.stderr) sys.exit(1) candidates = [p for p in prs if p.base_branch == base and p.status not in ("WRONG-BASE", "STALE")]