@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,465 @@
|
|
|
1
|
+
"""FastMCP server implementation for the Gemini MCP project."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import queue
|
|
8
|
+
import subprocess
|
|
9
|
+
import threading
|
|
10
|
+
import time
|
|
11
|
+
import uuid
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Annotated, Any, Dict, Generator, List, Optional
|
|
14
|
+
|
|
15
|
+
from mcp.server.fastmcp import FastMCP
|
|
16
|
+
from pydantic import BeforeValidator, Field
|
|
17
|
+
import shutil
|
|
18
|
+
|
|
19
|
+
mcp = FastMCP("Gemini MCP Server-from guda.studio")
|
|
20
|
+
|
|
21
|
+
# Mirror of model-routing.yaml backend_paths — keep these hints in sync with
|
|
22
|
+
# route-edit.ps1 and codexmcp/server.py FRONTEND_PATH_HINTS.
|
|
23
|
+
BACKEND_PATH_HINTS = (
|
|
24
|
+
"apps/api/",
|
|
25
|
+
"server/",
|
|
26
|
+
"src/server/",
|
|
27
|
+
"src/services/",
|
|
28
|
+
"src/repositories/",
|
|
29
|
+
"backend/",
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
BACKEND_FILE_HINTS = (
|
|
33
|
+
".py",
|
|
34
|
+
".go",
|
|
35
|
+
".rs",
|
|
36
|
+
".java",
|
|
37
|
+
".kt",
|
|
38
|
+
".cs",
|
|
39
|
+
".rb",
|
|
40
|
+
".php",
|
|
41
|
+
".sql",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _normalize_path_text(path_value: Path | str) -> str:
|
|
46
|
+
"""Normalize a path-like value to a forward-slash string."""
|
|
47
|
+
return str(path_value).replace("\\", "/").strip()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _is_probably_backend_path(path_value: Path | str) -> bool:
|
|
51
|
+
"""Best-effort check to keep frontend-only Gemini tasks away from backend files."""
|
|
52
|
+
normalized = _normalize_path_text(path_value).lower().lstrip("./")
|
|
53
|
+
if any(hint in normalized for hint in BACKEND_PATH_HINTS):
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
suffix = Path(normalized).suffix.lower()
|
|
57
|
+
if suffix in BACKEND_FILE_HINTS:
|
|
58
|
+
return True
|
|
59
|
+
|
|
60
|
+
return False
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _build_frontend_task_prompt(
|
|
64
|
+
task_summary: str,
|
|
65
|
+
target_paths: List[Path],
|
|
66
|
+
constraints: List[str],
|
|
67
|
+
acceptance_criteria: List[str],
|
|
68
|
+
) -> str:
|
|
69
|
+
"""Build a constrained prompt for frontend implementation tasks."""
|
|
70
|
+
lines = [
|
|
71
|
+
"You are implementing a frontend-only coding task.",
|
|
72
|
+
"Stay strictly within the provided target paths.",
|
|
73
|
+
"Do not edit backend files, API layers, repositories, or server logic.",
|
|
74
|
+
"Preserve the existing product structure unless the task explicitly requires otherwise.",
|
|
75
|
+
"Return a concise implementation summary, followed by risks if any remain.",
|
|
76
|
+
"",
|
|
77
|
+
"Task summary:",
|
|
78
|
+
task_summary.strip(),
|
|
79
|
+
"",
|
|
80
|
+
"Target paths:",
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
lines.extend(f"- {_normalize_path_text(path)}" for path in target_paths)
|
|
84
|
+
|
|
85
|
+
if constraints:
|
|
86
|
+
lines.append("")
|
|
87
|
+
lines.append("Constraints:")
|
|
88
|
+
lines.extend(f"- {item.strip()}" for item in constraints if item.strip())
|
|
89
|
+
|
|
90
|
+
if acceptance_criteria:
|
|
91
|
+
lines.append("")
|
|
92
|
+
lines.append("Acceptance criteria:")
|
|
93
|
+
lines.extend(f"- {item.strip()}" for item in acceptance_criteria if item.strip())
|
|
94
|
+
|
|
95
|
+
return "\n".join(lines).strip()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _validate_frontend_target_paths(target_paths: List[Path]) -> tuple[bool, List[str], str]:
|
|
99
|
+
"""Validate frontend task target paths and return policy check notes."""
|
|
100
|
+
if not target_paths:
|
|
101
|
+
return False, [], "The `target_paths` field must contain at least one file or directory."
|
|
102
|
+
|
|
103
|
+
normalized_paths = [_normalize_path_text(path) for path in target_paths]
|
|
104
|
+
backend_hits = [
|
|
105
|
+
path_text
|
|
106
|
+
for path_text in normalized_paths
|
|
107
|
+
if _is_probably_backend_path(path_text)
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
policy_checks = [
|
|
111
|
+
"target_paths_present",
|
|
112
|
+
"frontend_scope_requested",
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
if backend_hits:
|
|
116
|
+
return (
|
|
117
|
+
False,
|
|
118
|
+
policy_checks,
|
|
119
|
+
"The frontend executor refused backend-like paths: "
|
|
120
|
+
+ ", ".join(backend_hits),
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
policy_checks.append("backend_boundary_check_passed")
|
|
124
|
+
return True, policy_checks, ""
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def run_shell_command(cmd: list[str], cwd: str | None = None) -> Generator[str, None, None]:
|
|
128
|
+
"""Execute a command and stream its output line-by-line.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
cmd: Command and arguments as a list (e.g., ["gemini", "-o", "stream-json", "--", "prompt"])
|
|
132
|
+
cwd: Working directory for the command
|
|
133
|
+
|
|
134
|
+
Yields:
|
|
135
|
+
Output lines from the command
|
|
136
|
+
"""
|
|
137
|
+
popen_cmd = cmd
|
|
138
|
+
|
|
139
|
+
gemini_path = shutil.which("gemini") or cmd[0]
|
|
140
|
+
popen_cmd[0] = gemini_path
|
|
141
|
+
|
|
142
|
+
# if os.name == "nt" and gemini_path.lower().endswith((".cmd", ".bat")):
|
|
143
|
+
# from subprocess import list2cmdline
|
|
144
|
+
# popen_cmd = ["cmd.exe", "/s", "/c", list2cmdline(cmd)]
|
|
145
|
+
|
|
146
|
+
process = subprocess.Popen(
|
|
147
|
+
popen_cmd,
|
|
148
|
+
shell=False,
|
|
149
|
+
stdin=subprocess.DEVNULL,
|
|
150
|
+
stdout=subprocess.PIPE,
|
|
151
|
+
stderr=subprocess.STDOUT,
|
|
152
|
+
universal_newlines=True,
|
|
153
|
+
encoding='utf-8',
|
|
154
|
+
cwd=cwd,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
output_queue: queue.Queue[str | None] = queue.Queue()
|
|
158
|
+
GRACEFUL_SHUTDOWN_DELAY = 0.3
|
|
159
|
+
|
|
160
|
+
def is_turn_completed(line: str) -> bool:
|
|
161
|
+
"""Check if the line indicates turn completion via JSON parsing."""
|
|
162
|
+
try:
|
|
163
|
+
data = json.loads(line)
|
|
164
|
+
return data.get("type") in {"turn.completed", "result"}
|
|
165
|
+
except (json.JSONDecodeError, AttributeError, TypeError):
|
|
166
|
+
return False
|
|
167
|
+
|
|
168
|
+
def read_output() -> None:
|
|
169
|
+
"""Read process output in a separate thread."""
|
|
170
|
+
if process.stdout:
|
|
171
|
+
for line in iter(process.stdout.readline, ""):
|
|
172
|
+
stripped = line.strip()
|
|
173
|
+
output_queue.put(stripped)
|
|
174
|
+
if is_turn_completed(stripped):
|
|
175
|
+
time.sleep(GRACEFUL_SHUTDOWN_DELAY)
|
|
176
|
+
process.terminate()
|
|
177
|
+
break
|
|
178
|
+
process.stdout.close()
|
|
179
|
+
output_queue.put(None)
|
|
180
|
+
|
|
181
|
+
thread = threading.Thread(target=read_output)
|
|
182
|
+
thread.start()
|
|
183
|
+
|
|
184
|
+
# Yield lines while process is running
|
|
185
|
+
while True:
|
|
186
|
+
try:
|
|
187
|
+
line = output_queue.get(timeout=0.5)
|
|
188
|
+
if line is None:
|
|
189
|
+
break
|
|
190
|
+
yield line
|
|
191
|
+
except queue.Empty:
|
|
192
|
+
if process.poll() is not None and not thread.is_alive():
|
|
193
|
+
break
|
|
194
|
+
|
|
195
|
+
try:
|
|
196
|
+
process.wait(timeout=5)
|
|
197
|
+
except subprocess.TimeoutExpired:
|
|
198
|
+
process.kill()
|
|
199
|
+
process.wait()
|
|
200
|
+
thread.join(timeout=5)
|
|
201
|
+
|
|
202
|
+
while not output_queue.empty():
|
|
203
|
+
try:
|
|
204
|
+
line = output_queue.get_nowait()
|
|
205
|
+
if line is not None:
|
|
206
|
+
yield line
|
|
207
|
+
except queue.Empty:
|
|
208
|
+
break
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _execute_gemini_session(
|
|
212
|
+
*,
|
|
213
|
+
prompt: str,
|
|
214
|
+
cd: Path,
|
|
215
|
+
sandbox: bool,
|
|
216
|
+
session_id: str,
|
|
217
|
+
return_all_messages: bool,
|
|
218
|
+
model: str,
|
|
219
|
+
) -> Dict[str, Any]:
|
|
220
|
+
"""Execute Gemini CLI and return the parsed MCP response payload."""
|
|
221
|
+
if not cd.exists():
|
|
222
|
+
return {
|
|
223
|
+
"success": False,
|
|
224
|
+
"error": f"The workspace root directory `{cd.absolute().as_posix()}` does not exist. Please check the path and try again.",
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if os.name == "nt":
|
|
228
|
+
prompt = windows_escape(prompt)
|
|
229
|
+
|
|
230
|
+
cmd = ["gemini", "--skip-trust", "--prompt", prompt, "-o", "stream-json"]
|
|
231
|
+
|
|
232
|
+
if sandbox:
|
|
233
|
+
cmd.extend(["--sandbox"])
|
|
234
|
+
|
|
235
|
+
if model:
|
|
236
|
+
cmd.extend(["--model", model])
|
|
237
|
+
|
|
238
|
+
if session_id:
|
|
239
|
+
cmd.extend(["--resume", session_id])
|
|
240
|
+
|
|
241
|
+
all_messages = []
|
|
242
|
+
agent_messages = ""
|
|
243
|
+
success = True
|
|
244
|
+
err_message = ""
|
|
245
|
+
thread_id: Optional[str] = None
|
|
246
|
+
|
|
247
|
+
for line in run_shell_command(cmd, cwd=cd.absolute().as_posix()):
|
|
248
|
+
try:
|
|
249
|
+
line_dict = json.loads(line.strip())
|
|
250
|
+
all_messages.append(line_dict)
|
|
251
|
+
item_type = line_dict.get("type", "")
|
|
252
|
+
item_role = line_dict.get("role", "")
|
|
253
|
+
if item_type == "message" and item_role == "assistant":
|
|
254
|
+
if (
|
|
255
|
+
"The --prompt (-p) flag has been deprecated and will be removed in a future version. Please use a positional argument for your prompt. See gemini --help for more information.\n"
|
|
256
|
+
in line_dict.get("content", "")
|
|
257
|
+
):
|
|
258
|
+
continue
|
|
259
|
+
agent_messages = agent_messages + line_dict.get("content", "")
|
|
260
|
+
if line_dict.get("session_id") is not None:
|
|
261
|
+
thread_id = line_dict.get("session_id")
|
|
262
|
+
except json.JSONDecodeError:
|
|
263
|
+
err_message += "\n\n[json decode error] " + line
|
|
264
|
+
continue
|
|
265
|
+
except Exception as error:
|
|
266
|
+
err_message += "\n\n[unexpected error] " + f"Unexpected error: {error}. Line: {line!r}"
|
|
267
|
+
break
|
|
268
|
+
|
|
269
|
+
if thread_id is None:
|
|
270
|
+
success = False
|
|
271
|
+
err_message = (
|
|
272
|
+
"Failed to get `SESSION_ID` from the gemini session. \n\n" + err_message
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
if success and len(agent_messages) == 0:
|
|
276
|
+
success = False
|
|
277
|
+
err_message = (
|
|
278
|
+
"Failed to retrieve `agent_messages` data from the Gemini session. This might be due to Gemini performing a tool call. You can continue using the `SESSION_ID` to proceed with the conversation. \n\n "
|
|
279
|
+
+ err_message
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
if success:
|
|
283
|
+
result: Dict[str, Any] = {
|
|
284
|
+
"success": True,
|
|
285
|
+
"SESSION_ID": thread_id,
|
|
286
|
+
"agent_messages": agent_messages,
|
|
287
|
+
}
|
|
288
|
+
else:
|
|
289
|
+
result = {"success": False, "error": err_message}
|
|
290
|
+
|
|
291
|
+
if return_all_messages:
|
|
292
|
+
result["all_messages"] = all_messages
|
|
293
|
+
|
|
294
|
+
return result
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def windows_escape(prompt):
|
|
298
|
+
"""
|
|
299
|
+
Windows 风格的字符串转义函数。
|
|
300
|
+
把常见特殊字符转义成 \\ 形式,适合命令行、JSON 或路径使用。
|
|
301
|
+
比如:\n 变成 \\n," 变成 \\"。
|
|
302
|
+
"""
|
|
303
|
+
# 先处理反斜杠,避免它干扰其他替换
|
|
304
|
+
result = prompt.replace("\\", "\\\\")
|
|
305
|
+
# 双引号,转义成 \",防止字符串边界乱套
|
|
306
|
+
result = result.replace('"', '\\"')
|
|
307
|
+
# 换行符,Windows 常用 \r\n,但我们分开转义
|
|
308
|
+
result = result.replace("\n", "\\n")
|
|
309
|
+
result = result.replace("\r", "\\r")
|
|
310
|
+
# 制表符,空格的“超级版”
|
|
311
|
+
result = result.replace("\t", "\\t")
|
|
312
|
+
# 其他常见:退格符(像按了后退键)、换页符(打印机跳页用)
|
|
313
|
+
result = result.replace("\b", "\\b")
|
|
314
|
+
result = result.replace("\f", "\\f")
|
|
315
|
+
# 如果有单引号,也转义下(不过 Windows 命令行不那么严格,但保险起见)
|
|
316
|
+
result = result.replace("'", "\\'")
|
|
317
|
+
|
|
318
|
+
return result
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
@mcp.tool(
|
|
322
|
+
name="gemini",
|
|
323
|
+
description="""
|
|
324
|
+
Invokes the Gemini CLI to execute AI-driven tasks, returning structured JSON events and a session identifier for conversation continuity.
|
|
325
|
+
|
|
326
|
+
**Return structure:**
|
|
327
|
+
- `success`: boolean indicating execution status
|
|
328
|
+
- `SESSION_ID`: unique identifier for resuming this conversation in future calls
|
|
329
|
+
- `agent_messages`: concatenated assistant response text
|
|
330
|
+
- `all_messages`: (optional) complete array of JSON events when `return_all_messages=True`
|
|
331
|
+
- `error`: error description when `success=False`
|
|
332
|
+
|
|
333
|
+
**Best practices:**
|
|
334
|
+
- Always capture and reuse `SESSION_ID` for multi-turn interactions
|
|
335
|
+
- Enable `sandbox` mode when file modifications should be isolated
|
|
336
|
+
- Use `return_all_messages` only when detailed execution traces are necessary (increases payload size)
|
|
337
|
+
- Only pass `model` when the user has explicitly requested a specific model
|
|
338
|
+
""",
|
|
339
|
+
meta={"version": "0.0.0", "author": "guda.studio"},
|
|
340
|
+
)
|
|
341
|
+
async def gemini(
|
|
342
|
+
PROMPT: Annotated[str, "Instruction for the task to send to gemini."],
|
|
343
|
+
cd: Annotated[Path, "Set the workspace root for gemini before executing the task."],
|
|
344
|
+
sandbox: Annotated[
|
|
345
|
+
bool,
|
|
346
|
+
Field(description="Run in sandbox mode. Defaults to `False`."),
|
|
347
|
+
] = False,
|
|
348
|
+
SESSION_ID: Annotated[
|
|
349
|
+
str,
|
|
350
|
+
"Resume the specified session of the gemini. Defaults to empty string, start a new session.",
|
|
351
|
+
] = "",
|
|
352
|
+
return_all_messages: Annotated[
|
|
353
|
+
bool,
|
|
354
|
+
"Return all messages (e.g. reasoning, tool calls, etc.) from the gemini session. Set to `False` by default, only the agent's final reply message is returned.",
|
|
355
|
+
] = False,
|
|
356
|
+
model: Annotated[
|
|
357
|
+
str,
|
|
358
|
+
"The model to use for the gemini session. This parameter is strictly prohibited unless explicitly specified by the user.",
|
|
359
|
+
] = "",
|
|
360
|
+
) -> Dict[str, Any]:
|
|
361
|
+
"""Execute a gemini CLI session and return the results."""
|
|
362
|
+
return _execute_gemini_session(
|
|
363
|
+
prompt=PROMPT,
|
|
364
|
+
cd=cd,
|
|
365
|
+
sandbox=sandbox,
|
|
366
|
+
session_id=SESSION_ID,
|
|
367
|
+
return_all_messages=return_all_messages,
|
|
368
|
+
model=model,
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
@mcp.tool(
|
|
373
|
+
name="implement_frontend_task",
|
|
374
|
+
description="""
|
|
375
|
+
Executes a frontend-only implementation task via Gemini with extra policy checks.
|
|
376
|
+
Use this tool when Claude has already completed planning/design and needs Gemini to
|
|
377
|
+
perform the actual frontend code changes inside a constrained path set.
|
|
378
|
+
""",
|
|
379
|
+
meta={"version": "0.1.0", "author": "CodeCGC"},
|
|
380
|
+
)
|
|
381
|
+
async def implement_frontend_task(
|
|
382
|
+
task_id: Annotated[str, "Stable task identifier for audit and review."],
|
|
383
|
+
task_summary: Annotated[str, "Frontend implementation summary prepared by the orchestrator."],
|
|
384
|
+
target_paths: Annotated[
|
|
385
|
+
List[Path],
|
|
386
|
+
Field(
|
|
387
|
+
description="Frontend paths Gemini is allowed to touch for this task.",
|
|
388
|
+
),
|
|
389
|
+
],
|
|
390
|
+
constraints: Annotated[
|
|
391
|
+
List[str],
|
|
392
|
+
Field(
|
|
393
|
+
description="Non-negotiable implementation constraints for this frontend task.",
|
|
394
|
+
),
|
|
395
|
+
] = [],
|
|
396
|
+
acceptance_criteria: Annotated[
|
|
397
|
+
List[str],
|
|
398
|
+
Field(
|
|
399
|
+
description="Acceptance criteria the implementation should satisfy.",
|
|
400
|
+
),
|
|
401
|
+
] = [],
|
|
402
|
+
cd: Annotated[Path, "Workspace root for the frontend task."] = Path("."),
|
|
403
|
+
SESSION_ID: Annotated[
|
|
404
|
+
str,
|
|
405
|
+
"Resume the specified Gemini session. Empty string starts a new session.",
|
|
406
|
+
] = "",
|
|
407
|
+
sandbox: Annotated[
|
|
408
|
+
bool,
|
|
409
|
+
Field(description="Run Gemini in sandbox mode. Defaults to `False`."),
|
|
410
|
+
] = False,
|
|
411
|
+
return_all_messages: Annotated[
|
|
412
|
+
bool,
|
|
413
|
+
"Return full Gemini event logs for debugging. Defaults to `False`.",
|
|
414
|
+
] = False,
|
|
415
|
+
model: Annotated[
|
|
416
|
+
str,
|
|
417
|
+
"Optional model override. Only use when explicitly requested by the user.",
|
|
418
|
+
] = "",
|
|
419
|
+
) -> Dict[str, Any]:
|
|
420
|
+
"""Execute a frontend-only Gemini task with CodeCGC policy checks."""
|
|
421
|
+
valid, policy_checks, validation_error = _validate_frontend_target_paths(target_paths)
|
|
422
|
+
if not valid:
|
|
423
|
+
return {
|
|
424
|
+
"success": False,
|
|
425
|
+
"task_id": task_id,
|
|
426
|
+
"policy_checks": policy_checks,
|
|
427
|
+
"error": validation_error,
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
prompt = _build_frontend_task_prompt(
|
|
431
|
+
task_summary=task_summary,
|
|
432
|
+
target_paths=target_paths,
|
|
433
|
+
constraints=constraints,
|
|
434
|
+
acceptance_criteria=acceptance_criteria,
|
|
435
|
+
)
|
|
436
|
+
result = _execute_gemini_session(
|
|
437
|
+
prompt=prompt,
|
|
438
|
+
cd=cd,
|
|
439
|
+
sandbox=sandbox,
|
|
440
|
+
session_id=SESSION_ID,
|
|
441
|
+
return_all_messages=return_all_messages,
|
|
442
|
+
model=model,
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
if not result.get("success"):
|
|
446
|
+
result["task_id"] = task_id
|
|
447
|
+
result["policy_checks"] = policy_checks
|
|
448
|
+
return result
|
|
449
|
+
|
|
450
|
+
return {
|
|
451
|
+
"success": True,
|
|
452
|
+
"task_id": task_id,
|
|
453
|
+
"SESSION_ID": result["SESSION_ID"],
|
|
454
|
+
"summary": result["agent_messages"],
|
|
455
|
+
"agent_messages": result["agent_messages"],
|
|
456
|
+
"changed_files": [_normalize_path_text(path) for path in target_paths],
|
|
457
|
+
"policy_checks": policy_checks + ["frontend_executor_completed"],
|
|
458
|
+
"risks": [],
|
|
459
|
+
**({"all_messages": result["all_messages"]} if return_all_messages and "all_messages" in result else {}),
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def run() -> None:
|
|
464
|
+
"""Start the MCP server over stdio transport."""
|
|
465
|
+
mcp.run(transport="stdio")
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
version: 1
|
|
2
|
+
|
|
3
|
+
frontend_paths:
|
|
4
|
+
- "apps/web/**"
|
|
5
|
+
- "src/components/**"
|
|
6
|
+
- "src/pages/**"
|
|
7
|
+
- "src/app/**"
|
|
8
|
+
- "src/styles/**"
|
|
9
|
+
- "web/**"
|
|
10
|
+
- "frontend/**"
|
|
11
|
+
|
|
12
|
+
backend_paths:
|
|
13
|
+
- "apps/api/**"
|
|
14
|
+
- "server/**"
|
|
15
|
+
- "src/server/**"
|
|
16
|
+
- "src/services/**"
|
|
17
|
+
- "src/repositories/**"
|
|
18
|
+
- "backend/**"
|
|
19
|
+
|
|
20
|
+
shared_paths:
|
|
21
|
+
- "packages/shared/**"
|
|
22
|
+
- "src/shared/**"
|
|
23
|
+
- "src/lib/**"
|
|
24
|
+
- "src/types/**"
|
|
25
|
+
|
|
26
|
+
rules:
|
|
27
|
+
frontend_executor: "geminimcp"
|
|
28
|
+
backend_executor: "codexmcp"
|
|
29
|
+
shared_policy: "split-first"
|
|
30
|
+
claude_role: "plan-review-accept-only"
|
package/package.json
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hunyed15/codecgc",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Claude-hosted multi-model workflow product shell for CodeCGC.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"bin": {
|
|
8
|
+
"cgc": "bin/cgc.js",
|
|
9
|
+
"cgc-install": "bin/cgc-install.js",
|
|
10
|
+
"cgc-status": "bin/cgc-status.js",
|
|
11
|
+
"cgc-doctor": "bin/cgc-doctor.js",
|
|
12
|
+
"cgc-package-audit": "bin/cgc-package-audit.js",
|
|
13
|
+
"cgc-external-audit": "bin/cgc-external-audit.js",
|
|
14
|
+
"cgc-release-readiness": "bin/cgc-release-readiness.js",
|
|
15
|
+
"cgc-lifecycle": "bin/cgc-lifecycle.js",
|
|
16
|
+
"cgc-history": "bin/cgc-history.js",
|
|
17
|
+
"cgc-entry": "bin/cgc-entry.js",
|
|
18
|
+
"cgc-plan": "bin/cgc-plan.js",
|
|
19
|
+
"cgc-build": "bin/cgc-build.js",
|
|
20
|
+
"cgc-fix": "bin/cgc-fix.js",
|
|
21
|
+
"cgc-test": "bin/cgc-test.js",
|
|
22
|
+
"cgc-review": "bin/cgc-review.js",
|
|
23
|
+
"cgc-route": "bin/cgc-route.js"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"cgc:help": "node bin/cgc.js --help",
|
|
27
|
+
"cgc:status": "node bin/cgc-status.js --format summary",
|
|
28
|
+
"cgc:doctor": "node bin/cgc-doctor.js --format summary",
|
|
29
|
+
"cgc:package-audit": "node bin/cgc-package-audit.js --format summary",
|
|
30
|
+
"cgc:external-audit": "node bin/cgc-external-audit.js --format summary",
|
|
31
|
+
"cgc:release-readiness": "node bin/cgc-release-readiness.js --format summary",
|
|
32
|
+
"cgc:lifecycle": "node bin/cgc-lifecycle.js --format summary",
|
|
33
|
+
"cgc:history": "node bin/cgc-history.js --status open --last 10 --format summary",
|
|
34
|
+
"cgc:audit-historical-audits": "python scripts/audit_codecgc_historical_audits.py --format summary",
|
|
35
|
+
"cgc:audit-review-policy": "python scripts/audit_codecgc_review_policy.py --artifact-class all --format summary",
|
|
36
|
+
"cgc:refresh-review-policy": "python scripts/refresh_codecgc_review_policy.py --artifact-class all --write",
|
|
37
|
+
"cgc:pack-dry-run": "npm pack --dry-run"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"bin/",
|
|
41
|
+
"scripts/*.py",
|
|
42
|
+
"scripts/README-codecgc-cli.md",
|
|
43
|
+
".claude/hooks/route-edit.ps1",
|
|
44
|
+
"codecgc/cgc/",
|
|
45
|
+
"codecgc/cgc-arch/",
|
|
46
|
+
"codecgc/cgc-build/",
|
|
47
|
+
"codecgc/cgc-decide/",
|
|
48
|
+
"codecgc/cgc-fix/",
|
|
49
|
+
"codecgc/cgc-learn/",
|
|
50
|
+
"codecgc/cgc-onboard/",
|
|
51
|
+
"codecgc/cgc-plan/",
|
|
52
|
+
"codecgc/cgc-refactor/",
|
|
53
|
+
"codecgc/cgc-req/",
|
|
54
|
+
"codecgc/cgc-review/",
|
|
55
|
+
"codecgc/cgc-roadmap/",
|
|
56
|
+
"codecgc/cgc-test/",
|
|
57
|
+
"codecgc/reference/",
|
|
58
|
+
"codexmcp/pyproject.toml",
|
|
59
|
+
"codexmcp/README.md",
|
|
60
|
+
"codexmcp/LICENSE",
|
|
61
|
+
"codexmcp/src/codexmcp/",
|
|
62
|
+
"geminimcp/pyproject.toml",
|
|
63
|
+
"geminimcp/README.md",
|
|
64
|
+
"geminimcp/src/geminimcp/",
|
|
65
|
+
"requirements.txt",
|
|
66
|
+
"INSTALLATION.md",
|
|
67
|
+
"model-routing.yaml",
|
|
68
|
+
"README.md",
|
|
69
|
+
"LICENSE"
|
|
70
|
+
],
|
|
71
|
+
"keywords": [
|
|
72
|
+
"claude",
|
|
73
|
+
"mcp",
|
|
74
|
+
"workflow",
|
|
75
|
+
"codex",
|
|
76
|
+
"gemini",
|
|
77
|
+
"multi-model"
|
|
78
|
+
],
|
|
79
|
+
"homepage": "https://github.com/hunyed15/CodeCGC",
|
|
80
|
+
"bugs": {
|
|
81
|
+
"url": "https://github.com/hunyed15/CodeCGC/issues"
|
|
82
|
+
},
|
|
83
|
+
"engines": {
|
|
84
|
+
"node": ">=20"
|
|
85
|
+
},
|
|
86
|
+
"repository": {
|
|
87
|
+
"type": "git",
|
|
88
|
+
"url": "git+https://github.com/hunyed15/CodeCGC.git"
|
|
89
|
+
}
|
|
90
|
+
}
|
package/requirements.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pyyaml>=6.0
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# CodeCGC CLI
|
|
2
|
+
|
|
3
|
+
## 用途
|
|
4
|
+
|
|
5
|
+
`codecgc_cli.py` 是 CodeCGC 的统一本地封装入口。
|
|
6
|
+
|
|
7
|
+
相比逐个调用底层脚本,它更适合作为仓库维护和调试时的统一入口。
|
|
8
|
+
|
|
9
|
+
## 常用命令
|
|
10
|
+
|
|
11
|
+
单入口调度:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
python scripts/codecgc_cli.py entry --request "新增一个登录页面,放在 src/components/LoginForm.tsx"
|
|
15
|
+
python scripts/codecgc_cli.py entry --request "继续刚刚的工作"
|
|
16
|
+
python scripts/codecgc_cli.py entry --request "现在下一步该做什么"
|
|
17
|
+
python scripts/codecgc_cli.py entry --mode new --flow feature --slug demo-login-ui --summary "Demo login UI feature" --target-path src/components/LoginForm.tsx --kind frontend
|
|
18
|
+
python scripts/codecgc_cli.py entry --mode continue --flow feature --slug 2026-05-01-demo-login-ui
|
|
19
|
+
python scripts/codecgc_cli.py entry --mode explain --slug 2026-05-01-demo-login-ui
|
|
20
|
+
python scripts/codecgc_cli.py entry --mode continue --latest
|
|
21
|
+
python scripts/codecgc_cli.py entry --mode continue --slug 2026-05-01-demo-login-ui --auto-dispatch --dry-run
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
规划并补齐工作流骨架:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
python scripts/codecgc_cli.py plan --flow feature --slug demo-login-ui --summary "Demo login UI feature" --target-path src/components/LoginForm.tsx --kind frontend
|
|
28
|
+
python scripts/codecgc_cli.py plan --flow issue --slug demo-sync-bug --summary "Demo sync bug fix" --target-path backend/session/continue.py --kind backend
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
支持结构化 `plan` 输入:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
python scripts/codecgc_cli.py plan --flow feature --slug demo-login-ui --summary "Demo login UI feature" --target-path src/components/LoginForm.tsx --kind frontend --goal "Allow users to sign in from a dedicated login form." --in-scope "Create the login form UI." --acceptance "Login form renders email and password fields."
|
|
35
|
+
python scripts/codecgc_cli.py plan --flow issue --slug demo-sync-bug --summary "Demo sync bug fix" --target-path backend/src/sync.py --kind backend --symptom "Sync stops on malformed record." --expected "Valid records still sync." --actual "Whole batch stops." --root-cause "Validation aborts the batch loop." --preferred-fix "Catch per-record validation errors." --acceptance "Malformed records are skipped and logged."
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
也支持更丰富的规划结构:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
python scripts/codecgc_cli.py plan --flow feature --slug demo-login-ui --summary "Demo login UI feature" --target-path src/components/LoginForm.tsx --kind frontend --goal "Allow users to sign in from a dedicated login form." --user-story "As a returning user, I want a focused login entry so I can sign in quickly." --context "This replaces an inline login panel on the landing page." --dependency "Auth API already exists." --assumption "No backend contract changes are needed." --validation "Render the form and submit against the existing auth endpoint." --rollback "Restore the previous inline login entry if the new screen fails review."
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
初始化工作流产物:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
python scripts/codecgc_cli.py init --flow feature --slug demo-login-ui --summary "Demo login UI feature"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
运行一个高层功能开发或问题修复步骤:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
python scripts/codecgc_cli.py build --slug 2026-05-01-demo-login-ui --step-number 1 --dry-run
|
|
54
|
+
python scripts/codecgc_cli.py fix --slug 2026-05-01-demo-sync-bug --step-number 1 --dry-run
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
精确执行一个步骤:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
python scripts/codecgc_cli.py exec --flow feature --slug 2026-05-01-demo-login-ui --step-number 1 --dry-run
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
写回审核结果:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
python scripts/codecgc_cli.py review --audit-file codecgc/execution/demo-login-ui-step-1.json --decision accepted
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
判断当前产物状态的下一步命令:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
python scripts/codecgc_cli.py route --flow feature --slug 2026-05-01-demo-login-ui
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 说明
|
|
76
|
+
|
|
77
|
+
优先把 `codecgc_cli.py` 当作维护和调试时的统一入口。
|
|
78
|
+
如果你想用一个明显的开始/继续/解释入口,优先用 `entry`。
|
|
79
|
+
日常工作流阶段优先使用 `plan/build/fix/review/route`。
|
|
80
|
+
只有在需要更底层控制时,才直接使用 `init/exec`。
|
|
81
|
+
|
|
82
|
+
`entry` 现已支持通过 `--request` 接收轻量意图优先请求。
|
|
83
|
+
|
|
84
|
+
`plan` 现在也会返回可机读的 `planning_status`:
|
|
85
|
+
|
|
86
|
+
- `ready-for-build`
|
|
87
|
+
- `ready-for-fix`
|
|
88
|
+
- `needs-clarification`
|
|
89
|
+
- `needs-roadmap`
|