SLOPSHOPPER

row-logger

Scratch tool: logs session.append rows to /tmp/row-logger.json

new
A shopper browsing a rack in a slop shop
README

<h1 align="center">claude-code-mod-tracking</h1>

  __  __  ___  ___    _____ ___    _   ___ _  _____ _  _  ___
 |  \/  |/ _ \|   \  |_   _| _ \  /_\ / __| |/ /_ _| \| |/ __|
 | |\/| | (_) | |) |   | | |   / / _ \ (__| ' < | || .` | (_ |
 |_|  |_|\___/|___/    |_| |_|_\/_/ \_\___|_|\_\___|_|\_|\___|

   #42 ──▶  PROJ-7 ──▶  https://…   ──▶  [ tracking  GitHub 4 (1 ✗) ]

<strong>Claude Code mods that keep track of the work around your session.</strong><br> Issues, PRs, Jira items, links, worktrees and agents — in one live pane.

<a href="https://github.com/martin-macak/claude-code-mod-tracking/actions/workflows/test.yml"><img alt="test" src="https://github.com/martin-macak/claude-code-mod-tracking/actions/workflows/test.yml/badge.svg?branch=main"></a> <a href="https://github.com/martin-macak/claude-code-mod-tracking/actions/workflows/lint.yml"><img alt="lint" src="https://github.com/martin-macak/claude-code-mod-tracking/actions/workflows/lint.yml/badge.svg?branch=main"></a> <a href="https://github.com/martin-macak/claude-code-mod-tracking/actions/workflows/release.yml"><img alt="release workflow" src="https://github.com/martin-macak/claude-code-mod-tracking/actions/workflows/release.yml/badge.svg"></a> <a href="https://github.com/martin-macak/claude-code-mod-tracking/releases/latest"><img alt="latest release" src="https://img.shields.io/github/v/release/martin-macak/claude-code-mod-tracking?display_name=release&sort=semver"></a> <a href="#requirements"><img alt="Claude Code 2.1.295 or newer" src="https://img.shields.io/badge/Claude%20Code-2.1.295%2B-D97757"></a> <a href="LICENSE"><img alt="MIT license" src="https://img.shields.io/badge/license-MIT-blue"></a> <a href="https://github.com/martin-macak/claude-code-mod-tracking/commits/main"><img alt="last commit" src="https://img.shields.io/github/last-commit/martin-macak/claude-code-mod-tracking"></a> <a href="https://github.com/martin-macak/claude-code-mod-tracking/issues"><img alt="open issues" src="https://img.shields.io/github/issues/martin-macak/claude-code-mod-tracking"></a> <a href="https://www.conventionalcommits.org/en/v1.0.0/"><img alt="Conventional Commits" src="https://img.shields.io/badge/Conventional%20Commits-1.0.0-fe5196"></a> <a href="#report-a-bug-or-ask-for-a-feature"><img alt="PRs welcome" src="https://img.shields.io/badge/PRs-welcome-brightgreen"></a>

<a href="#features">Features</a> · <a href="#installation">Installation</a> · <a href="#usage">Usage</a> · <a href="#report-a-bug-or-ask-for-a-feature">Report a bug</a> · <a href="#development">Development</a> · <a href="plugins/artifact-tracking/README.md">Full docs</a>


This repo holds Claude Code mods (function-hooks plugins) and the research behind them.

The first mod is artifact-tracking. A session mentions GitHub issues and PRs, Jira work items, Claude artifacts and web links. The mod finds them, fetches their live state and shows them in a /track pane and in a band above the prompt. You stop losing the PR from an hour ago in the scrollback.

[!NOTE] Mods are in early access. The API changes between Claude Code releases. This repo needs Claude Code 2.1.295 or newer.

Demo

artifact-tracking in a Claude Code session: a prompt mentions GitHub items and a link, the band counts them, and /track shows their live state

You mention a PR, an issue and a docs link in a normal prompt. The mod finds them, and the band above the prompt counts them at once. A second prompt adds a PR with failing checks, and the band shows (1 ✗). /track opens a pane with the live title, state and checks of each item from gh, and a section for web links. Press r to refresh every item and Esc to close the pane.

The GIF comes from a Gherkin scenario, e2e/features/tracking.feature, that make demo records. The same scenario runs as a test under make e2e.

Features

  • Zero-effort capture. Finds GitHub issue and PR URLs, owner/repo#N, bare #N, Jira URLs and web links in your prompts and in Claude's replies, also from subagents. Tool output gives explicit GitHub and Jira URLs only.
  • Live state. Shows title, open, closed or merged, failing checks, Jira status and the Epic tree. Data comes from gh (one GraphQL call for each host) and acli.
  • Always in view. A one-line band above the prompt counts the items: tracking Jira 6 · GitHub 4 (1 ✗) · Links 2 /track.
  • Interactive pane. /track opens a pane with one section for each source. Press r to refresh, x to hide an auto row, Esc to close.
  • Auto tracking. Tracks the worktrees, agents and pull requests of the session with no mention, also stacked PRs and the refs in a PR body.
  • Survives restarts. Keeps the items across /clear, --resume and --continue.
  • Fails soft. Without gh or acli, the items stay in the list and the section says why. A circuit breaker pauses a failing tool for 5 minutes.
  • Private by design. The mod stores only the extracted fields, never raw CLI output.

Installation

Requirements

  • Claude Code 2.1.295 or newer, with mods.
  • Optional: gh 2.102 or newer, logged in. It gives the live state of GitHub items.
  • Optional: acli 1.3.19 or newer, logged in. It gives the live state of Jira items.

From the marketplace (recommended)

In a Claude Code session:

/plugin install artifact-tracking --marketplace martin-macak/claude-code-mod-tracking

Answer y to add the marketplace mod-tracking, then pick a scope. From a shell:

claude plugin install artifact-tracking --marketplace martin-macak/claude-code-mod-tracking

Get a new release:

claude plugin update artifact-tracking@mod-tracking

From a release zip

Each GitHub release attaches artifact-tracking-<version>.zip, a SHA256SUMS file and a build provenance attestation. Load a zip for one session:

claude --plugin-url https://github.com/martin-macak/claude-code-mod-tracking/releases/download/artifact-tracking--v<version>/artifact-tracking-<version>.zip

From source

git clone https://github.com/martin-macak/claude-code-mod-tracking.git
cd claude-code-mod-tracking
claude --plugin-dir plugins/artifact-tracking   # one session
make install                                    # every session, as a symlink

Usage

Work as usual. Mention issues, PRs, Jira keys and links in your prompts. The band fills by itself.

CommandAction
/trackOpens the pane and refreshes the items that are due.
/track refreshRefreshes every item and waits. Use it in claude -p.
/track clearRemoves all items of this session.

The mod README lists every captured pattern, the auto-tracking rules, persistence and the known limits.

Report a bug or ask for a feature

This project is young, and your feedback shapes it. A short report helps a lot. Did something break? Is a pattern missing? Do you want a new column or a new mod? Tell us.

Search the open issues first, and add a 👍 to an issue that you also need. Pull requests are welcome too. See Contributing.

Development

Requirements: claude 2.1.295 or newer, mise with node@24, and pre-commit. gh and acli are optional.

make init      # check the tools
make hooks     # install the git hooks (once per clone)
make test      # validate, type-check and test the mod
make install   # load the mod in every session
make e2e       # run the Gherkin scenarios against a real claude (local only)
make demo      # record docs/assets/demo.gif from the @demo scenario

make e2e and make demo also need tmux, asciinema, agg and gifsicle (brew install tmux asciinema agg gifsicle). They use the Claude login and trust store of the calling shell (CLAUDE_CONFIG_DIR, else ~/.claude), and they run real model turns. To refresh the GIF, follow the demo-scenario skill.

Run make help for every target.

Layout

PathContent
plugins/The mods. One folder per mod.
e2e/Gherkin scenarios that drive a real claude in tmux, the demo recorder and their tests.
docs/knowledge-base/Research notes, one folder per problem. Start with claude-code-mods/.
docs/superpowers/specs/Design specs.
AGENTS.mdRules for agents and contributors: lint, branches, commits, PRs, issues.
MakefileBuild, test, lint and install targets.

Lint

make lint runs all hooks in .pre-commit-config.yaml on all files.

HookChecks
pre-commit-hooksWhitespace, end of file, line endings, YAML, JSON, merge markers, symlinks, large files, private keys, no commit to main.
markdownlint-cli2Markdown. Config: .markdownlint-cli2.jsonc.
biome-checkFormat, lint and import order of plugins/ and e2e/. Config: biome.json.
shellcheckShell scripts.
actionlintGitHub Actions workflows.
typechecktsc on the mod and on e2e/ (make typecheck).
conventional-pre-commit, commit-subject-caseCommit messages. See AGENTS.md.

CI/CD

WorkflowRuns
testmake test and make e2e-unit on every PR and push to main.
lintThe pre-commit hooks, the PR title check and the branch name check.
releaseOn a tag <plugin>--v<version>: validates, tests, builds the zip and publishes the GitHub release.
claude, claude-code-reviewClaude answers @claude mentions and reviews pull requests.

Release steps: plugins/artifact-tracking/README.md.

Contributing

Read AGENTS.md first. In short: branch <type>/<summary>, Conventional Commits with a sentence-case subject, one PR per change with Closes #N, squash merge. Run make verify before you push.

License

MIT © Martin Macak

Source 1 files
hooks/register.ts 38 lines
1// Observes every row `session.append` hands over and writes them to /tmp/row-logger.json.
2// A scratch tool for the live checks in this folder, not a mod to keep. Attachments are logged by name only.
3import type { EngineInterface, Register } from 'claude-code'
4
5const LOG = '/tmp/row-logger.json'
6
7const rows: unknown[] = []
8
9async function save($: EngineInterface): Promise<void> {
10  await $.fs.write(LOG, JSON.stringify(rows, null, 1))
11}
12
13export const register: Register = (on) => {
14  on('session.append', async ($, e, next) => {
15    const stored = await next(e)
16    const body = e.door === 'attachment' ? '(omitted)' : e.message.content
17    rows.push({ door: e.door, origin: e.origin, agentId: e.agentId, uuid: e.uuid, type: e.message.type, name: e.message.name, role: e.message.role, isMeta: e.message.isMeta, content: body })
18    await save($)
19    return stored
20  })
21  on('turn.complete', async ($, e, next) => {
22    rows.push({ turnComplete: { answer: e.answer, reason: e.reason, agentId: e.agentId } })
23    await save($)
24    return next(e)
25  })
26  on('session.start', async ($, e, next) => {
27    const messages = await $.session.messages()
28    rows.push({ sessionStart: { surface: e.surface, isInteractive: e.isInteractive, loaded: messages.length, messages: messages.map((m) => ({ role: m.role, text: m.text.slice(0, 160), toolUses: m.toolUses.length, toolResults: m.toolResults?.length })) } })
29    await save($)
30    return next(e)
31  })
32  on('classic.SessionStart', async ($, e, next) => {
33    rows.push({ classicSessionStart: { source: e.source } }) // never logged on this machine: sec-default bypasses user-tier hooks of this event
34    await save($)
35    return next(e)
36  })
37}
38