@hunyed15/codecgc 0.2.2 → 0.2.5
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 +32 -10
- package/bin/cgc-build.js +1 -1
- package/bin/cgc-doctor.js +1 -1
- package/bin/cgc-entry.js +1 -1
- package/bin/cgc-explain.js +4 -0
- package/bin/cgc-external-audit.js +1 -1
- package/bin/cgc-external-status.js +1 -1
- package/bin/cgc-fix.js +1 -1
- package/bin/cgc-history.js +1 -1
- package/bin/cgc-init.js +1 -1
- package/bin/cgc-lifecycle.js +1 -1
- package/bin/cgc-package-audit.js +1 -1
- package/bin/cgc-plan.js +1 -1
- package/bin/cgc-release-readiness.js +1 -1
- package/bin/cgc-review.js +1 -1
- package/bin/cgc-route.js +1 -1
- package/bin/cgc-start.js +1 -1
- package/bin/cgc-status.js +1 -1
- package/bin/cgc-test.js +1 -1
- package/bin/codecgc.js +45 -2
- package/codecgc/cgc-onboard/SKILL.md +1 -1
- package/codecgc/reference/execution-model.md +1 -1
- package/codecgc/reference/policy-routing.md +1 -2
- package/codecgc/reference/project-structure.md +3 -3
- package/codecgc/reference/quickstart.md +1 -1
- package/codecgc/reference/shared-conventions.md +1 -1
- package/codecgc/templates/project/CLAUDE.md +214 -0
- package/codecgc/templates/project/claude/hooks/edit-guard.js +194 -0
- package/codecgc/templates/project/claude/settings.local.json +17 -4
- package/mcp/codexmcp/src/codexmcp/server.py +38 -4
- package/mcp/geminimcp/src/geminimcp/server.py +1 -1
- package/package.json +3 -2
- package/scripts/audit_codecgc_package_runtime.py +1 -1
- package/scripts/codecgc_error_catalog.py +172 -0
- package/scripts/codecgc_error_formatter.py +124 -0
- package/scripts/codecgc_flow_control.py +11 -0
- package/scripts/codecgc_runtime/console.py +9 -0
- package/scripts/codecgc_runtime/workflow_runtime.py +18 -0
- package/scripts/explain_codecgc_error.py +71 -0
- package/scripts/install_codecgc.py +92 -39
- package/scripts/postinstall_codecgc.js +23 -5
- package/.claude/hooks/route-edit.ps1 +0 -87
- package/codecgc/templates/project/claude/hooks/route-edit.ps1 +0 -87
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import os
|
|
4
5
|
import sys
|
|
5
6
|
from typing import Any
|
|
6
7
|
|
|
@@ -20,6 +21,14 @@ def configure_utf8_stdio() -> None:
|
|
|
20
21
|
|
|
21
22
|
def print_json(payload: dict[str, Any], *, file: Any | None = None) -> None:
|
|
22
23
|
target = file or sys.stdout
|
|
24
|
+
|
|
25
|
+
# Apply error formatting if this is an error result
|
|
26
|
+
if not payload.get("success") and "error_display" not in payload:
|
|
27
|
+
error_level = os.environ.get("CODECGC_ERROR_LEVEL", "summary")
|
|
28
|
+
if error_level in {"summary", "detail", "debug"}:
|
|
29
|
+
from codecgc_error_formatter import format_error_output
|
|
30
|
+
payload = format_error_output(payload, level=error_level)
|
|
31
|
+
|
|
23
32
|
text = json.dumps(payload, ensure_ascii=False, indent=2)
|
|
24
33
|
target.write(text)
|
|
25
34
|
target.write("\n")
|
|
@@ -14,6 +14,21 @@ WORKSPACE = PACKAGE_ROOT
|
|
|
14
14
|
PROJECT_WORKSPACE = PROJECT_ROOT
|
|
15
15
|
SCRIPTS_DIR = WORKSPACE / "scripts"
|
|
16
16
|
|
|
17
|
+
_SCRIPT_OPERATION_NAMES = {
|
|
18
|
+
"plan_codecgc_workflow.py": "规划工作流",
|
|
19
|
+
"route_codecgc_workflow.py": "路由检查",
|
|
20
|
+
"run_codecgc_build.py": "构建执行",
|
|
21
|
+
"run_codecgc_fix.py": "修复执行",
|
|
22
|
+
"run_codecgc_test.py": "测试执行",
|
|
23
|
+
"review_codecgc_workflow.py": "审核调度",
|
|
24
|
+
"build_codecgc_task.py": "任务构建",
|
|
25
|
+
"write_codecgc_decision.py": "写入决策",
|
|
26
|
+
"write_codecgc_learning.py": "写入经验",
|
|
27
|
+
"write_codecgc_architecture.py": "写入架构",
|
|
28
|
+
"write_codecgc_requirement.py": "写入需求",
|
|
29
|
+
"init_codecgc_workflow.py": "初始化工作流",
|
|
30
|
+
}
|
|
31
|
+
|
|
17
32
|
|
|
18
33
|
def build_script_command(script_name: str, *args: str) -> list[str]:
|
|
19
34
|
return [sys.executable, str(SCRIPTS_DIR / script_name), *args]
|
|
@@ -63,10 +78,13 @@ def run_json_script(script_name: str, *args: str) -> dict[str, Any]:
|
|
|
63
78
|
parsed.setdefault("returncode", completed.returncode)
|
|
64
79
|
return parsed
|
|
65
80
|
|
|
81
|
+
operation = _SCRIPT_OPERATION_NAMES.get(script_name, script_name)
|
|
66
82
|
return {
|
|
67
83
|
"success": False,
|
|
68
84
|
"returncode": completed.returncode,
|
|
69
85
|
"stdout": stdout,
|
|
70
86
|
"stderr": stderr,
|
|
71
87
|
"error": f"{script_name} failed with exit code {completed.returncode}.",
|
|
88
|
+
"human_error": f"{operation}步骤执行失败(内部脚本异常退出)。",
|
|
89
|
+
"suggestion": "请稍后重试。如果多次失败,运行 cgc-doctor 检查环境配置。",
|
|
72
90
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""cgc-explain: Explain CodeCGC error codes and provide actionable suggestions.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
cgc-explain <error_code>
|
|
5
|
+
cgc-explain --list
|
|
6
|
+
"""
|
|
7
|
+
import argparse
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
from codecgc_console_io import configure_utf8_stdio
|
|
11
|
+
from codecgc_console_io import print_json
|
|
12
|
+
from codecgc_error_catalog import explain_error
|
|
13
|
+
from codecgc_error_catalog import format_explanation
|
|
14
|
+
from codecgc_error_catalog import list_error_codes
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
18
|
+
parser = argparse.ArgumentParser(
|
|
19
|
+
description="Explain CodeCGC error codes and provide actionable suggestions."
|
|
20
|
+
)
|
|
21
|
+
parser.add_argument(
|
|
22
|
+
"error_code",
|
|
23
|
+
nargs="?",
|
|
24
|
+
help="Error code to explain (e.g., executor-crash, scope-error)",
|
|
25
|
+
)
|
|
26
|
+
parser.add_argument(
|
|
27
|
+
"--list",
|
|
28
|
+
action="store_true",
|
|
29
|
+
help="List all available error codes",
|
|
30
|
+
)
|
|
31
|
+
parser.add_argument(
|
|
32
|
+
"--format",
|
|
33
|
+
choices=["json", "summary"],
|
|
34
|
+
default="summary",
|
|
35
|
+
help="Output format",
|
|
36
|
+
)
|
|
37
|
+
return parser
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def main() -> int:
|
|
41
|
+
configure_utf8_stdio()
|
|
42
|
+
parser = build_parser()
|
|
43
|
+
args = parser.parse_args()
|
|
44
|
+
|
|
45
|
+
if args.list:
|
|
46
|
+
codes = list_error_codes()
|
|
47
|
+
if args.format == "json":
|
|
48
|
+
print_json({"success": True, "error_codes": codes})
|
|
49
|
+
else:
|
|
50
|
+
print("可用的错误代码:")
|
|
51
|
+
for code in codes:
|
|
52
|
+
print(f" - {code}")
|
|
53
|
+
print("\n使用 cgc-explain <error_code> 查看详细说明")
|
|
54
|
+
return 0
|
|
55
|
+
|
|
56
|
+
if not args.error_code:
|
|
57
|
+
parser.print_help()
|
|
58
|
+
return 1
|
|
59
|
+
|
|
60
|
+
explanation = explain_error(args.error_code)
|
|
61
|
+
|
|
62
|
+
if args.format == "json":
|
|
63
|
+
print_json({"success": True, "explanation": explanation})
|
|
64
|
+
else:
|
|
65
|
+
print(format_explanation(explanation))
|
|
66
|
+
|
|
67
|
+
return 0
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
if __name__ == "__main__":
|
|
71
|
+
sys.exit(main())
|
|
@@ -21,9 +21,10 @@ from sync_codecgc_mcp_config import write_mcp_config
|
|
|
21
21
|
WORKSPACE = PACKAGE_ROOT
|
|
22
22
|
PROJECT_ROUTING_PATH = WORKSPACE / "model-routing.yaml"
|
|
23
23
|
PROJECT_TEMPLATES_DIR = WORKSPACE / "codecgc" / "templates" / "project"
|
|
24
|
-
PROJECT_HOOK_PATH = PROJECT_TEMPLATES_DIR / "claude" / "hooks" / "
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
PROJECT_HOOK_PATH = PROJECT_TEMPLATES_DIR / "claude" / "hooks" / "edit-guard.js"
|
|
25
|
+
PROJECT_CLAUDE_MD_TEMPLATE = PROJECT_TEMPLATES_DIR / "CLAUDE.md"
|
|
26
|
+
CLAUDE_MD_MARKER = "<!-- codecgc:claude-md:v1 -->"
|
|
27
|
+
EDIT_GUARDRAIL_MATCHER = "Edit|Write|MultiEdit"
|
|
27
28
|
PROJECT_ONBOARDING_RELATIVE_PATH = "codecgc/START_HERE.md"
|
|
28
29
|
PROJECT_ONBOARDING_MARKER = "<!-- codecgc:onboarding:v1 -->"
|
|
29
30
|
CLAUDE_SETTINGS_TEMPLATE = PROJECT_TEMPLATES_DIR / "claude" / "settings.local.json"
|
|
@@ -38,7 +39,7 @@ DEFAULT_HOOKS = {
|
|
|
38
39
|
"hooks": [
|
|
39
40
|
{
|
|
40
41
|
"type": "command",
|
|
41
|
-
"command": "
|
|
42
|
+
"command": "node .claude/hooks/edit-guard.js",
|
|
42
43
|
}
|
|
43
44
|
],
|
|
44
45
|
}
|
|
@@ -83,7 +84,8 @@ def get_workspace_paths(override_workspace: str = "") -> dict[str, Path]:
|
|
|
83
84
|
"settings": claude_dir / "settings.local.json",
|
|
84
85
|
"legacy_settings": claude_dir / "settings.json",
|
|
85
86
|
"mcp": root / ".mcp.json",
|
|
86
|
-
"hook_script": hooks_dir / "
|
|
87
|
+
"hook_script": hooks_dir / "edit-guard.js",
|
|
88
|
+
"claude_md": claude_dir / "CLAUDE.md",
|
|
87
89
|
"commands_dir": claude_dir / "commands",
|
|
88
90
|
"routing_file": root / "model-routing.yaml",
|
|
89
91
|
"onboarding_file": root / PROJECT_ONBOARDING_RELATIVE_PATH,
|
|
@@ -132,6 +134,27 @@ def build_project_onboarding_text() -> str:
|
|
|
132
134
|
|
|
133
135
|
This file is generated by `cgc-init` for this project. It is intentionally project-relative and should not contain machine-specific install paths.
|
|
134
136
|
|
|
137
|
+
## Installation
|
|
138
|
+
|
|
139
|
+
**Recommended: Global Install + Project Init**
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
npm install -g @hunyed15/codecgc
|
|
143
|
+
cd your-project
|
|
144
|
+
cgc-init
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
After global installation, commands like `cgc-init`, `cgc-status`, `cgc-doctor` are available in any directory.
|
|
148
|
+
|
|
149
|
+
**Alternative: Project-Level Install**
|
|
150
|
+
|
|
151
|
+
For CI, Docker, or version isolation scenarios:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
npm install @hunyed15/codecgc
|
|
155
|
+
npx cgc-init
|
|
156
|
+
```
|
|
157
|
+
|
|
135
158
|
## First Run
|
|
136
159
|
|
|
137
160
|
Inside Claude:
|
|
@@ -165,7 +188,8 @@ cgc "新增一个登录页面,放在 src/components/LoginForm.tsx"
|
|
|
165
188
|
.mcp.json
|
|
166
189
|
model-routing.yaml
|
|
167
190
|
.claude/settings.local.json
|
|
168
|
-
.claude/hooks/
|
|
191
|
+
.claude/hooks/edit-guard.js
|
|
192
|
+
.claude/CLAUDE.md
|
|
169
193
|
.claude/commands/cgc*.md
|
|
170
194
|
.codex/codecgcrc.json
|
|
171
195
|
.gemini/policies/codecgc-policy.toml
|
|
@@ -250,10 +274,6 @@ def policy_file_is_valid(path: Path) -> bool:
|
|
|
250
274
|
return True
|
|
251
275
|
|
|
252
276
|
|
|
253
|
-
def _normalize_command_path_for_markdown(path: Path) -> str:
|
|
254
|
-
return str(path).replace("\\", "\\\\")
|
|
255
|
-
|
|
256
|
-
|
|
257
277
|
def build_mcp_first_command_template(
|
|
258
278
|
*,
|
|
259
279
|
filename: str,
|
|
@@ -559,7 +579,7 @@ def is_codecgc_route_edit_hook(hook: Any) -> bool:
|
|
|
559
579
|
if hook.get("type") != "command":
|
|
560
580
|
return False
|
|
561
581
|
command = str(hook.get("command", "")).replace("\\", "/").lower()
|
|
562
|
-
return "
|
|
582
|
+
return "edit-guard" in command
|
|
563
583
|
|
|
564
584
|
|
|
565
585
|
def merge_hook_settings(current: dict[str, Any], command_text: str) -> tuple[dict[str, Any], bool]:
|
|
@@ -578,7 +598,7 @@ def merge_hook_settings(current: dict[str, Any], command_text: str) -> tuple[dic
|
|
|
578
598
|
for item in list(pre_tool_use):
|
|
579
599
|
if not isinstance(item, dict):
|
|
580
600
|
continue
|
|
581
|
-
if item.get("matcher")
|
|
601
|
+
if item.get("matcher") != EDIT_GUARDRAIL_MATCHER:
|
|
582
602
|
continue
|
|
583
603
|
hook_list = item.get("hooks")
|
|
584
604
|
if not isinstance(hook_list, list):
|
|
@@ -796,32 +816,8 @@ def mcp_server_matches_runtime_shape(server_payload: dict[str, Any], spec: dict[
|
|
|
796
816
|
return True
|
|
797
817
|
|
|
798
818
|
|
|
799
|
-
def mcp_config_matches_runtime_shape(payload: dict[str, Any]) -> bool:
|
|
800
|
-
servers = payload.get("mcpServers")
|
|
801
|
-
if not isinstance(servers, dict):
|
|
802
|
-
return False
|
|
803
|
-
|
|
804
|
-
for server_name, spec in RUNTIME_MCP_SERVER_SPECS.items():
|
|
805
|
-
server_payload = servers.get(server_name)
|
|
806
|
-
if not isinstance(server_payload, dict):
|
|
807
|
-
return False
|
|
808
|
-
if not mcp_server_matches_runtime_shape(server_payload, spec):
|
|
809
|
-
return False
|
|
810
|
-
return True
|
|
811
|
-
|
|
812
|
-
|
|
813
819
|
def build_workspace_hook_command(workspace_paths: dict[str, Path]) -> str:
|
|
814
|
-
|
|
815
|
-
workspace_root_path = Path(workspace_paths["root"])
|
|
816
|
-
hook_script_path = workspace_paths.get("hook_script", workspace_root_path / ".claude" / "hooks" / "route-edit.ps1")
|
|
817
|
-
workspace_root = str(workspace_root_path).replace("'", "''")
|
|
818
|
-
hook_script = str(hook_script_path).replace("\\", "/").replace("'", "''")
|
|
819
|
-
return (
|
|
820
|
-
"powershell -ExecutionPolicy Bypass -Command "
|
|
821
|
-
f"\"$env:CODECGC_PACKAGE_ROOT='{package_root}'; "
|
|
822
|
-
f"$env:CODECGC_WORKSPACE_ROOT='{workspace_root}'; "
|
|
823
|
-
f"& '{hook_script}'\""
|
|
824
|
-
)
|
|
820
|
+
return "node .claude/hooks/edit-guard.js"
|
|
825
821
|
|
|
826
822
|
|
|
827
823
|
def build_mode_summary_payload(
|
|
@@ -841,6 +837,32 @@ def build_mode_summary_payload(
|
|
|
841
837
|
return summary
|
|
842
838
|
|
|
843
839
|
|
|
840
|
+
def install_project_claude_md(target_path: Path) -> str:
|
|
841
|
+
"""Install or append CodeCGC rules to .claude/CLAUDE.md.
|
|
842
|
+
|
|
843
|
+
- Does not exist: create with template content.
|
|
844
|
+
- Exists without marker: append template content.
|
|
845
|
+
- Exists with marker: skip (don't overwrite user modifications).
|
|
846
|
+
"""
|
|
847
|
+
if not PROJECT_CLAUDE_MD_TEMPLATE.exists():
|
|
848
|
+
return str(target_path)
|
|
849
|
+
|
|
850
|
+
template_content = PROJECT_CLAUDE_MD_TEMPLATE.read_text(encoding="utf-8")
|
|
851
|
+
|
|
852
|
+
if target_path.exists():
|
|
853
|
+
existing = target_path.read_text(encoding="utf-8")
|
|
854
|
+
if CLAUDE_MD_MARKER in existing:
|
|
855
|
+
return str(target_path)
|
|
856
|
+
# Append with separator
|
|
857
|
+
separator = "\n\n---\n\n" if not existing.endswith("\n\n") else ""
|
|
858
|
+
target_path.write_text(existing + separator + template_content, encoding="utf-8")
|
|
859
|
+
else:
|
|
860
|
+
target_path.parent.mkdir(parents=True, exist_ok=True)
|
|
861
|
+
target_path.write_text(template_content, encoding="utf-8")
|
|
862
|
+
|
|
863
|
+
return str(target_path)
|
|
864
|
+
|
|
865
|
+
|
|
844
866
|
def install_local_runtime(override_workspace: str = "") -> dict[str, Any]:
|
|
845
867
|
workspace_paths = get_workspace_paths(override_workspace)
|
|
846
868
|
mcp_path = write_mcp_config(workspace_paths["mcp"], workspace_paths["root"])
|
|
@@ -861,6 +883,7 @@ def install_local_runtime(override_workspace: str = "") -> dict[str, Any]:
|
|
|
861
883
|
shutil.copyfile(PROJECT_HOOK_PATH, workspace_paths["hook_script"])
|
|
862
884
|
written_commands = write_custom_command_files(workspace_paths["commands_dir"], WORKSPACE / "bin")
|
|
863
885
|
onboarding_file = write_project_onboarding_file(workspace_paths["root"])
|
|
886
|
+
claude_md_path = install_project_claude_md(workspace_paths["claude_md"])
|
|
864
887
|
|
|
865
888
|
summary = build_mode_summary_payload(
|
|
866
889
|
scope="项目级 Claude 与 MCP 集成面",
|
|
@@ -878,6 +901,7 @@ def install_local_runtime(override_workspace: str = "") -> dict[str, Any]:
|
|
|
878
901
|
"codex_policy": str(codex_policy_path),
|
|
879
902
|
"gemini_policy": str(gemini_policy_path),
|
|
880
903
|
"hook_script": str(workspace_paths["hook_script"]),
|
|
904
|
+
"claude_md": str(claude_md_path),
|
|
881
905
|
"commands_dir": str(workspace_paths["commands_dir"]),
|
|
882
906
|
"onboarding_file": str(onboarding_file),
|
|
883
907
|
"workflow_dirs": workflow_dirs,
|
|
@@ -887,7 +911,8 @@ def install_local_runtime(override_workspace: str = "") -> dict[str, Any]:
|
|
|
887
911
|
"Project-local model-routing.yaml was synchronized as the policy source of truth.",
|
|
888
912
|
"Project-local codecgc workflow directories were initialized.",
|
|
889
913
|
"Claude pre-edit guardrail hook was synchronized into the target workspace.",
|
|
890
|
-
"Claude project permissions were rendered
|
|
914
|
+
"Claude project permissions were rendered with whitelist (Edit/Write restricted to codecgc, .claude, docs, config).",
|
|
915
|
+
".claude/CLAUDE.md was installed with CodeCGC workflow rules and three-layer protection guidance.",
|
|
891
916
|
"Project-local Codex policy contract was synchronized into .codex/codecgcrc.json.",
|
|
892
917
|
"Project-local Gemini policy was synchronized into .gemini/policies/codecgc-policy.toml.",
|
|
893
918
|
"Project-local Claude slash commands were synchronized into .claude/commands.",
|
|
@@ -906,10 +931,29 @@ def build_doctor_fix_command(workspace_root: Path) -> str:
|
|
|
906
931
|
return f"cgc-init --workspace {shell_quote(str(workspace_root))}"
|
|
907
932
|
|
|
908
933
|
|
|
934
|
+
def is_global_npm_install() -> bool:
|
|
935
|
+
"""检测当前是否为全局安装的 CodeCGC"""
|
|
936
|
+
try:
|
|
937
|
+
# 检查是否在 npm 全局目录中
|
|
938
|
+
import sys
|
|
939
|
+
executable_path = Path(sys.executable).resolve()
|
|
940
|
+
# 简单启发式:如果在 node_modules/.bin 之外,可能是全局安装
|
|
941
|
+
# 更准确的方法是检查 cgc-init 命令是否在 PATH 中且不需要 npx
|
|
942
|
+
cgc_init_path = shutil.which("cgc-init")
|
|
943
|
+
if cgc_init_path:
|
|
944
|
+
# 如果能直接找到 cgc-init,说明是全局安装
|
|
945
|
+
return True
|
|
946
|
+
except Exception:
|
|
947
|
+
pass
|
|
948
|
+
return False
|
|
949
|
+
|
|
950
|
+
|
|
909
951
|
def build_install_mode_summary(result: dict[str, Any]) -> str:
|
|
910
952
|
mode = str(result.get("mode", "")).strip()
|
|
911
953
|
|
|
912
954
|
if mode == "local":
|
|
955
|
+
is_global = is_global_npm_install()
|
|
956
|
+
|
|
913
957
|
lines = [
|
|
914
958
|
f"- 工作区: {result.get('workspace', '')}",
|
|
915
959
|
"- 范围: 项目级 Claude 与 MCP 集成面",
|
|
@@ -922,8 +966,17 @@ def build_install_mode_summary(result: dict[str, Any]) -> str:
|
|
|
922
966
|
f"- Hook 脚本: {result.get('hook_script', '')}",
|
|
923
967
|
f"- Slash Commands: {result.get('commands_dir', '')}",
|
|
924
968
|
f"- 新手入口: {result.get('onboarding_file', '')}",
|
|
925
|
-
"- 说明: 可选外部能力如 MemOS 不由 cgc-init 自动写入;如需启用,请在 Claude 中单独配置官方 MCP。",
|
|
926
969
|
]
|
|
970
|
+
|
|
971
|
+
if is_global:
|
|
972
|
+
lines.append("- 安装方式: 全局安装(推荐)")
|
|
973
|
+
lines.append("- 说明: 初始化完成。现在可以在 Claude 中使用 /cgc 或在命令行使用 cgc 命令。")
|
|
974
|
+
else:
|
|
975
|
+
lines.append("- 安装方式: 项目级安装")
|
|
976
|
+
lines.append("- 说明: 项目级安装需要通过 npx 调用命令。推荐全局安装:npm install -g @hunyed15/codecgc")
|
|
977
|
+
|
|
978
|
+
lines.append("- 提示: 可选外部能力如 MemOS 不由 cgc-init 自动写入;如需启用,请在 Claude 中单独配置官方 MCP。")
|
|
979
|
+
|
|
927
980
|
next_actions = [
|
|
928
981
|
"cgc-start",
|
|
929
982
|
"cgc-status",
|
|
@@ -12,13 +12,31 @@ function isGlobalInstall() {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
function main() {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const isGlobal = isGlobalInstall();
|
|
16
|
+
|
|
17
|
+
if (isGlobal) {
|
|
18
|
+
console.log("");
|
|
19
|
+
console.log("✓ CodeCGC 已全局安装");
|
|
20
|
+
console.log("");
|
|
21
|
+
console.log("下一步:");
|
|
22
|
+
console.log(" 1. 进入项目目录:cd your-project");
|
|
23
|
+
console.log(" 2. 初始化项目:cgc-init");
|
|
24
|
+
console.log(" 3. 查看状态:cgc-status");
|
|
25
|
+
console.log("");
|
|
26
|
+
console.log("初始化后,可在 Claude 中使用 /cgc 或在命令行使用 cgc 命令。");
|
|
27
|
+
console.log("");
|
|
28
|
+
} else {
|
|
29
|
+
console.log("");
|
|
30
|
+
console.log("✓ CodeCGC 已安装到项目");
|
|
31
|
+
console.log("");
|
|
32
|
+
console.log("下一步:");
|
|
33
|
+
console.log(" 运行 'npx cgc-init' 来初始化项目");
|
|
34
|
+
console.log("");
|
|
35
|
+
console.log("提示:推荐全局安装以获得更好的体验:");
|
|
36
|
+
console.log(" npm install -g @hunyed15/codecgc");
|
|
37
|
+
console.log("");
|
|
17
38
|
}
|
|
18
39
|
|
|
19
|
-
console.warn("[codecgc] Global CLI installed.");
|
|
20
|
-
console.warn("[codecgc] CodeCGC no longer writes Claude user-level files during npm install.");
|
|
21
|
-
console.warn("[codecgc] Run `cgc-init` from each target project to create project-local .mcp.json, .claude/, and model-routing.yaml.");
|
|
22
40
|
return 0;
|
|
23
41
|
}
|
|
24
42
|
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
$ErrorActionPreference = "Stop"
|
|
2
|
-
|
|
3
|
-
function Write-Approve {
|
|
4
|
-
$payload = @{
|
|
5
|
-
decision = "approve"
|
|
6
|
-
} | ConvertTo-Json -Compress
|
|
7
|
-
[Console]::Out.Write($payload)
|
|
8
|
-
exit 0
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function Write-Deny($reason) {
|
|
12
|
-
$payload = @{
|
|
13
|
-
decision = "deny"
|
|
14
|
-
reason = $reason
|
|
15
|
-
} | ConvertTo-Json -Compress
|
|
16
|
-
[Console]::Out.Write($payload)
|
|
17
|
-
exit 0
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
$inputJson = [Console]::In.ReadToEnd()
|
|
21
|
-
if ([string]::IsNullOrWhiteSpace($inputJson)) {
|
|
22
|
-
Write-Approve
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
$configuredPackageRoot = [Environment]::GetEnvironmentVariable("CODECGC_PACKAGE_ROOT")
|
|
26
|
-
if (-not [string]::IsNullOrWhiteSpace($configuredPackageRoot)) {
|
|
27
|
-
$packageRoot = Resolve-Path $configuredPackageRoot
|
|
28
|
-
} else {
|
|
29
|
-
$packageRoot = Resolve-Path (Join-Path $PSScriptRoot "..\..")
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
$configuredWorkspaceRoot = [Environment]::GetEnvironmentVariable("CODECGC_WORKSPACE_ROOT")
|
|
33
|
-
if (-not [string]::IsNullOrWhiteSpace($configuredWorkspaceRoot)) {
|
|
34
|
-
$workspaceRoot = Resolve-Path $configuredWorkspaceRoot
|
|
35
|
-
} else {
|
|
36
|
-
$workspaceRoot = Resolve-Path (Get-Location)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
$policyScript = Join-Path $packageRoot "scripts\codecgc_policy.py"
|
|
40
|
-
$routingFile = Join-Path $workspaceRoot "model-routing.yaml"
|
|
41
|
-
$mcpConfigFile = Join-Path $workspaceRoot ".mcp.json"
|
|
42
|
-
|
|
43
|
-
if (-not (Test-Path $policyScript)) {
|
|
44
|
-
Write-Deny "CodeCGC: policy checker is missing: $policyScript"
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
$pythonCommand = [Environment]::GetEnvironmentVariable("CODECGC_PYTHON_COMMAND")
|
|
48
|
-
if ([string]::IsNullOrWhiteSpace($pythonCommand) -and (Test-Path $mcpConfigFile)) {
|
|
49
|
-
try {
|
|
50
|
-
$mcpConfig = Get-Content -Raw $mcpConfigFile | ConvertFrom-Json
|
|
51
|
-
$pythonCommand = [string]$mcpConfig.mcpServers.codecgc.command
|
|
52
|
-
} catch {
|
|
53
|
-
$pythonCommand = ""
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
if ([string]::IsNullOrWhiteSpace($pythonCommand)) {
|
|
57
|
-
$pythonCommand = "python"
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
$psi = New-Object System.Diagnostics.ProcessStartInfo
|
|
61
|
-
$psi.FileName = $pythonCommand
|
|
62
|
-
$escapedPolicyScript = $policyScript.Replace('"', '\"')
|
|
63
|
-
$escapedRoutingFile = $routingFile.Replace('"', '\"')
|
|
64
|
-
$psi.Arguments = "`"$escapedPolicyScript`" --hook-check --actor claude --operation write --routing-file `"$escapedRoutingFile`""
|
|
65
|
-
$psi.RedirectStandardInput = $true
|
|
66
|
-
$psi.RedirectStandardOutput = $true
|
|
67
|
-
$psi.RedirectStandardError = $true
|
|
68
|
-
$psi.UseShellExecute = $false
|
|
69
|
-
$psi.CreateNoWindow = $true
|
|
70
|
-
|
|
71
|
-
$process = [System.Diagnostics.Process]::Start($psi)
|
|
72
|
-
$process.StandardInput.Write($inputJson)
|
|
73
|
-
$process.StandardInput.Close()
|
|
74
|
-
$stdout = $process.StandardOutput.ReadToEnd()
|
|
75
|
-
$stderr = $process.StandardError.ReadToEnd()
|
|
76
|
-
$process.WaitForExit()
|
|
77
|
-
|
|
78
|
-
if ($process.ExitCode -ne 0) {
|
|
79
|
-
$detail = if ([string]::IsNullOrWhiteSpace($stderr)) { $stdout } else { $stderr }
|
|
80
|
-
Write-Deny "CodeCGC: policy checker failed. $detail"
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if ([string]::IsNullOrWhiteSpace($stdout)) {
|
|
84
|
-
Write-Approve
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
[Console]::Out.Write($stdout)
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
$ErrorActionPreference = "Stop"
|
|
2
|
-
|
|
3
|
-
function Write-Approve {
|
|
4
|
-
$payload = @{
|
|
5
|
-
decision = "approve"
|
|
6
|
-
} | ConvertTo-Json -Compress
|
|
7
|
-
[Console]::Out.Write($payload)
|
|
8
|
-
exit 0
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function Write-Deny($reason) {
|
|
12
|
-
$payload = @{
|
|
13
|
-
decision = "deny"
|
|
14
|
-
reason = $reason
|
|
15
|
-
} | ConvertTo-Json -Compress
|
|
16
|
-
[Console]::Out.Write($payload)
|
|
17
|
-
exit 0
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
$inputJson = [Console]::In.ReadToEnd()
|
|
21
|
-
if ([string]::IsNullOrWhiteSpace($inputJson)) {
|
|
22
|
-
Write-Approve
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
$configuredPackageRoot = [Environment]::GetEnvironmentVariable("CODECGC_PACKAGE_ROOT")
|
|
26
|
-
if (-not [string]::IsNullOrWhiteSpace($configuredPackageRoot)) {
|
|
27
|
-
$packageRoot = Resolve-Path $configuredPackageRoot
|
|
28
|
-
} else {
|
|
29
|
-
$packageRoot = Resolve-Path (Join-Path $PSScriptRoot "..\..")
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
$configuredWorkspaceRoot = [Environment]::GetEnvironmentVariable("CODECGC_WORKSPACE_ROOT")
|
|
33
|
-
if (-not [string]::IsNullOrWhiteSpace($configuredWorkspaceRoot)) {
|
|
34
|
-
$workspaceRoot = Resolve-Path $configuredWorkspaceRoot
|
|
35
|
-
} else {
|
|
36
|
-
$workspaceRoot = Resolve-Path (Get-Location)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
$policyScript = Join-Path $packageRoot "scripts\codecgc_policy.py"
|
|
40
|
-
$routingFile = Join-Path $workspaceRoot "model-routing.yaml"
|
|
41
|
-
$mcpConfigFile = Join-Path $workspaceRoot ".mcp.json"
|
|
42
|
-
|
|
43
|
-
if (-not (Test-Path $policyScript)) {
|
|
44
|
-
Write-Deny "CodeCGC: policy checker is missing: $policyScript"
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
$pythonCommand = [Environment]::GetEnvironmentVariable("CODECGC_PYTHON_COMMAND")
|
|
48
|
-
if ([string]::IsNullOrWhiteSpace($pythonCommand) -and (Test-Path $mcpConfigFile)) {
|
|
49
|
-
try {
|
|
50
|
-
$mcpConfig = Get-Content -Raw $mcpConfigFile | ConvertFrom-Json
|
|
51
|
-
$pythonCommand = [string]$mcpConfig.mcpServers.codecgc.command
|
|
52
|
-
} catch {
|
|
53
|
-
$pythonCommand = ""
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
if ([string]::IsNullOrWhiteSpace($pythonCommand)) {
|
|
57
|
-
$pythonCommand = "python"
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
$psi = New-Object System.Diagnostics.ProcessStartInfo
|
|
61
|
-
$psi.FileName = $pythonCommand
|
|
62
|
-
$escapedPolicyScript = $policyScript.Replace('"', '\"')
|
|
63
|
-
$escapedRoutingFile = $routingFile.Replace('"', '\"')
|
|
64
|
-
$psi.Arguments = "`"$escapedPolicyScript`" --hook-check --actor claude --operation write --routing-file `"$escapedRoutingFile`""
|
|
65
|
-
$psi.RedirectStandardInput = $true
|
|
66
|
-
$psi.RedirectStandardOutput = $true
|
|
67
|
-
$psi.RedirectStandardError = $true
|
|
68
|
-
$psi.UseShellExecute = $false
|
|
69
|
-
$psi.CreateNoWindow = $true
|
|
70
|
-
|
|
71
|
-
$process = [System.Diagnostics.Process]::Start($psi)
|
|
72
|
-
$process.StandardInput.Write($inputJson)
|
|
73
|
-
$process.StandardInput.Close()
|
|
74
|
-
$stdout = $process.StandardOutput.ReadToEnd()
|
|
75
|
-
$stderr = $process.StandardError.ReadToEnd()
|
|
76
|
-
$process.WaitForExit()
|
|
77
|
-
|
|
78
|
-
if ($process.ExitCode -ne 0) {
|
|
79
|
-
$detail = if ([string]::IsNullOrWhiteSpace($stderr)) { $stdout } else { $stderr }
|
|
80
|
-
Write-Deny "CodeCGC: policy checker failed. $detail"
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if ([string]::IsNullOrWhiteSpace($stdout)) {
|
|
84
|
-
Write-Approve
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
[Console]::Out.Write($stdout)
|