eduevidence 5.2.0

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 (312) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +506 -0
  3. package/README.zh-CN.md +434 -0
  4. package/SKILL.md +195 -0
  5. package/bin/eduevidence.js +127 -0
  6. package/domains/education/manifest.json +183 -0
  7. package/domains/education/outcome_taxonomy.json +127 -0
  8. package/domains/manifest.json +26 -0
  9. package/domains/policy/frame.schema.json +234 -0
  10. package/domains/policy/manifest.json +10 -0
  11. package/domains/policy/methodology_checklist.json +109 -0
  12. package/domains/policy/outcome_taxonomy.json +53 -0
  13. package/domains/policy/references/causal-identification.md +45 -0
  14. package/domains/policy/references/cost-evidence.md +44 -0
  15. package/domains/policy/references/equity.md +42 -0
  16. package/domains/policy/references/evidence-hierarchy.md +41 -0
  17. package/domains/policy/references/implementation-evidence.md +47 -0
  18. package/eduevidence_cli.py +26 -0
  19. package/engine/__init__.py +11 -0
  20. package/engine/__pycache__/__init__.cpython-312.pyc +0 -0
  21. package/engine/__pycache__/analysis.cpython-312.pyc +0 -0
  22. package/engine/__pycache__/bias.cpython-312.pyc +0 -0
  23. package/engine/__pycache__/briefs.cpython-312.pyc +0 -0
  24. package/engine/__pycache__/capabilities.cpython-312.pyc +0 -0
  25. package/engine/__pycache__/citation_check.cpython-312.pyc +0 -0
  26. package/engine/__pycache__/contracts.cpython-312.pyc +0 -0
  27. package/engine/__pycache__/datasets.cpython-312.pyc +0 -0
  28. package/engine/__pycache__/events.cpython-312.pyc +0 -0
  29. package/engine/__pycache__/evidence_graph.cpython-312.pyc +0 -0
  30. package/engine/__pycache__/evidence_review.cpython-312.pyc +0 -0
  31. package/engine/__pycache__/evidencecore.cpython-312.pyc +0 -0
  32. package/engine/__pycache__/gap_lens.cpython-312.pyc +0 -0
  33. package/engine/__pycache__/gaps.cpython-312.pyc +0 -0
  34. package/engine/__pycache__/graph_store.cpython-312.pyc +0 -0
  35. package/engine/__pycache__/graph_validate.cpython-312.pyc +0 -0
  36. package/engine/__pycache__/ids.cpython-312.pyc +0 -0
  37. package/engine/__pycache__/library.cpython-312.pyc +0 -0
  38. package/engine/__pycache__/library_builtin.cpython-312.pyc +0 -0
  39. package/engine/__pycache__/living.cpython-312.pyc +0 -0
  40. package/engine/__pycache__/log.cpython-312.pyc +0 -0
  41. package/engine/__pycache__/meta_analysis.cpython-312.pyc +0 -0
  42. package/engine/__pycache__/meta_synthesis.cpython-312.pyc +0 -0
  43. package/engine/__pycache__/migration.cpython-312.pyc +0 -0
  44. package/engine/__pycache__/mode_router.cpython-312.pyc +0 -0
  45. package/engine/__pycache__/paths.cpython-312.pyc +0 -0
  46. package/engine/__pycache__/pilot.cpython-312.pyc +0 -0
  47. package/engine/__pycache__/planner.cpython-312.pyc +0 -0
  48. package/engine/__pycache__/project.cpython-312.pyc +0 -0
  49. package/engine/__pycache__/projections.cpython-312.pyc +0 -0
  50. package/engine/__pycache__/robustness.cpython-312.pyc +0 -0
  51. package/engine/__pycache__/run.cpython-312.pyc +0 -0
  52. package/engine/__pycache__/semantics.cpython-312.pyc +0 -0
  53. package/engine/__pycache__/study_design.cpython-312.pyc +0 -0
  54. package/engine/__pycache__/synthesis.cpython-312.pyc +0 -0
  55. package/engine/__pycache__/tribunal.cpython-312.pyc +0 -0
  56. package/engine/__pycache__/update.cpython-312.pyc +0 -0
  57. package/engine/__pycache__/versions.cpython-312.pyc +0 -0
  58. package/engine/analysis.py +308 -0
  59. package/engine/bias.py +178 -0
  60. package/engine/briefs.py +106 -0
  61. package/engine/capabilities.py +99 -0
  62. package/engine/citation_check.py +192 -0
  63. package/engine/contracts.py +117 -0
  64. package/engine/datasets.py +165 -0
  65. package/engine/events.py +67 -0
  66. package/engine/evidence_graph.py +571 -0
  67. package/engine/evidence_review.py +88 -0
  68. package/engine/evidencecore.py +182 -0
  69. package/engine/gap_lens.py +132 -0
  70. package/engine/gaps.py +169 -0
  71. package/engine/graph_store.py +335 -0
  72. package/engine/graph_validate.py +87 -0
  73. package/engine/ids.py +77 -0
  74. package/engine/library.py +268 -0
  75. package/engine/library_builtin.py +301 -0
  76. package/engine/living.py +671 -0
  77. package/engine/log.py +39 -0
  78. package/engine/meta_analysis.py +333 -0
  79. package/engine/meta_synthesis.py +111 -0
  80. package/engine/migration.py +397 -0
  81. package/engine/mode_router.py +72 -0
  82. package/engine/paths.py +15 -0
  83. package/engine/pilot.py +368 -0
  84. package/engine/planner.py +126 -0
  85. package/engine/project.py +118 -0
  86. package/engine/projections.py +240 -0
  87. package/engine/robustness.py +109 -0
  88. package/engine/run.py +85 -0
  89. package/engine/semantics.py +135 -0
  90. package/engine/study_design.py +87 -0
  91. package/engine/synthesis.py +187 -0
  92. package/engine/tribunal.py +408 -0
  93. package/engine/update.py +113 -0
  94. package/engine/versions.py +12 -0
  95. package/install.sh +510 -0
  96. package/integrations/__init__.py +1 -0
  97. package/integrations/__pycache__/__init__.cpython-312.pyc +0 -0
  98. package/integrations/__pycache__/agent_mcp.cpython-312.pyc +0 -0
  99. package/integrations/__pycache__/smart_web_fetch.cpython-312.pyc +0 -0
  100. package/integrations/agent_mcp.py +856 -0
  101. package/integrations/smart_web_fetch.py +59 -0
  102. package/package.json +50 -0
  103. package/pyproject.toml +55 -0
  104. package/references/applicability-policy.md +88 -0
  105. package/references/education-framing.md +132 -0
  106. package/references/effect_size_formulas.md +35 -0
  107. package/references/evaluation-design.md +111 -0
  108. package/references/evidence-quality.md +79 -0
  109. package/references/grade_framework.md +29 -0
  110. package/references/intervention-design.md +98 -0
  111. package/references/methodology-audit.md +103 -0
  112. package/references/outcome-taxonomy.md +106 -0
  113. package/references/retrieval-protocol.md +142 -0
  114. package/references/skeptic-protocol.md +93 -0
  115. package/references/social_science_pitfalls.md +48 -0
  116. package/references/source-validity.md +140 -0
  117. package/references/tribunal-policy.md +112 -0
  118. package/references/wwc_standards.md +29 -0
  119. package/retrieval/__init__.py +1 -0
  120. package/retrieval/__pycache__/__init__.cpython-312.pyc +0 -0
  121. package/retrieval/__pycache__/corpus_store.cpython-312.pyc +0 -0
  122. package/retrieval/__pycache__/dedupe.cpython-312.pyc +0 -0
  123. package/retrieval/__pycache__/failures.cpython-312.pyc +0 -0
  124. package/retrieval/__pycache__/fetch.cpython-312.pyc +0 -0
  125. package/retrieval/__pycache__/search.cpython-312.pyc +0 -0
  126. package/retrieval/__pycache__/source.cpython-312.pyc +0 -0
  127. package/retrieval/__pycache__/validate.cpython-312.pyc +0 -0
  128. package/retrieval/corpus_store.py +181 -0
  129. package/retrieval/dedupe.py +127 -0
  130. package/retrieval/failures.py +90 -0
  131. package/retrieval/fetch.py +435 -0
  132. package/retrieval/search.py +493 -0
  133. package/retrieval/source.py +160 -0
  134. package/retrieval/validate.py +257 -0
  135. package/schemas/agent-mcp-approval.schema.json +57 -0
  136. package/schemas/chart-spec.schema.json +88 -0
  137. package/schemas/cross-model-review.schema.json +28 -0
  138. package/schemas/education-frame.schema.json +106 -0
  139. package/schemas/evaluation.schema.json +35 -0
  140. package/schemas/evidence.schema.json +81 -0
  141. package/schemas/fetch-result.schema.json +119 -0
  142. package/schemas/intervention.schema.json +46 -0
  143. package/schemas/methodology.schema.json +102 -0
  144. package/schemas/report-result.schema.json +381 -0
  145. package/schemas/report-spec.schema.json +130 -0
  146. package/schemas/source.schema.json +311 -0
  147. package/schemas/v2/analysis-plan.schema.json +28 -0
  148. package/schemas/v2/analysis-run.schema.json +33 -0
  149. package/schemas/v2/claim.schema.json +26 -0
  150. package/schemas/v2/dataset-asset.schema.json +40 -0
  151. package/schemas/v2/decision-snapshot.schema.json +53 -0
  152. package/schemas/v2/evidence-link.schema.json +38 -0
  153. package/schemas/v2/finding.schema.json +47 -0
  154. package/schemas/v2/graph-revision.schema.json +30 -0
  155. package/schemas/v2/knowledge-gap.schema.json +40 -0
  156. package/schemas/v2/methodology-audit.schema.json +30 -0
  157. package/schemas/v2/outcome.schema.json +18 -0
  158. package/schemas/v2/project.schema.json +31 -0
  159. package/schemas/v2/research-intent.schema.json +24 -0
  160. package/schemas/v2/run.schema.json +43 -0
  161. package/schemas/v2/source.schema.json +24 -0
  162. package/schemas/v2/study-design.schema.json +67 -0
  163. package/schemas/v2/study.schema.json +37 -0
  164. package/schemas/v3/pilot-outcome.schema.json +132 -0
  165. package/schemas/v3/run-manifest.schema.json +193 -0
  166. package/schemas/v3/synthesis.schema.json +86 -0
  167. package/schemas/v4/drift-report.schema.json +66 -0
  168. package/schemas/v4/evidence-library.schema.json +42 -0
  169. package/schemas/v4/living-subscription.schema.json +55 -0
  170. package/schemas/v4/meta-analysis.schema.json +152 -0
  171. package/schemas/verdict.schema.json +56 -0
  172. package/scripts/__init__.py +0 -0
  173. package/scripts/__pycache__/__init__.cpython-312.pyc +0 -0
  174. package/scripts/__pycache__/benchmark.cpython-312.pyc +0 -0
  175. package/scripts/__pycache__/benchmark_evaluator.cpython-312.pyc +0 -0
  176. package/scripts/__pycache__/benchmark_judge.cpython-312.pyc +0 -0
  177. package/scripts/__pycache__/benchmark_routing.cpython-312.pyc +0 -0
  178. package/scripts/__pycache__/benchmark_v2.cpython-312.pyc +0 -0
  179. package/scripts/__pycache__/benchmark_v3.cpython-312.pyc +0 -0
  180. package/scripts/__pycache__/build_result.cpython-312.pyc +0 -0
  181. package/scripts/__pycache__/claim_audit.cpython-312.pyc +0 -0
  182. package/scripts/__pycache__/complexity_gate.cpython-312.pyc +0 -0
  183. package/scripts/__pycache__/compute_confidence.cpython-312.pyc +0 -0
  184. package/scripts/__pycache__/dashboard_server.cpython-312.pyc +0 -0
  185. package/scripts/__pycache__/did_regression.cpython-312.pyc +0 -0
  186. package/scripts/__pycache__/effect_calculator.cpython-312.pyc +0 -0
  187. package/scripts/__pycache__/evidence_matrix.cpython-312.pyc +0 -0
  188. package/scripts/__pycache__/evidence_score.cpython-312.pyc +0 -0
  189. package/scripts/__pycache__/evidence_semantics.cpython-312.pyc +0 -0
  190. package/scripts/__pycache__/fetch_benchmark.cpython-312.pyc +0 -0
  191. package/scripts/__pycache__/lint_report_layout.cpython-312.pyc +0 -0
  192. package/scripts/__pycache__/orchestrator.cpython-312.pyc +0 -0
  193. package/scripts/__pycache__/pre_verdict_gate.cpython-312.pyc +0 -0
  194. package/scripts/__pycache__/recompute_demo_quality.cpython-312.pyc +0 -0
  195. package/scripts/__pycache__/render_report.cpython-312.pyc +0 -0
  196. package/scripts/__pycache__/render_report_html.cpython-312.pyc +0 -0
  197. package/scripts/__pycache__/run_workspace.cpython-312.pyc +0 -0
  198. package/scripts/__pycache__/skill_lint.cpython-312.pyc +0 -0
  199. package/scripts/__pycache__/startup_probe.cpython-312.pyc +0 -0
  200. package/scripts/__pycache__/sync_killer_demo_report.cpython-312.pyc +0 -0
  201. package/scripts/__pycache__/test_adversarial_empirical.cpython-312-pytest-9.0.2.pyc +0 -0
  202. package/scripts/__pycache__/test_adversarial_empirical.cpython-312-pytest-9.1.1.pyc +0 -0
  203. package/scripts/__pycache__/validate_schema.cpython-312.pyc +0 -0
  204. package/scripts/audit_dois.py +292 -0
  205. package/scripts/bake_pack.sh +37 -0
  206. package/scripts/benchmark.py +183 -0
  207. package/scripts/benchmark_evaluator.py +371 -0
  208. package/scripts/benchmark_judge.py +535 -0
  209. package/scripts/benchmark_routing.py +120 -0
  210. package/scripts/benchmark_v2.py +304 -0
  211. package/scripts/benchmark_v3.py +552 -0
  212. package/scripts/build_esl_artifacts.py +1921 -0
  213. package/scripts/build_evidence_library.py +307 -0
  214. package/scripts/build_killer_demo.py +295 -0
  215. package/scripts/build_result.py +311 -0
  216. package/scripts/check_version_consistency.py +96 -0
  217. package/scripts/citation_check.py +123 -0
  218. package/scripts/claim_audit.py +157 -0
  219. package/scripts/complexity_gate.py +180 -0
  220. package/scripts/compute_confidence.py +176 -0
  221. package/scripts/dashboard_server.py +536 -0
  222. package/scripts/did_regression.py +315 -0
  223. package/scripts/effect_calculator.py +99 -0
  224. package/scripts/enrich_projects_human_and_lieflat.py +315 -0
  225. package/scripts/evidence_matrix.py +129 -0
  226. package/scripts/evidence_score.py +234 -0
  227. package/scripts/evidence_semantics.py +87 -0
  228. package/scripts/fetch_benchmark.py +177 -0
  229. package/scripts/generate_metrics.py +99 -0
  230. package/scripts/generate_new_projects.py +686 -0
  231. package/scripts/generate_promo.py +192 -0
  232. package/scripts/lint_report_layout.py +182 -0
  233. package/scripts/orchestrator.py +1456 -0
  234. package/scripts/pre_verdict_gate.py +513 -0
  235. package/scripts/quickstart.py +121 -0
  236. package/scripts/rebake_all_5themes.py +88 -0
  237. package/scripts/recompute_demo_quality.py +205 -0
  238. package/scripts/render_report.py +270 -0
  239. package/scripts/render_report_html.py +356 -0
  240. package/scripts/retraction_watch.py +110 -0
  241. package/scripts/run_workspace.py +337 -0
  242. package/scripts/serve_web.py +54 -0
  243. package/scripts/skill_lint.py +150 -0
  244. package/scripts/startup_probe.py +265 -0
  245. package/scripts/sync_killer_demo_report.py +270 -0
  246. package/scripts/test_adversarial_empirical.py +541 -0
  247. package/scripts/validate_schema.py +256 -0
  248. package/skill/agents/education-planner.md +80 -0
  249. package/skill/agents/evaluation-designer.md +74 -0
  250. package/skill/agents/evidence-analyst.md +106 -0
  251. package/skill/agents/evidence-judge.md +111 -0
  252. package/skill/agents/evidence-retriever.md +80 -0
  253. package/skill/agents/intervention-designer.md +82 -0
  254. package/skill/agents/method-reviewer.md +104 -0
  255. package/skill/agents/skeptic.md +89 -0
  256. package/skill/sub-skills/aihot-trend-analysis/SKILL.md +31 -0
  257. package/skill/sub-skills/contradiction-analysis/SKILL.md +17 -0
  258. package/skill/sub-skills/data-analysis/SKILL.md +23 -0
  259. package/skill/sub-skills/ethics-review/SKILL.md +25 -0
  260. package/skill/sub-skills/evidence-extraction/SKILL.md +19 -0
  261. package/skill/sub-skills/evidence-review/SKILL.md +18 -0
  262. package/skill/sub-skills/gap-analysis/SKILL.md +25 -0
  263. package/skill/sub-skills/literature-review/SKILL.md +21 -0
  264. package/skill/sub-skills/methodology-audit/SKILL.md +20 -0
  265. package/skill/sub-skills/report-generation/SKILL.md +51 -0
  266. package/skill/sub-skills/research-planning/SKILL.md +21 -0
  267. package/skill/sub-skills/study-design/SKILL.md +16 -0
  268. package/skill/task-briefs/adjudicate.md +17 -0
  269. package/skill/task-briefs/audit.md +15 -0
  270. package/skill/task-briefs/challenge.md +15 -0
  271. package/skill/task-briefs/evaluate.md +13 -0
  272. package/skill/task-briefs/extract.md +16 -0
  273. package/skill/task-briefs/frame.md +17 -0
  274. package/skill/task-briefs/intervene.md +14 -0
  275. package/skill/task-briefs/present.md +16 -0
  276. package/skill/task-briefs/retrieve.md +15 -0
  277. package/visualization/eduevidence-report/assets/base.css +337 -0
  278. package/visualization/eduevidence-report/motion/motion.css +157 -0
  279. package/visualization/eduevidence-report/motion/motion.js +107 -0
  280. package/visualization/eduevidence-report/references/bilingual-style.md +7 -0
  281. package/visualization/eduevidence-report/references/component-catalog.md +145 -0
  282. package/visualization/eduevidence-report/references/evidence-expansion.md +65 -0
  283. package/visualization/eduevidence-report/references/full-report-outline.md +86 -0
  284. package/visualization/eduevidence-report/references/layout-constraints.md +63 -0
  285. package/visualization/eduevidence-report/references/lieflat-composition.md +79 -0
  286. package/visualization/eduevidence-report/references/motion-system.md +31 -0
  287. package/visualization/eduevidence-report/schemas/adapter-envelope.schema.json +22 -0
  288. package/visualization/eduevidence-report/schemas/visual-layout.schema.json +87 -0
  289. package/visualization/eduevidence-report/scripts/__pycache__/adapter_contract.cpython-312.pyc +0 -0
  290. package/visualization/eduevidence-report/scripts/__pycache__/build_artifact_manifest.cpython-312.pyc +0 -0
  291. package/visualization/eduevidence-report/scripts/__pycache__/build_charts.cpython-312.pyc +0 -0
  292. package/visualization/eduevidence-report/scripts/__pycache__/build_figures.cpython-312.pyc +0 -0
  293. package/visualization/eduevidence-report/scripts/__pycache__/build_infographics.cpython-312.pyc +0 -0
  294. package/visualization/eduevidence-report/scripts/__pycache__/build_report.cpython-312.pyc +0 -0
  295. package/visualization/eduevidence-report/scripts/__pycache__/charts_data.cpython-312.pyc +0 -0
  296. package/visualization/eduevidence-report/scripts/__pycache__/lieflat_engine.cpython-312.pyc +0 -0
  297. package/visualization/eduevidence-report/scripts/__pycache__/zh_labels.cpython-312.pyc +0 -0
  298. package/visualization/eduevidence-report/scripts/adapter_contract.py +72 -0
  299. package/visualization/eduevidence-report/scripts/build_artifact_manifest.py +70 -0
  300. package/visualization/eduevidence-report/scripts/build_charts.py +283 -0
  301. package/visualization/eduevidence-report/scripts/build_figures.py +515 -0
  302. package/visualization/eduevidence-report/scripts/build_infographics.py +268 -0
  303. package/visualization/eduevidence-report/scripts/build_report.py +3211 -0
  304. package/visualization/eduevidence-report/scripts/charts_data.py +617 -0
  305. package/visualization/eduevidence-report/scripts/check_mobile_layout.js +220 -0
  306. package/visualization/eduevidence-report/scripts/lieflat_engine.py +852 -0
  307. package/visualization/eduevidence-report/scripts/zh_labels.py +245 -0
  308. package/visualization/eduevidence-report/themes/academic.css +94 -0
  309. package/visualization/eduevidence-report/themes/claude.css +96 -0
  310. package/visualization/eduevidence-report/themes/datalab-dark.css +147 -0
  311. package/visualization/eduevidence-report/themes/datalab.css +151 -0
  312. package/visualization/eduevidence-report/themes/presentation.css +140 -0
@@ -0,0 +1,113 @@
1
+ """Close the Full Research Cycle: validated project analysis → graph evidence.
2
+
3
+ One atomic mutation: a project-local Source (portable project:// locator,
4
+ derived from DatasetAsset + AnalysisRun provenance), one Study, its
5
+ Findings, MethodologyAudit, and contextual Claims/Links commit in ONE graph
6
+ revision — never one revision per Finding.
7
+
8
+ AnalysisRun.status must be `validated`; otherwise ANALYSIS_INVALID is
9
+ returned and the graph stays unchanged. The engine derives the Source; the
10
+ caller supplies schema-valid Study/Findings/Audit/Claims/Links.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from datetime import datetime, timezone
16
+
17
+ from engine.contracts import validate_record
18
+ from engine.graph_store import GraphStore, GraphMutation, GraphRevision
19
+
20
+
21
+ def _now_iso() -> str:
22
+ return datetime.now(timezone.utc).isoformat()
23
+
24
+
25
+ def project_study_mutation(*, project: ProjectWorkspace,
26
+ design: dict,
27
+ dataset_asset: dict,
28
+ analysis_run: dict,
29
+ study: dict,
30
+ findings: list[dict],
31
+ methodology_audit: dict,
32
+ claims: list[dict],
33
+ links: list[dict]) -> GraphMutation:
34
+ """Build the bundle mutation; raises ValueError on invalid inputs.
35
+
36
+ The derived project-local Source carries a portable `project://` locator
37
+ referencing the dataset; raw filesystem paths stay provenance metadata.
38
+ """
39
+ if analysis_run.get("status") != "validated":
40
+ raise ValueError(
41
+ f"ANALYSIS_INVALID: AnalysisRun {analysis_run.get('analysis_run_id')} "
42
+ f"status {analysis_run.get('status')!r} is not 'validated'; graph "
43
+ f"unchanged")
44
+
45
+ source_id = f"SRC-{dataset_asset['dataset_id']}"
46
+ source = {
47
+ "source_id": source_id,
48
+ "origin": "project",
49
+ "source_type": "project_study",
50
+ "canonical_locator": (
51
+ f"project://{project.project_id}/datasets/{dataset_asset['dataset_id']}"),
52
+ "validation_status": "valid",
53
+ "content_hash": dataset_asset["content_hash"],
54
+ "extensions": {
55
+ "dataset_id": dataset_asset["dataset_id"],
56
+ "design_id": design["design_id"],
57
+ "analysis_run_id": analysis_run["analysis_run_id"],
58
+ "provenance": {
59
+ "provider": (analysis_run.get("extensions") or {}).get("provider"),
60
+ "software": (analysis_run.get("extensions") or {}).get("software"),
61
+ },
62
+ },
63
+ }
64
+ errors = validate_record("source", source)
65
+ if errors:
66
+ raise ValueError(f"derived project source invalid: {errors}")
67
+
68
+ # validate the whole bundle before touching the store
69
+ for label, schema, rec in (
70
+ ("study", "study", study),
71
+ ("audit", "methodology-audit", methodology_audit),
72
+ ):
73
+ errs = validate_record(schema, rec)
74
+ if errs:
75
+ raise ValueError(f"{label} invalid: {errs}")
76
+ for f in findings:
77
+ errs = validate_record("finding", f)
78
+ if errs:
79
+ raise ValueError(f"finding {f.get('finding_id')} invalid: {errs}")
80
+ for c in claims:
81
+ errs = validate_record("claim", c)
82
+ if errs:
83
+ raise ValueError(f"claim {c.get('claim_id')} invalid: {errs}")
84
+ for l in links:
85
+ errs = validate_record("evidence-link", l)
86
+ if errs:
87
+ raise ValueError(f"evidence_link {l.get('evidence_link_id')} invalid: {errs}")
88
+
89
+ return GraphMutation(
90
+ upserts={
91
+ "sources": [source],
92
+ "studies": [study],
93
+ "findings": findings,
94
+ "audits": [methodology_audit],
95
+ "claims": claims,
96
+ "evidence_links": links,
97
+ },
98
+ retire_ids={},
99
+ )
100
+
101
+
102
+ def commit_project_study(*, store: GraphStore, run_id: str,
103
+ design: dict, dataset_asset: dict,
104
+ analysis_run: dict, study: dict,
105
+ findings: list[dict], methodology_audit: dict,
106
+ claims: list[dict], links: list[dict]) -> GraphRevision:
107
+ """Validate the bundle and commit it as exactly ONE graph revision."""
108
+ mutation = project_study_mutation(
109
+ project=store.project, design=design, dataset_asset=dataset_asset,
110
+ analysis_run=analysis_run, study=study, findings=findings,
111
+ methodology_audit=methodology_audit, claims=claims, links=links)
112
+ return store.commit(run_id=run_id, reason="project study evidence",
113
+ mutation=mutation)
@@ -0,0 +1,12 @@
1
+ """Version pins for the V2 Research Engine.
2
+
3
+ Policy versions are frozen identifiers, not free-form strings: changing a
4
+ policy requires a new version, never silent mutation of an existing one.
5
+ """
6
+
7
+ ENGINE_VERSION = "5.2.0"
8
+ GRAPH_SCHEMA_VERSION = "2.0"
9
+ SOURCE_VALIDATION_POLICY_VERSION = "2026-08-12.v2"
10
+ METHODOLOGY_POLICY_VERSION = "2026-08-12.v2"
11
+ CONFIDENCE_POLICY_VERSION = "2026-08-12.v3"
12
+ REPORT_CONTRACT_VERSION = "3.0"
package/install.sh ADDED
@@ -0,0 +1,510 @@
1
+ #!/usr/bin/env bash
2
+ # EduEvidence 一键安装脚本(macOS / Linux)
3
+ #
4
+ # 供应链提示(E7):远程执行请优先走「clone + 本地审阅」路径——
5
+ # git clone --depth 1 https://github.com/37chengshan/eduevidence
6
+ # cd eduevidence && bash install.sh
7
+ # 若必须 curl 直跑,建议固定到具体 commit 并先人工审阅:
8
+ # curl -fsSL https://raw.githubusercontent.com/37chengshan/eduevidence/<commit>/install.sh -o install.sh
9
+ #
10
+ # 副作用披露:本脚本会 (a) 创建 Python venv 并安装依赖;
11
+ # (b) 写入 $HOME/.eduevidence/env(AGENT_MCP_INSTALLED=1 声明);
12
+ # (c) --skill 模式在所选宿主目录写入 skill 文件(覆盖前自动备份)。
13
+ #
14
+ # 用法:
15
+ # bash install.sh # 本地安装:venv + 依赖 + 自检 + 测试(等价 --dev)
16
+ # bash install.sh --dev # 同上(向后兼容,默认行为)
17
+ # bash install.sh --skill # 交互式安装为 AI Agent Skill(选择宿主 Agent)
18
+ # bash install.sh --list-hosts # 显示支持的宿主 Agent 与 Skill 落点
19
+ # bash install.sh --dry-run # 只预览将执行的变更,不写入任何文件
20
+ # bash install.sh --skill --dry-run # 预览 Skill 安装(仍会交互选择宿主)
21
+ # bash install.sh --skill --host claude # 非交互:安装到指定宿主
22
+ # bash install.sh --skill --host all # 非交互:安装到全部宿主
23
+ # bash install.sh --skill --dest ~/.cursor/skills # 非交互:自定义 skill 根目录
24
+ # bash install.sh --skill --skill-only --host claude # 仅复制 Skill,跳过 venv/pytest
25
+ set -euo pipefail
26
+
27
+ REPO_URL="https://github.com/37chengshan/eduevidence"
28
+ SKILL_NAME="eduevidence"
29
+ # Skill 本体(运行协议 + 确定性脚本 + 检索/集成层 + 展示层)。
30
+ # retrieval/ 与 integrations/ 会被 scripts/ 直接 import;visualization/ 负责最终 HTML 渲染。
31
+ SKILL_PAYLOAD=(SKILL.md engine domains skill references schemas scripts retrieval integrations visualization)
32
+ # Agent MCP 声明文件:安装完成后写入 AGENT_MCP_INSTALLED=1,供
33
+ # integrations/agent_mcp.py 作为 env 后备来源读取(真实环境变量优先于该文件)。
34
+ AGENT_MCP_ENV_FILE="${AGENT_MCP_ENV_FILE:-$HOME/.eduevidence/env}"
35
+
36
+ # ---------- curl 远程执行支持 ----------
37
+ # 用法: bash -c "$(curl -fsSL https://raw.githubusercontent.com/37chengshan/eduevidence/main/install.sh)"
38
+ # 脚本不在仓库内运行时(无 SKILL.md),自动 clone 仓库到 ./eduevidence 并进入。
39
+ if [ ! -f "SKILL.md" ]; then
40
+ echo "==> 未在 EduEvidence 仓库内,自动克隆仓库…"
41
+ if command -v git >/dev/null 2>&1; then
42
+ git clone --depth 1 "$REPO_URL" eduevidence
43
+ cd eduevidence
44
+ else
45
+ echo "ERROR: 未找到 git,请先安装 git 或手动 clone 仓库" >&2
46
+ exit 1
47
+ fi
48
+ fi
49
+
50
+ # ---------- 参数解析 ----------
51
+ INSTALL_DEV=""
52
+ HAS_DEV_FLAG=0
53
+ MODE="local" # local | skill | list
54
+ DRY_RUN=0
55
+ HOST_CHOICE=""
56
+ DEST_ROOT=""
57
+ SKILL_ONLY=0
58
+ while [ $# -gt 0 ]; do
59
+ case "$1" in
60
+ --dev) INSTALL_DEV="--dev"; HAS_DEV_FLAG=1; shift ;;
61
+ --skill) MODE="skill"; shift ;;
62
+ --skill-only) SKILL_ONLY=1; MODE="skill"; shift ;;
63
+ --list-hosts) MODE="list"; shift ;;
64
+ --dry-run) DRY_RUN=1; shift ;;
65
+ --host)
66
+ HOST_CHOICE="${2:-}"
67
+ if [ -z "$HOST_CHOICE" ]; then
68
+ echo "ERROR: --host 需要参数(如 claude / codex / all)" >&2
69
+ exit 1
70
+ fi
71
+ MODE="skill"
72
+ shift 2
73
+ ;;
74
+ --dest)
75
+ DEST_ROOT="${2:-}"
76
+ if [ -z "$DEST_ROOT" ]; then
77
+ echo "ERROR: --dest 需要 skill 根目录参数" >&2
78
+ exit 1
79
+ fi
80
+ DEST_ROOT="${DEST_ROOT%/}"
81
+ DEST_ROOT="${DEST_ROOT/#\~/$HOME}"
82
+ MODE="skill"
83
+ shift 2
84
+ ;;
85
+ *) echo "ERROR: 未知参数: $1" >&2; exit 1 ;;
86
+ esac
87
+ done
88
+ # 向后兼容:裸 `bash install.sh` 等价于 `bash install.sh --dev`
89
+ if [ "$MODE" = "local" ] && [ "$HAS_DEV_FLAG" -eq 0 ]; then
90
+ INSTALL_DEV="--dev"
91
+ fi
92
+
93
+ # ---------- 宿主 Agent 表 ----------
94
+ HOSTS=(claude codex cursor omp opencode kimi zcode openclaw harness grok copilot cline)
95
+
96
+ # 探测宿主是否已安装(尽力而为;未探测到不阻塞安装,只做提示)
97
+ host_detect() {
98
+ case "$1" in
99
+ claude) [ -d "$HOME/.claude" ] || [ -f "$HOME/.claude.json" ] ;;
100
+ codex) [ -d "$HOME/.codex" ] || command -v codex >/dev/null 2>&1 ;;
101
+ cursor) [ -d "$HOME/.cursor" ] ;;
102
+ omp) [ -d "$HOME/.omp" ] ;;
103
+ opencode) [ -d "$HOME/.config/opencode" ] || command -v opencode >/dev/null 2>&1 ;;
104
+ kimi) [ -d "${KIMI_CODE_HOME:-$HOME/.kimi-code}" ] || command -v kimi >/dev/null 2>&1 ;;
105
+ zcode) [ -d "$HOME/.zcode" ] ;;
106
+ openclaw) [ -d "$HOME/.openclaw" ] ;;
107
+ harness) [ -d "$HOME/.harness" ] ;;
108
+ grok) [ -d "$HOME/.grok" ] ;;
109
+ copilot) [ -d "$HOME/.copilot" ] || command -v copilot >/dev/null 2>&1 ;;
110
+ cline) [ -d "$HOME/.cline" ] || [ -d "$HOME/.config/cline" ] ;;
111
+ *) return 1 ;;
112
+ esac
113
+ }
114
+
115
+ # 返回某宿主的 skill 根目录(eduevidence/ 会创建为其子目录)
116
+ host_skill_root() {
117
+ case "$1" in
118
+ claude)
119
+ if [ -d "$HOME/.claude/skills" ] || [ -d "$HOME/.claude" ]; then
120
+ echo "$HOME/.claude/skills" # 用户级配置存在 → 用户级落点
121
+ else
122
+ echo "$PWD/.claude/skills" # 否则装到项目级 .claude/skills/
123
+ fi
124
+ ;;
125
+ codex)
126
+ # 官方标准 ~/.agents/skills,兼容 ~/.codex/skills 与 ~/.codex/prompts,按探测顺序取
127
+ if [ -d "$HOME/.agents/skills" ]; then
128
+ echo "$HOME/.agents/skills"
129
+ elif [ -d "$HOME/.codex/skills" ]; then
130
+ echo "$HOME/.codex/skills"
131
+ elif [ -d "$HOME/.codex/prompts" ]; then
132
+ echo "$HOME/.codex/prompts"
133
+ else
134
+ echo "$HOME/.agents/skills"
135
+ fi
136
+ ;;
137
+ cursor) echo "$HOME/.cursor/skills" ;;
138
+ omp) echo "$HOME/.omp/agent/skills" ;;
139
+ opencode) echo "$HOME/.config/opencode/skills" ;;
140
+ kimi) echo "${KIMI_CODE_HOME:-$HOME/.kimi-code}/skills" ;;
141
+ zcode) echo "$HOME/.zcode/skills" ;;
142
+ openclaw) echo "$HOME/.openclaw/skills" ;;
143
+ harness) echo "$HOME/.harness/skills" ;;
144
+ grok) echo "$HOME/.grok/skills" ;;
145
+ copilot) echo "$HOME/.copilot/skills" ;;
146
+ cline) echo "$HOME/.cline/skills" ;;
147
+ esac
148
+ }
149
+
150
+ # ---------- 通用提示词(方式三:宿主不在支持列表时交给任意 AI) ----------
151
+ UNIVERSAL_PROMPT="请把 https://github.com/37chengshan/eduevidence 仓库中的 EduEvidence 安装为 skill:
152
+ 1. 将仓库根目录的 SKILL.md、skill/、references/、schemas/、scripts/、retrieval/、integrations/、visualization/ 复制到你的 skill 目录
153
+ (如 ~/.claude/skills/eduevidence/、~/.omp/agent/skills/eduevidence/、~/.agents/skills/eduevidence/ 等),
154
+ 或按你的 skill 装载机制导入;
155
+ 2. 安装完成后确认能读取 SKILL.md,并能运行 scripts/ 下的确定性脚本;
156
+ 3. 告诉我安装结果。"
157
+
158
+ print_universal_prompt() {
159
+ echo ""
160
+ echo "============================================================"
161
+ echo "方式三:通用提示词(宿主 Agent 不在支持列表时使用)"
162
+ echo "============================================================"
163
+ echo "把下面这段提示词原样交给任意支持 skill / 自定义指令的 AI:"
164
+ echo ""
165
+ echo "$UNIVERSAL_PROMPT"
166
+ echo ""
167
+ echo "============================================================"
168
+ }
169
+
170
+ # ---------- 安装完成后的提示(仅提示,不自动执行任何 star 操作) ----------
171
+ star_prompt() {
172
+ echo ""
173
+ echo "==> If EduEvidence helps, consider starring ⭐ ${REPO_URL}(仅提示,不会自动点 star)"
174
+ }
175
+
176
+ # ---------- Agent MCP 声明(~/.eduevidence/env) ----------
177
+ # 安装完成后写入 AGENT_MCP_INSTALLED=1,供 integrations/agent_mcp.py 读取。
178
+ # 只写「已安装」声明,不安装 agent-mcp daemon:detect 仍要求 daemon 可达
179
+ # (声明但 daemon 未起 -> state: unavailable;daemon 起但未声明 ->
180
+ # state: daemon_reachable_undeclared)。
181
+ write_agent_mcp_env() {
182
+ if [ "$DRY_RUN" -eq 1 ]; then
183
+ echo " [dry-run] 将写入 $AGENT_MCP_ENV_FILE: AGENT_MCP_INSTALLED=1"
184
+ return 0
185
+ fi
186
+ mkdir -p "$(dirname "$AGENT_MCP_ENV_FILE")"
187
+ touch "$AGENT_MCP_ENV_FILE"
188
+ if grep -q '^AGENT_MCP_INSTALLED=' "$AGENT_MCP_ENV_FILE" 2>/dev/null; then
189
+ # 幂等更新(兼容 macOS/BSD 与 GNU sed:不用 -i)
190
+ local tmp
191
+ tmp="$(mktemp)"
192
+ sed 's/^AGENT_MCP_INSTALLED=.*/AGENT_MCP_INSTALLED=1/' \
193
+ "$AGENT_MCP_ENV_FILE" > "$tmp"
194
+ mv "$tmp" "$AGENT_MCP_ENV_FILE"
195
+ else
196
+ printf 'AGENT_MCP_INSTALLED=1\n' >> "$AGENT_MCP_ENV_FILE"
197
+ fi
198
+ echo " ✅ 已写入 ${AGENT_MCP_ENV_FILE}(AGENT_MCP_INSTALLED=1)"
199
+ }
200
+
201
+ # ---------- 本地安装:Python 检查 + venv + 依赖 + 自检(--dev 时含 pytest) ----------
202
+ local_setup() {
203
+ local PYTHON_BIN="${PYTHON:-python3}"
204
+
205
+ echo "==> EduEvidence 安装开始"
206
+
207
+ # 1. 检查 Python >= 3.10
208
+ if ! command -v "$PYTHON_BIN" >/dev/null 2>&1; then
209
+ if [ "$DRY_RUN" -eq 1 ]; then
210
+ echo " [dry-run] Python 版本检查将失败:未找到 ${PYTHON_BIN}(需要 3.10+)"
211
+ echo " [dry-run] 预览结束(本环境不会真正安装)"
212
+ exit 0
213
+ fi
214
+ echo "ERROR: 未找到 ${PYTHON_BIN},请先安装 Python 3.10+ (https://www.python.org/downloads/)" >&2
215
+ exit 1
216
+ fi
217
+ PY_VERSION="$("$PYTHON_BIN" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')"
218
+ PY_MAJOR="${PY_VERSION%%.*}"
219
+ PY_MINOR="${PY_VERSION#*.}"
220
+ if [ "$PY_MAJOR" -lt 3 ] || { [ "$PY_MAJOR" -eq 3 ] && [ "$PY_MINOR" -lt 10 ]; }; then
221
+ if [ "$DRY_RUN" -eq 1 ]; then
222
+ echo " [dry-run] Python 版本检查将失败:需要 3.10+,当前 $PY_VERSION"
223
+ echo " [dry-run] 预览结束(本环境不会真正安装)"
224
+ exit 0
225
+ fi
226
+ echo "ERROR: 需要 Python 3.10+,当前 $PY_VERSION" >&2
227
+ exit 1
228
+ fi
229
+ echo " Python $PY_VERSION OK"
230
+
231
+ # 2. 创建 venv(如不存在)
232
+ if [ ! -d ".venv" ]; then
233
+ if [ "$DRY_RUN" -eq 1 ]; then
234
+ echo " [dry-run] 将创建虚拟环境 .venv"
235
+ else
236
+ echo "==> 创建虚拟环境 .venv"
237
+ "$PYTHON_BIN" -m venv .venv
238
+ fi
239
+ fi
240
+ if [ "$DRY_RUN" -eq 1 ]; then
241
+ echo " [dry-run] 将激活 .venv 并安装依赖(pip install -e .)"
242
+ echo " [dry-run] 将运行自检:Schema 校验 + 渲染双语 HTML 报告"
243
+ write_agent_mcp_env
244
+ return 0
245
+ fi
246
+ # shellcheck disable=SC1091
247
+ source .venv/bin/activate
248
+ echo " venv: $(command -v python)"
249
+
250
+ # 3. 安装依赖(EduEvidence 核心零依赖;--dev 额外安装 pytest)
251
+ if [ "$INSTALL_DEV" = "--dev" ]; then
252
+ echo "==> 安装开发依赖(pytest)"
253
+ pip install --quiet --upgrade pip
254
+ pip install --quiet -e '.[dev]'
255
+ else
256
+ echo "==> 安装核心(零第三方依赖)"
257
+ pip install --quiet --upgrade pip
258
+ pip install --quiet -e .
259
+ fi
260
+
261
+ # 4. 可选:matplotlib 用于学术图 PNG/PDF 导出(非必需)
262
+ if python -c "import matplotlib" 2>/dev/null; then
263
+ echo " matplotlib: 已安装(学术图 PNG/PDF 可用)"
264
+ else
265
+ echo " matplotlib: 未安装(学术图仍可输出 SVG;PNG/PDF 导出需要 pip install matplotlib)"
266
+ fi
267
+
268
+ # 5. 自检:Schema 校验 + 报告渲染
269
+ echo "==> 自检:Schema 校验"
270
+ python scripts/validate_schema.py --schema schemas/verdict.schema.json \
271
+ --data examples/ai-coding-assistant/verdict.json
272
+ python scripts/validate_schema.py --schema schemas/evidence.schema.json \
273
+ --data examples/ai-coding-assistant/evidence.jsonl
274
+
275
+ echo "==> 自检:渲染双语 HTML 报告"
276
+ python visualization/eduevidence-report/scripts/build_report.py \
277
+ --result examples/ai-coding-assistant/result.json \
278
+ --out /tmp/eduevidence-smoke.html
279
+ rm -f /tmp/eduevidence-smoke.html
280
+
281
+ # 6. 运行测试(仅 dev 模式)
282
+ if [ "$INSTALL_DEV" = "--dev" ]; then
283
+ echo "==> 运行测试"
284
+ pytest -q
285
+ fi
286
+
287
+ # 7. Agent MCP 声明:写入 AGENT_MCP_INSTALLED=1(~/.eduevidence/env)
288
+ write_agent_mcp_env
289
+ }
290
+
291
+ local_finish() {
292
+ echo ""
293
+ echo "安装完成。下一步:"
294
+ echo " 1. 查看示例报告: open examples/ai-coding-assistant/EduEvidence_Report.html"
295
+ echo " 2. 渲染自己的 result.json(需同时准备 result.zh.json 中文平行数据):"
296
+ echo " python visualization/eduevidence-report/scripts/build_report.py \\"
297
+ echo " --result <你的 result.json> --out REPORT.html"
298
+ echo " 3. Agent MCP: 已写入 ${AGENT_MCP_ENV_FILE}(AGENT_MCP_INSTALLED=1),"
299
+ echo " 安装并启动 agent-mcp daemon 后 detect 即报 state: available"
300
+ }
301
+
302
+ # ---------- Skill 安装 ----------
303
+ # 复制 Skill 本体到指定目录(写前自动备份:cp -r 到 .bak-时间戳)
304
+ install_to_dir() {
305
+ local dest="$1"
306
+ if [ "$DRY_RUN" -eq 1 ]; then
307
+ echo " [dry-run] 将备份已有目录: $dest → $dest.bak-<时间戳>(如存在)"
308
+ echo " [dry-run] 将复制: ${SKILL_PAYLOAD[*]} → $dest/"
309
+ return 0
310
+ fi
311
+ if [ -e "$dest" ]; then
312
+ local bak="${dest}.bak-$(date +%Y%m%d%H%M%S)"
313
+ cp -r "$dest" "$bak"
314
+ echo " 已备份: $dest → $bak"
315
+ rm -rf "$dest"
316
+ fi
317
+ mkdir -p "$dest"
318
+ cp -R "${SKILL_PAYLOAD[@]}" "$dest"/
319
+ echo " ✅ 已安装: $dest"
320
+ }
321
+
322
+ # 安装到单个宿主
323
+ install_one_host() {
324
+ local host="$1"
325
+ local dest
326
+ dest="$(host_skill_root "$host")/$SKILL_NAME"
327
+ echo ""
328
+ echo "==> 安装 Skill 到 $host"
329
+ echo " 目标目录: $dest"
330
+ install_to_dir "$dest"
331
+ echo " 提示: 重启 $host 会话后 Skill 即可自动发现(或按宿主机制手动装载)"
332
+ }
333
+
334
+ # custom:手动指定 skill 根目录
335
+ custom_install() {
336
+ local dir=""
337
+ read -r -p "请输入 skill 根目录(将创建 <目录>/$SKILL_NAME/): " dir || true
338
+ dir="${dir%/}"
339
+ dir="${dir/#\~/$HOME}" # 展开 ~
340
+ if [ -z "$dir" ]; then
341
+ echo "ERROR: 未输入目录,取消安装。" >&2
342
+ exit 1
343
+ fi
344
+ echo "==> 安装 Skill 到自定义目录"
345
+ echo " 目标目录: $dir/$SKILL_NAME"
346
+ install_to_dir "$dir/$SKILL_NAME"
347
+ }
348
+
349
+ # 非交互:--host / --dest 指定落点
350
+ skill_install_noninteractive() {
351
+ local choice="$1"
352
+ case "$choice" in
353
+ all)
354
+ for h in "${HOSTS[@]}"; do install_one_host "$h"; done
355
+ print_universal_prompt
356
+ ;;
357
+ local)
358
+ echo "==> 仅本地安装:安装 pytest 并运行测试"
359
+ if [ "$DRY_RUN" -eq 1 ]; then
360
+ echo " [dry-run] 将执行: pip install -e '.[dev]' && pytest -q"
361
+ else
362
+ pip install --quiet -e '.[dev]'
363
+ pytest -q
364
+ fi
365
+ write_agent_mcp_env
366
+ ;;
367
+ custom)
368
+ echo "ERROR: 非交互模式请使用 --dest <skill根目录>" >&2
369
+ exit 1
370
+ ;;
371
+ *)
372
+ local found=0 h
373
+ for h in "${HOSTS[@]}"; do
374
+ [ "$h" = "$choice" ] && found=1
375
+ done
376
+ if [ "$found" -eq 0 ]; then
377
+ echo "ERROR: 无效宿主: $choice(bash install.sh --list-hosts 查看列表)" >&2
378
+ exit 1
379
+ fi
380
+ install_one_host "$choice"
381
+ print_universal_prompt
382
+ ;;
383
+ esac
384
+ }
385
+
386
+ # 交互式选择宿主并安装 Skill
387
+ skill_install() {
388
+ if [ -n "$DEST_ROOT" ]; then
389
+ echo "==> 安装 Skill 到自定义目录"
390
+ echo " 目标目录: $DEST_ROOT/$SKILL_NAME"
391
+ install_to_dir "$DEST_ROOT/$SKILL_NAME"
392
+ print_universal_prompt
393
+ return 0
394
+ fi
395
+ if [ -n "$HOST_CHOICE" ]; then
396
+ echo " 已选择: $HOST_CHOICE"
397
+ skill_install_noninteractive "$HOST_CHOICE"
398
+ return 0
399
+ fi
400
+
401
+ local i=1 choice="" mark=""
402
+ echo ""
403
+ echo "==> 选择安装到哪个宿主 Agent(Skill 落点见: bash install.sh --list-hosts):"
404
+ for h in "${HOSTS[@]}"; do
405
+ if host_detect "$h"; then mark="✓ 已探测到"; else mark="未探测到"; fi
406
+ printf ' %2d) %-9s %s\n' "$i" "$h" "$mark"
407
+ i=$((i + 1))
408
+ done
409
+ printf ' %2d) %-9s 安装到全部 Agent\n' "$i" "all"
410
+ i=$((i + 1))
411
+ printf ' %2d) %-9s 手动指定 skill 目录\n' "$i" "custom"
412
+ i=$((i + 1))
413
+ printf ' %2d) %-9s 只装本地(venv + pytest + 自检)\n' "$i" "local"
414
+
415
+ read -r -p "请输入编号或名称 [claude]: " choice || true
416
+ choice="${choice:-claude}"
417
+
418
+ # 数字编号 → 名称
419
+ if [[ "$choice" =~ ^[0-9]+$ ]]; then
420
+ local idx=$((choice - 1))
421
+ if [ "$idx" -lt "${#HOSTS[@]}" ]; then
422
+ choice="${HOSTS[$idx]}"
423
+ else
424
+ case "$idx" in
425
+ 11) choice="all" ;;
426
+ 12) choice="custom" ;;
427
+ 13) choice="local" ;;
428
+ *) echo "ERROR: 无效编号" >&2; exit 1 ;;
429
+ esac
430
+ fi
431
+ fi
432
+ # 名称校验
433
+ case "$choice" in
434
+ all|local|custom) ;;
435
+ *)
436
+ local found=0 h
437
+ for h in "${HOSTS[@]}"; do
438
+ [ "$h" = "$choice" ] && found=1
439
+ done
440
+ if [ "$found" -eq 0 ]; then
441
+ echo "ERROR: 无效选择: $choice" >&2
442
+ exit 1
443
+ fi
444
+ ;;
445
+ esac
446
+ echo " 已选择: $choice"
447
+
448
+ case "$choice" in
449
+ local)
450
+ echo "==> 仅本地安装:安装 pytest 并运行测试"
451
+ if [ "$DRY_RUN" -eq 1 ]; then
452
+ echo " [dry-run] 将执行: pip install -e '.[dev]' && pytest -q"
453
+ else
454
+ pip install --quiet -e '.[dev]'
455
+ pytest -q
456
+ fi
457
+ write_agent_mcp_env
458
+ ;;
459
+ all)
460
+ for h in "${HOSTS[@]}"; do install_one_host "$h"; done
461
+ print_universal_prompt
462
+ ;;
463
+ custom)
464
+ custom_install
465
+ print_universal_prompt
466
+ ;;
467
+ *)
468
+ install_one_host "$choice"
469
+ print_universal_prompt
470
+ ;;
471
+ esac
472
+ }
473
+
474
+ # ---------- --list-hosts:显示支持列表与落点 ----------
475
+ list_hosts() {
476
+ echo "==> EduEvidence 支持的宿主 Agent 与 Skill 落点"
477
+ printf ' %-10s %-12s %s\n' "宿主" "探测" "Skill 安装落点"
478
+ printf ' %-10s %-12s %s\n' "----" "----" "----------------"
479
+ for h in "${HOSTS[@]}"; do
480
+ if host_detect "$h"; then mark="✓ 已探测到"; else mark="未探测到"; fi
481
+ printf ' %-10s %-12s %s/%s/\n' "$h" "$mark" "$(host_skill_root "$h")" "$SKILL_NAME"
482
+ done
483
+ echo ""
484
+ echo " 选择 all 安装到全部 Agent;custom 手动指定目录;local 只装本地。"
485
+ echo " 未列出的 Agent(方式三):用通用提示词手动安装,详见 README「安装为 Skill」。"
486
+ }
487
+
488
+ # ---------- 主流程 ----------
489
+ case "$MODE" in
490
+ list)
491
+ list_hosts
492
+ ;;
493
+ skill)
494
+ if [ "$SKILL_ONLY" -eq 1 ] || [ -n "$HOST_CHOICE" ] || [ -n "$DEST_ROOT" ]; then
495
+ echo "==> Skill-only 安装(跳过 venv / pytest)"
496
+ write_agent_mcp_env
497
+ else
498
+ local_setup
499
+ fi
500
+ skill_install
501
+ star_prompt
502
+ ;;
503
+ local)
504
+ local_setup
505
+ local_finish
506
+ star_prompt
507
+ ;;
508
+ esac
509
+ echo ""
510
+ echo "完成。"
@@ -0,0 +1 @@
1
+ """EduEvidence integration modules."""