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

lastlight

v0.1.15

Published

GitHub repository maintenance agent — Agent SDK harness

Readme

An AI agent that maintains GitHub repositories: triaging issues, reviewing PRs, monitoring repo health, and building features through an Architect → Executor → Reviewer development cycle.

Built on the Claude Agent SDK with a lightweight TypeScript harness for webhook ingestion, cron scheduling, and process management.

Production Setup (Clean Server)

The fastest way to go from a bare server to a running Last Light instance:

npx lastlight setup

The setup wizard walks you through:

  1. GitHub App — enter your App ID, Installation ID, and PEM key path
  2. Anthropic API key — for the Claude Agent SDK
  3. Webhook secret — auto-generated if you don't have one
  4. Domain & TLS — optional Caddy config for automatic HTTPS
  5. Slack — optional bot token and app token for Slack integration
  6. Admin dashboard — optional password protection

It writes .env, copies your PEM into the secrets directory, generates docker-compose.yml and (optionally) a Caddyfile, then offers to build and start the containers. When it's done you have a running instance ready to receive webhooks.

Requires: Node.js 20+, Docker, and a GitHub App already created (see Create a GitHub App below).


Quick Start (Local Dev)

Prerequisites

Setup

git clone https://github.com/cliftonc/lastlight.git
cd lastlight
npm install

Copy and edit the environment file:

cp .env.example .env

Fill in the required values in .env:

# GitHub App (required)
GITHUB_APP_ID=123456
GITHUB_APP_PRIVATE_KEY_PATH=./your-app.private-key.pem
GITHUB_APP_INSTALLATION_ID=789012

# Webhook secret (required for webhook mode)
WEBHOOK_SECRET=your-secret-here

Run

npm run dev runs the harness on your host but spawns each agent task in a real Docker sandbox container, exactly like production. It is explicitly safe with your personal config:

| | Touched? | |---|---| | ~/.gitconfig (your identity, credential helper) | ❌ skipped (LASTLIGHT_LOCAL_DEV=1) | | ~/.claude/.credentials.json | ❌ read once on first run, never written | | ~/.claude/projects/... (your session history) | ❌ direct fallback disabled | | ./data/sandbox-claude-home/ | ✅ project-local seed of your claude login; sandboxes refresh tokens here, isolated from host | | ./data/lastlight.db, ./data/sandboxes/, ./data/logs/ | ✅ project-local state, gitignored |

One-time setup — build the sandbox image:

docker compose --profile build-only build sandbox

Then run the harness (server + dashboard, with hot reload):

npm run dev            # both server and dashboard, concurrent
npm run dev:server     # server only
npm run dev:dashboard  # dashboard only

Both server scripts call scripts/dev-local.sh, which:

  • Verifies Docker is running and the sandbox image exists
  • Locates your host claude credentials — ~/.claude/.credentials.json on Linux, or the macOS keychain entry Claude Code-credentials on macOS. Run claude login first if neither is present.
  • Re-seeds ./data/sandbox-claude-home/.credentials.json from the host credentials on every start, so a fresh claude login is picked up automatically without wiping anything.
  • Sets LASTLIGHT_LOCAL_DEV=1, SANDBOX_DATA_VOLUME=./data/sandbox-claude-home, STATE_DIR=./data, CLAUDE_HOME_DIR=./data/sandbox-claude-home, and ENABLE_DIRECT_FALLBACK=false
  • Starts the harness with tsx watch src/index.ts

Triggering work via the CLI

The CLI talks to the running server — it does not execute agents directly. Start the server first, then in another terminal:

# Cheap, safe defaults — single agent invocation
npx tsx src/cli.ts owner/repo#42                                # triage that one issue
npx tsx src/cli.ts https://github.com/owner/repo/issues/42      # same, full URL form
npx tsx src/cli.ts https://github.com/owner/repo/pull/99        # review that one PR
npx tsx src/cli.ts triage owner/repo                            # scan repo for new issues to triage
npx tsx src/cli.ts review owner/repo                            # scan repo for PRs to review
npx tsx src/cli.ts health owner/repo                            # weekly health report

# Expensive, opt-in — full Architect → Executor → Reviewer → PR cycle
npx tsx src/cli.ts build owner/repo#42
npx tsx src/cli.ts build https://github.com/owner/repo/issues/42

The default for a single-issue/PR shorthand is the cheap action (triage or review). Build cycles require the explicit build subcommand to opt in.

Authentication

Locally, the Agent SDK uses your Claude Code login (subscription). No API key needed — just make sure claude login works on your host. npm run dev copies the credentials into the project-local sandbox claude-home, so the sandbox is logged in without bind-mounting your real ~/.claude.

To use an API key instead, set ANTHROPIC_API_KEY in .env.


Docker Deployment

Build and Run

docker-compose build agent
docker-compose up -d agent

First-Time Auth

The container needs Claude Code credentials. Log in interactively once:

docker exec -it lastlight-agent-1 claude login

Follow the URL in your browser to authenticate. The auth token persists in the Docker volume — it survives container restarts and rebuilds.

Secrets

Create a secrets/ directory with your GitHub App credentials:

mkdir -p secrets
cp .env secrets/
cp your-app.private-key.pem secrets/

The entrypoint symlinks these into the container at startup.

Expose Webhooks

To receive GitHub webhooks, the server needs to be publicly reachable. The included Caddy config handles HTTPS:

# Set your domain
echo "DOMAIN=lastlight.example.com" >> .env

# Start both agent and caddy
docker-compose up -d

Or use ngrok / Cloudflare Tunnel for testing.

State & Monitoring

All persistent state lives in a single Docker volume (agent-data), mounted at /app/data:

data/
  lastlight.db              # SQLite: execution log, rate limits
  claude-home/              # Claude auth + session JSONL logs
    projects/-app/*.jsonl   # Full audit trail per agent session
  sandboxes/                # Cloned repos per task
  logs/                     # Structured logs
  sessions/                 # (reserved)

Mount this volume or bind-mount the directory for monitoring tools to access session logs and the execution database.

Trigger Work via CLI

With the container running:

# Health check
curl http://localhost:8644/health

# Trigger a build cycle
npx tsx src/cli.ts https://github.com/owner/repo/issues/42

# Trigger triage
npx tsx src/cli.ts triage owner/repo

Setup Details

1. Create a GitHub App

  1. Go to https://github.com/settings/apps/new
  2. Fill in:
    • Name: your bot name (appears on comments/PRs with a [bot] badge)
    • Homepage URL: your repo URL
    • Webhook URL: https://your-domain:8644/webhooks/github (or leave blank for now)
    • Webhook Secret: a random string (same as WEBHOOK_SECRET in .env)
  3. Set permissions:
    • Issues: Read & Write
    • Pull Requests: Read & Write
    • Contents: Read & Write
    • Metadata: Read
  4. Subscribe to events: Issues, Pull request, Issue comment
  5. Click Create GitHub App
  6. Click Generate a private key — save the .pem file into the project directory
  7. Note the App ID from the app settings page
  8. Click Install App → install on your repos
  9. Note the Installation ID from the URL: github.com/settings/installations/{ID}

2. Environment Variables

| Variable | Required | Description | |----------|----------|-------------| | GITHUB_APP_ID | Yes | GitHub App ID | | GITHUB_APP_PRIVATE_KEY_PATH | Yes | Path to .pem file | | GITHUB_APP_INSTALLATION_ID | Yes | Installation ID | | WEBHOOK_SECRET | Yes | GitHub webhook signature secret | | ANTHROPIC_API_KEY | No | Anthropic API key (uses Claude login if not set) | | CLAUDE_MODEL | No | Model to use (default: claude-sonnet-4-6) | | PORT / WEBHOOK_PORT | No | Webhook listener port (default: 8644) | | STATE_DIR | No | Persistent state directory (default: ./data) | | MAX_TURNS | No | Max agent turns per invocation (default: 200) | | BOT_LOGIN | No | Bot login name for self-event filtering (default: last-light[bot]) | | LASTLIGHT_LOCAL_DEV | No | Set to 1 to skip git config --global writes from git-auth.ts. Use this on dev machines so the harness doesn't overwrite your personal ~/.gitconfig. The installation token is still passed to sandboxes via the GIT_TOKEN env var. | | SANDBOX_DATA_VOLUME | No | Either a Docker named volume name (default: lastlight_agent-data, used in production) or a host path (starts with /, ./, ../, or ~) to bind-mount as /data inside each sandbox. Local dev uses ./data/sandbox-claude-home. | | CLAUDE_HOME_DIR | No | Directory the dashboard reads sessions from (default: ./data/claude-home). Local dev points this at ./data/sandbox-claude-home to match the bind-mounted sandbox volume. | | ENABLE_DIRECT_FALLBACK | No | If true, the harness falls back to in-process Agent SDK execution when the sandbox image is unavailable. Local dev sets this to false to keep all agent work isolated in containers. |

3. Managed Repositories

Edit agent-context/rules.md to list repositories the bot manages:

## Managed Repositories
- your-org/repo-one
- your-org/repo-two

4. Customize Behaviour

| What | Where | |------|-------| | Bot personality & communication style | agent-context/soul.md | | Operational rules, review guidelines, triage rules | agent-context/rules.md | | Skill definitions | skills/*/SKILL.md | | Orchestrator phases (Architect/Executor/Reviewer) | src/engine/orchestrator.ts | | Event routing rules | src/engine/router.ts | | Cron job schedules | src/cron/jobs.ts |


Architecture

┌─────────────────────────────────────────┐
│            Connector Layer              │
│  GitHub Webhook │ (future: Slack, etc.) │
│        ↓        │         ↓             │
│     Event Normalizer (EventEnvelope)    │
└────────────────┬────────────────────────┘
                 ↓
┌─────────────────────────────────────────┐
│             Core Engine                 │
│  Event Router (deterministic)           │
│        ↓                                │
│  Agent Executor (Claude Agent SDK)      │
│  - System prompt from agent-context/    │
│  - MCP tools (GitHub App)               │
│  - Skills (SKILL.md)                    │
│        ↓                                │
│  Sandboxes (git clone per task)         │
│  Cron Scheduler (health reports)        │
│  State DB (SQLite execution log)        │
└─────────────────────────────────────────┘

How Events Flow

  1. GitHub webhook → connector verifies signature, filters noise (bot events, edits, labels), normalizes to EventEnvelope
  2. Router maps event type to skill deterministically (no LLM in the routing loop):
    • issue.openedissue-triage
    • pr.openedpr-review
    • comment.created with @last-light from maintainer → github-orchestrator
  3. Executor spawns a Claude Agent SDK session with the skill prompt, MCP tools, and agent context
  4. Orchestrator (for build requests) runs a multi-phase cycle:
    • Phase 1: Architect — read-only analysis, writes plan to .lastlight/issue-N/architect-plan.md
    • Phase 2: Executor — TDD implementation following the plan
    • Phase 3: Reviewer — independent verification (no shared context with executor)
    • Phase 4: Fix loop (up to 2 cycles if reviewer requests changes)
    • Phase 5: Create PR

Cron

When webhooks are enabled, only the weekly health report runs on cron (issue/PR events arrive in real-time via webhooks). Without webhooks, triage and PR review also run on cron.

| Job | Schedule | Condition | |-----|----------|-----------| | Triage new issues | Every 15 min | Only without webhooks | | Check PRs for review | Every 30 min | Only without webhooks | | Weekly health report | Mondays 9am | Always |


Project Structure

lastlight/
  src/
    index.ts                # Server entry point
    cli.ts                  # CLI client (talks to server)
    config.ts               # Config loader (.env)
    connectors/
      types.ts              # Connector + EventEnvelope interfaces
      github-webhook.ts     # GitHub webhook connector (Hono)
      index.ts              # Connector registry
    engine/
      router.ts             # Deterministic event → skill routing
      executor.ts           # Agent SDK query() wrapper
      orchestrator.ts       # Architect → Executor → Reviewer cycle
      agents.ts             # Subagent role definitions
      git-auth.ts           # GitHub App git credential setup
    worktree/
      manager.ts            # Git worktree per-task isolation
    cron/
      scheduler.ts          # Cron with overlap protection
      jobs.ts               # Job definitions
    state/
      db.ts                 # SQLite execution tracking

  agent-context/
    soul.md                 # Bot personality, principles, communication style
    rules.md                # Operational rules, managed repos, review guidelines

  skills/
    github-orchestrator/    # Central build cycle coordinator
    issue-triage/           # Issue labeling and triage
    pr-review/              # Structured PR review
    repo-health/            # Health reports
    github/                 # GitHub API workflow skills
    software-development/   # Dev skills (architect, TDD, debugging)

  mcp-github-app/           # MCP server: 28+ GitHub tools via Octokit
    src/
      index.js              # MCP server entry (clone_repo, refresh_git_auth, etc.)
      auth.js               # GitHub App JWT + installation token
      github.js             # Octokit wrapper with retry/backoff

  deploy/
    entrypoint.sh           # Docker entrypoint
  Dockerfile
  docker-compose.yml
  Caddyfile                 # Reverse proxy for HTTPS

Troubleshooting

Server won't start

# Check .env is loaded
npm run dev:server
# Look for "Required environment variable not set" errors

npm run dev says the sandbox image is missing

docker compose --profile build-only build sandbox

npm run dev says claude is not logged in

claude login    # then re-run npm run dev

Agent exits with code 1

# Check if Claude is logged in
claude --version && claude -p "hello"

# In Docker: check auth persisted
docker exec lastlight-agent-1 claude -p "hello"
# If it fails, re-login AS THE LASTLIGHT USER (not root!):
docker exec -it --user lastlight lastlight-agent-1 claude login

IMPORTANT: Always use --user lastlight when running claude login in Docker. Running as root causes:

  • Permission errors (.credentials.json owned by root, session logs unwritable)
  • Config corruption (cached feature flags wiped, MCP auth cache polluted with Claude.ai servers)
  • bypassPermissions mode may stop working with MCP servers attached

If you accidentally logged in as root, rebuild the container: docker compose build agent && docker compose up -d agent

Webhooks not arriving

# Check health endpoint
curl http://localhost:8644/health

# Test with a fake POST (should return 401 — invalid signature)
curl -X POST http://localhost:8644/webhooks/github -d '{}'

# Check Docker port mapping
docker-compose ps

Credit balance / rate limit errors

If you see "Credit balance is too low", either:

  • Top up at https://console.anthropic.com (API key mode)
  • Remove ANTHROPIC_API_KEY from .env to use your Claude subscription instead

Permission denied on MCP tools

The executor runs with bypassPermissions mode. In Docker, this requires running as a non-root user (the Dockerfile handles this automatically).