Skip to content

Sources ​

Each source reads your own items from one place:

SourceReadsYou set
githubYour PRs, PRs awaiting your review, issues assigned to you, in the repos you list, plus any search (label:awaywork, mentions:@me){} is enough; optional search
asanaYour incomplete tasks in the workspace, optionally only some projectsworkspace gid; optional projects, repo_map
linearYour issues neither completed nor canceledOptional repo_map
jiraYour unresolved issuessite, email; optional cloud_id (scoped token), jql (no ORDER BY), repo_map
routinesEach routine's latest occurrenceRoutines
any other nameYour command's JSONCustom sources

Each also reads its finished items when a rule asks. The rest, defaults and token_env names included, is in the reference. Every source takes an every for run: soon; all but github and routines a link.

awaywork sources <id> lists, read-only, the ids and names your keys and where take (--json for scripts):

$ awaywork sources linear
linear:
  teams:
    - key: ENG
      name: Engineering
      states:
        - Todo
        - In Progress
    [...]

github lists your login and orgs; asana the workspaces, then the configured one's projects and sections; jira the projects and statuses, once site and email are set.

The forge, where the repos live and PRs open, is its own top-level key, GitHub unless you say otherwise:

yaml
forge: github                 # the default; leave it out
# forge: bitbucket            # Bitbucket Cloud, see below
# forge: { github: { host: github.acme.com, user: octocat } }   # GitHub Enterprise, or a pinned gh account

host is a GitHub Enterprise host gh is logged in to; user is in Which login. A config has one forge. GitHub items come only with sources.github: without it, GitHub is still where your trackers' work opens its PRs.

Experimental

forge.github.host has only run in tests so far, never against a real GitHub Enterprise. init, doctor and artifact downloads still use gh's default host. Tried it? Open an issue with how it went.

Tracker tokens ​

awaywork only reads with a tracker token, except for on_done, questions and plans, and never hands it to the agent unless you list it in an MCP server's secrets.

  • Asana has no read-only token: make one just for awaywork, so you can revoke it alone.
  • Linear: a personal API key (Settings > Account > Security & access), read-only when offered, or with write for those three.
  • Jira Cloud: an Atlassian API token (id.atlassian.com > Security > API tokens). Prefer one with scopes, app Jira, read:jira-work plus write:jira-work for those three: /away then finds the cloud_id, and awaywork calls api.atlassian.com, as scoped tokens require. An unscoped token can do anything you can. Tokens last at most a year.
  • Jira custom fields sharing a name are keyed by name and id: fields.Team (customfield_10040).

Context commands ​

A built-in source's context command fetches what an item points at, a Sentry issue or a runbook, with secrets the agent never gets:

yaml
sources:
  github:
    context:
      command: ./fetch-sentry.sh   # reads $AWAYWORK_ITEM_URL, writes $AWAYWORK_CONTEXT_DIR/sentry.md
      secrets: [SENTRY_TOKEN]      # kept out of the agent's env

It runs per item before the worker, in the config's folder, never the clone, with AWAYWORK_ITEM_KEY, AWAYWORK_ITEM_URL and AWAYWORK_ITEM_FILE (the item as JSON). What it writes to $AWAYWORK_CONTEXT_DIR lands in .away/context/<item>/extra/ as is, so never write a secret there (curl -v into a file, say). Its limits are in What runs in the clone. A custom source does this in its own command.

Bitbucket and Jira ​

Code on Bitbucket Cloud, tickets in Jira? Set forge: bitbucket and list at least one repo, and draft PRs open on Bitbucket:

yaml
forge: bitbucket
repos:
  acme/*:                          # the workspace is literal, the name globs
sources:
  jira:
    site: acme.atlassian.net
    email: me@acme.com
    repo_map: { ENG: acme/api }    # project key -> workspace/name on Bitbucket

It goes through Atlassian's twg CLI, and git over SSH with your keys. You need twg logged in (twg setup bitbucket, or a token in TWG_BBC_TOKEN) and an SSH key git can use without a passphrase prompt, since launchd and cron have no terminal; a key in an agent your scheduler sees works too. doctor checks both (details at the end of this section).

A run ends as on GitHub, with a draft PR whose footer links back to the item. away/{key} puts the Jira key in the branch, which Jira's development panel links on. Plans work too: twg finds task PRs, reads files a task ships, and the build statuses a task waits on. Not yet:

  • Bitbucket's own items: there is no Bitbucket source, so no fix or review rules on its PRs. Your open PRs still link to items.
  • deliver: push: refused, since twg reads neither branch restrictions nor auto-merge.
  • deliver: pending_review: refused; GitHub only.
  • Going on with its own draft: a draft that says continue, or asked, is yours to finish (Continue).
  • GitHub Actions or a container: schedule install refuses both; use launchd, systemd or cron.
  • Fork PRs: no pull refs to fetch, so the unit fails.
  • The github source: refused with forge: bitbucket; one forge per config.

What doctor prints:

✔  twg: 1.3.8
✔  twg acme
✔  git over ssh: acme/api

twg acme is twg bb workspace get per workspace; git over ssh a git ls-remote with -o BatchMode=yes added to git's ssh command (GIT_SSH_COMMAND, else core.sshCommand, else ssh; a GIT_SSH wrapper runs as is), so a prompting key fails here, not in a scheduled run. It warns when twg keeps its login anywhere but a file (Safety).

Routines ​

Work with no ticket, only a day: every Monday you triage Sentry. A routine is an item that comes due on the clock, so rules, plan and attempts treat it like any other:

yaml
sources:
  routines:
    sentry-triage:
      days: [mon]               # sun to sat; every day when unset
      at: "09:00"               # local time; or a list: ["09:00", "14:00"]
      repo: acme/api            # required: where the work happens
      title: Triage last week's Sentry issues
      description: Group new issues, link duplicates, draft fixes for the top three.

queue:
  - skill: triage
    source: routines
    where: { name: sentry-triage }
    deliver: report
    run: soon                   # within minutes of 09:00, not at the next window

Each is one task, its latest occurrence, keyed <name>-<YYYYMMDD>-<HHMM> in local time: branch away/sentry-triage-20261026-0900. plan lists when each comes due next, and --why which occurrence an item is:

$ awaywork plan --why sentry-triage-20261026-0900
routines sentry-triage-20261026-0900  Triage last week's Sentry issues
  https://github.com/acme/api
  routine sentry-triage, due mon 2026-10-26 09:00; next mon 2026-11-02 09:00
  [...]

Details:

  • A machine asleep all week wakes to one item, not a backlog. Once a unit on it finishes, it's unchanged until the next occurrence, a new item.
  • It's yours (mine), its url the repo's page. Rules match name, repo, created and updated (both the occurrence).
  • Any name but context and every.
  • On GitHub Actions, times are the workflow's TZ. Without run: soon it waits for a window: Monday's 09:00 runs Tuesday at 01:00 with the default one.
  • No tracker to write to: on_done and deliver: plan are refused, and results or questions stay in the report; notify can send them on.
  • It takes a context command. init writes no rules for routines: write yours.