Skip to content

Releases: dortort/claude-plugins

taint-guard--v0.1.0

Choose a tag to compare

TaintGuard 0.1.0

The first release of TaintGuard, a Claude Code mod that remembers which sensitive or
untrusted sources entered a session and refuses risky tool calls once they have:
prevent sensitive context from reaching unsafe tools.

What ships:

  • Session taint tracking. Sources add labels (private, untrusted, secret) when a
    matching tool call is attempted or succeeds; sinks refuse, ask about or audit tool calls
    while their label condition holds. Every built-in and MCP tool call, including subagent
    calls, is judged before it runs, on the taint from earlier calls.
  • Presets. trifecta (default) asks before outbound tools once private and untrusted
    content meet and refuses them once secrets are read; strict refuses outbound and
    file-changing tools once any label is present; none leaves only your policy files.
    MCP tools are classified by server family (mail, documents, chat, web) and verb, for
    local servers and claude.ai connectors. Every tool on a web, browser or fetch server
    (fetch, playwright, puppeteer, chrome, …) is an untrusted source and an
    outbound tool.
  • Declarative policy files with a published JSON Schema, merged from managed, user
    and project layers so that a repository can only tighten the rules.
  • Fail closed. A guard that throws or times out refuses on any possible match; an
    invalid managed or user policy, or an unreadable managed setting, puts the session in
    lockdown with every rule active.
  • Survives the session's lifecycle. The taint set survives reloads, is rebuilt from
    history and a stored copy after /resume, /branch and restarts, and is written into
    compacted conversations as a taint record.
  • Explains itself. A band above the prompt, a context line for Claude when taint grows
    and on every prompt, deny text written as an instruction, an Allow once / Refuse
    question for ask rules, and /taintguard (status, explain, tools, clear,
    reload, audit).
  • Audit log. One JSONL file per session under ~/.claude/taint-guard/audit/, with a
    keyed HMAC of each call's arguments.
  • Organization deployment. Managed enabled lock, preset floor and managed policy
    path, read from managed settings only; a deployment guide for
    running it first in the chain through prependPlugins.

Security fixes made in review before this release, and part of it:

  • Built-in self-protection. A tainted session cannot have Claude rewrite TaintGuard's
    policy files, Claude Code's settings files or the plugin and audit directories:
    builtin:self-protect refuses those writes while any label is held, in lockdown and
    until a rebuild ends, in every mode. Its Bash check reads the command's text and is best
    effort.
  • The managed floor holds. With managed settings that lock TaintGuard on or set a
    preset, a user policy can no longer lower mode, userDeclassify or
    onUnknownHistory, so it cannot turn the managed rules into audit-only; the hooks module
    now passes the floor to the policy loader, and the lock is honoured before
    session.start has loaded the policy.
  • Wider preset coverage. Web, browser and fetch MCP servers are sources and sinks;
    trifecta refuses WebSearch once secrets are read; Grep globs over secret files, and
    .git-credentials, gh/hosts.yml, .docker/config.json and .kube/config, add
    secret.
  • Fail closed on padding. Argument text over 100,000 characters is not run through a
    regex: it counts as matching in a source or a sink's block and as not matching in an
    except.
  • Project regex limits. A project policy may not use alternation under an unbounded
    quantifier, a regex over 256 characters, or more than 50 regexes, so a repository cannot
    stall the guard.
  • Rebuild without gaps. Every label is held until the restart or resume rebuild
    succeeds, a rebuild merges with taint added while it ran, and unreadable, cut or
    uncounted subagent history counts as unknown history (assume-tainted).
  • Clearer prompts. A project policy's reason is marked (repository policy) and does not
    outrank a trusted sink's words; an ask shows destination fields first and the end of a
    long command; preset tool patterns are named in plain words; /taintguard clear keeps
    labels that arrive while it asks to confirm.

Measured cost on ordinary work: the benign-task eval (120 replayed coding and mail-triage
tasks) meets the spec's amended target, which counts only misfires, asks or refusals
raised because a source labelled content that is not actually private, third-party or
secret. Under trifecta, 0.8% of tasks were interrupted by a misfire (target under 5%) and
0.8% failed because of one (target under 1%); the one misfire is a public test certificate,
since every *.pem read adds secret. The asks and refusals the preset exists to raise are
reported separately: designed gates interrupted 17.5% of tasks and stopped 1.7%. In all,
18.3% of tasks hit an ask or a refusal and 2.5% could not finish; under strict, 40.0%
could not finish, 39.2% by a designed gate. Under trifecta, TaintGuard asks before
replying to, forwarding or labelling mail and before Bash after reading mail, Slack or
Notion, and refuses Bash after .env or a *.pem has been read. To trade some of that
protection for less friction, a user policy can switch off a preset rule
({ "id": "preset:outbound-private", "enabled": false }) and add a narrower one, or use
preset: none with their own rules; neither is possible under a managed preset. The numbers
and the injection results are in evals/RESULTS.md.

Requirements: Claude Code 2.1.287 or later in the terminal, or the Desktop Code tab with
bundled Claude Code 2.1.286 or later. No Node.js, npm package or build step is needed at
runtime.

Install, once this release is promoted to the marketplace catalog:

claude plugin marketplace add dortort/claude-plugins
claude plugin install taint-guard@dortort

Or load it from a checkout of the release tag:

git clone --branch taint-guard--v0.1.0 https://github.com/dortort/claude-plugins.git
claude --plugin-dir ./claude-plugins/plugins/taint-guard

To update later:

claude plugin marketplace update dortort
claude plugin update taint-guard@dortort

Run /reload-plugins or restart Claude Code after updating. Organizations replace the
plugin directory in their managed marketplace instead; see the deployment guide.

Known limitations:

  • It reasons about tools, not data: allowed tools such as Write or Edit can still move
    data, and argument matching on Bash commands is easy to evade.
  • A background shell started before a source keeps running.
  • A mod earlier in the chain can act before TaintGuard; other mods' own API calls are not
    tool calls. Deploy it through managed prependPlugins to run first.
  • Allow once lets a sink through (audited); ask rules wait for a person, so use
    deny rules for unattended sessions.
  • Rebuilds read at most the newest 4,096 history entries (the stored copy and
    assume-tainted cover the rest), and a session compacted while TaintGuard was off has
    no taint record.
  • Installed as a user mod where Claude Code's built-in guard loads (machines with managed
    settings, Team and Enterprise logins), its classic.* and session.compact hooks do not
    run: no rebuild after /resume or /branch, no /clear reset, no compaction record and
    no background-task notes. Enforcement and restart rebuilds still work.
  • Taint is shared across subagents; per-agent taint, content-based sources and file taint
    are planned for later versions.
  • No preset labels GitHub, Jira, Confluence, Zendesk or other issue trackers, and MCP tools
    that write under verbs outside the spec's list (push_files, put_object) are not
    outbound; add them in a policy file.

The full list is in the README's
security model and limitations.

Scheduler 0.4.1

Choose a tag to compare

@dortort dortort released this 01 Nov 18:31
a5d7ccc

Fixes scheduler installation through the dortort Claude Code marketplace.

  • Aligns the plugin manifest and npm package at 0.4.1 so Claude reports the correct version.
  • Includes npm-shrinkwrap.json so Claude installs runtime dependencies automatically.
  • Checks version consistency during packing and publishing, validates the release tag, and tests the packed archive in a clean installation.

Update with claude plugin update scheduler@dortort, then reload plugins or restart Claude Code.

artifact-callbacks-channel--v0.1.1

Choose a tag to compare

Artifact Callbacks Channel 0.1.1

Clarifies the channel alternative's requirements and reports the installed plugin version in the MCP handshake.

  • Reads the MCP server name and version from the plugin manifest so the reported versions stay aligned.
  • Explains when to choose the channel instead of the mod, including its different event and reply behavior.
  • Documents the lack of a confirm band and remote/SSH checks.
  • Adds an independent release process and versioned release notes.

This remains a channels research-preview plugin. It requires Node.js 22.18+, Anthropic authentication, and channel access permitted by your organization. Publishing this release does not put it on Anthropic's channel allowlist. Unless your organization allowlists it, start an interactive session with the development flag:

claude plugin marketplace update dortort
claude plugin update artifact-callbacks-channel@dortort
claude --dangerously-load-development-channels plugin:artifact-callbacks-channel@dortort

Enable only this variant or the mod in a session, never both. No npm publishing or build step is required.

artifact-callbacks--v0.1.1

Choose a tag to compare

Artifact Callbacks 0.1.1

Clarifies installation requirements and when to choose the mod or its channel alternative.

  • Documents Claude Code 2.1.287+ in the terminal, or bundled Claude Code 2.1.286+ in Desktop's Code tab, and Node.js 22.18+.
  • Explains the confirm band, per-click turns, reply streaming, and local-session requirements.
  • Makes the choice between variants explicit: enable only one per session.
  • Adds an independent release process and versioned release notes.

This release keeps the existing callback behavior. It needs no npm package or build step.

Update:

claude plugin marketplace update dortort
claude plugin update artifact-callbacks@dortort

Run /reload-plugins or restart Claude Code after updating.

v0.4.0

Choose a tag to compare

@dortort dortort released this 26 Aug 12:14

What's New

Reliability: scheduled runs no longer aborted by the daily timezone sync (#5)

Scheduled tasks could intermittently fail right at launch — a sub-second exit-1 with empty logs and no session ever created — when the daily tz-sync job fired in the same slot (commonly 3am). Two independent hazards are now closed:

  • Live task torn down mid-run. sync re-registering a task did launchctl unload+load on its plist, killing the job's process tree if it was already running. sync now checks a new isTaskRunning predicate and defers a live task to the next cycle instead of tearing down its registration.
  • Torn read of a shared shim. sync rewrites the executor shims in place, and launchd could exec one mid-write. Plists and shims are now written atomically (write-temp-then-rename), so a concurrent reader always sees a whole file.

Configurable state directory

  • config: The scheduler state directory can now be overridden with the CLAUDE_SCHEDULER_STATE_DIR environment variable, making it possible to isolate or relocate scheduler state.

Bug Fixes

  • darwin: Timezone offset is now computed correctly across month and year boundaries (previously the day-of-month approximation could land in the wrong month/year at the edges).
  • executor: claude invocations are retried on transient macOS keychain auth failures, so a momentary "not logged in" hiccup no longer fails an otherwise-healthy run.

Under the hood

  • E2E suite migrated to agentry with per-scenario state isolation for deterministic runs; added coverage for the sync skip-running path, isTaskRunning, atomic writes, and executor auth-retry. Full suite: 353 tests green.

Full Changelog: v0.3.1...v0.4.0

v0.3.1

Choose a tag to compare

@dortort dortort released this 15 Apr 09:11

Bug Fix

  • init: Export USER env var in launchd shell template — fixes "not logged in" error when Claude CLI runs scheduled tasks, since launchd does not provide $USER

v0.3.0

Choose a tag to compare

@dortort dortort released this 13 Apr 20:33

What's New

macOS launchd timezone support

Tasks configured with a non-local timezone (e.g., timezone: "UTC") now fire at the correct time on macOS. Previously, launchd's StartCalendarInterval ignored the timezone field entirely, causing tasks to fire at the wrong hour.

How it works:

  • At registration time, CalendarInterval values are converted from the target timezone to system-local time using Intl.DateTimeFormat
  • A daily auto-sync launchd job (com.claude-scheduler.tz-sync) is registered alongside any timezone-aware task, catching DST transitions automatically
  • The existing sync command recalculates timezone offsets on every run

Example: A task scheduled for 0 7 * * 1-5 with timezone: "UTC" on a system in America/Toronto (EDT, UTC-4) now correctly registers as Hour: 3 in the plist instead of Hour: 7.

Known limitation

Day-of-month schedules where the timezone offset crosses a day boundary use an approximate day (clamped to 1–28) with a logged warning. This is a fundamental limitation of launchd's CalendarInterval which has no native timezone support.

No changes to Linux

The Linux crontab scheduler is completely unaffected — it already handles timezones natively via the TZ= environment variable.


Full Changelog: v0.2.1...v0.3.0

v0.2.1

Choose a tag to compare

@dortort dortort released this 01 Apr 07:22

Bug fixes for lock handling, process lifecycle, and time display.

Fixes

  • Correct SIGTERM handling: register before lock, write startTime before PID, abort on pre-spawn signal
  • DST-safe "tomorrow" label and normalized midnight hour from Intl.DateTimeFormat
  • Validate PID identity and preserve lock on unverified PID
  • Send SIGTERM to executor PID only (not process group)
  • Kill running processes when deleting a scheduled task
  • Use pre-computed relativeTime instead of LLM date math in list

Improvements

  • Deterministic relativeTime field in humanize CLI output
  • Simplified lock module and renamed formatRelativeTime

v0.2.0

Choose a tag to compare

@dortort dortort released this 27 Mar 14:18

@dortort/scheduler v0.2.0

Second release of the Claude Code scheduler plugin, focused on safer execution, one-time scheduling, task state synchronization, and project-scoped task loading.

Features

  • One-time triggers - schedule a task to run once at a specific timestamp
  • Enable/disable sync - keep config state and the native OS scheduler in sync
  • Project-scoped tasks - load merged config via projectPath for project-local scheduling
  • Validation hardening - stricter cron, timestamp, and path validation in mutation flows
  • Sensitive file protection - detect and block or warn on .env, keys, and other sensitive files in worktree commit flows
  • Improved scheduler UX - add, update, and sync flows handle one-time schedules and enabled-state changes more reliably

Internal

  • Trusted publishing - release automation now publishes to npm via GitHub Actions OIDC trusted publishing

See the README for the full command reference and usage details.

v0.1.0

Choose a tag to compare

@dortort dortort released this 27 Mar 12:48

@dortort/scheduler v0.1.0

First release of the Claude Code scheduler plugin.

Features

  • Natural language scheduling - "every weekday at 9am", "daily at 5pm", "every Monday at 10am"
  • Cron expressions - standard 5-field cron syntax with full validation
  • One-time tasks - schedule a task to run once at a specific time
  • Worktree isolation - run tasks in isolated git worktrees
  • Execution history - JSONL-based history with filtering
  • Log management - stdout/stderr capture with rotation
  • Security built-in - env blocklist, sensitive file detection, shell escaping, trust boundaries
  • Platform support - macOS (launchd) and Linux (crontab)

See the README for the full command reference and usage details.