claudex_cli/skill/
templates.rs1pub const AGENTS_START: &str = "<!-- claudex:start -->";
15pub const AGENTS_END: &str = "<!-- claudex:end -->";
16
17#[derive(Debug, Clone, Copy)]
19pub enum Flavor {
20 ClaudeCode,
22 Codex,
24 Pi,
26 OpenClaw,
28}
29
30pub fn description() -> &'static str {
32 "Query, search, and analyze Claude Code, OpenAI Codex, Pi, and OpenClaw sessions using the claudex CLI. \
33Use when asked about session history, token costs, tool usage, search past conversations, export \
34sessions, or inspect agent activity across providers and projects."
35}
36
37pub fn command_list(root: &clap::Command) -> String {
40 let mut lines = Vec::new();
41 for sub in root.get_subcommands() {
42 if sub.is_hide_set() {
43 continue;
44 }
45 let name = sub.get_name();
46 let about = sub
47 .get_about()
48 .map(|about| about.to_string())
49 .unwrap_or_default();
50 lines.push(format!("- `claudex {name}` — {about}"));
51 }
52 lines.join("\n")
53}
54
55fn body(command_list: &str) -> String {
57 format!(
58 r#"# claudex — multi-provider agent session analytics
59
60claudex indexes the local session transcripts of four coding agents into a
61SQLite database at `~/.claudex/index.db` and reports across all of them:
62
63- **Claude Code** — `~/.claude/projects/**.jsonl`
64- **OpenAI Codex** — `~/.codex/sessions/**` and `~/.codex/archived_sessions/`
65- **Pi** — `~/.pi/agent/sessions/**`
66- **OpenClaw** — `${{OPENCLAW_STATE_DIR:-~/.openclaw}}/agents/*/sessions/`
67
68Every reporting command spans all four providers by default. The index is
69**additive**: sessions archived or deleted from disk are retained, so historical
70usage never disappears.
71
72## Commands
73
74{command_list}
75
76Run `claudex <command> --help` for full flags.
77
78## Filtering
79
80| Flag | Effect |
81| --- | --- |
82| `--provider <claude\|codex\|pi\|openclaw>` | Restrict indexed reports to provider(s); repeatable or comma-separated. Default: all. |
83| `--model <substr>` | Filter indexed reports by model (e.g. `opus`, `gpt-5`). |
84| `--since <when>` / `--until <when>` | Date range. Accepts `YYYY-MM-DD`, RFC3339, or a relative span (`7d`, `12h`, `2w`). |
85| `--on-disk-only` | Exclude retained sessions whose file was archived/deleted. |
86| `--project <substr>` | Filter by project path substring on commands that expose project scoping. |
87| `--json` | Machine-readable output. Row-oriented reports include a `provider` key per row. |
88| `--no-index` | Scan Claude transcripts directly; this rejects non-Claude providers. |
89
90Provider/date/model filters work on indexed reporting commands including
91`summary`, `sessions`, `cost`, `tools`, `models`, `search`, `turns`, `prs`, and
92`files`. Session drill-down resolves OpenClaw/Codex/Pi sessions through indexed
93records. Use `--no-index` only for Claude transcript recovery/debugging.
94
95## When to use
96
97- "How much have I spent on Codex this month?" → `claudex cost --provider codex --since 30d`
98- "List my recent Pi sessions" → `claudex sessions --provider pi`
99- "Search OpenClaw trajectory-backed sessions" → `claudex search "tool timeout" --provider openclaw --json`
100- "Find where I discussed schema migrations" → `claudex search "schema migration"`
101- "What did session e1a2f4 do?" → `claudex session e1a2f4`
102- "Overall dashboard" → `claudex summary`
103- "Model cost breakdown across providers" → `claudex models`
104
105## Output for agents
106
107Add `--json` to any reporting command for stable, scriptable output. Each row
108carries a `"provider"` key so results are unambiguous across providers. Cost is
109in USD; Pi/OpenClaw sessions report the provider's own per-message cost when
110available (local models are $0), Claude/Codex are priced from a built-in
111per-model table.
112
113## Notes
114
115- The index refreshes automatically (staleness window 5 min); `claudex index`
116 forces a sync, `claudex index --force` wipes and rebuilds (the only path that
117 discards retained data).
118- Worktree sessions roll up to their parent project; Claude subagent transcripts
119 roll up to their parent session.
120"#
121 )
122}
123
124pub fn skill_md(flavor: Flavor, command_list: &str) -> String {
126 let description = description();
127 let frontmatter = match flavor {
128 Flavor::ClaudeCode => format!(
129 "---\nname: claudex\ndescription: {description}\nargument-hint: [subcommand or query]\nallowed-tools: Bash(claudex:*), Read, Glob, Grep\nlicense: MIT\n---\n"
130 ),
131 Flavor::Codex => format!("---\nname: claudex\ndescription: {description}\n---\n"),
132 Flavor::Pi => format!(
133 "---\nname: claudex\ndescription: {description}\nallowed-tools: Bash(claudex:*)\nlicense: MIT\n---\n"
134 ),
135 Flavor::OpenClaw => format!("---\nname: claudex\ndescription: {description}\n---\n"),
136 };
137 format!("{frontmatter}\n{}", body(command_list))
138}
139
140pub fn agents_block(command_list: &str) -> String {
142 format!(
143 r#"{AGENTS_START}
144## claudex — agent session analytics
145
146Use the `claudex` CLI to query and analyze local Claude Code, OpenAI Codex, Pi,
147and OpenClaw session transcripts: token cost, tool usage, full-text search,
148per-session drill-down, and exports — across all four providers from one index.
149
150- **Spans providers by default;** narrow with `--provider claude|codex|pi|openclaw`.
151- **Filter** with `--project`, `--model`, `--since`/`--until` (`7d`/`2w`/dates).
152- **JSON for agents:** add `--json`; every row carries a `provider` key.
153- The index is additive — archived/deleted sessions are retained.
154
155Commands:
156
157{command_list}
158
159Run `claudex --help` or `claudex <command> --help` for full flags.
160{AGENTS_END}"#
161 )
162}
163
164pub fn plugin_json() -> String {
166 let manifest = serde_json::json!({
167 "name": "claudex",
168 "description": description(),
169 "version": env!("CARGO_PKG_VERSION"),
170 "author": { "name": "James Brink" },
171 "homepage": "https://utensils.io/claudex/",
172 "repository": "https://github.com/utensils/claudex",
173 "license": "MIT",
174 "keywords": ["claude-code", "codex", "pi", "openclaw", "cli", "agents", "sessions"],
175 });
176 serde_json::to_string_pretty(&manifest).unwrap_or_default()
177}