@hunyed15/codecgc 0.1.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 (128) hide show
  1. package/.claude/hooks/route-edit.ps1 +86 -0
  2. package/INSTALLATION.md +550 -0
  3. package/LICENSE +21 -0
  4. package/README.md +171 -0
  5. package/bin/cgc-build.js +4 -0
  6. package/bin/cgc-doctor.js +4 -0
  7. package/bin/cgc-entry.js +4 -0
  8. package/bin/cgc-external-audit.js +4 -0
  9. package/bin/cgc-fix.js +4 -0
  10. package/bin/cgc-history.js +4 -0
  11. package/bin/cgc-install.js +4 -0
  12. package/bin/cgc-lifecycle.js +4 -0
  13. package/bin/cgc-package-audit.js +4 -0
  14. package/bin/cgc-plan.js +4 -0
  15. package/bin/cgc-release-readiness.js +4 -0
  16. package/bin/cgc-review.js +4 -0
  17. package/bin/cgc-route.js +4 -0
  18. package/bin/cgc-status.js +4 -0
  19. package/bin/cgc-test.js +4 -0
  20. package/bin/cgc.js +4 -0
  21. package/bin/codecgc.js +1284 -0
  22. package/codecgc/cgc/SKILL.md +46 -0
  23. package/codecgc/cgc-arch/SKILL.md +61 -0
  24. package/codecgc/cgc-build/SKILL.md +53 -0
  25. package/codecgc/cgc-decide/SKILL.md +55 -0
  26. package/codecgc/cgc-fix/SKILL.md +47 -0
  27. package/codecgc/cgc-learn/SKILL.md +46 -0
  28. package/codecgc/cgc-onboard/SKILL.md +52 -0
  29. package/codecgc/cgc-plan/SKILL.md +48 -0
  30. package/codecgc/cgc-refactor/SKILL.md +46 -0
  31. package/codecgc/cgc-req/SKILL.md +61 -0
  32. package/codecgc/cgc-review/SKILL.md +57 -0
  33. package/codecgc/cgc-roadmap/SKILL.md +55 -0
  34. package/codecgc/cgc-test/SKILL.md +21 -0
  35. package/codecgc/reference/api-cgc-review-libdoc.md +13 -0
  36. package/codecgc/reference/artifact-class-policy.md +81 -0
  37. package/codecgc/reference/build-flow.md +95 -0
  38. package/codecgc/reference/checklist-contract.md +103 -0
  39. package/codecgc/reference/execution-audit.md +121 -0
  40. package/codecgc/reference/execution-model.md +118 -0
  41. package/codecgc/reference/execution-routing.md +130 -0
  42. package/codecgc/reference/executor-contract.md +87 -0
  43. package/codecgc/reference/external-capability-registry.json +104 -0
  44. package/codecgc/reference/fix-flow.md +94 -0
  45. package/codecgc/reference/fixture-governance.md +60 -0
  46. package/codecgc/reference/flow-execution.md +65 -0
  47. package/codecgc/reference/lifecycle-map.md +172 -0
  48. package/codecgc/reference/lifecycle-playbook.md +104 -0
  49. package/codecgc/reference/long-lived-artifacts.md +98 -0
  50. package/codecgc/reference/operation-guide.md +242 -0
  51. package/codecgc/reference/release-maintenance-playbook.md +150 -0
  52. package/codecgc/reference/review-writeback.md +141 -0
  53. package/codecgc/reference/role-model.md +128 -0
  54. package/codecgc/reference/runtime-boundary.md +72 -0
  55. package/codecgc/reference/shared-conventions.md +93 -0
  56. package/codecgc/reference/workflow-scaffold.md +57 -0
  57. package/codexmcp/LICENSE +21 -0
  58. package/codexmcp/README.md +294 -0
  59. package/codexmcp/pyproject.toml +37 -0
  60. package/codexmcp/src/codexmcp/__init__.py +4 -0
  61. package/codexmcp/src/codexmcp/cli.py +12 -0
  62. package/codexmcp/src/codexmcp/server.py +529 -0
  63. package/geminimcp/README.md +258 -0
  64. package/geminimcp/pyproject.toml +15 -0
  65. package/geminimcp/src/geminimcp/__init__.py +4 -0
  66. package/geminimcp/src/geminimcp/cli.py +12 -0
  67. package/geminimcp/src/geminimcp/server.py +465 -0
  68. package/model-routing.yaml +30 -0
  69. package/package.json +90 -0
  70. package/requirements.txt +1 -0
  71. package/scripts/README-codecgc-cli.md +89 -0
  72. package/scripts/audit_codecgc_external_capabilities.py +276 -0
  73. package/scripts/audit_codecgc_historical_audits.py +242 -0
  74. package/scripts/audit_codecgc_lifecycle.py +241 -0
  75. package/scripts/audit_codecgc_package_runtime.py +445 -0
  76. package/scripts/audit_codecgc_release_readiness.py +202 -0
  77. package/scripts/audit_codecgc_review_policy.py +82 -0
  78. package/scripts/audit_codecgc_workflow_history.py +317 -0
  79. package/scripts/build_codecgc_task.py +487 -0
  80. package/scripts/codecgc_artifact_roots.py +40 -0
  81. package/scripts/codecgc_cli.py +843 -0
  82. package/scripts/codecgc_command_surface.py +28 -0
  83. package/scripts/codecgc_console_io.py +45 -0
  84. package/scripts/codecgc_executor_registry.py +54 -0
  85. package/scripts/codecgc_file_evidence.py +349 -0
  86. package/scripts/codecgc_flow_control.py +233 -0
  87. package/scripts/codecgc_governance_dedupe.py +161 -0
  88. package/scripts/codecgc_plan_decision.py +103 -0
  89. package/scripts/codecgc_review_control.py +588 -0
  90. package/scripts/codecgc_roadmap_templates.py +149 -0
  91. package/scripts/codecgc_routing_paths.py +16 -0
  92. package/scripts/codecgc_routing_template.py +135 -0
  93. package/scripts/codecgc_runtime_paths.py +22 -0
  94. package/scripts/codecgc_session_recovery.py +44 -0
  95. package/scripts/codecgc_step_control.py +154 -0
  96. package/scripts/codecgc_workflow_runtime.py +63 -0
  97. package/scripts/codecgc_workflow_templates.py +437 -0
  98. package/scripts/entry_codecgc_workflow.py +3419 -0
  99. package/scripts/exercise_mcp_tools.py +109 -0
  100. package/scripts/expand_codecgc_roadmap.py +664 -0
  101. package/scripts/init_codecgc_roadmap.py +134 -0
  102. package/scripts/init_codecgc_workflow.py +207 -0
  103. package/scripts/install_codecgc.py +938 -0
  104. package/scripts/migrate_demo_workflows_to_fixtures.py +128 -0
  105. package/scripts/normalize_codecgc_audits.py +114 -0
  106. package/scripts/normalize_codecgc_governance_docs.py +79 -0
  107. package/scripts/normalize_codecgc_workflow_docs.py +269 -0
  108. package/scripts/plan_codecgc_workflow.py +970 -0
  109. package/scripts/refresh_codecgc_review_policy.py +223 -0
  110. package/scripts/review_codecgc_workflow.py +88 -0
  111. package/scripts/route_codecgc_workflow.py +671 -0
  112. package/scripts/run_codecgc_build.py +104 -0
  113. package/scripts/run_codecgc_fix.py +104 -0
  114. package/scripts/run_codecgc_flow_step.py +165 -0
  115. package/scripts/run_codecgc_task.py +410 -0
  116. package/scripts/run_codecgc_test.py +105 -0
  117. package/scripts/sync_codecgc_mcp_config.py +41 -0
  118. package/scripts/write_codecgc_architecture.py +78 -0
  119. package/scripts/write_codecgc_decision.py +83 -0
  120. package/scripts/write_codecgc_explore.py +118 -0
  121. package/scripts/write_codecgc_guide.py +141 -0
  122. package/scripts/write_codecgc_learning.py +87 -0
  123. package/scripts/write_codecgc_libdoc.py +140 -0
  124. package/scripts/write_codecgc_refactor.py +78 -0
  125. package/scripts/write_codecgc_requirement.py +78 -0
  126. package/scripts/write_codecgc_review.py +291 -0
  127. package/scripts/write_codecgc_roadmap.py +122 -0
  128. package/scripts/write_codecgc_trick.py +123 -0
@@ -0,0 +1,78 @@
1
+ import argparse
2
+ from pathlib import Path
3
+
4
+ from codecgc_artifact_roots import artifact_output_root
5
+ from codecgc_console_io import configure_utf8_stdio
6
+ from codecgc_console_io import print_json
7
+ from codecgc_governance_dedupe import has_existing_governance_entry
8
+
9
+
10
+ SECTION_HEADING = "## 7. Governance Updates"
11
+
12
+
13
+ def resolve_requirement_path() -> Path:
14
+ return artifact_output_root("product") / "requirements" / "codecgc-core-requirements.md"
15
+
16
+
17
+ def build_parser() -> argparse.ArgumentParser:
18
+ parser = argparse.ArgumentParser(
19
+ description="把一条长期有效的需求治理更新写入 codecgc/requirements/codecgc-core-requirements.md。"
20
+ )
21
+ parser.add_argument("--summary", required=True, help="一行需求更新摘要。")
22
+ parser.add_argument("--note", default="", help="可选的稳定需求说明。")
23
+ parser.add_argument("--source", default="", help="可选的来源产物、工作流或讨论引用。")
24
+ return parser
25
+
26
+
27
+ def ensure_governance_section(text: str) -> str:
28
+ if SECTION_HEADING in text:
29
+ return text
30
+ stripped = text.rstrip()
31
+ suffix = "\n\n" if stripped else ""
32
+ return f"{stripped}{suffix}{SECTION_HEADING}\n\n"
33
+
34
+
35
+ def has_existing_entry(existing: str, summary: str) -> bool:
36
+ return has_existing_governance_entry(existing, summary, field_labels=["Summary", "摘要"])
37
+
38
+
39
+ def append_requirement_update(path: Path, summary: str, note: str, source: str) -> dict[str, str]:
40
+ existing = path.read_text(encoding="utf-8")
41
+ ensured = ensure_governance_section(existing)
42
+ cleaned_summary = summary.strip()
43
+ if has_existing_entry(ensured, cleaned_summary):
44
+ return {
45
+ "path": str(path),
46
+ "summary": cleaned_summary,
47
+ "note": note.strip(),
48
+ "source": source.strip(),
49
+ "created": "false",
50
+ }
51
+
52
+ lines = [f"### {cleaned_summary}", "", f"- 摘要: {cleaned_summary}"]
53
+ if note.strip():
54
+ lines.append(f"- 稳定需求说明: {note.strip()}")
55
+ if source.strip():
56
+ lines.append(f"- 来源: {source.strip()}")
57
+ lines.append("")
58
+
59
+ path.write_text(ensured + "\n".join(lines), encoding="utf-8")
60
+ return {
61
+ "path": str(path),
62
+ "summary": cleaned_summary,
63
+ "note": note.strip(),
64
+ "source": source.strip(),
65
+ "created": "true",
66
+ }
67
+
68
+
69
+ def main() -> int:
70
+ configure_utf8_stdio()
71
+ args = build_parser().parse_args()
72
+ result = append_requirement_update(resolve_requirement_path(), args.summary, args.note, args.source)
73
+ print_json({"success": True, "requirement": result})
74
+ return 0
75
+
76
+
77
+ if __name__ == "__main__":
78
+ raise SystemExit(main())
@@ -0,0 +1,291 @@
1
+ import argparse
2
+ import json
3
+ import sys
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ from codecgc_artifact_roots import flow_root
8
+ from codecgc_artifact_roots import normalize_artifact_class
9
+ from codecgc_console_io import configure_utf8_stdio
10
+ from codecgc_console_io import print_json
11
+ from codecgc_review_control import ACTION_KIND_LABELS
12
+ from codecgc_review_control import FALLBACK_STAGE_LABELS
13
+ from codecgc_review_control import display_policy_reason
14
+ from codecgc_review_control import evaluate_review
15
+ from codecgc_step_control import update_step_status
16
+
17
+
18
+ def build_parser() -> argparse.ArgumentParser:
19
+ parser = argparse.ArgumentParser(
20
+ description="把 CodeCGC 审核结果回写到功能验收或问题修复说明产物中。"
21
+ )
22
+ parser.add_argument(
23
+ "--audit-file",
24
+ required=True,
25
+ help="执行审计 JSON 文件路径。",
26
+ )
27
+ parser.add_argument(
28
+ "--decision",
29
+ choices=["accepted", "changes-requested"],
30
+ required=True,
31
+ help="审核决策。",
32
+ )
33
+ parser.add_argument(
34
+ "--risk",
35
+ action="append",
36
+ default=[],
37
+ help="可选:补充剩余风险说明。",
38
+ )
39
+ parser.add_argument(
40
+ "--next-step",
41
+ default="",
42
+ help="可选:未通过或未完成时的下一步说明。",
43
+ )
44
+ parser.add_argument(
45
+ "--force",
46
+ action="store_true",
47
+ help="允许覆盖已有的非 TODO 审核内容。",
48
+ )
49
+ return parser
50
+
51
+
52
+ def load_json(path: Path) -> dict[str, Any]:
53
+ if not path.exists():
54
+ raise FileNotFoundError(f"未找到审计文件:{path}")
55
+ data = json.loads(path.read_text(encoding="utf-8"))
56
+ if not isinstance(data, dict):
57
+ raise ValueError("审计文件必须包含一个 JSON 对象。")
58
+ return data
59
+
60
+
61
+ def resolve_artifact_path(audit: dict[str, Any]) -> tuple[str, Path]:
62
+ source = audit.get("source")
63
+ if not isinstance(source, dict):
64
+ raise ValueError("审计文件缺少有效的 source 区块。")
65
+
66
+ artifact_type = str(source.get("artifact_type", ""))
67
+ artifact_class = normalize_artifact_class(str(source.get("artifact_class", "product")))
68
+ artifact_slug = str(source.get("artifact_slug", ""))
69
+ if not artifact_type or not artifact_slug:
70
+ raise ValueError("审计 source 缺少 artifact_type 或 artifact_slug。")
71
+
72
+ if artifact_type == "feature":
73
+ base_slug = artifact_slug[11:] if len(artifact_slug) > 11 and artifact_slug[4] == "-" else artifact_slug
74
+ return artifact_type, flow_root("feature", artifact_class) / artifact_slug / f"{base_slug}-acceptance.md"
75
+
76
+ if artifact_type == "issue":
77
+ base_slug = artifact_slug[11:] if len(artifact_slug) > 11 and artifact_slug[4] == "-" else artifact_slug
78
+ return artifact_type, flow_root("issue", artifact_class) / artifact_slug / f"{base_slug}-fix-note.md"
79
+
80
+ raise ValueError(f"不支持的产物类型:{artifact_type}")
81
+
82
+
83
+ def resolve_checklist_path_from_audit(audit: dict[str, Any]) -> Path:
84
+ source = audit.get("source")
85
+ if not isinstance(source, dict):
86
+ raise ValueError("审计文件缺少有效的 source 区块。")
87
+ artifact_file = str(source.get("artifact_file", ""))
88
+ if not artifact_file:
89
+ raise ValueError("审计 source 缺少 artifact_file。")
90
+ return Path(artifact_file)
91
+
92
+
93
+ def resolve_step_number_from_audit(audit: dict[str, Any]) -> int:
94
+ source = audit.get("source")
95
+ if not isinstance(source, dict):
96
+ raise ValueError("审计文件缺少有效的 source 区块。")
97
+ step_number = int(source.get("step_number", 0))
98
+ if step_number < 1:
99
+ raise ValueError("审计 source 缺少有效的 step_number。")
100
+ return step_number
101
+
102
+
103
+ def extract_review_values(audit: dict[str, Any], decision: str, risks: list[str], next_step: str) -> dict[str, str]:
104
+ return evaluate_review(audit, decision, risks, next_step)
105
+
106
+
107
+ def ensure_overwrite_allowed(text: str, force: bool) -> None:
108
+ if force:
109
+ return
110
+ if "TODO" not in text:
111
+ raise ValueError("目标产物已经包含非 TODO 审核内容。如需覆盖,请使用 --force。")
112
+
113
+
114
+ def render_review_decision(value: str) -> str:
115
+ return {
116
+ "accepted": "通过",
117
+ "changes-requested": "需修改",
118
+ }.get(value.strip(), value.strip() or "未知")
119
+
120
+
121
+ def render_action_kind(value: str) -> str:
122
+ cleaned = value.strip()
123
+ return ACTION_KIND_LABELS.get(cleaned, cleaned or "未知")
124
+
125
+
126
+ def render_fallback_stage(value: str) -> str:
127
+ cleaned = value.strip()
128
+ return FALLBACK_STAGE_LABELS.get(cleaned, cleaned or "未知")
129
+
130
+
131
+ def render_feature_acceptance(original: str, values: dict[str, str]) -> str:
132
+ frontmatter, body = split_frontmatter(original)
133
+ title = extract_title(body)
134
+ return "\n".join(
135
+ [
136
+ frontmatter.strip(),
137
+ "",
138
+ title,
139
+ "",
140
+ "## 1. 范围检查",
141
+ "",
142
+ values["scope_check"],
143
+ "",
144
+ "## 2. 执行器检查",
145
+ "",
146
+ values["executor_check"],
147
+ "",
148
+ "## 3. 验证结果",
149
+ "",
150
+ values["verification"],
151
+ "",
152
+ "## 4. 剩余风险",
153
+ "",
154
+ values["remaining_risk"],
155
+ "",
156
+ "## 5. 审核结论",
157
+ "",
158
+ f"- 审核结果: {render_review_decision(str(values['final_decision']))}",
159
+ f"- 审核 task_id: {values['reviewed_task_id'] or '未知'}",
160
+ f"- 审核步骤序号: {values['reviewed_step_number'] or '未知'}",
161
+ f"- 审核动作类型: {render_action_kind(str(values.get('recommended_action_kind', '')))}",
162
+ f"- 审核回退阶段: {render_fallback_stage(str(values.get('fallback_stage', '')))}",
163
+ f"- 审核策略原因: {display_policy_reason(str(values.get('policy_reason', '')))}",
164
+ f"- 下一步: {values['next_step']}",
165
+ "",
166
+ ]
167
+ )
168
+
169
+
170
+ def render_issue_fix_note(original: str, values: dict[str, str]) -> str:
171
+ frontmatter, body = split_frontmatter(original)
172
+ title = extract_title(body)
173
+ return "\n".join(
174
+ [
175
+ frontmatter.strip(),
176
+ "",
177
+ title,
178
+ "",
179
+ "## 1. 已应用修复",
180
+ "",
181
+ values["verification"],
182
+ "",
183
+ "## 2. 验证结果",
184
+ "",
185
+ values["scope_check"],
186
+ "",
187
+ "## 3. 执行器检查",
188
+ "",
189
+ values["executor_check"],
190
+ "",
191
+ "## 4. 剩余风险",
192
+ "",
193
+ values["remaining_risk"],
194
+ "",
195
+ "## 5. 审核结论",
196
+ "",
197
+ f"- 审核结果: {render_review_decision(str(values['final_decision']))}",
198
+ f"- 审核 task_id: {values['reviewed_task_id'] or '未知'}",
199
+ f"- 审核步骤序号: {values['reviewed_step_number'] or '未知'}",
200
+ f"- 审核动作类型: {render_action_kind(str(values.get('recommended_action_kind', '')))}",
201
+ f"- 审核回退阶段: {render_fallback_stage(str(values.get('fallback_stage', '')))}",
202
+ f"- 审核策略原因: {display_policy_reason(str(values.get('policy_reason', '')))}",
203
+ f"- 下一步: {values['next_step']}",
204
+ "",
205
+ ]
206
+ )
207
+
208
+
209
+ def split_frontmatter(text: str) -> tuple[str, str]:
210
+ if not text.startswith("---\n"):
211
+ return "", text
212
+ end = text.find("\n---\n", 4)
213
+ if end == -1:
214
+ return "", text
215
+ return text[: end + 5], text[end + 5 :].lstrip("\n")
216
+
217
+
218
+ def extract_title(body: str) -> str:
219
+ for line in body.splitlines():
220
+ if line.startswith("# "):
221
+ return line
222
+ return "# 审核结果"
223
+
224
+
225
+ def write_review(audit_path: Path, decision: str, risks: list[str], next_step: str, force: bool) -> dict[str, str]:
226
+ audit = load_json(audit_path)
227
+ artifact_type, artifact_path = resolve_artifact_path(audit)
228
+ checklist_path = resolve_checklist_path_from_audit(audit)
229
+ step_number = resolve_step_number_from_audit(audit)
230
+ if not artifact_path.exists():
231
+ raise FileNotFoundError(f"未找到目标产物:{artifact_path}")
232
+
233
+ original = artifact_path.read_text(encoding="utf-8")
234
+ if not force:
235
+ ensure_overwrite_allowed(original, force=False)
236
+
237
+ values = extract_review_values(audit, decision, risks, next_step)
238
+ if artifact_type == "feature":
239
+ rendered = render_feature_acceptance(original, values)
240
+ else:
241
+ rendered = render_issue_fix_note(original, values)
242
+
243
+ artifact_path.write_text(rendered, encoding="utf-8")
244
+ update_step_status(
245
+ checklist_path,
246
+ step_number,
247
+ "done" if values["final_decision"] == "accepted" else "pending",
248
+ )
249
+ return {
250
+ "artifact_type": artifact_type,
251
+ "artifact_path": str(artifact_path),
252
+ "checklist_path": str(checklist_path),
253
+ "step_number": str(step_number),
254
+ "step_status": "done" if values["final_decision"] == "accepted" else "pending",
255
+ }
256
+
257
+
258
+ def main() -> int:
259
+ configure_utf8_stdio()
260
+ parser = build_parser()
261
+ args = parser.parse_args()
262
+
263
+ try:
264
+ result = write_review(
265
+ audit_path=Path(args.audit_file),
266
+ decision=args.decision,
267
+ risks=args.risk,
268
+ next_step=args.next_step,
269
+ force=args.force,
270
+ )
271
+ except Exception as error:
272
+ print_json(
273
+ {
274
+ "success": False,
275
+ "error": str(error),
276
+ },
277
+ file=sys.stderr,
278
+ )
279
+ return 1
280
+
281
+ print_json(
282
+ {
283
+ "success": True,
284
+ **result,
285
+ }
286
+ )
287
+ return 0
288
+
289
+
290
+ if __name__ == "__main__":
291
+ raise SystemExit(main())
@@ -0,0 +1,122 @@
1
+ import argparse
2
+ import re
3
+ from pathlib import Path
4
+
5
+ from codecgc_artifact_roots import artifact_output_root
6
+ from codecgc_console_io import configure_utf8_stdio
7
+ from codecgc_console_io import print_json
8
+ from codecgc_workflow_runtime import run_json_script
9
+
10
+
11
+ def normalize_slug(value: str) -> str:
12
+ normalized = value.strip().lower()
13
+ normalized = re.sub(r"[^a-z0-9]+", "-", normalized)
14
+ normalized = re.sub(r"-{2,}", "-", normalized).strip("-")
15
+ return normalized or "roadmap-item"
16
+
17
+
18
+ def build_parser() -> argparse.ArgumentParser:
19
+ parser = argparse.ArgumentParser(
20
+ description="根据治理请求在 codecgc/roadmap/ 下初始化长期 roadmap 产物。"
21
+ )
22
+ parser.add_argument("--summary", required=True, help="一行 roadmap 摘要。")
23
+ parser.add_argument("--goal", default="", help="可选的 roadmap 目标。")
24
+ parser.add_argument("--source", default="", help="可选的来源产物、工作流或讨论引用。")
25
+ parser.add_argument("--force", action="store_true")
26
+ return parser
27
+
28
+
29
+ def resolve_roadmap_directory(slug: str) -> Path:
30
+ return artifact_output_root("product") / "roadmap" / slug
31
+
32
+
33
+ def append_source_note(path: Path, source: str) -> None:
34
+ if not source.strip() or not path.exists():
35
+ return
36
+ content = path.read_text(encoding="utf-8")
37
+ note = f"\n## 7. 治理来源\n\n- 来源: {source.strip()}\n"
38
+ if "## 7. Governance Source" in content or "## 7. 治理来源" in content or f"- Source: {source.strip()}" in content or f"- 来源: {source.strip()}" in content:
39
+ return
40
+ path.write_text(content.rstrip() + note, encoding="utf-8")
41
+
42
+
43
+ def init_roadmap(summary: str, goal: str, source: str, force: bool) -> dict[str, object]:
44
+ slug = normalize_slug(summary)
45
+ roadmap_dir = resolve_roadmap_directory(slug)
46
+ result = run_json_script(
47
+ "init_codecgc_roadmap.py",
48
+ "--slug",
49
+ slug,
50
+ "--summary",
51
+ summary.strip(),
52
+ "--goal",
53
+ goal.strip() or summary.strip(),
54
+ *([] if not force else ["--force"]),
55
+ )
56
+ if not result.get("success") and roadmap_dir.exists() and not force:
57
+ overview_path = roadmap_dir / "overview.md"
58
+ append_source_note(overview_path, source)
59
+ return {
60
+ "success": True,
61
+ "roadmap": {
62
+ "slug": slug,
63
+ "directory": str(roadmap_dir),
64
+ "files": {
65
+ "overview": str(roadmap_dir / "overview.md"),
66
+ "phases": str(roadmap_dir / "phases.md"),
67
+ "delivery_plan": str(roadmap_dir / "delivery-plan.md"),
68
+ },
69
+ "source": source.strip(),
70
+ "created": "false",
71
+ },
72
+ }
73
+ if not result.get("success"):
74
+ return result
75
+
76
+ files = result.get("files", {}) if isinstance(result.get("files"), dict) else {}
77
+ overview_path = Path(str(files.get("overview", ""))) if files.get("overview") else roadmap_dir / "overview.md"
78
+ append_source_note(overview_path, source)
79
+ return {
80
+ "success": True,
81
+ "roadmap": {
82
+ "slug": slug,
83
+ "directory": result.get("directory", str(roadmap_dir)),
84
+ "files": files,
85
+ "source": source.strip(),
86
+ "created": "true",
87
+ },
88
+ }
89
+
90
+
91
+ def main() -> int:
92
+ configure_utf8_stdio()
93
+ args = build_parser().parse_args()
94
+ slug = normalize_slug(args.summary)
95
+ roadmap_dir = resolve_roadmap_directory(slug)
96
+ if roadmap_dir.exists() and not args.force:
97
+ append_source_note(roadmap_dir / "overview.md", args.source)
98
+ print_json(
99
+ {
100
+ "success": True,
101
+ "roadmap": {
102
+ "slug": slug,
103
+ "directory": str(roadmap_dir),
104
+ "files": {
105
+ "overview": str(roadmap_dir / "overview.md"),
106
+ "phases": str(roadmap_dir / "phases.md"),
107
+ "delivery_plan": str(roadmap_dir / "delivery-plan.md"),
108
+ },
109
+ "source": args.source.strip(),
110
+ "created": "false",
111
+ },
112
+ }
113
+ )
114
+ return 0
115
+
116
+ result = init_roadmap(args.summary, args.goal, args.source, args.force)
117
+ print_json(result)
118
+ return 0 if result.get("success") else 1
119
+
120
+
121
+ if __name__ == "__main__":
122
+ raise SystemExit(main())
@@ -0,0 +1,123 @@
1
+ import argparse
2
+ from pathlib import Path
3
+
4
+ from codecgc_artifact_roots import artifact_output_root
5
+ from codecgc_console_io import configure_utf8_stdio
6
+ from codecgc_console_io import print_json
7
+ from codecgc_governance_dedupe import has_existing_governance_entry
8
+
9
+
10
+ def resolve_trick_path() -> Path:
11
+ return artifact_output_root("product") / "compound" / "codecgc-tricks.md"
12
+
13
+
14
+ def build_parser() -> argparse.ArgumentParser:
15
+ parser = argparse.ArgumentParser(
16
+ description="把一条可复用技巧或技术处方写入 codecgc/compound/codecgc-tricks.md。"
17
+ )
18
+ parser.add_argument("--summary", required=True, help="一行 trick 摘要。")
19
+ parser.add_argument("--kind", choices=["pattern", "library", "technique"], default="technique")
20
+ parser.add_argument("--instruction", default="", help="简短的默认做法或使用建议。")
21
+ parser.add_argument("--source", default="", help="可选的来源产物、工作流或讨论引用。")
22
+ parser.add_argument("--artifact-path", default="", help="已有 trick 文件路径;传入时进入补充完善模式。")
23
+ parser.add_argument("--practice", default="", help="补充默认做法。")
24
+ parser.add_argument("--scope", default="", help="补充适用范围。")
25
+ parser.add_argument("--counterexample", default="", help="补充反例或误用。")
26
+ parser.add_argument("--append-note", default="", help="无法结构化时,附加到 trick 条目的补充说明。")
27
+ return parser
28
+
29
+
30
+ def ensure_document(path: Path) -> None:
31
+ if path.exists():
32
+ return
33
+ path.parent.mkdir(parents=True, exist_ok=True)
34
+ path.write_text(
35
+ "# CodeCGC Tricks\n\n"
36
+ "该文件用于记录可复用的模式、库用法与技术技巧。\n\n"
37
+ "## 条目\n\n",
38
+ encoding="utf-8",
39
+ )
40
+
41
+
42
+ def has_existing_entry(existing: str, summary: str) -> bool:
43
+ return has_existing_governance_entry(existing, summary, field_labels=["摘要"])
44
+
45
+
46
+ def append_trick(path: Path, summary: str, kind: str, instruction: str, source: str) -> dict[str, str]:
47
+ ensure_document(path)
48
+ cleaned_summary = summary.strip()
49
+ existing = path.read_text(encoding="utf-8")
50
+ if has_existing_entry(existing, cleaned_summary):
51
+ return {
52
+ "path": str(path),
53
+ "summary": cleaned_summary,
54
+ "kind": kind.strip(),
55
+ "instruction": instruction.strip(),
56
+ "source": source.strip(),
57
+ "created": "false",
58
+ }
59
+
60
+ lines = [f"### {cleaned_summary}", "", f"- 类型: {kind.strip()}", f"- 摘要: {cleaned_summary}"]
61
+ if instruction.strip():
62
+ lines.append(f"- 默认做法: {instruction.strip()}")
63
+ if source.strip():
64
+ lines.append(f"- 来源: {source.strip()}")
65
+ lines.append("")
66
+
67
+ path.write_text(existing + "\n".join(lines), encoding="utf-8")
68
+ return {
69
+ "path": str(path),
70
+ "summary": cleaned_summary,
71
+ "kind": kind.strip(),
72
+ "instruction": instruction.strip(),
73
+ "source": source.strip(),
74
+ "created": "true",
75
+ }
76
+
77
+
78
+ def update_trick(path: Path, practice: str, scope: str, counterexample: str, append_note: str) -> dict[str, str]:
79
+ if not path.exists():
80
+ raise FileNotFoundError(f"Trick file not found: {path}")
81
+ content = path.read_text(encoding="utf-8")
82
+ lines: list[str] = []
83
+ if practice.strip():
84
+ lines.append(f"- 默认做法补充: {practice.strip()}")
85
+ if scope.strip():
86
+ lines.append(f"- 适用范围: {scope.strip()}")
87
+ if counterexample.strip():
88
+ lines.append(f"- 反例或误用: {counterexample.strip()}")
89
+ if append_note.strip():
90
+ lines.append(f"- 补充说明: {append_note.strip()}")
91
+ if lines:
92
+ content = content.rstrip() + "\n" + "\n".join(lines) + "\n"
93
+ path.write_text(content, encoding="utf-8")
94
+ return {
95
+ "path": str(path),
96
+ "updated": "true",
97
+ "practice": practice.strip(),
98
+ "scope": scope.strip(),
99
+ "counterexample": counterexample.strip(),
100
+ "append_note": append_note.strip(),
101
+ }
102
+
103
+
104
+ def main() -> int:
105
+ configure_utf8_stdio()
106
+ args = build_parser().parse_args()
107
+ if args.artifact_path.strip():
108
+ result = update_trick(
109
+ Path(args.artifact_path),
110
+ args.practice,
111
+ args.scope,
112
+ args.counterexample,
113
+ args.append_note,
114
+ )
115
+ print_json({"success": True, "trick": result})
116
+ return 0
117
+ result = append_trick(resolve_trick_path(), args.summary, args.kind, args.instruction, args.source)
118
+ print_json({"success": True, "trick": result})
119
+ return 0
120
+
121
+
122
+ if __name__ == "__main__":
123
+ raise SystemExit(main())