{"name":"gptme","repo":"https://github.com/gptme/gptme","docs":"https://gptme.org/docs/","headless":{"finding":"Yes: -n/--non-interactive runs prompts and exits without human confirmations. Invocation and exit-code details follow.","quote":"which runs a prompt non-interactively and exits:","url":"https://raw.githubusercontent.com/gptme/gptme/master/docs/automation.rst","third_party":false,"evidence":[{"finding":"Invocation composed from documented flags: gptme -n -w /path/to/worktree --output-format json \"Implement the requested change\". Supply the actual path/prompt and preconfigure model credentials. -w/--workspace selects the directory; -n implies -y/--no-confirm. JSON output is one message per stdout line. A positional file is context: gptme -n \"Follow this file\" task.txt. No --prompt-file option was found.","quote":"Non-interactive mode. Implies –no-confirm.","url":"https://gptme.org/docs/cli/gptme.html","third_party":false},{"finding":"Stdin: gptme -n -w /path/to/worktree < prompt.txt. The CLI wraps stdin as a user-message code block, appending it to the first positional prompt if present. Bare - separates prompts; it is not a stdin filename. Non-interactive mode sets no_confirm=True.","quote":"append it to first prompt, or create a new prompt","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/cli/main.py","third_party":false},{"finding":"The automation guide documents 0 for successful completion, 1 for execution failure, and 2 for invalid arguments. These are process outcomes, not independently verified correctness of generated code.","quote":"``0``: Task completed successfully","url":"https://raw.githubusercontent.com/gptme/gptme/master/docs/automation.rst","third_party":false},{"finding":"Current master additionally classifies non-interactive fatal failures as 75 rate-limit/quota, 76 authentication/permission, and 77 model/service unavailable; generic failures use 1. SIGTERM raises SystemExit(128 + signum). Thus the automation guide is not the complete current exit-code contract.","quote":"EXIT_RATE_LIMIT = 75","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/cli/main.py","third_party":false},{"finding":"It can leave files behind: the save tool creates parent directories and writes files; save and append are tool operations, with confirmation handling. This is source verification, not a live model run.","quote":"f\"Save to {path}?\"","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/tools/save.py","third_party":false}]},"confinement":{"finding":"Default execution uses the user account’s permissions. Confirmations and tool selection are distinct from OS isolation. An optional sandbox exists, but is not a whole-agent boundary; unattended workers need to account for every enabled tool and host-side operation.","quote":"gptme runs code, edits files, and browses the web on your machine, with your permissions.","url":"https://gptme.org/docs/security.html","third_party":false,"evidence":[{"finding":"Tool allowlist: -t/--tools accepts comma-separated or repeated tool names. +tool adds to defaults, -tool excludes from defaults, none disables tools. For example, --tools read,save,patch,shell limits available tools to those named. -n auto-approves confirmations; it does not itself confine execution.","quote":"Tools to allow.","url":"https://gptme.org/docs/cli/gptme.html","third_party":false},{"finding":"Profiles enforce their tool allowlists in the CLI (--agent-profile) and subagent thread mode. Profile behavior booleans read_only and no_network are only prompting rules, not filesystem or network enforcement.","quote":"Behavior rules (read_only, no_network)\nremain soft/prompting-based.","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/profiles.py","third_party":false},{"finding":"Shell validation has a built-in conservative auto-approval allowlist (including ls, cat, rg) and regex deny groups for dangerous commands, including destructive git commands, broad rm, and overly permissive chmod. This is command-pattern checking, not a general arbitrary-command security policy.","quote":"Uses a conservative allowlist approach:","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/tools/shell_validation.py","third_party":false},{"finding":"The shell tool itself rejects denylisted commands before execution, independently of the optional guardrail hook; it also rechecks edited commands. Do not interpret -n as bypassing every deterministic denial.","quote":"is_denied, deny_reason, matched_cmd = is_denylisted(cmd)","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/tools/shell.py","third_party":false},{"finding":"Additional TOOL_CONFIRM guardrails use GPTME_GUARDRAILS=shadow (default, log only), enforce (skip blocked execution), or off. Enforce checks destructive shell patterns, credential-output commands and secret-path access; read-tool secret paths are checked too. This is selective protection, not a universal file-path allowlist.","quote":"``enforce`` — return ``ConfirmationResult.skip(reason)`` to block execution.","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/hooks/guardrails.py","third_party":false},{"finding":"GPTME_SANDBOX defaults to none. Linux shell backends: firejail/bwrap; Python: docker/wasmtime. GPTME_SANDBOX_NET=0 disables sandboxed network (default), 1 permits it where supported. Bubblewrap has read-only OS binds, writable workspace, private home/tmp, PID/network namespaces. Firejail uses private home, workspace whitelist, seccomp and dropped capabilities. Docker Python mounts workspace writable; Wasmtime exposes neither workspace nor network. This is tool-specific, not global offline mode; no seatbelt/gVisor backend was found.","quote":"GPTME_SANDBOX: sandbox backend (\"firejail\" | \"bwrap\" | \"docker\" | \"wasmtime\" | \"none\", default \"none\")","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/sandbox.py","third_party":false},{"finding":"Important coverage limit: wrap_shell_cmd only wraps firejail/bwrap; docker/wasmtime return the shell command unchanged. Conversely, Python takes the isolated path only for docker/wasmtime. Selecting bwrap does not isolate in-process Python; selecting docker does not put the shell tool in Docker.","quote":"if sandbox_cfg.backend in (\"docker\", \"wasmtime\"):","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/tools/python.py","third_party":false},{"finding":"Save/append reject relative paths resolving outside cwd but allow absolute paths. --workspace is therefore not a jail; the shell sandbox does not confine these host-side writes. Integration recommendation: isolate the entire runtime in an external container/VM with controlled mounts and egress.","quote":"# Absolute paths are intentional and allowed, relative paths should not escape cwd","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/tools/save.py","third_party":false}]},"token_reporting":{"finding":"Yes. GPTME_EXIT_STATS=1 prints a per-session JSON line on stderr, using API-reported counts. Fields: session_id, total_cost, total_input_tokens, total_output_tokens, cache_read_tokens, cache_creation_tokens, cache_hit_rate, request_count. No summary is emitted if no LLM requests were recorded. A worker can capture this stderr line and retain JSONL messages/session logs; it should treat absent usage as unknown.","quote":"Print a JSON session cost summary to stderr.","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/util/cost.py","third_party":false,"evidence":[{"finding":"The CLI actually calls print_exit_stats() in its final cleanup when GPTME_EXIT_STATS is enabled. This supports scripted subprocess metering without an OpenTelemetry installation.","quote":"if get_config().get_env_bool(\"GPTME_EXIT_STATS\"):","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/cli/main.py","third_party":false},{"finding":"Assistant-message metadata contains usage.input_tokens, usage.output_tokens, usage.cache_read_tokens, usage.cache_creation_tokens and optional usage.reasoning_tokens. metadata.cost is USD; metadata.cost_source=\"provider\" distinguishes provider-billed cost from catalog estimates. Reasoning tokens are provider-dependent; Anthropic includes thinking in output_tokens. --output-format json serializes metadata into stdout events. Fields are optional: do not assume every backend populates every field.","quote":"input_tokens: int\n    output_tokens: int\n    cache_read_tokens: int\n    cache_creation_tokens: int","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/message.py","third_party":false},{"finding":"Conversation persistence serializes messages with to_dict() into conversation.jsonl. Those message dictionaries preserve metadata, so per-message usage is available in session files as well as JSON stdout.","quote":"lines = (json.dumps(msg.to_dict()) + \"\\n\" for msg in self.messages[start:])","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/logmanager/manager.py","third_party":false},{"finding":"gptme-stats --json aggregates logs: total_sessions, total_cost, total_tokens.{total,input,output,cache_read}, avg_cost_per_session, days, by_model. Text tables are also supported. Here cache_read is a subset of input. This is cross-session analytics, not the exit summary.","quote":"Cache-read tokens are included in the input-token count, not added on top.","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/cli/cmd_stats.py","third_party":false},{"finding":"Session CostTracker summaries have no separate reasoning field. Their cache_hit_rate denominator adds input, cache-read and cache-creation counts. Do not assume the exit-summary input convention and the stored-log statistics convention are identical.","quote":"cache_read_tokens / (input_tokens + cache_read_tokens + cache_creation_tokens)","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/util/cost_tracker.py","third_party":false},{"finding":"Request accounting records CostTracker entries before the telemetry-enabled early return, so enabling exported telemetry is not required for local session accounting. Costs default to model-catalog estimates unless provider-billed costs are passed.","quote":"``cost`` is a provider-billed amount; when omitted it is estimated from\n    the model catalog.","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/telemetry.py","third_party":false}]},"parallelism":{"finding":"Yes: enable the subagent tool (CLI example: -t +subagent). subagent_parallel(tasks, ...) fans out agents and waits for all; subagent_batch supports batch execution. Per-agent model, profile, workdir and isolated=True can be configured; isolation here means separate git worktrees, not OS confinement. The docs also expose subagent_wait()/BatchJob.wait_all() input_tokens and output_tokens results.","quote":"Fan out N subagents in parallel and block until all complete.","url":"https://raw.githubusercontent.com/gptme/gptme/master/docs/tools/subagent.rst","third_party":false,"evidence":[{"finding":"Thread execution is supported, use_subprocess=True starts a separate gptme process, and use_acp=True delegates via an ACP command. Subprocess mode captures stdout/stderr separately. Explicit isolated=True requests a disposable git worktree. Separate workdirs prevent ordinary concurrent edits from sharing one checkout.","quote":"If True, run subagent in subprocess for output isolation.","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/tools/subagent/api.py","third_party":false},{"finding":"Concurrent subagents are capped by GPTME_SUBAGENT_MAX_CONCURRENT, then [subagent] max_concurrent in gptme.toml, then min(8, os.cpu_count() or 2). This is a module-level semaphore, not a machine-wide scheduler.","quote":"Provides a module-level semaphore that caps the number of concurrently running\nsubagents.","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/tools/subagent/concurrency.py","third_party":false},{"finding":"Multiple distinct conversations can be run as separate CLI processes (inference from per-conversation locking and conversation IDs). Do not run two processes against the same conversation: LogManager obtains an exclusive lock on its directory. Use separate names/log directories and worktrees for workers.","quote":"Acquire an exclusive lock on the conversation directory.","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/logmanager/manager.py","third_party":false}]},"providers":{"finding":"Documented built-ins: openai, anthropic, openrouter, requesty, groq, gemini, xai, deepseek, moonshot, nvidia, azure, openai-subscription, grok-subscription, gptme, local. Select with -m/--model provider/model. API providers use keys; Azure also requires an endpoint. The overview explicitly names llama-cpp-python as an OpenAI-compatible server.","quote":"Built-in providers, the model prefix to use, and how each one authenticates.","url":"https://raw.githubusercontent.com/gptme/gptme/master/docs/providers.rst","third_party":false,"evidence":[{"finding":"Local/custom OpenAI-compatible backends include Ollama, LM Studio and vLLM. Configure OPENAI_BASE_URL and use -m local/model, or named [[providers]] entries. These are model backends used by the gptme runtime, not delegation to other coding-agent CLIs.","quote":"This page covers **Ollama**, **LM Studio**, **vLLM**, and any other\nOpenAI-compatible server","url":"https://raw.githubusercontent.com/gptme/gptme/master/docs/providers-custom.rst","third_party":false}]},"subscription_login":{"finding":"Yes: ChatGPT Plus/Pro (openai-subscription) and SuperGrok (grok-subscription). Authenticate initially in a browser with gptme-auth openai-subscription or gptme-auth grok-subscription; later runs reuse/refresh credentials. SuperGrok can reuse grok CLI login. This is model access inside gptme, not another agent harness.","quote":"Access tokens are automatically refreshed before expiry, so you only need to authenticate once.","url":"https://raw.githubusercontent.com/gptme/gptme/master/docs/providers-supported.rst","third_party":false,"evidence":[{"finding":"Managed-service login: gptme-auth login uses Device Flow, stores/refreshes a token; use -m gptme/model. GPTME_CLOUD_API_KEY is an alternative. This proves account access, not unlimited flat-rate worker billing.","quote":"Authenticate using the Device Flow command:","url":"https://raw.githubusercontent.com/gptme/gptme/master/docs/providers-supported.rst","third_party":false},{"finding":"The project says Claude Max is unavailable and requires an Anthropic API key. Gemini consumer-subscription and GitHub Copilot logins were not found in inspected provider/auth docs and source.","quote":"**Claude Max** — not available:","url":"https://raw.githubusercontent.com/gptme/gptme/master/docs/providers.rst","third_party":false},{"finding":"OpenRouter browser OAuth is not evidence of subscription billing: its PKCE flow creates a permanent API key. It is onboarding that avoids manual key entry.","quote":"OpenRouter OAuth produces a **permanent API key**","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/llm/llm_openrouter_subscription.py","third_party":false}]},"license":{"finding":"MIT (SPDX: MIT). LICENSE contains the MIT permission/disclaimer text but does not literally print an SPDX identifier; GitHub’s repository API identifies it as MIT.","quote":"Permission is hereby granted, free of charge, to any person obtaining a copy","url":"https://raw.githubusercontent.com/gptme/gptme/master/LICENSE","third_party":false,"evidence":[{"finding":"GitHub reports license.spdx_id = MIT.","quote":"\"spdx_id\":\"MIT\"","url":"https://api.github.com/repos/gptme/gptme","third_party":false}]},"latest_stable":{"finding":"v0.35.1; published 2026-10-15T22:08:47Z (ISO date 2026-10-15), prerelease=false, draft=false. Selected from the GitHub releases API, not the HTML releases page. Metadata observed 2026-10-27.","quote":"\"tag_name\":\"v0.35.1\"; \"published_at\":\"2026-10-15T22:08:47Z\"","url":"https://api.github.com/repos/gptme/gptme/releases?per_page=100","third_party":false,"evidence":[{"finding":"PyPI package gptme reports current registry version 0.35.1. PyPI is the package registry, used specifically for the requested registry-version fact.","quote":"\"version\":\"0.35.1\"","url":"https://pypi.org/pypi/gptme/json","third_party":true}]},"latest_prerelease":{"finding":"v0.35.2.dev20261022; published 2026-10-22T16:43:37Z (ISO date 2026-10-22), prerelease=true, draft=false. Selected from the GitHub releases API, observed 2026-10-27.","quote":"\"tag_name\":\"v0.35.2.dev20261022\"; \"published_at\":\"2026-10-22T16:43:37Z\"","url":"https://api.github.com/repos/gptme/gptme/releases?per_page=100","third_party":false},"last_commit":{"finding":"Default branch master: d5d935c3474325f429b4dde7d4918e526031886e. Both author and committer timestamp are 2026-10-26T18:02:39Z (ISO date 2026-10-26), observed 2026-10-27. The commits endpoint without sha uses the repository default branch.","quote":"\"date\":\"2026-10-26T18:02:39Z\"","url":"https://api.github.com/repos/gptme/gptme/commits?per_page=1","third_party":false},"fork_of":{"finding":"GitHub marks this repository fork=false; no parent coding-agent repository is identified. This establishes GitHub fork status, not that no code was ever borrowed.","quote":"\"fork\":false","url":"https://api.github.com/repos/gptme/gptme","third_party":false},"maintenance":{"finding":"GitHub reports not archived/disabled; default branch master. Findings describe docs and master observed 2026-10-27, not verified stable-wheel behavior. No unmaintained designation found in README/API.","quote":"\"archived\":false; \"disabled\":false; \"default_branch\":\"master\"","url":"https://api.github.com/repos/gptme/gptme","third_party":false,"evidence":[{"finding":"The README explicitly states the repository is maintained with gptme.","quote":"This repo is maintained with [gptme](https://gptme.org).","url":"https://raw.githubusercontent.com/gptme/gptme/master/README.md","third_party":false}]},"gaps":[{"finding":"not found","quote":"","url":"https://gptme.org/docs/cli/gptme.html","third_party":false,"detail":"Dedicated prompt-file flag: checked CLI reference and gptme/cli/main.py. File-as-context and stdin redirection are supported instead.","checked":["https://gptme.org/docs/cli/gptme.html","https://raw.githubusercontent.com/gptme/gptme/master/gptme/cli/main.py"]},{"finding":"not found","quote":"","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/sandbox.py","third_party":false,"detail":"No global network-off policy covering browser/model/host tools, seatbelt/gVisor backend, or configurable all-tool file-path policy found in the listed sources.","checked":["https://gptme.org/docs/security.html","https://raw.githubusercontent.com/gptme/gptme/master/gptme/sandbox.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/profiles.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/tools/save.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/tools/shell.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/tools/python.py"]},{"finding":"not found","quote":"","url":"https://raw.githubusercontent.com/gptme/gptme/master/docs/providers.rst","third_party":false,"detail":"Gemini consumer-subscription and GitHub Copilot login: not found in listed sources; external proxies were not assessed.","checked":["https://raw.githubusercontent.com/gptme/gptme/master/docs/providers.rst","https://raw.githubusercontent.com/gptme/gptme/master/docs/providers-supported.rst","https://raw.githubusercontent.com/gptme/gptme/master/docs/providers-custom.rst","https://raw.githubusercontent.com/gptme/gptme/master/gptme/cli/auth.py"]},{"finding":"not found","quote":"","url":"https://raw.githubusercontent.com/gptme/gptme/master/gptme/util/cost_tracker.py","third_party":false,"detail":"No separate exit-summary reasoning field, or proof of complete auxiliary/retry/subprocess usage in a single parent total. No live model execution.","checked":["https://raw.githubusercontent.com/gptme/gptme/master/gptme/util/cost.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/util/cost_tracker.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/message.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/llm/__init__.py","https://raw.githubusercontent.com/gptme/gptme/master/docs/tools/subagent.rst"]}],"sources":["https://gptme.org/docs/","https://gptme.org/docs/cli.html","https://gptme.org/docs/security.html","https://gptme.org/docs/providers.html","https://gptme.org/docs/tools/subagent.html","https://gptme.org/docs/cli/gptme.html","https://gptme.org/docs/config.html","https://gptme.org/docs/providers-supported.html","https://gptme.org/docs/providers-custom.html","https://api.github.com/repos/gptme/gptme","https://api.github.com/repos/gptme/gptme/releases","https://api.github.com/repos/gptme/gptme/releases?per_page=100","https://api.github.com/repos/gptme/gptme/commits?per_page=1","https://api.github.com/repos/gptme/gptme/git/trees/master?recursive=1","https://github.com/gptme/gptme","https://pypi.org/pypi/gptme/json","https://raw.githubusercontent.com/gptme/gptme/master/docs/providers-supported.rst","https://raw.githubusercontent.com/gptme/gptme/master/docs/providers-custom.rst","https://raw.githubusercontent.com/gptme/gptme/master/docs/config.rst","https://raw.githubusercontent.com/gptme/gptme/master/docs/automation.rst","https://raw.githubusercontent.com/gptme/gptme/master/LICENSE","https://raw.githubusercontent.com/gptme/gptme/master/gptme/cli.py","https://raw.githubusercontent.com/gptme/gptme/master/docs/providers.rst","https://raw.githubusercontent.com/gptme/gptme/master/gptme/sandbox.py","https://raw.githubusercontent.com/gptme/gptme/master/docs/tools/subagent.rst","https://raw.githubusercontent.com/gptme/gptme/master/gptme/cli/main.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/cli/cmd_stats.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/hooks/guardrails.py","https://raw.githubusercontent.com/gptme/gptme/master/tests/test_json_output.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/llm/utils.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/llm/__init__.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/message.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/tools/shell_validation.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/util/cost_tracker.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/util/cost.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/tools/subagent/concurrency.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/tools/save.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/profiles.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/telemetry.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/util/_telemetry.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/tools/shell.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/tools/python.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/llm/llm_openrouter_subscription.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/cli/auth.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/tools/subagent/api.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/logmanager.py","https://raw.githubusercontent.com/gptme/gptme/master/README.md","https://raw.githubusercontent.com/gptme/gptme/master/gptme/logmanager/log.py","https://raw.githubusercontent.com/gptme/gptme/master/gptme/logmanager/manager.py"]}
