starship/starship
Statusline integration
The starship statusline subcommand renders prompts for non-shell statuslines. Today it has one provider: Claude Code.
Flow
sequenceDiagram
participant ClaudeCode as Claude Code
participant Main as src/main.rs
participant Print as src/print.rs
participant Statusline as src/utils/statusline.rs
participant Context
ClaudeCode->>Main: pipes JSON | starship statusline claude-code
Main->>Print: prompt_with_claude_code(props, Target::Profile("claude-code"))
Print->>Statusline: serde_json::from_reader(stdin) → ClaudeCodeData
Print->>Context: Context::new(...)
Print->>Context: with_claude_code_data(data)
Note over Context: shell = Shell::Unknown (no real shell)
Print->>Print: get_prompt(context)
Note over Print: target picks up<br/>internal_profiles["claude-code"]
Print-->>ClaudeCode: rendered stringData shape
ClaudeCodeData and friends are defined in src/utils/statusline.rs:
pub struct ClaudeCodeData {
pub cwd: Option<String>,
pub model: ModelInfo,
pub context_window: ContextWindow,
pub cost: Option<CostInfo>,
pub workspace: Option<Workspace>,
}All deserialization fields use #[serde(default)] so missing fields don't fail parsing. If stdin is empty or invalid, serde_json::from_reader returns an error; prompt_with_claude_code logs it at error level and falls back to ClaudeCodeData::default(), which means every Claude module returns None and the prompt renders as if Claude data is absent.
Built-in profile
The "claude-code" profile is registered in default_profiles() in src/configs/starship_root.rs:
pub fn default_profiles() -> IndexMap<String, String> {
IndexMap::from_iter([(
"claude-code".to_string(),
"$claude_model$git_branch$claude_context$claude_cost".to_string(),
)])
}It is stored separately from the user's [profiles] table on StarshipRootConfig:
pub user_profiles: IndexMap<String, String>, // from TOML
#[serde(skip)]
pub internal_profiles: IndexMap<String, String>, // hardcoded defaultsThe renderer in print::load_formatter_and_modules looks up Target::Profile(name) first in user_profiles, then in internal_profiles. Users can override the built-in by defining [profiles] claude-code = "..." in their starship.toml — see the test_prefer_user_profile test in src/print.rs.
Why shell = Unknown
The Claude Code statusline is not a real shell. There is no PROMPT_COMMAND, no cwd from process state, no SSH session, and no signal-handling. Setting context.shell = Shell::Unknown (which prompt_with_claude_code does explicitly) prevents shell-specific code paths in modules like character (which has vi-mode handling per shell) from misfiring.
Provider extensibility
The provider is a clap-derive enum:
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
enum Statuslines {
#[clap(alias = "claude")]
ClaudeCode,
}Adding a second provider (say "cursor" for Cursor's status line) means:
- Add a variant to
Statuslines. - Add a JSON shape in
src/utils/statusline.rs(and re-export fromsrc/context.rs). - Add
cwd: ...field onContextif needed (currently onlyclaude_code_datalives there; you'd add<provider>_data: Option<...>). - Add a built-in profile in
default_profiles. - Wire the new provider in the match block of
Commands::Statuslineinsrc/main.rs. - Write modules that read the new context field.
Entry points for modification
- Override the built-in
claude-codeprofile: define[profiles] claude-code = "..."in yourstarship.toml. - Disable the timer/cost separately: set
[claude_cost] disabled = trueand similar in yourstarship.toml. - Pipe in a different JSON shape: see provider extensibility above.
- Stop reading stdin: replace the
serde_json::from_reader(stdin)call inprompt_with_claude_codewith a directClaudeCodeData::default()(only useful for testing).
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.