-
Notifications
You must be signed in to change notification settings - Fork 0
First Run
The first time a user invokes stud-cli on a fresh machine, they have no global scope set up: no provider configured, no extensions picked, no trust list. This flow covers how core reaches a runnable session without silently assuming on the user's behalf.
Covers two orthogonal first-run checks:
- Provider setup — global scope has no provider; core cannot compose a request. The bundled TUI walks the user through adding one.
-
Project trust — the user launched inside a directory with
<cwd>/.stud/and the path is not on the global trust list. Core runs the standard Project Trust prompt.
Both checks run every startup; the first-run case is when both fire on the same invocation. Subsequent runs usually pass both checks silently.
- Fresh machine.
~/.stud/either does not exist or contains no provider config. - User invokes
stud-cliin some working directory. - The machine has network access.
- Core ships with the bundled TUI as the default subscriber + interactor UI and a bundled set of Provider extensions (see Providers contract).
sequenceDiagram
autonumber
actor User
participant CLI as stud-cli startup
participant Core
participant Settings as global settings<br/>(~/.stud/)
participant Trust as trust list<br/>(~/.stud/)
participant TUI as bundled TUI
participant Prov as Provider
participant Backend as LLM backend
User->>CLI: "stud-cli"
CLI->>Core: bootstrap
Core->>Settings: load providers
Settings-->>Core: none configured
Core->>TUI: "InteractionRequest<br/>(Select: provider)"
TUI->>User: list bundled providers
User-->>TUI: pick provider
TUI-->>Core: chosen provider
Core->>TUI: "InteractionRequest<br/>(Select: auth path)"
Note over Core,TUI: paths offered depend on what<br/>the chosen Provider's init drives
TUI->>User: offer env-backed apiKeyRef /<br/>Auth.DeviceCode / Auth.Password
User-->>TUI: pick path
alt env-backed apiKeyRef
TUI->>User: "Ask: env-var name"
User-->>TUI: env name
TUI-->>Core: env reference
else Auth.DeviceCode / Auth.Password
Core->>Prov: init
Prov->>Core: drive Auth.DeviceCode or<br/>Auth.Password via Interaction Protocol
Core->>TUI: render prompt (code + URL or password entry)
User->>Backend: complete auth
Backend-->>Prov: token
Prov-->>Core: auth complete
end
Core->>Settings: ensure ~/.stud/ exists<br/>(if just created, scaffold system.md)
Core->>Settings: write provider config<br/>(reference, never literal secret)
Core->>Core: resolve project root (cwd/.stud)
alt project/.stud exists
Core->>Trust: lookup path
Trust-->>Core: not listed
Core->>TUI: run Project-Trust prompt<br/>(see dedicated flow)
Note over Core,Trust: see Project First-Run Trust flow —<br/>outcome feeds the loader below
else no project/.stud
Note over Core: nothing to trust at project scope
end
Core->>Core: load bundled + global + (maybe) project
Core->>Core: open session
Core-->>TUI: ready
The two arms are independent: missing provider alone still runs the Project Trust check, and an already-configured provider still runs the Project Trust check when landing in a fresh project.
Provider setup writes to the global scope under ~/.stud/. It never writes to project scope — the project may not even be trusted yet.
The TUI lists every provider extension loaded from bundled + global scope. In a truly fresh setup only the bundled providers are visible. The user picks one by id.
A Provider's config-schema (apiKeyRef behavior) and init code together determine which auth paths make sense for it — see Providers § Configuration schema and Providers § Lifecycle. The TUI offers only the paths the chosen provider supports; core does not invent an auth path the provider does not handle.
The paths bundled providers use:
| Path | What the TUI collects | What is written to global settings |
|---|---|---|
Env-backed apiKeyRef
|
The name of an environment variable the user will export (e.g., OPENAI_API_KEY). |
A credential reference ({ kind: "env", name }) — the variable name — resolved at request time via the Env Provider. No literal secret is ever written to settings.
|
| Literal API key | The key value. | Warned against. Core writes a literal only if the user insists; the warning cites Secrets Hygiene and recommends the env-backed path. |
Auth.DeviceCode |
Nothing to type; the provider renders a code + URL through the Interaction Protocol. | A token reference — resolved through the env provider or a provider-owned state slot. Never the literal token. |
Auth.Password |
Password via secure input; the value never echoes and is never logged. | A token reference, same rule. |
The driving rule: the global settings.json never stores a resolved secret. Only references. Same rule the session manifest follows; see Secrets Hygiene.
Providers that need interactive auth drive the Interaction Protocol from their init hook — see Providers § Lifecycle. In a fresh default setup, the bundled TUI is the only active interactor and answers those requests. A provider that cannot complete init (bad key, network failure, backend down) raises a validation diagnostic; the TUI shows it and offers the user a chance to retry or pick a different provider.
If the global config directory was just created in this run, core also writes the bundled default system.md into it. The trigger is directory-first-creation, not file existence — on every subsequent launch the directory already exists, and a system.md the user has since deleted stays gone. Failure semantics, audit event, and content categories are normative on SystemPromptFile § First-run scaffold; this flow does not restate them.
Independent of provider setup. Runs whenever <cwd>/.stud/ exists and the path is not on the user-global trust list.
The prompt, the decision options, what is shown, and the re-prompt triggers are all normative on the Project Trust page. This flow does not restate them; the canonical sequence is Project First-Run Trust.
What matters here is the rule a first-run flow must honour:
No silent trust write. Entering a new project always prompts the user. The user's decision is the only thing that lands in the trust list.
Core never adds the current directory to the trust list "because stud-cli is running here". Launching stud-cli in a project .stud/ without a prior trust decision always opens the prompt.
| User action | Effect |
|---|---|
| Abort provider setup | Session does not start. Nothing is written. Next invocation re-runs the provider-setup prompt. |
| Decline project trust | Session starts with bundled + global only. No project extensions, no project MCP, no project ordering-manifest. /tools and /providers still show the global set. |
| "Trust once" on project | Project layer loads this session; nothing is written to the trust list. Next invocation in this path re-prompts. |
A provider that fails its own init after the user completed the TUI flow (e.g., a typo in the env var name) surfaces as a validation diagnostic and the TUI offers to edit or re-pick. The session does not start with a broken provider.
Provider setup emits:
-
ProviderRegistered{id, protocol, authPath}— note:authPath(the shape chosen, e.g.,env-api-key,Auth.DeviceCode,Auth.Password), not credentials. -
ExtensionSetRevisedif the provider rewrotesettings.json. -
SystemPromptScaffolded{path, byteCount}if the bundled defaultsystem.mdwas written during global-config-dir first creation. The content of the default is not duplicated into the audit record. See SystemPromptFile § First-run scaffold.
Project trust emits the records documented in Project First-Run Trust § Audit.
No audit record contains a resolved secret; see Audit Trail and Secrets Hygiene.
On the next invocation:
- If the user stays in the same directory, neither prompt fires — the provider is configured and the project is on the trust list.
- If the user cd's into another fresh project, only the Project-Trust prompt fires.
- If the user deletes
~/.stud/or the provider stanza, the provider-setup prompt fires again. A fully deleted~/.stud/also re-arms the system-prompt scaffold, because the trigger is directory-first-creation; deleting onlysystem.mddoes not. - Clearing the trust list re-arms the Project-Trust prompt for every project; see Project Trust § Re-prompt triggers.
- Execution Model
- Message Loop
- Concurrency and Cancellation
- Error Model
- Event and Command Ordering
- Event Bus
- Command Model
- Interaction Protocol
- Hook Taxonomy
- Host API
- Extension Lifecycle
- Env Provider
- Prompt Registry
- Resource Registry
- Session Lifecycle
- Session Manifest
- Persistence and Recovery
- Stage Executions
- Subagent Sessions
- Contract Pattern
- Versioning and Compatibility
- Deprecation Policy
- Capability Negotiation
- Dependency Resolution
- Validation Pipeline
- Cardinality and Activation
- Extension State
- Conformance and Testing
- Providers
- Provider Params
- Tools
- Hooks
- UI
- Loggers
- State Machines
- SM Stage Lifecycle
- Stage Definitions
- Commands
- Session Store
- Context Providers
- Settings Shape
- Trust Model
- Project Trust
- Extension Isolation
- Extension Integrity
- LLM Context Isolation
- Secrets Hygiene
- Security Modes
- Tool Approvals
- MCP Trust
- Sandboxing
- Configuration Scopes
- Project Root
- Extension Discovery
- Extension Installation
- Extension Reloading
- Headless and Interactor
- Determinism and Ordering
- Launch Arguments
- Network Policy
- Platform Integration
Tools
UI
Session Stores
Loggers
Providers
Hooks
Context Providers
Commands
- First Run
- Default Chat
- Tool Call Cycle
- Hook Interception
- Guard Deny Reproposal
- State Machine Workflow
- SM Stage Retry
- Hot Model Switch
- Capability Mismatch Switch
- Session Resume
- Session Resume Drift
- Approval and Auth
- Interaction Timeout
- Headless Run
- Parallel Tool Approvals
- Subagent Delegation
- Scope Layering
- Project First-Run Trust
- Reload Mid-Turn
- Compaction Warning
- MCP Remote Tool Call
- MCP Prompt Consume
- MCP Resource Bind
- MCP Reconnect