use std::path::{Path, PathBuf};
use anyhow::Result;
use crate::core::entity::{Instance, Organization};
mod args;
mod context;
mod headless;
mod issue;
mod paths;
mod resolve;
mod tui;
pub use headless::Linear;
pub use issue::issue_key;
pub trait Host {
type Linear: Linear;
async fn linear(&self) -> Result<Self::Linear>;
fn workspace_of(&self, cwd: &Path) -> PathBuf;
fn instances(&self, workspace: &Path) -> Result<Vec<Instance>>;
fn snapshot_file(&self, workspace: &Path, pid: u32) -> Result<PathBuf>;
fn state_dir(&self) -> Result<PathBuf>;
fn acting_organization(&self) -> Option<Organization>;
fn now(&self) -> u64;
}
pub const USAGE: &str = "\
linear-tui — a terminal UI for Linear
Usage:
linear-tui Open the TUI (sets up credentials on first run)
linear-tui open <ID> Open the TUI on an issue (ENG-123 or its URL)
linear-tui --headless [--size <W>x<H>]
Run the TUI with no terminal, for agents to drive
linear-tui auth login Sign in to a workspace through the browser
linear-tui auth list List the signed-in workspaces
linear-tui auth switch <workspace>
Use another signed-in workspace (by URL key or name)
linear-tui auth status Show which credentials are in use
linear-tui auth token <key> Sign in with a personal API key
linear-tui auth logout [<workspace>] [--all]
Forget a workspace's token (--all: every token and the API key)
linear-tui auth set-oauth <client-id> [client-secret]
Authorize against your own Linear application
For agents and scripts (Markdown by default, --json for JSON):
linear-tui context [--json] [--workspace <path>]
What linear-tui is showing in this repository
linear-tui issue show <ID> [--json]
An issue with its description and comments
linear-tui issue create --team <key> --title <text> [--description <text>]
[--priority <urgent|high|medium|low|none>]
[--assignee <me|name|email>] [--estimate <n>]
[--label <name>]... [--project <name>]
[--cycle <name|current>] [--parent <ID>] [--json]
linear-tui issue update <ID> [--title <text>] [--description <text>]
[--priority <level>] [--assignee <me|name|email|none>]
[--estimate <n|none>] [--label <name>]... [--unlabel <name>]...
[--project <name|none>] [--cycle <name|current|none>]
[--parent <ID|none>] [--json]
linear-tui issue comment <ID> <body> [--reply-to <comment-id>] [--json]
linear-tui issue comment edit <ID> <comment-id> <body> [--json]
linear-tui issue comment delete <ID> <comment-id> [--json]
linear-tui issue status <ID> <state> [--json]
linear-tui tui screen [--json]
The screen of the linear-tui running here
linear-tui tui press <keys> Press keys in it (`g m`, `<Enter>`, `<C-k>`)
linear-tui tui type <text> Type into the field that has focus
linear-tui tui run <title> Run a command palette entry by its title
linear-tui tui open <ID> Open an issue in it
linear-tui tui quit Quit it
linear-tui paths [--json] Where the config and the state (view snapshots) live
<ID> is an identifier (ENG-123) or an issue URL. A <body> or <text> of `-`
is read from stdin.
";
pub async fn handle_subcommand(args: &[String], host: &impl Host) -> Result<()> {
match args[0].as_str() {
"context" => context::run(&args[1..], host),
"tui" => tui::run(&args[1..], host).await,
"issue" => issue::run(&args[1..], host).await,
"paths" => paths::run(&args[1..], host),
"help" | "--help" | "-h" => {
print!("{USAGE}");
Ok(())
}
"--version" | "-V" => {
println!("linear-tui {}", env!("CARGO_PKG_VERSION"));
Ok(())
}
other => {
eprint!("{USAGE}");
anyhow::bail!("unknown command: {other}")
}
}
}