> ## Documentation Index
> Fetch the complete documentation index at: https://docs.runmaestro.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Cue Event Types

> Detailed reference for all ten Maestro Cue event types with configuration, payloads, and examples.

Cue supports ten event types. Each type watches for a different kind of activity and produces a payload that can be injected into prompts via [template variables](./maestro-cue-advanced#template-variables).

## app.startup

Fires exactly once when the Maestro application launches. Ideal for workspace setup, dependency installation, health checks, or any initialization that should happen once per session.

**Required fields:** None beyond the universal `name`, `event`, and either `prompt` or `prompt_file`.

**Behavior:**

* Fires once per application launch
* Does NOT re-fire when toggling Cue on/off in Settings
* Does not re-fire on YAML hot-reload (deduplication by subscription name)
* Resets on session removal (so re-adding the session fires again on next app launch)
* Not affected by sleep/wake reconciliation
* Works with `fan_out`, `filter`, `output_prompt`, and `prompt_file`

**Example:**

```yaml theme={"theme":"dracula"}
subscriptions:
  - name: init-workspace
    event: app.startup
    prompt: |
      Set up the development environment:
      1. Run `npm install` if node_modules is missing
      2. Check that required env vars are set
      3. Report any issues found
```

**Payload fields:**

| Field    | Type   | Description               |
| -------- | ------ | ------------------------- |
| `reason` | string | Always `"system_startup"` |

***

## time.heartbeat

Fires on a periodic timer. The subscription triggers immediately when the engine starts, then repeats at the configured interval.

**Required fields:**

| Field              | Type   | Description                            |
| ------------------ | ------ | -------------------------------------- |
| `interval_minutes` | number | Minutes between triggers (must be > 0) |

**Behavior:**

* Fires immediately on engine start (or when the subscription is first loaded)
* Reconciles missed intervals after system sleep - if your machine sleeps through one or more intervals, Cue fires a catch-up event on wake
* The interval resets after each trigger, not after each run completes

**Example:**

```yaml theme={"theme":"dracula"}
subscriptions:
  - name: hourly-summary
    event: time.heartbeat
    interval_minutes: 60
    prompt: |
      Generate a summary of git activity in the last hour.
      Run `git log --oneline --since="1 hour ago"` and organize by author.
```

**Payload fields:** None specific to this event type. Use common variables like `{{CUE_TRIGGER_NAME}}` and `{{CUE_EVENT_TIMESTAMP}}`.

***

## time.scheduled

Fires at specific times and days of the week - a cron-like trigger for precise scheduling.

**Required fields:**

| Field            | Type      | Description                                      |
| ---------------- | --------- | ------------------------------------------------ |
| `schedule_times` | string\[] | Array of times in `HH:MM` format (24-hour clock) |

**Optional fields:**

| Field           | Type      | Default   | Description                                                              |
| --------------- | --------- | --------- | ------------------------------------------------------------------------ |
| `schedule_days` | string\[] | every day | Days of the week to run: `mon`, `tue`, `wed`, `thu`, `fri`, `sat`, `sun` |

**Behavior:**

* Checks every 60 seconds if the current time matches any `schedule_times` entry
* If `schedule_days` is set, the current day must also match
* Does **not** fire immediately on engine start (unlike `time.heartbeat`)
* Multiple times per day are supported - add multiple entries to `schedule_times`

**Example - weekday standup:**

```yaml theme={"theme":"dracula"}
subscriptions:
  - name: morning-standup
    event: time.scheduled
    schedule_times:
      - '09:00'
    schedule_days:
      - mon
      - tue
      - wed
      - thu
      - fri
    prompt: |
      Generate a standup report:
      1. Run `git log --oneline --since="yesterday"` to find recent changes
      2. Check for any failing tests
      3. Summarize what was accomplished and what's next
```

**Example - multiple times daily:**

```yaml theme={"theme":"dracula"}
subscriptions:
  - name: status-check
    event: time.scheduled
    schedule_times:
      - '09:00'
      - '13:00'
      - '17:00'
    prompt: |
      Run a quick health check on all services.
```

**Payload fields:**

| Field          | Description                           | Example |
| -------------- | ------------------------------------- | ------- |
| `matched_time` | The scheduled time that matched       | `09:00` |
| `matched_day`  | The day of the week when it triggered | `mon`   |

***

## time.once

Fires exactly once at a specific wall-clock moment, then deletes itself from `cue.yaml`. This is the subsystem to reach for when a user says "in 20 minutes do X", "tomorrow at 9am email me a summary", "remind me at 4pm to push the rc branch", or "schedule a 1h check-in" - anything that maps to a single action tied to a clock.

**Required fields:**

| Field     | Type              | Description                                                                                                                                                         |
| --------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `fire_at` | string (ISO-8601) | The wall-clock moment to fire. **Must include a timezone offset** (`Z`, `±HH:MM`, or the colon-less `±HHMM`). Anything missing the offset is rejected at load time. |

**Optional fields:**

| Field                      | Type    | Default | Description                                                                                                                                                                                                                                                      |
| -------------------------- | ------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `grace_minutes`            | number  | `360`   | Missed-fire grace window in minutes. If Maestro is closed when `fire_at` arrives, the sub fires on next startup as long as the current time is within this window of `fire_at`. Past the window, the sub is logged as expired and self-destructs without firing. |
| `self_destruct_on_failure` | boolean | `true`  | Whether to remove the sub from `cue.yaml` on a `failed` or `timeout` outcome. Set `false` to keep the sub in place for post-mortem inspection.                                                                                                                   |

**Behavior:**

* Poll-based at roughly 30-second granularity
* Fires once when the wall clock crosses `fire_at`
* Self-destructs from `cue.yaml` after a successful run (status `completed`)
* On `failed` or `timeout`, also self-destructs unless `self_destruct_on_failure: false`
* Supports all standard `action` values: `prompt` (default, runs the configured agent with the rendered prompt), `notify` (surfaces a toast through the owning agent - clicking it jumps there), and `command` (shell or CLI invocation)
* The CLI command `maestro-cli cue schedule` is the supported way to create `time.once` subs - it handles ISO formatting, timezone offsets, `target_node_key` generation, and writes directly to the owning agent's `.maestro/cue.yaml`. **Do not hand-author `time.once` subscriptions.**

**Example - notify reminder:**

```yaml theme={"theme":"dracula"}
subscriptions:
  - name: tasks-once-push-rc-reminder
    pipeline_name: Tasks
    event: time.once
    enabled: true
    fire_at: '2026-05-22T16:00:00-05:00'
    agent_id: <agent-uuid>
    action: notify
    notify:
      message: 'Push rc branch - review the diff first'
      sticky: true
```

**Example - prompt run:**

```yaml theme={"theme":"dracula"}
subscriptions:
  - name: tasks-once-20m-deploy-check
    pipeline_name: Tasks
    event: time.once
    enabled: true
    fire_at: '2026-05-22T14:20:00-05:00'
    agent_id: <agent-uuid>
    prompt: |
      Check the status of the deploy I kicked off and summarize the result.
```

**Payload fields:**

| Variable          | Description                                             | Example                     |
| ----------------- | ------------------------------------------------------- | --------------------------- |
| `{{CUE_FIRE_AT}}` | The originally-scheduled `fire_at` timestamp (ISO-8601) | `2026-05-22T16:00:00-05:00` |

The standard `{{CUE_TRIGGER_NAME}}`, `{{CUE_EVENT_TYPE}}`, `{{CUE_EVENT_TIMESTAMP}}`, and `{{CUE_PIPELINE_NAME}}` variables are also available.

**Creating, listing, and cancelling:**

```bash theme={"theme":"dracula"}
# Schedule via relative offset
maestro-cli cue schedule --in 20m --agent <agent-id> --prompt "Check the deploy status."

# Schedule via absolute timestamp + notify-only
maestro-cli cue schedule --at "2026-05-22 16:00" --agent <agent-id> --notify --sticky --message "Push rc branch"

# List every pending one-time task across agents
maestro-cli cue schedule --list

# Cancel a pending task by sub name
maestro-cli cue schedule --cancel tasks-once-push-rc-reminder
```

***

## file.changed

Fires when files matching a glob pattern are created, modified, or deleted.

**Required fields:**

| Field   | Type          | Description                       |
| ------- | ------------- | --------------------------------- |
| `watch` | string (glob) | Glob pattern for files to monitor |

**Behavior:**

* Monitors for `add`, `change`, and `unlink` (delete) events
* Debounces by 5 seconds per file - rapid saves to the same file produce a single event
* The glob is evaluated relative to the project root
* Standard glob syntax: `*` matches within a directory, `**` matches across directories

**Example:**

```yaml theme={"theme":"dracula"}
subscriptions:
  - name: test-on-change
    event: file.changed
    watch: 'src/**/*.{ts,tsx}'
    filter:
      changeType: '!unlink' # Don't trigger on file deletions
    prompt: |
      The file {{CUE_FILE_PATH}} was {{CUE_EVENT_TYPE}}.
      Run the tests related to this file and report results.
```

**Payload fields:**

| Variable                   | Description                       | Example                   |
| -------------------------- | --------------------------------- | ------------------------- |
| `{{CUE_FILE_PATH}}`        | Absolute path to the changed file | `/project/src/app.ts`     |
| `{{CUE_FILE_NAME}}`        | Filename only                     | `app.ts`                  |
| `{{CUE_FILE_DIR}}`         | Directory containing the file     | `/project/src`            |
| `{{CUE_FILE_EXT}}`         | File extension (with dot)         | `.ts`                     |
| `{{CUE_FILE_CHANGE_TYPE}}` | Change type                       | `add`, `change`, `unlink` |

The `changeType` field is also available in [filters](./maestro-cue-advanced#filtering).

***

## agent.completed

Fires when another Maestro agent finishes a task. This is the foundation for agent chaining - building multi-step pipelines where one agent's completion triggers the next.

**Required fields:**

| Field            | Type           | Description                                     |
| ---------------- | -------------- | ----------------------------------------------- |
| `source_session` | string or list | Name(s) of the agent(s) to watch for completion |

**Behavior:**

* **Single source** (string): Fires immediately when the named agent completes
* **Multiple sources** (list): Waits for **all** named agents to complete before firing (fan-in). See [Fan-In](./maestro-cue-advanced#fan-in)
* The source agent's output is captured and available via `{{CUE_SOURCE_OUTPUT}}` (truncated to 5,000 characters)
* Matches agent names as shown in the Left Bar

**Example - single source:**

```yaml theme={"theme":"dracula"}
subscriptions:
  - name: deploy-after-build
    event: agent.completed
    source_session: 'builder'
    filter:
      exitCode: 0 # Only deploy if build succeeded
    prompt: |
      The build agent completed successfully.
      Output: {{CUE_SOURCE_OUTPUT}}

      Deploy to staging with `npm run deploy:staging`.
```

**Example - fan-in (multiple sources):**

```yaml theme={"theme":"dracula"}
subscriptions:
  - name: integration-tests
    event: agent.completed
    source_session:
      - 'backend-build'
      - 'frontend-build'
    prompt: |
      Both builds completed. Run the full integration test suite.
```

**Payload fields:**

| Variable                      | Description                                            | Example           |
| ----------------------------- | ------------------------------------------------------ | ----------------- |
| `{{CUE_SOURCE_SESSION}}`      | Name of the completing agent(s)                        | `builder`         |
| `{{CUE_SOURCE_OUTPUT}}`       | Truncated stdout from the source (max 5K chars)        | `Build succeeded` |
| `{{CUE_SOURCE_STATUS}}`       | Run status (`completed`, `failed`, `timeout`)          | `completed`       |
| `{{CUE_SOURCE_EXIT_CODE}}`    | Process exit code                                      | `0`               |
| `{{CUE_SOURCE_DURATION}}`     | Run duration in milliseconds                           | `15000`           |
| `{{CUE_SOURCE_TRIGGERED_BY}}` | Name of the subscription that triggered the source run | `lint-on-save`    |

These fields are also available in [filters](./maestro-cue-advanced#filtering).

The `triggeredBy` field is particularly useful when a source agent has multiple Cue subscriptions but you only want to chain from a specific one. See [Selective Chaining](./maestro-cue-examples#selective-chaining-with-triggeredby) for a complete example.

***

## task.pending

Watches markdown files for unchecked task items (`- [ ]`) and fires when pending tasks are found.

**Required fields:**

| Field   | Type          | Description                             |
| ------- | ------------- | --------------------------------------- |
| `watch` | string (glob) | Glob pattern for markdown files to scan |

**Optional fields:**

| Field          | Type   | Default | Description                       |
| -------------- | ------ | ------- | --------------------------------- |
| `poll_minutes` | number | 1       | Minutes between scans (minimum 1) |

**Behavior:**

* Scans files matching the glob pattern at the configured interval
* Fires when unchecked tasks (`- [ ]`) are found
* Only fires when the task list changes (new tasks appear or existing ones are modified)
* The full task list is formatted and available via `{{CUE_TASK_LIST}}`
* File content (truncated to 10K characters) is available via `{{CUE_TASK_CONTENT}}`

**Example:**

```yaml theme={"theme":"dracula"}
subscriptions:
  - name: todo-worker
    event: task.pending
    watch: '**/*.md'
    poll_minutes: 5
    prompt: |
      Found {{CUE_TASK_COUNT}} pending tasks in {{CUE_TASK_FILE}}:

      {{CUE_TASK_LIST}}

      Pick the most important task and complete it.
      When finished, mark it as done by changing `- [ ]` to `- [x]`.
```

**Payload fields:**

| Variable                 | Description                                | Example                |
| ------------------------ | ------------------------------------------ | ---------------------- |
| `{{CUE_TASK_FILE}}`      | Path to the file containing tasks          | `/project/TODO.md`     |
| `{{CUE_TASK_FILE_NAME}}` | Filename only                              | `TODO.md`              |
| `{{CUE_TASK_FILE_DIR}}`  | Directory containing the file              | `/project`             |
| `{{CUE_TASK_COUNT}}`     | Number of pending tasks found              | `3`                    |
| `{{CUE_TASK_LIST}}`      | Formatted list with line numbers           | `L5: Write unit tests` |
| `{{CUE_TASK_CONTENT}}`   | Full file content (truncated to 10K chars) | *(file contents)*      |

***

## github.pull\_request

Polls GitHub for new pull requests using the GitHub CLI (`gh`).

**Optional fields:**

| Field          | Type   | Default | Description                                                                  |
| -------------- | ------ | ------- | ---------------------------------------------------------------------------- |
| `repo`         | string | auto    | GitHub repo in `owner/repo` format. Auto-detected from git remote if omitted |
| `poll_minutes` | number | 5       | Minutes between polls (minimum 1)                                            |

**Behavior:**

* Requires the [GitHub CLI](https://cli.github.com/) (`gh`) to be installed and authenticated
* On first run, seeds the "seen" list with existing PRs - only **new** PRs trigger events
* Tracks seen PRs in a local database with 30-day retention
* Auto-detects the repository from the git remote if `repo` is not specified

**Example:**

```yaml theme={"theme":"dracula"}
subscriptions:
  - name: pr-reviewer
    event: github.pull_request
    poll_minutes: 3
    filter:
      draft: false # Skip draft PRs
      base_branch: main # Only PRs targeting main
    prompt: |
      New PR: {{CUE_GH_TITLE}} (#{{CUE_GH_NUMBER}})
      Author: {{CUE_GH_AUTHOR}}
      Branch: {{CUE_GH_BRANCH}} -> {{CUE_GH_BASE_BRANCH}}
      Labels: {{CUE_GH_LABELS}}
      URL: {{CUE_GH_URL}}

      {{CUE_GH_BODY}}

      Review this PR for:
      1. Code quality and style consistency
      2. Potential bugs or edge cases
      3. Test coverage
```

**Payload fields:**

| Variable                 | Description                       | Example                               |
| ------------------------ | --------------------------------- | ------------------------------------- |
| `{{CUE_GH_TYPE}}`        | Always `pull_request`             | `pull_request`                        |
| `{{CUE_GH_NUMBER}}`      | PR number                         | `42`                                  |
| `{{CUE_GH_TITLE}}`       | PR title                          | `Add user authentication`             |
| `{{CUE_GH_AUTHOR}}`      | Author's GitHub login             | `octocat`                             |
| `{{CUE_GH_URL}}`         | HTML URL to the PR                | `https://github.com/org/repo/pull/42` |
| `{{CUE_GH_BODY}}`        | PR description (truncated)        | *(PR body text)*                      |
| `{{CUE_GH_LABELS}}`      | Comma-separated label names       | `bug, priority-high`                  |
| `{{CUE_GH_STATE}}`       | PR state                          | `open`                                |
| `{{CUE_GH_BRANCH}}`      | Head (source) branch              | `feature/auth`                        |
| `{{CUE_GH_BASE_BRANCH}}` | Base (target) branch              | `main`                                |
| `{{CUE_GH_REPO}}`        | Repository in `owner/repo` format | `RunMaestro/Maestro`                  |

***

## github.issue

Polls GitHub for new issues using the GitHub CLI (`gh`). Behaves identically to `github.pull_request` but for issues.

**Optional fields:**

| Field          | Type   | Default | Description                        |
| -------------- | ------ | ------- | ---------------------------------- |
| `repo`         | string | auto    | GitHub repo in `owner/repo` format |
| `poll_minutes` | number | 5       | Minutes between polls (minimum 1)  |

**Behavior:**

Same as `github.pull_request` - requires GitHub CLI, seeds on first run, tracks seen issues.

**Example:**

```yaml theme={"theme":"dracula"}
subscriptions:
  - name: issue-triage
    event: github.issue
    poll_minutes: 5
    filter:
      labels: '!wontfix' # Skip issues labeled wontfix
    prompt: |
      New issue: {{CUE_GH_TITLE}} (#{{CUE_GH_NUMBER}})
      Author: {{CUE_GH_AUTHOR}}
      Assignees: {{CUE_GH_ASSIGNEES}}
      Labels: {{CUE_GH_LABELS}}

      {{CUE_GH_BODY}}

      Triage this issue:
      1. Identify the area of the codebase affected
      2. Estimate complexity (small/medium/large)
      3. Suggest which team member should handle it
```

**Payload fields:**

Same as `github.pull_request`, except:

| Variable               | Description                     | Example      |
| ---------------------- | ------------------------------- | ------------ |
| `{{CUE_GH_TYPE}}`      | Always `issue`                  | `issue`      |
| `{{CUE_GH_ASSIGNEES}}` | Comma-separated assignee logins | `alice, bob` |

The branch-specific variables (`{{CUE_GH_BRANCH}}`, `{{CUE_GH_BASE_BRANCH}}`) are not available for issues.

***

## cli.trigger

Fires only when explicitly triggered from the command line via `maestro-cli cue trigger <name>`. Unlike other event types, `cli.trigger` has no background watcher or poller - it waits for a manual invocation.

**No additional fields required** - just `name`, `event`, `prompt`, and optionally `enabled`.

**Behavior:**

* Does nothing on its own - only fires when you run `maestro-cli cue trigger <subscription-name>`
* Supports an optional `--prompt` flag to override or supply the prompt at invocation time
* The override text is available in the prompt template as `{{CUE_CLI_PROMPT}}`
* Ideal for deployment scripts, CI/CD integration, on-demand reviews, or ad-hoc automation

**Example:**

```yaml theme={"theme":"dracula"}
subscriptions:
  - name: deploy
    event: cli.trigger
    prompt: |
      Run the deployment pipeline for the current branch.

      Additional instructions: {{CUE_CLI_PROMPT}}
    enabled: true
```

**Triggering from the command line:**

```bash theme={"theme":"dracula"}
# Basic trigger - uses the configured prompt as-is
maestro-cli cue trigger deploy

# With a prompt override - {{CUE_CLI_PROMPT}} receives this text
maestro-cli cue trigger deploy --prompt "Deploy to staging only"

# JSON output for scripting
maestro-cli cue trigger deploy --json
```

**Discovering available subscriptions:**

```bash theme={"theme":"dracula"}
# List all subscriptions across agents
maestro-cli cue list
```

**Payload fields:**

| Variable               | Description                                 | Example             |
| ---------------------- | ------------------------------------------- | ------------------- |
| `{{CUE_CLI_PROMPT}}`   | Prompt text passed via `--prompt` flag      | `Deploy to staging` |
| `{{CUE_TRIGGER_NAME}}` | Name of the subscription that was triggered | `deploy`            |
| `{{CUE_EVENT_TYPE}}`   | Always `cli.trigger`                        | `cli.trigger`       |
