From c976da5fbb170269c9829cfbdecfed964bcf0243 Mon Sep 17 00:00:00 2001 From: yimin12 Date: Mon, 11 May 2026 02:03:20 -0400 Subject: [PATCH 1/3] chore: add commit message normalizer Nightshift-Task: commit-normalize Nightshift-Ref: https://github.com/marcus/nightshift --- Makefile | 6 ++- README.md | 25 ++++++++-- internal/tasks/tasks.go | 2 +- scripts/commit-msg.sh | 101 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 6 deletions(-) create mode 100755 scripts/commit-msg.sh diff --git a/Makefile b/Makefile index 088be01..152244e 100644 --- a/Makefile +++ b/Makefile @@ -75,10 +75,12 @@ help: @echo " check - Run tests and lint" @echo " install - Build and install to Go bin directory" @echo " calibrate-providers - Compare local Claude/Codex session usage for calibration" - @echo " install-hooks - Install git pre-commit hook" + @echo " install-hooks - Install git hooks" @echo " help - Show this help" -# Install git pre-commit hook +# Install git hooks install-hooks: @ln -sf ../../scripts/pre-commit.sh .git/hooks/pre-commit + @ln -sf ../../scripts/commit-msg.sh .git/hooks/commit-msg @echo "✓ pre-commit hook installed (.git/hooks/pre-commit → scripts/pre-commit.sh)" + @echo "✓ commit-msg hook installed (.git/hooks/commit-msg → scripts/commit-msg.sh)" diff --git a/README.md b/README.md index 84f92cd..f9524a8 100644 --- a/README.md +++ b/README.md @@ -258,19 +258,38 @@ Each task has a default cooldown interval to prevent the same task from running ## Development -### Pre-commit hooks +### Git hooks -Install the git pre-commit hook to catch formatting and vet issues before pushing: +Install the git hooks to catch formatting, vet, build, and commit message issues before pushing: ```bash make install-hooks ``` -This symlinks `scripts/pre-commit.sh` into `.git/hooks/pre-commit`. The hook runs: +This symlinks `scripts/pre-commit.sh` into `.git/hooks/pre-commit` and `scripts/commit-msg.sh` into `.git/hooks/commit-msg`. + +The pre-commit hook runs: - **gofmt** — flags any staged `.go` files that need formatting - **go vet** — catches common correctness issues - **go build** — ensures the project compiles +The commit message hook accepts: + +```text +type: summary +type(scope): summary +``` + +Accepted types are `feat`, `fix`, `docs`, `test`, `refactor`, `chore`, `build`, `ci`, `perf`, `style`, and `revert`. The hook normalizes safe cases such as `FIX: thing`, `feat - thing`, and extra whitespace, while preserving the body and trailers. Merge, revert, `fixup!`, and `squash!` commits are allowed. + +Valid examples: + +```text +feat: add project setup command +fix(config): preserve default provider +docs: document hook installation +``` + To bypass in a pinch: `git commit --no-verify` ## Uninstalling diff --git a/internal/tasks/tasks.go b/internal/tasks/tasks.go index 2c7dabb..393ec59 100644 --- a/internal/tasks/tasks.go +++ b/internal/tasks/tasks.go @@ -332,7 +332,7 @@ Apply safe updates directly, and leave concise follow-ups for anything uncertain Type: TaskCommitNormalize, Category: CategoryPR, Name: "Commit Message Normalizer", - Description: "Standardize commit message format", + Description: "Enforce repository commit message conventions via a git hook", CostTier: CostLow, RiskLevel: RiskLow, DefaultInterval: 24 * time.Hour, diff --git a/scripts/commit-msg.sh b/scripts/commit-msg.sh new file mode 100755 index 0000000..c234d07 --- /dev/null +++ b/scripts/commit-msg.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash +# commit-msg hook for nightshift +# Install: make install-hooks (or: ln -sf ../../scripts/commit-msg.sh .git/hooks/commit-msg) +set -euo pipefail + +MSG_FILE=${1:-} +if [[ -z "$MSG_FILE" || ! -f "$MSG_FILE" ]]; then + echo "commit-msg: missing commit message file" >&2 + exit 1 +fi + +TYPES="feat|fix|docs|test|refactor|chore|build|ci|perf|style|revert" +SUBJECT_MAX=72 + +trim() { + local value=$1 + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + printf '%s' "$value" +} + +subject_line_number=$( + awk ' + /^[[:space:]]*#/ { next } + /^[[:space:]]*$/ { next } + { print NR; exit } + ' "$MSG_FILE" +) + +if [[ -z "$subject_line_number" ]]; then + echo "commit-msg: empty commit message" >&2 + exit 1 +fi + +subject=$( + awk -v line="$subject_line_number" 'NR == line { print; exit }' "$MSG_FILE" +) +subject=$(trim "$subject") + +allow_special=false +case "$subject" in + Merge\ *|Revert\ *|fixup!\ *|squash!\ *) + allow_special=true + ;; +esac + +normalized=$subject +if [[ "$allow_special" == false ]]; then + if [[ "$normalized" =~ ^([A-Za-z]+)(\([A-Za-z0-9._/-]+\))?[[:space:]]*[-:][[:space:]]*(.+)$ ]]; then + type_part=$(printf '%s' "${BASH_REMATCH[1]}" | tr '[:upper:]' '[:lower:]') + scope_part=${BASH_REMATCH[2]:-} + summary_part=$(trim "${BASH_REMATCH[3]}") + normalized="${type_part}${scope_part}: ${summary_part}" + fi +fi + +if [[ "$normalized" != "$subject" ]]; then + tmp=$(mktemp) + awk -v line="$subject_line_number" -v replacement="$normalized" ' + NR == line { print replacement; next } + { print } + ' "$MSG_FILE" > "$tmp" + cat "$tmp" > "$MSG_FILE" + rm -f "$tmp" + subject=$normalized +fi + +if [[ "$allow_special" == true ]]; then + exit 0 +fi + +if ! [[ "$subject" =~ ^($TYPES)(\([A-Za-z0-9._/-]+\))?:[[:space:]][^[:space:]].*$ ]]; then + cat >&2 <<'EOF' +commit-msg: expected commit message format: + type: summary + type(scope): summary + +Accepted types: + feat, fix, docs, test, refactor, chore, build, ci, perf, style, revert + +Examples: + feat: add project setup command + fix(config): preserve default provider + docs: document hook installation + +Merge, Revert, fixup!, and squash! commits are allowed. +EOF + exit 1 +fi + +summary=${subject#*: } +if [[ -z "$(trim "$summary")" ]]; then + echo "commit-msg: summary must not be empty" >&2 + exit 1 +fi + +if (( ${#subject} > SUBJECT_MAX )); then + echo "commit-msg: subject must be ${SUBJECT_MAX} characters or fewer" >&2 + echo " ${subject}" >&2 + exit 1 +fi From 39932ae46af259acfb1cde3b6756ee75ee9f4062 Mon Sep 17 00:00:00 2001 From: yimin12 Date: Tue, 12 May 2026 02:04:47 -0400 Subject: [PATCH 2/3] chore: verify lint checks Nightshift-Task: lint-fix Nightshift-Ref: https://github.com/marcus/nightshift From 178beb20cf79d5a423c6de859ed56438413a8761 Mon Sep 17 00:00:00 2001 From: yimin12 Date: Wed, 13 May 2026 02:05:45 -0400 Subject: [PATCH 3/3] docs: backfill missing documentation Nightshift-Task: docs-backfill Nightshift-Ref: https://github.com/marcus/nightshift --- README.md | 7 +- docs/COPILOT_INTEGRATION.md | 144 +++++++++++++++++ docs/SPEC.md | 291 ++++++++++++++++++++++++++++++++++ website/docs/configuration.md | 9 +- website/docs/integrations.md | 4 + 5 files changed, 453 insertions(+), 2 deletions(-) create mode 100644 docs/COPILOT_INTEGRATION.md create mode 100644 docs/SPEC.md diff --git a/README.md b/README.md index f9524a8..49fae1e 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ Other useful flags: - `--category` — filter tasks by category (pr, analysis, options, safe, map, emergency) - `--cost` — filter by cost tier (low, medium, high, veryhigh) - `--prompt-only` — output just the raw prompt text for piping -- `--provider` — required for `task run`, choose claude or codex +- `--provider` — required for `task run`, choose claude, codex, or copilot - `--dry-run` — preview the prompt without executing - `--timeout` — execution timeout (default 30m) @@ -220,6 +220,7 @@ providers: preference: - claude - codex + - copilot claude: enabled: true data_path: "~/.claude" @@ -228,6 +229,10 @@ providers: enabled: true data_path: "~/.codex" dangerously_bypass_approvals_and_sandbox: true + copilot: + enabled: true + data_path: "~/.copilot" + dangerously_skip_permissions: false projects: - path: ~/code/sidecar diff --git a/docs/COPILOT_INTEGRATION.md b/docs/COPILOT_INTEGRATION.md new file mode 100644 index 0000000..0a007e3 --- /dev/null +++ b/docs/COPILOT_INTEGRATION.md @@ -0,0 +1,144 @@ +# GitHub Copilot Integration + +Nightshift can run tasks through GitHub Copilot CLI in the same provider pipeline as Claude Code and Codex. Copilot is selected when it is enabled, appears in `providers.preference`, its CLI is available, and the budget manager reports remaining allowance. + +## Installation + +Nightshift supports either the standalone Copilot CLI or the GitHub CLI Copilot extension. + +Standalone: + +```bash +npm install -g @github/copilot +``` + +GitHub CLI extension: + +```bash +gh extension install github/gh-copilot +``` + +If both `copilot` and `gh` are on `PATH`, Nightshift prefers the standalone `copilot` binary. Otherwise it falls back to `gh copilot`. + +## Authentication + +Copilot requires a GitHub account with an active Copilot subscription. Authenticate through GitHub CLI before using Nightshift: + +```bash +gh auth login +gh auth status +``` + +Then confirm Copilot is available: + +```bash +gh copilot -- --version +# or, for standalone installs: +copilot --version +``` + +## Configuration + +Copilot is enabled by default in Nightshift's config defaults, and the default provider preference order is `claude`, `codex`, then `copilot`. Add it explicitly when you want Copilot in your configured order: + +```yaml +providers: + preference: + - claude + - codex + - copilot + copilot: + enabled: true + data_path: "~/.copilot" + dangerously_skip_permissions: false +``` + +`providers.copilot.data_path` stores Nightshift's local request counter at `nightshift-usage.json`. The file is separate from GitHub's own account usage data. + +## Execution Model + +Nightshift runs Copilot non-interactively with a prompt and disables user questions: + +```bash +copilot -p "" --no-ask-user --silent +``` + +When using GitHub CLI passthrough, the equivalent command is: + +```bash +gh copilot -- -p "" --no-ask-user --silent +``` + +If `providers.copilot.dangerously_skip_permissions` is true, Nightshift also passes: + +```bash +--allow-all-tools --allow-all-urls +``` + +Leave this false for interactive or exploratory use. Set it only for unattended cron, daemon, or CI runs where Copilot must not stop for tool or URL prompts. + +## Running a Task + +Run Copilot directly for a single task: + +```bash +nightshift task run docs-backfill --provider copilot +nightshift task run lint-fix --provider copilot --dry-run +``` + +For scheduled or immediate runs, include Copilot in `providers.preference` and run: + +```bash +nightshift run +nightshift run --yes +``` + +Nightshift chooses the first enabled provider in preference order that has a CLI available and enough remaining budget. + +## Budget Tracking + +GitHub Copilot does not expose authoritative local usage percentages to Nightshift. Nightshift tracks Copilot conservatively by counting successful requests it sends through the Copilot provider: + +- Each Nightshift Copilot execution counts as one premium request. +- The counter resets monthly on the first day of the month at 00:00:00 UTC. +- The counter only includes Copilot usage made through Nightshift. +- Token costs are reported as zero because Copilot uses request limits rather than per-token pricing. + +Nightshift's budget manager still uses `budget.weekly_tokens` or `budget.per_provider.copilot` as the configured allowance input. Internally it approximates a monthly Copilot request limit as four times the configured weekly provider budget, then applies the same daily or weekly budget mode, max percent, reserve percent, and daytime usage reserve logic as other providers. + +Example request-limit-oriented configuration: + +```yaml +budget: + mode: weekly + max_percent: 75 + reserve_percent: 5 + per_provider: + copilot: 75 +``` + +With Copilot, treat this value as a request allowance rather than a token allowance. + +## Limitations + +- GitHub does not provide Nightshift with a local authoritative remaining-quota API. +- Usage outside Nightshift is not counted in `~/.copilot/nightshift-usage.json`. +- Daily and weekly Copilot percentages are estimates derived from monthly request tracking. +- The low-level provider adapter in `internal/providers/copilot.go` is used for budget tracking; task execution uses `internal/agents/copilot.go`. + +## Troubleshooting + +`copilot CLI not found in PATH`: Install either `copilot` or `gh`, then make sure the binary is visible to the shell that starts Nightshift. + +`copilot CLI not found in PATH (install via 'gh' or standalone)`: For `gh` mode, install the `github/gh-copilot` extension and verify `gh extension list` includes it. + +Copilot waits for permission: Set `providers.copilot.dangerously_skip_permissions: true` for unattended runs, or run interactively and approve the prompt. + +Budget shows unexpected request counts: Inspect or remove `~/.copilot/nightshift-usage.json`. Nightshift will recreate it for the current UTC month. + +Related docs: + +- [Technical specification](SPEC.md) +- [Run lifecycle](guides/run-lifecycle.md) +- [Codex budget tracking](guides/codex-budget-tracking.md) +- [Provider calibration](guides/provider-calibration.md) diff --git a/docs/SPEC.md b/docs/SPEC.md new file mode 100644 index 0000000..d903562 --- /dev/null +++ b/docs/SPEC.md @@ -0,0 +1,291 @@ +# Nightshift Technical Specification + +This document describes the implemented configuration and runtime model for Nightshift. It is a reference for local development and advanced configuration; the website docs remain the user-facing guide. + +## Architecture + +Nightshift is a Go CLI built around a scheduled run pipeline: + +1. Load global config from `~/.config/nightshift/config.yaml`. +2. Merge project config from `nightshift.yaml` in the target repo when a project path is active. +3. Apply `NIGHTSHIFT_` environment overrides. +4. Initialize logging, SQLite state, provider usage readers, budget calibration, and task selection. +5. Build a preflight plan: choose projects, choose an available provider, filter tasks by budget and cooldown, and print the summary. +6. Execute selected tasks through an agent CLI. +7. Record task/project state, run history, reports, and summaries. + +Provider responsibilities are split: + +- `internal/agents` runs CLI processes for Claude Code, Codex, and Copilot. +- `internal/providers` reads or estimates provider usage for budget decisions. +- `internal/budget` calculates per-run allowance. +- `internal/tasks` defines built-in and custom tasks, cost tiers, risk levels, intervals, and selection. +- `internal/orchestrator` coordinates prompt execution and result handling. + +## Config Loading + +Config precedence is: + +1. Defaults in `internal/config/config.go`. +2. Global config at `~/.config/nightshift/config.yaml`. +3. Project config at `nightshift.yaml`. +4. Environment variables with the `NIGHTSHIFT_` prefix. + +Project config overrides global config for the active project. Paths beginning with `~/` are expanded for provider paths, logs, and the database. + +Currently explicit environment bindings include: + +- `NIGHTSHIFT_BUDGET_MAX_PERCENT` +- `NIGHTSHIFT_BUDGET_MODE` +- `NIGHTSHIFT_LOG_LEVEL` +- `NIGHTSHIFT_LOG_PATH` + +## Config Schema + +```yaml +schedule: + cron: "0 2 * * *" + interval: "" + window: + start: "22:00" + end: "06:00" + timezone: "America/New_York" + max_projects: 1 + max_tasks: 1 + +budget: + mode: daily + max_percent: 75 + aggressive_end_of_week: false + reserve_percent: 5 + weekly_tokens: 700000 + per_provider: + claude: 700000 + codex: 700000 + copilot: 75 + billing_mode: subscription + calibrate_enabled: true + snapshot_interval: 30m + snapshot_retention_days: 90 + week_start_day: monday + db_path: "~/.local/share/nightshift/nightshift.db" + +providers: + preference: + - claude + - codex + - copilot + claude: + enabled: true + data_path: "~/.claude" + dangerously_skip_permissions: false + codex: + enabled: true + data_path: "~/.codex" + dangerously_bypass_approvals_and_sandbox: false + copilot: + enabled: true + data_path: "~/.copilot" + dangerously_skip_permissions: false + +projects: + - path: ~/code/project + priority: 1 + tasks: + - lint-fix + config: nightshift.yaml + pattern: "" + exclude: [] + +tasks: + enabled: + - lint-fix + - docs-backfill + disabled: [] + priorities: + lint-fix: 1 + intervals: + lint-fix: "24h" + custom: + - type: dependency-notes + name: Dependency Notes + description: Summarize risky dependencies. + category: analysis + cost_tier: medium + risk_level: low + interval: "168h" + +integrations: + claude_md: true + agents_md: true + task_sources: + - td: + enabled: true + teach_agent: true + - github_issues: true + - file: tasks.md + +logging: + level: info + path: "~/.local/share/nightshift/logs" + format: json + +reporting: + morning_summary: true + email: + slack_webhook: +``` + +Validation rules include: + +- `schedule.cron` and `schedule.interval` are mutually exclusive. +- `budget.mode` must be `daily` or `weekly`. +- `budget.billing_mode` must be `subscription` or `api`. +- `budget.week_start_day` must be `monday` or `sunday`. +- `budget.max_percent` and `budget.reserve_percent` must be between 0 and 100. +- `logging.level` must be `debug`, `info`, `warn`, or `error`. +- `logging.format` must be `json` or `text`. +- Task interval strings must parse as Go durations, for example `24h` or `168h`. +- Provider preference entries must be unique and one of `claude`, `codex`, or `copilot`. +- Custom task types must match `[a-z0-9][a-z0-9-]*`. + +When `budget.billing_mode: api` is set, calibration is disabled during config normalization. + +## Provider Model + +Nightshift supports three execution providers: + +| Provider | Agent command | Budget source | +| --- | --- | --- | +| Claude | `claude --print` | Claude local usage data, calibration, or configured fallback | +| Codex | `codex exec` | Codex local usage data, calibration, or configured fallback | +| Copilot | `copilot -p` or `gh copilot -- -p` | Nightshift request counter | + +Provider selection uses `providers.preference`, defaulting to `claude`, `codex`, `copilot`. A provider is eligible when: + +- It is enabled in config. +- Its CLI binary is on `PATH`. +- The budget manager returns a positive allowance, unless `--ignore-budget` is set. + +For Copilot, Nightshift prefers the standalone `copilot` binary and falls back to `gh` when the GitHub Copilot extension is installed. + +## Permission Flags + +Dangerous permission flags default to false in config defaults: + +- `providers.claude.dangerously_skip_permissions` +- `providers.codex.dangerously_bypass_approvals_and_sandbox` +- `providers.copilot.dangerously_skip_permissions` + +Codex's agent constructor still defaults to bypass mode for headless execution when no explicit option is passed. The command helper only passes the Codex bypass option when the config value is true, so existing headless fallback behavior is preserved. + +For Copilot, `dangerously_skip_permissions` adds `--allow-all-tools --allow-all-urls`. Without it, Copilot still runs with `--no-ask-user --silent`. + +## Budget Model + +The budget manager resolves a weekly provider budget from calibration or config. `budget.per_provider.` overrides `budget.weekly_tokens`; if neither produces a positive value, provider budget calculation fails. + +Daily mode: + +```text +daily_budget = weekly_budget / 7 +available_today = daily_budget * (1 - used_percent / 100) +allowance = available_today * max_percent / 100 +allowance = allowance - reserve_percent_of_daily_budget +allowance = allowance - predicted_daytime_usage +``` + +Weekly mode: + +```text +remaining_weekly = weekly_budget * (1 - used_percent / 100) +allowance = (remaining_weekly / days_until_reset) * max_percent / 100 +allowance = allowance * aggressive_end_of_week_multiplier +allowance = allowance - reserve_percent_of_remaining_weekly +allowance = allowance - predicted_daytime_usage +``` + +Copilot is request based. Nightshift's Copilot provider stores a monthly request count in `providers.copilot.data_path/nightshift-usage.json`, resets it on the first day of each UTC month, and estimates used percent from that count. In budget calculations, the configured weekly Copilot budget is approximated to a monthly request limit by multiplying by four. + +## Task Selection + +Task selection filters built-in and custom tasks in this order: + +1. Enabled and not disabled. +2. Estimated max token cost fits the selected provider allowance. +3. Not currently assigned. +4. Cooldown has elapsed. +5. Highest score wins, unless `--random-task` is set. + +Score is: + +```text +configured priority + staleness bonus + context mention bonus + task source bonus +``` + +Context mention bonus comes from `CLAUDE.md` and `AGENTS.md`. Task source bonus comes from configured sources such as td or GitHub issues. + +Cost tiers are: + +- `low`: 10k-50k tokens +- `medium`: 50k-150k tokens +- `high`: 150k-500k tokens +- `very-high`: 500k-1M tokens + +Default task intervals are 7 days for PR tasks, 3 days for analysis tasks, 14 days for safe execution tasks, and 30 days for emergency tasks, with individual task overrides in code and config. + +## Storage Locations + +| Data | Default location | +| --- | --- | +| Global config | `~/.config/nightshift/config.yaml` | +| Project config | `nightshift.yaml` | +| SQLite database | `~/.local/share/nightshift/nightshift.db` | +| Logs | `~/.local/share/nightshift/logs` | +| Run reports | `~/.local/share/nightshift/reports` | +| Summaries | `~/.local/share/nightshift/summaries` | +| Copilot request counter | `~/.copilot/nightshift-usage.json` | +| Claude data | `~/.claude` | +| Codex data | `~/.codex` | + +Older `state/state.json` data is migrated to SQLite by the database layer and then renamed to `state.json.migrated`. + +## Safety Model + +Nightshift's primary safety boundary is process and workflow control: + +- Runs begin with a preflight summary. +- Interactive runs ask for confirmation. +- Non-TTY runs auto-skip confirmation. +- `--dry-run` prints the plan and exits before execution. +- `--ignore-budget` is explicit and shown as a warning. +- Provider permission bypass flags are opt-in in config defaults. +- The orchestrator uses a bounded iteration count and a per-agent timeout. +- Task cooldowns reduce repeated churn on the same project. + +Nightshift does not make an agent safe by itself. The effective write and network permissions are those granted to the selected provider CLI and any dangerous flags you enable. + +## Operational Workflow + +Common commands: + +```bash +nightshift setup +nightshift doctor +nightshift preview --explain +nightshift run --dry-run +nightshift run --yes +nightshift task list +nightshift task show docs-backfill +nightshift task run docs-backfill --provider copilot --dry-run +nightshift budget +nightshift budget snapshot --local-only +nightshift budget calibrate +``` + +Related docs: + +- [GitHub Copilot integration](COPILOT_INTEGRATION.md) +- [Run lifecycle](guides/run-lifecycle.md) +- [Codex budget tracking](guides/codex-budget-tracking.md) +- [Provider calibration](guides/provider-calibration.md) diff --git a/website/docs/configuration.md b/website/docs/configuration.md index 4a7ee3d..bd1d2c1 100644 --- a/website/docs/configuration.md +++ b/website/docs/configuration.md @@ -30,6 +30,7 @@ providers: preference: - claude - codex + - copilot claude: enabled: true data_path: "~/.claude" @@ -38,6 +39,10 @@ providers: enabled: true data_path: "~/.codex" dangerously_bypass_approvals_and_sandbox: true + copilot: + enabled: true + data_path: "~/.copilot" + dangerously_skip_permissions: false projects: - path: ~/code/sidecar @@ -127,4 +132,6 @@ If `state/state.json` exists from older versions, Nightshift migrates it to the ## Providers -Nightshift supports Claude Code and Codex as execution providers. It will use whichever has budget remaining, in the order specified by `preference`. +Nightshift supports Claude Code, Codex, and GitHub Copilot as execution providers. It will use whichever has budget remaining, in the order specified by `preference`. + +For the full root-level configuration reference, see `docs/SPEC.md`. For Copilot-specific setup, see `docs/COPILOT_INTEGRATION.md`. diff --git a/website/docs/integrations.md b/website/docs/integrations.md index 96d5e3f..c5abe5b 100644 --- a/website/docs/integrations.md +++ b/website/docs/integrations.md @@ -28,6 +28,10 @@ codex --login All output is PR-based. Nightshift creates branches and pull requests for its findings. +## GitHub Copilot + +Nightshift can execute tasks through GitHub Copilot CLI via the standalone `copilot` binary or the `gh copilot` extension. See `docs/COPILOT_INTEGRATION.md` for installation, authentication, request tracking, and permission flag details. + ## td (Task Management) Nightshift can source tasks from [td](https://td.haplab.com) — task management for AI-assisted development. Tasks tagged with `nightshift` in td will be picked up automatically.