$npx -y skills add Borda/AI-Rig --skill profileSession clock-time analyzer. Reads the foundry plugin''s timings.jsonl and invocations.jsonl logs (written by task-log.js) and produces a per-session and per-skill wall-time breakdown — local-tool work vs subagent spawns vs Skill invocations vs AskUserQuestion idle vs main-loop r
| 1 | <objective> |
| 2 | |
| 3 | Bucket session clock time from existing `~/.claude/logs/{timings,invocations}.jsonl` into: |
| 4 | |
| 5 | 1. **Local tools** — Bash/Read/Edit/Write/Grep/Glob and other main-process tools |
| 6 | 2. **Agent / subagent spawns** — Task/Agent calls (sync + background) |
| 7 | 3. **Skill** — `tool=Skill` wall durations |
| 8 | 4. **AskUserQuestion idle** — human-wait, separate column, excluded from compute total |
| 9 | 5. **Main-loop reasoning (residual)** — session wall minus buckets above |
| 10 | |
| 11 | Outputs a markdown report at `.reports/profile/<UTC-timestamp>/report.md` plus a `.temp/output-profile-...md` copy. Includes per-session table, per-skill rollup, and top-N longest single calls. |
| 12 | |
| 13 | NOT for: token or cost accounting (model field null in current logs); per-line Python perf (use `foundry:perf-optimizer`); known failure diagnosis (use `/foundry:investigate`). |
| 14 | |
| 15 | </objective> |
| 16 | |
| 17 | <inputs> |
| 18 | |
| 19 | - **`--since DURATION`** (default `24h`) — window: `NNs|NNm|NNh|NNd` |
| 20 | - **`--session-id ID`** — optional; restrict to one session |
| 21 | - **`--top-n N`** (default `5`) — slowest single calls to list |
| 22 | |
| 23 | If $ARGUMENTS empty, default window is 24h. |
| 24 | |
| 25 | </inputs> |
| 26 | |
| 27 | <workflow> |
| 28 | |
| 29 | **Task tracking**: TaskCreate one task ("Run analyzer + render report"); mark in_progress before Bash, completed before final output. |
| 30 | |
| 31 | ## Step 1: Parse args + create run dir |
| 32 | |
| 33 | ```bash |
| 34 | export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}" |
| 35 | # timeout: 5000 |
| 36 | SINCE="24h" |
| 37 | SESSION_ID="" |
| 38 | TOP_N="5" |
| 39 | for tok in $ARGUMENTS; do |
| 40 | case "$tok" in |
| 41 | --since=*) SINCE="${tok#--since=}" ;; |
| 42 | --since) next_is_since=1 ;; |
| 43 | --session-id=*) SESSION_ID="${tok#--session-id=}" ;; |
| 44 | --top-n=*) TOP_N="${tok#--top-n=}" ;; |
| 45 | *) |
| 46 | if [ "${next_is_since:-0}" = "1" ]; then SINCE="$tok"; next_is_since=0; fi |
| 47 | ;; |
| 48 | esac |
| 49 | done |
| 50 | STAMP="$(date -u +%Y-%m-%dT%H-%M-%SZ)" |
| 51 | REPORT_DIR=".reports/profile/$STAMP" |
| 52 | mkdir -p "$REPORT_DIR" |
| 53 | { |
| 54 | echo "REPORT_DIR=$REPORT_DIR" |
| 55 | echo "SINCE=$SINCE" |
| 56 | echo "SESSION_ID=$SESSION_ID" |
| 57 | echo "TOP_N=$TOP_N" |
| 58 | } | tee "${TMPDIR:-/tmp}/foundry-profile-state-${CSID}" |
| 59 | ``` |
| 60 | |
| 61 | Values persisted to `${TMPDIR:-/tmp}/foundry-profile-state-${CSID}`; Steps 2–3 re-source it (bash state does not persist across Bash calls, and `REPORT_DIR` carries a per-shell timestamp that cannot be re-derived). |
| 62 | |
| 63 | ## Step 2: Run analyzer |
| 64 | |
| 65 | ```bash |
| 66 | export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}" |
| 67 | # timeout: 60000 |
| 68 | . "${TMPDIR:-/tmp}/foundry-profile-state-${CSID}" 2>/dev/null # reload REPORT_DIR/SINCE/SESSION_ID/TOP_N (fresh shell) |
| 69 | OPT_SID="" |
| 70 | [ -n "$SESSION_ID" ] && OPT_SID="--session-id $SESSION_ID" |
| 71 | python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/timing_analyzer.py" \ |
| 72 | --since "$SINCE" \ |
| 73 | --top-n "$TOP_N" \ |
| 74 | --output "$REPORT_DIR/report.md" \ |
| 75 | $OPT_SID 2>"$REPORT_DIR/warnings.log" |
| 76 | echo "exit=$?" |
| 77 | ``` |
| 78 | |
| 79 | `OPT_SID` left unquoted so empty expands to nothing (no flag). Exit code 1 → no sessions in window — surface that and stop. |
| 80 | |
| 81 | ## Step 3: Mark run complete |
| 82 | |
| 83 | ```bash |
| 84 | export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}" |
| 85 | # timeout: 5000 |
| 86 | . "${TMPDIR:-/tmp}/foundry-profile-state-${CSID}" 2>/dev/null # reload REPORT_DIR/SINCE/SESSION_ID/TOP_N (fresh shell) |
| 87 | echo '{"status":"complete","since":"'"$SINCE"'","session_id":"'"$SESSION_ID"'","top_n":'"$TOP_N"'}' > "$REPORT_DIR/result.jsonl" |
| 88 | ``` |
| 89 | |
| 90 | ## Step 4: Emit terminal output |
| 91 | |
| 92 | Read YAML header from `$REPORT_DIR/report.md` (first block between `---` lines) and print verbatim. Then print `→ $REPORT_DIR/report.md`. Then read Headline split block plus top 3 sessions from per-session table and surface as executive summary (per quality-gates.md output routing). |
| 93 | |
| 94 | Also Write the long-output dump per quality-gates rule: |
| 95 | |
| 96 | ```text |
| 97 | Write(file_path=".temp/output-profile-<branch>-<YYYY-MM-DD>.md", content=<full report contents>) |
| 98 | ``` |
| 99 | |
| 100 | Where `<branch>` = `$(git branch --show-current 2>/dev/null | tr '/' '-' || echo 'main')`. |
| 101 | |
| 102 | ## Step 5: Follow-up gate |
| 103 | |
| 104 | Invoke `AskUserQuestion`: |
| 105 | - (a) Drill into slowest session — re-run |