ckgraphify 0.1.3__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/PKG-INFO +8 -8
  2. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/README.md +7 -7
  3. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/ckgraphify.egg-info/SOURCES.txt +2 -0
  4. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/__main__.py +42 -17
  5. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/business_map.py +71 -8
  6. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/graph_main_backend.py +158 -31
  7. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/graph_main_frontend.py +718 -31
  8. ckgraphify-0.2.0/graphify/graph_main_frontend_sdk.py +789 -0
  9. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/graph_main_html.py +1 -1
  10. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/graph_main_merge.py +90 -18
  11. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/graph_main_trace.py +76 -7
  12. ckgraphify-0.2.0/graphify/repo_registry.py +252 -0
  13. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/pyproject.toml +1 -1
  14. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/skill/skill-codex.md +28 -12
  15. ckgraphify-0.2.0/skill/skill.md +132 -0
  16. ckgraphify-0.1.3/skill/skill.md +0 -188
  17. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/LICENSE +0 -0
  18. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/MANIFEST.in +0 -0
  19. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/__init__.py +0 -0
  20. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/analyze.py +0 -0
  21. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/benchmark.py +0 -0
  22. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/bridge_mtop.py +0 -0
  23. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/build.py +0 -0
  24. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/cache.py +0 -0
  25. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/callflow_html.py +0 -0
  26. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/cluster.py +0 -0
  27. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/dedup.py +0 -0
  28. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/detect.py +0 -0
  29. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/export.py +0 -0
  30. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/extract.py +0 -0
  31. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/global_graph.py +0 -0
  32. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/google_workspace.py +0 -0
  33. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/hooks.py +0 -0
  34. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/ingest.py +0 -0
  35. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/llm.py +0 -0
  36. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/manifest.py +0 -0
  37. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/report.py +0 -0
  38. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/security.py +0 -0
  39. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/serve.py +0 -0
  40. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/transcribe.py +0 -0
  41. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/tree_html.py +0 -0
  42. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/validate.py +0 -0
  43. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/watch.py +0 -0
  44. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/graphify/wiki.py +0 -0
  45. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/setup.cfg +0 -0
  46. {ckgraphify-0.1.3 → ckgraphify-0.2.0}/skill/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ckgraphify
3
- Version: 0.1.3
3
+ Version: 0.2.0
4
4
  Summary: AI coding assistant skill for Claude Code and Codex - graph-main boundary graphs, multi-repo call chains, business-map concepts, and business search
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://github.com/safishamsi/graphify
@@ -106,17 +106,17 @@ python3 -m pip install -e ./ckgraphify && CLAUDE_PLUGIN_ROOT=/Users/alsc/code/sh
106
106
 
107
107
  ## 生成并合并仓库图谱
108
108
 
109
- 下面命令会分别进入 `${CLAUDE_PLUGIN_ROOT}/repos/kl-health` 和 `${CLAUDE_PLUGIN_ROOT}/repos/ele-newretail-health-audit` 生成 `graphify-out/graph-main.json`,然后合并到 `${CLAUDE_PLUGIN_ROOT}/graphify-out/graph-hc.json`。
109
+ 下面命令会分别进入 `${CLAUDE_PLUGIN_ROOT}/repos/kl-health` 和 `${CLAUDE_PLUGIN_ROOT}/repos/ele-newretail-health-audit` 生成 `graphify-out/graph-main.json`,然后合并到 `${CLAUDE_PLUGIN_ROOT}/graphify-out/graph.json`。
110
110
 
111
111
  ```bash
112
- scripts/graph-main-repos.sh --repos-root "${CLAUDE_PLUGIN_ROOT}/repos" --out "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph-hc.json" kl-health ele-newretail-health-audit
112
+ scripts/graph-main-repos.sh --repos-root "${CLAUDE_PLUGIN_ROOT}/repos" --out "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph.json" kl-health ele-newretail-health-audit
113
113
  ```
114
114
 
115
115
  也可以传入更多 `${CLAUDE_PLUGIN_ROOT}/repos/` 下的仓库名:
116
116
 
117
117
  ```bash
118
118
  # 健康卡 init all repos
119
- scripts/graph-main-repos.sh --repos-root "${CLAUDE_PLUGIN_ROOT}/repos" --out "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph-hc.json" ele-newretail-drug ele-newretail-health-task ele-newretail-drug-grow ele-newretail-health-audit ele-newretail-health-client ele-newretail-drug-trade kl-health health-vip-card medicine-unicore p ele-newretail-venus ele-newretail-summaryx china-alsc-sales-eleme-newretail-app
119
+ scripts/graph-main-repos.sh --repos-root "${CLAUDE_PLUGIN_ROOT}/repos" --out "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph.json" ele-newretail-drug ele-newretail-health-task ele-newretail-drug-grow ele-newretail-health-audit ele-newretail-health-client ele-newretail-drug-trade kl-health health-vip-card medicine-unicore p ele-newretail-venus ele-newretail-summaryx china-alsc-sales-eleme-newretail-app
120
120
  ```
121
121
 
122
122
  ```bash
@@ -124,7 +124,7 @@ scripts/graph-main-repos.sh --repos-root "${CLAUDE_PLUGIN_ROOT}/repos" --out "${
124
124
  scripts/graph-main-repos.sh \
125
125
  --incremental \
126
126
  --repos-root "${CLAUDE_PLUGIN_ROOT}/repos" \
127
- --out "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph-hc.json" \
127
+ --out "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph.json" \
128
128
  p medicine-unicore
129
129
  ```
130
130
 
@@ -136,7 +136,7 @@ scripts/graph-main-repos.sh \
136
136
  关于ehealth-member实体的全部链路
137
137
  ```bash
138
138
  graphify main-trace \
139
- --graph "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph-hc.json" \
139
+ --graph "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph.json" \
140
140
  --from ehealth-member \
141
141
  --max-depth 8
142
142
  ```
@@ -144,7 +144,7 @@ graphify main-trace \
144
144
  选择ehealth-member实体的/shopping/healthCard/createHealthCard 支线链路
145
145
  ```bash
146
146
  graphify main-trace \
147
- --graph "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph-hc.json" \
147
+ --graph "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph.json" \
148
148
  --from ehealth-member \
149
149
  --api /shopping/healthCard/createHealthCard \
150
150
  --max-depth 8
@@ -154,7 +154,7 @@ graphify main-trace \
154
154
 
155
155
  ```bash
156
156
  graphify main-trace \
157
- --graph "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph-hc.json" \
157
+ --graph "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph.json" \
158
158
  --from ehealth-member \
159
159
  --max-depth 8 --sources
160
160
  ```
@@ -13,17 +13,17 @@ python3 -m pip install -e ./ckgraphify && CLAUDE_PLUGIN_ROOT=/Users/alsc/code/sh
13
13
 
14
14
  ## 生成并合并仓库图谱
15
15
 
16
- 下面命令会分别进入 `${CLAUDE_PLUGIN_ROOT}/repos/kl-health` 和 `${CLAUDE_PLUGIN_ROOT}/repos/ele-newretail-health-audit` 生成 `graphify-out/graph-main.json`,然后合并到 `${CLAUDE_PLUGIN_ROOT}/graphify-out/graph-hc.json`。
16
+ 下面命令会分别进入 `${CLAUDE_PLUGIN_ROOT}/repos/kl-health` 和 `${CLAUDE_PLUGIN_ROOT}/repos/ele-newretail-health-audit` 生成 `graphify-out/graph-main.json`,然后合并到 `${CLAUDE_PLUGIN_ROOT}/graphify-out/graph.json`。
17
17
 
18
18
  ```bash
19
- scripts/graph-main-repos.sh --repos-root "${CLAUDE_PLUGIN_ROOT}/repos" --out "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph-hc.json" kl-health ele-newretail-health-audit
19
+ scripts/graph-main-repos.sh --repos-root "${CLAUDE_PLUGIN_ROOT}/repos" --out "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph.json" kl-health ele-newretail-health-audit
20
20
  ```
21
21
 
22
22
  也可以传入更多 `${CLAUDE_PLUGIN_ROOT}/repos/` 下的仓库名:
23
23
 
24
24
  ```bash
25
25
  # 健康卡 init all repos
26
- scripts/graph-main-repos.sh --repos-root "${CLAUDE_PLUGIN_ROOT}/repos" --out "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph-hc.json" ele-newretail-drug ele-newretail-health-task ele-newretail-drug-grow ele-newretail-health-audit ele-newretail-health-client ele-newretail-drug-trade kl-health health-vip-card medicine-unicore p ele-newretail-venus ele-newretail-summaryx china-alsc-sales-eleme-newretail-app
26
+ scripts/graph-main-repos.sh --repos-root "${CLAUDE_PLUGIN_ROOT}/repos" --out "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph.json" ele-newretail-drug ele-newretail-health-task ele-newretail-drug-grow ele-newretail-health-audit ele-newretail-health-client ele-newretail-drug-trade kl-health health-vip-card medicine-unicore p ele-newretail-venus ele-newretail-summaryx china-alsc-sales-eleme-newretail-app
27
27
  ```
28
28
 
29
29
  ```bash
@@ -31,7 +31,7 @@ scripts/graph-main-repos.sh --repos-root "${CLAUDE_PLUGIN_ROOT}/repos" --out "${
31
31
  scripts/graph-main-repos.sh \
32
32
  --incremental \
33
33
  --repos-root "${CLAUDE_PLUGIN_ROOT}/repos" \
34
- --out "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph-hc.json" \
34
+ --out "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph.json" \
35
35
  p medicine-unicore
36
36
  ```
37
37
 
@@ -43,7 +43,7 @@ scripts/graph-main-repos.sh \
43
43
  关于ehealth-member实体的全部链路
44
44
  ```bash
45
45
  graphify main-trace \
46
- --graph "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph-hc.json" \
46
+ --graph "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph.json" \
47
47
  --from ehealth-member \
48
48
  --max-depth 8
49
49
  ```
@@ -51,7 +51,7 @@ graphify main-trace \
51
51
  选择ehealth-member实体的/shopping/healthCard/createHealthCard 支线链路
52
52
  ```bash
53
53
  graphify main-trace \
54
- --graph "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph-hc.json" \
54
+ --graph "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph.json" \
55
55
  --from ehealth-member \
56
56
  --api /shopping/healthCard/createHealthCard \
57
57
  --max-depth 8
@@ -61,7 +61,7 @@ graphify main-trace \
61
61
 
62
62
  ```bash
63
63
  graphify main-trace \
64
- --graph "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph-hc.json" \
64
+ --graph "${CLAUDE_PLUGIN_ROOT}/graphify-out/graph.json" \
65
65
  --from ehealth-member \
66
66
  --max-depth 8 --sources
67
67
  ```
@@ -20,6 +20,7 @@ graphify/global_graph.py
20
20
  graphify/google_workspace.py
21
21
  graphify/graph_main_backend.py
22
22
  graphify/graph_main_frontend.py
23
+ graphify/graph_main_frontend_sdk.py
23
24
  graphify/graph_main_html.py
24
25
  graphify/graph_main_merge.py
25
26
  graphify/graph_main_trace.py
@@ -27,6 +28,7 @@ graphify/hooks.py
27
28
  graphify/ingest.py
28
29
  graphify/llm.py
29
30
  graphify/manifest.py
31
+ graphify/repo_registry.py
30
32
  graphify/report.py
31
33
  graphify/security.py
32
34
  graphify/serve.py
@@ -570,10 +570,11 @@ def main() -> None:
570
570
  print(" merge-main-graphs <graph-main.json...> merge multiple graph-main boundary graphs")
571
571
  print(" --out <path> output path (default: graphify-out/graph-main-merged.json)")
572
572
  print(" --report <path> report path (default: graphify-out/graph-main-merged-report.md)")
573
+ print(" repo-list [path] create/update editable repo-list.json from ./repos git remotes")
573
574
  print(" main-trace --graph <path> --from <node> [--api <api>] [--max-depth N] [--sources] [--prefer-repo R] [--exclude-repo R]")
574
575
  print(" trace a graph-main chain from an exact entry/API")
575
- print(" business-init --concept <name> [--out graphify-out/business-map.json]")
576
- print(" create a business-map seed (currently: 健康卡)")
576
+ print(" business-init --concept <name> [--out graphify-out/business-map.json] [--force]")
577
+ print(" create a business-map or append an unexplored concept")
577
578
  print(" business-show [--map <path>] [--concept <name>] [--scenario <name>]")
578
579
  print(" inspect concepts, scenarios, anchors, and trace hints")
579
580
  print(" business-query \"question\" [--map <path>] [--graph <path>] [--trace] [--sources] [--format json|text]")
@@ -1329,9 +1330,9 @@ def main() -> None:
1329
1330
  sys.exit(1)
1330
1331
  else:
1331
1332
  graph_paths.append(Path(a)); i += 1
1332
- if len(graph_paths) < 2:
1333
+ if len(graph_paths) < 1:
1333
1334
  print(
1334
- "Usage: graphify merge-main-graphs <graph-main1.json> <graph-main2.json> [...] "
1335
+ "Usage: graphify merge-main-graphs <graph-main1.json> [graph-main2.json ...] "
1335
1336
  "[--out graph-main-merged.json] [--report report.md]",
1336
1337
  file=sys.stderr,
1337
1338
  )
@@ -1357,6 +1358,7 @@ def main() -> None:
1357
1358
  f"repos={stats.repo_count}, nodes={stats.nodes}, edges={stats.edges}, "
1358
1359
  f"cross_edges={stats.cross_edges}, mtop={stats.mtop_links}, rest={stats.rest_links}, "
1359
1360
  f"hsf_method={stats.hsf_method_links}, hsf_api={stats.hsf_api_links}, metaq={stats.metaq_links}, "
1361
+ f"npm_export={stats.npm_export_links}, "
1360
1362
  f"unresolved_dependencies={stats.unresolved_dependencies}"
1361
1363
  )
1362
1364
  print(f"Graph: {out_path}")
@@ -1723,6 +1725,12 @@ def main() -> None:
1723
1725
  args = sys.argv[2:]
1724
1726
  from graphify.business_map import default_business_graph_path, default_business_map_path
1725
1727
 
1728
+ def _require_business_trace_value(option: str, index: int) -> str:
1729
+ if index + 1 >= len(args) or args[index + 1].startswith("--"):
1730
+ print(f"error: business-trace option {option} requires a value", file=sys.stderr)
1731
+ sys.exit(1)
1732
+ return args[index + 1]
1733
+
1726
1734
  map_path = default_business_map_path()
1727
1735
  graph_path: Path | None = None
1728
1736
  concept = ""
@@ -1733,29 +1741,29 @@ def main() -> None:
1733
1741
  i = 0
1734
1742
  while i < len(args):
1735
1743
  a = args[i]
1736
- if a == "--map" and i + 1 < len(args):
1737
- map_path = Path(args[i + 1]); i += 2
1744
+ if a == "--map":
1745
+ map_path = Path(_require_business_trace_value(a, i)); i += 2
1738
1746
  elif a.startswith("--map="):
1739
1747
  map_path = Path(a.split("=", 1)[1]); i += 1
1740
- elif a == "--graph" and i + 1 < len(args):
1741
- graph_path = Path(args[i + 1]); i += 2
1748
+ elif a == "--graph":
1749
+ graph_path = Path(_require_business_trace_value(a, i)); i += 2
1742
1750
  elif a.startswith("--graph="):
1743
1751
  graph_path = Path(a.split("=", 1)[1]); i += 1
1744
- elif a == "--concept" and i + 1 < len(args):
1745
- concept = args[i + 1]; i += 2
1752
+ elif a == "--concept":
1753
+ concept = _require_business_trace_value(a, i); i += 2
1746
1754
  elif a.startswith("--concept="):
1747
1755
  concept = a.split("=", 1)[1]; i += 1
1748
- elif a == "--scenario" and i + 1 < len(args):
1749
- scenario = args[i + 1]; i += 2
1756
+ elif a == "--scenario":
1757
+ scenario = _require_business_trace_value(a, i); i += 2
1750
1758
  elif a.startswith("--scenario="):
1751
1759
  scenario = a.split("=", 1)[1]; i += 1
1752
- elif a == "--flow" and i + 1 < len(args):
1753
- flow = args[i + 1]; i += 2
1760
+ elif a == "--flow":
1761
+ flow = _require_business_trace_value(a, i); i += 2
1754
1762
  elif a.startswith("--flow="):
1755
1763
  flow = a.split("=", 1)[1]; i += 1
1756
- elif a == "--max-depth" and i + 1 < len(args):
1764
+ elif a == "--max-depth":
1757
1765
  try:
1758
- max_depth = int(args[i + 1])
1766
+ max_depth = int(_require_business_trace_value(a, i))
1759
1767
  except ValueError:
1760
1768
  print("error: --max-depth must be an integer", file=sys.stderr)
1761
1769
  sys.exit(1)
@@ -1921,6 +1929,22 @@ def main() -> None:
1921
1929
  if thin_out is not None:
1922
1930
  print(f"Thin graph: {thin_out}")
1923
1931
 
1932
+ elif cmd == "repo-list":
1933
+ start = Path(sys.argv[2]) if len(sys.argv) > 2 else Path(".")
1934
+ if len(sys.argv) > 3:
1935
+ print("Usage: graphify repo-list [path]", file=sys.stderr)
1936
+ sys.exit(1)
1937
+ try:
1938
+ from graphify.repo_registry import ensure_repo_list as _ensure_repo_list
1939
+
1940
+ list_path = _ensure_repo_list(start.resolve())
1941
+ data = json.loads(list_path.read_text(encoding="utf-8"))
1942
+ repos = data.get("repos", []) if isinstance(data, dict) else []
1943
+ print(f"Repo list: {list_path} ({len(repos)} repos)")
1944
+ except Exception as exc:
1945
+ print(f"error: repo-list failed: {exc}", file=sys.stderr)
1946
+ sys.exit(1)
1947
+
1924
1948
  elif cmd == "main-graph":
1925
1949
  args = sys.argv[2:]
1926
1950
  root = Path(".")
@@ -2095,7 +2119,8 @@ def main() -> None:
2095
2119
  print(
2096
2120
  "Node counts: "
2097
2121
  f"page={stats.page_nodes}, component={stats.component_nodes}, "
2098
- f"mtop_api={stats.mtop_api_nodes}, rest_api={stats.rest_api_nodes}"
2122
+ f"mtop_api={stats.mtop_api_nodes}, rest_api={stats.rest_api_nodes}, "
2123
+ f"sdk_export={stats.sdk_export_nodes}, sdk_dependency={stats.sdk_dependency_nodes}"
2099
2124
  )
2100
2125
  print(f"HTML: {html_out}")
2101
2126
  print(f"Report: {report_out}")
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  import json
4
4
  import os
5
5
  import re
6
+ import hashlib
6
7
  from dataclasses import dataclass
7
8
  from pathlib import Path
8
9
 
@@ -220,6 +221,49 @@ def health_card_seed() -> dict:
220
221
  }
221
222
 
222
223
 
224
+ def _concept_id_from_name(name: str) -> str:
225
+ raw = str(name or "").strip()
226
+ if _matches_text(raw, "健康卡", ["health_card", "healthCard", "health card"]):
227
+ return "health_card"
228
+ lowered = raw.lower()
229
+ if "ai" in lowered and "找药" in raw:
230
+ return "ai_find_drug"
231
+ ascii_parts = _ascii_terms(raw)
232
+ slug = "_".join(ascii_parts)
233
+ slug = re.sub(r"[^a-z0-9_]+", "_", slug).strip("_")
234
+ if slug and not slug[0].isdigit():
235
+ return slug[:48]
236
+ digest = hashlib.sha1(raw.encode("utf-8")).hexdigest()[:8]
237
+ return f"concept_{digest}"
238
+
239
+
240
+ def concept_seed(concept: str) -> dict:
241
+ name = str(concept or "").strip()
242
+ if not name:
243
+ raise ValueError("concept is required")
244
+ aliases: list[str] = []
245
+ if _concept_id_from_name(name) == "ai_find_drug":
246
+ aliases = ["闪购AI找药", "千问找药"]
247
+ return {
248
+ "id": _concept_id_from_name(name),
249
+ "name": name,
250
+ "aliases": aliases,
251
+ "status": "unexplored",
252
+ "summary": f"待探索{name}相关业务场景、入口、API、后端承接链路和边界。",
253
+ "scenarios": [],
254
+ "gaps": ["缺少场景、入口 repo、页面/API 和后端锚点。"],
255
+ }
256
+
257
+
258
+ def _business_map_seed() -> dict:
259
+ return {
260
+ "version": 1,
261
+ "kind": "business-map",
262
+ "description": "Business concept map layered on top of graph-main facts.",
263
+ "concepts": [],
264
+ }
265
+
266
+
223
267
  def load_business_map(path: Path) -> dict:
224
268
  data = json.loads(path.read_text(encoding="utf-8"))
225
269
  if not isinstance(data, dict):
@@ -301,9 +345,12 @@ def default_business_graph_path(map_path: Path, start: Path | None = None) -> Pa
301
345
  plugin_root = os.environ.get("CLAUDE_PLUGIN_ROOT", "").strip()
302
346
  if plugin_root:
303
347
  graphify_out = Path(plugin_root).expanduser().resolve() / "graphify-out"
304
- hc_graph = graphify_out / "graph-hc.json"
305
- if hc_graph.exists():
306
- return hc_graph
348
+ graph = graphify_out / "graph.json"
349
+ if graph.exists():
350
+ return graph
351
+ legacy_graph = graphify_out / "graph-hc.json"
352
+ if legacy_graph.exists():
353
+ return legacy_graph
307
354
  candidates = sorted(
308
355
  p for p in graphify_out.glob("graph*.json")
309
356
  if p.name != "business-map.json"
@@ -331,11 +378,27 @@ def default_business_graph_path(map_path: Path, start: Path | None = None) -> Pa
331
378
 
332
379
 
333
380
  def init_business_map(concept: str, out_path: Path, *, force: bool = False) -> dict:
334
- if out_path.exists() and not force:
335
- raise FileExistsError(f"business map already exists: {out_path}")
336
- if not _matches_text(concept, "健康卡", ["health_card", "healthCard", "health card"]):
337
- raise ValueError(f"unsupported seed concept: {concept}. Currently supported: 健康卡")
338
- data = health_card_seed()
381
+ if force or not out_path.exists():
382
+ if _matches_text(concept, "健康卡", ["health_card", "healthCard", "health card"]):
383
+ data = health_card_seed()
384
+ else:
385
+ data = _business_map_seed()
386
+ data["concepts"].append(concept_seed(concept))
387
+ write_business_map(data, out_path)
388
+ return data
389
+
390
+ data = load_business_map(out_path)
391
+ concepts = data.setdefault("concepts", [])
392
+ if not isinstance(concepts, list):
393
+ raise ValueError("business map concepts must be a list")
394
+
395
+ candidate = (
396
+ health_card_seed()["concepts"][0]
397
+ if _matches_text(concept, "健康卡", ["health_card", "healthCard", "health card"])
398
+ else concept_seed(concept)
399
+ )
400
+ if not any(isinstance(item, dict) and _concept_matches(item, concept) for item in concepts):
401
+ concepts.append(candidate)
339
402
  write_business_map(data, out_path)
340
403
  return data
341
404