npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

next-issue

v0.6.0

Published

Autonomous issue harness driven by the Claude Agent SDK

Downloads

1,060

Readme

next-issue

A small harness that works through the open issues of a GitHub repository with the Claude Agent SDK. Run it inside a git repository.

npx -y next-issue@latest

Or install it once:

npm install -g next-issue@latest
next-issue

What it does

For each open issue, oldest first:

  1. Claim the issue only when it holds the status:todo label, holds no status:in-progress or status:in-review label, and has no assignee. A status:done, status:needs-human or status:blocked label always stops the claim. An issue with saved state under .next-issue/ resumes instead, whatever its labels, unless another person is now the assignee.
  2. Assign the issue to you and add status:in-progress.
  3. Fetch the default branch, fast-forward the local copy of it when that is safe, then add a git worktree on a new issue-<n> branch from it.
  4. Let the implementer agent do the work, then commit and push.
  5. Open a draft pull request that closes the issue.
  6. Wait for the checks. A pending state waits again, up to checkTimeoutMinutes. A repository without checks goes straight to the review.
  7. On a red build, give the failed job logs to the fixer agent, then go to step 6 again. The budget is maxCiFixes.
  8. Let the reviewer agent judge the diff. The reviewer returns a structured verdict that rates each finding blocking or minor. The harness puts the result on the pull request.
  9. No blocking finding means approval: mark the pull request ready for review and set status:done. The merge stays with you.
  10. With a blocking finding, run the fixer agent and go to step 6 again. The budget is maxReviewRounds.

Stops for a loop

The harness hands the issue to a person, with the status:needs-human label, when:

  • the check budget or the review budget runs out;
  • a fixer round adds no commit, so there is no progress;
  • the reviewer repeats a finding set from an earlier round, which shows a ping-pong between the fixer and the reviewer;
  • the reviewer gives no verdict, or the checks do not finish in time;
  • a step of the run fails with an error.

The fixer sees the findings of all earlier rounds, not only the last one. From round two, the reviewer judges only the earlier findings and any regression.

Stop after the current issue

Run touch .next-issue/stop in a second terminal. The harness reads the file only between two issues, finishes the issue that it holds, deletes the file and then writes the summary as usual. A file from an earlier run is dropped at the start, so it stops nothing.

Ctrl-C is different: the terminal signals every child too, so the run dies in the middle of a step. The saved state lets the next run continue the issue, but the issue keeps status:in-progress and the run writes no summary.

Every status label that the harness puts on the issue goes on the pull request too, from the first review round. Thus a draft pull request shows work that the harness has not finished, and a status:needs-human pull request shows work that waits for a person.

The harness runs all git and gh commands itself. The agents only read and change files. The commit holds all the changes in the worktree, so a setup command that writes a file outside .gitignore puts that file in the commit.

Safety

The implementer and the fixer run with the permission checks off. They can run any command in the worktree. The prompt holds the title, the body and the comments of the issue, which come from GitHub.

Thus a person who can write an issue or a comment can try to give an instruction to the agent. Use the harness only on a repository where you trust the issue authors, or run it in a container.

Observability

Every run has an id, <timestamp>, and writes two files under .next-issue/runs/:

  • <id>.jsonl — one JSON object per event, with ts, runId, kind, and the issue, role or round in scope;
  • <id>.summary.json — token totals per agent role, event counts, and one report per issue with the outcome, the reason, the pull request number and the budgets used.

A short text summary of the same data goes to standard output. Use --json to get the full summary object there instead, so the harness fits in a pipe. All progress goes to standard error.

Recorded for each step: claim, comments, worktree, implement, push, pull-request, checks, review and fix, each with a .start, .end or .error event and a duration in ms. Every git and gh command is recorded with its exit code and duration. A tool event holds the tool name, and for a Bash tool the command too. Each agent call adds a usage event with the tokens, the cost estimate in dollars, the turn count, the tool-call count, the model and the session id, so you can open the session again and read the full transcript.

jq -r 'select(.kind=="usage") | [.issue,.role,.turns,.total] | @tsv' \
  .next-issue/runs/*.jsonl

Three levels of console output:

| Level | Shows | | --- | --- | | --quiet | Run and issue milestones, verdicts, hand-overs, errors | | default | The above plus each step end, each tool call and failed commands | | --verbose | The above plus every command and all agent text |

Requirements

  • Node 24 or later, because the command runs the TypeScript source directly
  • gh, authenticated with write access to the repository
  • Anthropic credentials for the Claude Agent SDK: ANTHROPIC_API_KEY, or a Claude subscription login

Configuration

To write the file with the defaults, run the init command in the repository:

npx -y next-issue@latest init

The command writes next-issue.config.json in the root of the repository and adds .next-issue/ to .gitignore. It stops when the config file is there already; --force replaces it. The file holds no setupCommand, because that field has no default.

A field that is not in the file keeps its default. A file that is not valid JSON stops the run.

{
  "remote": "origin",
  "issueLimit": 100,
  "maxCiFixes": 3,
  "maxReviewRounds": 3,
  "checkIntervalSeconds": 15,
  "checkTimeoutMinutes": 60,
  "logMaxChars": 20000,
  "draftPullRequest": true,
  "setupCommand": "npm ci",
  "models": {
    "implementer": "claude-opus-5",
    "reviewer": "claude-opus-5",
    "fixer": "claude-opus-5"
  },
  "labels": {
    "ready": "status:todo",
    "inProgress": "status:in-progress",
    "inReview": "status:in-review",
    "done": "status:done",
    "needsHuman": "status:needs-human",
    "skip": "status:blocked"
  }
}

| Field | Default | Effect | | --- | --- | --- | | remote | origin | The git remote for the repository and the branches | | issueLimit | 100 | The maximum number of open issues to read | | maxCiFixes | 3 | The budget for check fixes per issue | | maxReviewRounds | 3 | The budget for review rounds per issue | | checkIntervalSeconds | 15 | The time between two check states | | checkTimeoutMinutes | 60 | The limit for one check wait | | logMaxChars | 20000 | The maximum length of the failed job logs | | draftPullRequest | true | Open the pull request as a draft, until the review approves | | setupCommand | none | A shell command to run in a new worktree, before the implementer | | models | {} | The model per agent role | | labels | see above | The names of the labels that the harness reads and sets |

An empty labels.ready turns the ready requirement off. The harness then claims every open issue that no other label and no assignee holds back.

The log gives the reason for a skipped issue as stop-label, not-ready, in-flight or assigned. --issue obeys the same rules; it only makes the harness look at one issue.

A role without an entry in models uses the default model of the SDK. A setupCommand that fails hands the issue to a person. State per issue goes to .next-issue/<issue>.json, so a new run continues where the last run stopped.

Commands and options

| Argument | Effect | | --- | --- | | init | Write the config file with the defaults | | --force | Replace an existing config file, with init | | --issue <n> | Handle one issue only | | --once | Stop after the first handled issue | | --max <n> | Handle at most n issues | | --reset | Drop the saved budgets and findings first | | --json | Print the machine summary instead of the text one | | --verbose | Show every command and all agent output | | --quiet | Show only the milestones and the summary | | --help | Show the option list |

Development

npm install
npm run typecheck
npm test
npm run build

npm run build compiles src/*.mts to dist/*.mjs. The package holds the compiled files, because Node does not strip types under node_modules.

Releases

Releases are automatic and need no pull request. Push a conventional commit to main. The workflow reads the commits since the last tag and picks the bump: feat gives a minor, fix gives a patch, and a ! mark or a BREAKING CHANGE body gives a major. Any other type releases nothing.

The workflow then bumps the version and publishes the package to npm with a trusted publish, so no token is stored. Only after the publish does it push the tag and make the GitHub release, so a failed publish leaves no half-release behind. A manual run of the workflow publishes the version in package.json and changes nothing else, which repairs a release that reached GitHub but not npm. The release job stays on a GitHub runner, because npm takes provenance only from a GitHub runner.