Skip to content

Logs, version & completions 1.3 ​

Three small commands that help when something goes wrong, or when you just want tab completion. All three are new in engine 1.3.0.

The local log ​

Every CLI command, and every LSP command or workspace scan, appends one JSON line to pypilot.log. It records what PyPilot actually ran: the command, the workspace, the mode, every subprocess with its exit code and duration, and the stderr of the ones that failed. Nothing is sent anywhere.

PlatformLocation
Windows%LOCALAPPDATA%\PyPilot\pypilot\cache\logs\pypilot.log
macOS~/Library/Caches/dev.PyPilot.pypilot/logs/pypilot.log
Linux~/.cache/pypilot/logs/pypilot.log

The file rotates at 1 MB and three files are kept (pypilot.log, pypilot.log.1, pypilot.log.2). Writing it is best-effort: a log that cannot be written is skipped, and never breaks a command.

VariableEffect
PYPILOT_NO_LOG=1Turn the log off
PYPILOT_LOG_DIR=<dir>Write it to <dir> instead

Reading it ​

bash
pypilot logs                  # the log's path
pypilot logs --tail 5         # the last 5 entries, summarized
pypilot logs --tail --json    # the last 10 entries as raw JSON lines

pypilot logs --tail 5, after a doctor, a setup --dry-run, two checks (one of them a misspelt name) and an install --dry-run:

text
2026-10-29 17:07:44Z  cli doctor           ok      0.1s  C:\src\demo  [Auto]  21 steps
2026-10-29 17:07:44Z  cli setup            ok      0.1s  C:\src\demo  [Auto -> UvFull]  23 steps
2026-10-29 17:07:45Z  cli check            ok      0.1s  C:\src\demo  [Auto]  21 steps
2026-10-29 17:07:45Z  cli check            FAIL    1.4s  C:\src\demo  [Auto]  21 steps, 17 failed
    x py -3.12 -c import sys;print('%d.%d	%s'%(sys.version_info[0],sys.version_info[1],sys.executable))  (exit 103, 0.0s)
      or open the Microsoft Store to the requested version.
    x py -3.13 -c import sys;print('%d.%d	%s'%(sys.version_info[0],sys.version_info[1],sys.executable))  (exit 103, 0.0s)
      or open the Microsoft Store to the requested version.
    x py -3.15 -c import sys;print('%d.%d	%s'%(sys.version_info[0],sys.version_info[1],sys.executable))  (exit 103, 0.0s)
      or open the Microsoft Store to the requested version.
    error: `nosuchpkg-zzqq` is not on PyPI; check the spelling
2026-10-29 17:07:46Z  cli install          ok      0.0s  C:\src\demo  [Auto -> UvFull]  2 steps

Each summary shows the time (UTC), the source (cli or lsp), the command, the result, the duration, the workspace, the configured mode and, after ->, the mode it resolved to, then how many subprocesses ran. The step count includes the read-only probes, such as looking for interpreters; on this machine the Windows py launcher has no 3.12, 3.13 or 3.15, so those probes fail, which is normal and is why a failed entry lists them. For a failed entry, the last three failed steps and the error follow.

Fields ​

One line, pretty-printed:

json
{
  "ts": "2026-10-29T13:40:50.887Z",
  "pypilot": "1.3.0",
  "source": "cli",
  "command": "install",
  "args": ["httpx", "--dry-run"],
  "workspace": "C:\\src\\demo",
  "mode": "Auto",
  "resolved_mode": "UvFull",
  "ok": true,
  "duration_ms": 33,
  "steps": [
    { "cmd": "uv", "args": ["--version"], "ok": true, "exit": 0, "duration_ms": 15 },
    { "cmd": "uv", "args": ["--version"], "ok": true, "exit": 0, "duration_ms": 13 }
  ],
  "error": null
}
FieldMeaning
tsWhen the command started, UTC
pypilotEngine version
sourcecli or lsp
command, argsThe mode (or LSP action) and its arguments
workspaceThe project folder
mode, resolved_modeThe package_manager setting and the mode it resolved to (null when none was needed). Written as Auto, UvFull and so on, not the snake_case of the JSON output
ok, errorThe result, and the error message when it failed
duration_msWall time
stepsEvery subprocess: { cmd, args, ok, exit, duration_ms, stderr }. stderr holds the last 2,000 characters, for failed steps only. At most 100 steps are kept per entry; the rest are only counted

What is masked ​

Arguments, stderr and errors pass through a redactor before they are written: credentials in URLs (https://user:token@host), NAME=value pairs and --name value flags whose name contains token, secret, password, passwd, auth, credential or api-key/api_key/apikey, and well-known token formats (GitHub ghp_/github_pat_…, PyPI pypi-, GitLab glpat-, Slack xox…, sk- keys). The environment is never recorded. Still, read a log before you paste it into a public issue: paths and package names are in it.

Version ​

bash
pypilot --version          # pypilot 1.3.0
pypilot version            # also commit, target and data tables
pypilot --version --json   # everything, as JSON
text
pypilot 1.3.0
commit   9b532639be6e (2026-10-29)
target   x86_64-pc-windows-msvc
data     bundled 2026-10-29; refreshed 3 hours ago (next in 6 days); 1 of 5 bundled
  nvidia.json                schema 3   cache, refreshed 3 hours ago
  frameworks.json            schema 2   cache, refreshed 3 hours ago
  import_map.json            schema 1   cache, refreshed 3 hours ago
  cuda_extensions.json       schema 1   cache, refreshed 3 hours ago
  conda_forge_packages.json  schema 1   bundled

Bare --version stays one line forever: the Zed and VS Code extensions and pypilot-cli parse it. The data line says when the bundled tables were last changed in the source, how long ago the cache was refreshed and when the next refresh is due; the table below it shows, per table, its schema and whether the bundled copy or a refreshed one is in use. For the JSON fields see Machine-readable output.

Is the data current? ​

pypilot doctor shows the tables' state offline, as a line under Environment:

text
  data tables     : refreshed just now (next in 6 days); 1 of 5 bundled

pypilot update-data --check goes online and compares the copies in use with main, writing nothing. It exits 0 when current, 1 when stale, 2 when a fetch failed. See update-data --check.

Shell completion ​

pypilot completions <shell> prints a script that completes modes, flags, fix python|cuda, --to values and pixi platform names.

bash
# ~/.bashrc
source <(pypilot completions bash)
zsh
# ~/.zshrc
source <(pypilot completions zsh)
fish
pypilot completions fish > ~/.config/fish/completions/pypilot.fish
powershell
# $PROFILE
pypilot completions powershell | Out-String | Invoke-Expression

The PowerShell script registers a native argument completer for pypilot and pypilot.exe. It starts like this:

powershell
# pypilot PowerShell completion. Load with:
#   pypilot completions powershell | Out-String | Invoke-Expression
Register-ArgumentCompleter -Native -CommandName 'pypilot', 'pypilot.exe' -ScriptBlock {
    param($wordToComplete, $commandAst, $cursorPosition)
    $commands = @{
        'doctor' = @{ Flags = @('--json', '--markdown', '--fix', '--dry-run', '--path', '-p', '--pixi-env', '--help', '-h'); Takes = @{ '--path' = @(); '-p' = @(); '--pixi-env' = @() }; Positional = @() }
        'setup' = @{ Flags = @('--dry-run', '--json', '--path', '-p', '--pixi-env', '--help', '-h'); Takes = @{ '--path' = @(); '-p' = @(); '--pixi-env' = @() }; Positional = @() }
        'check' = @{ Flags = @('--suggest-alternatives', '--json', '--markdown', '--path', '-p', '--pixi-env', '--help', '-h'); Takes = @{ '--path' = @(); '-p' = @(); '--pixi-env' = @() }; Positional = @() }
        'explain' = @{ Flags = @('--json', '--path', '-p', '--pixi-env', '--help', '-h'); Takes = @{ '--path' = @(); '-p' = @(); '--pixi-env' = @() }; Positional = @() }
        'install' = @{ Flags = @('--dry-run', '--json', '--path', '-p', '--pixi-env', '--help', '-h'); Takes = @{ '--path' = @(); '-p' = @(); '--pixi-env' = @() }; Positional = @() }
        'fix' = @{ Flags = @('--dry-run', '--json', '--path', '-p', '--pixi-env', '--help', '-h'); Takes = @{ '--path' = @(); '-p' = @(); '--pixi-env' = @() }; Positional = @('python', 'cuda') }
        'update-data' = @{ Flags = @('--check', '--json'); Takes = @{  }; Positional = @() }

With pypilot-cli, the wrapper passes completions straight to the engine, so the same commands work.

Engine: AGPL-3.0-or-later. Zed and VS Code extensions: Apache-2.0. pypilot-cli: AGPL-3.0-or-later.