@hunyed15/codecgc 0.1.2 → 0.1.4
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/README.md +22 -0
- package/codecgcmcp/README.md +17 -0
- package/codecgcmcp/pyproject.toml +19 -0
- package/codecgcmcp/src/codecgcmcp/__init__.py +1 -0
- package/codecgcmcp/src/codecgcmcp/cli.py +12 -0
- package/codecgcmcp/src/codecgcmcp/server.py +525 -0
- package/model-routing.yaml +4 -0
- package/package.json +4 -1
- package/scripts/codecgc_workflow_runtime.py +1 -0
- package/scripts/install_codecgc.py +339 -67
- package/scripts/sync_codecgc_mcp_config.py +18 -1
package/README.md
CHANGED
|
@@ -52,12 +52,34 @@ cgc-install --mode user
|
|
|
52
52
|
- `~/.claude/hooks/route-edit.ps1`
|
|
53
53
|
- `~/.claude/commands/cgc*.md` 自定义 slash commands
|
|
54
54
|
|
|
55
|
+
当前安装链路还会注册 3 个 MCP server:
|
|
56
|
+
|
|
57
|
+
- `codecgc`:CodeCGC 编排器 MCP
|
|
58
|
+
- `codex`:后端执行器 MCP
|
|
59
|
+
- `gemini`:前端执行器 MCP
|
|
60
|
+
|
|
55
61
|
安装完成后,可以在 Claude 中直接使用:
|
|
56
62
|
|
|
57
63
|
- `/cgc`
|
|
58
64
|
- `/cgc-install`
|
|
59
65
|
- `/cgc-status`
|
|
60
66
|
- `/cgc-doctor`
|
|
67
|
+
- `/cgc-plan`
|
|
68
|
+
- `/cgc-build`
|
|
69
|
+
- `/cgc-fix`
|
|
70
|
+
- `/cgc-test`
|
|
71
|
+
- `/cgc-review`
|
|
72
|
+
- `/cgc-route`
|
|
73
|
+
- `/cgc-history`
|
|
74
|
+
- `/cgc-package-audit`
|
|
75
|
+
- `/cgc-external-audit`
|
|
76
|
+
- `/cgc-release-readiness`
|
|
77
|
+
- `/cgc-lifecycle`
|
|
78
|
+
|
|
79
|
+
当前这些 Claude commands 已调整为:
|
|
80
|
+
|
|
81
|
+
- 优先走 `codecgc` MCP orchestrator
|
|
82
|
+
- 只有在 MCP tool 路径不可用时,才回退到本地 CLI
|
|
61
83
|
|
|
62
84
|
如果安装时 Python 尚未就绪,自动集成会跳过,此时可在安装 Python 后手动执行 `cgc-install --mode user`。
|
|
63
85
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# CodeCGC MCP Server
|
|
2
|
+
|
|
3
|
+
This package contains the first minimal CodeCGC orchestrator MCP server.
|
|
4
|
+
|
|
5
|
+
Current scope:
|
|
6
|
+
|
|
7
|
+
- install
|
|
8
|
+
- status
|
|
9
|
+
- doctor
|
|
10
|
+
- entry
|
|
11
|
+
- continue
|
|
12
|
+
- explain
|
|
13
|
+
- review
|
|
14
|
+
- history
|
|
15
|
+
- route
|
|
16
|
+
|
|
17
|
+
This server currently reuses the existing CodeCGC runtime scripts rather than replacing them.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.25"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "codecgcmcp"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "FastMCP orchestrator server for CodeCGC."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"mcp[cli]>=1.21.2",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
codecgcmcp = "codecgcmcp.cli:main"
|
|
17
|
+
|
|
18
|
+
[tool.hatch.build.targets.wheel]
|
|
19
|
+
packages = ["src/codecgcmcp"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CodeCGC MCP orchestrator package."""
|
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
"""FastMCP server implementation for the CodeCGC orchestrator."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Annotated, Any, Literal
|
|
8
|
+
|
|
9
|
+
from mcp.server.fastmcp import FastMCP
|
|
10
|
+
from mcp.types import CallToolResult
|
|
11
|
+
from mcp.types import TextContent
|
|
12
|
+
from pydantic import Field
|
|
13
|
+
|
|
14
|
+
from codecgc_workflow_runtime import run_json_script
|
|
15
|
+
|
|
16
|
+
mcp = FastMCP("CodeCGC MCP Server")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _append_repeated_flag(args: list[str], flag: str, values: list[str]) -> None:
|
|
20
|
+
for value in values:
|
|
21
|
+
text = str(value).strip()
|
|
22
|
+
if text:
|
|
23
|
+
args.extend([flag, text])
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _normalize_workspace(workspace: str) -> str:
|
|
27
|
+
return str(Path(workspace).expanduser().resolve()) if str(workspace).strip() else ""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _as_tool_result(payload: dict[str, Any]) -> CallToolResult:
|
|
31
|
+
return CallToolResult(
|
|
32
|
+
content=[
|
|
33
|
+
TextContent(
|
|
34
|
+
type="text",
|
|
35
|
+
text=json.dumps(payload, ensure_ascii=True, indent=2),
|
|
36
|
+
)
|
|
37
|
+
],
|
|
38
|
+
isError=not bool(payload.get("success", True)),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@mcp.tool(
|
|
43
|
+
name="codecgc.install",
|
|
44
|
+
description="Install or sync CodeCGC integration for the current project or Claude user profile.",
|
|
45
|
+
meta={"version": "0.1.0", "author": "CodeCGC"},
|
|
46
|
+
)
|
|
47
|
+
async def codecgc_install(
|
|
48
|
+
mode: Annotated[
|
|
49
|
+
Literal["local", "user-dry-run", "user", "status", "doctor"],
|
|
50
|
+
Field(description="Install mode for CodeCGC integration."),
|
|
51
|
+
] = "local",
|
|
52
|
+
format: Annotated[
|
|
53
|
+
Literal["summary", "json"],
|
|
54
|
+
Field(description="Output format. Use summary for normal product-facing replies."),
|
|
55
|
+
] = "summary",
|
|
56
|
+
workspace: Annotated[
|
|
57
|
+
str,
|
|
58
|
+
Field(description="Optional target workspace root for local/status/doctor modes."),
|
|
59
|
+
] = "",
|
|
60
|
+
user_root: Annotated[
|
|
61
|
+
str,
|
|
62
|
+
Field(description="Optional explicit Claude user root for user/user-dry-run modes."),
|
|
63
|
+
] = "",
|
|
64
|
+
) -> CallToolResult:
|
|
65
|
+
args = ["--mode", mode, "--format", format]
|
|
66
|
+
normalized_workspace = _normalize_workspace(workspace)
|
|
67
|
+
if normalized_workspace:
|
|
68
|
+
args.extend(["--workspace", normalized_workspace])
|
|
69
|
+
if str(user_root).strip():
|
|
70
|
+
args.extend(["--user-root", str(user_root).strip()])
|
|
71
|
+
return _as_tool_result(run_json_script("install_codecgc.py", *args))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@mcp.tool(
|
|
75
|
+
name="codecgc.status",
|
|
76
|
+
description="Check CodeCGC integration readiness for the current or specified workspace.",
|
|
77
|
+
meta={"version": "0.1.0", "author": "CodeCGC"},
|
|
78
|
+
)
|
|
79
|
+
async def codecgc_status(
|
|
80
|
+
workspace: Annotated[
|
|
81
|
+
str,
|
|
82
|
+
Field(description="Optional target workspace root."),
|
|
83
|
+
] = "",
|
|
84
|
+
) -> CallToolResult:
|
|
85
|
+
args = ["--mode", "status", "--format", "json"]
|
|
86
|
+
normalized_workspace = _normalize_workspace(workspace)
|
|
87
|
+
if normalized_workspace:
|
|
88
|
+
args.extend(["--workspace", normalized_workspace])
|
|
89
|
+
return _as_tool_result(run_json_script("install_codecgc.py", *args))
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@mcp.tool(
|
|
93
|
+
name="codecgc.doctor",
|
|
94
|
+
description="Run CodeCGC runtime and integration health checks.",
|
|
95
|
+
meta={"version": "0.1.0", "author": "CodeCGC"},
|
|
96
|
+
)
|
|
97
|
+
async def codecgc_doctor(
|
|
98
|
+
workspace: Annotated[
|
|
99
|
+
str,
|
|
100
|
+
Field(description="Optional target workspace root."),
|
|
101
|
+
] = "",
|
|
102
|
+
) -> CallToolResult:
|
|
103
|
+
args = ["--mode", "doctor", "--format", "json"]
|
|
104
|
+
normalized_workspace = _normalize_workspace(workspace)
|
|
105
|
+
if normalized_workspace:
|
|
106
|
+
args.extend(["--workspace", normalized_workspace])
|
|
107
|
+
return _as_tool_result(run_json_script("install_codecgc.py", *args))
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@mcp.tool(
|
|
111
|
+
name="codecgc.entry",
|
|
112
|
+
description="Primary CodeCGC orchestration entry for new requests, continue, explain, and auto-dispatch decisions.",
|
|
113
|
+
meta={"version": "0.1.0", "author": "CodeCGC"},
|
|
114
|
+
)
|
|
115
|
+
async def codecgc_entry(
|
|
116
|
+
request: Annotated[str, "Natural-language request for CodeCGC."] = "",
|
|
117
|
+
mode: Annotated[
|
|
118
|
+
Literal["auto", "new", "continue", "explain"],
|
|
119
|
+
Field(description="Entry mode."),
|
|
120
|
+
] = "auto",
|
|
121
|
+
flow: Annotated[
|
|
122
|
+
Literal["", "feature", "issue"],
|
|
123
|
+
Field(description="Optional explicit workflow flow."),
|
|
124
|
+
] = "",
|
|
125
|
+
slug: Annotated[str, "Optional workflow slug."] = "",
|
|
126
|
+
latest: Annotated[bool, "Use the latest matching workflow."] = False,
|
|
127
|
+
include_fixtures: Annotated[bool, "Allow fixture workflows to be considered."] = False,
|
|
128
|
+
auto_dispatch: Annotated[bool, "Allow entry to dispatch when the route permits it."] = False,
|
|
129
|
+
dry_run: Annotated[bool, "Avoid real executor dispatch."] = False,
|
|
130
|
+
audit_file: Annotated[str, "Optional audit file for review dispatch."] = "",
|
|
131
|
+
decision: Annotated[
|
|
132
|
+
Literal["", "accepted", "changes-requested"],
|
|
133
|
+
Field(description="Optional review decision when the request is review-like."),
|
|
134
|
+
] = "",
|
|
135
|
+
) -> CallToolResult:
|
|
136
|
+
args = ["--mode", mode]
|
|
137
|
+
if str(request).strip():
|
|
138
|
+
args.extend(["--request", str(request).strip()])
|
|
139
|
+
if flow:
|
|
140
|
+
args.extend(["--flow", flow])
|
|
141
|
+
if str(slug).strip():
|
|
142
|
+
args.extend(["--slug", str(slug).strip()])
|
|
143
|
+
if latest:
|
|
144
|
+
args.append("--latest")
|
|
145
|
+
if include_fixtures:
|
|
146
|
+
args.append("--include-fixtures")
|
|
147
|
+
if auto_dispatch:
|
|
148
|
+
args.append("--auto-dispatch")
|
|
149
|
+
if dry_run:
|
|
150
|
+
args.append("--dry-run")
|
|
151
|
+
if str(audit_file).strip():
|
|
152
|
+
args.extend(["--audit-file", str(audit_file).strip()])
|
|
153
|
+
if decision:
|
|
154
|
+
args.extend(["--decision", decision])
|
|
155
|
+
return _as_tool_result(run_json_script("entry_codecgc_workflow.py", *args))
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@mcp.tool(
|
|
159
|
+
name="codecgc.continue",
|
|
160
|
+
description="Continue the current or latest CodeCGC workflow using the existing runtime state.",
|
|
161
|
+
meta={"version": "0.1.0", "author": "CodeCGC"},
|
|
162
|
+
)
|
|
163
|
+
async def codecgc_continue(
|
|
164
|
+
request: Annotated[str, "Optional continue request text. Defaults to continuing the current work."] = "继续刚刚的工作",
|
|
165
|
+
flow: Annotated[
|
|
166
|
+
Literal["", "feature", "issue"],
|
|
167
|
+
Field(description="Optional explicit workflow flow."),
|
|
168
|
+
] = "",
|
|
169
|
+
slug: Annotated[str, "Optional workflow slug."] = "",
|
|
170
|
+
latest: Annotated[bool, "Use the latest matching workflow."] = True,
|
|
171
|
+
include_fixtures: Annotated[bool, "Allow fixture workflows to be considered."] = False,
|
|
172
|
+
auto_dispatch: Annotated[bool, "Allow continue to dispatch when the route permits it."] = False,
|
|
173
|
+
dry_run: Annotated[bool, "Avoid real executor dispatch."] = False,
|
|
174
|
+
) -> CallToolResult:
|
|
175
|
+
args = ["--mode", "continue", "--request", str(request).strip() or "继续刚刚的工作"]
|
|
176
|
+
if flow:
|
|
177
|
+
args.extend(["--flow", flow])
|
|
178
|
+
if str(slug).strip():
|
|
179
|
+
args.extend(["--slug", str(slug).strip()])
|
|
180
|
+
if latest:
|
|
181
|
+
args.append("--latest")
|
|
182
|
+
if include_fixtures:
|
|
183
|
+
args.append("--include-fixtures")
|
|
184
|
+
if auto_dispatch:
|
|
185
|
+
args.append("--auto-dispatch")
|
|
186
|
+
if dry_run:
|
|
187
|
+
args.append("--dry-run")
|
|
188
|
+
return _as_tool_result(run_json_script("entry_codecgc_workflow.py", *args))
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@mcp.tool(
|
|
192
|
+
name="codecgc.explain",
|
|
193
|
+
description="Explain the current or latest CodeCGC workflow state and recommended next action.",
|
|
194
|
+
meta={"version": "0.1.0", "author": "CodeCGC"},
|
|
195
|
+
)
|
|
196
|
+
async def codecgc_explain(
|
|
197
|
+
request: Annotated[str, "Optional explain request text."] = "现在下一步该做什么",
|
|
198
|
+
flow: Annotated[
|
|
199
|
+
Literal["", "feature", "issue"],
|
|
200
|
+
Field(description="Optional explicit workflow flow."),
|
|
201
|
+
] = "",
|
|
202
|
+
slug: Annotated[str, "Optional workflow slug."] = "",
|
|
203
|
+
latest: Annotated[bool, "Use the latest matching workflow."] = True,
|
|
204
|
+
include_fixtures: Annotated[bool, "Allow fixture workflows to be considered."] = False,
|
|
205
|
+
) -> CallToolResult:
|
|
206
|
+
args = ["--mode", "explain", "--request", str(request).strip() or "现在下一步该做什么"]
|
|
207
|
+
if flow:
|
|
208
|
+
args.extend(["--flow", flow])
|
|
209
|
+
if str(slug).strip():
|
|
210
|
+
args.extend(["--slug", str(slug).strip()])
|
|
211
|
+
if latest:
|
|
212
|
+
args.append("--latest")
|
|
213
|
+
if include_fixtures:
|
|
214
|
+
args.append("--include-fixtures")
|
|
215
|
+
return _as_tool_result(run_json_script("entry_codecgc_workflow.py", *args))
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@mcp.tool(
|
|
219
|
+
name="codecgc.review",
|
|
220
|
+
description="Review a CodeCGC execution audit and write the decision back to workflow artifacts.",
|
|
221
|
+
meta={"version": "0.1.0", "author": "CodeCGC"},
|
|
222
|
+
)
|
|
223
|
+
async def codecgc_review(
|
|
224
|
+
audit_file: Annotated[str, "Execution audit JSON file path."],
|
|
225
|
+
decision: Annotated[
|
|
226
|
+
Literal["accepted", "changes-requested"],
|
|
227
|
+
Field(description="Requested review decision."),
|
|
228
|
+
],
|
|
229
|
+
risk: Annotated[list[str], Field(description="Optional remaining risks to record.")] = [],
|
|
230
|
+
next_step: Annotated[str, "Optional next-step text to record."] = "",
|
|
231
|
+
force: Annotated[bool, "Allow overwriting an existing review writeback."] = False,
|
|
232
|
+
) -> CallToolResult:
|
|
233
|
+
args = ["--audit-file", str(audit_file).strip(), "--decision", decision]
|
|
234
|
+
_append_repeated_flag(args, "--risk", risk)
|
|
235
|
+
if str(next_step).strip():
|
|
236
|
+
args.extend(["--next-step", str(next_step).strip()])
|
|
237
|
+
if force:
|
|
238
|
+
args.append("--force")
|
|
239
|
+
return _as_tool_result(run_json_script("review_codecgc_workflow.py", *args))
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
@mcp.tool(
|
|
243
|
+
name="codecgc.history",
|
|
244
|
+
description="Read recent CodeCGC workflow history across feature and issue flows.",
|
|
245
|
+
meta={"version": "0.1.0", "author": "CodeCGC"},
|
|
246
|
+
)
|
|
247
|
+
async def codecgc_history(
|
|
248
|
+
flow: Annotated[
|
|
249
|
+
Literal["all", "feature", "issue"],
|
|
250
|
+
Field(description="Workflow flow filter."),
|
|
251
|
+
] = "all",
|
|
252
|
+
status: Annotated[str, "Workflow status filter."] = "all",
|
|
253
|
+
last: Annotated[int, "Maximum number of records to return."] = 10,
|
|
254
|
+
include_fixtures: Annotated[bool, "Include fixture workflows."] = False,
|
|
255
|
+
) -> CallToolResult:
|
|
256
|
+
args = ["--flow", flow, "--status", str(status).strip() or "all", "--last", str(int(last)), "--format", "json"]
|
|
257
|
+
if include_fixtures:
|
|
258
|
+
args.append("--include-fixtures")
|
|
259
|
+
return _as_tool_result(run_json_script("audit_codecgc_workflow_history.py", *args))
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
@mcp.tool(
|
|
263
|
+
name="codecgc.route",
|
|
264
|
+
description="Route an existing CodeCGC workflow to the recommended next command.",
|
|
265
|
+
meta={"version": "0.1.0", "author": "CodeCGC"},
|
|
266
|
+
)
|
|
267
|
+
async def codecgc_route(
|
|
268
|
+
flow: Annotated[
|
|
269
|
+
Literal["feature", "issue"],
|
|
270
|
+
Field(description="Workflow flow."),
|
|
271
|
+
],
|
|
272
|
+
slug: Annotated[str, "Workflow slug."],
|
|
273
|
+
) -> CallToolResult:
|
|
274
|
+
return _as_tool_result(run_json_script("route_codecgc_workflow.py", "--flow", flow, "--slug", str(slug).strip()))
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
@mcp.tool(
|
|
278
|
+
name="codecgc.plan",
|
|
279
|
+
description="Plan or repair a CodeCGC feature or issue workflow scaffold.",
|
|
280
|
+
meta={"version": "0.1.0", "author": "CodeCGC"},
|
|
281
|
+
)
|
|
282
|
+
async def codecgc_plan(
|
|
283
|
+
flow: Annotated[
|
|
284
|
+
Literal["feature", "issue"],
|
|
285
|
+
Field(description="Workflow flow."),
|
|
286
|
+
],
|
|
287
|
+
slug: Annotated[str, "Workflow slug."],
|
|
288
|
+
summary: Annotated[str, "Planning summary."],
|
|
289
|
+
date: Annotated[str, "Optional workflow date, defaults to current date in runtime."] = "",
|
|
290
|
+
target_paths: Annotated[list[str], Field(description="Target paths for this workflow.")] = [],
|
|
291
|
+
kind: Annotated[
|
|
292
|
+
Literal["auto", "frontend", "backend"],
|
|
293
|
+
Field(description="Routing kind."),
|
|
294
|
+
] = "auto",
|
|
295
|
+
goal: Annotated[str, "Optional goal text."] = "",
|
|
296
|
+
context: Annotated[list[str], Field(description="Optional context notes.")] = [],
|
|
297
|
+
user_story: Annotated[str, "Optional user story."] = "",
|
|
298
|
+
in_scope: Annotated[list[str], Field(description="In-scope items.")] = [],
|
|
299
|
+
out_of_scope: Annotated[list[str], Field(description="Out-of-scope items.")] = [],
|
|
300
|
+
acceptance: Annotated[list[str], Field(description="Acceptance criteria.")] = [],
|
|
301
|
+
risk: Annotated[list[str], Field(description="Risk items.")] = [],
|
|
302
|
+
dependency: Annotated[list[str], Field(description="Dependencies.")] = [],
|
|
303
|
+
assumption: Annotated[list[str], Field(description="Assumptions.")] = [],
|
|
304
|
+
open_question: Annotated[list[str], Field(description="Open questions.")] = [],
|
|
305
|
+
validation: Annotated[list[str], Field(description="Validation steps.")] = [],
|
|
306
|
+
rollback: Annotated[list[str], Field(description="Rollback notes.")] = [],
|
|
307
|
+
symptom: Annotated[str, "Issue symptom."] = "",
|
|
308
|
+
reproduction: Annotated[str, "Issue reproduction."] = "",
|
|
309
|
+
expected: Annotated[str, "Expected behavior."] = "",
|
|
310
|
+
actual: Annotated[str, "Actual behavior."] = "",
|
|
311
|
+
root_cause: Annotated[str, "Root cause note."] = "",
|
|
312
|
+
preferred_fix: Annotated[str, "Preferred fix note."] = "",
|
|
313
|
+
rejected_fix: Annotated[str, "Rejected fix note."] = "",
|
|
314
|
+
artifact_class: Annotated[
|
|
315
|
+
Literal["product", "fixture"],
|
|
316
|
+
Field(description="Artifact class."),
|
|
317
|
+
] = "product",
|
|
318
|
+
force: Annotated[bool, "Allow overwriting scaffold state when needed."] = False,
|
|
319
|
+
) -> CallToolResult:
|
|
320
|
+
args = ["--flow", flow, "--slug", str(slug).strip(), "--summary", str(summary).strip()]
|
|
321
|
+
if str(date).strip():
|
|
322
|
+
args.extend(["--date", str(date).strip()])
|
|
323
|
+
_append_repeated_flag(args, "--target-path", target_paths)
|
|
324
|
+
if kind:
|
|
325
|
+
args.extend(["--kind", kind])
|
|
326
|
+
if str(goal).strip():
|
|
327
|
+
args.extend(["--goal", str(goal).strip()])
|
|
328
|
+
_append_repeated_flag(args, "--context", context)
|
|
329
|
+
if str(user_story).strip():
|
|
330
|
+
args.extend(["--user-story", str(user_story).strip()])
|
|
331
|
+
_append_repeated_flag(args, "--in-scope", in_scope)
|
|
332
|
+
_append_repeated_flag(args, "--out-of-scope", out_of_scope)
|
|
333
|
+
_append_repeated_flag(args, "--acceptance", acceptance)
|
|
334
|
+
_append_repeated_flag(args, "--risk", risk)
|
|
335
|
+
_append_repeated_flag(args, "--dependency", dependency)
|
|
336
|
+
_append_repeated_flag(args, "--assumption", assumption)
|
|
337
|
+
_append_repeated_flag(args, "--open-question", open_question)
|
|
338
|
+
_append_repeated_flag(args, "--validation", validation)
|
|
339
|
+
_append_repeated_flag(args, "--rollback", rollback)
|
|
340
|
+
if str(symptom).strip():
|
|
341
|
+
args.extend(["--symptom", str(symptom).strip()])
|
|
342
|
+
if str(reproduction).strip():
|
|
343
|
+
args.extend(["--reproduction", str(reproduction).strip()])
|
|
344
|
+
if str(expected).strip():
|
|
345
|
+
args.extend(["--expected", str(expected).strip()])
|
|
346
|
+
if str(actual).strip():
|
|
347
|
+
args.extend(["--actual", str(actual).strip()])
|
|
348
|
+
if str(root_cause).strip():
|
|
349
|
+
args.extend(["--root-cause", str(root_cause).strip()])
|
|
350
|
+
if str(preferred_fix).strip():
|
|
351
|
+
args.extend(["--preferred-fix", str(preferred_fix).strip()])
|
|
352
|
+
if str(rejected_fix).strip():
|
|
353
|
+
args.extend(["--rejected-fix", str(rejected_fix).strip()])
|
|
354
|
+
args.extend(["--artifact-class", artifact_class])
|
|
355
|
+
if force:
|
|
356
|
+
args.append("--force")
|
|
357
|
+
return _as_tool_result(run_json_script("plan_codecgc_workflow.py", *args))
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
@mcp.tool(
|
|
361
|
+
name="codecgc.build",
|
|
362
|
+
description="Execute a CodeCGC feature step through the existing runtime.",
|
|
363
|
+
meta={"version": "0.1.0", "author": "CodeCGC"},
|
|
364
|
+
)
|
|
365
|
+
async def codecgc_build(
|
|
366
|
+
slug: Annotated[str, "Feature workflow slug."],
|
|
367
|
+
step_number: Annotated[int | None, "Optional explicit step number."] = None,
|
|
368
|
+
checklist_file: Annotated[str, "Optional checklist file path."] = "",
|
|
369
|
+
audit_root: Annotated[str, "Optional audit root path."] = "",
|
|
370
|
+
timeout_seconds: Annotated[int, "Execution timeout in seconds."] = 120,
|
|
371
|
+
session_id: Annotated[str, "Optional reusable executor session id."] = "",
|
|
372
|
+
dry_run: Annotated[bool, "Avoid real executor dispatch."] = False,
|
|
373
|
+
return_all_messages: Annotated[bool, "Return executor event logs when available."] = False,
|
|
374
|
+
) -> CallToolResult:
|
|
375
|
+
args = ["--slug", str(slug).strip(), "--timeout-seconds", str(int(timeout_seconds))]
|
|
376
|
+
if step_number is not None:
|
|
377
|
+
args.extend(["--step-number", str(int(step_number))])
|
|
378
|
+
if str(checklist_file).strip():
|
|
379
|
+
args.extend(["--checklist-file", str(checklist_file).strip()])
|
|
380
|
+
if str(audit_root).strip():
|
|
381
|
+
args.extend(["--audit-root", str(audit_root).strip()])
|
|
382
|
+
if str(session_id).strip():
|
|
383
|
+
args.extend(["--session-id", str(session_id).strip()])
|
|
384
|
+
if dry_run:
|
|
385
|
+
args.append("--dry-run")
|
|
386
|
+
if return_all_messages:
|
|
387
|
+
args.append("--return-all-messages")
|
|
388
|
+
return _as_tool_result(run_json_script("run_codecgc_build.py", *args))
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
@mcp.tool(
|
|
392
|
+
name="codecgc.fix",
|
|
393
|
+
description="Execute a CodeCGC issue fix step through the existing runtime.",
|
|
394
|
+
meta={"version": "0.1.0", "author": "CodeCGC"},
|
|
395
|
+
)
|
|
396
|
+
async def codecgc_fix(
|
|
397
|
+
slug: Annotated[str, "Issue workflow slug."],
|
|
398
|
+
step_number: Annotated[int | None, "Optional explicit step number."] = None,
|
|
399
|
+
checklist_file: Annotated[str, "Optional checklist file path."] = "",
|
|
400
|
+
audit_root: Annotated[str, "Optional audit root path."] = "",
|
|
401
|
+
timeout_seconds: Annotated[int, "Execution timeout in seconds."] = 120,
|
|
402
|
+
session_id: Annotated[str, "Optional reusable executor session id."] = "",
|
|
403
|
+
dry_run: Annotated[bool, "Avoid real executor dispatch."] = False,
|
|
404
|
+
return_all_messages: Annotated[bool, "Return executor event logs when available."] = False,
|
|
405
|
+
) -> CallToolResult:
|
|
406
|
+
args = ["--slug", str(slug).strip(), "--timeout-seconds", str(int(timeout_seconds))]
|
|
407
|
+
if step_number is not None:
|
|
408
|
+
args.extend(["--step-number", str(int(step_number))])
|
|
409
|
+
if str(checklist_file).strip():
|
|
410
|
+
args.extend(["--checklist-file", str(checklist_file).strip()])
|
|
411
|
+
if str(audit_root).strip():
|
|
412
|
+
args.extend(["--audit-root", str(audit_root).strip()])
|
|
413
|
+
if str(session_id).strip():
|
|
414
|
+
args.extend(["--session-id", str(session_id).strip()])
|
|
415
|
+
if dry_run:
|
|
416
|
+
args.append("--dry-run")
|
|
417
|
+
if return_all_messages:
|
|
418
|
+
args.append("--return-all-messages")
|
|
419
|
+
return _as_tool_result(run_json_script("run_codecgc_fix.py", *args))
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
@mcp.tool(
|
|
423
|
+
name="codecgc.test",
|
|
424
|
+
description="Execute a CodeCGC test step through the existing runtime.",
|
|
425
|
+
meta={"version": "0.1.0", "author": "CodeCGC"},
|
|
426
|
+
)
|
|
427
|
+
async def codecgc_test(
|
|
428
|
+
flow: Annotated[
|
|
429
|
+
Literal["feature", "issue"],
|
|
430
|
+
Field(description="Workflow flow."),
|
|
431
|
+
],
|
|
432
|
+
slug: Annotated[str, "Workflow slug."],
|
|
433
|
+
step_number: Annotated[int | None, "Optional explicit step number."] = None,
|
|
434
|
+
checklist_file: Annotated[str, "Optional checklist file path."] = "",
|
|
435
|
+
audit_root: Annotated[str, "Optional audit root path."] = "",
|
|
436
|
+
timeout_seconds: Annotated[int, "Execution timeout in seconds."] = 120,
|
|
437
|
+
session_id: Annotated[str, "Optional reusable executor session id."] = "",
|
|
438
|
+
dry_run: Annotated[bool, "Avoid real executor dispatch."] = False,
|
|
439
|
+
return_all_messages: Annotated[bool, "Return executor event logs when available."] = False,
|
|
440
|
+
) -> CallToolResult:
|
|
441
|
+
args = ["--flow", flow, "--slug", str(slug).strip(), "--timeout-seconds", str(int(timeout_seconds))]
|
|
442
|
+
if step_number is not None:
|
|
443
|
+
args.extend(["--step-number", str(int(step_number))])
|
|
444
|
+
if str(checklist_file).strip():
|
|
445
|
+
args.extend(["--checklist-file", str(checklist_file).strip()])
|
|
446
|
+
if str(audit_root).strip():
|
|
447
|
+
args.extend(["--audit-root", str(audit_root).strip()])
|
|
448
|
+
if str(session_id).strip():
|
|
449
|
+
args.extend(["--session-id", str(session_id).strip()])
|
|
450
|
+
if dry_run:
|
|
451
|
+
args.append("--dry-run")
|
|
452
|
+
if return_all_messages:
|
|
453
|
+
args.append("--return-all-messages")
|
|
454
|
+
return _as_tool_result(run_json_script("run_codecgc_test.py", *args))
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
@mcp.tool(
|
|
458
|
+
name="codecgc.package_audit",
|
|
459
|
+
description="Audit whether the published CodeCGC package contains all required runtime files.",
|
|
460
|
+
meta={"version": "0.1.0", "author": "CodeCGC"},
|
|
461
|
+
)
|
|
462
|
+
async def codecgc_package_audit(
|
|
463
|
+
format: Annotated[
|
|
464
|
+
Literal["summary", "json"],
|
|
465
|
+
Field(description="Output format."),
|
|
466
|
+
] = "json",
|
|
467
|
+
) -> CallToolResult:
|
|
468
|
+
return _as_tool_result(run_json_script("audit_codecgc_package_runtime.py", "--format", format))
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
@mcp.tool(
|
|
472
|
+
name="codecgc.external_audit",
|
|
473
|
+
description="Audit external capability registration policy and locally observed MCP servers.",
|
|
474
|
+
meta={"version": "0.1.0", "author": "CodeCGC"},
|
|
475
|
+
)
|
|
476
|
+
async def codecgc_external_audit(
|
|
477
|
+
workspace: Annotated[str, "Optional target workspace root."] = "",
|
|
478
|
+
format: Annotated[
|
|
479
|
+
Literal["summary", "json"],
|
|
480
|
+
Field(description="Output format."),
|
|
481
|
+
] = "json",
|
|
482
|
+
) -> CallToolResult:
|
|
483
|
+
args = ["--format", format]
|
|
484
|
+
normalized_workspace = _normalize_workspace(workspace)
|
|
485
|
+
if normalized_workspace:
|
|
486
|
+
args.extend(["--workspace", normalized_workspace])
|
|
487
|
+
return _as_tool_result(run_json_script("audit_codecgc_external_capabilities.py", *args))
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
@mcp.tool(
|
|
491
|
+
name="codecgc.release_readiness",
|
|
492
|
+
description="Run the combined CodeCGC release, maintenance, and ops readiness audit.",
|
|
493
|
+
meta={"version": "0.1.0", "author": "CodeCGC"},
|
|
494
|
+
)
|
|
495
|
+
async def codecgc_release_readiness(
|
|
496
|
+
workspace: Annotated[str, "Optional target workspace root."] = "",
|
|
497
|
+
format: Annotated[
|
|
498
|
+
Literal["summary", "json"],
|
|
499
|
+
Field(description="Output format."),
|
|
500
|
+
] = "json",
|
|
501
|
+
) -> CallToolResult:
|
|
502
|
+
args = ["--format", format]
|
|
503
|
+
normalized_workspace = _normalize_workspace(workspace)
|
|
504
|
+
if normalized_workspace:
|
|
505
|
+
args.extend(["--workspace", normalized_workspace])
|
|
506
|
+
return _as_tool_result(run_json_script("audit_codecgc_release_readiness.py", *args))
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
@mcp.tool(
|
|
510
|
+
name="codecgc.lifecycle",
|
|
511
|
+
description="Audit CodeCGC lifecycle coverage across roadmap, workflows, and execution artifacts.",
|
|
512
|
+
meta={"version": "0.1.0", "author": "CodeCGC"},
|
|
513
|
+
)
|
|
514
|
+
async def codecgc_lifecycle(
|
|
515
|
+
format: Annotated[
|
|
516
|
+
Literal["summary", "json"],
|
|
517
|
+
Field(description="Output format."),
|
|
518
|
+
] = "json",
|
|
519
|
+
) -> CallToolResult:
|
|
520
|
+
return _as_tool_result(run_json_script("audit_codecgc_lifecycle.py", "--format", format))
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
def run() -> None:
|
|
524
|
+
"""Start the MCP server over stdio transport."""
|
|
525
|
+
mcp.run(transport="stdio")
|
package/model-routing.yaml
CHANGED
|
@@ -9,6 +9,8 @@ frontend_paths:
|
|
|
9
9
|
- "web/**"
|
|
10
10
|
- "frontend/**"
|
|
11
11
|
|
|
12
|
+
custom_frontend_paths:
|
|
13
|
+
|
|
12
14
|
backend_paths:
|
|
13
15
|
- "apps/api/**"
|
|
14
16
|
- "server/**"
|
|
@@ -17,6 +19,8 @@ backend_paths:
|
|
|
17
19
|
- "src/repositories/**"
|
|
18
20
|
- "backend/**"
|
|
19
21
|
|
|
22
|
+
custom_backend_paths:
|
|
23
|
+
|
|
20
24
|
shared_paths:
|
|
21
25
|
- "packages/shared/**"
|
|
22
26
|
- "src/shared/**"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hunyed15/codecgc",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Claude-hosted multi-model workflow product shell for CodeCGC.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -57,6 +57,9 @@
|
|
|
57
57
|
"codecgc/cgc-roadmap/",
|
|
58
58
|
"codecgc/cgc-test/",
|
|
59
59
|
"codecgc/reference/",
|
|
60
|
+
"codecgcmcp/pyproject.toml",
|
|
61
|
+
"codecgcmcp/README.md",
|
|
62
|
+
"codecgcmcp/src/codecgcmcp/",
|
|
60
63
|
"codexmcp/pyproject.toml",
|
|
61
64
|
"codexmcp/README.md",
|
|
62
65
|
"codexmcp/LICENSE",
|
|
@@ -42,6 +42,11 @@ DEFAULT_HOOKS = {
|
|
|
42
42
|
|
|
43
43
|
SENSITIVE_KEYWORDS = ("token", "secret", "key", "password", "auth")
|
|
44
44
|
MCP_RUNTIME_REQUIREMENT = 'mcp[cli]>=1.21.2'
|
|
45
|
+
DEFAULT_ALLOWED_TOOLS = [
|
|
46
|
+
"mcp__codecgc__*",
|
|
47
|
+
"mcp__codex__*",
|
|
48
|
+
"mcp__gemini__*",
|
|
49
|
+
]
|
|
45
50
|
|
|
46
51
|
|
|
47
52
|
def get_user_claude_root(override_root: str = "") -> Path:
|
|
@@ -116,62 +121,233 @@ def _normalize_command_path_for_markdown(path: Path) -> str:
|
|
|
116
121
|
return str(path).replace("\\", "\\\\")
|
|
117
122
|
|
|
118
123
|
|
|
119
|
-
def
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
+
def build_mcp_first_command_template(
|
|
125
|
+
*,
|
|
126
|
+
filename: str,
|
|
127
|
+
description: str,
|
|
128
|
+
argument_hint: str,
|
|
129
|
+
primary_tool: str,
|
|
130
|
+
direct_rules: list[str],
|
|
131
|
+
missing_rules: list[str] | None = None,
|
|
132
|
+
fallback_command: str,
|
|
133
|
+
) -> tuple[str, str]:
|
|
134
|
+
lines = [
|
|
135
|
+
"---",
|
|
136
|
+
f"description: {description}",
|
|
137
|
+
f"argument-hint: \"{argument_hint}\"",
|
|
138
|
+
"---",
|
|
139
|
+
f"优先使用 `{primary_tool}` MCP 工具作为主执行路径。",
|
|
140
|
+
"内部思考语言可自行选择,但面向用户的最终回复默认使用中文。",
|
|
141
|
+
"",
|
|
142
|
+
"执行规则:",
|
|
143
|
+
]
|
|
144
|
+
lines.extend(f"- {item}" for item in direct_rules)
|
|
145
|
+
if missing_rules:
|
|
146
|
+
lines.append("")
|
|
147
|
+
lines.append("缺少参数时:")
|
|
148
|
+
lines.extend(f"- {item}" for item in missing_rules)
|
|
149
|
+
lines.append("")
|
|
150
|
+
lines.append("回退规则:")
|
|
151
|
+
lines.append(f"- 只有在 MCP 工具路径不可用,或用户明确要求走 CLI 时,才回退到 Bash + `{fallback_command}`。")
|
|
152
|
+
lines.append("- 向用户用中文简要总结结果。")
|
|
153
|
+
return filename, "\n".join(lines) + "\n"
|
|
124
154
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
""
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
""
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
-
|
|
173
|
-
|
|
174
|
-
|
|
155
|
+
|
|
156
|
+
def build_custom_command_templates(bin_dir: Path) -> dict[str, str]:
|
|
157
|
+
templates = dict(
|
|
158
|
+
[
|
|
159
|
+
build_mcp_first_command_template(
|
|
160
|
+
filename="cgc.md",
|
|
161
|
+
description="在当前项目中运行 CodeCGC",
|
|
162
|
+
argument_hint="[需求或参数]",
|
|
163
|
+
primary_tool="codecgc.entry",
|
|
164
|
+
direct_rules=[
|
|
165
|
+
"如果用户提供的是自然语言需求,传给 `codecgc.entry`。",
|
|
166
|
+
"如果用户想继续最近的工作,使用 `codecgc.continue`。",
|
|
167
|
+
"如果用户想知道下一步做什么,使用 `codecgc.explain`。",
|
|
168
|
+
],
|
|
169
|
+
fallback_command="cgc",
|
|
170
|
+
),
|
|
171
|
+
build_mcp_first_command_template(
|
|
172
|
+
filename="cgc-install.md",
|
|
173
|
+
description="为当前项目或 Claude 用户目录安装/同步 CodeCGC 集成",
|
|
174
|
+
argument_hint="[参数]",
|
|
175
|
+
primary_tool="codecgc.install",
|
|
176
|
+
direct_rules=[
|
|
177
|
+
"把安装参数映射到 `codecgc.install` 的 `mode`、`workspace`、`user_root` 等字段。",
|
|
178
|
+
"如果用户没有提供参数,就使用默认安装模式。",
|
|
179
|
+
],
|
|
180
|
+
fallback_command="cgc-install",
|
|
181
|
+
),
|
|
182
|
+
build_mcp_first_command_template(
|
|
183
|
+
filename="cgc-status.md",
|
|
184
|
+
description="检查 CodeCGC 集成状态",
|
|
185
|
+
argument_hint="[参数]",
|
|
186
|
+
primary_tool="codecgc.status",
|
|
187
|
+
direct_rules=[
|
|
188
|
+
"使用 `codecgc.status` 检查安装与集成就绪状态。",
|
|
189
|
+
"如果用户明确给出目标项目目录,映射 `workspace`。",
|
|
190
|
+
],
|
|
191
|
+
fallback_command="cgc-status",
|
|
192
|
+
),
|
|
193
|
+
build_mcp_first_command_template(
|
|
194
|
+
filename="cgc-doctor.md",
|
|
195
|
+
description="运行 CodeCGC 自检",
|
|
196
|
+
argument_hint="[参数]",
|
|
197
|
+
primary_tool="codecgc.doctor",
|
|
198
|
+
direct_rules=[
|
|
199
|
+
"使用 `codecgc.doctor` 检查运行时与集成健康状态。",
|
|
200
|
+
"如果用户明确给出目标项目目录,映射 `workspace`。",
|
|
201
|
+
],
|
|
202
|
+
fallback_command="cgc-doctor",
|
|
203
|
+
),
|
|
204
|
+
build_mcp_first_command_template(
|
|
205
|
+
filename="cgc-plan.md",
|
|
206
|
+
description="规划或修复一个 CodeCGC 工作流",
|
|
207
|
+
argument_hint="[结构化规划参数]",
|
|
208
|
+
primary_tool="codecgc.plan",
|
|
209
|
+
direct_rules=[
|
|
210
|
+
"调用前提取 `flow`、`slug` 和 `summary`。",
|
|
211
|
+
"映射用户提供的 `target_paths`、`kind`,以及 `goal`、`acceptance`、`risk` 等规划字段和 issue 专属字段。",
|
|
212
|
+
],
|
|
213
|
+
missing_rules=[
|
|
214
|
+
"如果缺少 `flow`,询问这是 `feature` 还是 `issue` 工作流。",
|
|
215
|
+
"如果缺少 `slug`,询问稳定的工作流 slug。",
|
|
216
|
+
"如果缺少 `summary`,询问一个简短规划摘要。",
|
|
217
|
+
],
|
|
218
|
+
fallback_command="cgc-plan",
|
|
219
|
+
),
|
|
220
|
+
build_mcp_first_command_template(
|
|
221
|
+
filename="cgc-build.md",
|
|
222
|
+
description="执行 CodeCGC 功能开发步骤",
|
|
223
|
+
argument_hint="[参数]",
|
|
224
|
+
primary_tool="codecgc.build",
|
|
225
|
+
direct_rules=[
|
|
226
|
+
"调用前提取 `slug`。",
|
|
227
|
+
"映射可选执行字段,如 `step_number`、`checklist_file`、`audit_root`、`timeout_seconds`、`session_id`、`dry_run`。",
|
|
228
|
+
],
|
|
229
|
+
missing_rules=[
|
|
230
|
+
"如果缺少 `slug`,询问目标功能工作流的 slug。",
|
|
231
|
+
],
|
|
232
|
+
fallback_command="cgc-build",
|
|
233
|
+
),
|
|
234
|
+
build_mcp_first_command_template(
|
|
235
|
+
filename="cgc-fix.md",
|
|
236
|
+
description="执行 CodeCGC 问题修复步骤",
|
|
237
|
+
argument_hint="[参数]",
|
|
238
|
+
primary_tool="codecgc.fix",
|
|
239
|
+
direct_rules=[
|
|
240
|
+
"调用前提取 `slug`。",
|
|
241
|
+
"映射可选执行字段,如 `step_number`、`checklist_file`、`audit_root`、`timeout_seconds`、`session_id`、`dry_run`。",
|
|
242
|
+
],
|
|
243
|
+
missing_rules=[
|
|
244
|
+
"如果缺少 `slug`,询问目标问题工作流的 slug。",
|
|
245
|
+
],
|
|
246
|
+
fallback_command="cgc-fix",
|
|
247
|
+
),
|
|
248
|
+
build_mcp_first_command_template(
|
|
249
|
+
filename="cgc-test.md",
|
|
250
|
+
description="执行 CodeCGC 测试步骤",
|
|
251
|
+
argument_hint="[参数]",
|
|
252
|
+
primary_tool="codecgc.test",
|
|
253
|
+
direct_rules=[
|
|
254
|
+
"调用前提取 `flow` 和 `slug`。",
|
|
255
|
+
"映射可选执行字段,如 `step_number`、`checklist_file`、`audit_root`、`timeout_seconds`、`session_id`、`dry_run`。",
|
|
256
|
+
],
|
|
257
|
+
missing_rules=[
|
|
258
|
+
"如果缺少 `flow`,询问该测试属于 `feature` 还是 `issue` 工作流。",
|
|
259
|
+
"如果缺少 `slug`,询问目标工作流 slug。",
|
|
260
|
+
],
|
|
261
|
+
fallback_command="cgc-test",
|
|
262
|
+
),
|
|
263
|
+
build_mcp_first_command_template(
|
|
264
|
+
filename="cgc-review.md",
|
|
265
|
+
description="审核一份 CodeCGC 执行审计结果",
|
|
266
|
+
argument_hint="[参数]",
|
|
267
|
+
primary_tool="codecgc.review",
|
|
268
|
+
direct_rules=[
|
|
269
|
+
"调用前提取 `audit_file` 和 `decision`。",
|
|
270
|
+
"如果用户明确提供,映射可选字段 `risk`、`next_step`、`force`。",
|
|
271
|
+
],
|
|
272
|
+
missing_rules=[
|
|
273
|
+
"如果缺少 `audit_file`,询问审计 JSON 路径。",
|
|
274
|
+
"如果缺少 `decision`,询问审核结论是 `accepted` 还是 `changes-requested`。",
|
|
275
|
+
],
|
|
276
|
+
fallback_command="cgc-review",
|
|
277
|
+
),
|
|
278
|
+
build_mcp_first_command_template(
|
|
279
|
+
filename="cgc-route.md",
|
|
280
|
+
description="为 CodeCGC 工作流推荐下一条命令",
|
|
281
|
+
argument_hint="[参数]",
|
|
282
|
+
primary_tool="codecgc.route",
|
|
283
|
+
direct_rules=[
|
|
284
|
+
"调用前提取 `flow` 和 `slug`。",
|
|
285
|
+
"当用户已经知道目标工作流,只想得到下一步推荐动作时,使用这个命令。",
|
|
286
|
+
],
|
|
287
|
+
missing_rules=[
|
|
288
|
+
"如果缺少 `flow`,询问工作流是 `feature` 还是 `issue`。",
|
|
289
|
+
"如果缺少 `slug`,询问工作流 slug。",
|
|
290
|
+
],
|
|
291
|
+
fallback_command="cgc-route",
|
|
292
|
+
),
|
|
293
|
+
build_mcp_first_command_template(
|
|
294
|
+
filename="cgc-history.md",
|
|
295
|
+
description="查看最近的 CodeCGC 工作流历史",
|
|
296
|
+
argument_hint="[参数]",
|
|
297
|
+
primary_tool="codecgc.history",
|
|
298
|
+
direct_rules=[
|
|
299
|
+
"映射可选历史筛选字段,如 `flow`、`status`、`last`、`include_fixtures`。",
|
|
300
|
+
"如果没有提供筛选条件,就使用默认历史查询。",
|
|
301
|
+
],
|
|
302
|
+
fallback_command="cgc-history",
|
|
303
|
+
),
|
|
304
|
+
build_mcp_first_command_template(
|
|
305
|
+
filename="cgc-package-audit.md",
|
|
306
|
+
description="审计 CodeCGC 发布包运行时内容",
|
|
307
|
+
argument_hint="[参数]",
|
|
308
|
+
primary_tool="codecgc.package_audit",
|
|
309
|
+
direct_rules=[
|
|
310
|
+
"当用户明确要求 `summary` 或 `json` 时,映射 `format`。",
|
|
311
|
+
"该命令用于发布包和运行时完整性检查。",
|
|
312
|
+
],
|
|
313
|
+
fallback_command="cgc-package-audit",
|
|
314
|
+
),
|
|
315
|
+
build_mcp_first_command_template(
|
|
316
|
+
filename="cgc-external-audit.md",
|
|
317
|
+
description="审计外部 MCP 能力注册与接入状态",
|
|
318
|
+
argument_hint="[参数]",
|
|
319
|
+
primary_tool="codecgc.external_audit",
|
|
320
|
+
direct_rules=[
|
|
321
|
+
"映射可选字段 `workspace` 和 `format`。",
|
|
322
|
+
"该命令用于外部能力策略与注册检查。",
|
|
323
|
+
],
|
|
324
|
+
fallback_command="cgc-external-audit",
|
|
325
|
+
),
|
|
326
|
+
build_mcp_first_command_template(
|
|
327
|
+
filename="cgc-release-readiness.md",
|
|
328
|
+
description="运行 CodeCGC 发布就绪检查",
|
|
329
|
+
argument_hint="[参数]",
|
|
330
|
+
primary_tool="codecgc.release_readiness",
|
|
331
|
+
direct_rules=[
|
|
332
|
+
"映射可选字段 `workspace` 和 `format`。",
|
|
333
|
+
"该命令用于联合检查发布、维护和运维就绪状态。",
|
|
334
|
+
],
|
|
335
|
+
fallback_command="cgc-release-readiness",
|
|
336
|
+
),
|
|
337
|
+
build_mcp_first_command_template(
|
|
338
|
+
filename="cgc-lifecycle.md",
|
|
339
|
+
description="审计 CodeCGC 生命周期覆盖情况",
|
|
340
|
+
argument_hint="[参数]",
|
|
341
|
+
primary_tool="codecgc.lifecycle",
|
|
342
|
+
direct_rules=[
|
|
343
|
+
"当用户明确要求 `summary` 或 `json` 时,映射 `format`。",
|
|
344
|
+
"该命令用于检查 roadmap、workflow、execution 的生命周期覆盖情况。",
|
|
345
|
+
],
|
|
346
|
+
fallback_command="cgc-lifecycle",
|
|
347
|
+
),
|
|
348
|
+
]
|
|
349
|
+
)
|
|
350
|
+
return templates
|
|
175
351
|
|
|
176
352
|
|
|
177
353
|
def write_custom_command_files(target_dir: Path, bin_dir: Path) -> list[str]:
|
|
@@ -219,6 +395,33 @@ def merge_hook_settings(current: dict[str, Any], command_text: str) -> tuple[dic
|
|
|
219
395
|
return current, True
|
|
220
396
|
|
|
221
397
|
|
|
398
|
+
def merge_permission_settings(current: dict[str, Any], allow_rules: list[str]) -> tuple[dict[str, Any], bool]:
|
|
399
|
+
permissions = current.get("permissions")
|
|
400
|
+
changed = False
|
|
401
|
+
|
|
402
|
+
if not isinstance(permissions, dict):
|
|
403
|
+
permissions = {}
|
|
404
|
+
current["permissions"] = permissions
|
|
405
|
+
changed = True
|
|
406
|
+
|
|
407
|
+
allow = permissions.get("allow")
|
|
408
|
+
if not isinstance(allow, list):
|
|
409
|
+
allow = []
|
|
410
|
+
permissions["allow"] = allow
|
|
411
|
+
changed = True
|
|
412
|
+
|
|
413
|
+
existing = {str(item).strip() for item in allow if str(item).strip()}
|
|
414
|
+
for rule in allow_rules:
|
|
415
|
+
normalized = str(rule).strip()
|
|
416
|
+
if not normalized or normalized in existing:
|
|
417
|
+
continue
|
|
418
|
+
allow.append(normalized)
|
|
419
|
+
existing.add(normalized)
|
|
420
|
+
changed = True
|
|
421
|
+
|
|
422
|
+
return current, changed
|
|
423
|
+
|
|
424
|
+
|
|
222
425
|
def write_json_file(path: Path, payload: dict[str, Any]) -> Path:
|
|
223
426
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
224
427
|
path.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
@@ -276,6 +479,17 @@ def settings_have_hook_command(settings: dict[str, Any], command_text: str) -> b
|
|
|
276
479
|
return False
|
|
277
480
|
|
|
278
481
|
|
|
482
|
+
def settings_have_allowed_tools(settings: dict[str, Any], allow_rules: list[str]) -> bool:
|
|
483
|
+
permissions = settings.get("permissions")
|
|
484
|
+
if not isinstance(permissions, dict):
|
|
485
|
+
return False
|
|
486
|
+
allow = permissions.get("allow")
|
|
487
|
+
if not isinstance(allow, list):
|
|
488
|
+
return False
|
|
489
|
+
existing = {str(item).strip() for item in allow if str(item).strip()}
|
|
490
|
+
return all(str(rule).strip() in existing for rule in allow_rules)
|
|
491
|
+
|
|
492
|
+
|
|
279
493
|
def build_workspace_hook_command(workspace_paths: dict[str, Path]) -> str:
|
|
280
494
|
return "powershell -ExecutionPolicy Bypass -File .claude/hooks/route-edit.ps1"
|
|
281
495
|
|
|
@@ -310,8 +524,11 @@ def install_local_runtime(override_workspace: str = "") -> dict[str, Any]:
|
|
|
310
524
|
settings,
|
|
311
525
|
build_workspace_hook_command(workspace_paths),
|
|
312
526
|
)
|
|
527
|
+
merged_settings, permissions_changed = merge_permission_settings(merged_settings, DEFAULT_ALLOWED_TOOLS)
|
|
313
528
|
if settings_changed or not workspace_paths["settings"].exists():
|
|
314
529
|
write_json_file(workspace_paths["settings"], merged_settings)
|
|
530
|
+
elif permissions_changed:
|
|
531
|
+
write_json_file(workspace_paths["settings"], merged_settings)
|
|
315
532
|
|
|
316
533
|
if PROJECT_HOOK_PATH.resolve() != workspace_paths["hook_script"].resolve():
|
|
317
534
|
shutil.copyfile(PROJECT_HOOK_PATH, workspace_paths["hook_script"])
|
|
@@ -337,6 +554,7 @@ def install_local_runtime(override_workspace: str = "") -> dict[str, Any]:
|
|
|
337
554
|
"Repository-local MCP config was synced from the executor registry.",
|
|
338
555
|
"Project-local model-routing.yaml was synchronized and preserves custom path blocks.",
|
|
339
556
|
"Claude pre-edit guardrail hook was synchronized into the target workspace.",
|
|
557
|
+
"Claude MCP tool permissions were merged into project .claude/settings.json.",
|
|
340
558
|
"Project-local Claude slash commands were synchronized into .claude/commands.",
|
|
341
559
|
"This mode prepares project-level integration surfaces for the selected workspace.",
|
|
342
560
|
],
|
|
@@ -352,6 +570,7 @@ def preview_user_install(override_root: str = "") -> dict[str, Any]:
|
|
|
352
570
|
user_paths = get_user_claude_paths(override_root)
|
|
353
571
|
user_settings = load_json_file(user_paths["settings"])
|
|
354
572
|
merged_settings, settings_changed = merge_hook_settings(user_settings, build_user_hook_command(user_paths))
|
|
573
|
+
merged_settings, permissions_changed = merge_permission_settings(merged_settings, DEFAULT_ALLOWED_TOOLS)
|
|
355
574
|
mcp_config = build_mcp_config()
|
|
356
575
|
recommended_next_action = f"cgc-install --mode user --user-root {shell_quote(str(user_paths['root']))}"
|
|
357
576
|
summary = build_mode_summary_payload(
|
|
@@ -372,7 +591,7 @@ def preview_user_install(override_root: str = "") -> dict[str, Any]:
|
|
|
372
591
|
"commands_dir": str(user_paths["commands_dir"]),
|
|
373
592
|
},
|
|
374
593
|
"would_write": {
|
|
375
|
-
"settings_changed": settings_changed or not user_paths["settings"].exists(),
|
|
594
|
+
"settings_changed": settings_changed or permissions_changed or not user_paths["settings"].exists(),
|
|
376
595
|
"mcp_changed": True,
|
|
377
596
|
"hook_changed": True,
|
|
378
597
|
"commands_changed": True,
|
|
@@ -384,6 +603,7 @@ def preview_user_install(override_root: str = "") -> dict[str, Any]:
|
|
|
384
603
|
"notes": [
|
|
385
604
|
"This mode does not modify user-level Claude files.",
|
|
386
605
|
"Use this preview to inspect the future user-level integration surface.",
|
|
606
|
+
"The preview includes MCP tool allow rules for codecgc, codex, and gemini servers.",
|
|
387
607
|
"Current CodeCGC product policy still defaults to project-local installation.",
|
|
388
608
|
],
|
|
389
609
|
"summary": summary,
|
|
@@ -398,6 +618,7 @@ def install_user_runtime(override_root: str = "") -> dict[str, Any]:
|
|
|
398
618
|
|
|
399
619
|
settings = load_json_file(user_paths["settings"])
|
|
400
620
|
merged_settings, settings_changed = merge_hook_settings(settings, build_user_hook_command(user_paths))
|
|
621
|
+
merged_settings, permissions_changed = merge_permission_settings(merged_settings, DEFAULT_ALLOWED_TOOLS)
|
|
401
622
|
write_json_file(user_paths["settings"], merged_settings)
|
|
402
623
|
write_json_file(user_paths["mcp"], build_mcp_config())
|
|
403
624
|
shutil.copyfile(PROJECT_HOOK_PATH, user_paths["hook_script"])
|
|
@@ -420,7 +641,7 @@ def install_user_runtime(override_root: str = "") -> dict[str, Any]:
|
|
|
420
641
|
"commands_dir": str(user_paths["commands_dir"]),
|
|
421
642
|
},
|
|
422
643
|
"changes": {
|
|
423
|
-
"settings_changed": settings_changed or not user_paths["settings"].exists(),
|
|
644
|
+
"settings_changed": settings_changed or permissions_changed or not user_paths["settings"].exists(),
|
|
424
645
|
"mcp_changed": True,
|
|
425
646
|
"hook_changed": True,
|
|
426
647
|
"commands_changed": True,
|
|
@@ -429,6 +650,7 @@ def install_user_runtime(override_root: str = "") -> dict[str, Any]:
|
|
|
429
650
|
"notes": [
|
|
430
651
|
"User-level Claude integration files were written to the selected root.",
|
|
431
652
|
"The user-level hook script was copied from the project hook source.",
|
|
653
|
+
"MCP tool allow rules were merged into ~/.claude/settings.json.",
|
|
432
654
|
"User-level Claude slash commands were written to ~/.claude/commands.",
|
|
433
655
|
"This mode is explicit and should be used only when a broader Claude integration surface is intended.",
|
|
434
656
|
],
|
|
@@ -521,6 +743,7 @@ def collect_project_status(workspace_paths: dict[str, Path]) -> dict[str, Any]:
|
|
|
521
743
|
routing_exists = workspace_paths["routing_file"].exists()
|
|
522
744
|
|
|
523
745
|
hook_registered = settings_have_hook_command(current_settings, expected_hook_command)
|
|
746
|
+
permissions_registered = settings_have_allowed_tools(current_settings, DEFAULT_ALLOWED_TOOLS)
|
|
524
747
|
mcp_matches = current_mcp == expected_mcp if workspace_paths["mcp"].exists() else False
|
|
525
748
|
hook_file_matches = current_hook_text == expected_hook_text if workspace_paths["hook_script"].exists() else False
|
|
526
749
|
|
|
@@ -531,6 +754,8 @@ def collect_project_status(workspace_paths: dict[str, Path]) -> dict[str, Any]:
|
|
|
531
754
|
missing.append("mcp_json")
|
|
532
755
|
if not hook_registered:
|
|
533
756
|
missing.append("claude_settings_hook")
|
|
757
|
+
if not permissions_registered:
|
|
758
|
+
missing.append("claude_settings_permissions")
|
|
534
759
|
if not hook_file_matches:
|
|
535
760
|
missing.append("hook_script")
|
|
536
761
|
|
|
@@ -546,11 +771,13 @@ def collect_project_status(workspace_paths: dict[str, Path]) -> dict[str, Any]:
|
|
|
546
771
|
"hook_exists": workspace_paths["hook_script"].exists(),
|
|
547
772
|
"mcp_matches_expected": mcp_matches,
|
|
548
773
|
"hook_registered": hook_registered,
|
|
774
|
+
"permissions_registered": permissions_registered,
|
|
549
775
|
"hook_file_matches_expected": hook_file_matches,
|
|
550
776
|
"ready": ready,
|
|
551
777
|
"missing_or_outdated": missing,
|
|
552
778
|
"recommended_command": "" if ready else build_workspace_install_command(workspace_paths["root"]),
|
|
553
779
|
"hook_expected": {"hooks": build_hook_payload(expected_hook_command)},
|
|
780
|
+
"permissions_expected": {"permissions": {"allow": DEFAULT_ALLOWED_TOOLS}},
|
|
554
781
|
}
|
|
555
782
|
|
|
556
783
|
|
|
@@ -563,6 +790,7 @@ def collect_user_status(user_paths: dict[str, Path]) -> dict[str, Any]:
|
|
|
563
790
|
current_hook_text = load_text_file(user_paths["hook_script"])
|
|
564
791
|
|
|
565
792
|
hook_registered = settings_have_hook_command(current_settings, expected_hook_command)
|
|
793
|
+
permissions_registered = settings_have_allowed_tools(current_settings, DEFAULT_ALLOWED_TOOLS)
|
|
566
794
|
mcp_matches = current_mcp == expected_mcp if user_paths["mcp"].exists() else False
|
|
567
795
|
hook_file_matches = current_hook_text == expected_hook_text if user_paths["hook_script"].exists() else False
|
|
568
796
|
|
|
@@ -571,6 +799,8 @@ def collect_user_status(user_paths: dict[str, Path]) -> dict[str, Any]:
|
|
|
571
799
|
missing.append("mcp_json")
|
|
572
800
|
if not hook_registered:
|
|
573
801
|
missing.append("claude_settings_hook")
|
|
802
|
+
if not permissions_registered:
|
|
803
|
+
missing.append("claude_settings_permissions")
|
|
574
804
|
if not hook_file_matches:
|
|
575
805
|
missing.append("hook_script")
|
|
576
806
|
|
|
@@ -585,6 +815,7 @@ def collect_user_status(user_paths: dict[str, Path]) -> dict[str, Any]:
|
|
|
585
815
|
"hook_exists": user_paths["hook_script"].exists(),
|
|
586
816
|
"mcp_matches_expected": mcp_matches,
|
|
587
817
|
"hook_registered": hook_registered,
|
|
818
|
+
"permissions_registered": permissions_registered,
|
|
588
819
|
"hook_file_matches_expected": hook_file_matches,
|
|
589
820
|
"ready": ready,
|
|
590
821
|
"missing_or_outdated": missing,
|
|
@@ -673,18 +904,33 @@ def build_pip_install_command(python_command: str, requirement: str) -> str:
|
|
|
673
904
|
|
|
674
905
|
def build_local_editable_install_command(python_command: str) -> str:
|
|
675
906
|
runtime_command = python_command.strip() or sys.executable
|
|
907
|
+
codecgcmcp_path = WORKSPACE / "codecgcmcp"
|
|
676
908
|
codexmcp_path = WORKSPACE / "codexmcp"
|
|
677
909
|
geminimcp_path = WORKSPACE / "geminimcp"
|
|
910
|
+
if not (codecgcmcp_path / "pyproject.toml").exists():
|
|
911
|
+
return ""
|
|
678
912
|
if not (codexmcp_path / "pyproject.toml").exists():
|
|
679
913
|
return ""
|
|
680
914
|
if not (geminimcp_path / "pyproject.toml").exists():
|
|
681
915
|
return ""
|
|
682
916
|
return (
|
|
683
|
-
f"{shell_quote(runtime_command)} -m pip install -e {shell_quote(str(
|
|
917
|
+
f"{shell_quote(runtime_command)} -m pip install -e {shell_quote(str(codecgcmcp_path))} "
|
|
918
|
+
f"-e {shell_quote(str(codexmcp_path))} "
|
|
684
919
|
f"-e {shell_quote(str(geminimcp_path))}"
|
|
685
920
|
)
|
|
686
921
|
|
|
687
922
|
|
|
923
|
+
def format_bool_zh(value: Any) -> str:
|
|
924
|
+
return "是" if bool(value) else "否"
|
|
925
|
+
|
|
926
|
+
|
|
927
|
+
def format_list_zh(items: Any, empty_text: str = "无") -> str:
|
|
928
|
+
if not isinstance(items, list):
|
|
929
|
+
return empty_text
|
|
930
|
+
values = [str(item).strip() for item in items if str(item).strip()]
|
|
931
|
+
return "、".join(values) if values else empty_text
|
|
932
|
+
|
|
933
|
+
|
|
688
934
|
def classify_doctor_failures(
|
|
689
935
|
checks: list[dict[str, Any]],
|
|
690
936
|
configured_python_command: str,
|
|
@@ -753,6 +999,25 @@ def classify_doctor_failures(
|
|
|
753
999
|
)
|
|
754
1000
|
continue
|
|
755
1001
|
|
|
1002
|
+
if name == "python_runtime_import_probe_codecgcmcp":
|
|
1003
|
+
if configured_python_missing:
|
|
1004
|
+
continue
|
|
1005
|
+
if "No module named 'codecgcmcp'" in detail or 'No module named "codecgcmcp"' in detail:
|
|
1006
|
+
add_failure(
|
|
1007
|
+
"codecgcmcp-package-missing",
|
|
1008
|
+
"当前解释器无法导入本地 `codecgcmcp` 包。",
|
|
1009
|
+
"确认当前安装包已包含 `codecgcmcp/src`;仓库开发环境可执行本地 editable install,安装产物则应重新安装 CodeCGC 包。",
|
|
1010
|
+
editable_install_command,
|
|
1011
|
+
)
|
|
1012
|
+
else:
|
|
1013
|
+
add_failure(
|
|
1014
|
+
"codecgcmcp-runtime-broken",
|
|
1015
|
+
"`codecgcmcp` 启动入口存在,但当前运行时仍无法导入。",
|
|
1016
|
+
"仓库开发环境可先重装本地编排器包;若你使用的是已安装产物,则优先重新安装 CodeCGC,再检查编排器源码是否缺失或损坏。",
|
|
1017
|
+
editable_install_command,
|
|
1018
|
+
)
|
|
1019
|
+
continue
|
|
1020
|
+
|
|
756
1021
|
if name == "python_runtime_import_probe_codexmcp":
|
|
757
1022
|
if configured_python_missing:
|
|
758
1023
|
continue
|
|
@@ -866,11 +1131,18 @@ def collect_doctor_status(override_workspace: str = "") -> dict[str, Any]:
|
|
|
866
1131
|
|
|
867
1132
|
runtime_probe_command = configured_python_command if configured_python_command else (python_command or sys.executable)
|
|
868
1133
|
runtime_env = dict(os.environ)
|
|
869
|
-
combined_pythonpath = os.pathsep.join(
|
|
1134
|
+
combined_pythonpath = os.pathsep.join(
|
|
1135
|
+
[
|
|
1136
|
+
str(WORKSPACE / "scripts"),
|
|
1137
|
+
str(WORKSPACE / "codecgcmcp" / "src"),
|
|
1138
|
+
str(WORKSPACE / "codexmcp" / "src"),
|
|
1139
|
+
str(WORKSPACE / "geminimcp" / "src"),
|
|
1140
|
+
]
|
|
1141
|
+
)
|
|
870
1142
|
existing_pythonpath = runtime_env.get("PYTHONPATH", "").strip()
|
|
871
1143
|
runtime_env["PYTHONPATH"] = f"{combined_pythonpath}{os.pathsep}{existing_pythonpath}" if existing_pythonpath else combined_pythonpath
|
|
872
1144
|
|
|
873
|
-
for module_name in ("mcp", "codexmcp.cli", "geminimcp.cli"):
|
|
1145
|
+
for module_name in ("mcp", "codecgcmcp.cli", "codexmcp.cli", "geminimcp.cli"):
|
|
874
1146
|
probe = probe_python_import(runtime_probe_command, module_name, runtime_env)
|
|
875
1147
|
checks.append(
|
|
876
1148
|
{
|
|
@@ -972,12 +1244,12 @@ def main() -> int:
|
|
|
972
1244
|
lines = [
|
|
973
1245
|
f"- 工作区: {result.get('workspace', '')}",
|
|
974
1246
|
f"- 范围: {summary.get('scope', '')}",
|
|
975
|
-
f"- 项目级就绪: {
|
|
976
|
-
f"- 用户级就绪: {
|
|
1247
|
+
f"- 项目级就绪: {format_bool_zh(summary.get('project_ready'))}",
|
|
1248
|
+
f"- 用户级就绪: {format_bool_zh(summary.get('user_ready'))}",
|
|
977
1249
|
f"- 策略: {summary.get('default_policy', '')}",
|
|
978
1250
|
f"- 摘要: {summary.get('human_summary', '')}",
|
|
979
|
-
f"-
|
|
980
|
-
f"-
|
|
1251
|
+
f"- 项目级缺失项: {format_list_zh(project.get('missing_or_outdated', []))}",
|
|
1252
|
+
f"- 用户级缺失项: {format_list_zh(user.get('missing_or_outdated', []))}",
|
|
981
1253
|
]
|
|
982
1254
|
recommended_project = str(summary.get("recommended_project_command", "")).strip()
|
|
983
1255
|
recommended_user = str(summary.get("recommended_user_command", "")).strip()
|
|
@@ -992,11 +1264,11 @@ def main() -> int:
|
|
|
992
1264
|
lines = [
|
|
993
1265
|
f"- 工作区: {result.get('workspace', '')}",
|
|
994
1266
|
f"- 范围: {summary.get('scope', '')}",
|
|
995
|
-
f"- 就绪: {
|
|
1267
|
+
f"- 就绪: {format_bool_zh(summary.get('ready'))}",
|
|
996
1268
|
f"- 摘要: {summary.get('human_summary', '')}",
|
|
997
1269
|
]
|
|
998
1270
|
failed_checks = summary.get("failed_checks", [])
|
|
999
|
-
lines.append(f"- 失败检查项: {
|
|
1271
|
+
lines.append(f"- 失败检查项: {format_list_zh(failed_checks)}")
|
|
1000
1272
|
failure_categories = summary.get("failure_categories", [])
|
|
1001
1273
|
for item in failure_categories:
|
|
1002
1274
|
if not isinstance(item, dict):
|
|
@@ -1013,7 +1285,7 @@ def main() -> int:
|
|
|
1013
1285
|
fix_command = str(summary.get("recommended_fix_command", "")).strip()
|
|
1014
1286
|
runtime_fix_command = str(summary.get("recommended_runtime_fix_command", "")).strip()
|
|
1015
1287
|
next_actions = [item for item in [runtime_fix_command, fix_command] if item]
|
|
1016
|
-
print(render_summary_block("CodeCGC
|
|
1288
|
+
print(render_summary_block("CodeCGC 自检", lines, next_actions))
|
|
1017
1289
|
return 0 if result.get("success") else 1
|
|
1018
1290
|
|
|
1019
1291
|
if args.format == "summary" and args.mode in {"local", "user-dry-run", "user"}:
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import os
|
|
1
2
|
import json
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
|
|
@@ -8,16 +9,32 @@ WORKSPACE = Path(__file__).resolve().parents[1]
|
|
|
8
9
|
MCP_CONFIG_PATH = WORKSPACE / ".mcp.json"
|
|
9
10
|
|
|
10
11
|
|
|
12
|
+
def build_runtime_pythonpath(*extra_paths: Path) -> str:
|
|
13
|
+
paths = [str(WORKSPACE / "scripts"), *(str(path) for path in extra_paths)]
|
|
14
|
+
return os.pathsep.join(paths)
|
|
15
|
+
|
|
16
|
+
|
|
11
17
|
def build_mcp_config() -> dict:
|
|
12
18
|
registry = build_executor_registry()
|
|
13
19
|
servers: dict[str, dict] = {}
|
|
14
20
|
|
|
21
|
+
servers["codecgc"] = {
|
|
22
|
+
"command": str(next(iter(registry.values()))["python_command"]),
|
|
23
|
+
"args": ["-m", "codecgcmcp.cli"],
|
|
24
|
+
"env": {
|
|
25
|
+
"PYTHONPATH": build_runtime_pythonpath(WORKSPACE / "codecgcmcp" / "src"),
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
|
|
15
29
|
for config in registry.values():
|
|
16
30
|
servers[str(config["mcp_server_name"])] = {
|
|
17
31
|
"command": str(config["python_command"]),
|
|
18
32
|
"args": ["-m", str(config["python_module"])],
|
|
19
33
|
"env": {
|
|
20
|
-
"PYTHONPATH":
|
|
34
|
+
"PYTHONPATH": build_runtime_pythonpath(
|
|
35
|
+
WORKSPACE / "codecgcmcp" / "src",
|
|
36
|
+
Path(str(config["pythonpath"])),
|
|
37
|
+
),
|
|
21
38
|
},
|
|
22
39
|
}
|
|
23
40
|
|