Skip to content

feat(hooks): index skill usage by project and session - #25

Merged
chipfighter merged 5 commits into
mainfrom
feat/usage-by-project
Sep 15, 2026
Merged

chipfighter merged 5 commits into
mainfrom
feat/usage-by-project

Conversation

@chipfighter

@chipfighter chipfighter commented Sep 15, 2026 •

Copy link
Copy Markdown
Owner

Why

The usage log exists to show where skills fire, and on Windows it could not.
Skill records took cwd from the hook payload without undoing its JSON escaping,
so C:\Users was logged as C://Users. Session records took Git Bash's $PWD,
which reads /c/Users, or /tmp/... under a mount. The same project sat under
two spellings, skills-count.sh had no per-project view, and nothing tied a skill
record back to the conversation that produced it.

Checked against a real log (2,474 records since 2026-07): every Windows project
was split this way, and 73 of the 182 skill records came from one
claude plugin eval scratch directory, which stayed invisible until usage is
broken down by project.

What changed

  • fix(hooks): the shared writer canonicalizes every path. Windows paths
    become C:/x; MSYS paths go through cygpath, the only thing that knows where
    a mount such as /tmp points. POSIX hosts are unchanged.
  • feat(hooks): skill records carry the host's session_id, appended last so
    existing readers keep working. skills-count.sh breaks skill invocations down
    by project and folds records written before the fix onto the current spelling,
    so an existing log stays comparable with new records.
  • docs: both READMEs describe the per-project summary and the session id.
  • fix(hooks), found in review: decoding the payload left real backslashes in
    skill and session_id, and the writer escaped only quotes, so odd\name
    was logged as a newline and a trailing backslash broke the record. The writer
    now escapes backslashes before quotes.
  • chore(release): both manifests bumped to 0.10.0.

No skill text, primer, or fixed-context change. The sink is still
~/.coding-discipline/usage.jsonl.

Version

Ships as v0.10.0. v0.9.0 was tagged and released from main (f45a6a2) first,
so its frozen scope stays intact; this branch is a minor bump because
per-project usage and session ids are a new capability.

Verification

Written test-first. Each new case failed on the old code for the real reason
before passing: the payload case logged "cwd":"C://Users//dev//proj", on
Windows one directory came out as /tmp/.../repo and C://Users//...//repo,
and the review case produced a record that json.loads rejects.

Review probes beyond the suites: a directory named with Chinese characters and a
space, run both through Git Bash directly and through cmd.exe and
run-hook.cmd, produced one spelling and valid UTF-8 for session and skill
records alike.

All four suites pass locally on 35e39d9: plugin metadata, the safety scan, the
hook suite under Git Bash, and the Windows suite, which drives log-usage
through run-hook.cmd the way Claude Code does on Windows. Run read-only against
the real log, the new section prints one line per project with pre-fix and
post-fix records merged.

Notes:

  • Existing session records keep their /d/... spelling. The report only folds
    skill records, because the per-project view counts skill invocations.
  • On Windows, SessionStart now spends one extra cygpath process. Acceptable
    for a once-per-session hook; noted in case startup time ever matters.

🤖 Generated with Claude Code

chipfighter and others added 5 commits September 15, 2026 14:46
Skill records took the cwd from the hook payload without undoing its JSON
escaping, so every backslash in C:\Users became a doubled forward slash.
Session records took $PWD, which Git Bash spells /d/x or, under a mount, /tmp/x.
The real log carried the same project under both spellings, so usage could not
be grouped by project.

The shared writer now canonicalizes every path: Windows paths become C:/x, and
MSYS paths go through cygpath, the only thing that knows where a mount such as
/tmp points. POSIX hosts are unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The log could say which skills fired but not where, or from which conversation.
skills-count.sh now breaks skill invocations down by project. Records written
before the path fix doubled every separator, so the report folds them onto the
current spelling and an existing log stays comparable with new records.

Skill records also carry the host's session_id. It is the only pointer from a
suspicious trigger back to the transcript that produced it, which is where a
false-trigger report has to start. The field is appended last so existing
readers of the record keep working.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
States that usage stays attributable to a project after the project is deleted,
and that the summary breaks down by host, skill, and project and accepts a
keyword to narrow it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
log-usage now decodes the payload, so a skill name or session id can hold a
real backslash. The writer escaped only quotes: odd\name was logged as a
newline, and a trailing backslash swallowed the closing quote and broke the
whole record. Found reviewing this branch; directory names are unaffected
because canonical paths carry no backslashes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Per-project usage and session ids on skill records are a new capability, so
this is a minor bump. No skill behavior changes and the fixed-context baseline
is untouched at 4580.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chipfighter
chipfighter merged commit 1054b11 into main Sep 15, 2026
2 checks passed
@chipfighter
chipfighter deleted the feat/usage-by-project branch September 15, 2026 06:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant