diff --git a/docs/COPILOT_INTEGRATION.md b/docs/COPILOT_INTEGRATION.md new file mode 100644 index 0000000..c24f7a5 --- /dev/null +++ b/docs/COPILOT_INTEGRATION.md @@ -0,0 +1,145 @@ +# GitHub Copilot Integration + +Nightshift can execute tasks with GitHub Copilot through the standalone +`copilot` command or through the GitHub CLI Copilot extension. + +## Requirements + +- A GitHub account with a Copilot subscription. +- Either the standalone `copilot` command or `gh` with the + `github/gh-copilot` extension. +- An authenticated CLI session for the command Nightshift will use. + +Install with the GitHub CLI: + +```bash +gh extension install github/gh-copilot +gh auth login +``` + +Or install the standalone command: + +```bash +npm install -g @github/copilot +# or +curl -fsSL https://gh.io/copilot-install | bash +``` + +For automatic provider selection, Nightshift prefers `copilot` when it is +available in `PATH`; otherwise it falls back to `gh copilot`. + +## Configuration + +Copilot uses the same provider configuration shape as Claude Code and Codex: + +```yaml +providers: + preference: + - claude + - codex + - copilot + copilot: + enabled: true + data_path: "~/.copilot" + dangerously_skip_permissions: false +``` + +`providers.preference` controls provider selection order. Copilot is enabled by +default, but it is only selected when a usable CLI is present and budget checks +allow it. + +`providers.copilot.data_path` is used for Nightshift's local Copilot usage +tracking file. The default is `~/.copilot`. + +## Execution Model + +Nightshift invokes Copilot in non-interactive prompt mode: + +```bash +copilot -p "" --no-ask-user --silent +``` + +When using `gh`, Nightshift passes the same prompt flags through the extension: + +```bash +gh copilot -- -p "" --no-ask-user --silent +``` + +`--no-ask-user` prevents scheduled runs from blocking on Copilot's ask-user +tool. `--silent` keeps output focused on the agent response. + +If `providers.copilot.dangerously_skip_permissions` is `true`, Nightshift also +passes: + +```bash +--allow-all-tools --allow-all-urls +``` + +Only enable that flag for trusted repositories and trusted execution +environments. It allows Copilot to use tools and URLs without interactive +approval. + +## Budget Tracking + +GitHub Copilot does not expose authoritative usage data to Nightshift through a +local file or public API. Nightshift therefore has only local, request-count +based support for Copilot budget checks: + +- the Copilot provider can read `nightshift-usage.json` under + `providers.copilot.data_path`; +- the local counter resets on the first day of each month at 00:00 UTC; +- each request in that local counter is treated as one premium request; +- daily mode estimates daily usage from the monthly counter divided by elapsed + days in the current month; +- weekly mode compares the monthly counter directly with the supplied monthly + request limit. + +Important limitation: current Copilot task execution does not automatically +increment this counter. The `IncrementRequestCount` helper exists on the +provider, but `nightshift run` and `nightshift task run` execute Copilot through +the agent wrapper and do not call that helper after successful runs. External +Copilot usage is also invisible to Nightshift. + +As a result, Copilot budget status is an estimate based on whatever local +counter data already exists. It should not be treated as an authoritative +GitHub Copilot quota reading. + +The run budget manager uses `budget.per_provider.copilot`, or +`budget.weekly_tokens` if no per-provider value is set, as a configured +allowance input. Because the shared budget model is token-oriented, Copilot +handling is approximate. Use a conservative Copilot value if you rely on request +limits: + +```yaml +budget: + per_provider: + copilot: 100 +``` + +## Troubleshooting + +Run: + +```bash +nightshift doctor +``` + +If Copilot is skipped during provider selection: + +- confirm `providers.copilot.enabled: true`; +- confirm `providers.preference` includes `copilot` if you customized it; +- run `which copilot` or `which gh`; +- when using `gh`, run `gh extension list` and confirm `github/gh-copilot` is + installed; +- run the Copilot CLI directly to confirm authentication. + +If a scheduled run blocks or fails on permissions, either keep Copilot later in +`providers.preference` and use another provider for unattended runs, or enable +`providers.copilot.dangerously_skip_permissions` only for trusted projects. + +## Related Docs + +- [Technical specification](SPEC.md) +- [Run lifecycle](guides/run-lifecycle.md) +- [Configuration docs](../website/docs/configuration.md) +- [Integrations docs](../website/docs/integrations.md) diff --git a/docs/SPEC.md b/docs/SPEC.md new file mode 100644 index 0000000..ebf11d6 --- /dev/null +++ b/docs/SPEC.md @@ -0,0 +1,277 @@ +# Nightshift Technical Specification + +This document describes Nightshift's implemented runtime configuration, +provider model, budget model, task selection, storage locations, and operating +workflow. + +## Architecture + +Nightshift is a Go CLI that selects repository maintenance tasks, chooses an +available AI provider, and runs a plan, implementation, and review workflow in a +target repository. + +Main components: + +- `cmd/nightshift/commands`: Cobra commands such as `run`, `task`, `budget`, + `doctor`, `setup`, and `daemon`. +- `internal/config`: YAML loading, defaults, environment overrides, validation, + and path expansion. +- `internal/providers`: provider-side usage and budget data for Claude, Codex, + and Copilot. +- `internal/agents`: command execution wrappers for Claude Code, Codex, and + GitHub Copilot. +- `internal/budget`: allowance calculation and reset-time reporting. +- `internal/tasks`: built-in task definitions, custom tasks, scoring, and + cooldowns. +- `internal/orchestrator`: task execution loop, run metadata, and PR-oriented + prompts. +- `internal/state` and `internal/db`: SQLite-backed run and task state. + +## Configuration Loading + +Nightshift loads configuration in this order: + +1. global config at `~/.config/nightshift/config.yaml`; +2. project config named `nightshift.yaml` in the selected project; +3. environment variables with the `NIGHTSHIFT_` prefix. + +Run `nightshift setup` for interactive configuration. Project config is loaded +after the global config, so project-level values can override global values for +that repository. + +## Configuration Schema + +Representative schema: + +```yaml +schedule: + cron: "0 2 * * *" + interval: "8h" + 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: 100 + 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 + - pattern: ~/code/oss/* + exclude: + - ~/code/oss/archived + +tasks: + enabled: + - lint-fix + - docs-backfill + disabled: [] + priorities: + lint-fix: 1 + intervals: + lint-fix: "24h" + custom: + - type: dependency-review + name: "Dependency Review" + description: "Review dependency health and open a PR for safe updates." + category: safe + cost_tier: medium + risk_level: low + interval: "168h" + +integrations: + claude_md: true + agents_md: true + task_sources: + - td: + enabled: true + teach_agent: true + +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` must be between 0 and 100; +- `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`; +- provider preferences must be unique and limited to `claude`, `codex`, and + `copilot`; +- custom task types, categories, cost tiers, risk levels, and intervals are + validated before use. + +When `budget.billing_mode` is `api`, calibration is disabled during config +normalization. + +## Provider Model + +Nightshift separates providers from agents: + +- providers expose usage and budget data; +- agents execute prompts through local CLIs. + +Supported execution providers: + +- `claude`: Claude Code CLI, data path default `~/.claude`; +- `codex`: Codex CLI, data path default `~/.codex`; +- `copilot`: GitHub Copilot CLI, data path default `~/.copilot`. + +Automatic provider selection follows `providers.preference`. It skips disabled +providers, providers whose CLI is unavailable, and providers with no remaining +allowance unless `--ignore-budget` is set. + +For Copilot, selection prefers the standalone `copilot` command when present and +falls back to `gh` with the Copilot extension. + +## Budget Model + +Nightshift calculates an allowance before selecting tasks: + +- daily mode uses `weekly_budget / 7` as the starting daily budget; +- weekly mode uses remaining weekly budget divided by days until reset; +- `max_percent` limits how much of the available budget a run may use; +- `reserve_percent` is subtracted from the calculated allowance; +- predicted daytime usage may be reserved when trend data is available. + +Claude and Codex are token-based providers. Codex can use rate-limit data from +session JSONL files, with token totals as a fallback. Subscription calibration +uses snapshots to infer a weekly token budget and falls back to +`budget.weekly_tokens` or `budget.per_provider`. + +Copilot is modeled as local monthly request usage because GitHub does not expose +authoritative Copilot usage data to Nightshift. The provider can read and write +`nightshift-usage.json` under the Copilot data path, but current task execution +paths do not call the increment helper after Copilot runs. Copilot budget +enforcement is therefore approximate and only reflects existing local counter +data, not total GitHub-side Copilot usage. + +## Task Selection + +Tasks are selected per project after provider allowance is known. + +Selection inputs: + +- task definitions and estimated token ranges; +- `tasks.enabled` and `tasks.disabled`; +- per-task priorities from `tasks.priorities`; +- per-project task overrides; +- cooldowns from task defaults or `tasks.intervals`; +- project state recorded in SQLite; +- optional `--task`, `--random-task`, `--max-projects`, and `--max-tasks`. + +Custom tasks require `type`, `name`, and `description`. Optional metadata +includes `category`, `cost_tier`, `risk_level`, and `interval`. + +## Runtime Workflow + +`nightshift run` performs these steps: + +1. load config and initialize logging; +2. open the SQLite database; +3. clear stale task assignments; +4. initialize providers, calibration, trends, and budget manager; +5. resolve projects; +6. select a base branch; +7. register custom tasks; +8. build and display a preflight plan; +9. confirm execution unless skipped by `--yes`, `--dry-run`, or a non-TTY + environment; +10. run each selected task through the orchestrator; +11. record state and write run reports. + +`nightshift task run --provider ` bypasses automatic provider +selection and runs one task in the selected project with the requested provider. + +## Storage Locations + +Default locations: + +| Data | Location | +| --- | --- | +| Global config | `~/.config/nightshift/config.yaml` | +| Project config | `nightshift.yaml` | +| SQLite database | `~/.local/share/nightshift/nightshift.db` | +| Logs | `~/.local/share/nightshift/logs` | +| Reports | `~/.local/share/nightshift/reports` | +| Summaries | `~/.local/share/nightshift/summaries` | +| Claude data | `~/.claude` | +| Codex data | `~/.codex` | +| Copilot data | `~/.copilot` | + +`budget.db_path`, `logging.path`, and provider `data_path` fields can override +the defaults. + +## Safety Model + +Nightshift is designed for unattended execution, but dangerous provider flags +remain explicit: + +- Claude only receives `--dangerously-skip-permissions` when + `providers.claude.dangerously_skip_permissions` is enabled. +- Codex only receives the bypass option when + `providers.codex.dangerously_bypass_approvals_and_sandbox` is enabled. +- Copilot only receives `--allow-all-tools --allow-all-urls` when + `providers.copilot.dangerously_skip_permissions` is enabled. + +Interactive `nightshift run` displays a preflight summary and asks for +confirmation. Non-TTY runs auto-confirm. Use `--dry-run` to inspect planned work +without executing it. + +## Related Docs + +- [Copilot integration](COPILOT_INTEGRATION.md) +- [Run lifecycle](guides/run-lifecycle.md) +- [Codex budget tracking](guides/codex-budget-tracking.md) +- [Configuration docs](../website/docs/configuration.md) +- [Budget docs](../website/docs/budget.md) +- [Tasks docs](../website/docs/tasks.md)