@mcgrapeng/ccg 3.1.0 → 4.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.
@@ -3,22 +3,19 @@
3
3
  #
4
4
  # Usage:
5
5
  # curl -fsSL https://raw.githubusercontent.com/mcgrapeng/ccg/main/scripts/curl-install.sh | bash
6
- # curl -fsSL https://raw.githubusercontent.com/mcgrapeng/ccg/main/scripts/curl-install.sh | bash -s -- --version v3.0.0
6
+ # curl -fsSL ... | bash -s -- --version v3.2.0
7
7
  #
8
- # What it does:
9
- # 1. Downloads the requested release tarball from GitHub Releases (default: latest)
10
- # 2. Extracts ccg.sh + ccg.md into ~/.claude/commands/
11
- # 3. Runs preflight checks (Codex / Gemini / API key)
8
+ # Defaults to jsdelivr CDN for cross-region reliability (especially mainland China),
9
+ # falls back to raw.githubusercontent.com if jsdelivr is unreachable.
12
10
  #
13
- # No npm / Node.js required. Just bash + curl + tar.
11
+ # No npm / Node.js required. Just bash + curl.
14
12
  set -eu
15
13
 
16
14
  REPO="${CCG_REPO_OVERRIDE:-mcgrapeng/ccg}"
17
15
  TAG="${CCG_VERSION:-latest}"
18
16
  TARGET_DIR="${HOME}/.claude/commands"
19
- TMPDIR="$(mktemp -d -t ccg-install.XXXXXXXX)"
20
- cleanup() { rm -rf "$TMPDIR"; }
21
- trap cleanup EXIT
17
+
18
+ CURL_OPTS="-fsSL --connect-timeout 15 --max-time 120 --retry 2 --retry-delay 2"
22
19
 
23
20
  # parse flags
24
21
  while [ $# -gt 0 ]; do
@@ -29,56 +26,132 @@ while [ $# -gt 0 ]; do
29
26
  cat <<EOF
30
27
  Usage: install.sh [--version <tag>]
31
28
 
32
- Installs the /ccg slash command into ~/.claude/commands/ from GitHub Releases.
29
+ Installs the /ccg slash command into ~/.claude/commands/ from GitHub
30
+ (via jsdelivr CDN by default; falls back to raw.githubusercontent.com).
33
31
 
34
32
  Options:
35
33
  --version <tag> Install a specific release (default: latest)
36
- Example: --version v3.0.0
34
+ Example: --version v3.2.0
37
35
 
38
36
  Environment:
39
37
  CCG_REPO_OVERRIDE Override repo (default: mcgrapeng/ccg)
38
+ CCG_NO_CDN=1 Skip jsdelivr; use raw.githubusercontent.com only
40
39
  EOF
41
40
  exit 0 ;;
42
41
  *) echo "Unknown flag: $1" >&2; exit 2 ;;
43
42
  esac
44
43
  done
45
44
 
46
- # Resolve "latest" → actual tag via GitHub API (no auth required for public repos)
45
+ # Resolve "latest" → actual tag via GitHub API
46
+ # (api.github.com is usually reachable even when github.com archive is slow)
47
47
  if [ "$TAG" = "latest" ]; then
48
48
  echo "→ Resolving latest release of $REPO ..."
49
49
  if command -v jq >/dev/null 2>&1; then
50
- TAG=$(curl -fsSL "https://api.github.com/repos/$REPO/releases/latest" | jq -r .tag_name)
50
+ TAG=$(curl $CURL_OPTS "https://api.github.com/repos/$REPO/releases/latest" | jq -r .tag_name)
51
51
  else
52
- TAG=$(curl -fsSL "https://api.github.com/repos/$REPO/releases/latest" \
52
+ TAG=$(curl $CURL_OPTS "https://api.github.com/repos/$REPO/releases/latest" \
53
53
  | grep -E '"tag_name"' | head -1 | sed -E 's/.*"tag_name":[[:space:]]*"([^"]+)".*/\1/')
54
54
  fi
55
- [ -n "$TAG" ] && [ "$TAG" != "null" ] || { echo "FATAL: could not resolve latest release tag" >&2; exit 2; }
55
+ if [ -z "$TAG" ] || [ "$TAG" = "null" ]; then
56
+ echo "FATAL: could not resolve latest release tag" >&2
57
+ echo " Try: bash -s -- --version v3.2.0" >&2
58
+ exit 2
59
+ fi
56
60
  fi
57
61
  echo "→ Installing $REPO@$TAG"
58
62
 
59
- TARBALL_URL="https://github.com/$REPO/archive/refs/tags/$TAG.tar.gz"
60
- ARCHIVE="$TMPDIR/ccg.tar.gz"
63
+ # Download a single file. Tries jsdelivr first (fast in mainland China + global CDN),
64
+ # then falls back to raw.githubusercontent.com.
65
+ download_file() {
66
+ rel_path="$1"
67
+ dest="$2"
68
+
69
+ if [ "${CCG_NO_CDN:-0}" != "1" ]; then
70
+ url="https://cdn.jsdelivr.net/gh/$REPO@$TAG/$rel_path"
71
+ echo " → $url"
72
+ if curl $CURL_OPTS -o "$dest" "$url" 2>/dev/null; then
73
+ return 0
74
+ fi
75
+ echo " (jsdelivr failed, trying raw.githubusercontent.com)"
76
+ fi
77
+
78
+ url="https://raw.githubusercontent.com/$REPO/$TAG/$rel_path"
79
+ echo " → $url"
80
+ if curl $CURL_OPTS -o "$dest" "$url" 2>/dev/null; then
81
+ return 0
82
+ fi
83
+
84
+ return 1
85
+ }
86
+
87
+ # Use a non-standard variable name to avoid polluting the global TMPDIR
88
+ # which other tools (including ccg.sh itself) depend on.
89
+ CCG_INSTALL_TMPDIR="$(mktemp -d -t ccg-install.XXXXXXXX)"
90
+ TMPDIR="$CCG_INSTALL_TMPDIR"
91
+ trap 'rm -rf "$CCG_INSTALL_TMPDIR"' EXIT
92
+
93
+ echo "→ Downloading ccg.sh"
94
+ if ! download_file "ccg.sh" "$TMPDIR/ccg.sh"; then
95
+ echo "FATAL: could not download ccg.sh from any source" >&2
96
+ echo " Network issue? Try setting a proxy or use:" >&2
97
+ echo " CCG_NO_CDN=1 bash -s -- --version $TAG" >&2
98
+ exit 2
99
+ fi
61
100
 
62
- echo "→ Downloading $TARBALL_URL"
63
- curl -fsSL -o "$ARCHIVE" "$TARBALL_URL" || { echo "FATAL: download failed" >&2; exit 2; }
101
+ echo "→ Downloading ccg.md"
102
+ if ! download_file "ccg.md" "$TMPDIR/ccg.md"; then
103
+ echo "FATAL: could not download ccg.md from any source" >&2
104
+ exit 2
105
+ fi
64
106
 
65
- echo "→ Extracting"
66
- tar -xzf "$ARCHIVE" -C "$TMPDIR"
67
- SRC_DIR=$(find "$TMPDIR" -maxdepth 1 -type d -name 'ccg-*' | head -1)
68
- [ -d "$SRC_DIR" ] || { echo "FATAL: extracted dir not found" >&2; exit 2; }
107
+ # Sanity check downloaded files
108
+ [ -s "$TMPDIR/ccg.sh" ] || { echo "FATAL: ccg.sh downloaded but empty" >&2; exit 2; }
109
+ [ -s "$TMPDIR/ccg.md" ] || { echo "FATAL: ccg.md downloaded but empty" >&2; exit 2; }
110
+ head -1 "$TMPDIR/ccg.sh" | grep -q '^#!' \
111
+ || { echo "FATAL: ccg.sh doesn't look like a shell script" >&2; exit 2; }
69
112
 
70
- [ -r "$SRC_DIR/ccg.sh" ] || { echo "FATAL: ccg.sh missing in archive" >&2; exit 2; }
71
- [ -r "$SRC_DIR/ccg.md" ] || { echo "FATAL: ccg.md missing in archive" >&2; exit 2; }
113
+ # Integrity check: verify SHA-256 checksums against GitHub release assets if available.
114
+ # This prevents CDN poisoning or MITM attacks from executing arbitrary code.
115
+ echo "→ Verifying integrity..."
116
+ _ccg_install_verify_checksum() {
117
+ local file="$1"
118
+ local checksum_url="https://raw.githubusercontent.com/$REPO/$TAG/scripts/checksums.sha256"
119
+ local checksums
120
+ if checksums=$(curl -fsSL --connect-timeout 10 --max-time 30 "$checksum_url" 2>/dev/null); then
121
+ local expected
122
+ expected=$(printf '%s\n' "$checksums" | grep "$(basename "$file")" | awk '{print $1}')
123
+ if [ -n "$expected" ]; then
124
+ local actual
125
+ if command -v shasum >/dev/null 2>&1; then
126
+ actual=$(shasum -a 256 "$file" | awk '{print $1}')
127
+ elif command -v sha256sum >/dev/null 2>&1; then
128
+ actual=$(sha256sum "$file" | awk '{print $1}')
129
+ fi
130
+ if [ -n "$actual" ] && [ "$actual" != "$expected" ]; then
131
+ echo "FATAL: checksum mismatch for $(basename "$file")" >&2
132
+ echo " expected: $expected" >&2
133
+ echo " actual: $actual" >&2
134
+ echo " This may indicate a compromised download. Aborting." >&2
135
+ return 1
136
+ fi
137
+ fi
138
+ fi
139
+ # If checksums file is not available (older releases), proceed with a warning
140
+ return 0
141
+ }
142
+ _ccg_install_verify_checksum "$TMPDIR/ccg.sh" || exit 2
143
+ _ccg_install_verify_checksum "$TMPDIR/ccg.md" || exit 2
72
144
 
73
145
  echo "→ Installing to $TARGET_DIR"
74
146
  mkdir -p "$TARGET_DIR"
75
- install -m 0755 "$SRC_DIR/ccg.sh" "$TARGET_DIR/ccg.sh"
76
- install -m 0644 "$SRC_DIR/ccg.md" "$TARGET_DIR/ccg.md"
147
+ install -m 0755 "$TMPDIR/ccg.sh" "$TARGET_DIR/ccg.sh"
148
+ install -m 0644 "$TMPDIR/ccg.md" "$TARGET_DIR/ccg.md"
77
149
  echo " ✓ $TARGET_DIR/ccg.sh"
78
150
  echo " ✓ $TARGET_DIR/ccg.md"
79
151
 
80
152
  echo
81
153
  echo "→ Preflight checks:"
154
+ # shellcheck disable=SC1091
82
155
  . "$TARGET_DIR/ccg.sh"
83
156
  out=$(ccg_preflight)
84
157
  echo "$out" | sed 's/^/ /'
@@ -20,19 +20,35 @@ CCG_SH="$HERE/ccg.sh"
20
20
  CCG_MD="$HERE/ccg.md"
21
21
  TARGET_DIR="${HOME}/.claude/commands"
22
22
 
23
- [ -r "$CCG_SH" ] || { echo "FATAL: $CCG_SH not found"; exit 2; }
24
- [ -r "$CCG_MD" ] || { echo "FATAL: $CCG_MD not found"; exit 2; }
23
+ [ -r "$CCG_SH" ] || { echo "FATAL: $CCG_SH not found or not readable" >&2; exit 2; }
24
+ [ -r "$CCG_MD" ] || { echo "FATAL: $CCG_MD not found or not readable" >&2; exit 2; }
25
25
 
26
26
  echo "→ Installing /ccg to: $TARGET_DIR"
27
27
 
28
28
  if [ "$DRY" = "0" ]; then
29
- mkdir -p "$TARGET_DIR"
30
- install -m 0755 "$CCG_SH" "$TARGET_DIR/ccg.sh"
31
- install -m 0644 "$CCG_MD" "$TARGET_DIR/ccg.md"
29
+ if ! mkdir -p "$TARGET_DIR" 2>/dev/null; then
30
+ echo "❌ Failed to create directory: $TARGET_DIR" >&2
31
+ echo " Check permissions or run: mkdir -p $TARGET_DIR" >&2
32
+ exit 2
33
+ fi
34
+ if ! install -m 0755 "$CCG_SH" "$TARGET_DIR/ccg.sh" 2>/dev/null; then
35
+ echo "❌ Failed to install ccg.sh to $TARGET_DIR/ccg.sh" >&2
36
+ echo " Check file permissions and disk space" >&2
37
+ exit 2
38
+ fi
39
+ if ! install -m 0644 "$CCG_MD" "$TARGET_DIR/ccg.md" 2>/dev/null; then
40
+ echo "❌ Failed to install ccg.md to $TARGET_DIR/ccg.md" >&2
41
+ echo " Check file permissions and disk space" >&2
42
+ exit 2
43
+ fi
32
44
  echo " ✓ wrote $TARGET_DIR/ccg.sh ($(wc -l <"$CCG_SH" | tr -d ' ') lines)"
33
45
  echo " ✓ wrote $TARGET_DIR/ccg.md"
34
46
  else
35
47
  echo " [dry-run] would write: $TARGET_DIR/{ccg.sh,ccg.md}"
48
+ echo " [dry-run] skipping preflight checks (no code executed)"
49
+ echo
50
+ echo "→ Done (dry-run). Run without --dry-run to install."
51
+ exit 0
36
52
  fi
37
53
 
38
54
  echo
@@ -0,0 +1,90 @@
1
+ #!/bin/bash
2
+ # 完整测试 Bailian 集成
3
+
4
+ set -e
5
+
6
+ source "$(dirname "$0")/ccg.sh"
7
+
8
+ echo "=== Bailian 集成测试 ==="
9
+ echo ""
10
+
11
+ # 1. 检查配置
12
+ echo "1️⃣ 检查配置..."
13
+ eval "$(ccg_init)"
14
+ eval "$(ccg_preflight)"
15
+
16
+ if [ "$CCG_PREFLIGHT_BAILIAN" != "ok" ]; then
17
+ echo "❌ BAILIAN_API_KEY 未设置"
18
+ exit 1
19
+ fi
20
+ echo "✅ API 密钥已配置"
21
+ echo ""
22
+
23
+ # 2. 测试模型解析
24
+ echo "2️⃣ 测试模型解析..."
25
+ for mode in cost balanced quality; do
26
+ export CCG_MODE="$mode"
27
+ model=$(_ccg_resolve_bailian_model)
28
+ echo " $mode → $model"
29
+ done
30
+ echo ""
31
+
32
+ # 3. 测试基础调用
33
+ echo "3️⃣ 测试基础调用..."
34
+ TEST_PROMPT="Review this code:
35
+ function test() { return 42; }
36
+ Any issues?"
37
+
38
+ echo "$TEST_PROMPT" > "$CCG_BAILIAN_PROMPT"
39
+
40
+ if ccg_bailian "$CCG_BAILIAN_PROMPT" "$CCG_BAILIAN_RESULT"; then
41
+ echo "✅ 基础调用成功"
42
+ echo " 响应大小: $(wc -c < "$CCG_BAILIAN_RESULT") 字节"
43
+ else
44
+ echo "❌ 基础调用失败"
45
+ cat "$CCG_BAILIAN_ERR"
46
+ exit 1
47
+ fi
48
+ echo ""
49
+
50
+ # 4. 测试缓存
51
+ echo "4️⃣ 测试缓存..."
52
+ if ccg_bailian "$CCG_BAILIAN_PROMPT" "$CCG_BAILIAN_RESULT"; then
53
+ if grep -q "cache=hit" <<< "$(ccg_bailian "$CCG_BAILIAN_PROMPT" "$CCG_BAILIAN_RESULT" 2>&1)"; then
54
+ echo "✅ 缓存命中"
55
+ else
56
+ echo "⚠️ 缓存未命中(首次调用)"
57
+ fi
58
+ fi
59
+ echo ""
60
+
61
+ # 5. 测试参数控制
62
+ echo "5️⃣ 测试参数控制..."
63
+ export CCG_BAILIAN_TEMP="0.3"
64
+ export CCG_BAILIAN_MAX_TOKENS="2048"
65
+ echo "✅ 参数已设置 (temp=0.3, max_tokens=2048)"
66
+ echo ""
67
+
68
+ # 6. 测试重试机制
69
+ echo "6️⃣ 测试重试机制..."
70
+ export CCG_BAILIAN_RETRIES="2"
71
+ echo "✅ 重试次数: 2"
72
+ echo ""
73
+
74
+ # 7. 测试流式输出
75
+ echo "7️⃣ 测试流式输出..."
76
+ echo "Streaming response:"
77
+ timeout 10 ccg_bailian_stream "$CCG_BAILIAN_PROMPT" 2>/dev/null | head -5 || echo "⚠️ 流式输出超时或失败"
78
+ echo ""
79
+
80
+ # 8. 测试所有模型
81
+ echo "8️⃣ 测试所有模型..."
82
+ for model in qwen-3.7 qwen-3.6 qwen-3.6-plus qwen-3.5-sonnet qwen-3.5-haiku; do
83
+ export CCG_BAILIAN_MODEL="$model"
84
+ price_in=$(_ccg_price "$model" input)
85
+ price_out=$(_ccg_price "$model" output)
86
+ echo " $model: \$$price_in/\$$price_out per 1M tokens"
87
+ done
88
+ echo ""
89
+
90
+ echo "✅ 所有测试完成!"
@@ -0,0 +1,49 @@
1
+ #!/bin/bash
2
+ # Test Bailian integration for CCG
3
+
4
+ set -e
5
+
6
+ # Source CCG functions
7
+ source "$(dirname "$0")/ccg.sh"
8
+
9
+ # Initialize
10
+ eval "$(ccg_init)"
11
+ eval "$(ccg_preflight)"
12
+
13
+ echo "=== CCG Bailian Integration Test ==="
14
+ echo ""
15
+ echo "Preflight Status:"
16
+ echo " Codex: $CCG_PREFLIGHT_CODEX"
17
+ echo " Gemini: $CCG_PREFLIGHT_GEMINI"
18
+ echo " Bailian: $CCG_PREFLIGHT_BAILIAN"
19
+ echo ""
20
+
21
+ if [ "$CCG_PREFLIGHT_BAILIAN" != "ok" ]; then
22
+ echo "❌ Bailian not configured. Set BAILIAN_API_KEY environment variable."
23
+ exit 1
24
+ fi
25
+
26
+ # Create test prompt
27
+ TEST_PROMPT="Review this code for bugs:
28
+ function add(a, b) {
29
+ return a + b
30
+ }
31
+
32
+ Is this correct?"
33
+
34
+ echo "$TEST_PROMPT" > "$CCG_BAILIAN_PROMPT"
35
+
36
+ echo "Testing Bailian API call..."
37
+ if ccg_bailian "$CCG_BAILIAN_PROMPT" "$CCG_BAILIAN_RESULT"; then
38
+ echo "✅ Bailian call successful"
39
+ echo ""
40
+ echo "Response:"
41
+ head -20 "$CCG_BAILIAN_RESULT"
42
+ else
43
+ echo "❌ Bailian call failed"
44
+ cat "$CCG_BAILIAN_ERR" 2>/dev/null || echo "No error details"
45
+ exit 1
46
+ fi
47
+
48
+ echo ""
49
+ echo "=== Test Complete ==="
package/CHANGELOG.md DELETED
@@ -1,119 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to /ccg.
4
-
5
- ## [3.1.0] — 2026-05-23
6
-
7
- Documentation and discoverability release. No core behavior changes; safe drop-in upgrade from 3.0.0.
8
-
9
- ### Added
10
- - **`ccg about` subcommand** — 7-layer capability probe. Shows what ccg can do in *this* environment (not what the README claims), with green/yellow/red status per layer, environment readiness checks (Codex CLI / Gemini CLI / GEMINI_API_KEY / git / slash command installation), XDG storage state (ledger entries, usage entries, cache size), and a quick-reference command palette.
11
- - Aliases: `ccg capabilities`, `ccg caps`
12
- - Use case: "I'm a new user / contributor — what is ccg actually doing on my machine right now?"
13
- - **`docs/ARCHITECTURE.md`** — 387-line engineering reference for contributors and integrators. Documents the 7-layer architecture (L1 Safe CLI scheduling → L7 Divergence synthesis), each layer's purpose / problem / solution / "what breaks if you remove it", end-to-end data flow, extension points (signed contract for new risk rules / new LLM providers / new storage paths / pricing customization), invariants enforced by the test suite, and load-bearing design decisions that look weird at first glance.
14
- - **4-language ARCHITECTURE translations**: `docs/ARCHITECTURE.zh-CN.md`, `docs/ARCHITECTURE.ja.md`, `docs/ARCHITECTURE.ko.md`. Cross-linked from each language's README.
15
-
16
- ### Changed
17
- - **README fully rewritten** with a concrete bcrypt walkthrough — actual output (not placeholder mockups) showing how AGREEMENT / DIVERGENCE / BLINDSPOT sections render in practice. The example shows two real-world disagreement: should `bcrypt` be wrapped with `subtle.ConstantTimeCompare`? Claude synthesizes that Codex's recommendation would break the comparison entirely because bcrypt uses a fresh salt every call.
18
- - **"When to use ccg" rewritten** — replaced the security-only framing (auth / payments / migrations / crypto) with judgment-difficulty framing. New trigger: *feeling*, not *domain*. Added cross-domain examples across social platforms, data/AI infra, frontend, API design, distributed systems, database, and security — emphasizing that divergence detection earns its cost on *any* change where two reasonable engineers might disagree.
19
- - **README structure** restructured into three-part flow: What / Why-vs-alternatives / Install / Walkthrough — designed to answer "what does it do" and "what does the output mean" before diving into config.
20
- - README v-prefix removed from H1 headings (no more "v3" branding in titles; version lives in CHANGELOG + npm).
21
-
22
- ### Internal
23
- - `PROMO.md` added to `.gitignore` (marketing copy file, not part of npm package).
24
-
25
- ## [3.0.0] — 2026-05-23
26
-
27
- Repositioning: from "multi-model review tool" to **"code divergence detector"**. Three structural pillars added; product opinion sharpened. 99-test regression (stable across 3 consecutive runs).
28
-
29
- ### Identity shift
30
- **Before (v2):** "Get multi-model second opinions, see consensus + disagreement + actions."
31
- **After (v3):** "Surface where Codex and Gemini *disagree* — that's where you actually need to make a call. Agreement is low-signal; divergence is the gold."
32
-
33
- This is a category change. v3 deliberately downgrades the AGREEMENT section (one-liners only) and elevates DIVERGENCE (full expansion + `NEEDS HUMAN DECISION` markers).
34
-
35
- ### Added — Pillar 1: Divergence Engine
36
- - Structured `[FINDING]…[/FINDING]` prompt protocol; both reviewers must conform
37
- - Three-section synthesis output: `AGREEMENT (N) / DIVERGENCE (M) / BLINDSPOT (≤2)`
38
- - `VERDICT` line: merge | fix-required | discuss
39
- - `NEEDS HUMAN DECISION` is an explicit signal, not implied
40
-
41
- ### Added — Pillar 2: Risk-Aware Routing
42
- - `ccg_risk_score <diff_file>` — deterministic 0..100+ scoring
43
- - Path signals: auth/payment/migration/crypto/security/infra/ci (+15..+40)
44
- - Body signals: shell exec/SQL interp/fs delete/privilege ops (+5..+30)
45
- - Size signals: lines and files-changed (+5..+25)
46
- - Docs-only subtraction (-40)
47
- - Auto mode selection: <20 cost, <60 balanced, ≥60 quality
48
- - Manual `CCG_MODE=` override always wins
49
- - Outputs reason string for full transparency: `auth+35 sql_interp+30 size>300+15`
50
- - Pure rules, zero LLM cost, social-PR friendly
51
-
52
- ### Added — Pillar 3: Review Ledger
53
- - `ccg_ledger_record <workdir>` — append JSONL row to `~/.ccg/ledger.jsonl`
54
- - Fields: ts, repo, branch, sha, mode, risk, files, lines, paths[], synthesis (redacted, ≤400 chars)
55
- - `ccg_ledger_query [path-substring]` — search prior reviews
56
- - `CCG_LEDGER_LOG` env override
57
- - Synthesis excerpt runs through `_ccg_redact` before write (secret hygiene)
58
-
59
- ### Fixed — diff capture deep gap
60
- - Old behavior: `git diff HEAD` only → empty after commit
61
- - New 4-level fallback: `worktree → staged → upstream:@{u}…HEAD → origin/HEAD…HEAD`
62
- - Reports `CCG_DIFF_SOURCE=<level>` so caller knows what scope was captured
63
- - Resolves "I committed and now /ccg sees nothing" footgun
64
-
65
- ### Changed
66
- - Default `CCG_MODE=auto` (was `balanced`); auto uses risk score
67
- - `ccg_init` now also exposes `CCG_SYNTHESIS_FILE` and `CCG_RISK_FILE` paths
68
- - Dispatch subcommands added: `risk_score`, `ledger_record`, `ledger_query`
69
-
70
- ### Tests
71
- - 13 new test cases (13.1 - 13.13) covering all three pillars + diff fallback
72
- - All 99 tests pass; 31s runtime; stable across consecutive runs
73
-
74
- ## [2.0.0] — 2026-05-23
75
-
76
- Major refactor based on honest self-critique: drop low-value features, add high-leverage ones.
77
- 86-test regression (stable across 10 consecutive runs).
78
-
79
- ### Removed
80
- - **Pre-execution cost estimate** (`ccg_estimate`). Estimating output tokens by assumption produced 3-5× wrong predictions, misleading users. Post-execution `ccg_actual` remains — it uses real byte counts.
81
- - **Asymmetric prompt split** (codex=architecture, gemini=UX). It was pure intuition with no evidence. v2 sends the **same prompt to both providers** — training-data differences create natural diversity.
82
- - `ccg_mode_resolve` public subcommand (resolution is now internal/silent)
83
- - `CCG_USD_CNY_RATE`, `CCG_OUTPUT_TOKENS_ESTIMATE`, `CCG_TOKEN_CHARS_RATIO` knobs (no one tuned them)
84
-
85
- ### Added
86
- - **Auto `git diff` mode**: naked `/ccg` (no args) captures `git diff HEAD` and runs a pre-commit triangulated review. One-keystroke workflow.
87
- - `ccg_diff_capture <out_file>` — captures `git diff HEAD`, falls back to `--cached`, returns `CCG_DIFF_OK/FAIL`
88
- - **24h prompt-hash cache** keyed by SHA-256(model + prompt). Same prompt + same model → cached result, $0.00 cost. Iterating on the same code saves 90%+ on repeat calls.
89
- - `CCG_CACHE_DIR` (default `~/.ccg/cache`) and `CCG_CACHE_TTL_HOURS` (default 24)
90
- - `CCG_NO_CACHE=1` opt-out per-call
91
- - **Usage log** at `~/.ccg/usage.log` (TSV: timestamp, provider, model, in_tokens, out_tokens, USD, cached). Only successful calls logged. Override path via `CCG_USAGE_LOG`.
92
- - `ccg_usage [--this-month|--all|--since=YYYY-MM]` aggregates spend by provider with cache-hit count
93
- - **Prompt size guard**: `CCG_MAX_PROMPT_KB=100` default. Hard reject prompts above this. Prevents the "I piped my whole repo and got a $5 bill" footgun.
94
- - New dispatch subcommands: `diff_capture`, `usage`, `actual`
95
-
96
- ### Fixed
97
- - Test suite now isolates cache via `CCG_CACHE_DIR` (not `HOME`), so test runs no longer break codex/gemini auth lookup
98
- - Mock-mode test helpers always set `CCG_NO_CACHE=1` so failure-mode tests can't be masked by an earlier success-mode cache entry
99
-
100
- ## [1.0.0] — 2026-05-22
101
-
102
- First production-ready release.
103
-
104
- ### Added
105
- - `CCG_MODE=cost|balanced|quality` for model selection
106
- - `CCG_CODEX_MODEL`/`CCG_GEMINI_MODEL` explicit overrides
107
- - `ccg_codex` passes `-m <model>` when set
108
- - LICENSE (MIT), CHANGELOG, scripts/install.sh
109
- - 86-test regression in `tests/test_ccg.sh`
110
-
111
- ### Fixed
112
- - **CRITICAL**: pure-bash timeout fallback lost stdin in async children. Real Codex/Gemini stdin delivery now works in all bash modes via explicit `<&0`.
113
- - Timeout polling granularity: 1s → 0.1s + wall-clock deadline for sub-second responsiveness.
114
- - Orphan sweep threshold: 60min → 1440min (24h).
115
- - `eval`-safety for any printed shell-meta in mode descriptions.
116
-
117
- ## [0.x] — pre-release
118
-
119
- Initial two-file plugin (commands/ccg.md + commands/ccg.sh).