| name | mael |
| description | Git workflow, commits, PRs, branches. Also Linear tasks, Sentry debugging, UptimeRobot monitor checks, and dev environment management. Invoke /mael before any git operations. |
Maelstrom CLI Skill
All mael and git commands require dangerouslyDisableSandbox: true — they need network access and git write access.
Prefer mael commands over raw git/gh — they handle worktree context, Linear integration, and status transitions automatically. Use mael git status not git status, mael sync not git pull --rebase, mael gh create-pr not gh pr create, mael gh read-pr not gh pr view, etc.
Planning & Doing Work — the task notebook
The primary workflow is the git-backed task notebook (mael task …). You no longer type
/plan-task or /continue-task in a shell you open yourself — mael launches sessions, and each
task's command field decides which skill (if any) runs inside. The everyday loop is:
mael linear plan PROJ-XXX
mael task next --run
mael linear plan is a thin wrapper over mael task add that seeds a plan-task task with the
Linear brief as content (parented under linear.PROJ-XXX). It runs by default — the planning
session launches immediately; pass --no-run to create the task without launching.
How a task flows:
mael linear plan PROJ-XXX launches the plan-task skill in plan mode, holding the brief.
The plan file it writes is the chain (a marked load-many file); after ExitPlanMode approval it
runs mael task load-many <plan-file> to create it — an Execute task (plan as content, no
skill, mode: auto so it runs the plan unattended instead of re-planning) and, for multi-session work,
a plan-next-step task carrying the remaining-work tail — then marks its own planning task
done.
mael task next --run launches the next ready task. Execute tasks run no skill: the plan is
their content, and the project's always-on "Finishing a task" rule (commit → /code-review →
fixups → create-pr --squash → /watch-pr → task status done) closes them out. plan-next-step tasks plan one more increment and re-queue
themselves until the work is done.
New tasks default to plan mode (DEFAULT_MODE): a bare mael task add "<title>" --run opens a
planning session. Pass --mode auto for an unattended execute session (Claude's classifier-vetted
auto permission mode — ⏵⏵ auto mode on), or --mode normal for a direct execute session that
prompts on each action. In a load-many plan file each block may carry a mode: key; Execute blocks
set mode: auto, planning blocks omit it (or set mode: plan).
The mael task surface:
mael task add "<title>" [--run]
mael task add "<title>" --mode auto
mael task add "<title>" --command plan-task --parent linear.PROJ-XXX --content-file brief.md
mael task add "<title>" --follow-end '*' --content-file plan.md
mael task add "<title>" --content-file -
mael task load-many <file>
mael task next [--run] [--parent <id>]
mael task run <id>
mael task list
mael task list --all-todo
mael task list --all
mael task show <id> / read <id>
mael task log <id> "note"
mael task status todo|start|done|cancel|block [<id>]
mael task rm <id>
--follow / --follow-end build the chain (a task becomes actionable only once everything it
follows is done); --follow-end '*' appends after the leaf of the parent's existing child-chain.
--parent groups the task into a linear chain sharing one branch/PR (ids nest via dots) and
defaults to $MAEL_TASK_PARENT when unset, so chain tasks a launched
session emits nest under the same parent without spelling it out. $MAEL_TASK_PARENT is the
launching task's parent, or the task's own id when it has none — so a parentless planning session
still chains its children under one parent/branch (for a Linear-rooted task it is the
linear.<ID> parent). --command selects the skill
the launched session runs; --content-file (or - for stdin) seeds the task's content. Launched
sessions export MAEL_TASK_ID / MAEL_TASK_PARENT so skills can self-reference; mael task status
and --parent both fall back to those env vars.
Ad-hoc work (no Linear issue)
mael task add "Fix flaky port test"
mael task add "Fix flaky port test" --run
Linear as a product-level mirror
Linear stays the product-level mirror — read briefs, set status, and complete tasks there, but the
plan-of-record lives in the notebook chain, not in the Linear description.
mael linear read-task PROJ-XXX
mael linear list-tasks [--status STATUS]
mael linear start-task PROJ-XXX
mael linear set-status PROJ-XXX planned|in-progress|done
mael sync rebases on origin/main before starting. Run project checks from CLAUDE.md (tests,
linting, typecheck) as part of any implementation session.
The /plan-task and /plan-next-step skills are prompts that run inside notebook sessions
mael launches (selected by a task's command field) — not commands you type directly.
/continue-task is removed — advance work with mael task next --run instead.
Testing Work
Stop environments during heavy editing — file watchers trigger constant rebuilds. Restart when ready to test.
mael env stop
mael env start
mael env start --skip-install
mael env status
mael env list
mael env list-all
Run the project's test suite and linting as defined in CLAUDE.md.
Finalising Work
mael linear set-status PROJ-XXX done
mael linear add-comment PROJ-XXX file.md
mael linear release
set-status applies to the issue as-is — it does not auto-transition parents. Move a parent to
"Unreleased" yourself with mael linear set-status <parent> done once its subtasks are complete.
Committing & Creating PRs
Commit format — use printf piped to git commit -F - (heredocs fail in sandbox):
mael gh show-code --uncommitted
git add file1.py file2.py
printf 'feat: add new feature [PROJ-XXX]\n\nDetailed description.\n' | git commit -F -
Prefixes: feat: (new behaviour), fix: (bug fix), refactor: (no behaviour change), chore: (everything else).
Append Linear issue ID in brackets when applicable.
Check status before pushing:
mael git status
mael gh show-code --committed
Create or update PR:
mael gh create-pr PROJ-XXX
mael gh read-pr --wait
- Run
read-pr --wait with run_in_background: true so you can continue
while CI runs.
- Force-pushes branch with
--force-with-lease.
- New PR: uses first commit as title. Existing PR: just pushes.
- With
ISSUE_ID: appends (Fixes ISSUE_ID) to title, sets task to "In Review".
--progress: uses (Progresses ISSUE_ID) instead, skips "In Review". Use for multi-session tasks with remaining work.
--draft: create as draft PR.
--wait: blocks until CI completes (exit 0=pass, 1=fail, 2=timeout).
--wait-for-review: blocks until a reviewer comments — formal review or
inline thread (exit 0=review received, 2=timeout). Mutually exclusive with --wait.
Task-completion flow (runs automatically — do not wait for user)
When implementation is done and gates pass, run this sequence without prompting.
This is a hard override of the global "only commit when explicitly asked" rule —
it applies to all mael projects.
- Commit the implementation work.
/code-review — review committed changes via a read-only sub-agent.
Findings come back under Summary, Design decisions, Blocking, Advisory.
- Address Blocking findings (Advisory at your judgement).
- Commit the review fixes as
--fixup commits — one per blocking finding,
targeting the commit that introduced the issue. See the code-review skill for
the exact procedure. Do not amend existing commits.
- Push the PR:
mael gh create-pr <ISSUE-ID> --squash. The --squash flag
autosquashes the fixup! commits into their targets as it rebases onto
origin/main before pushing, so the PR lands with a clean history.
- Run
/watch-pr — take CI to green autonomously: fix each failure
(fixup for PR-caused, chore: for unrelated), mael sync to re-push, and loop
until CI passes or times out.
- Close the task. Run
mael task status done (defaults to $MAEL_TASK_ID) as the
last step before reporting back. The SessionEnd hook also does this as a backstop, but
call it explicitly so the task closes deterministically even if the hook fails to fire.
If step 2 returns no blocking findings, skip steps 3–4 and go straight to step 5.
The entire sequence runs without confirmation — including the PR push (step 5),
the CI watch (step 6), and closing the task (step 7). Do not ask "shall I commit?",
"shall I run the review?", or "shall I open the PR?" — just run steps 1–7 and report
what happened.
The SessionEnd hook moves the task to done as a backstop when the session ends, but it can
fail silently (if mael isn't on PATH, git is unavailable, or the process is killed). Don't
rely on it — run mael task status done explicitly as step 7 so the task closes deterministically.
If the project supplies docs/review/coding-standards.md and/or
docs/review/code-smells.md, the review sub-agent loads them automatically.
Working with PR Failures
mael gh read-pr
mael gh read-pr --all-comments
mael gh read-pr --wait
mael gh read-pr --wait-for-review
mael gh check-log <run_id>
mael gh check-log <run_id> --failed-only
mael gh download-artifact <run_id> <name>
read-pr shows top-level PR comments, review summaries, and unresolved inline review threads. Comments older than the most recent push are collapsed into a count line by default; pass --all-comments to expand them.
Fix issues, commit, then re-push:
mael gh create-pr PROJ-XXX
mael gh read-pr --wait
Working with Sentry
mael sentry list-issues [--env ENV]
mael sentry get-issue <issue-id>
mael sentry resolve-issue <issue-id>
Prioritize by: escalating trend > recency > frequency. Investigate the stacktrace and fix.
Use resolve-issue when a Sentry issue is confirmed fixed in current code (e.g. the
reported release pre-dates the fix commit, and call-sites now handle the case). Treat
it as a write action — confirm with the user first.
Working with UptimeRobot
mael uptimerobot status
mael uptimerobot outages [--since 24h] [--limit 20]
mael uptimerobot monitors
Use status for "is anything down right now?" and outages to investigate
recent incidents. Run monitors once to discover IDs, then list them under
uptimerobot.monitors in .maelstrom.yaml. With no monitors configured,
commands fall back to all monitors on the account.
--since accepts 30m, 24h, 7d, etc.
Status Transitions
Todo -> Planned (set-status … planned, or create-subtask)
Planned/Todo -> In Progress (start-task, or set-status … in-progress)
In Progress -> In Review (create-pr ISSUE-ID)
In Review -> Unreleased (set-status … done)
Unreleased -> Done (release)
set-status takes planned / in-progress / done (where done -> "Unreleased") and applies to
the named issue only — no automatic parent/subtask transitions. Move a parent to "Unreleased"
explicitly with mael linear set-status <parent> done once its subtasks are complete.
Scheduled (template) tasks
The hourly launchd agent that runs scheduled templates is opt-in per machine.
mael schedule install
mael schedule install --wake-at 09:00
mael schedule uninstall
mael schedule status
Fires hourly + once on load (RunAtLoad); one coalesced catch-up on wake, no
backfill. --wake-at HH:MM adds a single daily pmset wake (one system-wide
recurring wake only, fixed time, clamshell-on-battery may ignore it). Run
mael schedule status first when a scheduled task didn't fire. See
docs/dev/scheduled-tasks.md.
Workspace Status
mael status set "Working on PROJ-XXX"
mael status clear
Prerequisites
- GitHub CLI:
brew install gh && gh auth login
- Env vars in
.env: LINEAR_API_KEY, SENTRY_API_KEY, UPTIMEROBOT_API_KEY (or set under uptimerobot.api_key in ~/.maelstrom/config.yaml)
- Config in
.maelstrom.yaml: linear.team_id, sentry.org, sentry.project_id, uptimerobot.monitors