Warning
Jules is shutting down, so cjules is no longer maintained. Google announced that Jules becomes read-only after November 11, 2026 and shuts down on December 31, 2026. v0.3.0 is the final release of cjules. The repository will be archived after that. Your code, branches, and PRs stay on GitHub. To keep session history, use the official export from the announcement before the service goes away.
A scriptable CLI for Jules, written in Crystal.
- Git-aware
new— auto-detects--repoand--branch; reads the prompt from args,stdin, or--file. - Parallel sessions —
cjules new --parallel Nruns N sessions with the same prompt. - Watch —
cjules watch <id>tails activities;--auto-approve --reply --notify desktop|slack|discordfor hands-free runs. - Prune — filter by state, age, repo, or
--all; dry-run by default,-yto apply. - Patch & PR —
cjules patch <id> --apply(or-ifor interactive hunks) runsgit apply;cjules pr <id> --openopens the PR. - Git helpers —
cjules commit-msggenerates a commit message fromgit diff(or a session);cjules pr-describe <id> --copygenerates a Markdown PR description from the session's actions and patch. - Logs —
cjules logs <id> -f mdfor a full report,--bashfor shell transcripts,--save-mediafor artifacts. - Pipe-friendly —
-f table|json|jsonl|yamlon every list command. - Multi-account — aliases via
cjules accounts use, or one-shot with--account. - Pick —
cjules pick(usesfzfif available) with--action show|watch|pr|delete. - Retry —
cjules retry <id>re-runs a session by cloning its prompt, repo, branch, and flags;--with-failure-reasoncarries the original failure message into the new prompt;cjules troubleshoot <id>diagnoses compile errors, missing shards, and failing specs from the logs and offers a retry with a fix note. - Templates — drop reusable prompts into
~/.config/cjules/templates/and reference them via--template <name>onneworretry. Templates support dynamic variables:{{.File "path"}},{{.GitDiff}}, and{{.Var "name"}}for powerful prompt generation.
Heads up: the Jules API is currently labelled
v1alpha. Schema and behaviour can change without notice. Pin a release of cjules in scripts you care about and read the CHANGELOG when upgrading.
brew tap hahwul/cjules
brew install cjules
shards build --release
install bin/cjules /usr/local/bin/npx skills add hahwul/cjulesIssue an API key at jules.google.com/settings/api and save it with cjules login.
cjules login --alias work # prompts for the API key (input hidden)
cjules login --alias personal --key AIza...
cjules accounts ls
cjules accounts use personal
cjules logout workResolution order: JULES_API_KEY env var → JULES_ACCOUNT env var → active alias from config.
# Create a session for the current git repo/branch
cjules new "Add a healthcheck endpoint"
# Pipe a longer prompt and let Jules open the PR automatically
cat PROMPT.md | cjules new --auto-pr -
# Read the prompt from a file, override repo/branch, require plan approval
cjules new --file PROMPT.md --repo hahwul/cjules --branch main --require-approval
# Repoless session (no sourceContext)
cjules new --no-repo "Draft release notes for v0.2.0"
# Fan out N parallel sessions with the same prompt and capture the IDs
cjules new --parallel 5 --auto-pr "Refactor the config loader" -f json | jq -r '.[].id' > ids.txtBurst submissions: under heavy parallelism the Jules API can return
HTTP 400 Precondition check failedfor a session that did get created server-side. By defaultcjules newreconciles 4xx failures by matching against sessions in the account that were created since the call started (preferring--title, falling back to the prompt). Pass--no-reconcile-on-errorto opt out and surface the raw error.
# Watch a session live (3s default poll)
cjules watch <session-id> --interval 5
# Hands-free run: auto-approve plans, reply to feedback prompts, and send desktop/webhook notifications
cjules watch <session-id> --auto-approve --reply --notify desktop
cjules watch <session-id> --notify slack,discord
# Send a follow-up message
cjules msg <session-id> "Also add a /readyz endpoint"
# Or pipe one in
echo "Please add tests for the error path" | cjules msg <session-id> -
# Approve a pending plan explicitly
cjules approve <session-id># Recent failures as JSONL, pipe into jq
cjules ls --state FAILED --since 7d -f jsonl | jq -r '.id + "\t" + .title'
# Re-pull logs for the latest failure
cjules ls --state FAILED --since 7d -f jsonl | jq -r .id | head -1 | xargs cjules logs
# Interactive picker (uses fzf if installed) — default action shows the session
cjules pick
cjules pick --action watch # pick → watch
cjules pick --action pr # pick → print PR URL# Inspect or apply the resulting patch locally
cjules patch <session-id> --list
cjules patch <session-id> --apply
cjules patch <session-id> --interactive # select hunks (like `git add -p`)
# Print or open the PR
cjules pr <session-id>
cjules pr <session-id> --open
# Generate a Markdown PR description (and optionally copy to clipboard)
cjules pr-describe <session-id>
cjules pr-describe <session-id> --copy
# Generate a commit message from local git diff or from a session's patch
cjules commit-msg
cjules commit-msg --staged --copy
cjules commit-msg <session-id> --copy
# Full session report as Markdown
cjules logs <session-id> -f md > report.md
# Pull only bash command/output blocks (handy for debugging long runs)
cjules logs <session-id> --bash
# Save media artifacts (screenshots, etc.) to a directory
cjules logs <session-id> --save-media ./artifactsretry clones a session's prompt, repo, branch, title, and --auto-pr /
--require-approval flags into a brand-new session. Handy for FAILED runs.
# Plain retry: same prompt, same repo/branch, new session
cjules retry <session-id>
# Retry and feed the original failure reason back into the prompt
cjules retry <session-id> --with-failure-reason
# Tweak the prompt for the next attempt without losing the rest of the context
cjules retry <session-id> --note "Skip the migration step this time."
# Override prompt or branch entirely
cjules retry <session-id> --prompt "New approach: ..."
cjules retry <session-id> --branch experimental-fix
# Pick the most-recent failure and retry it in one line
cjules ls --state FAILED --limit 1 -f jsonl | jq -r .id | xargs cjules retry --with-failure-reason
# Diagnose a failure from its bash logs and retry with a suggested fix note
cjules troubleshoot <session-id> # asks [Y/n] before retrying
cjules troubleshoot <session-id> --dry-run
cjules troubleshoot <session-id> --yesDrop *.md or *.txt files into ~/.config/cjules/templates/ and reference
them by filename. Templates are looked up there by short name.
Templates now support dynamic variables for powerful prompt generation:
{{.File "path"}}— Insert file contents{{.GitDiff}}— Insert current git diff{{.Var "name"}}— Insert user-defined variables passed via--var
# See what's available and where they live
cjules templates ls
cjules templates path
cjules templates show bugfix # print body
# Use a template as the prompt for a new session
cjules new --template bugfix
cjules new --template bugfix --auto-pr --branch hotfix
# Use templates with variables
cjules new --template refactor --var file=parser.cr --var priority=high
# Or as the prompt for a retry
cjules retry <session-id> --template bugfixExample template (~/.config/cjules/templates/refactor.md):
Please refactor the following code:
[Code]
{{.File "src/parser.cr"}}
[Notes]
{{.Var "note"}}
[Current Changes]
{{.GitDiff}}Usage:
cjules new --template refactor --var note="Improve error handling"prune is dry-run by default — review the matches, then re-run with -y to actually delete.
# Preview what would be deleted
cjules prune --completed --older-than 30d
# Actually delete after the preview looks right
cjules prune --completed --older-than 30d -y
# Sweep failed sessions for a specific repo
cjules prune --failed --repo hahwul/cjules -y
# Match an arbitrary state
cjules prune --state AWAITING_USER_FEEDBACK --older-than 14d -y
# Wipe every session for the active account (cannot be combined with other filters)
cjules prune --all # dry-run preview
cjules prune --all -y # prompts for typed 'yes' confirmation
# One-off targeted delete (no filters needed)
cjules rm <session-id> <session-id> ...# Run a single command against a non-active account
cjules --account personal ls --since 24h
# One-off override via env var
JULES_ACCOUNT=work cjules new "Bump dependencies"
JULES_API_KEY=AIza... cjules lsStored at ~/.config/cjules/config.yml with 0600 permissions. Set defaults:
cjules config set default_repo hahwul/cjules
cjules config set default_branch main
cjules config set slack_webhook https://hooks.slack.com/services/...
cjules config set discord_webhook https://discord.com/api/webhooks/...cjules completion zsh > "${fpath[1]}/_cjules"
cjules completion bash > /etc/bash_completion.d/cjules
cjules completion fish > ~/.config/fish/completions/cjules.fishshards build
crystal spec
just lint # requires shards install (pulls ameba)
just check # format checkMIT — see LICENSE.
