@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.
- package/.claude/hooks/route-edit.ps1 +86 -0
- package/INSTALLATION.md +550 -0
- package/LICENSE +21 -0
- package/README.md +171 -0
- package/bin/cgc-build.js +4 -0
- package/bin/cgc-doctor.js +4 -0
- package/bin/cgc-entry.js +4 -0
- package/bin/cgc-external-audit.js +4 -0
- package/bin/cgc-fix.js +4 -0
- package/bin/cgc-history.js +4 -0
- package/bin/cgc-install.js +4 -0
- package/bin/cgc-lifecycle.js +4 -0
- package/bin/cgc-package-audit.js +4 -0
- package/bin/cgc-plan.js +4 -0
- package/bin/cgc-release-readiness.js +4 -0
- package/bin/cgc-review.js +4 -0
- package/bin/cgc-route.js +4 -0
- package/bin/cgc-status.js +4 -0
- package/bin/cgc-test.js +4 -0
- package/bin/cgc.js +4 -0
- package/bin/codecgc.js +1284 -0
- package/codecgc/cgc/SKILL.md +46 -0
- package/codecgc/cgc-arch/SKILL.md +61 -0
- package/codecgc/cgc-build/SKILL.md +53 -0
- package/codecgc/cgc-decide/SKILL.md +55 -0
- package/codecgc/cgc-fix/SKILL.md +47 -0
- package/codecgc/cgc-learn/SKILL.md +46 -0
- package/codecgc/cgc-onboard/SKILL.md +52 -0
- package/codecgc/cgc-plan/SKILL.md +48 -0
- package/codecgc/cgc-refactor/SKILL.md +46 -0
- package/codecgc/cgc-req/SKILL.md +61 -0
- package/codecgc/cgc-review/SKILL.md +57 -0
- package/codecgc/cgc-roadmap/SKILL.md +55 -0
- package/codecgc/cgc-test/SKILL.md +21 -0
- package/codecgc/reference/api-cgc-review-libdoc.md +13 -0
- package/codecgc/reference/artifact-class-policy.md +81 -0
- package/codecgc/reference/build-flow.md +95 -0
- package/codecgc/reference/checklist-contract.md +103 -0
- package/codecgc/reference/execution-audit.md +121 -0
- package/codecgc/reference/execution-model.md +118 -0
- package/codecgc/reference/execution-routing.md +130 -0
- package/codecgc/reference/executor-contract.md +87 -0
- package/codecgc/reference/external-capability-registry.json +104 -0
- package/codecgc/reference/fix-flow.md +94 -0
- package/codecgc/reference/fixture-governance.md +60 -0
- package/codecgc/reference/flow-execution.md +65 -0
- package/codecgc/reference/lifecycle-map.md +172 -0
- package/codecgc/reference/lifecycle-playbook.md +104 -0
- package/codecgc/reference/long-lived-artifacts.md +98 -0
- package/codecgc/reference/operation-guide.md +242 -0
- package/codecgc/reference/release-maintenance-playbook.md +150 -0
- package/codecgc/reference/review-writeback.md +141 -0
- package/codecgc/reference/role-model.md +128 -0
- package/codecgc/reference/runtime-boundary.md +72 -0
- package/codecgc/reference/shared-conventions.md +93 -0
- package/codecgc/reference/workflow-scaffold.md +57 -0
- package/codexmcp/LICENSE +21 -0
- package/codexmcp/README.md +294 -0
- package/codexmcp/pyproject.toml +37 -0
- package/codexmcp/src/codexmcp/__init__.py +4 -0
- package/codexmcp/src/codexmcp/cli.py +12 -0
- package/codexmcp/src/codexmcp/server.py +529 -0
- package/geminimcp/README.md +258 -0
- package/geminimcp/pyproject.toml +15 -0
- package/geminimcp/src/geminimcp/__init__.py +4 -0
- package/geminimcp/src/geminimcp/cli.py +12 -0
- package/geminimcp/src/geminimcp/server.py +465 -0
- package/model-routing.yaml +30 -0
- package/package.json +90 -0
- package/requirements.txt +1 -0
- package/scripts/README-codecgc-cli.md +89 -0
- package/scripts/audit_codecgc_external_capabilities.py +276 -0
- package/scripts/audit_codecgc_historical_audits.py +242 -0
- package/scripts/audit_codecgc_lifecycle.py +241 -0
- package/scripts/audit_codecgc_package_runtime.py +445 -0
- package/scripts/audit_codecgc_release_readiness.py +202 -0
- package/scripts/audit_codecgc_review_policy.py +82 -0
- package/scripts/audit_codecgc_workflow_history.py +317 -0
- package/scripts/build_codecgc_task.py +487 -0
- package/scripts/codecgc_artifact_roots.py +40 -0
- package/scripts/codecgc_cli.py +843 -0
- package/scripts/codecgc_command_surface.py +28 -0
- package/scripts/codecgc_console_io.py +45 -0
- package/scripts/codecgc_executor_registry.py +54 -0
- package/scripts/codecgc_file_evidence.py +349 -0
- package/scripts/codecgc_flow_control.py +233 -0
- package/scripts/codecgc_governance_dedupe.py +161 -0
- package/scripts/codecgc_plan_decision.py +103 -0
- package/scripts/codecgc_review_control.py +588 -0
- package/scripts/codecgc_roadmap_templates.py +149 -0
- package/scripts/codecgc_routing_paths.py +16 -0
- package/scripts/codecgc_routing_template.py +135 -0
- package/scripts/codecgc_runtime_paths.py +22 -0
- package/scripts/codecgc_session_recovery.py +44 -0
- package/scripts/codecgc_step_control.py +154 -0
- package/scripts/codecgc_workflow_runtime.py +63 -0
- package/scripts/codecgc_workflow_templates.py +437 -0
- package/scripts/entry_codecgc_workflow.py +3419 -0
- package/scripts/exercise_mcp_tools.py +109 -0
- package/scripts/expand_codecgc_roadmap.py +664 -0
- package/scripts/init_codecgc_roadmap.py +134 -0
- package/scripts/init_codecgc_workflow.py +207 -0
- package/scripts/install_codecgc.py +938 -0
- package/scripts/migrate_demo_workflows_to_fixtures.py +128 -0
- package/scripts/normalize_codecgc_audits.py +114 -0
- package/scripts/normalize_codecgc_governance_docs.py +79 -0
- package/scripts/normalize_codecgc_workflow_docs.py +269 -0
- package/scripts/plan_codecgc_workflow.py +970 -0
- package/scripts/refresh_codecgc_review_policy.py +223 -0
- package/scripts/review_codecgc_workflow.py +88 -0
- package/scripts/route_codecgc_workflow.py +671 -0
- package/scripts/run_codecgc_build.py +104 -0
- package/scripts/run_codecgc_fix.py +104 -0
- package/scripts/run_codecgc_flow_step.py +165 -0
- package/scripts/run_codecgc_task.py +410 -0
- package/scripts/run_codecgc_test.py +105 -0
- package/scripts/sync_codecgc_mcp_config.py +41 -0
- package/scripts/write_codecgc_architecture.py +78 -0
- package/scripts/write_codecgc_decision.py +83 -0
- package/scripts/write_codecgc_explore.py +118 -0
- package/scripts/write_codecgc_guide.py +141 -0
- package/scripts/write_codecgc_learning.py +87 -0
- package/scripts/write_codecgc_libdoc.py +140 -0
- package/scripts/write_codecgc_refactor.py +78 -0
- package/scripts/write_codecgc_requirement.py +78 -0
- package/scripts/write_codecgc_review.py +291 -0
- package/scripts/write_codecgc_roadmap.py +122 -0
- package/scripts/write_codecgc_trick.py +123 -0
|
@@ -0,0 +1,83 @@
|
|
|
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_decision_path() -> Path:
|
|
11
|
+
return artifact_output_root("product") / "compound" / "codecgc-decisions.md"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
15
|
+
parser = argparse.ArgumentParser(
|
|
16
|
+
description="把一条长期有效的已确认决定写入 codecgc/compound/codecgc-decisions.md。"
|
|
17
|
+
)
|
|
18
|
+
parser.add_argument("--summary", required=True, help="一行决定摘要。")
|
|
19
|
+
parser.add_argument("--constraint", default="", help="这条决定会约束哪些后续行为。")
|
|
20
|
+
parser.add_argument("--source", default="", help="可选的来源产物、工作流或讨论引用。")
|
|
21
|
+
return parser
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def ensure_parent(path: Path) -> None:
|
|
25
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def ensure_document(path: Path) -> None:
|
|
29
|
+
if path.exists():
|
|
30
|
+
return
|
|
31
|
+
ensure_parent(path)
|
|
32
|
+
path.write_text(
|
|
33
|
+
"# CodeCGC 长期决策\n\n"
|
|
34
|
+
"该文件用于记录当前仓库的长期有效决定。\n\n"
|
|
35
|
+
"## 条目\n\n",
|
|
36
|
+
encoding="utf-8",
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def has_existing_entry(existing: str, summary: str) -> bool:
|
|
41
|
+
return has_existing_governance_entry(existing, summary, field_labels=["Decision", "决定"])
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def append_decision(path: Path, summary: str, constraint: str, source: str) -> dict[str, str]:
|
|
45
|
+
ensure_document(path)
|
|
46
|
+
cleaned_summary = summary.strip()
|
|
47
|
+
existing = path.read_text(encoding="utf-8")
|
|
48
|
+
if has_existing_entry(existing, cleaned_summary):
|
|
49
|
+
return {
|
|
50
|
+
"path": str(path),
|
|
51
|
+
"summary": cleaned_summary,
|
|
52
|
+
"constraint": constraint.strip(),
|
|
53
|
+
"source": source.strip(),
|
|
54
|
+
"created": "false",
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
lines = [f"### {cleaned_summary}", "", f"- 决定: {cleaned_summary}"]
|
|
58
|
+
if constraint.strip():
|
|
59
|
+
lines.append(f"- 约束: {constraint.strip()}")
|
|
60
|
+
if source.strip():
|
|
61
|
+
lines.append(f"- 来源: {source.strip()}")
|
|
62
|
+
lines.append("")
|
|
63
|
+
|
|
64
|
+
path.write_text(existing + "\n".join(lines), encoding="utf-8")
|
|
65
|
+
return {
|
|
66
|
+
"path": str(path),
|
|
67
|
+
"summary": cleaned_summary,
|
|
68
|
+
"constraint": constraint.strip(),
|
|
69
|
+
"source": source.strip(),
|
|
70
|
+
"created": "true",
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def main() -> int:
|
|
75
|
+
configure_utf8_stdio()
|
|
76
|
+
args = build_parser().parse_args()
|
|
77
|
+
result = append_decision(resolve_decision_path(), args.summary, args.constraint, args.source)
|
|
78
|
+
print_json({"success": True, "decision": result})
|
|
79
|
+
return 0
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
if __name__ == "__main__":
|
|
83
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,118 @@
|
|
|
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_explore_path() -> Path:
|
|
11
|
+
return artifact_output_root("product") / "compound" / "codecgc-explorations.md"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
15
|
+
parser = argparse.ArgumentParser(
|
|
16
|
+
description="把一条定向代码探索请求写入 codecgc/compound/codecgc-explorations.md。"
|
|
17
|
+
)
|
|
18
|
+
parser.add_argument("--summary", required=True, help="一行 explore 摘要。")
|
|
19
|
+
parser.add_argument("--kind", choices=["question", "module-overview", "spike"], default="question")
|
|
20
|
+
parser.add_argument("--source", default="", help="可选的来源产物、工作流或讨论引用。")
|
|
21
|
+
parser.add_argument("--artifact-path", default="", help="已有 explore 文件路径;传入时进入补充完善模式。")
|
|
22
|
+
parser.add_argument("--question-detail", default="", help="补充核心问题。")
|
|
23
|
+
parser.add_argument("--target-modules", default="", help="补充目标模块、目录或文件。")
|
|
24
|
+
parser.add_argument("--expected-output", default="", help="补充预期输出形式。")
|
|
25
|
+
parser.add_argument("--append-note", default="", help="无法结构化时,附加到 explore 条目的补充说明。")
|
|
26
|
+
return parser
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def ensure_document(path: Path) -> None:
|
|
30
|
+
if path.exists():
|
|
31
|
+
return
|
|
32
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
33
|
+
path.write_text(
|
|
34
|
+
"# CodeCGC Explorations\n\n"
|
|
35
|
+
"该文件用于记录待做或已登记的定向代码探索请求。\n\n"
|
|
36
|
+
"## 条目\n\n",
|
|
37
|
+
encoding="utf-8",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def has_existing_entry(existing: str, summary: str) -> bool:
|
|
42
|
+
return has_existing_governance_entry(existing, summary, field_labels=["摘要"])
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def append_exploration(path: Path, summary: str, kind: str, source: str) -> dict[str, str]:
|
|
46
|
+
ensure_document(path)
|
|
47
|
+
cleaned_summary = summary.strip()
|
|
48
|
+
existing = path.read_text(encoding="utf-8")
|
|
49
|
+
if has_existing_entry(existing, cleaned_summary):
|
|
50
|
+
return {
|
|
51
|
+
"path": str(path),
|
|
52
|
+
"summary": cleaned_summary,
|
|
53
|
+
"kind": kind.strip(),
|
|
54
|
+
"source": source.strip(),
|
|
55
|
+
"created": "false",
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
lines = [f"### {cleaned_summary}", "", f"- 类型: {kind.strip()}", f"- 摘要: {cleaned_summary}"]
|
|
59
|
+
if source.strip():
|
|
60
|
+
lines.append(f"- 来源: {source.strip()}")
|
|
61
|
+
lines.append("")
|
|
62
|
+
|
|
63
|
+
path.write_text(existing + "\n".join(lines), encoding="utf-8")
|
|
64
|
+
return {
|
|
65
|
+
"path": str(path),
|
|
66
|
+
"summary": cleaned_summary,
|
|
67
|
+
"kind": kind.strip(),
|
|
68
|
+
"source": source.strip(),
|
|
69
|
+
"created": "true",
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def update_exploration(path: Path, question_detail: str, target_modules: str, expected_output: str, append_note: str) -> dict[str, str]:
|
|
74
|
+
if not path.exists():
|
|
75
|
+
raise FileNotFoundError(f"Explore file not found: {path}")
|
|
76
|
+
content = path.read_text(encoding="utf-8")
|
|
77
|
+
lines: list[str] = []
|
|
78
|
+
if question_detail.strip():
|
|
79
|
+
lines.append(f"- 核心问题: {question_detail.strip()}")
|
|
80
|
+
if target_modules.strip():
|
|
81
|
+
lines.append(f"- 目标模块: {target_modules.strip()}")
|
|
82
|
+
if expected_output.strip():
|
|
83
|
+
lines.append(f"- 预期输出: {expected_output.strip()}")
|
|
84
|
+
if append_note.strip():
|
|
85
|
+
lines.append(f"- 补充说明: {append_note.strip()}")
|
|
86
|
+
if lines:
|
|
87
|
+
content = content.rstrip() + "\n" + "\n".join(lines) + "\n"
|
|
88
|
+
path.write_text(content, encoding="utf-8")
|
|
89
|
+
return {
|
|
90
|
+
"path": str(path),
|
|
91
|
+
"updated": "true",
|
|
92
|
+
"question_detail": question_detail.strip(),
|
|
93
|
+
"target_modules": target_modules.strip(),
|
|
94
|
+
"expected_output": expected_output.strip(),
|
|
95
|
+
"append_note": append_note.strip(),
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def main() -> int:
|
|
100
|
+
configure_utf8_stdio()
|
|
101
|
+
args = build_parser().parse_args()
|
|
102
|
+
if args.artifact_path.strip():
|
|
103
|
+
result = update_exploration(
|
|
104
|
+
Path(args.artifact_path),
|
|
105
|
+
args.question_detail,
|
|
106
|
+
args.target_modules,
|
|
107
|
+
args.expected_output,
|
|
108
|
+
args.append_note,
|
|
109
|
+
)
|
|
110
|
+
print_json({"success": True, "explore": result})
|
|
111
|
+
return 0
|
|
112
|
+
result = append_exploration(resolve_explore_path(), args.summary, args.kind, args.source)
|
|
113
|
+
print_json({"success": True, "explore": result})
|
|
114
|
+
return 0
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
if __name__ == "__main__":
|
|
118
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,141 @@
|
|
|
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
|
+
|
|
9
|
+
|
|
10
|
+
def slugify(value: str) -> str:
|
|
11
|
+
normalized = value.strip().lower()
|
|
12
|
+
normalized = re.sub(r"[^a-z0-9]+", "-", normalized)
|
|
13
|
+
normalized = re.sub(r"-{2,}", "-", normalized).strip("-")
|
|
14
|
+
return normalized or "guide"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def resolve_docs_root() -> Path:
|
|
18
|
+
return artifact_output_root("product") / "docs"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
22
|
+
parser = argparse.ArgumentParser(
|
|
23
|
+
description="把一条面向用户或开发者的指南请求写入 codecgc/docs/ 下的 guide 文档。"
|
|
24
|
+
)
|
|
25
|
+
parser.add_argument("--summary", required=True, help="一行 guide 摘要。")
|
|
26
|
+
parser.add_argument("--audience", choices=["user", "developer"], default="developer")
|
|
27
|
+
parser.add_argument("--note", default="", help="可选的指南用途说明。")
|
|
28
|
+
parser.add_argument("--source", default="", help="可选的来源产物、工作流或讨论引用。")
|
|
29
|
+
parser.add_argument("--artifact-path", default="", help="已有 guide 文件路径;传入时进入补充完善模式。")
|
|
30
|
+
parser.add_argument("--purpose", default="", help="补充 guide 的目的说明。")
|
|
31
|
+
parser.add_argument("--steps", default="", help="补充 guide 的使用步骤。")
|
|
32
|
+
parser.add_argument("--boundary", default="", help="补充 guide 的边界或前置条件。")
|
|
33
|
+
parser.add_argument("--append-note", default="", help="无法结构化时,附加到 guide 的补充说明。")
|
|
34
|
+
return parser
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def ensure_parent(path: Path) -> None:
|
|
38
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def write_guide(path: Path, summary: str, audience: str, note: str, source: str) -> dict[str, str]:
|
|
42
|
+
ensure_parent(path)
|
|
43
|
+
created = not path.exists()
|
|
44
|
+
if created:
|
|
45
|
+
title = summary.strip()
|
|
46
|
+
lines = [
|
|
47
|
+
f"# {title}",
|
|
48
|
+
"",
|
|
49
|
+
f"- Audience: {'开发者' if audience == 'developer' else '用户'}",
|
|
50
|
+
f"- 摘要: {title}",
|
|
51
|
+
]
|
|
52
|
+
if note.strip():
|
|
53
|
+
lines.append(f"- 用途: {note.strip()}")
|
|
54
|
+
if source.strip():
|
|
55
|
+
lines.append(f"- 来源: {source.strip()}")
|
|
56
|
+
lines.extend(
|
|
57
|
+
[
|
|
58
|
+
"",
|
|
59
|
+
"## 1. 目的",
|
|
60
|
+
"",
|
|
61
|
+
"待补充。",
|
|
62
|
+
"",
|
|
63
|
+
"## 2. 使用步骤",
|
|
64
|
+
"",
|
|
65
|
+
"待补充。",
|
|
66
|
+
"",
|
|
67
|
+
]
|
|
68
|
+
)
|
|
69
|
+
path.write_text("\n".join(lines), encoding="utf-8")
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
"path": str(path),
|
|
73
|
+
"summary": summary.strip(),
|
|
74
|
+
"audience": audience,
|
|
75
|
+
"note": note.strip(),
|
|
76
|
+
"source": source.strip(),
|
|
77
|
+
"created": "true" if created else "false",
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def replace_section(text: str, heading: str, body: str) -> str:
|
|
82
|
+
pattern = rf"({re.escape(heading)}\n\n)(.*?)(?=\n## |\Z)"
|
|
83
|
+
match = re.search(pattern, text, flags=re.DOTALL)
|
|
84
|
+
if match:
|
|
85
|
+
return text[:match.start()] + f"{heading}\n\n{body.strip()}\n" + text[match.end():]
|
|
86
|
+
stripped = text.rstrip()
|
|
87
|
+
suffix = "\n\n" if stripped else ""
|
|
88
|
+
return f"{stripped}{suffix}{heading}\n\n{body.strip()}\n"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def update_guide(path: Path, purpose: str, steps: str, boundary: str, append_note: str) -> dict[str, str]:
|
|
92
|
+
if not path.exists():
|
|
93
|
+
raise FileNotFoundError(f"Guide file not found: {path}")
|
|
94
|
+
content = path.read_text(encoding="utf-8")
|
|
95
|
+
if purpose.strip():
|
|
96
|
+
content = replace_section(content, "## 1. 目的", purpose.strip())
|
|
97
|
+
if steps.strip():
|
|
98
|
+
content = replace_section(content, "## 2. 使用步骤", steps.strip())
|
|
99
|
+
if boundary.strip():
|
|
100
|
+
content = replace_section(content, "## 3. 使用边界", boundary.strip())
|
|
101
|
+
elif "## 3. 使用边界" not in content and append_note.strip():
|
|
102
|
+
content = replace_section(content, "## 3. 使用边界", append_note.strip())
|
|
103
|
+
append_note = ""
|
|
104
|
+
if append_note.strip():
|
|
105
|
+
note_block = f"## 4. 补充说明\n\n{append_note.strip()}\n"
|
|
106
|
+
if "## 4. 补充说明" in content:
|
|
107
|
+
content = replace_section(content, "## 4. 补充说明", append_note.strip())
|
|
108
|
+
else:
|
|
109
|
+
content = content.rstrip() + "\n\n" + note_block
|
|
110
|
+
path.write_text(content, encoding="utf-8")
|
|
111
|
+
return {
|
|
112
|
+
"path": str(path),
|
|
113
|
+
"updated": "true",
|
|
114
|
+
"purpose": purpose.strip(),
|
|
115
|
+
"steps": steps.strip(),
|
|
116
|
+
"boundary": boundary.strip(),
|
|
117
|
+
"append_note": append_note.strip(),
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def main() -> int:
|
|
122
|
+
configure_utf8_stdio()
|
|
123
|
+
args = build_parser().parse_args()
|
|
124
|
+
if args.artifact_path.strip():
|
|
125
|
+
result = update_guide(
|
|
126
|
+
Path(args.artifact_path),
|
|
127
|
+
args.purpose,
|
|
128
|
+
args.steps,
|
|
129
|
+
args.boundary,
|
|
130
|
+
args.append_note,
|
|
131
|
+
)
|
|
132
|
+
print_json({"success": True, "guide": result})
|
|
133
|
+
return 0
|
|
134
|
+
filename = f"{slugify(args.summary)}-guide.md"
|
|
135
|
+
result = write_guide(resolve_docs_root() / filename, args.summary, args.audience, args.note, args.source)
|
|
136
|
+
print_json({"success": True, "guide": result})
|
|
137
|
+
return 0
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
if __name__ == "__main__":
|
|
141
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,87 @@
|
|
|
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_learning_path() -> Path:
|
|
11
|
+
return artifact_output_root("product") / "compound" / "codecgc-learning-log.md"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
15
|
+
parser = argparse.ArgumentParser(
|
|
16
|
+
description="把一条可复用经验写入 codecgc/compound/codecgc-learning-log.md。"
|
|
17
|
+
)
|
|
18
|
+
parser.add_argument("--summary", required=True, help="一行经验摘要。")
|
|
19
|
+
parser.add_argument("--kind", choices=["pitfall", "practice"], default="practice")
|
|
20
|
+
parser.add_argument("--instruction", default="", help="简短的可复用后续指引。")
|
|
21
|
+
parser.add_argument("--source", default="", help="可选的来源产物、工作流或讨论引用。")
|
|
22
|
+
return parser
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def ensure_parent(path: Path) -> None:
|
|
26
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def ensure_document(path: Path) -> None:
|
|
30
|
+
if path.exists():
|
|
31
|
+
return
|
|
32
|
+
ensure_parent(path)
|
|
33
|
+
path.write_text(
|
|
34
|
+
"# CodeCGC 经验沉淀\n\n"
|
|
35
|
+
"该文件用于记录当前仓库可复用的经验、坑点和推荐做法。\n\n"
|
|
36
|
+
"## 条目\n\n",
|
|
37
|
+
encoding="utf-8",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def has_existing_entry(existing: str, summary: str) -> bool:
|
|
42
|
+
return has_existing_governance_entry(existing, summary, field_labels=["Summary", "摘要"])
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def append_learning(path: Path, summary: str, kind: str, instruction: str, source: str) -> dict[str, str]:
|
|
46
|
+
ensure_document(path)
|
|
47
|
+
cleaned_summary = summary.strip()
|
|
48
|
+
existing = path.read_text(encoding="utf-8")
|
|
49
|
+
if has_existing_entry(existing, cleaned_summary):
|
|
50
|
+
return {
|
|
51
|
+
"path": str(path),
|
|
52
|
+
"summary": cleaned_summary,
|
|
53
|
+
"kind": kind.strip(),
|
|
54
|
+
"instruction": instruction.strip(),
|
|
55
|
+
"source": source.strip(),
|
|
56
|
+
"created": "false",
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
kind_label = "坑点" if kind.strip() == "pitfall" else "实践"
|
|
60
|
+
lines = [f"### {cleaned_summary}", "", f"- 类型: {kind_label}", 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 main() -> int:
|
|
79
|
+
configure_utf8_stdio()
|
|
80
|
+
args = build_parser().parse_args()
|
|
81
|
+
result = append_learning(resolve_learning_path(), args.summary, args.kind, args.instruction, args.source)
|
|
82
|
+
print_json({"success": True, "learning": result})
|
|
83
|
+
return 0
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
if __name__ == "__main__":
|
|
87
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,140 @@
|
|
|
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
|
+
|
|
9
|
+
|
|
10
|
+
def slugify(value: str) -> str:
|
|
11
|
+
normalized = value.strip().lower()
|
|
12
|
+
normalized = re.sub(r"[^a-z0-9]+", "-", normalized)
|
|
13
|
+
normalized = re.sub(r"-{2,}", "-", normalized).strip("-")
|
|
14
|
+
return normalized or "libdoc"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def resolve_reference_root() -> Path:
|
|
18
|
+
return artifact_output_root("product") / "reference"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
22
|
+
parser = argparse.ArgumentParser(
|
|
23
|
+
description="把一条 API/组件/命令参考文档请求写入 codecgc/reference/ 下的 libdoc 文档。"
|
|
24
|
+
)
|
|
25
|
+
parser.add_argument("--summary", required=True, help="一行 libdoc 摘要。")
|
|
26
|
+
parser.add_argument("--surface", default="public-api", help="可选的公开表面类型说明。")
|
|
27
|
+
parser.add_argument("--source", default="", help="可选的来源产物、工作流或讨论引用。")
|
|
28
|
+
parser.add_argument("--artifact-path", default="", help="已有 libdoc 文件路径;传入时进入补充完善模式。")
|
|
29
|
+
parser.add_argument("--entry", default="", help="补充入口说明。")
|
|
30
|
+
parser.add_argument("--contract", default="", help="补充公开契约说明。")
|
|
31
|
+
parser.add_argument("--example", default="", help="补充最小示例说明。")
|
|
32
|
+
parser.add_argument("--boundary", default="", help="补充边界说明。")
|
|
33
|
+
parser.add_argument("--append-note", default="", help="无法结构化时,附加到 libdoc 的补充说明。")
|
|
34
|
+
return parser
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def ensure_parent(path: Path) -> None:
|
|
38
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def write_libdoc(path: Path, summary: str, surface: str, source: str) -> dict[str, str]:
|
|
42
|
+
ensure_parent(path)
|
|
43
|
+
created = not path.exists()
|
|
44
|
+
if created:
|
|
45
|
+
title = summary.strip()
|
|
46
|
+
lines = [
|
|
47
|
+
f"# {title}",
|
|
48
|
+
"",
|
|
49
|
+
f"- 表面类型: {surface.strip() or 'public-api'}",
|
|
50
|
+
f"- 摘要: {title}",
|
|
51
|
+
]
|
|
52
|
+
if source.strip():
|
|
53
|
+
lines.append(f"- 来源: {source.strip()}")
|
|
54
|
+
lines.extend(
|
|
55
|
+
[
|
|
56
|
+
"",
|
|
57
|
+
"## 1. 入口",
|
|
58
|
+
"",
|
|
59
|
+
"待补充。",
|
|
60
|
+
"",
|
|
61
|
+
"## 2. 公开契约",
|
|
62
|
+
"",
|
|
63
|
+
"待补充。",
|
|
64
|
+
"",
|
|
65
|
+
]
|
|
66
|
+
)
|
|
67
|
+
path.write_text("\n".join(lines), encoding="utf-8")
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
"path": str(path),
|
|
71
|
+
"summary": summary.strip(),
|
|
72
|
+
"surface": surface.strip() or "public-api",
|
|
73
|
+
"source": source.strip(),
|
|
74
|
+
"created": "true" if created else "false",
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def replace_section(text: str, heading: str, body: str) -> str:
|
|
79
|
+
pattern = rf"({re.escape(heading)}\n\n)(.*?)(?=\n## |\Z)"
|
|
80
|
+
match = re.search(pattern, text, flags=re.DOTALL)
|
|
81
|
+
if match:
|
|
82
|
+
return text[:match.start()] + f"{heading}\n\n{body.strip()}\n" + text[match.end():]
|
|
83
|
+
stripped = text.rstrip()
|
|
84
|
+
suffix = "\n\n" if stripped else ""
|
|
85
|
+
return f"{stripped}{suffix}{heading}\n\n{body.strip()}\n"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def update_libdoc(path: Path, entry: str, contract: str, example: str, boundary: str, append_note: str) -> dict[str, str]:
|
|
89
|
+
if not path.exists():
|
|
90
|
+
raise FileNotFoundError(f"Libdoc file not found: {path}")
|
|
91
|
+
content = path.read_text(encoding="utf-8")
|
|
92
|
+
if entry.strip():
|
|
93
|
+
content = replace_section(content, "## 1. 入口", entry.strip())
|
|
94
|
+
if contract.strip():
|
|
95
|
+
content = replace_section(content, "## 2. 公开契约", contract.strip())
|
|
96
|
+
combined = "\n\n".join(part for part in [example.strip(), boundary.strip()] if part)
|
|
97
|
+
if combined:
|
|
98
|
+
content = replace_section(content, "## 3. 示例与边界", combined)
|
|
99
|
+
elif "## 3. 示例与边界" not in content and append_note.strip():
|
|
100
|
+
content = replace_section(content, "## 3. 示例与边界", append_note.strip())
|
|
101
|
+
append_note = ""
|
|
102
|
+
if append_note.strip():
|
|
103
|
+
if "## 4. 补充说明" in content:
|
|
104
|
+
content = replace_section(content, "## 4. 补充说明", append_note.strip())
|
|
105
|
+
else:
|
|
106
|
+
content = content.rstrip() + f"\n\n## 4. 补充说明\n\n{append_note.strip()}\n"
|
|
107
|
+
path.write_text(content, encoding="utf-8")
|
|
108
|
+
return {
|
|
109
|
+
"path": str(path),
|
|
110
|
+
"updated": "true",
|
|
111
|
+
"entry": entry.strip(),
|
|
112
|
+
"contract": contract.strip(),
|
|
113
|
+
"example": example.strip(),
|
|
114
|
+
"boundary": boundary.strip(),
|
|
115
|
+
"append_note": append_note.strip(),
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def main() -> int:
|
|
120
|
+
configure_utf8_stdio()
|
|
121
|
+
args = build_parser().parse_args()
|
|
122
|
+
if args.artifact_path.strip():
|
|
123
|
+
result = update_libdoc(
|
|
124
|
+
Path(args.artifact_path),
|
|
125
|
+
args.entry,
|
|
126
|
+
args.contract,
|
|
127
|
+
args.example,
|
|
128
|
+
args.boundary,
|
|
129
|
+
args.append_note,
|
|
130
|
+
)
|
|
131
|
+
print_json({"success": True, "libdoc": result})
|
|
132
|
+
return 0
|
|
133
|
+
filename = f"{slugify(args.summary)}-libdoc.md"
|
|
134
|
+
result = write_libdoc(resolve_reference_root() / filename, args.summary, args.surface, args.source)
|
|
135
|
+
print_json({"success": True, "libdoc": result})
|
|
136
|
+
return 0
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
if __name__ == "__main__":
|
|
140
|
+
raise SystemExit(main())
|
|
@@ -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. Refactor Queue"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def resolve_refactor_path() -> Path:
|
|
14
|
+
return artifact_output_root("product") / "compound" / "codecgc-productization-gap.md"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
18
|
+
parser = argparse.ArgumentParser(
|
|
19
|
+
description="把一条保持行为不变的长期重构候选写入 codecgc/compound/codecgc-productization-gap.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_refactor_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_refactor_candidate(path: Path, summary: str, note: str, source: str) -> dict[str, str]:
|
|
40
|
+
existing = path.read_text(encoding="utf-8")
|
|
41
|
+
ensured = ensure_refactor_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_refactor_candidate(resolve_refactor_path(), args.summary, args.note, args.source)
|
|
73
|
+
print_json({"success": True, "refactor": result})
|
|
74
|
+
return 0
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
raise SystemExit(main())
|