master-skill 0.10.0 → 0.10.1

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 (85) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.cursor-plugin/plugin.json +1 -1
  4. package/ETHICS.md +23 -17
  5. package/README.md +18 -11
  6. package/README_EN.md +22 -13
  7. package/SKILL.md +5 -5
  8. package/bin/cli.mjs +309 -77
  9. package/gemini-extension.json +1 -1
  10. package/hooks/run-hook.cmd +18 -5
  11. package/hooks/tests/test_run_hook.sh +114 -0
  12. package/hooks/tests/test_run_hook_cmd.sh +94 -0
  13. package/masters/.gitkeep +0 -0
  14. package/package.json +8 -2
  15. package/prebuilt/compare/SKILL.md +5 -5
  16. package/prebuilt/master-ajahn-chah/SKILL.md +13 -11
  17. package/prebuilt/master-ajahn-chah/meta.json +8 -0
  18. package/prebuilt/master-ajahn-chah/references/voice.md +1 -1
  19. package/prebuilt/master-atisha/SKILL.md +13 -11
  20. package/prebuilt/master-atisha/meta.json +8 -0
  21. package/prebuilt/master-atisha/references/voice.md +1 -1
  22. package/prebuilt/master-buddhaghosa/SKILL.md +13 -11
  23. package/prebuilt/master-buddhaghosa/meta.json +8 -0
  24. package/prebuilt/master-buddhaghosa/references/voice.md +1 -1
  25. package/prebuilt/master-fazang/SKILL.md +3 -3
  26. package/prebuilt/master-fazang/meta.json +8 -0
  27. package/prebuilt/master-huineng/SKILL.md +3 -3
  28. package/prebuilt/master-huineng/meta.json +8 -0
  29. package/prebuilt/master-kumarajiva/SKILL.md +3 -3
  30. package/prebuilt/master-kumarajiva/meta.json +8 -0
  31. package/prebuilt/master-mahasi-sayadaw/SKILL.md +13 -11
  32. package/prebuilt/master-mahasi-sayadaw/meta.json +8 -0
  33. package/prebuilt/master-mahasi-sayadaw/references/voice.md +2 -2
  34. package/prebuilt/master-milarepa/SKILL.md +13 -11
  35. package/prebuilt/master-milarepa/meta.json +8 -0
  36. package/prebuilt/master-milarepa/references/voice.md +1 -1
  37. package/prebuilt/master-nagarjuna/SKILL.md +3 -3
  38. package/prebuilt/master-nagarjuna/meta.json +8 -0
  39. package/prebuilt/master-ouyi/SKILL.md +3 -3
  40. package/prebuilt/master-ouyi/meta.json +8 -0
  41. package/prebuilt/master-tsongkhapa/SKILL.md +13 -11
  42. package/prebuilt/master-tsongkhapa/meta.json +8 -0
  43. package/prebuilt/master-tsongkhapa/references/voice.md +1 -1
  44. package/prebuilt/master-xuanzang/SKILL.md +3 -3
  45. package/prebuilt/master-xuanzang/meta.json +8 -0
  46. package/prebuilt/master-xuyun/SKILL.md +3 -3
  47. package/prebuilt/master-xuyun/meta.json +8 -0
  48. package/prebuilt/master-yinguang/SKILL.md +3 -3
  49. package/prebuilt/master-yinguang/meta.json +8 -0
  50. package/prebuilt/master-zhiyi/SKILL.md +3 -3
  51. package/prebuilt/master-zhiyi/meta.json +8 -0
  52. package/prompts/correction_handler.md +104 -0
  53. package/prompts/doctrine_reviewer.md +61 -0
  54. package/prompts/intake.md +62 -0
  55. package/prompts/merger.md +62 -0
  56. package/prompts/rag_instructions.md +54 -0
  57. package/prompts/sutra_analyzer.md +83 -0
  58. package/prompts/teaching_builder.md +41 -0
  59. package/prompts/voice_analyzer.md +92 -0
  60. package/prompts/voice_builder.md +48 -0
  61. package/prompts/voice_reviewer.md +66 -0
  62. package/references/README.md +12 -0
  63. package/references/ethics-runtime.md +112 -0
  64. package/references/fojin-api.md +223 -0
  65. package/references/source-conventions.md +129 -0
  66. package/references/teaching-modes.md +84 -0
  67. package/references/traditions.md +72 -0
  68. package/references/workflow-details.md +361 -0
  69. package/requirements.txt +6 -0
  70. package/scripts/select-fidelity-smoke.py +78 -0
  71. package/scripts/test-fidelity.py +19 -2
  72. package/scripts/tests/test_select_fidelity_smoke.py +142 -0
  73. package/scripts/tests/test_validate_citation_contract.py +408 -0
  74. package/scripts/tests/test_validate_workflow.py +265 -0
  75. package/scripts/validate-citation-contract.py +193 -0
  76. package/scripts/verify_citations.py +8 -1
  77. package/skill-catalog.json +147 -0
  78. package/tools/cross_reference.py +365 -0
  79. package/tools/fojin_bridge.py +146 -0
  80. package/tools/master_builder.py +341 -0
  81. package/tools/rag_query.py +336 -0
  82. package/tools/skill_writer.py +230 -0
  83. package/tools/sutra_collector.py +237 -0
  84. package/tools/verify_sources.py +512 -0
  85. package/tools/version_manager.py +88 -0
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env python3
2
+ """Validate source-neutral citation contracts in persona metadata."""
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import sys
7
+ from pathlib import Path
8
+
9
+
10
+ PREBUILT_DIR = Path(__file__).resolve().parent.parent / "prebuilt"
11
+
12
+ EXPECTED_PERSONA_SLUGS = (
13
+ "ajahn-chah",
14
+ "atisha",
15
+ "buddhaghosa",
16
+ "fazang",
17
+ "huineng",
18
+ "kumarajiva",
19
+ "mahasi-sayadaw",
20
+ "milarepa",
21
+ "nagarjuna",
22
+ "ouyi",
23
+ "tsongkhapa",
24
+ "xuanzang",
25
+ "xuyun",
26
+ "yinguang",
27
+ "zhiyi",
28
+ )
29
+
30
+ EXPECTED_REQUIRED_FOR = [
31
+ "doctrinal_claim",
32
+ "practice_guidance",
33
+ "text_interpretation",
34
+ ]
35
+ EXPECTED_KEYS = {
36
+ "version",
37
+ "claim_policy",
38
+ "required_for",
39
+ "allowed_source_types",
40
+ "minimum_claim_coverage",
41
+ "live_retrieval_allowed",
42
+ }
43
+
44
+
45
+ def _display_path(meta_path: Path) -> str:
46
+ return f"{meta_path.parent.name}/meta.json"
47
+
48
+
49
+ def _persona_count(prebuilt_dir: Path) -> int:
50
+ count = 0
51
+ for meta_path in prebuilt_dir.glob("master-*/meta.json"):
52
+ try:
53
+ data = json.loads(meta_path.read_text(encoding="utf-8"))
54
+ except (OSError, json.JSONDecodeError):
55
+ continue
56
+ if data.get("kind") != "meta-skill":
57
+ count += 1
58
+ return count
59
+
60
+
61
+ def validate(prebuilt_dir: Path) -> list[str]:
62
+ """Return deterministic citation-contract errors for persona metadata."""
63
+ errors: list[str] = []
64
+
65
+ for meta_path in sorted(prebuilt_dir.glob("master-*/meta.json")):
66
+ path = _display_path(meta_path)
67
+ try:
68
+ data = json.loads(meta_path.read_text(encoding="utf-8"))
69
+ except (OSError, json.JSONDecodeError) as exc:
70
+ errors.append(f"{path}: invalid metadata: {exc}")
71
+ continue
72
+
73
+ if data.get("kind") == "meta-skill":
74
+ if "citation_contract" in data:
75
+ errors.append(
76
+ f"{path}: meta-skill must not declare citation_contract"
77
+ )
78
+ continue
79
+
80
+ sources = data.get("sources")
81
+ if not isinstance(sources, list) or not sources:
82
+ errors.append(f"{path}: persona sources must be a non-empty list")
83
+ continue
84
+
85
+ source_types: list[str] = []
86
+ for index, source in enumerate(sources):
87
+ source_type = source.get("type") if isinstance(source, dict) else None
88
+ if not isinstance(source_type, str) or not source_type.strip():
89
+ errors.append(
90
+ f"{path}: sources[].type at index {index} must be a non-empty string"
91
+ )
92
+ continue
93
+ source_types.append(source_type)
94
+
95
+ contract = data.get("citation_contract")
96
+ if not isinstance(contract, dict):
97
+ errors.append(f"{path}: citation_contract must be an object")
98
+ continue
99
+
100
+ contract_keys = set(contract)
101
+ if contract_keys != EXPECTED_KEYS:
102
+ missing = sorted(EXPECTED_KEYS - contract_keys)
103
+ extra = sorted(contract_keys - EXPECTED_KEYS)
104
+ errors.append(
105
+ f"{path}: citation_contract keys must be exact; "
106
+ f"missing={missing}, extra={extra}"
107
+ )
108
+
109
+ version = contract.get("version")
110
+ if type(version) is not int or version != 1:
111
+ errors.append(f"{path}: version must be integer 1")
112
+
113
+ if contract.get("claim_policy") != "declared_sources_only":
114
+ errors.append(
115
+ f"{path}: claim_policy must be 'declared_sources_only'"
116
+ )
117
+
118
+ if contract.get("required_for") != EXPECTED_REQUIRED_FOR:
119
+ errors.append(
120
+ f"{path}: required_for must equal {EXPECTED_REQUIRED_FOR}"
121
+ )
122
+
123
+ allowed_source_types = contract.get("allowed_source_types")
124
+ expected_source_types = sorted(set(source_types))
125
+ if (
126
+ not isinstance(allowed_source_types, list)
127
+ or any(
128
+ not isinstance(source_type, str) or not source_type.strip()
129
+ for source_type in allowed_source_types
130
+ )
131
+ or allowed_source_types != expected_source_types
132
+ ):
133
+ errors.append(
134
+ f"{path}: allowed_source_types must equal {expected_source_types}"
135
+ )
136
+
137
+ coverage = contract.get("minimum_claim_coverage")
138
+ if (
139
+ isinstance(coverage, bool)
140
+ or not isinstance(coverage, (int, float))
141
+ or coverage != 0.9
142
+ ):
143
+ errors.append(f"{path}: minimum_claim_coverage must be numeric 0.9")
144
+
145
+ if contract.get("live_retrieval_allowed") is not True:
146
+ errors.append(f"{path}: live_retrieval_allowed must be true")
147
+
148
+ return errors
149
+
150
+
151
+ def validate_repository(prebuilt_dir: Path) -> list[str]:
152
+ """Validate contracts plus the repository's fixed 15-persona roster."""
153
+ if not prebuilt_dir.is_dir():
154
+ return [f"{prebuilt_dir}: prebuilt directory not found"]
155
+
156
+ errors = validate(prebuilt_dir)
157
+ discovered: set[str] = set()
158
+ for meta_path in sorted(prebuilt_dir.glob("master-*/meta.json")):
159
+ try:
160
+ data = json.loads(meta_path.read_text(encoding="utf-8"))
161
+ except (OSError, json.JSONDecodeError):
162
+ continue
163
+ if data.get("kind") == "meta-skill":
164
+ continue
165
+ discovered.add(meta_path.parent.name.removeprefix("master-"))
166
+
167
+ expected = set(EXPECTED_PERSONA_SLUGS)
168
+ missing = sorted(expected - discovered)
169
+ unexpected = sorted(discovered - expected)
170
+ if missing or unexpected or len(discovered) != len(EXPECTED_PERSONA_SLUGS):
171
+ errors.append(
172
+ "persona roster must contain exactly "
173
+ f"{len(EXPECTED_PERSONA_SLUGS)} declared personas; "
174
+ f"missing={[f'master-{slug}' for slug in missing]}, "
175
+ f"unexpected={[f'master-{slug}' for slug in unexpected]}"
176
+ )
177
+
178
+ return errors
179
+
180
+
181
+ def main() -> int:
182
+ errors = validate_repository(PREBUILT_DIR)
183
+ if errors:
184
+ for error in errors:
185
+ print(error, file=sys.stderr)
186
+ return 1
187
+
188
+ print(f"citation contracts OK ({_persona_count(PREBUILT_DIR)} personas)")
189
+ return 0
190
+
191
+
192
+ if __name__ == "__main__":
193
+ sys.exit(main())
@@ -33,7 +33,14 @@ _SAFE_MASTER = re.compile(r"^[A-Za-z0-9_-]+$")
33
33
 
34
34
  # CBETA id 形态:T48n2008 / T08n0235(藏经卷+n+编号),及 API 返回的 X1218 / X0303
35
35
  # (无卷号)。无 `n` 的形态只认 T/X 两个集合,避免误吞 Wikidata 的 Q1234 / P5008。
36
- _CBETA_ID = re.compile(r"\b(?:[A-Z]{1,2}\d+n\d+|[TX]\d{3,})\b")
36
+ #
37
+ # 边界不能用 \b:Python 的 \w 覆盖 CJK,故「卷一T99n9999」的「一」与「T」之间
38
+ # 没有 \b,整块引文会被 audit_answer 当作无 id 跳过 —— 而格式跑偏正是模型最可能
39
+ # 编造经号的时候。改判「前后不是拉丁字母或数字」:汉字紧邻属真实引文形态,须命中;
40
+ # 拉丁字母紧邻(FakeSutraT99n9999)通常意味着它只是更长 token 的一部分,不算引文。
41
+ _CBETA_ID = re.compile(
42
+ r"(?<![0-9A-Za-z])(?:[A-Z]{1,2}\d+n\d+|[TX]\d{3,})(?![0-9A-Za-z])"
43
+ )
37
44
  # 引文块 【…】
38
45
  _CITATION_BLOCK = re.compile(r"【([^】]*)】")
39
46
  # live 链接 fojin.app/texts/<数字>
@@ -0,0 +1,147 @@
1
+ {
2
+ "version": 1,
3
+ "skills": [
4
+ {
5
+ "name": "master-nagarjuna",
6
+ "kind": "persona",
7
+ "source": "prebuilt/master-nagarjuna",
8
+ "install_dir": "master-nagarjuna",
9
+ "aliases": ["nagarjuna", "master-nagarjuna"]
10
+ },
11
+ {
12
+ "name": "master-xuanzang",
13
+ "kind": "persona",
14
+ "source": "prebuilt/master-xuanzang",
15
+ "install_dir": "master-xuanzang",
16
+ "aliases": ["xuanzang", "master-xuanzang"]
17
+ },
18
+ {
19
+ "name": "master-kumarajiva",
20
+ "kind": "persona",
21
+ "source": "prebuilt/master-kumarajiva",
22
+ "install_dir": "master-kumarajiva",
23
+ "aliases": ["kumarajiva", "master-kumarajiva"]
24
+ },
25
+ {
26
+ "name": "master-huineng",
27
+ "kind": "persona",
28
+ "source": "prebuilt/master-huineng",
29
+ "install_dir": "master-huineng",
30
+ "aliases": ["huineng", "master-huineng"]
31
+ },
32
+ {
33
+ "name": "master-zhiyi",
34
+ "kind": "persona",
35
+ "source": "prebuilt/master-zhiyi",
36
+ "install_dir": "master-zhiyi",
37
+ "aliases": ["zhiyi", "master-zhiyi"]
38
+ },
39
+ {
40
+ "name": "master-fazang",
41
+ "kind": "persona",
42
+ "source": "prebuilt/master-fazang",
43
+ "install_dir": "master-fazang",
44
+ "aliases": ["fazang", "master-fazang"]
45
+ },
46
+ {
47
+ "name": "master-yinguang",
48
+ "kind": "persona",
49
+ "source": "prebuilt/master-yinguang",
50
+ "install_dir": "master-yinguang",
51
+ "aliases": ["yinguang", "master-yinguang"]
52
+ },
53
+ {
54
+ "name": "master-ouyi",
55
+ "kind": "persona",
56
+ "source": "prebuilt/master-ouyi",
57
+ "install_dir": "master-ouyi",
58
+ "aliases": ["ouyi", "master-ouyi"]
59
+ },
60
+ {
61
+ "name": "master-xuyun",
62
+ "kind": "persona",
63
+ "source": "prebuilt/master-xuyun",
64
+ "install_dir": "master-xuyun",
65
+ "aliases": ["xuyun", "master-xuyun"]
66
+ },
67
+ {
68
+ "name": "master-atisha",
69
+ "kind": "persona",
70
+ "source": "prebuilt/master-atisha",
71
+ "install_dir": "master-atisha",
72
+ "aliases": ["atisha", "master-atisha"]
73
+ },
74
+ {
75
+ "name": "master-tsongkhapa",
76
+ "kind": "persona",
77
+ "source": "prebuilt/master-tsongkhapa",
78
+ "install_dir": "master-tsongkhapa",
79
+ "aliases": ["tsongkhapa", "master-tsongkhapa"]
80
+ },
81
+ {
82
+ "name": "master-milarepa",
83
+ "kind": "persona",
84
+ "source": "prebuilt/master-milarepa",
85
+ "install_dir": "master-milarepa",
86
+ "aliases": ["milarepa", "master-milarepa"]
87
+ },
88
+ {
89
+ "name": "master-buddhaghosa",
90
+ "kind": "persona",
91
+ "source": "prebuilt/master-buddhaghosa",
92
+ "install_dir": "master-buddhaghosa",
93
+ "aliases": ["buddhaghosa", "master-buddhaghosa"]
94
+ },
95
+ {
96
+ "name": "master-mahasi-sayadaw",
97
+ "kind": "persona",
98
+ "source": "prebuilt/master-mahasi-sayadaw",
99
+ "install_dir": "master-mahasi-sayadaw",
100
+ "aliases": ["mahasi-sayadaw", "master-mahasi-sayadaw"]
101
+ },
102
+ {
103
+ "name": "master-ajahn-chah",
104
+ "kind": "persona",
105
+ "source": "prebuilt/master-ajahn-chah",
106
+ "install_dir": "master-ajahn-chah",
107
+ "aliases": ["ajahn-chah", "master-ajahn-chah"]
108
+ },
109
+ {
110
+ "name": "compare-masters",
111
+ "kind": "teaching-mode",
112
+ "source": "prebuilt/compare",
113
+ "install_dir": "compare-masters",
114
+ "aliases": ["compare-masters"]
115
+ },
116
+ {
117
+ "name": "master-debate",
118
+ "kind": "teaching-mode",
119
+ "source": "prebuilt/master-debate",
120
+ "install_dir": "master-debate",
121
+ "aliases": ["master-debate"]
122
+ },
123
+ {
124
+ "name": "master-curriculum",
125
+ "kind": "teaching-mode",
126
+ "source": "prebuilt/master-curriculum",
127
+ "install_dir": "master-curriculum",
128
+ "aliases": ["master-curriculum"]
129
+ },
130
+ {
131
+ "name": "create-master",
132
+ "kind": "generator",
133
+ "source": ".",
134
+ "install_dir": "create-master",
135
+ "aliases": ["create-master"],
136
+ "bundle_paths": [
137
+ "SKILL.md",
138
+ "tools",
139
+ "prompts",
140
+ "references",
141
+ "requirements.txt",
142
+ "ETHICS.md",
143
+ "masters"
144
+ ]
145
+ }
146
+ ]
147
+ }