tejitpabari99/crontick

TypeScript

2

12 commits

updated Oct 2, 2026

See the code

See what people are saying

SourceMessageScoreDate

Crontick - Local prompt scheduler (r/ClaudeAI)

Sharing crontick - an open-source tool to schedule AI prompts locally. I run a lot of tasks on a schedule: summarize my email every day, fetch logs and create bug reports, or pull meeting notes and reconcile updates. I wanted a way to schedule these using my claude cli instance, on my VPS. So I…

1

Oct 2, 2026

README

crontick

AI-native local cron. Schedule an AI agent to run on a cron, interval, or one-shot schedule — capture its output and session, observe it in a dashboard — all on one machine, no server.

crontick is a standalone daemon, CLI, and MCP server. Its job kind is a prompt job: a natural-language prompt that runs against a configured AI engine. Claude Code is the built-in default.

Documentation

ResourcePath
Documentation hubdocs/README.md
Architecturedocs/architecture.md
Conceptsdocs/concepts/
Reference (API, CLI, MCP, schemas)docs/reference/
Runnable examplesdocs/examples/
Behavior specsdocs/specs/
Design decisions (ADRs)docs/decisions/
Troubleshootingdocs/troubleshooting.md

What is crontick?

  • AI-first. Schedule a prompt to run against Claude Code out of the box or another configured CLI engine.
  • Local & single-machine. A demand-started daemon binds to 127.0.0.1 only. No cloud, no accounts, no remote listeners — the trust boundary is your user session.
  • Three faces, one behavior. The same operations are available from the CLI, a Node.js library, and an MCP server so a human, a script, or an AI assistant can manage the same jobs.
  • Observable. Every run records its status, the engine's cleaned output and session id, and crontick-side lifecycle events in a per-job log file (the engine keeps its own transcript) — browsable in a built-in web dashboard.

Think of it as cron where the thing on a schedule is an AI agent instead of a shell script.


Install & requirements

Requires Node.js >= 22.5 (uses the node:sqlite built-in).

npm install -g crontick     # global, for CLI use
# or run without installing:
npx crontick info

The default claude engine needs the Claude Code CLI on your PATH. You can select another configured engine; see Engines & configuration.

Verify your setup:

crontick info      # version, runtime, config path, storage paths, daemon status, dashboard URL
crontick doctor    # system health check

Quick start — an AI job in 60 seconds

Schedule an AI agent to summarize your open PRs every morning at 9:00. The prompt is passed with --prompt, and an alias is generated automatically (no id to manage):

crontick jobs new --desc "daily standup" --cron "0 9 * * *" --prompt "Summarize my open GitHub PRs"

crontick prints the new job, including its auto-assigned alias (e.g. fern-270). Use that alias (or the job's GUID id) everywhere:

crontick jobs list                 # see all jobs
crontick jobs get fern-270         # inspect one job
crontick jobs run-now fern-270     # run once now (works on disabled jobs too; schedule unchanged)

Watch what the agent did:

crontick runs list                 # recent runs across all jobs
crontick runs get <runId>          # status, timing, Runner Session ID, transcript + log file path, then the cleaned output (final answer, error, stderr; no tool calls, thinking or hook noise)

Prefer a UI? crontick info prints the dashboard URL (by default http://127.0.0.1:47615/dashboard; if that port is taken the daemon starts on a free port and says so) where you can browse jobs (with details, search and run-once) in a light or dark theme, runs (multi-select filters, sortable columns, search across run output), and per-run results (final answer, error, stderr, plus the log file and transcript paths with copy buttons).


Scheduling

Every job carries exactly one schedule. Pick the flag that matches:

# cron expression (fires in the machine's local timezone)
crontick jobs new --cron "0 9 * * *" --prompt "Summarize my open PRs" --alias standup

# fixed interval, in seconds or with an s/m/h/d suffix
crontick jobs new --every 1h --prompt "Check the build and report failures" --alias hourly

# one-shot at a specific ISO-8601 time
crontick jobs new --at "2026-12-01T09:00:00" --prompt "Remind me to cut the release" --alias release-reminder

Preview the next fire times for any job:

crontick jobs schedule standup -n 5

Other create/update options: --timeout <sec>, --overlap skip|queue|cancel-previous (default skip), --retry <max>, --force (replace a job with the same alias). Omitted policy values come from the defaults section of config.json (overlap, timeoutSec, retry); precedence is CLI flag > per-job JSON > config.json > built-in, and the resolved values are saved on the job. When an overlap skip job fires while its previous run is still active, the fire is recorded as a skipped run (never started), distinct from canceled.


Engines & configuration

A prompt engine is the AI CLI crontick invokes for a prompt job. crontick ships with a built-in claude engine:

{ "command": "claude", "args": [], "env": {}, "type": "claude" }

The Claude adapter invokes claude -p "<your prompt>" --output-format stream-json with a pre-assigned session ID. A custom engine with no type uses the generic raw adapter, which appends the prompt after its configured args. For raw engines that need a prompt-taking flag, put it last in args.

The config file

crontick info prints the path to config.json (under the data dir). Edit that file directly. Engine, logging, and per-run retention changes apply on the next run; retention.maxRunsPerJob is cached by the daemon, so after changing it run crontick daemon reload.

{
  "defaultEngine": "claude",
  "engines": {
    "claude": { "command": "claude", "args": [], "env": {}, "type": "claude" },
    "custom": { "command": "my-agent", "args": ["--prompt"], "env": {}, "type": "raw" }
  },
  "retention": { "maxRunsPerJob": 100, "maxOutputBytesPerRun": 2000000, "maxLogFiles": 30 },
  "logging": { "fileEnabled": true },
  "defaults": { "overlap": "skip", "retry": { "max": 0, "backoffSec": 30 } }
}

Select an engine per job with --runner:

crontick jobs new --every 3600 --prompt "Review recent commits for risky changes" --runner claude --alias review

Pass engine options as unknown long flags on jobs new or jobs update, for example --permission-mode acceptEdits. Crontick stores them in the job's action.args and forwards them to the engine. It rejects flags it manages itself, including --output-format and --settings.

Multi-turn continuity

Prompt jobs can carry an AI session across runs so the agent remembers prior context:

  • --session-id <id> — reuse a fixed engine session id on every run.
  • --reuse-session — capture a reusable session after a complete Claude result (including a failed result) or a successful raw-engine run. It requires --overlap skip. Claude resumes need the session transcript on disk, otherwise the run fails with SESSION_NOT_FOUND.
crontick jobs new --cron "0 * * * *" --prompt "Continue triaging the incident queue" --reuse-session --alias triage

Working directory and Claude trust

A job runs in its working directory: --cwd <dir> / -C <dir> (default: the directory you run jobs new from; MCP and library callers should pass the project folder). For Claude jobs the folder must be trusted in Claude's config. If it is not, jobs new/jobs update/share import ask Folder X is not trusted by Claude. Trust it? (y/N) on a terminal; without one, they fail with TRUST_REQUIRED unless you pass --trust-folder. Changing --cwd of a job that has a session (--session-id or --reuse-session) is rejected (CWD_CHANGE_BREAKS_SESSION) unless you also set a new session.

See docs/reference/configuration.md for the full schema, environment variables (CRONTICK_HOME, CRONTICK_DAEMON_URL, CRONTICK_VERBOSE), and precedence.


Observing runs

crontick stores only its own logs: lifecycle events (start, timeout, retry, exit) go to one per-job log file, and the cleaned output (final answer, error and stderr) is kept with the run. The engine's raw logs and transcript stay with the engine; crontick does not copy them. runs get prints the log file's path and the cleaned output.

crontick runs list --job standup --status failed
crontick runs get <runId>            # Runner Session ID, log file path and cleaned output; Claude runs also show cost, turns, usage

When logging.fileEnabled is true (the default), crontick-side events are written to <logsDir>/<jobGuid>.log (one file per job, each line tagged with its run id, deleted with the job). Run crontick info for the exact logsDir and other storage paths, plus the dashboard URL — the dashboard offers job/run filters, search, and a per-run output view with a link to the log file. Output is redacted for common secret patterns before storage.


Use from an AI assistant (MCP)

crontick ships an MCP server so an AI assistant can manage schedules for you. The tools mirror the CLI one-to-one (prefix crontick_).

Start it with crontick mcp (or the crontick-mcp bin) over stdio, and wire it into your MCP host — Copilot, Claude Desktop, Cursor, etc.:

{
  "mcpServers": {
    "crontick": { "command": "crontick", "args": ["mcp"] }
  }
}

See docs/reference/mcp-tools.md for the full tool list.


Use as a library

import { createClient } from 'crontick';

const client = createClient();

// Schedule an AI prompt job.
const job = await client.createJob({
  alias: 'daily-summary',
  schedule: { kind: 'cron', cron: '0 9 * * *' },
  action: { kind: 'prompt', prompt: 'Summarize my open GitHub PRs', engine: 'claude' },
});

console.log('created', job.alias ?? job.id);

const runs = await client.listRuns({ jobId: 'daily-summary' });
console.log(runs.length, 'runs so far');

After daemon-backed calls, prefer setting process.exitCode = n and letting Node exit naturally rather than calling process.exit(n) immediately.

Full API in docs/reference/library-api.md; runnable samples in docs/examples/.


Command reference at a glance

GroupCommands
jobsnew · list · get · update · schedule · run-now · delete
runslist · get · cancel
shareexport · import (job definitions only, schema 1; imports get new ids)
statssummary · job
infoinfo (version, paths, daemon status, dashboard URL)
doctordoctor (system health check)
daemondaemon start · daemon stop · daemon restart · daemon status · daemon reload (the daemon also starts on demand)
mcpmcp (start the MCP server on stdio)

Full CLI reference: docs/reference/cli.md.


Storage locations

State and configuration live in a platform-specific data directory (override with CRONTICK_HOME):

OSDefault path
Windows%LOCALAPPDATA%\crontick\
macOS~/Library/Application Support/crontick/
Linux~/.local/share/crontick/

Contributing

See CONTRIBUTING.md for the full guide (DCO, code style, PR process). For coding agents, see AGENTS.md. For testing, see docs/testing/testing.md.

Validate a change:

npm run validate    # lint, type-check, tests, and build

Report bugs at https://github.com/tejitpabari99/crontick/issues.


License

MIT — crontick contributors

tejitpabari99/crontick

TypeScript

2

12 commits

updated Oct 2, 2026

See the code

See what people are saying

SourceMessageScoreDate

Crontick - Local prompt scheduler (r/ClaudeAI)

Sharing crontick - an open-source tool to schedule AI prompts locally. I run a lot of tasks on a schedule: summarize my email every day, fetch logs and create bug reports, or pull meeting notes and reconcile updates. I wanted a way to schedule these using my claude cli instance, on my VPS. So I…

1

Oct 2, 2026

README

crontick

AI-native local cron. Schedule an AI agent to run on a cron, interval, or one-shot schedule — capture its output and session, observe it in a dashboard — all on one machine, no server.

crontick is a standalone daemon, CLI, and MCP server. Its job kind is a prompt job: a natural-language prompt that runs against a configured AI engine. Claude Code is the built-in default.

Documentation

ResourcePath
Documentation hubdocs/README.md
Architecturedocs/architecture.md
Conceptsdocs/concepts/
Reference (API, CLI, MCP, schemas)docs/reference/
Runnable examplesdocs/examples/
Behavior specsdocs/specs/
Design decisions (ADRs)docs/decisions/
Troubleshootingdocs/troubleshooting.md

What is crontick?

  • AI-first. Schedule a prompt to run against Claude Code out of the box or another configured CLI engine.
  • Local & single-machine. A demand-started daemon binds to 127.0.0.1 only. No cloud, no accounts, no remote listeners — the trust boundary is your user session.
  • Three faces, one behavior. The same operations are available from the CLI, a Node.js library, and an MCP server so a human, a script, or an AI assistant can manage the same jobs.
  • Observable. Every run records its status, the engine's cleaned output and session id, and crontick-side lifecycle events in a per-job log file (the engine keeps its own transcript) — browsable in a built-in web dashboard.

Think of it as cron where the thing on a schedule is an AI agent instead of a shell script.


Install & requirements

Requires Node.js >= 22.5 (uses the node:sqlite built-in).

npm install -g crontick     # global, for CLI use
# or run without installing:
npx crontick info

The default claude engine needs the Claude Code CLI on your PATH. You can select another configured engine; see Engines & configuration.

Verify your setup:

crontick info      # version, runtime, config path, storage paths, daemon status, dashboard URL
crontick doctor    # system health check

Quick start — an AI job in 60 seconds

Schedule an AI agent to summarize your open PRs every morning at 9:00. The prompt is passed with --prompt, and an alias is generated automatically (no id to manage):

crontick jobs new --desc "daily standup" --cron "0 9 * * *" --prompt "Summarize my open GitHub PRs"

crontick prints the new job, including its auto-assigned alias (e.g. fern-270). Use that alias (or the job's GUID id) everywhere:

crontick jobs list                 # see all jobs
crontick jobs get fern-270         # inspect one job
crontick jobs run-now fern-270     # run once now (works on disabled jobs too; schedule unchanged)

Watch what the agent did:

crontick runs list                 # recent runs across all jobs
crontick runs get <runId>          # status, timing, Runner Session ID, transcript + log file path, then the cleaned output (final answer, error, stderr; no tool calls, thinking or hook noise)

Prefer a UI? crontick info prints the dashboard URL (by default http://127.0.0.1:47615/dashboard; if that port is taken the daemon starts on a free port and says so) where you can browse jobs (with details, search and run-once) in a light or dark theme, runs (multi-select filters, sortable columns, search across run output), and per-run results (final answer, error, stderr, plus the log file and transcript paths with copy buttons).


Scheduling

Every job carries exactly one schedule. Pick the flag that matches:

# cron expression (fires in the machine's local timezone)
crontick jobs new --cron "0 9 * * *" --prompt "Summarize my open PRs" --alias standup

# fixed interval, in seconds or with an s/m/h/d suffix
crontick jobs new --every 1h --prompt "Check the build and report failures" --alias hourly

# one-shot at a specific ISO-8601 time
crontick jobs new --at "2026-12-01T09:00:00" --prompt "Remind me to cut the release" --alias release-reminder

Preview the next fire times for any job:

crontick jobs schedule standup -n 5

Other create/update options: --timeout <sec>, --overlap skip|queue|cancel-previous (default skip), --retry <max>, --force (replace a job with the same alias). Omitted policy values come from the defaults section of config.json (overlap, timeoutSec, retry); precedence is CLI flag > per-job JSON > config.json > built-in, and the resolved values are saved on the job. When an overlap skip job fires while its previous run is still active, the fire is recorded as a skipped run (never started), distinct from canceled.


Engines & configuration

A prompt engine is the AI CLI crontick invokes for a prompt job. crontick ships with a built-in claude engine:

{ "command": "claude", "args": [], "env": {}, "type": "claude" }

The Claude adapter invokes claude -p "<your prompt>" --output-format stream-json with a pre-assigned session ID. A custom engine with no type uses the generic raw adapter, which appends the prompt after its configured args. For raw engines that need a prompt-taking flag, put it last in args.

The config file

crontick info prints the path to config.json (under the data dir). Edit that file directly. Engine, logging, and per-run retention changes apply on the next run; retention.maxRunsPerJob is cached by the daemon, so after changing it run crontick daemon reload.

{
  "defaultEngine": "claude",
  "engines": {
    "claude": { "command": "claude", "args": [], "env": {}, "type": "claude" },
    "custom": { "command": "my-agent", "args": ["--prompt"], "env": {}, "type": "raw" }
  },
  "retention": { "maxRunsPerJob": 100, "maxOutputBytesPerRun": 2000000, "maxLogFiles": 30 },
  "logging": { "fileEnabled": true },
  "defaults": { "overlap": "skip", "retry": { "max": 0, "backoffSec": 30 } }
}

Select an engine per job with --runner:

crontick jobs new --every 3600 --prompt "Review recent commits for risky changes" --runner claude --alias review

Pass engine options as unknown long flags on jobs new or jobs update, for example --permission-mode acceptEdits. Crontick stores them in the job's action.args and forwards them to the engine. It rejects flags it manages itself, including --output-format and --settings.

Multi-turn continuity

Prompt jobs can carry an AI session across runs so the agent remembers prior context:

  • --session-id <id> — reuse a fixed engine session id on every run.
  • --reuse-session — capture a reusable session after a complete Claude result (including a failed result) or a successful raw-engine run. It requires --overlap skip. Claude resumes need the session transcript on disk, otherwise the run fails with SESSION_NOT_FOUND.
crontick jobs new --cron "0 * * * *" --prompt "Continue triaging the incident queue" --reuse-session --alias triage

Working directory and Claude trust

A job runs in its working directory: --cwd <dir> / -C <dir> (default: the directory you run jobs new from; MCP and library callers should pass the project folder). For Claude jobs the folder must be trusted in Claude's config. If it is not, jobs new/jobs update/share import ask Folder X is not trusted by Claude. Trust it? (y/N) on a terminal; without one, they fail with TRUST_REQUIRED unless you pass --trust-folder. Changing --cwd of a job that has a session (--session-id or --reuse-session) is rejected (CWD_CHANGE_BREAKS_SESSION) unless you also set a new session.

See docs/reference/configuration.md for the full schema, environment variables (CRONTICK_HOME, CRONTICK_DAEMON_URL, CRONTICK_VERBOSE), and precedence.


Observing runs

crontick stores only its own logs: lifecycle events (start, timeout, retry, exit) go to one per-job log file, and the cleaned output (final answer, error and stderr) is kept with the run. The engine's raw logs and transcript stay with the engine; crontick does not copy them. runs get prints the log file's path and the cleaned output.

crontick runs list --job standup --status failed
crontick runs get <runId>            # Runner Session ID, log file path and cleaned output; Claude runs also show cost, turns, usage

When logging.fileEnabled is true (the default), crontick-side events are written to <logsDir>/<jobGuid>.log (one file per job, each line tagged with its run id, deleted with the job). Run crontick info for the exact logsDir and other storage paths, plus the dashboard URL — the dashboard offers job/run filters, search, and a per-run output view with a link to the log file. Output is redacted for common secret patterns before storage.


Use from an AI assistant (MCP)

crontick ships an MCP server so an AI assistant can manage schedules for you. The tools mirror the CLI one-to-one (prefix crontick_).

Start it with crontick mcp (or the crontick-mcp bin) over stdio, and wire it into your MCP host — Copilot, Claude Desktop, Cursor, etc.:

{
  "mcpServers": {
    "crontick": { "command": "crontick", "args": ["mcp"] }
  }
}

See docs/reference/mcp-tools.md for the full tool list.


Use as a library

import { createClient } from 'crontick';

const client = createClient();

// Schedule an AI prompt job.
const job = await client.createJob({
  alias: 'daily-summary',
  schedule: { kind: 'cron', cron: '0 9 * * *' },
  action: { kind: 'prompt', prompt: 'Summarize my open GitHub PRs', engine: 'claude' },
});

console.log('created', job.alias ?? job.id);

const runs = await client.listRuns({ jobId: 'daily-summary' });
console.log(runs.length, 'runs so far');

After daemon-backed calls, prefer setting process.exitCode = n and letting Node exit naturally rather than calling process.exit(n) immediately.

Full API in docs/reference/library-api.md; runnable samples in docs/examples/.


Command reference at a glance

GroupCommands
jobsnew · list · get · update · schedule · run-now · delete
runslist · get · cancel
shareexport · import (job definitions only, schema 1; imports get new ids)
statssummary · job
infoinfo (version, paths, daemon status, dashboard URL)
doctordoctor (system health check)
daemondaemon start · daemon stop · daemon restart · daemon status · daemon reload (the daemon also starts on demand)
mcpmcp (start the MCP server on stdio)

Full CLI reference: docs/reference/cli.md.


Storage locations

State and configuration live in a platform-specific data directory (override with CRONTICK_HOME):

OSDefault path
Windows%LOCALAPPDATA%\crontick\
macOS~/Library/Application Support/crontick/
Linux~/.local/share/crontick/

Contributing

See CONTRIBUTING.md for the full guide (DCO, code style, PR process). For coding agents, see AGENTS.md. For testing, see docs/testing/testing.md.

Validate a change:

npm run validate    # lint, type-check, tests, and build

Report bugs at https://github.com/tejitpabari99/crontick/issues.


License

MIT — crontick contributors

Languages

TypeScript

89.5%

JavaScript

8.6%

CSS

1.2%