Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 0.0.3 — 2026-03-12

- Add `/cfo` skill — session cost banner + 30-day AI spend dashboard. Reads token usage directly from `~/.claude/projects/` transcripts. Breaks spend down by day, project/branch, detects repeated prompts, and gives personalized cost-saving tips. Includes `ai_spend.py`, a standalone Python dashboard that also supports team/org views and manual logging of Cursor, Lovable, ChatGPT, and other AI tool spend.

## 0.0.2 — 2026-03-12

- Fix project-local `/browse` installs — compiled binary now resolves `server.ts` from its own directory instead of assuming a global install exists
Expand Down
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ gstack/
├── review/ # PR review skill
├── plan-ceo-review/ # /plan-ceo-review skill
├── plan-eng-review/ # /plan-eng-review skill
├── cfo/ # /cfo skill — session cost + project spend dashboard
├── retro/ # Retrospective skill
├── setup # One-time setup: build binary + symlink skills
├── SKILL.md # Browse skill (Claude discovers this)
Expand Down
73 changes: 67 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Six opinionated workflow skills for [Claude Code](https://docs.anthropic.com/en/
| `/ship` | Release engineer | Sync main, run tests, push, open PR. For a ready branch, not for deciding what to build. |
| `/browse` | QA engineer | Give the agent eyes. It logs in, clicks through your app, takes screenshots, catches breakage. Full QA pass in 60 seconds. |
| `/retro` | Engineering manager | Analyze commit history, work patterns, and shipping velocity for the week. |
| `/cfo` | CFO | Show current session cost and elapsed time, then run the full AI spend dashboard for the project. |

## Demo: one feature, five modes

Expand Down Expand Up @@ -76,17 +77,17 @@ This is not a prompt pack for beginners. It is an operating system for people wh

## Install

**Requirements:** [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [Git](https://git-scm.com/), [Bun](https://bun.sh/) v1.0+. `/browse` compiles a native binary — works on macOS and Linux (x64 and arm64).
**Requirements:** [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [Git](https://git-scm.com/), [Bun](https://bun.sh/) v1.0+, `jq` (`brew install jq`), Python 3.10+. `/browse` compiles a native binary — works on macOS and Linux (x64 and arm64).

### Step 1: Install on your machine

Open Claude Code and paste this. Claude will do the rest.

> Install gstack: run `git clone https://github.com/garrytan/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup` then add a "gstack" section to CLAUDE.md that says to use the /browse skill from gstack for all web browsing, never use mcp\_\_claude-in-chrome\_\_\* tools, and lists the available skills: /plan-ceo-review, /plan-eng-review, /review, /ship, /browse, /retro. Then ask the user if they also want to add gstack to the current project so teammates get it.
> Install gstack: run `git clone https://github.com/garrytan/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup` then add a "gstack" section to CLAUDE.md that says to use the /browse skill from gstack for all web browsing, never use mcp\_\_claude-in-chrome\_\_\* tools, and lists the available skills: /plan-ceo-review, /plan-eng-review, /review, /ship, /browse, /retro, /cfo. Then ask the user if they also want to add gstack to the current project so teammates get it.

### Step 2: Add to your repo so teammates get it (optional)

> Add gstack to this project: run `cp -Rf ~/.claude/skills/gstack .claude/skills/gstack && rm -rf .claude/skills/gstack/.git && cd .claude/skills/gstack && ./setup` then add a "gstack" section to this project's CLAUDE.md that says to use the /browse skill from gstack for all web browsing, never use mcp\_\_claude-in-chrome\_\_\* tools, lists the available skills: /plan-ceo-review, /plan-eng-review, /review, /ship, /browse, /retro, and tells Claude that if gstack skills aren't working, run `cd .claude/skills/gstack && ./setup` to build the binary and register skills.
> Add gstack to this project: run `cp -Rf ~/.claude/skills/gstack .claude/skills/gstack && rm -rf .claude/skills/gstack/.git && cd .claude/skills/gstack && ./setup` then add a "gstack" section to this project's CLAUDE.md that says to use the /browse skill from gstack for all web browsing, never use mcp\_\_claude-in-chrome\_\_\* tools, lists the available skills: /plan-ceo-review, /plan-eng-review, /review, /ship, /browse, /retro, /cfo, and tells Claude that if gstack skills aren't working, run `cd .claude/skills/gstack && ./setup` to build the binary and register skills.

Real files get committed to your repo (not a submodule), so `git clone` just works. The binary and node\_modules are gitignored — teammates just need to run `cd .claude/skills/gstack && ./setup` once to build (or `/browse` handles it automatically on first use).

Expand Down Expand Up @@ -383,6 +384,66 @@ It saves a JSON snapshot to `.context/retros/` so the next run can show trends.

---

## `/cfo`

This is my **CFO mode**.

I want to know what I'm spending before I forget to check. `/cfo` tells me what the current session cost, how long I've been in it, and what the total spend on the project looks like over the last 30 days.

It reads Claude Code's session transcripts directly — no API keys, no third-party tracking. Just token counts from `~/.claude/projects/` turned into dollars.

```
You: /cfo

Claude:
╔══════════════════════════════════════════════════╗
║ This session ║
╠══════════════════════════════════════════════════╣
║ $4.81 · 6h 30m · +122 −14 ║
╚══════════════════════════════════════════════════╝

AI Spend Dashboard · @you · my-project · last 30 days

Daily Spend
────────────────────────────────────────────────────────────
Mar 12 ██████████████████████ $36.23 ✓ 36% of budget

Spend per Feature / Branch
────────────────────────────────────────────────────────────
my-project / main ████████████████████ $36.23

Prompt Repetition (potential waste)
────────────────────────────────────────────────────────────
Estimated waste from repetition: $1.29

Cost-Saving Strategies
────────────────────────────────────────────────────────────
1. Excellent cache hit rate (93%). Keep sessions open rather than starting fresh.
2. Average session cost is $18. Scope one feature per session.
...
```

The dashboard breaks spend down by day, by project/branch, detects repeated prompts that are wasting money, and gives personalized cost-saving tips based on your actual usage patterns.

You can also log spend from other AI tools (Cursor, Lovable, ChatGPT, etc.) and see a unified view:

```bash
python3 ~/.claude/skills/cfo/ai_spend.py --add
# Tool name: Cursor
# Cost in USD: 5.00
# Date: today
```

And if your team shares a directory, you can see aggregated spend across everyone:

```bash
python3 ~/.claude/skills/cfo/ai_spend.py --setup # configure team_dir
python3 ~/.claude/skills/cfo/ai_spend.py --export # share your data
python3 ~/.claude/skills/cfo/ai_spend.py --team # see the full team
```

---

## Troubleshooting

**Skill not showing up in Claude Code?**
Expand All @@ -392,7 +453,7 @@ Run `cd ~/.claude/skills/gstack && ./setup` (or `cd .claude/skills/gstack && ./s
Run `cd ~/.claude/skills/gstack && bun install && bun run build`. This compiles the browser binary. Requires Bun v1.0+.

**Project copy is stale?**
Re-copy from global: `for s in browse plan-ceo-review plan-eng-review review ship retro; do rm -f .claude/skills/$s; done && rm -rf .claude/skills/gstack && cp -Rf ~/.claude/skills/gstack .claude/skills/gstack && rm -rf .claude/skills/gstack/.git && cd .claude/skills/gstack && ./setup`
Re-copy from global: `for s in browse plan-ceo-review plan-eng-review review ship retro cfo; do rm -f .claude/skills/$s; done && rm -rf .claude/skills/gstack && cp -Rf ~/.claude/skills/gstack .claude/skills/gstack && rm -rf .claude/skills/gstack/.git && cd .claude/skills/gstack && ./setup`

**`bun` not installed?**
Install it: `curl -fsSL https://bun.sh/install | bash`
Expand All @@ -401,15 +462,15 @@ Install it: `curl -fsSL https://bun.sh/install | bash`

Paste this into Claude Code:

> Update gstack: run `cd ~/.claude/skills/gstack && git fetch origin && git reset --hard origin/main && ./setup`. If this project also has gstack at .claude/skills/gstack, update it too: run `for s in browse plan-ceo-review plan-eng-review review ship retro; do rm -f .claude/skills/$s; done && rm -rf .claude/skills/gstack && cp -Rf ~/.claude/skills/gstack .claude/skills/gstack && rm -rf .claude/skills/gstack/.git && cd .claude/skills/gstack && ./setup`
> Update gstack: run `cd ~/.claude/skills/gstack && git fetch origin && git reset --hard origin/main && ./setup`. If this project also has gstack at .claude/skills/gstack, update it too: run `for s in browse plan-ceo-review plan-eng-review review ship retro cfo; do rm -f .claude/skills/$s; done && rm -rf .claude/skills/gstack && cp -Rf ~/.claude/skills/gstack .claude/skills/gstack && rm -rf .claude/skills/gstack/.git && cd .claude/skills/gstack && ./setup`

The `setup` script rebuilds the browser binary and re-symlinks skills. It takes a few seconds.

## Uninstalling

Paste this into Claude Code:

> Uninstall gstack: remove the skill symlinks by running `for s in browse plan-ceo-review plan-eng-review review ship retro; do rm -f ~/.claude/skills/$s; done` then run `rm -rf ~/.claude/skills/gstack` and remove the gstack section from CLAUDE.md. If this project also has gstack at .claude/skills/gstack, remove it by running `for s in browse plan-ceo-review plan-eng-review review ship retro; do rm -f .claude/skills/$s; done && rm -rf .claude/skills/gstack` and remove the gstack section from the project CLAUDE.md too.
> Uninstall gstack: remove the skill symlinks by running `for s in browse plan-ceo-review plan-eng-review review ship retro cfo; do rm -f ~/.claude/skills/$s; done` then run `rm -rf ~/.claude/skills/gstack` and remove the gstack section from CLAUDE.md. If this project also has gstack at .claude/skills/gstack, remove it by running `for s in browse plan-ceo-review plan-eng-review review ship retro cfo; do rm -f .claude/skills/$s; done && rm -rf .claude/skills/gstack` and remove the gstack section from the project CLAUDE.md too.

## Development

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.0.2
0.0.3
103 changes: 103 additions & 0 deletions cfo/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
---
name: cfo
version: 1.0.0
description: |
CFO mode: show current session cost and elapsed time, then run the AI spend
dashboard for this project. Tells you exactly what you've spent this session
and in total on the project.
allowed-tools:
- Bash
- Read
---

# /cfo — Session Cost + Project Spend Dashboard

You are running the `/cfo` skill. Show what this session cost and what the total project spend looks like.

## Step 1 — Locate the ai_spend.py script

```bash
if [ -f .claude/skills/cfo/ai_spend.py ]; then
echo "SCRIPT=.claude/skills/cfo/ai_spend.py"
elif [ -f ~/.claude/skills/cfo/ai_spend.py ]; then
echo "SCRIPT=$HOME/.claude/skills/cfo/ai_spend.py"
else
echo "SCRIPT=NOT_FOUND"
fi
```

If `NOT_FOUND`, tell the user: "ai_spend.py not found. Run `cd ~/.claude/skills/gstack && ./setup` (user install) or `cd .claude/skills/gstack && ./setup` (project install) to reinstall." Then stop.

Set `SCRIPT` to whichever path was found.

## Step 2 — Gather all session metrics in one call

Run this entire block as a single bash command. It finds the transcript, computes cost and elapsed time, and gets git stats — all in one shell so variables don't get lost between calls.

Note: cost is computed using Claude Sonnet pricing (input $3/MTok, cache_write $3.75/MTok, cache_read $0.30/MTok, output $15/MTok). If using Opus or Haiku the estimate will differ; the full dashboard in Step 3 uses per-model pricing.

```bash
PROJECT_DIR=$(pwd | sed 's|/|-|g')
TRANSCRIPT=$(ls -t "$HOME/.claude/projects/$PROJECT_DIR"/*.jsonl 2>/dev/null | head -1)

if [ -z "$TRANSCRIPT" ]; then
echo "SESSION_COST=unavailable"
echo "ELAPSED=unavailable"
else
# Compute cost and timestamps in one awk pass
eval $(jq -r 'select(.message.usage) | [
(.message.usage.input_tokens // 0),
(.message.usage.cache_creation_input_tokens // 0),
(.message.usage.cache_read_input_tokens // 0),
(.message.usage.output_tokens // 0),
(.timestamp // "")
] | @tsv' "$TRANSCRIPT" | awk -F'\t' '
NR==1 { first_ts = $5 }
{ input += $1; cache_write += $2; cache_read += $3; output += $4; last_ts = $5 }
END {
cost = (input * 3 + cache_write * 3.75 + cache_read * 0.30 + output * 15) / 1000000
printf "SESSION_COST=$%.4f\nFIRST_TS=%s\nLAST_TS=%s\n", cost, first_ts, last_ts
}')

# Compute elapsed time
ELAPSED=$(python3 -c "
from datetime import datetime
def p(s): return datetime.fromisoformat(s.replace('Z','+00:00'))
diff = p('$LAST_TS') - p('$FIRST_TS')
m = int(diff.total_seconds() / 60)
print(f'{m//60}h {m%60}m' if m >= 60 else f'{m}m')
" 2>/dev/null || echo "unavailable")

echo "SESSION_COST=$SESSION_COST"
echo "ELAPSED=$ELAPSED"
fi

# Git stats
GIT_STATS=$(git diff --stat HEAD 2>/dev/null | tail -1)
echo "GIT_STATS=${GIT_STATS:-none}"
```

Use the SESSION_COST, ELAPSED, and GIT_STATS values for the banner in Step 4.

## Step 3 — Run the project spend dashboard

```bash
python3 "$SCRIPT" "$(pwd)" --days 30
```

## Step 4 — Print the session banner, then the dashboard

Print this banner using the values from Step 2:

```
╔══════════════════════════════════════════════════╗
║ This session ║
╠══════════════════════════════════════════════════╣
║ $<session_cost> · <elapsed> · <git +N −N> ║
╚══════════════════════════════════════════════════╝
```

Then output the full ai_spend.py dashboard (Step 3 output) below it.

- If SESSION_COST is "unavailable", omit the cost from the banner and note that the transcript was not found.
- If GIT_STATS is "none", omit the git portion of the banner line.
Loading