@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,241 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import json
|
|
3
|
+
import re
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from codecgc_artifact_roots import FIXTURE_ROOT
|
|
8
|
+
from codecgc_artifact_roots import PRODUCT_ROOT
|
|
9
|
+
from codecgc_console_io import render_summary_block
|
|
10
|
+
|
|
11
|
+
WORKSPACE = Path(__file__).resolve().parents[1]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def count_dirs(path: Path) -> int:
|
|
15
|
+
if not path.exists():
|
|
16
|
+
return 0
|
|
17
|
+
return sum(1 for item in path.iterdir() if item.is_dir())
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def count_files(path: Path, pattern: str) -> int:
|
|
21
|
+
if not path.exists():
|
|
22
|
+
return 0
|
|
23
|
+
return sum(1 for _ in path.rglob(pattern))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def collect_root_stats(root: Path) -> dict[str, int]:
|
|
27
|
+
return {
|
|
28
|
+
"features": count_dirs(root / "features"),
|
|
29
|
+
"issues": count_dirs(root / "issues"),
|
|
30
|
+
"execution_audits": count_files(root / "execution", "*.json"),
|
|
31
|
+
"roadmaps": count_dirs(root / "roadmap"),
|
|
32
|
+
"requirements": count_files(root / "requirements", "*.md"),
|
|
33
|
+
"architecture": count_files(root / "architecture", "*.md"),
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def count_child_workflow_lines(path: Path) -> int:
|
|
38
|
+
if not path.exists():
|
|
39
|
+
return 0
|
|
40
|
+
content = path.read_text(encoding="utf-8")
|
|
41
|
+
patterns = [
|
|
42
|
+
r"(?m)^- (?:Frontend|Backend) 子工作流:",
|
|
43
|
+
r"(?m)^- (?:Frontend|Backend) child:",
|
|
44
|
+
]
|
|
45
|
+
total = 0
|
|
46
|
+
for pattern in patterns:
|
|
47
|
+
total += len(re.findall(pattern, content))
|
|
48
|
+
return total
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def collect_roadmap_tracking(root: Path) -> dict[str, Any]:
|
|
52
|
+
roadmap_root = root / "roadmap"
|
|
53
|
+
roadmaps: list[dict[str, Any]] = []
|
|
54
|
+
if not roadmap_root.exists():
|
|
55
|
+
return {
|
|
56
|
+
"roadmap_count": 0,
|
|
57
|
+
"tracked_roadmap_count": 0,
|
|
58
|
+
"child_workflow_count": 0,
|
|
59
|
+
"unexpanded_roadmaps": [],
|
|
60
|
+
"roadmaps": [],
|
|
61
|
+
"child_tracking_ready": True,
|
|
62
|
+
"roadmap_coverage": "empty",
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for item in sorted(path for path in roadmap_root.iterdir() if path.is_dir()):
|
|
66
|
+
phases = item / "phases.md"
|
|
67
|
+
delivery = item / "delivery-plan.md"
|
|
68
|
+
child_count = count_child_workflow_lines(phases) + count_child_workflow_lines(delivery)
|
|
69
|
+
tracked = child_count > 0
|
|
70
|
+
roadmaps.append(
|
|
71
|
+
{
|
|
72
|
+
"initiative": item.name,
|
|
73
|
+
"tracked": tracked,
|
|
74
|
+
"child_workflow_count": child_count,
|
|
75
|
+
"phases_exists": phases.exists(),
|
|
76
|
+
"delivery_plan_exists": delivery.exists(),
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
roadmap_count = len(roadmaps)
|
|
81
|
+
tracked_roadmap_count = sum(1 for item in roadmaps if item["tracked"])
|
|
82
|
+
child_workflow_count = sum(int(item["child_workflow_count"]) for item in roadmaps)
|
|
83
|
+
unexpanded_roadmaps = [item["initiative"] for item in roadmaps if not item["tracked"]]
|
|
84
|
+
child_tracking_ready = roadmap_count == 0 or tracked_roadmap_count == roadmap_count
|
|
85
|
+
if roadmap_count == 0:
|
|
86
|
+
roadmap_coverage = "empty"
|
|
87
|
+
elif child_tracking_ready:
|
|
88
|
+
roadmap_coverage = "tracked"
|
|
89
|
+
elif tracked_roadmap_count == 0:
|
|
90
|
+
roadmap_coverage = "unexpanded"
|
|
91
|
+
else:
|
|
92
|
+
roadmap_coverage = "partial"
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
"roadmap_count": roadmap_count,
|
|
96
|
+
"tracked_roadmap_count": tracked_roadmap_count,
|
|
97
|
+
"child_workflow_count": child_workflow_count,
|
|
98
|
+
"unexpanded_roadmaps": unexpanded_roadmaps,
|
|
99
|
+
"roadmaps": roadmaps,
|
|
100
|
+
"child_tracking_ready": child_tracking_ready,
|
|
101
|
+
"roadmap_coverage": roadmap_coverage,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def classify_maturity(product_stats: dict[str, int]) -> dict[str, str]:
|
|
106
|
+
feature_count = int(product_stats.get("features", 0))
|
|
107
|
+
issue_count = int(product_stats.get("issues", 0))
|
|
108
|
+
roadmap_count = int(product_stats.get("roadmaps", 0))
|
|
109
|
+
execution_count = int(product_stats.get("execution_audits", 0))
|
|
110
|
+
|
|
111
|
+
if roadmap_count > 0 and feature_count == 0 and issue_count == 0:
|
|
112
|
+
return {
|
|
113
|
+
"stage": "initiative-planning",
|
|
114
|
+
"human_summary": "当前仓库已经进入 roadmap 规划期,但还没有明显进入批量执行阶段。",
|
|
115
|
+
"next_action": "优先检查 roadmap 子项是否已拆成具体 feature 或 issue workflow。",
|
|
116
|
+
}
|
|
117
|
+
if feature_count + issue_count == 0:
|
|
118
|
+
return {
|
|
119
|
+
"stage": "setup-only",
|
|
120
|
+
"human_summary": "当前仓库已具备 CodeCGC 产品壳,但还没有明显的业务工作流沉淀。",
|
|
121
|
+
"next_action": "优先从 cgc 开始一个新需求,或先用 cgc-plan 建立第一条 workflow。",
|
|
122
|
+
}
|
|
123
|
+
if execution_count == 0:
|
|
124
|
+
return {
|
|
125
|
+
"stage": "planned-not-executed",
|
|
126
|
+
"human_summary": "当前仓库已有 feature / issue 规划,但执行审计还不多,主要处于规划后待执行阶段。",
|
|
127
|
+
"next_action": "优先检查最近 workflow 是否已经进入 cgc-build / cgc-fix。",
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
"stage": "active-delivery",
|
|
131
|
+
"human_summary": "当前仓库已经进入活跃交付阶段,roadmap、workflow 与 execution 审计都已有沉淀。",
|
|
132
|
+
"next_action": "优先结合 cgc-route 与 cgc-release-readiness 判断当前是继续交付、收尾审核还是进入维护周期。",
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def audit_lifecycle() -> dict[str, Any]:
|
|
137
|
+
product_stats = collect_root_stats(PRODUCT_ROOT)
|
|
138
|
+
fixture_stats = collect_root_stats(FIXTURE_ROOT)
|
|
139
|
+
product_roadmap_tracking = collect_roadmap_tracking(PRODUCT_ROOT)
|
|
140
|
+
fixture_roadmap_tracking = collect_roadmap_tracking(FIXTURE_ROOT)
|
|
141
|
+
maturity = classify_maturity(product_stats)
|
|
142
|
+
lifecycle_assets = [
|
|
143
|
+
"codecgc/reference/lifecycle-map.md",
|
|
144
|
+
"codecgc/reference/lifecycle-playbook.md",
|
|
145
|
+
"codecgc/reference/release-maintenance-playbook.md",
|
|
146
|
+
"codecgc/compound/codecgc-operating-model.md",
|
|
147
|
+
"codecgc/compound/codecgc-capability-matrix.md",
|
|
148
|
+
"codecgc/roadmap/README.md",
|
|
149
|
+
]
|
|
150
|
+
missing_assets = [item for item in lifecycle_assets if not (WORKSPACE / item).exists()]
|
|
151
|
+
|
|
152
|
+
ready = len(missing_assets) == 0
|
|
153
|
+
human_summary = maturity["human_summary"]
|
|
154
|
+
if not ready:
|
|
155
|
+
human_summary = "生命周期资产还不完整,当前还不适合把 lifecycle 作为稳定操作面。"
|
|
156
|
+
|
|
157
|
+
recommended_next_action = maturity["next_action"]
|
|
158
|
+
if missing_assets:
|
|
159
|
+
recommended_next_action = "先补齐生命周期参考资产,再继续使用 lifecycle 总览作为稳定入口。"
|
|
160
|
+
elif product_roadmap_tracking["roadmap_count"] > 0 and not product_roadmap_tracking["child_tracking_ready"]:
|
|
161
|
+
recommended_next_action = "优先把还未扩成 child workflow 的 roadmap initiative 继续拆解,再进入批量执行。"
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
"success": ready,
|
|
165
|
+
"mode": "lifecycle-audit",
|
|
166
|
+
"workspace": str(WORKSPACE),
|
|
167
|
+
"summary": {
|
|
168
|
+
"ready": ready,
|
|
169
|
+
"scope": "roadmap / feature / issue / execution 的生命周期总览",
|
|
170
|
+
"human_summary": human_summary,
|
|
171
|
+
"stage": maturity["stage"],
|
|
172
|
+
"recommended_next_action": recommended_next_action,
|
|
173
|
+
"roadmap_coverage": product_roadmap_tracking["roadmap_coverage"],
|
|
174
|
+
"child_tracking_ready": product_roadmap_tracking["child_tracking_ready"],
|
|
175
|
+
},
|
|
176
|
+
"product_stats": product_stats,
|
|
177
|
+
"fixture_stats": fixture_stats,
|
|
178
|
+
"product_roadmap_tracking": product_roadmap_tracking,
|
|
179
|
+
"fixture_roadmap_tracking": fixture_roadmap_tracking,
|
|
180
|
+
"missing_assets": missing_assets,
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def build_summary(result: dict[str, Any]) -> str:
|
|
185
|
+
summary = result.get("summary", {}) if isinstance(result.get("summary"), dict) else {}
|
|
186
|
+
product_stats = result.get("product_stats", {}) if isinstance(result.get("product_stats"), dict) else {}
|
|
187
|
+
fixture_stats = result.get("fixture_stats", {}) if isinstance(result.get("fixture_stats"), dict) else {}
|
|
188
|
+
product_roadmap_tracking = result.get("product_roadmap_tracking", {}) if isinstance(result.get("product_roadmap_tracking"), dict) else {}
|
|
189
|
+
fixture_roadmap_tracking = result.get("fixture_roadmap_tracking", {}) if isinstance(result.get("fixture_roadmap_tracking"), dict) else {}
|
|
190
|
+
lines = [
|
|
191
|
+
f"- 工作区: {result.get('workspace', '')}",
|
|
192
|
+
f"- 范围: {summary.get('scope', '')}",
|
|
193
|
+
f"- 就绪: {'是' if summary.get('ready') else '否'}",
|
|
194
|
+
f"- 生命周期阶段: {summary.get('stage', '')}",
|
|
195
|
+
f"- 摘要: {summary.get('human_summary', '')}",
|
|
196
|
+
f"- Product features: {product_stats.get('features', 0)}",
|
|
197
|
+
f"- Product issues: {product_stats.get('issues', 0)}",
|
|
198
|
+
f"- Product audits: {product_stats.get('execution_audits', 0)}",
|
|
199
|
+
f"- Product roadmaps: {product_stats.get('roadmaps', 0)}",
|
|
200
|
+
f"- Product roadmap coverage: {summary.get('roadmap_coverage', '')}",
|
|
201
|
+
f"- Product child tracking ready: {'是' if summary.get('child_tracking_ready') else '否'}",
|
|
202
|
+
f"- Product tracked roadmaps: {product_roadmap_tracking.get('tracked_roadmap_count', 0)}",
|
|
203
|
+
f"- Product child workflows: {product_roadmap_tracking.get('child_workflow_count', 0)}",
|
|
204
|
+
f"- Fixture features: {fixture_stats.get('features', 0)}",
|
|
205
|
+
f"- Fixture issues: {fixture_stats.get('issues', 0)}",
|
|
206
|
+
f"- Fixture audits: {fixture_stats.get('execution_audits', 0)}",
|
|
207
|
+
f"- Fixture roadmaps: {fixture_stats.get('roadmaps', 0)}",
|
|
208
|
+
f"- Fixture tracked roadmaps: {fixture_roadmap_tracking.get('tracked_roadmap_count', 0)}",
|
|
209
|
+
f"- Fixture child workflows: {fixture_roadmap_tracking.get('child_workflow_count', 0)}",
|
|
210
|
+
]
|
|
211
|
+
for item in product_roadmap_tracking.get("unexpanded_roadmaps", []):
|
|
212
|
+
lines.append(f"- 未扩出的 product roadmap: {item}")
|
|
213
|
+
for item in fixture_roadmap_tracking.get("unexpanded_roadmaps", []):
|
|
214
|
+
lines.append(f"- 未扩出的 fixture roadmap: {item}")
|
|
215
|
+
for item in result.get("missing_assets", []):
|
|
216
|
+
lines.append(f"- 缺少生命周期资产: {item}")
|
|
217
|
+
next_action = str(summary.get("recommended_next_action", "")).strip()
|
|
218
|
+
next_actions = [next_action] if next_action else []
|
|
219
|
+
return render_summary_block("CodeCGC Lifecycle", lines, next_actions)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def main() -> int:
|
|
223
|
+
parser = argparse.ArgumentParser(description="Audit CodeCGC lifecycle coverage across roadmap, workflows, and execution artifacts.")
|
|
224
|
+
parser.add_argument(
|
|
225
|
+
"--format",
|
|
226
|
+
choices=["json", "summary"],
|
|
227
|
+
default="summary",
|
|
228
|
+
help="Output format. Summary is the default product-facing mode; use json for debugging or automation.",
|
|
229
|
+
)
|
|
230
|
+
args = parser.parse_args()
|
|
231
|
+
|
|
232
|
+
result = audit_lifecycle()
|
|
233
|
+
if args.format == "summary":
|
|
234
|
+
print(build_summary(result))
|
|
235
|
+
else:
|
|
236
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
237
|
+
return 0 if result.get("success") else 1
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
if __name__ == "__main__":
|
|
241
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import ast
|
|
3
|
+
import json
|
|
4
|
+
import subprocess
|
|
5
|
+
from fnmatch import fnmatch
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from codecgc_console_io import render_summary_block
|
|
10
|
+
|
|
11
|
+
WORKSPACE = Path(__file__).resolve().parents[1]
|
|
12
|
+
PACKAGE_JSON_PATH = WORKSPACE / "package.json"
|
|
13
|
+
|
|
14
|
+
RUNTIME_ENTRYPOINTS = [
|
|
15
|
+
"bin/codecgc.js",
|
|
16
|
+
"scripts/install_codecgc.py",
|
|
17
|
+
"scripts/codecgc_cli.py",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
RUNTIME_STATIC_REQUIREMENTS = [
|
|
21
|
+
".claude/hooks/route-edit.ps1",
|
|
22
|
+
"model-routing.yaml",
|
|
23
|
+
"requirements.txt",
|
|
24
|
+
"scripts/audit_codecgc_external_capabilities.py",
|
|
25
|
+
"scripts/audit_codecgc_lifecycle.py",
|
|
26
|
+
"codexmcp/pyproject.toml",
|
|
27
|
+
"codexmcp/src/codexmcp/__init__.py",
|
|
28
|
+
"codexmcp/src/codexmcp/cli.py",
|
|
29
|
+
"codexmcp/src/codexmcp/server.py",
|
|
30
|
+
"geminimcp/pyproject.toml",
|
|
31
|
+
"geminimcp/src/geminimcp/__init__.py",
|
|
32
|
+
"geminimcp/src/geminimcp/cli.py",
|
|
33
|
+
"geminimcp/src/geminimcp/server.py",
|
|
34
|
+
"scripts/audit_codecgc_release_readiness.py",
|
|
35
|
+
"scripts/write_codecgc_guide.py",
|
|
36
|
+
"scripts/write_codecgc_libdoc.py",
|
|
37
|
+
"scripts/write_codecgc_trick.py",
|
|
38
|
+
"scripts/write_codecgc_explore.py",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
DOC_RUNTIME_PATHS = [
|
|
42
|
+
"codecgc/cgc/SKILL.md",
|
|
43
|
+
"codecgc/cgc-build/SKILL.md",
|
|
44
|
+
"codecgc/cgc-fix/SKILL.md",
|
|
45
|
+
"codecgc/cgc-onboard/SKILL.md",
|
|
46
|
+
"codecgc/cgc-plan/SKILL.md",
|
|
47
|
+
"codecgc/cgc-review/SKILL.md",
|
|
48
|
+
"codecgc/reference/external-capability-registry.json",
|
|
49
|
+
"codecgc/reference/lifecycle-playbook.md",
|
|
50
|
+
"codecgc/reference/operation-guide.md",
|
|
51
|
+
"codecgc/reference/release-maintenance-playbook.md",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
PLACEHOLDER_METADATA_MARKERS = (
|
|
55
|
+
"your-org",
|
|
56
|
+
"example.com",
|
|
57
|
+
"todo",
|
|
58
|
+
"placeholder",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def normalize_path_text(path: str) -> str:
|
|
63
|
+
return str(path).replace("\\", "/").strip()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def load_package_manifest() -> dict[str, Any]:
|
|
67
|
+
return json.loads(PACKAGE_JSON_PATH.read_text(encoding="utf-8"))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def contains_placeholder_marker(value: str) -> bool:
|
|
71
|
+
normalized = normalize_path_text(value).lower()
|
|
72
|
+
return any(marker in normalized for marker in PLACEHOLDER_METADATA_MARKERS)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def audit_manifest_metadata(manifest: dict[str, Any]) -> list[dict[str, str]]:
|
|
76
|
+
issues: list[dict[str, str]] = []
|
|
77
|
+
|
|
78
|
+
homepage = str(manifest.get("homepage", "")).strip()
|
|
79
|
+
if not homepage:
|
|
80
|
+
issues.append(
|
|
81
|
+
{
|
|
82
|
+
"field": "homepage",
|
|
83
|
+
"problem": "missing",
|
|
84
|
+
"detail": "Package homepage is missing.",
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
elif contains_placeholder_marker(homepage):
|
|
88
|
+
issues.append(
|
|
89
|
+
{
|
|
90
|
+
"field": "homepage",
|
|
91
|
+
"problem": "placeholder",
|
|
92
|
+
"detail": homepage,
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
bugs = manifest.get("bugs", {})
|
|
97
|
+
bugs_url = str(bugs.get("url", "")).strip() if isinstance(bugs, dict) else ""
|
|
98
|
+
if not bugs_url:
|
|
99
|
+
issues.append(
|
|
100
|
+
{
|
|
101
|
+
"field": "bugs.url",
|
|
102
|
+
"problem": "missing",
|
|
103
|
+
"detail": "Package bugs.url is missing.",
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
elif contains_placeholder_marker(bugs_url):
|
|
107
|
+
issues.append(
|
|
108
|
+
{
|
|
109
|
+
"field": "bugs.url",
|
|
110
|
+
"problem": "placeholder",
|
|
111
|
+
"detail": bugs_url,
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
repository = manifest.get("repository", {})
|
|
116
|
+
repository_url = str(repository.get("url", "")).strip() if isinstance(repository, dict) else ""
|
|
117
|
+
if not repository_url:
|
|
118
|
+
issues.append(
|
|
119
|
+
{
|
|
120
|
+
"field": "repository.url",
|
|
121
|
+
"problem": "missing",
|
|
122
|
+
"detail": "Package repository.url is missing.",
|
|
123
|
+
}
|
|
124
|
+
)
|
|
125
|
+
elif contains_placeholder_marker(repository_url):
|
|
126
|
+
issues.append(
|
|
127
|
+
{
|
|
128
|
+
"field": "repository.url",
|
|
129
|
+
"problem": "placeholder",
|
|
130
|
+
"detail": repository_url,
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
return issues
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def path_matches_package_files(path_text: str, file_rules: list[str]) -> bool:
|
|
138
|
+
normalized = normalize_path_text(path_text)
|
|
139
|
+
for rule in file_rules:
|
|
140
|
+
normalized_rule = normalize_path_text(rule)
|
|
141
|
+
if normalized_rule.endswith("/"):
|
|
142
|
+
if normalized.startswith(normalized_rule):
|
|
143
|
+
return True
|
|
144
|
+
continue
|
|
145
|
+
if "*" in normalized_rule or "?" in normalized_rule:
|
|
146
|
+
if fnmatch(normalized, normalized_rule):
|
|
147
|
+
return True
|
|
148
|
+
continue
|
|
149
|
+
if normalized == normalized_rule:
|
|
150
|
+
return True
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def resolve_local_python_module(module_name: str) -> str:
|
|
155
|
+
relative = normalize_path_text(module_name.replace(".", "/") + ".py")
|
|
156
|
+
script_candidate = f"scripts/{relative.split('/')[-1]}"
|
|
157
|
+
if (WORKSPACE / script_candidate).exists():
|
|
158
|
+
return script_candidate
|
|
159
|
+
if (WORKSPACE / relative).exists():
|
|
160
|
+
return relative
|
|
161
|
+
return ""
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def parse_local_python_dependencies(relative_path: str) -> tuple[list[str], list[str]]:
|
|
165
|
+
path = WORKSPACE / relative_path
|
|
166
|
+
tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
|
|
167
|
+
imports: list[str] = []
|
|
168
|
+
missing_modules: list[str] = []
|
|
169
|
+
|
|
170
|
+
for node in ast.walk(tree):
|
|
171
|
+
if isinstance(node, ast.Import):
|
|
172
|
+
for alias in node.names:
|
|
173
|
+
resolved = resolve_local_python_module(alias.name)
|
|
174
|
+
if resolved:
|
|
175
|
+
imports.append(resolved)
|
|
176
|
+
elif isinstance(node, ast.ImportFrom):
|
|
177
|
+
if node.level != 0 or not node.module:
|
|
178
|
+
continue
|
|
179
|
+
resolved = resolve_local_python_module(node.module)
|
|
180
|
+
if resolved:
|
|
181
|
+
imports.append(resolved)
|
|
182
|
+
continue
|
|
183
|
+
if node.module.startswith("codexmcp.") or node.module.startswith("geminimcp."):
|
|
184
|
+
resolved_package = normalize_path_text(node.module.replace(".", "/") + ".py")
|
|
185
|
+
if (WORKSPACE / resolved_package).exists():
|
|
186
|
+
imports.append(resolved_package)
|
|
187
|
+
else:
|
|
188
|
+
missing_modules.append(node.module)
|
|
189
|
+
|
|
190
|
+
unique_imports = sorted(set(imports))
|
|
191
|
+
unique_missing = sorted(set(missing_modules))
|
|
192
|
+
return unique_imports, unique_missing
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def collect_python_runtime_graph(entrypoints: list[str]) -> dict[str, Any]:
|
|
196
|
+
queue = list(entrypoints)
|
|
197
|
+
visited: set[str] = set()
|
|
198
|
+
graph: dict[str, list[str]] = {}
|
|
199
|
+
missing_modules: dict[str, list[str]] = {}
|
|
200
|
+
|
|
201
|
+
while queue:
|
|
202
|
+
current = normalize_path_text(queue.pop(0))
|
|
203
|
+
if current in visited:
|
|
204
|
+
continue
|
|
205
|
+
visited.add(current)
|
|
206
|
+
|
|
207
|
+
if not (WORKSPACE / current).exists():
|
|
208
|
+
graph[current] = []
|
|
209
|
+
continue
|
|
210
|
+
|
|
211
|
+
if not current.endswith(".py"):
|
|
212
|
+
graph[current] = []
|
|
213
|
+
continue
|
|
214
|
+
|
|
215
|
+
imports, unresolved = parse_local_python_dependencies(current)
|
|
216
|
+
graph[current] = imports
|
|
217
|
+
if unresolved:
|
|
218
|
+
missing_modules[current] = unresolved
|
|
219
|
+
for item in imports:
|
|
220
|
+
if item not in visited:
|
|
221
|
+
queue.append(item)
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
"required_python_files": sorted(path for path in visited if path.endswith(".py")),
|
|
225
|
+
"import_graph": graph,
|
|
226
|
+
"unresolved_local_modules": missing_modules,
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def build_runtime_requirement_set() -> dict[str, list[str]]:
|
|
231
|
+
python_graph = collect_python_runtime_graph([item for item in RUNTIME_ENTRYPOINTS if item.endswith(".py")])
|
|
232
|
+
required = sorted(
|
|
233
|
+
set(RUNTIME_ENTRYPOINTS)
|
|
234
|
+
| set(RUNTIME_STATIC_REQUIREMENTS)
|
|
235
|
+
| set(DOC_RUNTIME_PATHS)
|
|
236
|
+
| set(python_graph["required_python_files"])
|
|
237
|
+
)
|
|
238
|
+
return {
|
|
239
|
+
"required_paths": required,
|
|
240
|
+
"required_python_files": python_graph["required_python_files"],
|
|
241
|
+
"import_graph": python_graph["import_graph"],
|
|
242
|
+
"unresolved_local_modules": python_graph["unresolved_local_modules"],
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def audit_package_runtime() -> dict[str, Any]:
|
|
247
|
+
manifest = load_package_manifest()
|
|
248
|
+
file_rules = [str(item) for item in manifest.get("files", []) if isinstance(item, str)]
|
|
249
|
+
runtime = build_runtime_requirement_set()
|
|
250
|
+
metadata_issues = audit_manifest_metadata(manifest)
|
|
251
|
+
review_policy_refresh_audit = run_review_policy_refresh_audit()
|
|
252
|
+
historical_audit = run_historical_audit()
|
|
253
|
+
|
|
254
|
+
missing_from_package = [
|
|
255
|
+
path for path in runtime["required_paths"]
|
|
256
|
+
if not path_matches_package_files(path, file_rules)
|
|
257
|
+
]
|
|
258
|
+
|
|
259
|
+
refresh_candidates = int(review_policy_refresh_audit.get("missing_count", 0) or 0)
|
|
260
|
+
refresh_failed = int(review_policy_refresh_audit.get("failed_count", 0) or 0)
|
|
261
|
+
historical_issue_count = int(historical_audit.get("issue_count", 0) or 0)
|
|
262
|
+
|
|
263
|
+
ready = (
|
|
264
|
+
not missing_from_package
|
|
265
|
+
and not runtime["unresolved_local_modules"]
|
|
266
|
+
and not metadata_issues
|
|
267
|
+
and refresh_candidates == 0
|
|
268
|
+
and refresh_failed == 0
|
|
269
|
+
and historical_issue_count == 0
|
|
270
|
+
)
|
|
271
|
+
human_summary = "发布包运行时覆盖检查通过。"
|
|
272
|
+
if not ready:
|
|
273
|
+
if metadata_issues and not missing_from_package and not runtime["unresolved_local_modules"] and refresh_candidates == 0:
|
|
274
|
+
human_summary = "发布元数据检查发现占位值或缺失项。"
|
|
275
|
+
elif historical_issue_count > 0:
|
|
276
|
+
human_summary = "历史执行审计一致性检查未通过。"
|
|
277
|
+
elif refresh_candidates > 0 or refresh_failed > 0:
|
|
278
|
+
human_summary = "历史审核策略一致性检查未通过。"
|
|
279
|
+
else:
|
|
280
|
+
human_summary = "发布包运行时覆盖检查发现缺失依赖或发布元数据问题。"
|
|
281
|
+
|
|
282
|
+
recommended_next_action = ""
|
|
283
|
+
if missing_from_package:
|
|
284
|
+
recommended_next_action = "更新 package.json 的 files,覆盖缺失的运行时路径。"
|
|
285
|
+
elif runtime["unresolved_local_modules"]:
|
|
286
|
+
recommended_next_action = "先修复未解析的本地 Python 模块,再进行发布。"
|
|
287
|
+
elif metadata_issues:
|
|
288
|
+
recommended_next_action = "先替换 homepage/bugs/repository 的占位元数据,再进行发布。"
|
|
289
|
+
elif historical_issue_count > 0:
|
|
290
|
+
recommended_next_action = "先运行 npm run cgc:audit-historical-audits,定位历史执行审计放置错误或旧仓库名残留问题。"
|
|
291
|
+
elif refresh_candidates > 0:
|
|
292
|
+
recommended_next_action = "先运行 npm run cgc:refresh-review-policy,刷新历史审核策略字段。"
|
|
293
|
+
elif refresh_failed > 0:
|
|
294
|
+
recommended_next_action = "先检查 refresh_codecgc_review_policy.py 的失败项,再进行发布。"
|
|
295
|
+
|
|
296
|
+
return {
|
|
297
|
+
"success": ready,
|
|
298
|
+
"mode": "package-runtime-audit",
|
|
299
|
+
"workspace": str(WORKSPACE),
|
|
300
|
+
"summary": {
|
|
301
|
+
"ready": ready,
|
|
302
|
+
"human_summary": human_summary,
|
|
303
|
+
"scope": "package.json 运行时覆盖与发布元数据就绪状态",
|
|
304
|
+
"missing_from_package_files": missing_from_package,
|
|
305
|
+
"unresolved_local_modules": runtime["unresolved_local_modules"],
|
|
306
|
+
"manifest_metadata_issues": metadata_issues,
|
|
307
|
+
"required_path_count": len(runtime["required_paths"]),
|
|
308
|
+
"recommended_next_action": recommended_next_action,
|
|
309
|
+
"review_policy_refresh_audit": review_policy_refresh_audit,
|
|
310
|
+
"historical_audit": historical_audit,
|
|
311
|
+
},
|
|
312
|
+
"package_files_rules": file_rules,
|
|
313
|
+
"manifest_metadata_issues": metadata_issues,
|
|
314
|
+
"required_runtime_paths": runtime["required_paths"],
|
|
315
|
+
"required_python_files": runtime["required_python_files"],
|
|
316
|
+
"import_graph": runtime["import_graph"],
|
|
317
|
+
"unresolved_local_modules": runtime["unresolved_local_modules"],
|
|
318
|
+
"review_policy_refresh_audit": review_policy_refresh_audit,
|
|
319
|
+
"historical_audit": historical_audit,
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def run_review_policy_refresh_audit() -> dict[str, Any]:
|
|
324
|
+
command = [
|
|
325
|
+
"python",
|
|
326
|
+
str(WORKSPACE / "scripts" / "audit_codecgc_review_policy.py"),
|
|
327
|
+
"--artifact-class",
|
|
328
|
+
"all",
|
|
329
|
+
"--format",
|
|
330
|
+
"json",
|
|
331
|
+
]
|
|
332
|
+
try:
|
|
333
|
+
completed = subprocess.run(
|
|
334
|
+
command,
|
|
335
|
+
cwd=str(WORKSPACE),
|
|
336
|
+
capture_output=True,
|
|
337
|
+
text=True,
|
|
338
|
+
encoding="utf-8",
|
|
339
|
+
)
|
|
340
|
+
parsed = json.loads(completed.stdout)
|
|
341
|
+
if not isinstance(parsed, dict):
|
|
342
|
+
raise ValueError("Refresh audit did not return a JSON object.")
|
|
343
|
+
return parsed
|
|
344
|
+
except Exception as error:
|
|
345
|
+
return {
|
|
346
|
+
"success": False,
|
|
347
|
+
"scan_target": "all",
|
|
348
|
+
"candidate_count": -1,
|
|
349
|
+
"failed_count": 1,
|
|
350
|
+
"missing_count": -1,
|
|
351
|
+
"missing": [{"artifact_path": "", "audit_path": "", "error": str(error)}],
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def run_historical_audit() -> dict[str, Any]:
|
|
356
|
+
command = [
|
|
357
|
+
"python",
|
|
358
|
+
str(WORKSPACE / "scripts" / "audit_codecgc_historical_audits.py"),
|
|
359
|
+
"--format",
|
|
360
|
+
"json",
|
|
361
|
+
]
|
|
362
|
+
try:
|
|
363
|
+
completed = subprocess.run(
|
|
364
|
+
command,
|
|
365
|
+
cwd=str(WORKSPACE),
|
|
366
|
+
capture_output=True,
|
|
367
|
+
text=True,
|
|
368
|
+
encoding="utf-8",
|
|
369
|
+
)
|
|
370
|
+
parsed = json.loads(completed.stdout)
|
|
371
|
+
if not isinstance(parsed, dict):
|
|
372
|
+
raise ValueError("Historical audit did not return a JSON object.")
|
|
373
|
+
return parsed
|
|
374
|
+
except Exception as error:
|
|
375
|
+
return {
|
|
376
|
+
"success": False,
|
|
377
|
+
"scanned": -1,
|
|
378
|
+
"issue_count": 1,
|
|
379
|
+
"issues": [{"path": "", "problem": "audit-execution-failed", "detail": str(error)}],
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def build_summary_lines(result: dict[str, Any]) -> str:
|
|
384
|
+
summary = result.get("summary", {}) if isinstance(result.get("summary"), dict) else {}
|
|
385
|
+
missing = summary.get("missing_from_package_files", [])
|
|
386
|
+
unresolved = summary.get("unresolved_local_modules", {})
|
|
387
|
+
metadata_issues = summary.get("manifest_metadata_issues", [])
|
|
388
|
+
refresh_audit = summary.get("review_policy_refresh_audit", {}) if isinstance(summary.get("review_policy_refresh_audit"), dict) else {}
|
|
389
|
+
historical_audit = summary.get("historical_audit", {}) if isinstance(summary.get("historical_audit"), dict) else {}
|
|
390
|
+
lines = [
|
|
391
|
+
"CodeCGC 发布包审计",
|
|
392
|
+
f"- 工作区: {result.get('workspace', '')}",
|
|
393
|
+
f"- 范围: {summary.get('scope', '')}",
|
|
394
|
+
f"- 就绪: {'是' if summary.get('ready') else '否'}",
|
|
395
|
+
f"- 摘要: {summary.get('human_summary', '')}",
|
|
396
|
+
f"- 必需路径数: {summary.get('required_path_count', 0)}",
|
|
397
|
+
f"- package.json files 缺失项: {', '.join(str(item) for item in missing) or '无'}",
|
|
398
|
+
f"- 历史执行审计问题数: {historical_audit.get('issue_count', 0)}",
|
|
399
|
+
f"- 审核策略待刷新项: {refresh_audit.get('missing_count', 0)}",
|
|
400
|
+
f"- 审核策略刷新失败项: {refresh_audit.get('failed_count', 0)}",
|
|
401
|
+
]
|
|
402
|
+
if isinstance(historical_audit.get("issues"), list):
|
|
403
|
+
for item in historical_audit["issues"]:
|
|
404
|
+
if not isinstance(item, dict):
|
|
405
|
+
continue
|
|
406
|
+
lines.append(
|
|
407
|
+
f"- 历史执行审计问题 {item.get('problem', '')}: {item.get('path', '')} ({item.get('detail', '')})"
|
|
408
|
+
)
|
|
409
|
+
if unresolved:
|
|
410
|
+
for source, modules in unresolved.items():
|
|
411
|
+
if not isinstance(modules, list):
|
|
412
|
+
continue
|
|
413
|
+
lines.append(f"- 未解析本地模块 {source}: {', '.join(str(item) for item in modules) or '无'}")
|
|
414
|
+
if isinstance(metadata_issues, list) and metadata_issues:
|
|
415
|
+
for item in metadata_issues:
|
|
416
|
+
if not isinstance(item, dict):
|
|
417
|
+
continue
|
|
418
|
+
lines.append(
|
|
419
|
+
f"- 元数据 {item.get('field', '')}: {item.get('problem', '')} ({item.get('detail', '')})"
|
|
420
|
+
)
|
|
421
|
+
next_action = str(summary.get("recommended_next_action", "")).strip()
|
|
422
|
+
next_actions = [next_action] if next_action else []
|
|
423
|
+
return render_summary_block("CodeCGC 发布包审计", lines[1:], next_actions)
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def main() -> int:
|
|
427
|
+
parser = argparse.ArgumentParser(description="Audit whether package.json files cover CodeCGC runtime dependencies.")
|
|
428
|
+
parser.add_argument(
|
|
429
|
+
"--format",
|
|
430
|
+
choices=["json", "summary"],
|
|
431
|
+
default="summary",
|
|
432
|
+
help="Output format. Summary is the default product-facing mode; use json for debugging or automation.",
|
|
433
|
+
)
|
|
434
|
+
args = parser.parse_args()
|
|
435
|
+
|
|
436
|
+
result = audit_package_runtime()
|
|
437
|
+
if args.format == "summary":
|
|
438
|
+
print(build_summary_lines(result))
|
|
439
|
+
else:
|
|
440
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
441
|
+
return 0 if result.get("success") else 1
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
if __name__ == "__main__":
|
|
445
|
+
raise SystemExit(main())
|