diff --git a/README.md b/README.md index 84f92cd..d7da7cd 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ![Nightshift logo](logo.png) -Your tokens get reset every week, you might as well use them. Nightshift runs overnight to find dead code, doc drift, test gaps, security issues, and 20+ other things silently accumulating while you ship features. Like a Roomba for your codebase — runs overnight, worst case you close the PR. +Your tokens get reset every week, you might as well use them. Nightshift runs overnight to find dead code, doc drift, test gaps, security issues, and 20+ other things silently accumulating while you ship features. Like a Roomba for your codebase - runs overnight, worst case you close the PR. Everything lands as a branch or PR. It never writes directly to your primary branch. Don't like something? Close it. That's the whole rollback plan. @@ -14,7 +14,7 @@ Everything lands as a branch or PR. It never writes directly to your primary bra - **Budget-aware**: Uses remaining daily allotment, never exceeds configurable max (default 75%) - **Multi-project**: Point it at your repos, it already knows what to look for -- **Zero risk**: Everything is a PR — merge what surprises you, close the rest +- **Zero risk**: Everything is a PR - merge what surprises you, close the rest - **Great DX**: Thoughtful CLI defaults with clear output and reports ## Installation @@ -43,7 +43,9 @@ After installing, run the guided setup: nightshift setup ``` -This walks you through provider configuration, project selection, budget calibration, and daemon setup. Once complete you can preview what nightshift will do: +This walks you through provider configuration, project selection, budget calibration, and daemon setup. It covers Claude, Codex, and Copilot, and checks for the provider CLIs on `PATH`. If you want a manual flow instead, use `nightshift init` for a project config, `nightshift init --global` for `~/.config/nightshift/config.yaml`, and `nightshift config validate` to verify the result. + +Once complete you can preview what nightshift will do: ```bash nightshift preview @@ -72,6 +74,14 @@ nightshift preview --write ./nightshift-prompts # Guided global setup nightshift setup +# Bootstrap or inspect config +nightshift init +nightshift init --global +nightshift config +nightshift config get budget.max_percent +nightshift config set budget.max_percent 15 +nightshift config validate + # Check environment and config health nightshift doctor @@ -95,6 +105,18 @@ nightshift task show lint-fix --prompt-only nightshift task run lint-fix --provider claude nightshift task run skill-groom --provider codex --dry-run nightshift task run lint-fix --provider codex --dry-run + +# Manage the scheduler and service lifecycle +nightshift daemon start +nightshift daemon start --foreground +nightshift daemon start --timeout 45m +nightshift daemon status +nightshift daemon stop +nightshift install +nightshift install launchd +nightshift install systemd +nightshift install cron +nightshift uninstall ``` If `gum` is available, preview output is shown through the gum pager. Use `--plain` to disable. @@ -115,6 +137,8 @@ daemon, CI) confirmation is auto-skipped. | `--max-tasks` | `1` | Max tasks per project (ignored when `--task` is set) | | `--random-task` | `false` | Pick a random task from eligible tasks instead of the highest-scored one | | `--ignore-budget` | `false` | Bypass budget checks (use with caution) | +| `--branch`, `-b` | _(current branch)_ | Base branch for new feature branches | +| `--timeout` | `30m` | Per-agent execution timeout | | `--yes`, `-y` | `false` | Skip the confirmation prompt | ```bash @@ -138,24 +162,30 @@ nightshift run --ignore-budget # Target a specific project and task directly nightshift run -p ./my-project -t lint-fix + +# Base new branches off develop +nightshift run --branch develop + +# Give agents more time +nightshift run --timeout 45m ``` Other useful flags: - `nightshift status --today` to see today's activity summary - `nightshift daemon start --foreground` for debug -- `--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 -- `--dry-run` — preview the prompt without executing -- `--timeout` — execution timeout (default 30m) +- `--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, codex, or copilot +- `--dry-run` - preview the prompt without executing +- `--timeout` - execution timeout (default 30m) -## Authentication (Subscriptions) +## Provider Setup Nightshift supports three AI providers: - **Claude Code** - Anthropic's Claude via local CLI -- **Codex** - OpenAI's GPT via local CLI -- **GitHub Copilot** - GitHub's Copilot via GitHub CLI +- **Codex** - OpenAI's Codex via local CLI +- **GitHub Copilot** - GitHub's Copilot via the standalone `copilot` binary or `gh copilot` ### Claude Code @@ -177,13 +207,17 @@ Supports signing in with ChatGPT or an API key. ### GitHub Copilot ```bash -# Install Copilot CLI +# Standalone binary npm install -g @github/copilot # or curl -fsSL https://gh.io/copilot-install | bash +# GitHub CLI extension +gh extension install github/gh-copilot ``` -Requires GitHub Copilot subscription. See [docs/COPILOT_INTEGRATION.md](docs/COPILOT_INTEGRATION.md) for details. +Nightshift prefers the standalone `copilot` binary when it exists, otherwise it falls back to `gh copilot`. `nightshift setup` checks for both and uses whichever is on `PATH`. + +If you use `gh copilot`, authenticate with `gh auth login` first. If you prefer API-based usage, you can authenticate Claude and Codex CLIs with API keys instead. @@ -198,15 +232,17 @@ Nightshift uses YAML config files to define: - Task priorities - Schedule preferences -Run `nightshift setup` to create/update the global config at `~/.config/nightshift/config.yaml`. +Use `nightshift setup` for guided onboarding. Use `nightshift init` to create a project config in the current directory, or `nightshift init --global` to create `~/.config/nightshift/config.yaml`. `nightshift config` shows the merged configuration, `nightshift config get KEY` reads a value, `nightshift config set KEY VALUE` writes a value, and `nightshift config validate` checks both the global and project files. -See the [full configuration docs](https://nightshift.haplab.com/docs/configuration) or [SPEC.md](docs/SPEC.md) for detailed options. +See the [full configuration docs](https://nightshift.haplab.com/docs/configuration) for detailed options. Minimal example: ```yaml schedule: cron: "0 2 * * *" + max_projects: 1 + max_tasks: 1 budget: mode: daily @@ -220,6 +256,7 @@ providers: preference: - claude - codex + - copilot claude: enabled: true data_path: "~/.claude" @@ -228,12 +265,18 @@ 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 - path: ~/code/td ``` +Default provider order is `claude -> codex -> copilot`. Nightshift uses whichever provider has budget remaining, unless you override the provider or disable one in config. + Task selection: ```yaml @@ -258,6 +301,38 @@ Each task has a default cooldown interval to prevent the same task from running ## Development +Nightshift is a Go module. Clone the repo and pull dependencies: + +```bash +git clone https://github.com/marcus/nightshift.git +cd nightshift +make deps +``` + +### Building and testing + +Common targets in the `Makefile` (run `make help` for the full list): + +```bash +make build # build ./cmd/nightshift into ./nightshift +make test # go test ./... +make test-race # go test -race ./... +make coverage # test with a coverage profile, printed per function +make lint # golangci-lint run (requires golangci-lint on PATH) +make check # test + lint +make clean # remove the binary and coverage artifacts +``` + +`make install` installs the binary into your Go bin directory. `make coverage-html` writes `coverage.html` from the coverage profile. + +To compare local Claude and Codex session token usage for budget calibration: + +```bash +make calibrate-providers +``` + +This runs `cmd/provider-calibration` against the current repo. See [Provider Calibration Guide](docs/guides/provider-calibration.md) for the flags and how to read the output. + ### Pre-commit hooks Install the git pre-commit hook to catch formatting and vet issues before pushing: @@ -267,9 +342,9 @@ make install-hooks ``` This symlinks `scripts/pre-commit.sh` into `.git/hooks/pre-commit`. The hook runs: -- **gofmt** — flags any staged `.go` files that need formatting -- **go vet** — catches common correctness issues -- **go build** — ensures the project compiles +- **gofmt** - flags any staged `.go` files that need formatting +- **go vet** - catches common correctness issues +- **go build** - ensures the project compiles To bypass in a pinch: `git commit --no-verify` diff --git a/cmd/provider-calibration/main.go b/cmd/provider-calibration/main.go index ee573b2..ddc1c7e 100644 --- a/cmd/provider-calibration/main.go +++ b/cmd/provider-calibration/main.go @@ -1,3 +1,15 @@ +// Package main is the CLI entry point for provider-calibration, a helper that +// compares locally recorded Claude and Codex session usage so token-cost +// assumptions can be re-checked. +// +// It reads Codex session files (--codex-sessions) and Claude project files +// (--claude-projects), optionally filtered by repo path (--repo), Codex +// originator (--codex-originator), and a minimum user-turn count +// (--min-user-turns). It reports per-session and per-user-turn token +// distributions for each provider, the cross-provider ratios, and a suggested +// multiplier, either as a human-readable report or as JSON (--json). +// +// See docs/guides/provider-calibration.md for the full workflow. package main import ( diff --git a/website/docs/budget.md b/website/docs/budget.md index e965cea..b1b97e4 100644 --- a/website/docs/budget.md +++ b/website/docs/budget.md @@ -3,11 +3,11 @@ sidebar_position: 6 title: Budget --- -# Budget Management +# Budget management -Nightshift is designed to use tokens you'd otherwise waste. It tracks your remaining budget and never exceeds your configured limits. +Nightshift calculates an allowance for each enabled provider before it selects work. The allowance is based on local usage data, the configured or calibrated weekly budget, and the limits in `budget`. -## Check Budget +Use the status command to inspect the resolved budget, usage, reserve, allowance, and any reset times captured in a snapshot: ```bash nightshift budget @@ -15,58 +15,82 @@ nightshift budget --provider claude nightshift budget --provider codex ``` -## Configuration Options +The provider must be enabled in [Configuration](/docs/configuration). If a provider has no remaining allowance, Nightshift skips it and tries the next provider in `providers.preference`. -| Option | Type | Default | Description | -|--------|------|---------|-------------| -| `budget.mode` | string | `daily` | `daily` or `weekly` usage model | -| `budget.max_percent` | int | `75` | Max % of budget per run | -| `budget.reserve_percent` | int | `5` | Always keep this % in reserve | -| `budget.billing_mode` | string | `subscription` | `subscription` or `api` | -| `budget.calibrate_enabled` | bool | `true` | Enable subscription calibration via snapshots | -| `budget.snapshot_interval` | duration | `30m` | Automatic snapshot cadence | -| `budget.snapshot_retention_days` | int | `90` | Snapshot retention window | -| `budget.week_start_day` | string | `monday` | Week boundary for calibration | -| `budget.db_path` | string | `~/.local/share/nightshift/nightshift.db` | Override DB path | +## Configuration -## Budget Modes +These settings belong under the top-level `budget` key. -### Daily Mode (recommended) +| Field | Default | Purpose | +| --- | --- | --- | +| `mode` | `daily` | Allowance model: `daily` or `weekly`. | +| `max_percent` | `75` | Percentage of the available budget that one Nightshift run may use. Valid values are 1–100. | +| `reserve_percent` | `5` | Percentage held back after the allowance calculation. Valid values are 0–100. | +| `aggressive_end_of_week` | `false` | In weekly mode, doubles the allowance with one day remaining before reset. | +| `weekly_tokens` | `700000` | Default weekly token budget when there is no provider-specific value or usable calibration. | +| `per_provider` | unset | Weekly token overrides keyed by `claude`, `codex`, or `copilot`. | +| `billing_mode` | `subscription` | `subscription` enables optional calibration; `api` uses configured token limits. | +| `calibrate_enabled` | `true` | Enables subscription-budget inference from snapshots. It is disabled automatically for API billing. | +| `snapshot_interval` | `30m` | How often the daemon collects snapshots. This must be a positive Go duration, such as `30m` or `1h`. | +| `snapshot_retention_days` | `90` | Number of days to retain snapshots. Set `0` to keep them indefinitely. | +| `week_start_day` | `monday` | `monday` or `sunday`; determines how snapshots are grouped for calibration. | +| `db_path` | `~/.local/share/nightshift/nightshift.db` | SQLite database location. | -Each night uses up to `max_percent` of your daily budget (weekly / 7). Consistent, predictable usage. +For example: -### Weekly Mode +```yaml +budget: + mode: weekly + max_percent: 60 + reserve_percent: 10 + aggressive_end_of_week: true + billing_mode: subscription + calibrate_enabled: true + snapshot_interval: 30m + snapshot_retention_days: 90 + week_start_day: monday + weekly_tokens: 700000 + per_provider: + claude: 700000 + codex: 500000 +``` -Uses `max_percent` of *remaining* weekly budget. With `aggressive_end_of_week: true`, spends more near week's end to avoid waste. +## Allowance modes -## Calibration +`daily` is the default. Nightshift divides the resolved weekly budget by seven, calculates the unused part of that daily budget, applies `max_percent`, then subtracts the configured reserve and any predicted daytime usage. -Nightshift infers subscription budgets by correlating local token counts with provider usage percentages. +`weekly` spreads the unused weekly budget over the days remaining until the provider's reset, then applies `max_percent`, the reserve, and any predicted daytime usage. With `aggressive_end_of_week: true`, the weekly calculation uses a 1× multiplier with two days remaining and a 2× multiplier with one day remaining. -Formula: `inferred_budget = local_tokens / (scraped_pct / 100)` +The status output shows the values it used, including the budget source and confidence when calibration is available. `--ignore-budget` on `nightshift run` bypasses these checks; use it deliberately because it can select an otherwise exhausted provider. -Confidence levels: -- **low**: 1–2 samples or high variance -- **medium**: stable signal across several snapshots -- **high**: consistent signal across a week or more +## Subscription calibration and snapshots -```bash -nightshift budget calibrate +For subscription accounts, Nightshift can infer a provider's weekly budget from snapshots. A snapshot records local token totals and, when tmux scraping is available, the provider's reported usage percentage. The inference is: + +```text +weekly budget = local weekly tokens / (reported percentage / 100) ``` -Enable auto-calibration in config: +Snapshots with a reported percentage between 10% and 95% and nonzero local tokens are used for the current configured week. Nightshift uses the median after filtering outliers and falls back to the previous week or configured budget when it has no usable current samples. -```yaml -budget: - calibrate_enabled: true - snapshot_interval: 30m +Capture and inspect snapshots with the `nightshift budget` subcommands: + +```bash +nightshift budget snapshot +nightshift budget snapshot --provider claude +nightshift budget snapshot --local-only +nightshift budget history -n 10 +nightshift budget history --provider codex --n 20 +nightshift budget calibrate ``` -> Calibration uses tmux to scrape usage percentages. If tmux is unavailable, snapshots are local-only and budgets fall back to config values. +`snapshot` reads local usage data for enabled providers. For Claude and Codex, it can also use tmux to run the provider usage command and capture its percentage and reset information. `--local-only`, `calibrate_enabled: false`, and `billing_mode: api` disable that scraping. Copilot snapshots are local-only. -## API Billing +When the daemon is running, it takes a snapshot immediately and then at `snapshot_interval`. It also prunes old rows once every 24 hours. The status and history commands display captured session and weekly reset times when the provider supplied them; a missing reset line simply means no reset time was captured. -For API-billed accounts, set explicit token limits: +## API billing + +Use explicit limits for API accounts. API mode turns calibration off and uses `per_provider` where set, otherwise `weekly_tokens`. ```yaml budget: @@ -77,23 +101,6 @@ budget: codex: 500000 ``` -`weekly_tokens` and `per_provider` are authoritative for `billing_mode: api`. For subscription users, they act as a fallback until calibration has enough snapshots. - -## Budget History - -View past budget snapshots: - -```bash -nightshift budget history -n 10 -nightshift budget snapshot --local-only -``` - -## Morning Summary - -After each run, Nightshift generates a summary at `~/.local/share/nightshift/summaries/nightshift-YYYY-MM-DD.md` covering budget usage, tasks completed, and suggested next steps. - -## Safety +## Troubleshooting -- `max_percent` (default 75%) caps how much budget a single run can use -- `reserve_percent` (default 5%) always keeps some budget available for your daytime work -- If budget is exhausted, Nightshift skips remaining tasks gracefully +If a budget result has little confidence or falls back to configuration, run `nightshift budget snapshot --provider ` and inspect the output. It identifies missing local data, disabled calibration, unavailable tmux, and provider data-path problems. [Doctor](/docs/cli-reference) also checks recent snapshot health. diff --git a/website/docs/cli-reference.md b/website/docs/cli-reference.md index d5a2cd4..370b79b 100644 --- a/website/docs/cli-reference.md +++ b/website/docs/cli-reference.md @@ -5,12 +5,14 @@ title: CLI Reference # CLI Reference -## Core Commands +## Top-Level Commands | Command | Description | |---------|-------------| -| `nightshift setup` | Guided global configuration | -| `nightshift run` | Execute scheduled tasks | +| `nightshift setup` | Guided end-to-end onboarding | +| `nightshift init` | Create a global or project config file | +| `nightshift config` | Show, edit, or validate config files | +| `nightshift run` | Execute configured tasks now | | `nightshift preview` | Show upcoming runs | | `nightshift budget` | Check token budget status | | `nightshift task` | Browse and run tasks | @@ -18,75 +20,180 @@ title: CLI Reference | `nightshift status` | View run history | | `nightshift logs` | Stream or export logs | | `nightshift stats` | Token usage statistics | -| `nightshift daemon` | Background scheduler | +| `nightshift report` | Read run reports | +| `nightshift snapshot` | Capture usage snapshots | +| `nightshift busfactor` | Analyze ownership concentration | +| `nightshift daemon` | Manage the background daemon lifecycle | +| `nightshift install` | Install a launchd/systemd/cron service | +| `nightshift uninstall` | Remove the installed service | + +## Bootstrap and Config + +`nightshift setup` walks through provider setup, project selection, budget calibration, PATH setup, and daemon installation. + +```bash +nightshift setup +``` + +`nightshift init` creates `nightshift.yaml` in the current directory by default. Use `--global` to create `~/.config/nightshift/config.yaml`, and `--force` to overwrite an existing file without prompting. + +```bash +nightshift init +nightshift init --global +nightshift init --force +``` + +`nightshift config` shows the merged configuration from global and project files, plus environment overrides. `nightshift config set` writes to the project config when one exists, otherwise to the global config. Use `--global` to force the global file. + +```bash +nightshift config +nightshift config get budget.max_percent +nightshift config set budget.max_percent 15 +nightshift config set --global logging.level debug +nightshift config validate +``` + +| Subcommand | Description | +|------------|-------------| +| `config` | Show merged config and source paths | +| `config get KEY` | Read a nested value by key path | +| `config set KEY VALUE` | Update a value; use `--global` to force global config | +| `config validate` | Validate global, project, and merged config | + +`nightshift config set` accepts booleans, integers, floats, and strings. ## Run Options -`nightshift run` shows a preflight summary before executing, then prompts for confirmation in interactive terminals. +`nightshift run` shows a preflight summary, then prompts for confirmation in interactive terminals. Non-TTY contexts skip the prompt automatically. ```bash -nightshift run # Preflight + confirm + execute (1 project, 1 task) -nightshift run --yes # Skip confirmation -nightshift run --dry-run # Show preflight, don't execute -nightshift run --max-projects 3 # Process up to 3 projects -nightshift run --max-tasks 2 # Run up to 2 tasks per project -nightshift run --random-task # Pick a random eligible task -nightshift run --ignore-budget # Bypass budget limits (use with caution) -nightshift run --project ~/code/myapp # Target specific project (ignores --max-projects) -nightshift run --task lint-fix # Run specific task (ignores --max-tasks) +nightshift run # Preflight + confirm + execute +nightshift run --yes # Skip confirmation +nightshift run --dry-run # Show preflight summary and exit +nightshift run --project ~/code/myapp # Target a single project +nightshift run --task lint-fix # Run a specific task +nightshift run --max-projects 3 # Process up to 3 projects +nightshift run --max-tasks 2 # Run up to 2 tasks per project +nightshift run --random-task # Pick a random eligible task +nightshift run --ignore-budget # Bypass budget checks +nightshift run --branch develop # Base new feature branches on develop +nightshift run --timeout 45m # Increase per-agent timeout +nightshift run --no-color # Disable ANSI colors ``` | Flag | Default | Description | |------|---------|-------------| -| `--dry-run` | `false` | Show preflight summary and exit without executing | -| `--yes`, `-y` | `false` | Skip confirmation prompt | -| `--max-projects` | `1` | Max projects to process (ignored when `--project` is set) | -| `--max-tasks` | `1` | Max tasks per project (ignored when `--task` is set) | -| `--random-task` | `false` | Pick a random task from eligible tasks instead of the highest-scored one | +| `--dry-run` | `false` | Show the preflight summary and exit without executing | +| `--yes`, `-y` | `false` | Skip the confirmation prompt | +| `--project`, `-p` | _(all configured)_ | Target a single project directory | +| `--task`, `-t` | _(auto-select)_ | Run a specific task by name | +| `--max-projects` | `1` | Max projects to process when `--project` is not set | +| `--max-tasks` | `1` | Max tasks per project when `--task` is not set | +| `--random-task` | `false` | Pick one random eligible task instead of the highest-scored task | | `--ignore-budget` | `false` | Bypass budget checks with a warning | -| `--project`, `-p` | | Target a specific project directory | -| `--task`, `-t` | | Run a specific task by name | +| `--branch`, `-b` | _(current branch)_ | Base branch for new feature branches | +| `--timeout` | `30m` | Per-agent execution timeout | +| `--no-color` | `false` | Disable colored output | + +`--random-task` and `--task` are mutually exclusive. When `--max-projects` or `--max-tasks` is omitted, Nightshift falls back to the values in `schedule.max_projects` and `schedule.max_tasks`. + +## Daemon and Services + +`nightshift daemon` manages the scheduler loop. `daemon start` backgrounds the process by default, `--foreground` keeps it in the current terminal, and `--timeout` defaults to 30m. + +```bash +nightshift daemon start +nightshift daemon start --foreground +nightshift daemon start --timeout 45m +nightshift daemon status +nightshift daemon stop +``` -Non-interactive contexts (daemon, cron, piped output) skip the confirmation prompt automatically. +| Subcommand | Description | +|------------|-------------| +| `daemon start` | Start the scheduler in the background by default | +| `daemon start --foreground` | Run the scheduler in the current terminal | +| `daemon start --timeout 45m` | Set the per-agent execution timeout | +| `daemon status` | Show whether the daemon is running | +| `daemon stop` | Stop the running daemon | -## Preview Options +`nightshift install` installs the scheduler as a system service. If you do not pass an init system, Nightshift auto-detects one from the current platform. + +```bash +nightshift install +nightshift install launchd +nightshift install systemd +nightshift install cron +nightshift uninstall +``` + +`launchd` targets macOS, `systemd` targets Linux, and `cron` works everywhere. `nightshift uninstall` removes the matching service entry if one is installed. + +## Preview ```bash nightshift preview # Default view nightshift preview -n 3 # Next 3 runs -nightshift preview --long # Detailed view -nightshift preview --explain # With prompt previews -nightshift preview --plain # No pager +nightshift preview --long # Detailed prompts +nightshift preview --explain # Budget and cooldown explanations +nightshift preview --plain # Disable pager output nightshift preview --json # JSON output -nightshift preview --write ./dir # Write prompts to files +nightshift preview --write ./dir # Write prompts to files ``` -## Task Commands +## Budget + +Budget commands accept `--provider` values of `claude`, `codex`, or `copilot`. ```bash -nightshift task list # All tasks +nightshift budget +nightshift budget --provider claude +nightshift budget --provider copilot +nightshift budget snapshot --local-only +nightshift budget snapshot --provider codex +nightshift budget history -n 10 +nightshift budget calibrate +``` + +| Command | Notes | +|---------|-------| +| `budget` | Show current budget status | +| `budget snapshot` | Capture a usage snapshot for calibration | +| `budget history` | Show recent snapshots | +| `budget calibrate` | Show inferred calibration status | + +## Tasks + +Task commands also accept `--provider` values of `claude`, `codex`, or `copilot` when running tasks. + +```bash +nightshift task list nightshift task list --category pr nightshift task list --cost low --json nightshift task show lint-fix nightshift task show lint-fix --prompt-only nightshift task run lint-fix --provider claude -nightshift task run lint-fix --provider codex --dry-run +nightshift task run lint-fix --provider copilot --dry-run +nightshift task run lint-fix --provider codex --timeout 45m +nightshift task run lint-fix --provider claude -p ~/code/myapp --branch develop ``` -## Budget Commands +`nightshift task run` requires `--provider`. It accepts `--project`, `--dry-run`, `--timeout` (default 30m), and `--branch` for new feature branches. + +## Reports and Diagnostics ```bash -nightshift budget # Current status -nightshift budget --provider claude -nightshift budget snapshot --local-only -nightshift budget history -n 10 -nightshift budget calibrate +nightshift status --today +nightshift logs --follow +nightshift stats +nightshift report --period last-night +nightshift snapshot --provider claude +nightshift busfactor . +nightshift doctor ``` -## Global Flags +## Shared Flags -| Flag | Description | -|------|-------------| -| `--verbose` | Verbose output | -| `--provider` | Select provider (claude, codex) | -| `--timeout` | Execution timeout (default 30m) | +| Flag | Scope | Description | +|------|-------|-------------| +| `--verbose` | Root command | Verbose output | diff --git a/website/docs/configuration.md b/website/docs/configuration.md index 4a7ee3d..f85697e 100644 --- a/website/docs/configuration.md +++ b/website/docs/configuration.md @@ -5,18 +5,55 @@ title: Configuration # Configuration -Nightshift uses YAML config files. Run `nightshift setup` for an interactive setup, or edit directly. +Nightshift uses YAML config files. Use `nightshift setup` for guided bootstrap, `nightshift init` or `nightshift init --global` to create a config file, and `nightshift config` to inspect or edit the merged view. -## Config Location +## Bootstrap Workflow -- **Global:** `~/.config/nightshift/config.yaml` -- **Per-project:** `nightshift.yaml` or `.nightshift.yaml` in the repo root +```bash +nightshift setup +nightshift init +nightshift init --global +nightshift config +nightshift config get budget.max_percent +nightshift config set budget.max_percent 15 +nightshift config set --global logging.level debug +nightshift config validate +``` + +- `nightshift setup` walks through provider setup, projects, budget, schedule, PATH, and daemon installation. +- `nightshift init` creates `nightshift.yaml` in the current directory. +- `nightshift init --global` creates `~/.config/nightshift/config.yaml`. +- `nightshift config` shows the merged config plus the source paths. +- `nightshift config get` reads a nested value by key path. +- `nightshift config set` writes to the project config when one exists, otherwise to the global config. Use `--global` to force the global file. +- `nightshift config validate` checks the global file, project file, and merged config. + +`nightshift config set` accepts booleans, integers, floats, and strings. For example, `true`, `15`, `12.5`, and `debug` are all parsed correctly. + +## Config Sources + +Nightshift reads config in this order: + +1. Global config: `~/.config/nightshift/config.yaml` +2. Project config: `nightshift.yaml` in the current project directory +3. Environment overrides such as `NIGHTSHIFT_BUDGET_MAX_PERCENT` + +Project config values override global config values, and environment variables override both. `nightshift config` reflects that same merge order when it prints the current configuration. + +## Config Locations + +| Type | Location | +|------|----------| +| Global | `~/.config/nightshift/config.yaml` | +| Project | `nightshift.yaml` | ## Minimal Config ```yaml schedule: cron: "0 2 * * *" + max_projects: 1 + max_tasks: 1 budget: mode: daily @@ -30,6 +67,7 @@ providers: preference: - claude - codex + - copilot claude: enabled: true data_path: "~/.claude" @@ -38,6 +76,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 @@ -46,17 +88,33 @@ projects: ## Schedule -Use cron syntax or interval-based scheduling: +Use either cron or interval scheduling. Nightshift rejects configs that set both. ```yaml schedule: cron: "0 2 * * *" # Every night at 2am # interval: "8h" # Or run every 8 hours + window: + start: "22:00" + end: "06:00" + timezone: "America/Denver" + max_projects: 1 + max_tasks: 1 ``` +| Field | Default | Description | +|-------|---------|-------------| +| `cron` | - | Cron expression for scheduled runs | +| `interval` | - | Duration string for repeated runs | +| `window.start` | `22:00` | Start of the allowed execution window | +| `window.end` | `06:00` | End of the allowed execution window | +| `window.timezone` | local time | Time zone for the window | +| `max_projects` | `1` | Default max projects per run | +| `max_tasks` | `1` | Default max tasks per project | + ## Budget -Control how much of your token budget Nightshift uses: +Control how much of your token budget Nightshift uses. | Field | Default | Description | |-------|---------|-------------| @@ -64,11 +122,50 @@ Control how much of your token budget Nightshift uses: | `max_percent` | `75` | Max budget % to use per run | | `reserve_percent` | `5` | Always keep this % available | | `billing_mode` | `subscription` | `subscription` or `api` | -| `calibrate_enabled` | `true` | Auto-calibrate from local CLI data | +| `calibrate_enabled` | `true` | Enable subscription calibration via snapshots | +| `snapshot_interval` | `30m` | Automatic snapshot cadence | +| `snapshot_retention_days` | `90` | Snapshot retention window | +| `weekly_tokens` | `700000` | Fallback weekly budget | +| `per_provider` | - | Provider-specific weekly budgets | +| `week_start_day` | `monday` | Week boundary for calibration | +| `db_path` | `~/.local/share/nightshift/nightshift.db` | Override database path | +| `aggressive_end_of_week` | `false` | Spend more near the end of the week | + +If `billing_mode: api`, Nightshift uses the explicit token budgets in `weekly_tokens` and `per_provider` instead of calibration. + +## Providers + +Nightshift supports Claude Code, Codex, and GitHub Copilot. It uses the providers listed in `providers.preference` order. + +```yaml +providers: + preference: + - claude + - codex + - copilot + copilot: + enabled: true + data_path: "~/.copilot" +``` + +| Field | Default | Description | +|-------|---------|-------------| +| `providers.preference` | `["claude", "codex", "copilot"]` | Provider priority order | +| `providers.claude.enabled` | `true` | Enable Claude provider | +| `providers.claude.data_path` | `~/.claude` | Claude Code data directory | +| `providers.claude.dangerously_skip_permissions` | `false` | Skip Claude permission prompts | +| `providers.codex.enabled` | `true` | Enable Codex provider | +| `providers.codex.data_path` | `~/.codex` | Codex data directory | +| `providers.codex.dangerously_bypass_approvals_and_sandbox` | `false` | Bypass Codex approvals and sandboxing | +| `providers.copilot.enabled` | `true` | Enable Copilot provider | +| `providers.copilot.data_path` | `~/.copilot` | Copilot request-tracking directory | +| `providers.copilot.dangerously_skip_permissions` | `false` | Allow Copilot to run with broader tool access | + +Copilot tracks request counts, not token usage. Its budget view is an estimate based on monthly request limits. ## Task Selection -Enable/disable tasks and set priorities: +Enable and prioritize built-in tasks, disable specific tasks, or define custom tasks. ```yaml tasks: @@ -82,9 +179,40 @@ tasks: intervals: lint-fix: "24h" docs-backfill: "168h" + custom: + - type: pr-review + name: "PR Review Session" + description: | + Review open PRs and check for regressions. + Create follow-up tasks for anything that needs attention. + category: pr + cost_tier: high + risk_level: medium + interval: "72h" ``` -Each task has a default cooldown interval to prevent the same task from running too frequently on a project. +- `tasks.enabled` restricts the built-in tasks Nightshift may run. +- `tasks.disabled` explicitly blocks a task even if it is enabled elsewhere. +- `tasks.intervals` overrides cooldowns per task. +- `tasks.custom` defines user-authored tasks. `type`, `name`, and `description` are required. + +## Integrations + +```yaml +integrations: + claude_md: true + agents_md: true + task_sources: + - td: + enabled: true + teach_agent: true +``` + +| Field | Default | Description | +|-------|---------|-------------| +| `integrations.claude_md` | `true` | Read `CLAUDE.md` or `claude.md` for context | +| `integrations.agents_md` | `true` | Read `AGENTS.md` for context | +| `integrations.task_sources` | - | External task sources like `td` or GitHub issues | ## Multi-Project Setup @@ -93,25 +221,27 @@ projects: - path: ~/code/project1 priority: 1 # Higher priority = processed first tasks: - - lint - - docs + - lint-fix + - docs-backfill - path: ~/code/project2 priority: 2 - - # Or use glob patterns - pattern: ~/code/oss/* exclude: - ~/code/oss/archived ``` +Each project can point at a path or a glob pattern. Use `exclude` to skip directories that match the pattern. + ## Safe Defaults | Feature | Default | Override | |---------|---------|----------| -| Read-only first run | Yes | `--enable-writes` | -| Max budget per run | 75% | `budget.max_percent` | -| Auto-push to remote | No | Manual only | -| Reserve budget | 5% | `budget.reserve_percent` | +| Confirmation prompt in TTY | Yes | `--yes` | +| Confirmation prompt in non-TTY | Auto-skip | `--yes` or interactive terminal | +| Max projects per run | `1` | `--max-projects` or `schedule.max_projects` | +| Max tasks per project | `1` | `--max-tasks` or `schedule.max_tasks` | +| Max budget per run | `75%` | `budget.max_percent` | +| Reserve budget | `5%` | `budget.reserve_percent` | ## File Locations @@ -124,7 +254,3 @@ projects: | PID file | `~/.local/share/nightshift/nightshift.pid` | If `state/state.json` exists from older versions, Nightshift migrates it to the SQLite database and renames the file to `state.json.migrated`. - -## Providers - -Nightshift supports Claude Code and Codex as execution providers. It will use whichever has budget remaining, in the order specified by `preference`. diff --git a/website/docs/installation.md b/website/docs/installation.md index e2b37d2..2b41ca3 100644 --- a/website/docs/installation.md +++ b/website/docs/installation.md @@ -39,16 +39,52 @@ nightshift --version nightshift --help ``` -## Prerequisites +## Provider Prerequisites -- **Claude Code CLI** (`claude`) and/or **Codex CLI** (`codex`) installed -- Authenticated via subscription login or API keys: +Nightshift can use Claude Code, Codex, and GitHub Copilot. Install and authenticate the providers you want to use before running `nightshift setup` or `nightshift run`: + +### Claude Code ```bash -# Claude Code claude /login +``` + +### Codex -# Codex +```bash codex --login ``` + +### GitHub Copilot + +Install either the standalone `copilot` binary or GitHub CLI with the Copilot extension: + +```bash +# Standalone binary +npm install -g @github/copilot +# or +curl -fsSL https://gh.io/copilot-install | bash +# GitHub CLI extension +gh extension install github/gh-copilot +``` + +Nightshift prefers the standalone `copilot` binary when it is available and falls back to `gh copilot`. `nightshift setup` checks for both. + +If you use `gh copilot`, authenticate with `gh auth login` first. + +## Next Step + +Use the guided setup for the fastest path: + +```bash +nightshift setup +``` + +If you prefer to bootstrap manually, create a config first and validate it: + +```bash +nightshift init +nightshift init --global +nightshift config validate +``` diff --git a/website/docs/integrations.md b/website/docs/integrations.md index 96d5e3f..4cba437 100644 --- a/website/docs/integrations.md +++ b/website/docs/integrations.md @@ -7,7 +7,19 @@ title: Integrations Nightshift integrates with your existing development workflow. -## Claude Code +## AI Providers + +Nightshift supports three execution providers. It uses the first enabled provider in `providers.preference`, which defaults to `claude -> codex -> copilot`. + +```yaml +providers: + preference: + - claude + - codex + - copilot +``` + +### Claude Code Nightshift uses the Claude Code CLI to execute tasks. Authenticate via subscription or API key: @@ -16,7 +28,15 @@ claude /login ``` -## Codex +Nightshift looks for the `claude` binary on `PATH`. + +Relevant config keys: + +- `providers.claude.enabled` +- `providers.claude.data_path` +- `providers.claude.dangerously_skip_permissions` + +### Codex Nightshift supports OpenAI's Codex CLI as an alternative provider: @@ -24,13 +44,44 @@ Nightshift supports OpenAI's Codex CLI as an alternative provider: codex --login ``` +Nightshift looks for the `codex` binary on `PATH`. + +Relevant config keys: + +- `providers.codex.enabled` +- `providers.codex.data_path` +- `providers.codex.dangerously_bypass_approvals_and_sandbox` + +### GitHub Copilot + +Nightshift supports GitHub Copilot through either the standalone `copilot` binary or `gh copilot`. + +```bash +# Standalone binary +npm install -g @github/copilot +# or +curl -fsSL https://gh.io/copilot-install | bash +# GitHub CLI extension +gh extension install github/gh-copilot +``` + +Nightshift prefers the standalone `copilot` binary when it is available and falls back to `gh copilot`. Copilot usage is tracked by request count instead of token usage. Nightshift stores that tracking data under `providers.copilot.data_path` (default: `~/.copilot`). + +If you use `gh copilot`, authenticate with `gh auth login` first. + +Relevant config keys: + +- `providers.copilot.enabled` +- `providers.copilot.data_path` +- `providers.copilot.dangerously_skip_permissions` + ## GitHub All output is PR-based. Nightshift creates branches and pull requests for its findings. ## 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. +Nightshift can source tasks from [td](https://td.haplab.com) - task management for AI-assisted development. The td reader imports every task from `td list --format json`; there is no tag filter. ```yaml integrations: @@ -40,17 +91,20 @@ integrations: teach_agent: true # Include td usage + core workflow in prompts ``` +When `teach_agent` is enabled, Nightshift adds td workflow notes to the agent prompt. + ## CLAUDE.md / AGENTS.md Nightshift reads project-level instruction files to understand context when executing tasks. Place a `CLAUDE.md` or `AGENTS.md` in your repo root to give Nightshift project-specific guidance. Tasks mentioned in these files get a priority bonus (+2). ## GitHub Issues -Source tasks from GitHub issues labeled with `nightshift`: +Source tasks from GitHub issues by enabling the GitHub task source: ```yaml integrations: - github_issues: - enabled: true - label: "nightshift" + task_sources: + - github_issues: true ``` + +Nightshift reads open issues with the hard-coded `nightshift` label via `gh issue list --label nightshift --state open`. The label is not configurable in Nightshift's current schema. diff --git a/website/docs/scheduling.md b/website/docs/scheduling.md index b552a79..e94457b 100644 --- a/website/docs/scheduling.md +++ b/website/docs/scheduling.md @@ -5,52 +5,94 @@ title: Scheduling # Scheduling -Nightshift can run automatically on a schedule. +Configure exactly one schedule, then run the daemon to execute Nightshift automatically. A schedule uses either a five-field cron expression or a Go duration interval; setting both is invalid. -## Cron-Based +## Cron or interval + +Use cron when work should start at a calendar time. Nightshift accepts five fields: minute, hour, day of month, month, and day of week. + +```yaml +schedule: + cron: "0 2 * * *" # Every day at 2:00 AM +``` + +Use an interval when work should recur relative to the last scheduled run: ```yaml schedule: - cron: "0 2 * * *" # Every night at 2am + interval: "8h" ``` -## Daemon Mode +Intervals use Go duration syntax, for example `30m`, `1h`, or `24h`, and must be positive. The interval scheduler's first run is one interval after it starts; neither scheduling mode runs immediately on daemon startup. + +## Execution windows + +An optional window restricts jobs to a time range. The start is inclusive and the end is exclusive. Windows may cross midnight. + +```yaml +schedule: + cron: "0 * * * *" + window: + start: "22:00" + end: "06:00" + timezone: "America/Los_Angeles" +``` + +In this example, Nightshift runs hourly from 22:00 through 05:00 in the specified timezone. If `timezone` is omitted, the daemon's local timezone is used. `start` and `end` must be `HH:MM` values with hours from 0–23 and minutes from 0–59. + +For cron schedules, an occurrence outside the window is skipped; the next cron occurrence is evaluated normally. For interval schedules, the next interval that falls outside the window is moved to the next window start. + +## Per-run limits -Run as a persistent background process: +The `schedule` section also supplies defaults for manual `nightshift run` invocations when its matching flag was not explicitly passed: + +```yaml +schedule: + cron: "0 2 * * *" + max_projects: 3 + max_tasks: 2 +``` + +- `max_projects` limits eligible projects per `nightshift run`; `0` leaves the command's default of one project in effect. +- `max_tasks` limits selected tasks per project; `0` leaves the command's default of one task in effect. +- An explicit `nightshift run --max-projects` or `--max-tasks` flag overrides the corresponding configuration value. `--project` ignores the project limit and `--task` ignores the task limit. + +The current daemon loop does not read these two limits: it processes configured projects and selects up to five tasks for each eligible project. Use `nightshift run` when you need these particular limits enforced. + +## Daemon lifecycle + +The daemon requires either `schedule.cron` or `schedule.interval`. ```bash nightshift daemon start -nightshift daemon start --foreground # For debugging +nightshift daemon status nightshift daemon stop ``` -## System Service - -Install as a system service for automatic startup: +`nightshift daemon start` detaches into the background. Use `--foreground` to keep it attached for debugging, or `--timeout 45m` to set the per-agent execution timeout: ```bash -# macOS (launchd) -nightshift install launchd +nightshift daemon start --foreground --timeout 45m +``` -# Linux (systemd) -nightshift install systemd +The daemon writes its PID to `~/.local/share/nightshift/nightshift.pid`. `status` reports the process, configured schedule, and window. `stop` sends `SIGTERM` and waits up to ten seconds before force-stopping a process that has not exited. The daemon also handles `SIGINT` and `SIGTERM` for graceful scheduler shutdown. -# Universal (cron) -nightshift install cron -``` +## Preview and manual runs -## Manual Runs +Preview upcoming work without starting a daemon: + +```bash +nightshift preview +nightshift preview -n 3 +nightshift preview --explain +``` -Skip the scheduler and run immediately: +To run once without waiting for the schedule: ```bash -nightshift run # Preflight summary + confirm + execute -nightshift run --dry-run # Show preflight summary, don't execute -nightshift run --yes # Skip confirmation prompt -nightshift run --project ~/code/myproject -nightshift run --task lint-fix -nightshift run --max-projects 3 --max-tasks 2 # Process more projects/tasks -nightshift run --ignore-budget # Bypass budget limits +nightshift run --dry-run +nightshift run --yes +nightshift run --max-projects 3 --max-tasks 2 ``` -In interactive terminals, `nightshift run` shows a preflight summary and asks for confirmation before executing. Use `--yes` to skip the prompt (e.g., in scripts). Non-TTY contexts auto-skip confirmation. +In an interactive terminal, `nightshift run` shows a preflight summary and asks for confirmation. Non-interactive runs, including daemon runs, skip that prompt automatically. See [Configuration](/docs/configuration) for the complete YAML layout and [CLI Reference](/docs/cli-reference) for command options. diff --git a/website/docs/troubleshooting.md b/website/docs/troubleshooting.md index e998551..7614707 100644 --- a/website/docs/troubleshooting.md +++ b/website/docs/troubleshooting.md @@ -8,33 +8,41 @@ title: Troubleshooting ## Common Issues **"Something feels off"** -- Run `nightshift doctor` to check config, schedule, and provider health +- Run `nightshift doctor` to check config, schedule, provider, and budget health **"No config file found"** ```bash -nightshift init # Create project config -nightshift init --global # Create global config +nightshift setup # Guided bootstrap with provider and daemon checks +nightshift init # Create nightshift.yaml in the current directory +nightshift init --global # Create ~/.config/nightshift/config.yaml +nightshift config validate ``` +**"No schedule configured"** +- Set either `schedule.cron` or `schedule.interval` in config +- Use `nightshift setup` or `nightshift init` if you want the bootstrap flow +- Re-run `nightshift config validate` after editing the schedule + **"Insufficient budget"** - Check current budget: `nightshift budget` -- Increase `max_percent` in config -- Wait for budget reset (check reset time in output) +- Increase `budget.max_percent` in config +- Wait for budget reset (check the reset time in the output) **"Calibration confidence is low"** - Run `nightshift budget snapshot` a few times to collect samples -- Ensure tmux is installed so usage percentages are available +- Ensure `tmux` is installed so usage percentages are available - Keep snapshots running for at least a few days **"tmux not found"** -- Install tmux or set `budget.billing_mode: api` if you pay per token +- Install `tmux` or set `budget.billing_mode: api` if you pay per token **"Week boundary looks wrong"** - Set `budget.week_start_day` to `monday` or `sunday` **"Provider not available"** -- Ensure Claude/Codex CLI is installed and in PATH -- Check API key environment variables are set +- Ensure Claude Code, Codex, or Copilot is installed and in `PATH` +- For Copilot, install either `gh` or the standalone `copilot` binary, then run `gh auth login` if you use `gh copilot` +- Check API key or subscription login state for the provider you are using ## Debug Mode