diff --git a/.growmos/.gitignore b/.growmos/.gitignore index 8439346..ad61d3d 100644 --- a/.growmos/.gitignore +++ b/.growmos/.gitignore @@ -1,2 +1,3 @@ *.tmp cache/ +graph.html diff --git a/.growmos/aliases.jsonl b/.growmos/aliases.jsonl index c6bb5cb..2073519 100644 --- a/.growmos/aliases.jsonl +++ b/.growmos/aliases.jsonl @@ -34,6 +34,7 @@ {"alias": "Anthropic API", "entity": "dependency/anthropic-api", "type": "DEPENDENCY"} {"alias": "OpenAI-compatible API", "entity": "dependency/openai-compatible-api", "type": "DEPENDENCY"} {"alias": "xAI API", "entity": "dependency/xai-api", "type": "DEPENDENCY"} +{"alias": "growmos view (view.html)", "entity": "feature/growmos-view-view-html", "type": "FEATURE"} {"alias": "Headless mode", "entity": "feature/headless-mode", "type": "FEATURE"} {"alias": "Presets", "entity": "feature/presets", "type": "FEATURE"} {"alias": "Codician", "entity": "organization/codician", "type": "ORGANIZATION"} diff --git a/.growmos/entities.jsonl b/.growmos/entities.jsonl index 2fdf43d..2a2eee7 100644 --- a/.growmos/entities.jsonl +++ b/.growmos/entities.jsonl @@ -34,6 +34,7 @@ {"created": "2026-08-17T11:50:35Z", "description": "Provider used with ANTHROPIC_API_KEY; structured outputs via output_config json_schema.", "id": "dependency/anthropic-api", "mentions": 1, "name": "Anthropic API", "provisional": false, "schema_version": 1, "sources": ["src_644a9dc71c36"], "type": "DEPENDENCY", "updated": "2026-08-17T11:50:35Z"} {"created": "2026-08-17T11:50:35Z", "description": "Provider used with OPENAI_API_KEY or GROWMOS_BASE_URL; response_format json_schema.", "id": "dependency/openai-compatible-api", "mentions": 1, "name": "OpenAI-compatible API", "provisional": false, "schema_version": 1, "sources": ["src_644a9dc71c36"], "type": "DEPENDENCY", "updated": "2026-08-17T11:50:35Z"} {"created": "2026-08-17T11:50:35Z", "description": "Grok provider used with XAI_API_KEY.", "id": "dependency/xai-api", "mentions": 1, "name": "xAI API", "provisional": false, "schema_version": 1, "sources": ["src_644a9dc71c36"], "type": "DEPENDENCY", "updated": "2026-08-17T11:50:35Z"} +{"created": "2026-08-17T12:27:04Z", "description": "Self-contained interactive HTML explorer of the graph: force layout, search, type filters, node cards with profile, edges and provenance; growmos view opens it, export --format html writes it.", "id": "feature/growmos-view-view-html", "mentions": 1, "name": "growmos view (view.html)", "provisional": true, "schema_version": 1, "sources": ["src_a384388adcb2"], "type": "FEATURE", "updated": "2026-08-17T12:27:04Z"} {"created": "2026-08-17T11:50:35Z", "description": "growmos ingest calls Anthropic, OpenAI-compatible or xAI APIs directly for cron and CI, following the playbook's fast-vs-reasoning model split.", "id": "feature/headless-mode", "mentions": 2, "name": "Headless mode", "provisional": false, "schema_version": 1, "sources": ["src_b33563055168", "src_644a9dc71c36"], "type": "FEATURE", "updated": "2026-08-17T11:50:35Z"} {"created": "2026-08-17T11:50:35Z", "description": "software, general, research and business entity-type vocabularies sharing the same prompts.", "id": "feature/presets", "mentions": 1, "name": "Presets", "provisional": false, "schema_version": 1, "sources": ["src_b33563055168"], "type": "FEATURE", "updated": "2026-08-17T11:50:35Z"} {"created": "2026-08-17T11:50:35Z", "description": "Company (codician.com) that builds and maintains growmos under the MIT license.", "id": "organization/codician", "mentions": 2, "name": "Codician", "provisional": false, "schema_version": 1, "sources": ["src_b33563055168", "src_eca12c0a30e2"], "type": "ORGANIZATION", "updated": "2026-08-17T11:50:35Z"} diff --git a/.growmos/journal.md b/.growmos/journal.md index 772cff5..79436bf 100644 --- a/.growmos/journal.md +++ b/.growmos/journal.md @@ -6,3 +6,7 @@ Shared memo between humans and agents. Append-only; newest at the bottom. ### 2026-08-17T11:51:11Z · claude Initial build of growmos from the Anthropic Knowledge Graph Playbook PDF: package (store, graph, prompts, schema, evaluate, export, providers, integrate, mcp, cli), Apollo example corpus, 18 tests, docs. Design decisions: agent-native task packets (no API key needed), zero dependencies, JSONL store committed with code, MCP server for any CLI. Verified end-to-end on the Apollo corpus (1 component, density 1.58, precision 1.0). + +### 2026-08-17T12:27:04Z · agent + +Added growmos view: interactive HTML explorer + README demo screenshot; bumped to 0.1.1. diff --git a/.growmos/mentions.jsonl b/.growmos/mentions.jsonl index 1e5a345..4e4f7ac 100644 --- a/.growmos/mentions.jsonl +++ b/.growmos/mentions.jsonl @@ -58,3 +58,4 @@ {"chunk": null, "description": "Python module implementing the .growmos store: load/save JSONL tables, register sources, apply extraction/resolution, merge entities, remember/link/journal.", "name": "GraphStore (store.py)", "schema_version": 1, "source": "src_7a4afa70f300", "ts": "2026-08-17T11:51:11Z", "type": "COMPONENT"} {"chunk": null, "description": "Builds extraction/resolution/profile/query/check task packets from editable prompt templates; chunks long docs at section boundaries with overlap.", "name": "Packet builder (prompts.py)", "schema_version": 1, "source": "src_7a4afa70f300", "ts": "2026-08-17T11:51:11Z", "type": "COMPONENT"} {"chunk": null, "description": "Adjacency, k-hop traversal, triple serialization with provenance, diagnostics, seed finding, deterministic claim checking, sampling.", "name": "Graph engine (graph.py)", "schema_version": 1, "source": "src_7a4afa70f300", "ts": "2026-08-17T11:51:11Z", "type": "COMPONENT"} +{"chunk": null, "description": "Self-contained interactive HTML explorer of the graph: force layout, search, type filters, node cards with profile, edges and provenance; growmos view opens it, export --format html writes it.", "name": "growmos view (view.html)", "schema_version": 1, "source": "src_a384388adcb2", "ts": "2026-08-17T12:27:04Z", "type": "FEATURE"} diff --git a/.growmos/relations.jsonl b/.growmos/relations.jsonl index e96f71e..f29ee2e 100644 --- a/.growmos/relations.jsonl +++ b/.growmos/relations.jsonl @@ -15,6 +15,7 @@ {"confidence": 1, "created": "2026-08-17T11:51:11Z", "id": "r_3b6d37efba09", "predicate": "depends on", "schema_version": 1, "source": "component/graph-engine-graph-py", "sources": ["src_7a4afa70f300"], "target": "component/graphstore-store-py", "updated": "2026-08-17T11:51:11Z"} {"confidence": 1, "created": "2026-08-17T11:51:11Z", "id": "r_3c068163c973", "predicate": "depends on", "schema_version": 1, "source": "component/packet-builder-prompts-py", "sources": ["src_7a4afa70f300"], "target": "component/graphstore-store-py", "updated": "2026-08-17T11:51:11Z"} {"confidence": 1, "created": "2026-08-17T11:50:35Z", "id": "r_3e6b5ce7ae5d", "predicate": "part of", "schema_version": 1, "source": "tool/growmos-sample", "sources": ["src_15e0af4a3dd5"], "target": "component/evaluation-harness", "updated": "2026-08-17T11:50:35Z"} +{"confidence": 1, "created": "2026-08-17T12:27:04Z", "id": "r_455737c6171f", "predicate": "part of", "schema_version": 1, "source": "feature/growmos-view-view-html", "sources": ["src_a384388adcb2"], "target": "tool/growmos", "updated": "2026-08-17T12:27:04Z"} {"confidence": 1, "created": "2026-08-17T11:50:35Z", "id": "r_5160f89fa514", "predicate": "implements", "schema_version": 1, "source": "tool/growmos", "sources": ["src_b33563055168"], "target": "concept/anthropic-knowledge-graph-playbook", "updated": "2026-08-17T11:50:35Z"} {"confidence": 1, "created": "2026-08-17T11:51:11Z", "id": "r_51b0f6496aa6", "predicate": "calls", "schema_version": 1, "source": "component/mcp-server", "sources": ["src_7a4afa70f300"], "target": "tool/growmos", "updated": "2026-08-17T11:51:11Z"} {"confidence": 1, "created": "2026-08-17T11:50:35Z", "id": "r_56711eca826a", "predicate": "uses", "schema_version": 1, "source": "tool/growmos-eval", "sources": ["src_15e0af4a3dd5"], "target": "artifact/scorer-alias-map", "updated": "2026-08-17T11:50:35Z"} diff --git a/.growmos/sources.jsonl b/.growmos/sources.jsonl index edff9cb..22126c4 100644 --- a/.growmos/sources.jsonl +++ b/.growmos/sources.jsonl @@ -1,8 +1,8 @@ -{"added": "2026-08-17T11:49:36Z", "extracted_at": "2026-08-17T11:50:35Z", "id": "src_15e0af4a3dd5", "kind": "file", "ref": "docs/evaluation.md", "schema_version": 1, "sha256": "1eb6e959059e66ecdbcc35b93ab524635607f157254f763ef030a58b797cd227", "stats": {"entities": 5, "relations": 5}, "status": "pending", "title": "evaluation.md", "updated": "2026-08-17T11:59:04Z"} +{"added": "2026-08-17T11:49:36Z", "extracted_at": "2026-08-17T11:50:35Z", "id": "src_15e0af4a3dd5", "kind": "file", "ref": "docs/evaluation.md", "schema_version": 1, "sha256": "1eb6e959059e66ecdbcc35b93ab524635607f157254f763ef030a58b797cd227", "stats": {"entities": 5, "relations": 5}, "status": "pending", "title": "evaluation.md", "updated": "2026-08-17T12:19:10Z"} {"added": "2026-08-17T11:49:36Z", "extracted_at": "2026-08-17T11:50:35Z", "id": "src_3fd317a2cb2b", "kind": "file", "ref": "docs/file-format.md", "schema_version": 1, "sha256": "9f93981a6778d7a991c735ff0b2e8b1d3a51bd1d11cd1bd908dc3b401ce33c04", "stats": {"entities": 7, "relations": 6}, "status": "extracted", "title": "file-format.md", "updated": "2026-08-17T11:49:36Z"} {"added": "2026-08-17T11:49:36Z", "extracted_at": "2026-08-17T11:50:35Z", "id": "src_5dd15b5b20d6", "kind": "file", "ref": "METHODOLOGY.md", "schema_version": 1, "sha256": "7ad5c4305900d6f954fb124e41692573267f3ccb49e363786c4c136fb7e3ba1e", "stats": {"entities": 13, "relations": 13}, "status": "extracted", "title": "METHODOLOGY.md", "updated": "2026-08-17T11:49:36Z"} {"added": "2026-08-17T11:49:36Z", "extracted_at": "2026-08-17T11:50:35Z", "id": "src_644a9dc71c36", "kind": "file", "ref": "docs/headless.md", "schema_version": 1, "sha256": "5db7953e9111f7722f740e80220d3ad099e667e5d848e6e47c02047f722eb983", "stats": {"entities": 5, "relations": 4}, "status": "extracted", "title": "headless.md", "updated": "2026-08-17T11:49:36Z"} {"added": "2026-08-17T11:51:11Z", "id": "src_7a4afa70f300", "kind": "note", "ref": "session:2026-08-17 build", "sha256": "7a4afa70f3001f4f653daa0f7af9b9219e8ea734ce4e7604c707c1437906efe2", "status": "note", "title": "session:2026-08-17 build", "updated": "2026-08-17T11:51:11Z"} -{"added": "2026-08-17T11:49:36Z", "extracted_at": "2026-08-17T11:50:35Z", "id": "src_a384388adcb2", "kind": "file", "ref": "docs/agents.md", "schema_version": 1, "sha256": "1db8b7ef7f859f0d26caa4f2b53b52d581510ea8a02ed5b2fb8beed9fe972ad1", "stats": {"entities": 9, "relations": 8}, "status": "extracted", "title": "agents.md", "updated": "2026-08-17T11:49:36Z"} -{"added": "2026-08-17T11:49:36Z", "extracted_at": "2026-08-17T11:50:35Z", "id": "src_b33563055168", "kind": "file", "ref": "README.md", "schema_version": 1, "sha256": "eb3d831021dd979a6f2a888a37d10caefdacf30dd3d3961b73e83c1c6b8a01c7", "stats": {"entities": 14, "relations": 13}, "status": "pending", "title": "README.md", "updated": "2026-08-17T11:59:04Z"} -{"added": "2026-08-17T11:49:36Z", "extracted_at": "2026-08-17T11:50:35Z", "id": "src_eca12c0a30e2", "kind": "file", "ref": "CONTRIBUTING.md", "schema_version": 1, "sha256": "ae1583a24ce33b8de5367fed5186e634552c5109ef944d78fafe0fca4fd5b3a6", "stats": {"entities": 4, "relations": 3}, "status": "pending", "title": "CONTRIBUTING.md", "updated": "2026-08-17T11:59:04Z"} +{"added": "2026-08-17T11:49:36Z", "extracted_at": "2026-08-17T11:50:35Z", "id": "src_a384388adcb2", "kind": "file", "ref": "docs/agents.md", "schema_version": 1, "sha256": "a384388adcb2ed2bfeb1762af9c9b8b6f59402e1a6c94a7c459f120642085e64", "stats": {"entities": 9, "relations": 8}, "status": "note", "title": "docs/agents.md", "updated": "2026-08-17T12:27:04Z"} +{"added": "2026-08-17T11:49:36Z", "extracted_at": "2026-08-17T11:50:35Z", "id": "src_b33563055168", "kind": "file", "ref": "README.md", "schema_version": 1, "sha256": "eb3d831021dd979a6f2a888a37d10caefdacf30dd3d3961b73e83c1c6b8a01c7", "stats": {"entities": 14, "relations": 13}, "status": "pending", "title": "README.md", "updated": "2026-08-17T12:19:10Z"} +{"added": "2026-08-17T11:49:36Z", "extracted_at": "2026-08-17T11:50:35Z", "id": "src_eca12c0a30e2", "kind": "file", "ref": "CONTRIBUTING.md", "schema_version": 1, "sha256": "ae1583a24ce33b8de5367fed5186e634552c5109ef944d78fafe0fca4fd5b3a6", "stats": {"entities": 4, "relations": 3}, "status": "pending", "title": "CONTRIBUTING.md", "updated": "2026-08-17T12:19:10Z"} diff --git a/.growmos/state.json b/.growmos/state.json index 0d672c0..eb714af 100644 --- a/.growmos/state.json +++ b/.growmos/state.json @@ -10,7 +10,7 @@ "docs": 2, "ts": "2026-08-17T11:51:25Z" }, - "last_run": "2026-08-17T11:51:11Z", + "last_run": "2026-08-17T12:27:04Z", "last_sample": "2026-08-17T11:51:11Z", "last_sample_entity": "concept/agent-protocol", "open_packets": {}, @@ -181,6 +181,16 @@ "renamed": 0, "ts": "2026-08-17T11:51:11Z", "type": "COMPONENT" + }, + { + "entity": "feature/growmos-view-view-html", + "kind": "remember", + "ts": "2026-08-17T12:27:04Z" + }, + { + "kind": "link", + "relation": "r_455737c6171f", + "ts": "2026-08-17T12:27:04Z" } ] } diff --git a/README.md b/README.md index 86a366a..5941f9c 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ closing the circle). See [METHODOLOGY.md](METHODOLOGY.md) for the full methodolo evaluation loop: change prompt → growmos eval → watch F1 move ``` +

growmos view — interactive graph explorer

+

growmos view — after a few days of development, this is what lays in your graph: hubs sized by degree, colored by type, every edge with provenance, profiles on click.

+ ## Why Multi-agent systems and long-running coding sessions share one weakness: memory dies with the @@ -47,6 +50,10 @@ growmos makes that a **living organism inside your repo**: resolved), the 10-item `growmos doctor` checklist and the health signals (components, density, compression) all stay green on autopilot. Every gold file records who reviewed it (`agent` / `human`) — humans can overrule at any time, but never have to. +- **It shows itself.** `growmos view` opens a self-contained, offline interactive explorer + (force layout, search, type filters, click a node for its profile, edges and provenance) — + no server, no dependencies. `growmos export --format html|json|dot|mermaid|cypher|sql` for + everything else. - **It is agent-native.** No API key needed: the CLI does the deterministic work, and hands the *judgment* work (extraction, resolution, summarization) to whatever agent you already run as a **task packet** — prompt + JSON shape + the exact `growmos apply …` command. Optional @@ -84,6 +91,7 @@ growmos remember "Scheduler" --type COMPONENT --desc "Schedules jobs; depends on growmos link "Scheduler" "depends on" "Store" growmos journal "Moved Store to Postgres (ADR-001)." growmos check "(Alice Chen) --[owns]--> (Scheduler)" +growmos view # open the interactive explorer in your browser growmos status · growmos context · growmos doctor · growmos eval · growmos sample ``` @@ -126,8 +134,8 @@ to your CLI's MCP config. journal.md the shared memo, append-only ``` -Plain JSONL: diff-able, merge-friendly, greppable, exportable (`growmos export --format -json|dot|mermaid|cypher|sql`). Storage is an infrastructure decision, not a pipeline decision: +Plain JSONL: diff-able, merge-friendly, greppable, viewable (`growmos view`) and exportable +(`growmos export --format html|json|dot|mermaid|cypher|sql`). Storage is an infrastructure decision, not a pipeline decision: the same schema maps onto Neo4j or three Postgres tables. ## Presets diff --git a/docs/agents.md b/docs/agents.md index a9e4c5d..06d9698 100644 --- a/docs/agents.md +++ b/docs/agents.md @@ -82,6 +82,13 @@ the resolver step (`growmos resolve` → apply) merge surface forms across worke `growmos query` and cites edges. Everything writes to the same JSONL files; commit and merge like code. +## Seeing the graph + +`growmos view` writes `.growmos/graph.html` (self-contained, offline) and opens it: force layout +sized by degree, colored by type, search, type filters, and a card per node with description, +profile, edges (with edge ids, corroboration and provenance) and aliases. `--focus ` opens +on a node; `--out path.html` writes elsewhere (share it, embed it in CI artifacts). + ## Packet anatomy ``` diff --git a/docs/assets/growmos-view.png b/docs/assets/growmos-view.png new file mode 100644 index 0000000..8f44c0d Binary files /dev/null and b/docs/assets/growmos-view.png differ diff --git a/pyproject.toml b/pyproject.toml index 88c4932..7f10f14 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "growmos" -version = "0.1.0" +version = "0.1.1" description = "A living knowledge graph that grows with your repo — agent-native, zero-dependency, plug & play for Claude Code, Codex, Grok, Cursor and any MCP-capable CLI." readme = "README.md" license = { text = "MIT" } diff --git a/src/growmos/__init__.py b/src/growmos/__init__.py index 27fe432..50dd109 100644 --- a/src/growmos/__init__.py +++ b/src/growmos/__init__.py @@ -3,4 +3,4 @@ Developed by Codician (https://codician.com). MIT licensed. """ -__version__ = "0.1.0" +__version__ = "0.1.1" diff --git a/src/growmos/cli.py b/src/growmos/cli.py index 2283c31..774c293 100644 --- a/src/growmos/cli.py +++ b/src/growmos/cli.py @@ -787,6 +787,23 @@ def cmd_ingest(args: argparse.Namespace) -> int: return 0 +def cmd_view(args: argparse.Namespace) -> int: + """Render the interactive explorer to .growmos/graph.html and open it.""" + from .export import to_html + import webbrowser + st = _store(args) + focus = "" + if args.focus: + focus = st.resolve_name(args.focus) or (args.focus if args.focus in st.entities else "") + out = Path(args.out) if args.out else st.p("graph.html") + out.parent.mkdir(parents=True, exist_ok=True) + out.write_text(to_html(st, focus=focus), encoding="utf-8") + print(f"✓ wrote {out} ({len(st.entities)} nodes, {len(st.relations)} edges)") + if not args.no_open: + webbrowser.open(out.resolve().as_uri()) + return 0 + + def cmd_mcp(args: argparse.Namespace) -> int: from .mcp import serve serve() @@ -938,7 +955,7 @@ def build_parser() -> argparse.ArgumentParser: sp.add_argument("--strict", action="store_true", help="exit 1 unless all green") sp.set_defaults(fn=cmd_doctor) - sp = sub.add_parser("export", help="export graph: json|dot|mermaid|cypher|sql") + sp = sub.add_parser("export", help="export graph: json|html|dot|mermaid|cypher|sql") sp.add_argument("--format", default="json") sp.add_argument("--out") sp.set_defaults(fn=cmd_export) @@ -962,6 +979,12 @@ def build_parser() -> argparse.ArgumentParser: sp.add_argument("--scan", action="store_true") sp.set_defaults(fn=cmd_ingest) + sp = sub.add_parser("view", help="open an interactive visual explorer of the graph in your browser") + sp.add_argument("--focus", help="entity to select on open") + sp.add_argument("--out", help="write HTML here instead of .growmos/graph.html") + sp.add_argument("--no-open", action="store_true", help="just write the file") + sp.set_defaults(fn=cmd_view) + sp = sub.add_parser("mcp", help="run the MCP stdio server (tools for any MCP-capable agent CLI)") sp.set_defaults(fn=cmd_mcp) diff --git a/src/growmos/export.py b/src/growmos/export.py index 0097faa..320cd08 100644 --- a/src/growmos/export.py +++ b/src/growmos/export.py @@ -94,4 +94,38 @@ def to_sql(store: Store) -> str: return "\n".join(lines) -EXPORTERS = {"json": to_json, "dot": to_dot, "mermaid": to_mermaid, "cypher": to_cypher, "sql": to_sql} +def to_html(store: Store, focus: str = "") -> str: + """Self-contained interactive explorer (force layout, cards, search). No external assets.""" + from pathlib import Path as _P + from .util import now_iso + g = Graph(store) + d = g.diagnostics() + profiles = {} + for eid in store.entities: + prof = store.get_profile(eid) + if prof: + prof = dict(prof) + prof["stale"] = store.profile_is_stale(eid) + profiles[eid] = prof + aliases: Dict[str, List[str]] = {} + for (a, t), eid in store.aliases.items(): + aliases.setdefault(eid, []).append(store._alias_display.get((a, t), a)) + data = { + "entities": [{"id": e["id"], "name": e["name"], "type": e["type"], "description": e.get("description", ""), + "sources": e.get("sources", []), "provisional": bool(e.get("provisional"))} + for e in store.entities.values()], + "relations": [{"id": r["id"], "source": r["source"], "target": r["target"], "predicate": r["predicate"], + "sources": r.get("sources", []), "confidence": r.get("confidence", 1)} + for r in store.relations.values()], + "profiles": profiles, + "aliases": {k: sorted(set(v)) for k, v in aliases.items()}, + "sources": {sid: rec.get("ref", sid) for sid, rec in store.sources.items()}, + "meta": {"components": d["components"], "density": d["density"], "schema_version": d["schema_version"], + "generated": now_iso()[:16].replace("T", " "), "focus": focus}, + } + tpl = (_P(__file__).parent / "templates" / "view.html").read_text(encoding="utf-8") + payload = json.dumps(data, ensure_ascii=False).replace(" + + + + +growmos · __TITLE__ + + + +
+

growmos · __TITLE__

+ +
+
+
+ +
×
+
drag to pan · wheel to zoom · drag nodes · click a node for its card · double-click background to reset
+ + + + diff --git a/tests/test_growmos.py b/tests/test_growmos.py index 9952f91..d5d1770 100644 --- a/tests/test_growmos.py +++ b/tests/test_growmos.py @@ -186,6 +186,11 @@ class ApolloFixture(unittest.TestCase): out = fn(self.st) self.assertTrue(out.strip(), name) self.assertIn("MERGE (n:PERSON", EXPORTERS["cypher"](self.st)) + html = EXPORTERS["html"](self.st) + self.assertIn("Buzz Aldrin", html) + self.assertNotIn("__DATA__", html) + out = run_cli("--root", str(self.tmp), "view", "--no-open") + self.assertIn("graph.html", out) self.assertIn("CREATE TABLE IF NOT EXISTS entities", EXPORTERS["sql"](self.st)) def test_cli_status_context_show(self):