diff --git a/README.md b/README.md index 84f92cd..577028d 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,8 @@ 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 +scheduled-service setup. Once complete you can preview what nightshift will do: ```bash nightshift preview @@ -56,6 +57,21 @@ Or kick off a run immediately: nightshift run ``` +For a non-interactive configuration bootstrap: + +```bash +nightshift init --global # ~/.config/nightshift/config.yaml +nightshift init # ./nightshift.yaml +nightshift config validate +nightshift config get budget.max_percent +nightshift config set budget.max_percent 60 --global +``` + +Configuration precedence is built-in defaults, global config, one project `nightshift.yaml`, +then supported environment overrides. `run --project`, `preview --project`, and +`task run --project` load the project file from the selected directory; otherwise Nightshift uses the +current directory. Command flags then override the behavior of that invocation. + ## Common CLI Usage Full reference: [CLI Reference docs](https://nightshift.haplab.com/docs/cli-reference) @@ -72,6 +88,12 @@ nightshift preview --write ./nightshift-prompts # Guided global setup nightshift setup +# Create and validate config +nightshift init --global +nightshift init +nightshift config +nightshift config validate + # Check environment and config health nightshift doctor @@ -93,8 +115,17 @@ nightshift task show lint-fix --prompt-only # Run a task immediately 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 +nightshift task run skill-groom --provider codex --dry-run --branch main +nightshift task run docs-backfill --provider copilot --timeout 45m + +# Persistent scheduler lifecycle +nightshift daemon start +nightshift daemon status +nightshift daemon stop + +# Or install an OS-managed scheduled run +nightshift install +nightshift uninstall ``` If `gum` is available, preview output is shown through the gum pager. Use `--plain` to disable. @@ -109,13 +140,16 @@ daemon, CI) confirmation is auto-skipped. | Flag | Default | Description | |------|---------|-------------| | `--dry-run` | `false` | Show preflight summary and exit without executing | -| `--project`, `-p` | _(all configured)_ | Target a single project directory | +| `--project`, `-p` | configured paths, or current directory | Target one project and load its `nightshift.yaml` | | `--task`, `-t` | _(auto-select)_ | Run a specific task by name | | `--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 | | `--ignore-budget` | `false` | Bypass budget checks (use with caution) | | `--yes`, `-y` | `false` | Skip the confirmation prompt | +| `--branch`, `-b` | _(current branch)_ | Base branch for new task branches | +| `--timeout` | `30m` | Per-agent execution timeout | +| `--no-color` | `false` | Disable colored output (`NO_COLOR` also works) | ```bash # Interactive run with preflight summary + confirmation prompt @@ -138,15 +172,19 @@ nightshift run --ignore-budget # Target a specific project and task directly nightshift run -p ./my-project -t lint-fix + +# Select a base branch and timeout +nightshift run --branch develop --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 +- `--provider` — required for `task run`, choose claude, codex, or copilot - `--dry-run` — preview the prompt without executing - `--timeout` — execution timeout (default 30m) @@ -155,7 +193,7 @@ Other useful flags: 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 +- **GitHub Copilot** - GitHub's standalone Copilot CLI, with a retired `gh` extension fallback ### Claude Code @@ -169,21 +207,28 @@ Supports Claude.ai subscriptions or Anthropic Console credentials. ### Codex ```bash -codex --login +codex ``` -Supports signing in with ChatGPT or an API key. +Choose ChatGPT sign-in, or configure API-key authentication. ### GitHub Copilot ```bash -# Install Copilot CLI +# Preferred: standalone Copilot CLI npm install -g @github/copilot -# or -curl -fsSL https://gh.io/copilot-install | bash +copilot login + +# Compatibility only: retired gh copilot extension +gh auth login --web +gh extension install github/gh-copilot --force ``` -Requires GitHub Copilot subscription. See [docs/COPILOT_INTEGRATION.md](docs/COPILOT_INTEGRATION.md) for details. +Nightshift prefers the standalone `copilot` executable. It retains a `gh copilot` compatibility +path, but GitHub has retired that extension. Automatic `nightshift run` checks only that `gh` +exists before selecting this fallback; `nightshift task run` also checks the extension list. +Install standalone Copilot for reliable unattended runs. A Copilot plan and any required +organization policy are prerequisites. If you prefer API-based usage, you can authenticate Claude and Codex CLIs with API keys instead. @@ -198,9 +243,15 @@ 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`. +Run `nightshift setup` or `nightshift init --global` to create the global config at +`~/.config/nightshift/config.yaml`. A project config is named exactly `nightshift.yaml`. +`init` writes an opinionated starter rather than the built-in defaults: the current global +template prefers Claude and Codex and explicitly enables their unattended permission-bypass +flags. Review those values and replace the template's task names with slugs reported by +`nightshift task list`. -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: @@ -220,14 +271,19 @@ providers: preference: - claude - codex + - copilot claude: enabled: true data_path: "~/.claude" - dangerously_skip_permissions: true + dangerously_skip_permissions: false codex: enabled: true data_path: "~/.codex" - dangerously_bypass_approvals_and_sandbox: true + dangerously_bypass_approvals_and_sandbox: false + copilot: + enabled: true + data_path: "~/.copilot" + dangerously_skip_permissions: false projects: - path: ~/code/sidecar @@ -256,6 +312,12 @@ Each task has a default cooldown interval to prevent the same task from running `skill-groom` is enabled by default. Add it to `tasks.disabled` if you want to opt out. It updates project-local skills under `.claude/skills` and `.codex/skills` using `README.md` as project context and starts Agent Skills docs lookup from `https://agentskills.io/llms.txt`. +Provider executables are discovered through `PATH`; `providers.*.data_path` points to usage and +session data, not to the executable. Automatic `run` selection uses `providers.preference` and +skips disabled, missing, or budget-exhausted providers. It does not verify authentication before +execution. `preview` uses the first enabled provider in the fixed Claude, Codex, Copilot order +and does not check executable availability. + ## Development ### Pre-commit hooks diff --git a/website/docs/cli-reference.md b/website/docs/cli-reference.md index d5a2cd4..b397f04 100644 --- a/website/docs/cli-reference.md +++ b/website/docs/cli-reference.md @@ -5,88 +5,242 @@ title: CLI Reference # CLI Reference -## Core Commands +Run `nightshift --help` or `nightshift COMMAND --help` for the version installed on your machine. + +## Command overview + +| Command | Purpose | +|---------|---------| +| `setup` | Run the interactive global onboarding wizard | +| `init` | Create a global or project configuration file | +| `config` | Show, read, write, or validate configuration | +| `run` | Plan and execute configured tasks immediately | +| `preview` | Preview future runs without changing state | +| `task` | List, inspect, or execute one task | +| `budget` | Show budget status, snapshots, and calibration | +| `daemon` | Start, stop, or inspect the persistent scheduler | +| `install` | Install an OS-managed scheduled service | +| `uninstall` | Remove installed launchd, systemd, and cron entries | +| `status` | Show recent run history or today's activity | +| `report` | Render structured run reports | +| `logs` | Read, filter, follow, or export logs | +| `stats` | Show aggregate run and token statistics | +| `doctor` | Diagnose configuration, providers, scheduling, database, and budget | +| `busfactor` | Analyze code ownership concentration | +| `completion` | Generate shell completion scripts | + +## Configuration bootstrap -| Command | Description | -|---------|-------------| -| `nightshift setup` | Guided global configuration | -| `nightshift run` | Execute scheduled tasks | -| `nightshift preview` | Show upcoming runs | -| `nightshift budget` | Check token budget status | -| `nightshift task` | Browse and run tasks | -| `nightshift doctor` | Check environment health | -| `nightshift status` | View run history | -| `nightshift logs` | Stream or export logs | -| `nightshift stats` | Token usage statistics | -| `nightshift daemon` | Background scheduler | - -## Run Options +```bash +nightshift setup +nightshift init +nightshift init --global +nightshift init --global --force + +nightshift config +nightshift config get budget.max_percent +nightshift config set budget.max_percent 60 +nightshift config set providers.copilot.enabled true --global +nightshift config validate +``` -`nightshift run` shows a preflight summary before executing, then prompts for confirmation in interactive terminals. +`init` creates `nightshift.yaml` in the current directory. `init --global` creates +`~/.config/nightshift/config.yaml`. Without `--global`, `config set` writes to the current +project config when it exists and otherwise writes to the global config. `init --force` skips the +overwrite prompt. + +The generated global file is an opinionated starter, not a dump of built-in defaults. In the +current template it limits provider preference to Claude and Codex, enables their unattended +permission-bypass options, and includes legacy task aliases. Review the file after creation and +use `nightshift task list` for accepted task slugs. + +`config validate` validates each existing file and the merged configuration. It catches schema +rules such as mutually exclusive `schedule.cron` and `schedule.interval`, budget ranges, provider +preference names, task durations, and custom-task fields. It does not parse cron syntax, +`schedule.interval`, or execution-window values. Use `nightshift doctor`, `nightshift preview`, +or `nightshift daemon start --foreground` for scheduler parsing. + +## `nightshift run` + +`run` builds a preflight plan, showing the base branch, provider, budget, projects, and tasks. +It prompts before execution in an interactive terminal. Non-interactive invocations, including +cron and system services, skip the prompt automatically. + +| Flag | Default | Behavior | +|------|---------|----------| +| `--dry-run` | `false` | Show the preflight plan and exit without execution | +| `--project`, `-p` | configured paths or current directory | Target one existing directory, load its `nightshift.yaml`, and ignore `--max-projects` | +| `--task`, `-t` | automatic selection | Run one built-in task; ignores `--max-tasks`, task enablement, cooldown, estimated-cost filtering, and the processed-today skip | +| `--max-projects` | `1` | Maximum eligible projects; a positive `schedule.max_projects` supplies the default when the flag is omitted; explicit `0` is unlimited | +| `--max-tasks` | `1` | Maximum selected tasks per project; a positive `schedule.max_tasks` supplies the default when the flag is omitted | +| `--random-task` | `false` | Pick exactly one random eligible task; cannot be combined with `--task` | +| `--ignore-budget` | `false` | Permit selection even when a provider budget is exhausted | +| `--yes`, `-y` | `false` | Skip the interactive confirmation | +| `--branch`, `-b` | current branch of the first project | Set the base branch used for task branches | +| `--timeout` | `30m` | Set the per-agent execution timeout | +| `--no-color` | `false` | Disable colored output; `NO_COLOR` also works | ```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 --dry-run +nightshift run --yes --max-projects 3 --max-tasks 2 +nightshift run -p ~/code/myapp -t docs-backfill +nightshift run --random-task +nightshift run --branch develop --timeout 45m ``` -| 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 | -| `--ignore-budget` | `false` | Bypass budget checks with a warning | -| `--project`, `-p` | | Target a specific project directory | -| `--task`, `-t` | | Run a specific task by name | +## `nightshift preview` -Non-interactive contexts (daemon, cron, piped output) skip the confirmation prompt automatically. +Preview requires a valid schedule. It never executes tasks or records project/task runs, although +it opens the configured database and `--write` deliberately creates prompt files. -## Preview Options +| Flag | Default | Behavior | +|------|---------|----------| +| `--runs`, `-n` | `3` | Number of upcoming runs | +| `--project`, `-p` | all | Limit output to a project path | +| `--task`, `-t` | all | Limit output to a task type | +| `--long` | `false` | Show full prompts instead of truncated previews | +| `--write DIR` | unset | Write full prompts to a directory | +| `--explain` | `false` | Include budget and task-filter reasons | +| `--plain` | `false` | Disable the optional `gum` pager | +| `--json` | `false` | Emit JSON, including full prompts | ```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 --json # JSON output -nightshift preview --write ./dir # Write prompts to files +nightshift preview --explain +nightshift preview -n 5 --task lint-fix +nightshift preview --json +nightshift preview --write ./nightshift-prompts ``` -## Task Commands +Preview chooses the first enabled provider in the fixed order Claude, Codex, Copilot. It does not +use `providers.preference` or test the provider executable. Its task plan contains one task per +project per previewed run. + +## `nightshift task` ```bash -nightshift task list # All tasks -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 list +nightshift task list --category analysis --cost medium --json +nightshift task show docs-backfill +nightshift task show docs-backfill --prompt-only +nightshift task show docs-backfill --project ~/code/myapp --json +nightshift task run docs-backfill --provider copilot --project ~/code/myapp --dry-run +nightshift task run docs-backfill --provider codex --branch main --timeout 45m ``` -## Budget Commands +`task list` accepts categories `pr`, `analysis`, `options`, `safe`, `map`, and `emergency`. +Its cost filter accepts `low`, `medium`, `high`, and `veryhigh`. `task run` requires one of +`claude`, `codex`, or `copilot`; its default project is the current directory and its default +timeout is `30m`. + +The `task` subcommands currently expose built-in tasks only; they do not register `tasks.custom` +from configuration. `task run` is a direct execution path: it does not consult provider +enablement, provider preference, budgets, task enablement, cooldowns, or run history. Even +`--dry-run` checks that the requested provider executable is available (and, for the `gh` +Copilot fallback, that the extension is listed). + +## `nightshift budget` + +All budget commands accept `--provider`/`-p` with `claude`, `codex`, or `copilot`. ```bash -nightshift budget # Current status -nightshift budget --provider claude -nightshift budget snapshot --local-only +nightshift budget +nightshift budget --provider codex +nightshift budget snapshot +nightshift budget snapshot --provider claude --local-only nightshift budget history -n 10 -nightshift budget calibrate +nightshift budget calibrate --provider copilot +``` + +`budget history` defaults to 20 snapshots. `snapshot --local-only` records provider data without +starting a tmux scraping session. `calibrate` reports inferred budget status; it does not consume +an agent run. Claude and Codex support tmux percentage scraping; Copilot snapshots are local-only. + +## Daemon and installed services + +The daemon is a persistent scheduler: + +```bash +nightshift daemon start +nightshift daemon start --foreground --timeout 45m +nightshift daemon status +nightshift daemon stop +``` + +It loads global plus current-directory configuration once at startup and does not reload changed +files. At each scheduled time it walks every existing explicit `projects[].path`, skips projects +already processed that day, and selects up to five tasks per project. The daemon does not use +`schedule.max_projects` or `schedule.max_tasks`; those fields affect `nightshift run` only. + +An installed service is OS-managed and invokes `nightshift run` at scheduled times: + +```bash +nightshift install +nightshift install launchd +nightshift install systemd +nightshift install cron +nightshift uninstall +``` + +With no argument, `install` chooses launchd on macOS, systemd on Linux when `systemctl` is +available, and cron otherwise. `uninstall` looks for and removes all three Nightshift service +types. + +For predictable installed-service timing, configure a simple five-field cron schedule before +installation. Cron preserves that expression; launchd uses only its numeric minute and hour; +systemd converts only its minute, hour, day-of-month, and month fields. Installed services ignore +execution windows. Interval configuration is fully supported by `daemon start`, but the service +generators do not translate it consistently: launchd and cron fall back to 02:00, while systemd +performs only a simple minute-style conversion. See [Scheduling](/docs/scheduling) for lifecycle +details. + +## History, reports, logs, and diagnostics + +```bash +nightshift status --last 10 +nightshift status --today + +nightshift report +nightshift report --report tasks --period last-7d --format markdown +nightshift report --since 2026-07-01 --until 2026-07-08 --runs 0 --paths + +nightshift logs --tail 100 +nightshift logs --follow +nightshift logs --since "2026-07-25 22:00" --level warn --component daemon +nightshift logs --match "budget exhausted" --summary +nightshift logs --export ./nightshift.log --raw --no-color + +nightshift stats --period last-30d +nightshift stats --json +nightshift doctor ``` -## Global Flags +Report types are `overview`, `tasks`, `projects`, `budget`, and `raw`. Report formats are +`fancy`, `plain`, `markdown`, and `json`. `--period` accepts `last-night`, `last-run`, +`last-24h`, `last-7d`, `today`, `yesterday`, or `all`; `--since` and `--until` accept a date, +local date/time, or RFC3339 timestamp. + +`logs --level` accepts `debug`, `info`, `warn`, or `error`. Statistics periods are `all`, +`last-7d`, `last-30d`, and `last-night`. + +`doctor` parses the scheduler and checks enabled Claude/Codex executables, provider data paths, +database state, budget readiness, snapshots, tmux, daemon state, and the detected service files. +It does not test provider authentication, and it does not check whether a Copilot executable or +the retired `gh copilot` extension is installed. + +## Bus factor analysis + +```bash +nightshift busfactor . +nightshift busfactor --path ~/code/myapp --since 2026-01-01 --json +nightshift busfactor . --file "internal/*.go" --save +nightshift busfactor . --db ~/.local/share/nightshift/nightshift.db +``` + +The optional positional path and `--path`/`-p` select the repository. Date boundaries accept +`YYYY-MM-DD` or RFC3339. + +## Global flags -| Flag | Description | -|------|-------------| -| `--verbose` | Verbose output | -| `--provider` | Select provider (claude, codex) | -| `--timeout` | Execution timeout (default 30m) | +`--verbose` is the only Nightshift-wide behavior flag. Cobra also provides `--help`, and the root +command provides `--version`. Provider, timeout, JSON, and formatting flags belong only to the +commands that list them. diff --git a/website/docs/configuration.md b/website/docs/configuration.md index 4a7ee3d..223e246 100644 --- a/website/docs/configuration.md +++ b/website/docs/configuration.md @@ -5,126 +5,280 @@ title: Configuration # Configuration -Nightshift uses YAML config files. Run `nightshift setup` for an interactive setup, or edit directly. +Nightshift reads YAML configuration and applies built-in defaults when a field is omitted. -## Config Location +## Create and inspect configuration -- **Global:** `~/.config/nightshift/config.yaml` -- **Per-project:** `nightshift.yaml` or `.nightshift.yaml` in the repo root +Use the guided wizard for a complete global setup: -## Minimal Config +```bash +nightshift setup +``` + +For a generated file you can edit directly: + +```bash +nightshift init --global # ~/.config/nightshift/config.yaml +nightshift init # ./nightshift.yaml +nightshift config validate +``` + +`--force` overwrites an existing file without the confirmation prompt. You can inspect the +effective configuration or change scalar values without opening an editor: + +```bash +nightshift config +nightshift config get providers.preference +nightshift config set budget.max_percent 60 +nightshift config set logging.level debug --global +``` + +`config set` writes to `nightshift.yaml` when that file exists in the current directory; +otherwise it writes to the global file. It parses `true`/`false`, integers, and decimal numbers, +and treats other values as strings. + +The `init` templates are opinionated examples rather than serialized built-in defaults. In +particular, the current global template prefers only Claude and Codex, explicitly enables their +unattended permission-bypass flags, and contains legacy task aliases such as `lint` and `docs`. +Review the generated file and use `nightshift task list` for current task slugs. + +## Source precedence + +From lowest to highest precedence: + +1. Built-in defaults. +2. `~/.config/nightshift/config.yaml`. +3. One project `nightshift.yaml`: normally from the current directory, or from the directory + supplied to `run --project`, `preview --project`, or `task run --project`. +4. `NIGHTSHIFT_` environment overrides. +5. Command-line flags that override behavior for the command being run. + +The project filename is exactly `nightshift.yaml`; `.nightshift.yaml` is not loaded automatically. +The explicitly bound environment variables are: + +| Variable | Configuration key | +|----------|-------------------| +| `NIGHTSHIFT_BUDGET_MAX_PERCENT` | `budget.max_percent` | +| `NIGHTSHIFT_BUDGET_MODE` | `budget.mode` | +| `NIGHTSHIFT_LOG_LEVEL` | `logging.level` | +| `NIGHTSHIFT_LOG_PATH` | `logging.path` | + +## Complete example ```yaml schedule: cron: "0 2 * * *" + max_projects: 2 + max_tasks: 2 + window: + start: "22:00" + end: "06:00" + timezone: "America/Los_Angeles" budget: mode: daily max_percent: 75 + aggressive_end_of_week: false reserve_percent: 5 + weekly_tokens: 700000 + per_provider: + claude: 700000 + codex: 500000 + copilot: 500000 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 + preference: [claude, codex, copilot] claude: enabled: true - data_path: "~/.claude" - dangerously_skip_permissions: true + data_path: ~/.claude + dangerously_skip_permissions: false codex: enabled: true - data_path: "~/.codex" - dangerously_bypass_approvals_and_sandbox: true + data_path: ~/.codex + dangerously_bypass_approvals_and_sandbox: false + copilot: + enabled: true + data_path: ~/.copilot + dangerously_skip_permissions: false projects: - path: ~/code/sidecar + priority: 2 - path: ~/code/td + priority: 1 + +tasks: + enabled: [lint-fix, docs-backfill, bug-finder] + disabled: [td-review] + priorities: + bug-finder: 3 + docs-backfill: 2 + intervals: + lint-fix: 24h + docs-backfill: 168h + +integrations: + claude_md: true + agents_md: true + task_sources: + - td: + enabled: true + teach_agent: true + - github_issues: true + +logging: + level: info + path: ~/.local/share/nightshift/logs + format: json + +reporting: + morning_summary: true ``` ## Schedule -Use cron syntax or interval-based scheduling: +Choose exactly one scheduler: -```yaml -schedule: - cron: "0 2 * * *" # Every night at 2am - # interval: "8h" # Or run every 8 hours -``` +| Key | Default | Rules | +|-----|---------|-------| +| `schedule.cron` | unset | Five-field minute/hour/day/month/weekday expression | +| `schedule.interval` | unset | Positive Go duration such as `30m`, `8h`, or `24h` | +| `schedule.window.start` | unset | `HH:MM`, hour 00-23 and minute 00-59 | +| `schedule.window.end` | unset | `HH:MM`; the end is exclusive | +| `schedule.window.timezone` | local timezone | IANA name such as `America/Los_Angeles` | +| `schedule.max_projects` | `0` | Positive value supplies the default for `run --max-projects`; otherwise the CLI default is 1 | +| `schedule.max_tasks` | `0` | Positive value supplies the default for `run --max-tasks`; otherwise the CLI default is 1 | + +`cron` and `interval` are mutually exclusive. A missing schedule is valid for manual commands, +but `preview` and `daemon start` require one. Windows may stay within one day or cross midnight. + +`config validate` checks mutual exclusion, but it does not parse the cron expression, interval, +window times, or timezone. Scheduler construction in `doctor`, `preview`, and `daemon start` +performs those checks. A five-field cron expression and a positive Go duration are required at +that point. ## Budget -Control how much of your token budget Nightshift uses: +| Key | Default | Rules | +|-----|---------|-------| +| `budget.mode` | `daily` | `daily` or `weekly` | +| `budget.max_percent` | `75` | 0-100 | +| `budget.aggressive_end_of_week` | `false` | Increase weekly-mode spending in the final two days | +| `budget.reserve_percent` | `5` | 0-100 | +| `budget.weekly_tokens` | `700000` | Fallback weekly allowance | +| `budget.per_provider` | unset | Provider-specific weekly token overrides | +| `budget.billing_mode` | `subscription` | `subscription` or `api` | +| `budget.calibrate_enabled` | `true` | Enable subscription usage calibration | +| `budget.snapshot_interval` | `30m` | Daemon snapshot cadence | +| `budget.snapshot_retention_days` | `90` | Non-negative snapshot retention | +| `budget.week_start_day` | `monday` | `monday` or `sunday` | +| `budget.db_path` | `~/.local/share/nightshift/nightshift.db` | SQLite state and snapshot database | -| Field | Default | Description | -|-------|---------|-------------| -| `mode` | `daily` | `daily` or `weekly` | -| `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 | +API billing automatically disables calibration after configuration is loaded. See +[Budget Management](/docs/budget) for the operational workflow. -## Task Selection +## Providers -Enable/disable tasks and set priorities: +Claude, Codex, and Copilot are enabled by default. The default preference order is: ```yaml -tasks: - enabled: - - lint-fix - - docs-backfill - - bug-finder - priorities: - lint-fix: 1 - bug-finder: 2 - intervals: - lint-fix: "24h" - docs-backfill: "168h" +providers: + preference: [claude, codex, copilot] ``` -Each task has a default cooldown interval to prevent the same task from running too frequently on a project. +At run time, Nightshift walks that order and chooses the first enabled provider whose executable +is available and whose calculated allowance is positive. This selection checks executable +presence, not authentication. Preference names are case-insensitive when used and must be unique +values from `claude`, `codex`, and `copilot` during validation. + +`data_path` points to a provider's local usage/session data. It does not select an executable; +executables must be discoverable through `PATH`. The effective data-path defaults are +`~/.claude`, `~/.codex`, and `~/.copilot`. + +The permission-bypass fields opt autonomous runs into the corresponding provider flags. Their +configuration defaults are `false`. Codex execution itself preserves its headless bypass default +when the setting is not enabled, so disable the Codex provider if you do not want it selected for +autonomous execution. + +`preview` is an exception to provider preference: it uses the first enabled provider in the fixed +Claude, Codex, Copilot order and does not check executable availability. `task run` is another +exception: its required `--provider` is used even when that provider is disabled in configuration. -## Multi-Project Setup +## Projects + +Top-level commands currently resolve explicit `path` entries: ```yaml projects: - - path: ~/code/project1 - priority: 1 # Higher priority = processed first - tasks: - - lint - - docs - - path: ~/code/project2 + - path: ~/code/project-one priority: 2 + - path: ~/code/project-two + priority: 1 +``` + +The schema also accepts `priority`, `tasks`, `config`, `pattern`, and `exclude` fields: - # Or use glob patterns - - pattern: ~/code/oss/* +```yaml +projects: + - pattern: ~/code/open-source/* + priority: 1 exclude: - - ~/code/oss/archived + - ~/code/open-source/archived ``` -## Safe Defaults +The project resolver package implements pattern expansion, exclusions, priority ordering, and a +limited per-project merge, but `run`, `preview`, and the daemon do not call that resolver. Their +current path enumeration uses only existing `projects[].path` values and preserves configuration +order. List concrete paths for unattended execution; the other project fields are accepted by +the schema but do not affect those commands. + +Nightshift also does not automatically merge a `nightshift.yaml` found inside each configured +project. To use a project's file, invoke `run --project PATH`, `preview --project PATH`, or +`task run --project PATH`, or start Nightshift with that project as the current directory. -| 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` | +## Task customization -## File Locations +An empty `tasks.enabled` list means all built-in tasks are eligible. `tasks.disabled` always wins. +Higher priority numbers increase selection score. Interval values must be valid Go durations. -| Type | Location | -|------|----------| -| Run logs | `~/.local/share/nightshift/logs/nightshift-YYYY-MM-DD.log` | -| Audit logs | `~/.local/share/nightshift/audit/audit-YYYY-MM-DD.jsonl` | -| Summaries | `~/.local/share/nightshift/summaries/` | -| Database | `~/.local/share/nightshift/nightshift.db` | -| PID file | `~/.local/share/nightshift/nightshift.pid` | +```yaml +tasks: + custom: + - type: dependency-review + name: Dependency Review + description: | + Review direct dependencies for stale or risky versions. + Open a pull request for safe updates. + category: analysis + cost_tier: medium + risk_level: low + interval: 72h +``` -If `state/state.json` exists from older versions, Nightshift migrates it to the SQLite database and renames the file to `state.json.migrated`. +Custom `type` values use lowercase letters, numbers, and hyphens. `type`, `name`, and +`description` are required. Categories are `pr`, `analysis`, `options`, `safe`, `map`, or +`emergency`; cost tiers are `low`, `medium`, `high`, or `very-high`; risk levels are `low`, +`medium`, or `high`. -## Providers +`run` and `preview` register configured custom tasks. The `task list`, `task show`, and `task run` +commands currently expose only built-in task definitions. + +## Integrations, logging, and reporting + +`integrations.claude_md` and `integrations.agents_md` default to `true`. The schema also accepts +td, GitHub issue, and file task-source entries. Reader implementations exist for Claude/agent +instruction files, td, and GitHub issues, but no current CLI command invokes the integration +manager, so these settings do not yet add prompt context or tasks. See +[Integrations](/docs/integrations). -Nightshift supports Claude Code and Codex as execution providers. It will use whichever has budget remaining, in the order specified by `preference`. +Logging defaults to level `info`, JSON format, and +`~/.local/share/nightshift/logs`. Valid levels are `debug`, `info`, `warn`, and `error`; valid +formats are `json` and `text`. `reporting.morning_summary` defaults to `true`. Email and Slack +webhook fields exist in the configuration schema. Notification implementations also exist, but +the run finalizer currently saves the local summary without calling notification dispatch. diff --git a/website/docs/installation.md b/website/docs/installation.md index e2b37d2..046c3ed 100644 --- a/website/docs/installation.md +++ b/website/docs/installation.md @@ -5,50 +5,131 @@ title: Installation # Installation -## Homebrew (Recommended) +## Install Nightshift + +### Homebrew ```bash brew install marcus/tap/nightshift ``` -## Binary Downloads +### Binary release -Pre-built binaries are available on the [GitHub releases page](https://github.com/marcus/nightshift/releases) for macOS and Linux (Intel and ARM). +Download a macOS or Linux archive from the +[GitHub releases page](https://github.com/marcus/nightshift/releases), place the executable in a +directory on `PATH`, and verify it: -## From Source +```bash +nightshift --version +nightshift --help +``` -Requires Go 1.24+: +### From source + +Nightshift requires Go 1.24 or later: ```bash go install github.com/marcus/nightshift/cmd/nightshift@latest ``` -Or build from the repository: +The resulting binary is normally placed in `$GOBIN` or `$GOPATH/bin`. Ensure that directory is +on `PATH`. + +## Install at least one provider + +Nightshift executes work through a local provider CLI. Install and authenticate one or more of +Claude Code, Codex, or GitHub Copilot before running setup. + +### Claude Code + +Claude Code requires Node.js 18 or later for the npm installation: ```bash -git clone https://github.com/marcus/nightshift.git -cd nightshift -go build -o nightshift ./cmd/nightshift -sudo mv nightshift /usr/local/bin/ +npm install -g @anthropic-ai/claude-code +claude ``` -## Verify Installation +Complete the browser login after starting `claude`. Claude.ai plans, Anthropic Console accounts, +and supported enterprise providers can authenticate the CLI. See +[Anthropic's setup guide](https://docs.anthropic.com/en/docs/claude-code/getting-started). + +### Codex ```bash -nightshift --version -nightshift --help +# macOS or Linux installer +curl -fsSL https://chatgpt.com/codex/install.sh | sh + +# Alternatives +npm install -g @openai/codex +brew install --cask codex + +codex ``` -## Prerequisites +Choose ChatGPT sign-in on first launch, or configure API-key authentication. See the +[official Codex CLI README](https://github.com/openai/codex#readme). -- **Claude Code CLI** (`claude`) and/or **Codex CLI** (`codex`) installed -- Authenticated via subscription login or API keys: +### GitHub Copilot CLI + +The standalone `copilot` executable is Nightshift's preferred Copilot mode: ```bash -# Claude Code -claude -/login +npm install -g @github/copilot +copilot login +``` + +Homebrew and GitHub's install script are also supported: + +```bash +brew install copilot-cli +curl -fsSL https://gh.io/copilot-install | bash +``` + +An eligible Copilot plan and any required organization policy are prerequisites. Copilot can also +reuse an authenticated GitHub CLI token. See +[GitHub's Copilot CLI installation guide](https://docs.github.com/en/copilot/how-tos/copilot-cli/set-up-copilot-cli/install-copilot-cli). + +Nightshift also recognizes the legacy `gh copilot` extension when standalone `copilot` is not +installed: + +```bash +gh auth login --web +gh extension install github/gh-copilot --force +gh extension list +``` + +GitHub has retired that extension in favor of the standalone CLI, so prefer `copilot` for new +installations. The fallback remains documented only because Nightshift retains a compatibility +execution path. + +## Provider discovery + +Nightshift looks for `claude`, `codex`, and `copilot` on `PATH`. For Copilot it prefers +standalone `copilot`, then falls back to `gh`. `task run` verifies the retired extension through +`gh extension list`, but automatic `run` selection only verifies that `gh` exists; without the +extension, execution fails later. Provider `data_path` settings point to usage data and do not +override executable paths. + +When launched by a daemon or service, Nightshift also searches existing directories at: -# Codex -codex --login +- `~/.local/bin` +- `~/go/bin` +- `~/.cargo/bin` +- `~/.npm-global/bin` +- `/usr/local/bin` +- `/opt/homebrew/bin` +- `$GOPATH/bin`, when `GOPATH` is set + +## Finish setup + +```bash +nightshift setup +nightshift doctor +nightshift preview --explain ``` + +The wizard creates or updates the global config, checks provider availability, adds projects, +configures budget and safety options, captures a snapshot, previews a run, and can install a +scheduled service. Its environment screen treats any `gh` executable as Copilot availability and +does not test authentication, so verify standalone `copilot` and its login directly before +unattended use. diff --git a/website/docs/integrations.md b/website/docs/integrations.md index 96d5e3f..955a42a 100644 --- a/website/docs/integrations.md +++ b/website/docs/integrations.md @@ -5,52 +5,161 @@ title: Integrations # Integrations -Nightshift integrates with your existing development workflow. +## Execution providers -## Claude Code +Nightshift supports Claude Code, Codex, and GitHub Copilot. They are enabled by built-in defaults: -Nightshift uses the Claude Code CLI to execute tasks. Authenticate via subscription or API key: +```yaml +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 +``` + +Automatic `nightshift run` walks `providers.preference` and skips a provider when it is disabled, +its executable is unavailable, or its calculated allowance is exhausted. It does not test +authentication before selection. `run --ignore-budget` changes only the exhausted-budget +decision. + +The generated `nightshift init --global` starter differs from these built-in defaults: it +explicitly prefers Claude and Codex, omits Copilot from the preference list, and enables the +Claude/Codex unattended permission-bypass settings. + +### Claude Code + +Install and start the CLI once to authenticate: ```bash +npm install -g @anthropic-ai/claude-code claude -/login ``` -## Codex +Nightshift invokes `claude --print`. When +`providers.claude.dangerously_skip_permissions` is true it also passes +`--dangerously-skip-permissions`. + +### Codex + +```bash +npm install -g @openai/codex +codex +``` + +Nightshift invokes `codex exec` for headless work. Codex may use ChatGPT sign-in or +`OPENAI_API_KEY`. Autonomous Codex execution uses its approval-and-sandbox bypass behavior; if +that is inappropriate for your environment, remove Codex from `providers.preference` or set +`providers.codex.enabled: false`. + +### GitHub Copilot + +Standalone Copilot is preferred: + +```bash +npm install -g @github/copilot +copilot login +``` + +Copilot can also authenticate with `COPILOT_GITHUB_TOKEN`, `GH_TOKEN`, or `GITHUB_TOKEN`, or reuse +an authenticated `gh` session. Nightshift invokes non-interactive prompt mode and, when +`providers.copilot.dangerously_skip_permissions` is true, allows all tools and URLs. -Nightshift supports OpenAI's Codex CLI as an alternative provider: +If `copilot` is absent, Nightshift falls back to `gh` and requires the legacy Copilot extension +to appear in `gh extension list`: ```bash -codex --login +gh auth login --web +gh extension install github/gh-copilot --force +gh extension list ``` -## GitHub +GitHub has retired the extension, so standalone Copilot is recommended. `task run` confirms that +the extension is listed before using the fallback. Automatic `run` only checks for `gh` on +`PATH`; if the extension is absent, it can select Copilot and then fail during execution. -All output is PR-based. Nightshift creates branches and pull requests for its findings. +## Provider selection exceptions -## td (Task Management) +- `preview` ignores `providers.preference`, chooses the first enabled provider in fixed Claude, + Codex, Copilot order, and does not check executable availability. +- `task run --provider NAME` uses the requested provider even when it is disabled in + configuration. It checks executable availability but bypasses budgets and automatic + eligibility. +- Copilot usage is modeled as local premium-request counts under `providers.copilot.data_path`. + The current agent execution path does not increment that counter, so budget output cannot be + treated as authoritative Copilot account usage. -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. +## Project instruction files + +These integrations default to enabled: + +```yaml +integrations: + claude_md: true + agents_md: true +``` + +Nightshift looks in the project root, in order: + +| Integration | Recognized filenames | +|-------------|----------------------| +| Claude context | `claude.md`, `CLAUDE.md`, `.claude.md` | +| Agent instructions | `AGENTS.md`, `agents.md`, `.agents.md` | + +The readers can return the full file as context and extract task suggestions, conventions, +constraints, safety guidance, and action/tool restrictions as hints. However, the current CLI +does not construct or call the integration manager, so these files do not currently change +prompts or task selection through these configuration keys. Provider CLIs may still discover +their own instruction files independently. + +## td task source ```yaml integrations: task_sources: - td: enabled: true - teach_agent: true # Include td usage + core workflow in prompts + teach_agent: true ``` -## 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). +The implemented reader runs `td list --format json` from each project and can add td usage +context. A missing or unconfigured td CLI is non-fatal. The current Nightshift commands do not +invoke this reader, so configuring it does not yet import td work. -## GitHub Issues - -Source tasks from GitHub issues labeled with `nightshift`: +## GitHub issue source ```yaml integrations: - github_issues: - enabled: true - label: "nightshift" + task_sources: + - github_issues: true ``` + +This reader requires: + +- `gh` on `PATH` and authenticated. +- A GitHub `origin` remote in the project. +- Open issues labeled `nightshift`. + +The implemented reader uses `gh issue list` to read number, title, body, labels, and state. +Priority labels such as `critical`, `high`, `medium`, `low`, or `p0` through `p3` affect the +imported task priority. Authentication or repository failures are non-fatal. The current +Nightshift commands do not invoke this reader, so configuring it does not yet add GitHub issues +to task selection. + +`task_sources[].file` is accepted by the configuration schema, but there is no file-source reader +in the current implementation. + +## Generated reports + +`reporting.morning_summary` defaults to `true` and writes local summaries after runs. The +`reporting.email` and `reporting.slack_webhook` keys are accepted by the configuration schema, +and notification helper implementations exist, but the current run finalizer does not call +notification dispatch. diff --git a/website/docs/scheduling.md b/website/docs/scheduling.md index b552a79..4817f71 100644 --- a/website/docs/scheduling.md +++ b/website/docs/scheduling.md @@ -5,52 +5,173 @@ title: Scheduling # Scheduling -Nightshift can run automatically on a schedule. +Nightshift supports a persistent daemon and OS-managed scheduled runs. Configure one schedule +type before starting the daemon. -## Cron-Based +## Cron or interval + +Use a five-field cron expression: + +```yaml +schedule: + cron: "0 2 * * *" +``` + +Or use a positive Go duration: + +```yaml +schedule: + interval: 8h +``` + +Do not set both. `nightshift config validate` rejects a config containing both values, but it does +not parse either value. `nightshift doctor`, `nightshift preview`, and `nightshift daemon start` +construct the scheduler and catch malformed values. The cron parser accepts minute, hour, day of +month, month, and day of week fields; it does not accept a seconds field. Interval examples +include `30m`, `8h`, and `24h`, and the duration must be positive. + +A schedule is optional for manual `run`, `task run`, and configuration commands. It is required +for `preview` and `daemon start`. + +## Execution windows + +Restrict scheduler execution to a local or named timezone: ```yaml schedule: - cron: "0 2 * * *" # Every night at 2am + cron: "0 * * * *" + window: + start: "22:00" + end: "06:00" + timezone: "America/Los_Angeles" ``` -## Daemon Mode +Times use `HH:MM`. The start is inclusive and the end is exclusive. When start is later than end, +the window crosses midnight. If `timezone` is omitted, Nightshift uses the machine's local +timezone. Invalid times and unknown IANA timezone names prevent scheduler creation. + +The scheduler moves an out-of-window next-run preview to the next window start. For an interval +daemon that becomes the next timer firing. Cron jobs are different: the cron engine still fires +only on matching cron times, and an out-of-window firing is skipped rather than deferred to the +displayed window start. + +## Project and task limits + +```yaml +schedule: + cron: "0 2 * * *" + max_projects: 3 + max_tasks: 2 +``` + +Positive `max_projects` and `max_tasks` values become defaults for `nightshift run` when the +matching CLI flags are omitted. With no positive config value, `run` defaults to one eligible +project and one task per project. `--project` ignores the project limit; `--task` ignores the +task limit. `--random-task` always selects exactly one task. + +The persistent daemon does not use these two fields. It visits every existing explicit +`projects[].path` in configuration order and selects up to five tasks per project. -Run as a persistent background process: +Task cooldowns and budget filters can reduce the actual count. Projects already processed today +are skipped during automatic selection; specifying `--task` bypasses that processed-today skip. + +## Persistent daemon ```bash nightshift daemon start -nightshift daemon start --foreground # For debugging +nightshift daemon status nightshift daemon stop ``` -## System Service +`daemon start` loads global plus current-directory configuration, detaches a child process, and +waits for the next cron or interval time; it does not run a task immediately. Configuration is +not reloaded while the process is running. The daemon: + +- Writes `~/.local/share/nightshift/nightshift.pid`. +- Runs scheduled work only inside the configured window. +- Selects providers by preference and remaining budget. +- Processes only existing explicit `projects[].path` entries (or the current directory when none + are configured); project patterns and per-project config merging are not used. +- Captures Claude and Codex snapshots and prunes old snapshots in the background. +- Handles SIGINT and SIGTERM for graceful shutdown. -Install as a system service for automatic startup: +Use foreground mode to see failures directly: ```bash -# macOS (launchd) -nightshift install launchd +nightshift daemon start --foreground --timeout 45m +``` -# Linux (systemd) -nightshift install systemd +`daemon status` reports the PID, schedule, window, and PID-file path. `daemon stop` sends SIGTERM, +waits up to ten seconds, and sends SIGKILL if the process does not exit. + +## OS-managed service -# Universal (cron) +`install` creates an OS scheduler entry that invokes `nightshift run`; it does not start the +persistent daemon. Installed jobs therefore load the global config plus any `nightshift.yaml` +visible from the service's working directory, and they bypass execution windows because manual +`run` does not enforce the scheduler window. + +```bash +nightshift install # auto-detect +nightshift install launchd +nightshift install systemd nightshift install cron ``` -## Manual Runs +| Type | Installed artifact | Lifecycle | +|------|--------------------|-----------| +| launchd | `~/Library/LaunchAgents/com.nightshift.agent.plist` | Uses numeric hour/minute from `schedule.cron`, unloads an old plist, writes, and loads | +| systemd | `~/.config/systemd/user/nightshift.service` and `nightshift.timer` | Converts part of the schedule to `OnCalendar`, reloads user units, enables, and starts | +| cron | Managed `# nightshift managed cron entry` in the user's crontab | Preserves `schedule.cron` and replaces an older Nightshift entry | + +With no type, macOS selects launchd. Linux selects systemd when `systemctl` exists and cron +otherwise; other systems select cron. + +The service captures the resolved path of the Nightshift executable. Reinstall after moving or +upgrading a manually placed binary. It also runs non-interactively, so `run` confirmation is +automatically skipped. + +The three generators do not implement identical schedule semantics: + +- Use a simple five-field cron schedule for predictable installed-service behavior. +- launchd reads only numeric minute and hour. It ignores day, month, weekday, interval, and + execution-window constraints; without cron it uses 02:00. +- systemd maps interval text with a simple minute-oriented conversion and drops cron weekday. + Complex cron or non-minute intervals may produce an invalid or unintended timer. +- cron preserves a configured cron expression, ignores interval and execution-window settings, + and uses `0 2 * * *` when cron is empty. +- If loading configuration fails, the standalone `install` command silently installs the 02:00 + fallback instead of returning the configuration error. Run `config validate` and `doctor` + first. + +Use the persistent daemon when interval or execution-window semantics matter. + +Remove installed services with: + +```bash +nightshift uninstall +``` + +`uninstall` checks launchd, systemd, and cron and removes every Nightshift installation it finds. +Use `daemon stop` separately for a daemon started with `daemon start`. -Skip the scheduler and run immediately: +## Manual overrides + +Manual runs bypass schedule timing and windows: ```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 --project ~/code/myproject --task lint-fix +nightshift run --max-projects 3 --max-tasks 2 +nightshift run --ignore-budget ``` -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. +Automatic `run` still honors provider enablement, executable availability, task enablement, +cooldowns, and budget. `--task` bypasses task enablement, cooldown, cost filtering, and the +processed-today check. `--ignore-budget` bypasses budget exhaustion but does not enable a +disabled provider or make a missing executable available. + +`task run` is more direct: its explicit provider and task bypass provider enablement, preference, +budgets, task enablement, cooldowns, and run history. It still requires the executable and a +non-sensitive project path. diff --git a/website/docs/troubleshooting.md b/website/docs/troubleshooting.md index e998551..c7b9d60 100644 --- a/website/docs/troubleshooting.md +++ b/website/docs/troubleshooting.md @@ -5,57 +5,234 @@ title: Troubleshooting # Troubleshooting -## Common Issues +Start with: -**"Something feels off"** -- Run `nightshift doctor` to check config, schedule, and provider health +```bash +nightshift doctor +nightshift config +nightshift config validate +``` + +`doctor` checks configuration, scheduler parsing, enabled Claude/Codex executables, provider +data, database health, snapshots, and budget readiness. It does not test authentication or check +the Copilot executable. `config` shows which global and current-project files exist before +printing the merged configuration. + +## Configuration is missing or loaded from the wrong place + +Create the intended file: + +```bash +nightshift init --global # ~/.config/nightshift/config.yaml +nightshift init # ./nightshift.yaml +``` + +Only `nightshift.yaml` is recognized as a project config. A file named `.nightshift.yaml` is not +loaded. Run commands from the project root or use `run --project PATH` so Nightshift can load that +project's file. + +Configured `projects[].path` entries select directories to work on, but Nightshift does not +automatically load a separate `nightshift.yaml` from each of those directories. Project +`pattern`, `exclude`, `priority`, `tasks`, and `config` fields are also not used by the current +`run`, `preview`, or daemon path resolver. + +`config set` writes to the project file only when it already exists in the current directory. +Use `--global` when you intend to change the global file: + +```bash +nightshift config set logging.level debug --global +``` + +## The schedule is invalid + +Typical errors are: + +- Both `schedule.cron` and `schedule.interval` are set. +- Cron does not have the supported five-field form. +- An interval is not a positive Go duration. +- A window time is not valid `HH:MM`. +- A window timezone is not an installed IANA timezone. + +Keep one schedule type and validate: + +```yaml +schedule: + interval: 8h + window: + start: "22:00" + end: "06:00" + timezone: "America/Los_Angeles" +``` + +```bash +nightshift config validate +nightshift doctor +nightshift preview --explain +``` + +`config validate` catches the case where both schedule types are set, but it does not parse cron, +interval, window time, or timezone values. `doctor`, `preview`, and `daemon start` perform that +runtime validation. If `daemon start` says no schedule is configured, add `cron` or `interval`. +Manual runs do not require a schedule. + +## Nothing ran + +Use a dry run with explanations: + +```bash +nightshift preview --explain +nightshift run --dry-run --no-color +``` + +Common skip reasons include: + +- The project was already processed today. +- Every enabled task is on cooldown. +- No task fits the remaining token allowance. +- All preferred providers are disabled, unavailable, or out of budget. +- A configured project path does not exist. + +If configuration contains only project patterns, add explicit `path` entries; current execution +commands do not invoke pattern discovery. + +To inspect a task independently of automatic selection: + +```bash +nightshift task show docs-backfill --project . +nightshift task run docs-backfill --provider claude --project . --dry-run +``` + +## Budget is exhausted + +```bash +nightshift budget +nightshift budget --provider claude +nightshift budget history -n 10 +``` + +Check `budget.max_percent`, `reserve_percent`, `weekly_tokens`, `per_provider`, and the reported +reset boundary. For a deliberate one-off manual run: + +```bash +nightshift run --ignore-budget +``` + +That flag bypasses the exhausted-budget decision, but not provider enablement, executable +discovery, or task validation. + +## Calibration is missing or low-confidence + +Subscription calibration for Claude and Codex needs local usage data plus provider usage +percentages: -**"No config file found"** ```bash -nightshift init # Create project config -nightshift init --global # Create global config +nightshift budget snapshot --provider claude +nightshift budget history --provider claude +nightshift budget calibrate --provider claude ``` -**"Insufficient budget"** -- Check current budget: `nightshift budget` -- Increase `max_percent` in config -- Wait for budget reset (check reset time in output) +Install `tmux`, enable `budget.calibrate_enabled`, and collect several snapshots over time. +`snapshot --local-only` records token counts but cannot by itself infer a budget from a provider +percentage. Verify the provider `data_path` when Nightshift cannot find local session data. +Copilot does not support tmux percentage scraping; its snapshots are local-only. -**"Calibration confidence is low"** -- Run `nightshift budget snapshot` a few times to collect samples -- Ensure tmux is installed so usage percentages are available -- Keep snapshots running for at least a few days +If you are billed by API token rather than a subscription, avoid scraping and set explicit +limits: -**"tmux not found"** -- Install tmux or set `budget.billing_mode: api` if you pay per token +```yaml +budget: + billing_mode: api + weekly_tokens: 700000 + per_provider: + codex: 500000 +``` + +API billing disables calibration after configuration is loaded. -**"Week boundary looks wrong"** -- Set `budget.week_start_day` to `monday` or `sunday` +## A provider is unavailable or unauthenticated -**"Provider not available"** -- Ensure Claude/Codex CLI is installed and in PATH -- Check API key environment variables are set +Check executable discovery first: -## Debug Mode +```bash +command -v claude +command -v codex +command -v copilot +command -v gh +nightshift doctor +``` -Enable verbose logging: +Then authenticate the provider directly: ```bash -nightshift run --verbose +claude # Complete Claude Code login +codex # Choose ChatGPT sign-in or API-key setup +copilot login +gh auth status # Legacy gh copilot fallback +gh extension list ``` -Or set log level in config: +Nightshift prefers standalone `copilot`. The direct `task run` path checks `gh extension list` +before using the retired fallback, but automatic `run` selection checks only whether `gh` exists +and can fail later when the extension is missing. Install standalone Copilot for unattended use. +A provider `data_path` is not an executable path; changing it will not fix a missing CLI. + +For a service-only PATH problem, compare the interactive executable path with the PATH captured +by launchd, systemd, or cron. Nightshift supplements service PATH with common local bin +directories, but a custom installation directory must still be available. Reinstall the service +after correcting PATH: + +```bash +nightshift uninstall +nightshift install +``` + +## An installed service runs at the wrong time + +Installed launchd, systemd, and cron jobs do not share all persistent-daemon scheduling +semantics. Use a simple five-field cron expression before `nightshift install`: ```yaml -logging: - level: debug # debug | info | warn | error +schedule: + cron: "0 2 * * *" ``` -## Getting Help +launchd reads only numeric minute/hour, systemd performs a limited conversion, and cron preserves +the expression. All three ignore execution windows, and launchd/cron fall back to 02:00 for an +interval-only config. Use `nightshift daemon start` when intervals or windows matter. + +The standalone installer also falls back to 02:00 if configuration loading fails. Run these +before reinstalling: ```bash -nightshift --help -nightshift --help +nightshift config validate +nightshift doctor +nightshift uninstall +nightshift install ``` -Report issues: https://github.com/marcus/nightshift/issues +## Daemon lifecycle problems + +```bash +nightshift daemon status +nightshift logs --component daemon --level warn --tail 100 +nightshift daemon start --foreground +``` + +The PID file is `~/.local/share/nightshift/nightshift.pid`. A foreground start is the fastest way +to expose config, database, provider, and scheduler errors. `nightshift uninstall` removes +OS-managed scheduled services; it does not replace `nightshift daemon stop` for a detached daemon. + +## Inspect logs and reports + +```bash +nightshift logs --since "2026-07-25 22:00" --level warn +nightshift logs --match "no provider" --summary +nightshift report --period last-run --format plain --paths +nightshift status --today +``` + +Set `logging.level: debug` or `NIGHTSHIFT_LOG_LEVEL=debug` for more detail. + +Report reproducible problems at +[github.com/marcus/nightshift/issues](https://github.com/marcus/nightshift/issues) with the +Nightshift version, `doctor` output, relevant config with secrets removed, and filtered logs.