# Plugin Watcher (`organized_ai/plugin-watcher`) Actor

Analyzes Claude Code session transcripts to find the workflow patterns you repeat, then suggests and scaffolds the skills, agents, commands, and hooks worth building.

- **URL**: https://apify.com/organized\_ai/plugin-watcher.md
- **Developed by:** [Jordaaan Hill](https://apify.com/organized_ai) (community)
- **Categories:** AI, Developer tools, Agents
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-usage

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Plugin Watcher

**Find the workflows you keep repeating in Claude Code — and turn them into plugins.**

Plugin Watcher reads your Claude Code session transcripts, detects tool-call sequences you run over and over, and tells you which ones are worth capturing as a **skill, agent, slash command, hook, CLI tool, or config preset**. It then writes the scaffold files for you.

No LLM calls. No conversation content leaves your machine in the output — only structural patterns.

***

### Why use it

You've probably noticed you do the same dance a lot: read a config, edit it, `npm run build`, `ssh` to the box, restart the service. That five-step loop is a plugin waiting to happen — you just never notice it while you're in it.

Plugin Watcher notices it for you. It answers three questions:

1. **What am I actually repeating?** — n-gram analysis over your real tool-call history.
2. **Is it worth automating?** — a weighted score across frequency, consistency, complexity, and recency.
3. **What shape should it take?** — a mapped plugin type plus a ready-to-drop-in scaffold file.

***

### How it works

```
Session JSONL
    │
    ▼
┌─────────┐   ┌────────────┐   ┌──────────┐   ┌───────────┐   ┌────────────┐
│ Parser  │──▶│ Classifier │──▶│ N-grams  │──▶│  Scorer   │──▶│ Suggester  │
└─────────┘   └────────────┘   └──────────┘   └───────────┘   └────────────┘
 extract       label calls      find repeat     rank by         map to a
 tool calls    semantically     sequences       value           plugin type
                                                                     │
                                                                     ▼
                                                          Dataset + Markdown
                                                          report + scaffolds
```

Sessions are stored in a **named key-value store** (`plugin-watcher-sessions`) that persists across runs, so patterns compound the more sessions you feed it. Sessions are deduplicated by ID — re-ingesting the same session is a no-op.

#### Semantic classification

Raw tool names are too coarse. `Bash → Bash → Bash` tells you nothing. So every tool call is also labelled by **what it actually does**, using deterministic regex matching on the tool's input:

| Label | Means |
|---|---|
| `Bash:ssh` | `ssh` / `scp` / `rsync` |
| `Bash:git` | `git` / `gh` |
| `Bash:test` | `vitest`, `jest`, `pytest`, `mocha`, `npm test` |
| `Bash:build` | `npm run build`, `tsc`, `make`, `cargo build` |
| `Bash:npm` | `npm` / `npx` / `pnpm` / `yarn` / `bun` |
| `Bash:apify` · `Bash:docker` · `Bash:curl` | Apify CLI · Docker · HTTP requests |
| `Bash:system` · `Bash:env` · `Bash:data` · `Bash:shell` | filesystem · `cd`/`export` · `jq` · everything else |
| `Read:config` · `Edit:source` · `Write:docs` | file operations, classified by file type |

File categories: `config`, `source`, `docs`, `script`, `test`, `lock`, `glob`, `notebook`, `other`.

That turns an opaque `Bash → Bash → Edit` into a legible `Bash:git → Bash:build → Edit:config` — which is a pattern you can actually name.

***

### Input

Every field is optional. Running with no input at all analyzes everything already stored.

| Field | Type | Default | Description |
|---|---|---|---|
| `sessionJsonl` | string | — | Raw JSONL content of a Claude Code session transcript. |
| `sessionId` | string | auto | Session UUID. Extracted from the JSONL if omitted. |
| `analyzeSessionId` | string | — | Analyze only this one stored session instead of all of them. |
| `analysisMode` | enum | `full` | `ingest` · `analyze` · `full` · `schedule` |

#### Modes

- **`ingest`** — parse and store a session. No analysis. This is what the stop hook uses on every session end.
- **`analyze`** — run pattern detection across all stored sessions. No new input needed.
- **`full`** *(default)* — ingest, then analyze.
- **`schedule`** — incremental. Only runs if new sessions arrived since the last analysis, then updates the watermark. Use this for scheduled runs so you don't burn compute on unchanged data.

**Example input:**

```json
{
  "sessionJsonl": "{\"type\":\"assistant\",\"uuid\":\"...\"}\n{...}",
  "sessionId": "3f2a91c4-8b7e-4d1a-9c33-6e5f0a2b7d81",
  "analysisMode": "ingest"
}
```

***

### Output

#### Dataset — one item per suggestion

```json
{
  "name": "remote-deploy",
  "pluginType": "skill",
  "score": 78.4,
  "description": "Automate the Git → build → SSH remote workflow pattern.",
  "rationale": "Detected 14 times across 6 session(s). Complex 4-step workflow that would benefit from automation. High frequency pattern — significant time savings potential.",
  "trigger": "/remote-deploy",
  "patternKey": "Bash:git→Bash:build→Bash:ssh→Bash:ssh",
  "patternOccurrences": 14,
  "patternSessions": 6,
  "sessionIds": ["3f2a91c4-...", "8d0b7e21-..."],
  "scores": { "frequency": 100, "complexitySavings": 80, "feasibility": 70 }
}
```

A pre-configured **Plugin Suggestions** table view surfaces name, type, score, description, and rationale.

#### Key-value store (`plugin-watcher-sessions`)

| Key | Content |
|---|---|
| `report` | Markdown report — summary metrics, sessions analyzed, top semantic patterns, top 10 recommendations with score breakdowns, full suggestion list. |
| `scaffolds` | Markdown bundle of **ready-to-use plugin files**, each with its target path. |
| `session-<uuid>` | A stored parsed session. |
| `session-index` | Index of all stored session IDs. |
| `last-analysis-time` | Watermark for `schedule` mode. |

#### Plugin types and where scaffolds land

| Type | Scaffold path | Chosen when |
|---|---|---|
| `skill` | `.claude/skills/<name>/SKILL.md` | SSH-heavy or long (4+ step) patterns |
| `command` | `.claude/commands/<name>.md` | short git workflows, 3 steps or fewer |
| `agent` | `.claude/agents/<name>.md` | read + write combinations (code transformation) |
| `hook` | `.claude/hooks/<name>.sh` | patterns that start *and* end with a shell command |
| `cli-tool` | `npx <name>` | shell-dominated mixed patterns |
| `config` | `.claude/settings.json` snippet | all-shell environment/setup patterns |

***

### Scoring

Two scores, applied in sequence.

**1. Pattern score** — is this sequence real, or noise?

| Component | Weight | Measures |
|---|---|---|
| Frequency | 40% | occurrences relative to session count |
| Consistency | 30% | how many distinct sessions it spans (a pattern in one session is a fluke) |
| Complexity | 20% | sequence length, capped at 5 |
| Recency | 10% | 100 within 7 days, 50 within 30, 10 beyond |

**2. Suggestion score** — is this worth building?

| Component | Weight | Measures |
|---|---|---|
| Frequency | 40% | inherited from the pattern |
| Complexity savings | 35% | how many steps get collapsed |
| Feasibility | 25% | how easy the plugin type is to build |

Feasibility by type: `command` 90 · `config` 85 · `hook` 80 · `skill` 70 · `cli-tool` 60 · `agent` 50. A dead-simple slash command beats a speculative agent at equal frequency — the tool is biased toward things you'll actually ship.

Defaults: sequences of 2–5 tool calls, top 50 patterns scored, top 20 suggestions returned.

***

### Automate it

#### Capture every session with a stop hook

Add to `~/.claude/settings.json` (global) or `.claude/settings.json` (per-project):

```json
{
  "hooks": {
    "stop": [
      { "command": "/path/to/plugin-watcher/hooks/stop.sh" }
    ]
  }
}
```

The hook reads the transcript path from stdin, ships the JSONL to this Actor in `ingest` mode, and backgrounds the call so Claude Code never blocks on it. Requires `jq` and an authenticated `apify` CLI.

#### Then analyze on a schedule

In the Apify Console → **Schedules**, with input `{"analysisMode": "schedule"}`:

- Daily — `0 9 * * *`
- Weekly — `0 9 * * 1`

Ingest continuously, analyze periodically. That's the intended shape.

#### Call it directly

```bash
## CLI
apify call organized_ai/plugin-watcher --input='{"analysisMode":"analyze"}'

## API
curl -X POST "https://api.apify.com/v2/acts/organized_ai~plugin-watcher/runs?token=YOUR_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"analysisMode":"analyze"}'
```

***

### Privacy

This Actor is built to analyze *structure*, not content:

- **No prompts, no responses, no code** appear in the dataset, report, or scaffolds — only tool names, semantic labels, sequences, counts, and timestamps.
- **Paths are sanitized** — `/Users/<you>`, `/home/<you>`, and `C:\Users\<you>` are collapsed to `~`.
- **No third-party calls.** Classification is regex-based and runs entirely inside the Actor. Nothing is sent to an LLM.

Note that ingested session transcripts are stored in your own Apify key-value store so patterns can accumulate across runs. They live in your account and are visible only to you.

***

### Limitations

- Detects sequences of **2–5** consecutive tool calls. Longer workflows show up as overlapping fragments rather than one unit.
- Pattern quality scales with volume. One session gives you noise; twenty give you signal.
- Generated names come from a category-combination table, so they're descriptive rather than clever — rename them.
- Scaffolds are **starting points**, not finished plugins. They capture the shape of the workflow; you supply the logic.

***

### Local development

```bash
npm install
npm run build     # tsc
npm test          # vitest
apify run         # run locally
apify push        # deploy
```

Node.js 22+. Built with the Apify SDK v3 and TypeScript.

# Actor input Schema

## `sessionJsonl` (type: `string`):

Raw JSONL content from a Claude Code session transcript.

## `sessionId` (type: `string`):

Unique identifier for the session (UUID). If omitted, extracted from JSONL.

## `analyzeSessionId` (type: `string`):

If provided, analyze only this session (by UUID) instead of all stored sessions. Requires the session to be already ingested.

## `analysisMode` (type: `string`):

Operation mode: 'ingest' to store session, 'analyze' to run pattern detection, 'full' for both, 'schedule' for incremental.

## Actor input object example

```json
{
  "analysisMode": "full"
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("organized_ai/plugin-watcher").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("organized_ai/plugin-watcher").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call organized_ai/plugin-watcher --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,organized_ai/plugin-watcher"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/xU82DQRMygiUmUeaI/builds/TVLmyZ83Y9ZyiOTxT/openapi.json
