Skip to content

Commit 4dc7f62

Browse files
authored
feat(brand): rebrand user-facing surfaces to Coven + direct-invocation notice (Phase 5) (#154)
* feat(brand): rebrand user-facing surfaces to Coven; add direct-invocation engine notice Renames the ~17 user-facing "Coven Code" strings (system prompt identity, TUI welcome/title/dialogs, User-Agent, help text, npm description) to the unified "Coven" brand. Adds a dim one-line notice when coven-code is run directly in an interactive TTY without COVEN_PARENT, pointing users at the unified `coven` CLI. Binary name, crate names, env vars, and internal paths are unchanged. * feat(brand): complete user-facing Coven Code -> Coven sweep (commands, tips, dialogs, oauth, acp) * docs: add Phase 5 brand-sweep plan * test(tui): track renamed boot banner 'Coven v' in interactive suite
1 parent c20ee27 commit 4dc7f62

31 files changed

Lines changed: 191 additions & 112 deletions
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Phase 5 — Brand / UX Naming Sweep
2+
3+
> Phase 5 sub-plan of the coven CLI unification. Curated, recon-grounded. Only USER-FACING surfaces change; the binary name (`coven-code`/`coven-cave`), `claurst-*` crates, `COVEN_CODE_*` env vars, `.coven-code` internal paths, and repo URLs STAY (per COVEN.md).
4+
5+
**Goal:** users see the unified "Coven" brand; nobody is told to install/use "coven-code" separately; `coven --version` surfaces the whole stack.
6+
7+
## Decisions (defaults — vetoable in review)
8+
- npm package NAME stays `@opencoven/coven-code` (only description changes; it is DEPRECATED in 5.6, not renamed — renaming a published package is disruptive and breaks the engine install/release flow).
9+
- Assistant identity → "You are Coven, an open-source agentic coding assistant by OpenCoven (based on Claurst, GPL-3.0)." (keep the credit).
10+
- User-Agent `CovenCode/x.y``Coven/x.y`.
11+
- Direct-invocation notice (dim, stderr): `coven-code is the Coven engine — the supported CLI is 'coven' (npm i -g @opencoven/cli)`.
12+
13+
## Already done (verified — no-op tasks)
14+
- **Theme (5.4):** `crates/tui/src/theme_colors.rs default_theme()` is ALREADY the OpenCoven palette (accent `Rgb(139,92,246)` #8B5CF6, secondary `Rgb(236,72,153)` #EC4899). Nothing to change.
15+
- **Completions (5.5):** `coven completions` generates from the clap `Command`, so the passthroughs (auth/models/acp/code) are already covered. Add a coven-side test asserting they appear (optional).
16+
17+
## Task 5.1 + 5.2 — coven-code brand sweep [coven-code]
18+
### 5.2 — rename the ~17 user-facing strings (curated list; do NOT touch anything else)
19+
- `crates/core/src/system_prompt.rs:174,177` — "You are Coven Code, ..." → "You are Coven, ..." (keep the "(based on Claurst.../GPL-3.0)" credit).
20+
- `crates/cli/src/main.rs:123``about = "Coven Code - AI-powered coding assistant"``"Coven - AI-powered coding assistant"`. `:4689` — auth help "...Coven Code OAuth client" → "...Coven OAuth client".
21+
- `crates/tools/src/web_search.rs:163``"CovenCode/1.0"``"Coven/1.0"`.
22+
- `crates/core/src/update_check.rs:67``format!("CovenCode/{}", current)``format!("Coven/{}", current)`.
23+
- `crates/tui/src/onboarding_dialog.rs:304,320,333,337` — "Welcome to Coven Code" / "Coven Code is..." / "Coven Code can..." → "Coven".
24+
- `crates/tui/src/lib.rs:345,371` — terminal title `"✨ Coven Code"``"✨ Coven"`.
25+
- `crates/tui/src/invalid_config_dialog.rs:159,164,168` — "Restart Coven Code." → "Restart Coven.".
26+
- `crates/tui/src/export_dialog.rs:171,176` — "# Coven Code Conversation Export" → "# Coven Conversation Export"; the `"**Coven Code**"` assistant role label → "**Coven**".
27+
- `crates/tui/src/feedback_survey.rs:146` — "How is Coven Code doing this session?" → "How is Coven doing this session?".
28+
- `npm/package.json:4` — description: replace "Coven engine (agentic coding TUI)..." wording's "Coven Code" (if present) — keep pointing at @opencoven/cli. NAME unchanged.
29+
DO NOT change: binary/crate/env/path/theme-const/test/comment/repo-URL references.
30+
31+
### 5.1 — direct-invocation notice [coven-code]
32+
In `crates/cli/src/main.rs` after `Cli::parse()` (~line 503) and after the `is_headless` determination (~643): if the run is INTERACTIVE (not headless/print/prompt), stdout/stderr is a TTY (`crossterm::terminal::is_terminal`), AND `COVEN_PARENT` is unset → `eprintln!` the dim notice once (before `run_interactive`). Silent when `COVEN_PARENT` is set (driven by coven) or non-interactive. The `coven-cave` alias re-execs `coven-code`, so it inherits this. Test the pure decision (`should_show_engine_notice(is_interactive, is_tty, coven_parent_set) -> bool`).
33+
34+
## Task 5.3 — `coven --version` surfaces engine + pin [coven] — IN PROGRESS
35+
Custom `--version` intercept → `coven <desc> (engine coven-code <installed|not installed>, pinned <pinned>)`. (dispatched)
36+
37+
## Task 5.6 — npm deprecation [HELD — outward-facing, needs explicit user go-ahead]
38+
`npm deprecate @opencoven/coven-code "Install @opencoven/cli — coven-code is now the Coven engine"`. Engine binaries keep shipping via GitHub Releases. DO NOT run autonomously; surface for confirmation.
39+
40+
## Exit check
41+
A new user reaches install → first session without seeing the string "coven-code" in any prose surface; running `coven-code` directly prints the "use coven" hint; `coven --version` shows coven + engine + pin.

scripts/tui-tests/cases/02_startup.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ tc_startup() {
1515
wait_for "What's new" 5
1616
local s; s="$(tui_capture)"
1717

18-
assert_contains "$s" "Coven Code v" "title banner renders with version"
18+
assert_contains "$s" "Coven v" "title banner renders with version"
1919

2020
# Right-hand welcome column. Wording is stable; the username is not, so we
2121
# assert on the static labels only.

scripts/tui-tests/cases/06_quit.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ tc_quit() {
1616

1717
# Prove the process actually exited by dropping a marker on the shell that
1818
# is revealed once the TUI tears down.
19-
if wait_for "Coven Code v" 2; then
19+
if wait_for "Coven v" 2; then
2020
: # still showing — fall through to explicit check below
2121
fi
2222
tui_type "echo __TUI_EXITED__"; tui_keys Enter

scripts/tui-tests/lib.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ set -uo pipefail
2020
: "${TUI_SOCKET:=coven-tui-test}" # dedicated tmux server socket (-L)
2121
: "${TUI_WIDTH:=120}" # terminal columns
2222
: "${TUI_HEIGHT:=40}" # terminal rows
23-
: "${TUI_BOOT_STRING:=Coven Code v}" # string proving the TUI has drawn
23+
: "${TUI_BOOT_STRING:=Coven v}" # string proving the TUI has drawn
2424
: "${TUI_WAIT_TIMEOUT:=20}" # seconds to wait for a string
2525
: "${TUI_POLL_INTERVAL:=0.4}" # seconds between capture polls
2626
: "${TUI_SETTLE:=0.6}" # seconds to let a keypress redraw

src-rust/crates/acp/src/server.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ impl AgentServer {
136136
*self.client_capabilities.write() = req.client_capabilities.clone();
137137

138138
let agent_info = acp::Implementation::new("coven-code", env!("CARGO_PKG_VERSION"))
139-
.title(Some("Coven Code".to_string()));
139+
.title(Some("Coven".to_string()));
140140

141141
let mut response = acp::InitializeResponse::new(acp::ProtocolVersion::V1)
142142
.agent_capabilities(

src-rust/crates/cli/src/codex_oauth_flow.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ async fn wait_for_callback(listener: TcpListener) -> anyhow::Result<(String, Str
166166
// Send HTML response to browser before processing
167167
let html = if error.is_empty() {
168168
"<html><body style='background:#131010;color:#f1ecec;display:flex;justify-content:center;align-items:center;height:100vh;font-family:system-ui'>\
169-
<div style='text-align:center'><h1>Authorization Successful</h1><p>You can close this window and return to Coven Code.</p></div>\
169+
<div style='text-align:center'><h1>Authorization Successful</h1><p>You can close this window and return to Coven.</p></div>\
170170
<script>setTimeout(()=>window.close(),2000)</script></body></html>"
171171
} else {
172172
"<html><body style='background:#131010;color:#f1ecec;display:flex;justify-content:center;align-items:center;height:100vh;font-family:system-ui'>\

src-rust/crates/cli/src/headless.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1544,7 +1544,7 @@ pub fn write_result(path: &Path, envelope: &ResultEnvelope) -> anyhow::Result<()
15441544
fn familiar_name(brief: Option<&SessionBrief>) -> String {
15451545
brief
15461546
.map(|b| b.familiar.display_name.clone())
1547-
.unwrap_or_else(|| "Coven Code".to_string())
1547+
.unwrap_or_else(|| "Coven".to_string())
15481548
}
15491549

15501550
/// One-line, familiar-voice summary (Check Run + PR title). Prefers the first

src-rust/crates/cli/src/main.rs

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ impl Tool for McpToolWrapper {
120120
#[command(
121121
name = "coven-code",
122122
version = APP_VERSION,
123-
about = "Coven Code - AI-powered coding assistant",
123+
about = "Coven - AI-powered coding assistant",
124124
long_about = None,
125125
)]
126126
struct Cli {
@@ -199,7 +199,7 @@ struct Cli {
199199
#[arg(long = "auto-commits", action = ArgAction::SetTrue)]
200200
auto_commits: bool,
201201

202-
/// Grant Coven Code access to an additional directory (can be repeated)
202+
/// Grant Coven access to an additional directory (can be repeated)
203203
#[arg(long = "add-dir", value_name = "DIR", action = ArgAction::Append)]
204204
add_dir: Vec<PathBuf>,
205205

@@ -998,6 +998,16 @@ async fn main() -> anyhow::Result<()> {
998998
}
999999

10001000
// Interactive REPL mode
1001+
if should_show_engine_notice(
1002+
!is_headless,
1003+
std::io::IsTerminal::is_terminal(&std::io::stderr()),
1004+
std::env::var_os("COVEN_PARENT").is_some(),
1005+
) {
1006+
eprintln!(
1007+
"\x1b[2mcoven-code is the Coven engine \u{2014} the supported CLI is 'coven' (npm i -g @opencoven/cli)\x1b[0m"
1008+
);
1009+
}
1010+
10011011
let auth_store = claurst_core::AuthStore::load();
10021012
let has_saved_credentials = !auth_store.credentials.is_empty()
10031013
|| claurst_core::oauth_config::get_codex_tokens().is_some();
@@ -1356,10 +1366,7 @@ fn spawn_models_cache_refresh() {
13561366
let url = models_source_url();
13571367
let resp = match client
13581368
.get(&url)
1359-
.header(
1360-
"User-Agent",
1361-
concat!("CovenCode/", env!("CARGO_PKG_VERSION")),
1362-
)
1369+
.header("User-Agent", concat!("Coven/", env!("CARGO_PKG_VERSION")))
13631370
.send()
13641371
.await
13651372
{
@@ -4686,7 +4693,9 @@ async fn handle_auth_command(args: &[String]) -> anyhow::Result<()> {
46864693

46874694
fn print_auth_usage() {
46884695
eprintln!("Usage: coven-code auth <subcommand>");
4689-
eprintln!(" login [--console] [--label <name>] Authenticate with a configured Coven Code OAuth client");
4696+
eprintln!(
4697+
" login [--console] [--label <name>] Authenticate with a configured Coven OAuth client"
4698+
);
46904699
eprintln!(" logout Remove the active account's credentials");
46914700
eprintln!(" status [--json] Show authentication status");
46924701
eprintln!(" list List all stored Anthropic accounts");
@@ -5028,7 +5037,7 @@ async fn auth_status(json_output: bool) {
50285037
.or_else(|| {
50295038
usable_oauth_tokens.map(|tokens| {
50305039
if tokens.uses_bearer_auth() {
5031-
"Coven Code Account".to_string()
5040+
"Coven Account".to_string()
50325041
} else {
50335042
"Console Account".to_string()
50345043
}
@@ -5208,6 +5217,12 @@ fn json_null_or_string(opt: &Option<String>) -> serde_json::Value {
52085217
}
52095218
}
52105219

5220+
/// Show the "use coven" hint only for a direct interactive run that is NOT
5221+
/// driven by the unified coven CLI.
5222+
fn should_show_engine_notice(is_interactive: bool, is_tty: bool, coven_parent_set: bool) -> bool {
5223+
is_interactive && is_tty && !coven_parent_set
5224+
}
5225+
52115226
#[cfg(test)]
52125227
mod tests {
52135228
use super::*;
@@ -5579,4 +5594,30 @@ mod tests {
55795594
assert!(cli.print);
55805595
assert_eq!(cli.prompt.as_deref(), Some("hello"));
55815596
}
5597+
5598+
// --- should_show_engine_notice truth table ---
5599+
5600+
#[test]
5601+
fn engine_notice_shown_for_direct_interactive_tty() {
5602+
// All three conditions met: interactive + TTY + no COVEN_PARENT
5603+
assert!(should_show_engine_notice(true, true, false));
5604+
}
5605+
5606+
#[test]
5607+
fn engine_notice_suppressed_when_coven_parent_set() {
5608+
// Driven by the unified coven CLI
5609+
assert!(!should_show_engine_notice(true, true, true));
5610+
}
5611+
5612+
#[test]
5613+
fn engine_notice_suppressed_when_non_interactive() {
5614+
// Headless/print/prompt mode
5615+
assert!(!should_show_engine_notice(false, true, false));
5616+
}
5617+
5618+
#[test]
5619+
fn engine_notice_suppressed_when_not_a_tty() {
5620+
// Piped stdout/stderr (e.g. CI, script)
5621+
assert!(!should_show_engine_notice(true, false, false));
5622+
}
55825623
}

src-rust/crates/cli/src/oauth_flow.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ fn configured_anthropic_oauth_client_id() -> anyhow::Result<String> {
184184
.filter(|value| !value.is_empty())
185185
.ok_or_else(|| {
186186
anyhow::anyhow!(
187-
"Anthropic OAuth requires a Coven Code OAuth client ID. Set {} after registering a first-party OAuth client, or use ANTHROPIC_API_KEY for now.",
187+
"Anthropic OAuth requires a Coven OAuth client ID. Set {} after registering a first-party OAuth client, or use ANTHROPIC_API_KEY for now.",
188188
oauth::CLIENT_ID_ENV
189189
)
190190
})

0 commit comments

Comments
 (0)