Skip to content

GitHub Copilot App workflows (Experimental)

See the Targets matrix for where copilot-app fits alongside the other deploy targets.

When copilot-app is enabled and a package ships a prompt with workflow frontmatter (any of interval, schedule_hour, schedule_day at the top level), apm install --target copilot-app inserts the prompt as a row in the GitHub Copilot desktop App’s SQLite store at ~/.copilot/data.db. Add --global to install from a user-scope ~/.apm/apm.yml, or omit it to install from a project’s apm.yml (typical for team-shared scheduled prompts). The App reads new rows on next launch (or refresh) and lists them under Workflows.

Prompts that do not carry workflow frontmatter are plain slash commands: they deploy to file-based targets (copilot, vscode, claude, …) and APM hard-errors with an actionable diagnostic if you point them at copilot-app directly. A single .prompt.md belongs to exactly ONE surface — whichever its frontmatter shape selects.

The copilot target writes prompts as files (.github/prompts/<name>.prompt.md) for Copilot in IDEs. The desktop App stores workflows in a SQLite database, not on disk. They are different surfaces; copilot-app exists so that one APM install can serve both without leakage.

Add workflow frontmatter (flat top-level keys) to any .prompt.md file in your package’s .apm/prompts/ folder:

---
name: Daily Digest
interval: daily # one of: manual, hourly, daily, weekly
schedule_hour: 9 # 0-23, UTC; ignored for manual / hourly
schedule_day: 1 # 0-6 (weekly only)
mode: interactive # one of: interactive, plan
model: claude-opus-4.7 # optional
reasoning_effort: high # optional
---
Summarise yesterday's commits across all open PRs ...

Manual-only workflows omit schedule_hour / schedule_day and set interval: manual (the default when any other execution-shape key is present). The Copilot App provides a “run now” affordance for every workflow, so manual-only is a useful shape — no schedule, just a named, parameterised prompt the user can fire from the App UI.

The Copilot App also defines an autopilot mode, but APM intentionally does NOT accept it via this target. Until package signing ships, a third-party package could declare mode: autopilot and have the App auto-run the prompt the moment you flip the in-App enable toggle. Refusing autopilot at the writer is the secure-by-default behaviour; you can still set autopilot yourself on a per-row basis from the App UI after install.

apm actionEffect on ~/.copilot/data.db
apm installINSERT row with enabled = 0 (always disabled on install — you opt in).
apm install (already installed, content unchanged)UPDATE display fields only. enabled, last_run_at, next_run_at are preserved.
apm install (already installed, any execution-affecting field changed)UPDATE row; reset enabled = 0; clear next_run_at.
apm uninstallDELETE only APM-namespaced rows (apm--<owner>--<pkg>--<prompt>). User-authored rows are never touched.

Execution-affecting fields are the prompt body, schedule (interval / schedule_hour / schedule_day), mode, model, and reasoning_effort. The reset is by design: you opted in to a specific prompt, so any change to what runs or when is a new consent surface.

Removing the source .prompt.md from a package and re-syncing drops the lockfile entry but does NOT delete the corresponding row from ~/.copilot/data.db — it merely orphans it. Run apm uninstall <pkg> to remove the row.

Workflows are scoped to a real Copilot App project so “Run now” in the Workflows tab CWDs into the right repository and the row groups under the correct sidebar entry. APM resolves the project once per install, then stamps every workflow row’s project_id.

Two resolution paths run in this order:

OrderWhen it firesWhat it does
1The Copilot App is running and is reachable on its loopback WebSocket (~/.copilot/run/ws.{port,token}, 0o600).APM calls the App’s own create_project_from_path over IPC. The App runs full discovery (GitHub owner/repo detection, default branch, account binding) and the resulting project is immediately known to the webview, so opening the Workflows tab cannot hit the white-screen-on-unknown-project failure mode.
2The Copilot App is closed, OR the WebSocket surface is unreachable for any reason (stale token after restart, etc).APM falls back to a direct-SQLite BEGIN IMMEDIATE resolver: SELECT by main_repo_path (UNIQUE), INSERT if missing.

In both paths the workflow rows are written via direct SQLite — the WS surface is currently project-registration only — so lockfile ids stay namespaced and stable (apm--<owner>--<pkg>--<prompt>) across runs and across the WS-vs-SQLite branch.

If APM cannot derive a repo context at all (no .git/, no origin, etc) the workflows install with project_id = NULL. You can attach them to any project after the fact from the App’s Workflows tab.

The first time APM registers a brand-new project for a given repository, install prints:

[i] Registered a new Copilot App project for this repo. Restart the Copilot App once so the new project appears in the UI (see github/github-app#5483).

Subsequent installs into the same repo are silent. The hint is upstream-bug guidance: the App’s webview does not currently refresh its projects sidebar when a new projects row appears mid-session, so one restart wires the new project into the UI. Once the App learns about the project, neither the install nor the App needs to be restarted again.

Workflows installed with apm install --global run with CWD=~/.copilot, not a repository — which is almost never what the user wants. APM still deploys global workflows (so global skills and commands keep working), but emits a one-time warn-and-proceed diagnostic whenever a --global install carries any workflow-shape prompt:

[!] Copilot App workflows installed with --global run with CWD=~/.copilot, not a project. Attach the workflow to a project from the App's Workflows tab to fix this, or re-run `apm install` from a repo without --global.

The remediation is per-row: attach the workflow to any project from the Workflows tab in the App. Or re-run apm install from inside a repo without --global.

Use apm experimental enable copilot-app to turn the target on, apm experimental list to see all flags, and apm experimental disable copilot-app to turn it off again. See the Experimental flags reference for the complete subcommand surface.

OrderSource
1APM_COPILOT_APP_DB environment variable (absolute path; used as-is).
2~/.copilot/data.db if it exists.

If neither resolves, the install fails with [x] GitHub Copilot desktop App not detected. Expected ~/.copilot/data.db ... and the command exits 1.

There is none. The DB file is local; access is governed by your filesystem permissions. APM never sends credentials or syncs the DB anywhere. Treat the DB as user-scope state.

APM guards writes with PRAGMA user_version and accepts the closed range [13, 13] today. If the App ships a newer schema, APM refuses to write and asks you to update APM rather than risk corruption.

The Copilot App owns the DB and keeps it in WAL mode while running. APM coexists with the App’s writer connection by issuing BEGIN IMMEDIATE with a bounded retry; if a lock cannot be acquired after the retry window, the install fails with a [!] warning noting that the Copilot App DB stayed locked and asking you to close the GitHub Copilot app momentarily and retry.

Deployed rows are tracked in the project / user lockfile under the copilot-app-db://workflows/<namespaced-id> URI scheme. Standard sync semantics apply: lockfile drift triggers redeploy; removal from lockfile triggers row delete on next install.

  • Package signing (would unlock additional trust-gated capabilities such as mode: autopilot).
  • Scheduled-execution-on-install (deliberately not implemented — first-run is always manual).
  • gh-aw outer-loop target (separate roadmap).