# Readme Skill

> 生成一份对外可分享、脱敏的 AI-Native 开发者 README。 量化展示我对 Claude Code + Codex CLI + Kiro (AWS) + Trae (ByteDance) + Gemini Antigravity (Google) + Cursor 的使用深度、AI 协作风格、 项目与领域分布、兴趣主题，以及与 GitHub 提交的产出关联。 Trigger when the user says: "生成我的 AI 档案" / "做一份 AI-native README" / "分析我的 Claude / Codex / Kiro / Trae / Antigravity / Cursor 使用情况" / "总结我的 AI 使用" / "生成 AI 月度报告" / "按月份分析我的 AI 编码" / "分析 2026-05 的 AI 使用" / "build my AI usage profile" / "build my monthly AI coding report" / "analyze my AI usage for May 2026" / "summarize my Claude / Codex / Kiro / Trae / Antigravity / Cursor history" / "生成开发者画像". 全程本地、只读、默认匿名、不上传任何数据。

- Skill: `study8677/readme-skill` (Agent Skill)
- Install (CLI): `npx skillmds@latest add study8677/readme-skill`
- Raw SKILL.md: https://api.skillmd.com/api/skills/study8677/readme-skill/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- License: MIT
- Author: study8677 (https://skillmd.com/u/study8677)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/study8677/readme-skill

---


# Readme.skill — AI-Native 开发者档案生成器

You (the AI agent invoking this skill) will read local Claude Code + Codex CLI
+ Kiro (AWS) + Trae (ByteDance) + Gemini Antigravity (Google) + Cursor data,
compute a fixed set of dimensions, and render both a Markdown profile and a
validated SVG poster under `./output/` in the user's requested language (Chinese
by default; English when the user asks in English or explicitly requests
English). The profile and poster can cover the default history view or an
explicit month / date range. **You do all of the work** — read the files with
`Read`, query sqlite via `Bash`, synthesize the prose yourself, then write and
validate the SVG. Do **not** write helper scripts; the skill is the recipe.

> 支持的 6 个 AI 编程工具（任一缺失都自动降级跳过）：
> 1. **Claude Code** (`~/.claude/`) — Step 2
> 2. **Codex CLI** (`~/.codex/`) — Step 3
> 3. **Kiro CLI / IDE** (`~/.kiro/` + `~/.local/share/kiro-cli/`) — Step 3b
> 4. **Trae IDE** (`~/Library/Application Support/Trae/` + 项目 `.trae/`) — Step 3c
> 5. **Gemini Antigravity** (`~/.gemini/antigravity/brain/`) — Step 3d
> 6. **Cursor** (`~/Library/Application Support/Cursor/` + 项目 `.cursor/`) — Step 3e

> 默认行为：**对外分享版** —— 项目名匿名、敏感信息脱敏。
> 如果用户明确说"私人版 / 不要脱敏 / show real names"，跳过匿名步骤。

---

## Step 1 — 准备

```bash
cd <repo-with-this-skill>   # e.g. ~/Projects/Readme.skill
mkdir -p output
DATE=$(date +%Y%m%d)
```

Decide anonymization mode (default = on). Build an in-memory mapping
`real_path → "项目 A/B/C"` as you encounter project paths in later steps.
Use the same mapping consistently across all sections.

### 1.1 时间窗口 / 月度报告模式

If the user asks for a month, quarter, stage, date range, "月度报告",
"按月份分析", "time range", "monthly report", or similar, set a report window
before reading any data. The window is a half-open local-date interval:
`[REPORT_START, REPORT_END_EXCL)`.

Supported phrases:
- Single month: `2026-05`, `2026年5月`, `May 2026` → `REPORT_START=2026-05-01`,
  `REPORT_END_EXCL=2026-06-01`, `REPORT_LABEL=2026-05`,
  `REPORT_SLUG=202605`, `REPORT_MODE=monthly`
- Month range: `2026-04 到 2026-05`, `Apr-May 2026` → start at the first day of
  the first month, end at the first day after the last month,
  `REPORT_MODE=range`
- Explicit dates: `2026-05-03 到 2026-05-19` / `2026-05-03..2026-05-19` →
  include both named dates by setting `REPORT_END_EXCL` to the day after the
  final date, `REPORT_MODE=range`
- Relative range: `最近30天` / `last 30 days` → compute from today's local date,
  `REPORT_MODE=range`

If no explicit time window is requested, keep the existing default profile
behavior: AI tool totals may use all available local history, while GitHub and
local git use their existing 365-day windows. Set `WINDOW_REQUESTED=0`.

If a window is requested, set:

```bash
WINDOW_REQUESTED=1
REPORT_START=<YYYY-MM-DD>
REPORT_END_EXCL=<YYYY-MM-DD>   # exclusive
REPORT_LABEL=<human-readable label, e.g. "2026-05" or "2026-04..2026-05">
REPORT_SLUG=<filesystem-safe slug, e.g. "202605" or "202604-202605">
```

For every source below, include only records whose timestamp is
`>= REPORT_START 00:00:00` and `< REPORT_END_EXCL 00:00:00` in local time.
Never mix all-time counts into a windowed report unless the metric is explicitly
labeled "all-time context" or "fallback, not window-filtered".

For windowed reports, also compute a previous comparison window of the same
length when possible:

```bash
# macOS date syntax. Use equivalent date math on other systems.
window_start_ts=$(date -j -f "%Y-%m-%d" "$REPORT_START" +%s)
window_end_ts=$(date -j -f "%Y-%m-%d" "$REPORT_END_EXCL" +%s)
WINDOW_DAYS=$(( (window_end_ts - window_start_ts) / 86400 ))
PREV_END_EXCL="$REPORT_START"
PREV_START=$(date -j -v-"${WINDOW_DAYS}"d -f "%Y-%m-%d" "$REPORT_START" +%Y-%m-%d)
```

---

## Step 2 — 读取 Claude Code 数据 (`~/.claude/` + 项目 `.claude/`)

### 2.1 预聚合统计（最权威，先看这个）

Read `~/.claude/stats-cache.json`. Extract:

| 字段 | 含义 |
| --- | --- |
| `totalSessions` | session 总数 |
| `totalMessages` | 消息总数 |
| `firstSessionDate` | 首个 session ISO 时间 |
| `longestSession.{duration,messageCount,timestamp}` | 最长 session |
| `hourCounts` | `{hour: count}` 24h 热力 |
| `modelUsage[model].{inputTokens,outputTokens,cacheReadInputTokens,cacheCreationInputTokens}` | 每模型 token 细分 |
| `dailyActivity[].{date,messageCount,sessionCount,toolCallCount}` | 每日活跃 |
| `dailyModelTokens[].{date,tokensByModel}` | 每日按模型 token |

**派生量（你来算）**:
- `claude_tokens_spent = Σ (inputTokens + outputTokens + cacheCreationInputTokens)` —— 真实新付费 token
- `claude_cache_read = Σ cacheReadInputTokens` —— 缓存复用，反映 prompt-caching 熟练度
- `cache_to_spent_ratio = claude_cache_read / claude_tokens_spent` —— 比值越大越熟

**时间窗口模式**：如果 `WINDOW_REQUESTED=1`，优先从 `dailyActivity` 与
`dailyModelTokens` 中按 `REPORT_START <= date < REPORT_END_EXCL` 过滤后汇总
Claude sessions / messages / tokens / cache。`modelUsage` 是全局聚合；只有默认
profile 模式才能直接当总量使用。若某个 Claude 字段只有全局聚合、无法按日期切分，
在月度报告里写 `—` 或标注「仅有 all-time 聚合，未纳入窗口统计」，不要把全局值混进
月度值。

### 2.2 Slash-command 热度

`~/.claude/history.jsonl` —— 每行 `{display, timestamp, project, sessionId}`。

```bash
# Top 15 slash commands
jq -r 'select(.display | startswith("/")) | (.display | split(" ")[0])' \
   ~/.claude/history.jsonl | sort | uniq -c | sort -rn | head -15

# 总条数 vs 命令条数 vs 直接 prompt 条数
total=$(wc -l < ~/.claude/history.jsonl)
cmd=$(jq -r 'select(.display | startswith("/")) | .display' ~/.claude/history.jsonl | wc -l)
echo "total=$total cmd=$cmd plain=$((total - cmd))"
```

时间窗口模式下，所有 `history.jsonl` 统计先过滤：

```bash
jq --arg start "$REPORT_START" --arg end "$REPORT_END_EXCL" '
  select((.timestamp // "")[0:10] >= $start and (.timestamp // "")[0:10] < $end)
' ~/.claude/history.jsonl
```

记录：`/effort`、`/plan`、`/skill*`、`/usage`、`/clear`、`/resume`、`/compact`、`/init` 各自次数。

### 2.3 项目分布 (`~/.claude/projects/`)

Each subdir is one project; per-project `*.jsonl` files = sessions.
The dir name encodes the absolute path with `/` → `-` (ambiguous when the
original path itself contains `-`).

```bash
# Top 15 by session-file count
for d in ~/.claude/projects/*/; do
  n=$(ls "$d"*.jsonl 2>/dev/null | wc -l | tr -d ' ')
  echo "$n $(basename "$d")"
done | sort -rn | head -15
```

To recover the canonical real path (so you can run `git log` later), read
the `cwd` field from the first JSONL in each dir:

```bash
head -1 ~/.claude/projects/<encoded>/*.jsonl 2>/dev/null \
  | jq -r 'select(.cwd) | .cwd' | head -1
```

### 2.4 计划与 skill 自研

Claude Code 的 plan 文件目录不是固定值。默认在 `~/.claude/plans`,
但用户可以通过 `plansDirectory` 改到项目工作目录下，例如
`"./.claude/plans"`。统计 plans 时必须先解析候选 plan 目录，不能只枚举
`~/.claude/plans/*.md`。

解析规则：
- 先从 `~/.claude/projects/*/*.jsonl` 的 `cwd` 字段恢复 Claude Code 访问过的项目根目录。
- 对每个项目根目录，按 Claude Code settings 优先级读取：
  `.claude/settings.local.json` > `.claude/settings.json` > `~/.claude/settings.json` > default。
- 如果有效 settings 中存在 `plansDirectory`：
  - 绝对路径保持不变；
  - `~/...` 展开为 `$HOME/...`；
  - `./...` 或其他相对路径按该项目根目录解析。
- 如果没有配置，使用默认 `~/.claude/plans`。
- 把所有候选目录下的 `*.md` 真实路径去重后，再统计 plan 数量和标题。

```bash
# Plan titles (first # heading of each plan) from all resolved plan dirs.
# Include ~/.claude/plans plus any per-project plansDirectory targets.
# Count plan files by file count, not by title extraction success.
plan_count=<resolved-plan-file-count>
for f in <resolved-plan-files>; do
  awk '/^# / { sub(/^# /, ""); print; exit }' "$f"
done

ls ~/.claude/skills/ | wc -l       # skills installed / authored
ls ~/.claude/tasks/  | wc -l        # tasks tracked
ls ~/.claude/todos/  | wc -l
```

### 2.4b Skill 清单（AI 基础设施采集）

For each `~/.claude/skills/*/SKILL.md` and `~/.codex/skills/*/SKILL.md`,
**use the `Read` tool to inspect the frontmatter** (top of file, between `---` markers). Extract `name` and the full `description` as YAML semantics dictate.

Support all four YAML scalar styles：

| 写法 | 处理 |
| --- | --- |
| 单行: `description: foo bar` | 直接取冒号后内容 |
| 引号: `description: "foo bar"` 或 `'foo bar'` | 去掉首尾引号 |
| `>` folded（多行折叠） | join indented continuation lines with spaces |
| `\|` literal（多行保留） | preserve line breaks |

停止条件：遇到下一个**未缩进的 frontmatter key**（行首无空格且形如 `key:`），或遇到关闭的 `---` 行。如果 `description` 字段缺失，回落到 `<目录名> (no description)`。

**绝不使用 `head \| grep`** —— 那会把 `>`/`\|` 多行风格静默截断到只剩 `>`，这是 v2.2 之前的真实 bug。务必 `Read` 完整 frontmatter 后按 YAML 语义解析。

枚举候选 skill 目录：

```bash
ls -d ~/.claude/skills/*/ ~/.codex/skills/*/ 2>/dev/null
```

然后对每个目录：`Read` 它的 `SKILL.md` 头部 ~30 行 → 按上表解析 YAML → 输出 `<source>|<name>|<full_description>`。

记录每个 skill 是「自建」还是「安装」。如果 skill 目录下有 git remote 指向用户自己的 repo，标记为自建；否则标记为安装。

### 2.5 配置深度

Read `~/.claude/settings.json`. Count:
- `hooks` 个数（结构化自动化能力）
- `mcpServers` 个数（外部能力接入）
- `permissions.defaultMode`

---

## Step 3 — 读取 Codex CLI 数据 (`~/.codex/`)

### 3.1 SQLite (read-only)

The primary analytics store is `~/.codex/state_5.sqlite`, table `threads`.
**Always open with `mode=ro`** so you can never write:

```bash
SQ='sqlite3 file:'"$HOME"'/.codex/state_5.sqlite?mode=ro&immutable=1'

# If WINDOW_REQUESTED=1, compute unix-second bounds once and add the filter to
# every threads query below. For queries that already have WHERE, append `AND`.
FROM_TS=$(date -j -f "%Y-%m-%d" "$REPORT_START" +%s 2>/dev/null || true)
TO_TS=$(date -j -f "%Y-%m-%d" "$REPORT_END_EXCL" +%s 2>/dev/null || true)
# created_at >= FROM_TS AND created_at < TO_TS

# Aggregate
$SQ "SELECT COUNT(*), SUM(tokens_used), MIN(created_at), MAX(created_at) FROM threads;"

# Model breakdown (note: empty/NULL model = older sessions, label as 'Codex (未标注)')
$SQ "SELECT COALESCE(NULLIF(model,''),'Codex(未标注)'), COUNT(*), SUM(tokens_used) \
     FROM threads GROUP BY 1 ORDER BY 3 DESC;"

# Reasoning effort distribution (xhigh / high / medium / low / unspecified)
$SQ "SELECT COALESCE(NULLIF(reasoning_effort,''),'unspecified'), COUNT(*) \
     FROM threads GROUP BY 1 ORDER BY 2 DESC;"

# Top 15 working dirs
$SQ "SELECT cwd, COUNT(*), SUM(tokens_used) FROM threads \
     WHERE cwd != '' GROUP BY cwd ORDER BY 2 DESC LIMIT 15;"

# Hour-of-day heatmap
$SQ "SELECT strftime('%H', datetime(created_at,'unixepoch')), COUNT(*) \
     FROM threads GROUP BY 1 ORDER BY 1;"

# Day-of-activity timeseries
$SQ "SELECT date(created_at,'unixepoch'), COUNT(*) FROM threads GROUP BY 1;"

# Sample titles + first user messages for keyword extraction (titles only — no body)
$SQ "SELECT title FROM threads WHERE title != '' ORDER BY created_at DESC LIMIT 200;"
$SQ "SELECT first_user_message FROM threads WHERE first_user_message != '' \
     ORDER BY created_at DESC LIMIT 200;"

# CLI versions used (Codex evolution signal)
$SQ "SELECT cli_version, COUNT(*) FROM threads WHERE cli_version != '' \
     GROUP BY 1 ORDER BY 2 DESC LIMIT 10;"

# --- 以下为 v2.0 新增查询 ---

# 月度聚合（Evolution 曲线用）
$SQ "SELECT strftime('%Y-%m', datetime(created_at,'unixepoch')), COUNT(*), \
     SUM(tokens_used), COALESCE(NULLIF(model,''),'unknown') \
     FROM threads GROUP BY 1,4 ORDER BY 1,3 DESC;"

# CLI 版本时间线（Evolution 曲线用）
$SQ "SELECT cli_version, MIN(date(created_at,'unixepoch','localtime')), \
     MAX(date(created_at,'unixepoch','localtime')), COUNT(*) \
     FROM threads WHERE cli_version != '' GROUP BY 1 ORDER BY 2;"

# 每项目 token 消耗（双工具编排分析用）
$SQ "SELECT cwd, COALESCE(NULLIF(model,''),'unknown'), COUNT(*), SUM(tokens_used) \
     FROM threads WHERE cwd != '' GROUP BY 1,2 ORDER BY 1,4 DESC;"
```

### 3.2 Codex 全局历史

`~/.codex/history.jsonl` — `{session_id, ts, text}`. Sample for keywords:

```bash
wc -l ~/.codex/history.jsonl                                 # total prompts
jq -r '.text' ~/.codex/history.jsonl | head -300 > /tmp/codex_text.txt   # corpus
jq -r '.session_id' ~/.codex/history.jsonl | sort -u | wc -l # distinct sessions
```

时间窗口模式下，先按 `.ts` 过滤再做计数、关键词采样和 distinct sessions：

```bash
jq --arg start "$REPORT_START" --arg end "$REPORT_END_EXCL" '
  select((.ts // "")[0:10] >= $start and (.ts // "")[0:10] < $end)
' ~/.codex/history.jsonl
```

### 3.3 自研 artifacts

```bash
ls ~/.codex/skills/      | wc -l   # codex skills
ls ~/.codex/automations/ | wc -l   # scheduled automations
ls ~/.codex/rules/       | wc -l   # custom rules
```

---

## Step 3b — 读取 Kiro 数据 (`~/.kiro/` + `~/.local/share/kiro-cli/`)

Kiro 是 AWS 出的 agentic IDE / CLI（kirodotdev/Kiro）。Kiro CLI 把 ACP
session 存到 `~/.kiro/sessions/cli/`（每个 session 两个文件：`<id>.json`
元数据 + `<id>.jsonl` 事件流），把 token / model / provider 细分存到
`~/.local/share/kiro-cli/data.sqlite3`。Steering / Agents / Skills / Prompts
等基础设施在 `~/.kiro/` 下，跟 Claude Code 风格一致。

**所有读取必须只读**：SQLite 用 `mode=ro&immutable=1`；JSON / JSONL 只
`Read` / `jq`，不要修改。本步骤先检测 `~/.kiro/` 是否存在，不存在直接跳过本节。

### 3b.1 总量与 token 细分 (SQLite, read-only)

```bash
[ -d "$HOME/.kiro" ] || { echo "Kiro not installed; skip Step 3b"; }

KIRO_DB="$HOME/.local/share/kiro-cli/data.sqlite3"
if [ -f "$KIRO_DB" ]; then
  KSQ='sqlite3 file:'"$KIRO_DB"'?mode=ro&immutable=1'

  # 先 dump schema 再决定查询列名 —— Kiro CLI 仍在迭代，表名可能演进
  $KSQ ".schema" | head -80
  $KSQ ".tables"
fi
```

读 schema 后，按实际表名（常见为 `messages` / `sessions` / `usage` 等）
**自适应**编写聚合 SQL。期望提取的字段：

| 字段 | 含义 | 来源（按 schema 自适应）|
| --- | --- | --- |
| `kiro_sessions` | 总 session 数 | `COUNT(DISTINCT session_id)` |
| `kiro_messages` | 总消息数 | `COUNT(*)` from message-like 表 |
| `kiro_input_tokens` / `kiro_output_tokens` | 每模型 token | `SUM(input_tokens)` / `SUM(output_tokens)` |
| `kiro_model_breakdown` | 按 `model` / `provider` 分组 | `GROUP BY model, provider` |
| `kiro_by_date` | 按 `date(created_at)` 聚合 | 每日活跃 |
| `kiro_by_hour` | 按 `strftime('%H', created_at)` | 24h 热力 |

**降级**：如果 schema 找不到 token / model 列，仅按 session 计数即可，并在报告里说明
「Kiro 早期版本未持久化 token 细分，本节按 session 总量给出」。

时间窗口模式下，所有 Kiro SQL 聚合必须按实际 schema 的 `created_at` /
`updated_at` / timestamp-like 字段过滤到 `[REPORT_START, REPORT_END_EXCL)`。
如果 schema 没有可靠时间列，只把该表用于 all-time context，不参与月度指标。

### 3b.2 ACP Session 文件 (JSON + JSONL)

```bash
KIRO_SESS="$HOME/.kiro/sessions/cli"
if [ -d "$KIRO_SESS" ]; then
  # session 总数
  ls "$KIRO_SESS"/*.json 2>/dev/null | wc -l

  # 每个 session 抽元数据：cwd、agent、起止时间
  for f in "$KIRO_SESS"/*.json; do
    jq -r '[.cwd // "", .agent // "", .created_at // "", .updated_at // ""] | @tsv' "$f"
  done | sort -u

  # 项目分布（按 cwd 聚合）
  for f in "$KIRO_SESS"/*.json; do
    jq -r '.cwd // empty' "$f"
  done | sort | uniq -c | sort -rn | head -15
fi
```

`*.jsonl` 是事件流（user/assistant/tool-call 逐条）。**只采样前若干行用于
关键词语料**（同 Claude `projects/*/*.jsonl` 的处理方式），不要把原文写进
report：

```bash
for f in "$KIRO_SESS"/*.jsonl; do
  head -50 "$f" | jq -r 'select(.role == "user") | .content // empty' 2>/dev/null
done | head -300 > /tmp/kiro_corpus.txt   # 关键词语料
```

### 3b.3 Kiro 基础设施层（agents / skills / steering / prompts / mcp）

跟 Claude / Codex 的 skills 体系**一一对应**，扫法一致：

```bash
# 全局 agents（每个文件是一个 .json，filename 即 agent 名）
ls ~/.kiro/agents/*.json 2>/dev/null | wc -l

# 全局 skills（每个目录一个，含 SKILL.md，frontmatter 同 Agent Skills 标准）
ls -d ~/.kiro/skills/*/ 2>/dev/null

# Steering 文件（项目规范 / 架构决策，markdown）
ls ~/.kiro/steering/*.md 2>/dev/null | wc -l

# Prompts 模板
ls ~/.kiro/prompts/ 2>/dev/null | wc -l

# Settings & MCP
[ -f ~/.kiro/settings/cli.json ] && cat ~/.kiro/settings/cli.json | jq 'keys'
[ -f ~/.kiro/settings/mcp.json ] && cat ~/.kiro/settings/mcp.json | jq '.mcpServers | keys'
```

对每个 `~/.kiro/skills/*/SKILL.md`，**沿用 Step 2.4b 的 YAML frontmatter 解析逻辑**
（`Read` 完整 frontmatter，按 `>` / `|` / 引号 / 单行四种 scalar 处理）。
Kiro skills 用的就是 Agent Skills 开放标准，跟 Claude / Codex 字段完全相同
（`name` + `description`）。

把 `~/.kiro/skills/` 合并进 Step 2.4b 的 skill 总表，新增一列「来源 = Kiro」。

### 3b.4 Knowledge bases（实验功能，可选）

```bash
KIRO_KB="$HOME/.local/share/kiro-cli/knowledge_bases"
if [ -d "$KIRO_KB" ]; then
  ls -d "$KIRO_KB"/*/ 2>/dev/null   # 每个 agent 一个独立 KB
fi
```

知识库属于「AI 基础设施层」高级信号 —— 用户主动给 agent 喂资料。统计有几个
KB、覆盖哪些 agent 即可，不读 `data.json` 原文。

### 3b.5 工作区 `.kiro/` 配置（按项目）

对 Step 5 候选目录路径列表里的每个项目根，再检查项目内的 workspace-level
Kiro 配置（这往往是用户日常工作的真实证据）：

```bash
for path in <candidate-paths>; do
  for kind in agents skills steering prompts; do
    if [ -d "$path/.kiro/$kind" ]; then
      echo "$path::$kind::$(ls "$path/.kiro/$kind" 2>/dev/null | wc -l)"
    fi
  done
done
```

合并到 6.4 「项目与领域」时，给配置了 `.kiro/` 的项目打 `Kiro+` 标记。

### 3b.6 Kiro 数据来源不可读时的诚实声明

如果 Kiro 安装但 `data.sqlite3` 不存在（用户只用过 IDE 桌面版，未跑 CLI），
本步骤仅能采集到 Steering / Agents / Skills 配置数，**不要编造 session / token 数字**。
在最终报告的「Kiro 章节」明确写：「Kiro CLI 数据未生成，本节仅展示 Steering /
Agents / Skills 配置；如需完整 session/token 统计请先运行 Kiro CLI。」

---

## Step 3c — 读取 Trae 数据 (`~/Library/Application Support/Trae/` + 项目 `.trae/`)

Trae 是字节跳动出的 AI IDE，基于 VS Code fork（Electron）。**chat 对话存在
本地 SQLite**（`User/workspaceStorage/<hash>/state.vscdb`，与 Cursor 同款机制），
**但 token 用量统计走云端 API**（`query_user_usage_group_by_session`），
本机不持久化。所以本步骤只读两类本地数据：

1. 工作区 `state.vscdb` 里的 chat 元数据（数量、cwd、关键词）
2. 项目 `.trae/` 与 home 配置里的 rules / skills / settings

**所有读取必须只读**：SQLite 强制 `mode=ro&immutable=1`；不要触发任何 Trae
进程写操作。先检测目录是否存在，不存在直接跳过本节。

### 3c.1 工作区数量与项目分布

```bash
# macOS 路径（Linux 类似在 ~/.config/Trae/，Windows 在 %APPDATA%\Trae\）
TRAE_BASE="$HOME/Library/Application Support/Trae"
TRAE_WS="$TRAE_BASE/User/workspaceStorage"

[ -d "$TRAE_WS" ] || { echo "Trae not installed or no workspaces; skip Step 3c"; }

# 工作区数（每个 hash 目录 = 一个被打开过的项目）
ls -d "$TRAE_WS"/*/ 2>/dev/null | wc -l

# 每个工作区对应的真实项目路径（workspace.json 里有 folder/uri）
for d in "$TRAE_WS"/*/; do
  if [ -f "$d/workspace.json" ]; then
    jq -r '.folder // .configuration // empty' "$d/workspace.json"
  fi
done | sort -u
```

### 3c.2 Chat 元数据（SQLite, read-only）

每个工作区有自己的 `state.vscdb`；另外 `~/Library/Application Support/Trae/User/globalStorage/state.vscdb` 是全局聚合库。**Trae 的 chat 表名 / key 前缀
在版本间会变化**（早期沿用 VS Code 的 `ItemTable`，新版本可能新增 Trae 专用表），
**先 dump 一下结构再下查询**：

```bash
TRAE_GLOBAL="$TRAE_BASE/User/globalStorage/state.vscdb"

if [ -f "$TRAE_GLOBAL" ]; then
  TSQ='sqlite3 file:'"$TRAE_GLOBAL"'?mode=ro&immutable=1'
  $TSQ ".tables"
  $TSQ "SELECT name FROM sqlite_master WHERE type='table';"

  # 常见结构：ItemTable(key TEXT, value BLOB) —— 类 VS Code KV
  # Trae 把 chat 存为 key='trae.chat.*' 或 'composer.*' 形式（版本不同前缀不同）
  $TSQ "SELECT key, length(value) FROM ItemTable \
        WHERE key LIKE '%chat%' OR key LIKE '%conversation%' OR key LIKE '%composer%' \
        ORDER BY length(value) DESC LIMIT 30;" 2>/dev/null
fi

# 工作区级 chat
for d in "$TRAE_WS"/*/; do
  db="$d/state.vscdb"
  [ -f "$db" ] || continue
  ws_chat_keys=$(sqlite3 "file:$db?mode=ro&immutable=1" \
    "SELECT COUNT(*) FROM ItemTable WHERE key LIKE '%chat%' OR key LIKE '%composer%';" 2>/dev/null)
  echo "$(basename "$d") chat_keys=$ws_chat_keys"
done
```

**期望提取**：

| 字段 | 含义 | 备注 |
| --- | --- | --- |
| `trae_workspaces` | 打开过的项目数 | `ls workspaceStorage/*/` 计数 |
| `trae_chat_session_count` | 估算的 chat session 数 | 按 chat-related key 数估算 |
| `trae_active_projects` | 有 chat 的项目数 | `ws_chat_keys > 0` 的工作区数 |
| `trae_corpus` | chat 标题 / 首条 user message | 仅采样若干条，用于关键词，不入报告原文 |

时间窗口模式下，Trae workspace / chat 只能在存在可靠 timestamp 或文件 mtime
落入窗口时计入窗口活跃。否则只作为「检测到 Trae 配置 / all-time context」展示，
不要计入月度 sessions、active projects 或关键词。

**强烈降级提示**：
- Trae chat 的 key 格式**没有公开稳定文档**。如果 `LIKE` 没匹中任何 row，
  老老实实在报告里写「Trae 本地 chat 仅检测到 workspace 数量 N，对话内容
  key 命名约定本工具暂不解析」，**不要编造 session 数**。
- 如果工作区目录为空或 `state.vscdb` 文件不存在，直接跳过该工作区。

### 3c.3 Token 用量 —— 仅云端，本地无法读

Trae 的 token / 模型用量走云端 API。第三方工具（如 tokscale）的做法是：
用户先 `tokscale trae login`，再调 `query_user_usage_group_by_session` 拉数据
缓存到 `~/.config/tokscale/trae-cache/sessions/*.json`。

**本 skill 不发起任何网络请求**，所以 Trae 的 token 数字无法被采集。
最终报告里诚实写：「Trae 的 token 用量数据由 ByteDance 云端 API 持有，
本 skill 出于『100% 本地 + 只读』原则不接入；如需 Trae token，请使用
tokscale 等第三方工具单独采集后人工补入。」

可选：如果用户**已经**在 `~/.config/tokscale/trae-cache/sessions/` 里有
导出的 JSON 缓存，可以读它（只读、本地）：

```bash
TOKSCALE_TRAE="$HOME/.config/tokscale/trae-cache/sessions"
if [ -d "$TOKSCALE_TRAE" ]; then
  jq -s 'map(.token_count // 0) | add' "$TOKSCALE_TRAE"/*.json 2>/dev/null
  jq -r '.model // empty' "$TOKSCALE_TRAE"/*.json 2>/dev/null | sort | uniq -c
fi
```

### 3c.4 项目 `.trae/` 配置（rules / skills / .ignore）

跟 Kiro `.kiro/`、Claude 项目 `.claude/` 一样，Trae 在项目内提供 `.trae/`
工作区目录。这是「用户给 AI 立规矩」的一手证据。

```bash
for path in <candidate-paths>; do
  trae_dir="$path/.trae"
  [ -d "$trae_dir" ] || continue
  echo "$path::trae::rules=$(ls "$trae_dir"/rules/*.md 2>/dev/null | wc -l)::skills=$(ls -d "$trae_dir"/skills/*/ 2>/dev/null | wc -l)::ignore=$([ -f "$trae_dir/.ignore" ] && echo 1 || echo 0)"
done
```

`.trae/rules/*.md` 与 `.trae/skills/*/SKILL.md` 都是 markdown，
**继续沿用 Step 2.4b 的 YAML frontmatter 解析逻辑**。把它们合并进 6.2 的
「AI 基础设施层」总表，新增一列「来源 = Trae」。

### 3c.5 Trae 数据缺失时的诚实声明

- `~/Library/Application Support/Trae/` 不存在 → 完全跳过本节
- 存在但工作区 chat key 解析失败 → 仅展示「打开过的项目数 + `.trae/` 配置」
- token 数据永远缺失 → 明确写「本地未持有，需经云端 API 拉取，本 skill 不联网」

---

## Step 3d — 读取 Gemini Antigravity 数据 (`~/.gemini/antigravity/`)

Antigravity is the third local AI tool source. Treat each
`~/.gemini/antigravity/brain/<uuid>/` directory as one Antigravity task/session.
Only count directories whose basename is a UUID; ignore non-task directories such
as `tempmediaStorage`.

**Only read local text data**:
- `*.metadata.json` for artifact metadata and summaries
- `task.md`, `implementation_plan.md`, `walkthrough.md`
- text variants ending in `.resolved`, `.resolved.0`, `.resolved.1`, etc.

**Never read for analytics**:
- screenshots or images (`*.png`, `*.webp`, `*.jpg`, `*.jpeg`)
- `~/.gemini/antigravity/annotations/*.pbtxt`
- `~/.config/Antigravity/*` browser/cache data
- browser profiles or cache directories

```bash
AG_BRAIN="$HOME/.gemini/antigravity/brain"
AG_UUID_RE='[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'

# Count Antigravity task/session directories; exclude temp/media helper dirs
find "$AG_BRAIN" -mindepth 1 -maxdepth 1 -type d 2>/dev/null \
  | grep -E "/$AG_UUID_RE$" | wc -l

# Artifact type breakdown from metadata in task/session directories
find "$AG_BRAIN" -mindepth 2 -maxdepth 2 -name '*.metadata.json' -type f 2>/dev/null \
  | grep -E "/$AG_UUID_RE/[^/]+\.metadata\.json$" \
  | xargs -r jq -r '.artifactType // "unknown"' | sort | uniq -c | sort -rn

# Activity by day from metadata updatedAt
find "$AG_BRAIN" -mindepth 2 -maxdepth 2 -name '*.metadata.json' -type f 2>/dev/null \
  | grep -E "/$AG_UUID_RE/[^/]+\.metadata\.json$" \
  | xargs -r jq -r '.updatedAt // empty' \
  | cut -c1-10 | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}$' \
  | sort | uniq -c

# Monthly activity for Evolution curve
find "$AG_BRAIN" -mindepth 2 -maxdepth 2 -name '*.metadata.json' -type f 2>/dev/null \
  | grep -E "/$AG_UUID_RE/[^/]+\.metadata\.json$" \
  | xargs -r jq -r '.updatedAt // empty' \
  | cut -c1-7 | grep -E '^[0-9]{4}-[0-9]{2}$' \
  | sort | uniq -c

# Summaries for topic extraction; do not quote full text in the README
find "$AG_BRAIN" -mindepth 2 -maxdepth 2 -name '*.metadata.json' -type f 2>/dev/null \
  | grep -E "/$AG_UUID_RE/[^/]+\.metadata\.json$" \
  | xargs -r jq -r '.summary // empty' | head -200

# Markdown headings for topic extraction
find "$AG_BRAIN" -mindepth 2 -maxdepth 2 -type f \
  \( -name 'task.md' -o -name 'implementation_plan.md' -o -name 'walkthrough.md' \
     -o -name 'task.md.resolved*' -o -name 'implementation_plan.md.resolved*' \
     -o -name 'walkthrough.md.resolved*' \) 2>/dev/null \
  | grep -E "/$AG_UUID_RE/[^/]+$" \
  | xargs -r grep -hE '^#{1,3} ' | head -200

# Checkbox volume, useful for task/planning depth
find "$AG_BRAIN" -mindepth 2 -maxdepth 2 -type f \
  \( -name 'task.md' -o -name 'implementation_plan.md' -o -name 'walkthrough.md' \
     -o -name 'task.md.resolved*' -o -name 'implementation_plan.md.resolved*' \
     -o -name 'walkthrough.md.resolved*' \) 2>/dev/null \
  | grep -E "/$AG_UUID_RE/[^/]+$" \
  | xargs -r grep -hE '^- \[[ xX/-]\]' | wc -l

# Antigravity text artifact scale. This is NOT billing usage and MUST NOT be
# merged into Claude/Codex token totals.
find "$AG_BRAIN" -mindepth 2 -maxdepth 2 -type f \
  \( -name 'task.md' -o -name 'implementation_plan.md' -o -name 'walkthrough.md' \
     -o -name 'task.md.resolved*' -o -name 'implementation_plan.md.resolved*' \
     -o -name 'walkthrough.md.resolved*' \) 2>/dev/null \
  | grep -E "/$AG_UUID_RE/[^/]+$" \
  | xargs -r wc -l -m \
  | awk '
      $NF != "total" { files++; lines += $1; chars += $2 }
      END {
        printf "antigravity_text_files=%d\n", files + 0
        printf "antigravity_text_lines=%d\n", lines + 0
        printf "antigravity_text_chars=%d\n", chars + 0
        printf "antigravity_estimated_token_equivalent=%d\n", int(chars / 4 + 0.5)
      }'
```

Compute:
- `antigravity_tasks` = count of `brain/<uuid>/` directories.
- `antigravity_artifacts_by_type` = counts by `artifactType`.
- `antigravity_active_days` = unique dates from valid `updatedAt` values.
- `antigravity_first_active` / `antigravity_last_active` = min/max valid `updatedAt` dates.
- `antigravity_monthly_activity` = monthly counts from valid `updatedAt` values.
- `antigravity_topics` = metadata summaries + markdown headings + checkbox section labels, used only for keywords and high-level themes.
- `antigravity_text_files` = count of eligible Antigravity text artifact files.
- `antigravity_text_chars` = total character count across eligible Antigravity text artifacts.
- `antigravity_text_lines` = total line count across eligible Antigravity text artifacts.
- `antigravity_estimated_token_equivalent = round(antigravity_text_chars / 4)` as a rough text-scale proxy only.

时间窗口模式下，Antigravity 只统计 `updatedAt`、文件 mtime 或可解析 metadata 时间
落入 `[REPORT_START, REPORT_END_EXCL)` 的 task/artifact。没有可靠时间的 artifact
可以出现在 all-time context 或缺失说明里，不参与月度增量。

Antigravity data does not expose verified billing token counts. Use `—` in token columns or omit token metrics for Antigravity. If reporting `antigravity_estimated_token_equivalent`, label it exactly as **estimated token-equivalent (non-billing)** and keep it outside all real token totals, token economics tables, and billing/paid-token claims.

---

## Step 3e — 读取 Cursor 数据 (`~/Library/Application Support/Cursor/` + 项目 `.cursor/`)

Cursor 是 Anysphere 出的 AI IDE，**基于 VS Code fork**（Electron），存储模型
跟 Trae / VS Code 同款（`User/workspaceStorage/<hash>/state.vscdb` 的 `ItemTable`
KV 表，加 `User/globalStorage/state.vscdb` 全局聚合库）。**chat / composer
数据本地完整缓存**，但 **token 用量统计走云端 dashboard**（Cursor Pro 计费
依赖云端），本机不持久化精确 token 数字。所以本步骤只读两类本地数据：

1. 工作区 / 全局 `state.vscdb` 里的 chat / composer 元数据（数量、cwd、关键词）
2. 项目 `.cursor/` 与 home 配置里的 rules / mcp / settings

**所有读取必须只读**：SQLite 强制 `mode=ro&immutable=1`；不要触发任何 Cursor
进程写操作。先检测目录是否存在，不存在直接跳过本节。

### 3e.1 工作区数量与项目分布

```bash
# macOS 路径（Linux: ~/.config/Cursor/User/，Windows: %APPDATA%\Cursor\User\）
CURSOR_BASE="$HOME/Library/Application Support/Cursor"
CURSOR_WS="$CURSOR_BASE/User/workspaceStorage"

[ -d "$CURSOR_WS" ] || { echo "Cursor not installed or no workspaces; skip Step 3e"; }

# 工作区数（每个 hash 目录 = 一个被打开过的项目）
ls -d "$CURSOR_WS"/*/ 2>/dev/null | wc -l

# 每个工作区对应的真实项目路径（workspace.json 里有 folder / configuration）
for d in "$CURSOR_WS"/*/; do
  if [ -f "$d/workspace.json" ]; then
    jq -r '.folder // .configuration // empty' "$d/workspace.json"
  fi
done | sort -u
```

### 3e.2 Chat / Composer 元数据（SQLite, read-only）

全局聚合库在 `User/globalStorage/state.vscdb`。Cursor 的 chat / composer
key 命名比 Trae 略稳定一些（社区有逆向资料）。优先读取
`composer.composerHeaders`：它通常是 JSON object，内部 `allComposers` 数组包含
composer 标题、subtitle、创建/更新时间、workspaceIdentifier、trackedGitRepos、
变更行数等元数据。这些属于「内容线索」但不是完整对话正文，适合用于关键词、
项目分布和 Cursor 协作强度。常见 prefix 还有 `composer.*`、`aiService.*`、
`workbench.panel.aichat.*`、`aiCodeBlockDiff.*`。但仍然 **版本会变化**，
必须先 dump 结构再下查询：

```bash
CURSOR_GLOBAL="$CURSOR_BASE/User/globalStorage/state.vscdb"

if [ -f "$CURSOR_GLOBAL" ]; then
  sqlite3 "file:$CURSOR_GLOBAL?mode=ro&immutable=1" ".tables"

  # Composer / chat 类 key 排行（按 value 大小，大的通常是真实对话数据）
  sqlite3 "file:$CURSOR_GLOBAL?mode=ro&immutable=1" \
       "SELECT key, length(value) FROM ItemTable \
        WHERE key LIKE 'composer.%' OR key LIKE 'aiService.%' \
           OR key LIKE '%aichat%' OR key LIKE '%aiCodeBlockDiff%' \
        ORDER BY length(value) DESC LIMIT 30;" 2>/dev/null

  # Cursor 新版常见：composer.composerHeaders -> {"allComposers":[...]}。
  sqlite3 "file:$CURSOR_GLOBAL?mode=ro&immutable=1" \
    "SELECT value FROM ItemTable WHERE key = 'composer.composerHeaders';" 2>/dev/null \
    | jq '.allComposers | length' 2>/dev/null

  # 只抽元数据，不输出完整对话正文：name / subtitle / date / workspace /
  # changed lines / tracked repos. Use this as cursor_corpus and project signal.
  sqlite3 "file:$CURSOR_GLOBAL?mode=ro&immutable=1" \
    "SELECT value FROM ItemTable WHERE key = 'composer.composerHeaders';" 2>/dev/null \
    | jq -r '
        (.allComposers // [])[]
        | [
            (.name // ""),
            (.subtitle // ""),
            ((.createdAt // .lastUpdatedAt // 0) / 1000 | strftime("%Y-%m-%d")),
            (.workspaceIdentifier.uri.fsPath // .workspaceIdentifier.uri.path // ""),
            (.totalLinesAdded // 0),
            (.totalLinesRemoved // 0),
            ((.trackedGitRepos // []) | map(.repoPath // empty) | join(","))
          ] | @tsv
      ' 2>/dev/null | head -300

  # Cursor plans/spec-like work, often stored as object keys. Use keys as topic
  # signals only; do not treat them as exact session counts unless schema is clear.
  sqlite3 "file:$CURSOR_GLOBAL?mode=ro&immutable=1" \
    "SELECT value FROM ItemTable WHERE key = 'composer.planRegistry';" 2>/dev/null \
    | jq -r 'if type=="object" then keys[] else empty end' 2>/dev/null | head -200
fi

# 工作区级 chat / composer
for d in "$CURSOR_WS"/*/; do
  db="$d/state.vscdb"
  [ -f "$db" ] || continue
  ws_chat_keys=$(sqlite3 "file:$db?mode=ro&immutable=1" \
    "SELECT COUNT(*) FROM ItemTable WHERE key LIKE 'composer.%' OR key LIKE '%aichat%' OR key LIKE 'aiService.%';" 2>/dev/null)
  echo "$(basename "$d") cursor_chat_keys=$ws_chat_keys"
done
```

**期望提取**：

| 字段 | 含义 | 备注 |
| --- | --- | --- |
| `cursor_workspaces` | 打开过的项目数 | `ls workspaceStorage/*/` 计数 |
| `cursor_composer_count` | composer 会话估算 | 优先 `composer.composerHeaders.allComposers | length`；降级用 composer-related key 数 |
| `cursor_chat_session_count` | 估算的 chat session 数 | 按 `aichat`/`aiService` key 数估算 |
| `cursor_active_projects` | 有 chat / composer 的项目数 | `ws_chat_keys > 0` 的工作区数 |
| `cursor_corpus` | composer / chat 标题片段 | 从 `name` / `subtitle` / plan key 采样，用于关键词，不入报告原文 |
| `cursor_projects_from_headers` | Cursor 项目路径 | 从 `workspaceIdentifier.uri.fsPath` / `trackedGitRepos[].repoPath` 提取，最终输出仍按匿名规则处理 |
| `cursor_lines_changed_hint` | Cursor 辅助改动规模 | `Σ totalLinesAdded/Removed`，仅作为 Cursor 本地元数据参考，不与 git numstat 混为同一口径 |

时间窗口模式下，Cursor composer headers 有 `createdAt` / `lastUpdatedAt`（通常为
毫秒 epoch）时，按这些字段过滤到 `[REPORT_START, REPORT_END_EXCL)`；workspace
mtime 只能作为弱信号。无法解析时间时，只作为「检测到 Cursor 配置 / all-time
context」展示，不参与月度 sessions、active projects 或关键词。

**强烈降级提示**：跟 Trae 一样，Cursor 内部 key 没有官方稳定文档。如果
`LIKE` 没匹中任何 row，老老实实写「Cursor 本地仅检测到 workspace 数量 N，
chat / composer 内容 key 命名约定本工具暂不解析」，**不要编造 session 数**。

### 3e.3 Token 用量 —— 本地部分可见，权威数字仅云端

Cursor Pro 的精确 token 用量在云端 dashboard。本机 `ItemTable` 里可能含有
**部分** token 元数据（比如 `aiService.applyAiHistory` 等 key 内嵌 JSON
里会有 input/output token 字段），但 schema 不稳定也未公开。

本 skill 的策略：
1. **不发起任何网络请求**，云端 dashboard 永远不读。
2. 如果能从 `ItemTable` 里靠 jq 抽出 token 字段 → **作为参考值**展示，明确
   注明「Cursor 本地估算 token，非云端 dashboard 计费值」。
3. 抽不出来就老实说「Cursor token 数据由 Anysphere 云端 dashboard 持有，
   本 skill 出于『100% 本地 + 只读』原则不接入」。

### 3e.4 项目 `.cursor/` 配置（rules / mcp / ignore）

跟 Kiro `.kiro/`、Trae `.trae/` 一样，Cursor 在项目内提供 `.cursor/` 工作区
目录。这是「用户给 AI 立规矩」的一手证据。

```bash
for project_path in <candidate-paths>; do
  cursor_dir="$project_path/.cursor"
  [ -d "$cursor_dir" ] || continue
  echo "$project_path::cursor::rules=$(ls "$cursor_dir"/rules/*.{md,mdc} 2>/dev/null | wc -l)::mcp=$([ -f "$cursor_dir/mcp.json" ] && echo 1 || echo 0)::ignore=$([ -f "$cursor_dir/.cursorignore" ] && echo 1 || echo 0)"
done

# 兼容旧版根目录的 .cursorrules 单文件
for project_path in <candidate-paths>; do
  [ -f "$project_path/.cursorrules" ] && echo "$project_path::cursorrules=1"
done
```

`.cursor/rules/*.{md,mdc}` 是 markdown / Markdown-with-frontmatter，**继续沿用
Step 2.4b 的 YAML frontmatter 解析逻辑**。把它们合并进 6.2 的「AI 基础设施层」
总表，新增一列「来源 = Cursor」。

### 3e.5 Cursor 数据缺失时的诚实声明

- `~/Library/Application Support/Cursor/` 不存在 → 完全跳过 Step 3e
- 存在但 `composer.composerHeaders` 缺失 → 降级用 composer/chat key 计数与 workspace folder
- 存在但工作区 chat key 解析失败 → 仅展示「打开过的工作区数 + `.cursor/` 配置」
- token 数据本地不可信 → 明确写「权威 token 在云端 dashboard，本 skill 不联网；
  本地估算仅作参考」

---

## Step 4 — GitHub (via `gh`)

```bash
gh auth status >/dev/null 2>&1 || { echo "gh not auth'd, skipping"; }
```

If authenticated:

```bash
# Default profile mode keeps the existing 365-day GitHub window. Windowed /
# monthly mode uses REPORT_START..REPORT_END_EXCL so GitHub matches local AI
# metrics.
if [ "${WINDOW_REQUESTED:-0}" = "1" ]; then
  GH_FROM="${REPORT_START}T00:00:00Z"
  GH_TO="${REPORT_END_EXCL}T00:00:00Z"
else
  GH_FROM="$(date -u -v -365d +%Y-%m-%dT00:00:00Z)"
  GH_TO="$(date -u +%Y-%m-%dT00:00:00Z)"
fi

# GitHub contributions + top repos in the current report window
gh api graphql -f query='
query($from: DateTime!, $to: DateTime!) {
  viewer {
    login name bio
    contributionsCollection(from: $from, to: $to) {
      totalCommitContributions
      totalPullRequestContributions
      totalIssueContributions
      totalRepositoryContributions
      totalPullRequestReviewContributions
      restrictedContributionsCount
      contributionCalendar { totalContributions
        weeks { contributionDays { date contributionCount } } }
      commitContributionsByRepository(maxRepositories: 25) {
        contributions { totalCount }
        repository { nameWithOwner isPrivate isFork stargazerCount
                     primaryLanguage { name } }
      }
    }
    repositories(first: 1, ownerAffiliations: OWNER) { totalCount }
    pullRequests(first: 1) { totalCount }
    issues(first: 1) { totalCount }
  }
}' -F from="$GH_FROM" -F to="$GH_TO"
```

Then page through repositories for language bytes (up to 5 pages × 100 repos):

```bash
gh api graphql -f query='
query($cursor: String) {
  viewer { repositories(first: 100, after: $cursor, ownerAffiliations: OWNER,
                         isFork: false, orderBy: {field: UPDATED_AT, direction: DESC}) {
    pageInfo { hasNextPage endCursor }
    nodes { nameWithOwner isPrivate stargazerCount
            languages(first: 10, orderBy: {field: SIZE, direction: DESC}) {
              edges { size node { name } } } }
  } } }' -F cursor=""
```

Aggregate languages by `Σ size` per language across all repos.

---

## Step 5 — 本地 Git 提交

Build the candidate path set from:
1. Real `cwd` recovered for each `~/.claude/projects/<encoded>/`
2. `cwd` column from Codex `threads` table
3. `cwd` field in Kiro `~/.kiro/sessions/cli/*.json`
4. `folder` field in Trae `User/workspaceStorage/*/workspace.json`

Dedupe the union before running git checks.

For each path that's a git repo, count the **current user's** commits in the
current report window. Default profile mode uses the past year; monthly/range
mode uses `REPORT_START..REPORT_END_EXCL`:

```bash
me=$(git config --global user.email)

if [ "${WINDOW_REQUESTED:-0}" = "1" ]; then
  GIT_SINCE="$REPORT_START 00:00:00"
  GIT_BEFORE="$REPORT_END_EXCL 00:00:00"
else
  GIT_SINCE="1.year.ago"
  GIT_BEFORE="now"
fi

for path in <candidate-paths>; do
  [ -d "$path/.git" ] || continue
  git -C "$path" log --since="$GIT_SINCE" --before="$GIT_BEFORE" --author="$me" \
      --numstat --no-renames --pretty=format:'COMMIT|%H|%aI'
done
```

Aggregate:
- `commits` (count of `COMMIT|` lines)
- `additions`, `deletions` (sum the numstat columns)
- `last_commit_iso`
- Per-extension LOC (count `+` `-` per file extension → top 10 languages)

---

## Step 6 — 计算 10 个维度（你做推理，不要写脚本）

If `WINDOW_REQUESTED=1`, every number in Step 6 is scoped to
`REPORT_LABEL` unless explicitly labeled otherwise. Do not silently fall back to
all-time data. If the selected window has no data, generate a short honest
report that says the time range has no measurable local activity instead of
expanding the window.

For windowed reports, add a "阶段变化" interpretation by comparing the current
window with `PREV_START..PREV_END_EXCL` when enough data exists:
- activity delta: active days, Claude sessions/messages, Codex threads,
  Antigravity tasks, Kiro sessions, Trae/Cursor workspace signals
- output delta: GitHub contributions, local commits, LOC churn, active repos
- AI investment delta: verified Claude/Codex/Kiro tokens, Claude cache leverage
- mix shift: top tools, top domains, top projects, model migration, command mix
- narrative conclusion: 2-4 bullets answering "这个阶段 AI 编码带来了什么效果 /
  发生了什么变化"

If the previous comparison window has no data, use week-by-week or first-half vs
second-half changes inside the selected window. If even that is too sparse,
state that the report is a snapshot, not a trend.

### 6.1 一览
- 总活跃天数 = unique union of all dates from
  `dailyActivity` (Claude) + Codex `by_date` + Claude `history by_date`
  + Kiro `by_date` (3b.1) + `antigravity_active_days` (3d) + Trae / Cursor
  工作区最后访问日期（如果能从 `workspace.json` 或 `state.vscdb` 的 mtime
  推断；推不出就略过这两项）
- 跨度 = `min..max` of those dates
- 总 sessions / 总消息 / **claude_spent**（Σ input+output+cache_creation）
  / **claude_cache_read** / 总 codex threads / **codex_tokens** /
  **kiro_sessions** / **kiro_tokens**（如果 3b.1 拿到了）/
  **trae_workspaces** / **cursor_workspaces** + **cursor_composer_count** /
  **antigravity_tasks**（Antigravity task/session 数）
  —— 这些数字必须出现在「一览」里，缺失项显示 `—`，不要省略行
- 同期 GitHub: commits, PRs, issues, calendar_total
- 本地 git: commits / +additions / −deletions / repos

If available, include `antigravity_text_files`, `antigravity_text_chars`, `antigravity_text_lines`, and `antigravity_estimated_token_equivalent` as an Antigravity artifact scale note, not as real token usage.

- **Velocity 指标**（v2.0 新增）:
  - `commits_per_day = git_local_commits / active_days`
  - `loc_churn_per_day = (additions + deletions) / active_days`
  - `simultaneous_repos = count of repos with ≥1 commit`
  - `cross_stack_langs = count of distinct primary languages across repos`

### 6.2 AI-Native 实践（核心章节）
- **多工具 / 多模型编排**: 列出每个模型的 spent / cache_read tokens。
  - **Claude / Codex / Kiro** model breakdown 合并到同一张 token 表（Kiro
    schema 有 model 列时按 3b.1 抽取）。
  - **Trae / Cursor** 的 token 数据本地不可信（云端权威），表中标注
    「Trae: 云端 only」/「Cursor: 云端权威，本地仅参考」。
  - **Gemini Antigravity** 的 tasks/artifacts 单列展示，token 不可得时显示
    `—`，不要估算。
  - **总编排维度** = 同时活跃使用的 AI 工具数（Claude / Codex / Kiro / Trae /
    Antigravity / Cursor 六选 N）。用过 ≥ 3 个工具 → 报告里强调「多引擎
    编排者」叙事。
- **高级能力使用**: plan-mode 次数、effort 调节次数、skill 调用次数、
  自研 skills 数、hooks/MCP 数、plans/tasks 数、automations 数
- **Antigravity 任务制协作**: `antigravity_tasks`、artifact type breakdown、
  walkthrough / implementation_plan / task artifacts，用来描述「从任务 → 计划 → walkthrough」的交付闭环。
- **Prompt caching 熟练度**: cache_to_spent_ratio
- **Reasoning effort 分布**: xhigh/high/medium/low 占比
- **AI 基础设施层**（v2.0 新增 —— 这是最 AI-native 的信号）:
  列出用户亲手构建的 skills / hooks / automations / rules / steering / agents，
  每项给 `名称 | 一句话描述 | 来源（Claude/Codex/Kiro/Trae）| 调用次数（如可从 history 统计）`。
  区分「自建」（用户原创）与「安装」（第三方）。
  **跨工具复用的 skill**（同名 SKILL.md 同时出现在 `~/.claude/skills/` 和
  `~/.kiro/skills/`）单独高亮 —— 这是真正的 AI 基础设施互操作信号。
  这一段的叙事重点：**不只是 AI 的使用者，更是 AI 工作流的建设者**。

### 6.3 协作风格
- Top 10 slash commands (cmd, count, 简短解读)
- Plan-to-direct ratio = `/plan count / non-command prompt count`
- 平均消息/session = `totalMessages / totalSessions`
- 最长 session: 时长(小时) + 消息数
- **Session 架构**（v2.0 新增）:
  从 history.jsonl 中按 sessionId 分组，统计典型 session 内的命令序列模式：
  - 以 `/plan` 开头的 session 占比 → 说明「先想再做」的习惯有多强
  - session 内使用 `/compact` 或 `/clear` 的比例 → 上下文管理意识
  - `/effort` 在 session 内的切换频率 → 是否按阶段调节推理深度
  - `/resume` 使用率 = resume_count / totalSessions → session 连续性
  用 2-3 句话总结出用户的 **session 驾驭模式**（例如：
  「典型流程：/plan 规划 → 迭代 → /compact 回收上下文 → 继续交付」）

### 6.4 项目与领域
- 合并维度: each project key (real_cwd) accumulates
  `claude_session

…(truncated)
