Skip to content

Commit 06a241c

Browse files
authored
feat: daemon integration, walking familiar animation, glyph alignment fix (#1)
Co-authored-by: Nova <nova@opencoven.ai> - Integrate Coven daemon state (familiars, skills) - feat: walking familiar animation in welcome panel - fix: normalize all familiar glyph rows to 11 chars - chore(release): stamp v0.0.9
1 parent b540899 commit 06a241c

14 files changed

Lines changed: 396 additions & 47 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
**Coven Code** is an open-source, multi-provider agentic coding TUI built in Rust. It is maintained by [OpenCoven](https://opencoven.ai) as a GPL-3.0 fork of [Claurst](https://github.com/Kuberwastaken/claurst) by Kuber Mehta.
44

5-
> **Attribution:** Coven Code is derived from Claurst v0.0.8 under the GNU General Public License v3.0. The full license is in [`LICENSE.md`](LICENSE.md) and upstream attribution is in [`ATTRIBUTION.md`](ATTRIBUTION.md).
5+
> **Attribution:** Coven Code is derived from Claurst v0.0.9 under the GNU General Public License v3.0. The full license is in [`LICENSE.md`](LICENSE.md) and upstream attribution is in [`ATTRIBUTION.md`](ATTRIBUTION.md).
66
77
---
88

@@ -16,7 +16,7 @@ Multi-provider terminal coding agent with a rich ratatui TUI: chat forking, memo
1616

1717
## Status
1818

19-
> **Beta (v0.0.8).** Core agent, multi-provider routing, and TUI are stable for daily use. Experimental features are flagged below.
19+
> **Beta (v0.0.9).** Core agent, multi-provider routing, and TUI are stable for daily use. Experimental features are flagged below.
2020
2121
Recent highlights:
2222
- **/share** — share sessions via unlisted GitHub Gists `[EXPERIMENTAL]`

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
Coven Code is a high-performance Rust reimplementation of Claude Code — a terminal-native AI coding agent with streaming responses, 40+ built-in tools, 15+ LLM provider integrations, a full ratatui TUI, and an extensible plugin system.
88

9-
**Version:** 0.0.8 (Beta) · **License:** GPL-3.0 · [GitHub](https://github.com/OpenCoven/coven-code)
9+
**Version:** 0.0.9 (Beta) · **License:** GPL-3.0 · [GitHub](https://github.com/OpenCoven/coven-code)
1010

1111
</div>
1212

docs/installation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ coven-code --version
170170
A successful installation prints the version string, for example:
171171

172172
```
173-
coven-code 0.0.8
173+
coven-code 0.0.9
174174
```
175175

176176
To confirm the binary is the one you installed:

npm/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@opencoven/coven-code",
3-
"version": "0.0.8",
3+
"version": "0.0.9",
44
"description": "Open-source, multi-provider agentic coding TUI for the terminal \u2014 OpenCoven fork of Claurst",
55
"license": "GPL-3.0-only",
66
"repository": {

src-rust/Cargo.lock

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-rust/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ members = [
1616
]
1717

1818
[workspace.package]
19-
version = "0.0.8"
19+
version = "0.0.9"
2020
edition = "2021"
2121
authors = ["OpenCoven (based on Claurst by Kuber Mehta)"]
2222
license = "GPL-3.0"

src-rust/crates/acp/registry-template/agent.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"id": "coven-code",
33
"name": "Coven Code",
4-
"version": "0.0.8",
4+
"version": "0.0.9",
55
"description": "Open-source, multi-provider agentic coding TUI built in Rust (OpenCoven fork of Coven Code). Multi-provider routing (Anthropic, OpenAI, Google, Groq, Ollama, OpenAI-compatible, \u2026), full MCP support, plugin system, companion mascot, chat forking, memory consolidation. No telemetry.",
66
"repository": "https://github.com/OpenCoven/coven-code",
77
"website": "https://opencoven.ai",
@@ -18,35 +18,35 @@
1818
"distribution": {
1919
"binary": {
2020
"darwin-aarch64": {
21-
"archive": "https://github.com/OpenCoven/coven-code/releases/download/v0.0.8/coven-code-macos-aarch64.tar.gz",
21+
"archive": "https://github.com/OpenCoven/coven-code/releases/download/v0.0.9/coven-code-macos-aarch64.tar.gz",
2222
"cmd": "./coven-code",
2323
"args": [
2424
"acp"
2525
]
2626
},
2727
"darwin-x86_64": {
28-
"archive": "https://github.com/OpenCoven/coven-code/releases/download/v0.0.8/coven-code-macos-x86_64.tar.gz",
28+
"archive": "https://github.com/OpenCoven/coven-code/releases/download/v0.0.9/coven-code-macos-x86_64.tar.gz",
2929
"cmd": "./coven-code",
3030
"args": [
3131
"acp"
3232
]
3333
},
3434
"linux-aarch64": {
35-
"archive": "https://github.com/OpenCoven/coven-code/releases/download/v0.0.8/coven-code-linux-aarch64.tar.gz",
35+
"archive": "https://github.com/OpenCoven/coven-code/releases/download/v0.0.9/coven-code-linux-aarch64.tar.gz",
3636
"cmd": "./coven-code",
3737
"args": [
3838
"acp"
3939
]
4040
},
4141
"linux-x86_64": {
42-
"archive": "https://github.com/OpenCoven/coven-code/releases/download/v0.0.8/coven-code-linux-x86_64.tar.gz",
42+
"archive": "https://github.com/OpenCoven/coven-code/releases/download/v0.0.9/coven-code-linux-x86_64.tar.gz",
4343
"cmd": "./coven-code",
4444
"args": [
4545
"acp"
4646
]
4747
},
4848
"windows-x86_64": {
49-
"archive": "https://github.com/OpenCoven/coven-code/releases/download/v0.0.8/coven-code-windows-x86_64.zip",
49+
"archive": "https://github.com/OpenCoven/coven-code/releases/download/v0.0.9/coven-code-windows-x86_64.zip",
5050
"cmd": "./coven-code.exe",
5151
"args": [
5252
"acp"

src-rust/crates/commands/src/lib.rs

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8560,6 +8560,48 @@ const FAMILIAR_ROSTER: &[(&str, &str)] = &[
85608560
("echo", "👻 Memory familiar — round ghost, mirror eyes, echo trail"),
85618561
];
85628562

8563+
/// Merge daemon-declared familiars (`~/.coven/familiars.toml`) with the
8564+
/// hardcoded TUI roster. Daemon entries take precedence; hardcoded entries
8565+
/// for names not declared by the daemon remain selectable so the bundled
8566+
/// mascot art keeps working when the daemon isn't installed.
8567+
fn current_familiar_roster() -> Vec<(String, String)> {
8568+
let mut seen = std::collections::HashSet::new();
8569+
let mut out: Vec<(String, String)> = Vec::new();
8570+
8571+
if let Some(daemon) = claurst_core::coven_shared::load_familiars() {
8572+
for f in daemon {
8573+
let desc = format_daemon_familiar(&f);
8574+
seen.insert(f.id.clone());
8575+
out.push((f.id, desc));
8576+
}
8577+
}
8578+
8579+
for (name, desc) in FAMILIAR_ROSTER {
8580+
if !seen.contains(*name) {
8581+
out.push(((*name).to_string(), (*desc).to_string()));
8582+
}
8583+
}
8584+
out
8585+
}
8586+
8587+
fn format_daemon_familiar(f: &claurst_core::coven_shared::CovenFamiliar) -> String {
8588+
let emoji = f.emoji.as_deref().unwrap_or("").trim();
8589+
let role = f.role.as_deref().unwrap_or("").trim();
8590+
let desc = f.description.as_deref().unwrap_or("").trim();
8591+
let mut parts: Vec<String> = Vec::new();
8592+
if !emoji.is_empty() {
8593+
parts.push(emoji.to_string());
8594+
}
8595+
let tail = match (role.is_empty(), desc.is_empty()) {
8596+
(true, true) => f.display_name.clone().unwrap_or_else(|| f.id.clone()),
8597+
(false, true) => role.to_string(),
8598+
(true, false) => desc.to_string(),
8599+
(false, false) => format!("{} — {}", role, desc),
8600+
};
8601+
parts.push(tail);
8602+
parts.join(" ")
8603+
}
8604+
85638605
/// Infer a familiar from the system username — maps known coven member names
85648606
/// to their canonical familiar. Falls back to None (kitty default).
85658607
fn infer_familiar_from_env() -> Option<String> {
@@ -8602,16 +8644,27 @@ impl SlashCommand for FamiliarCommand {
86028644

86038645
async fn execute(&self, args: &str, ctx: &mut CommandContext) -> CommandResult {
86048646
let arg = args.trim().to_lowercase();
8647+
let roster = current_familiar_roster();
8648+
let from_daemon = claurst_core::coven_shared::coven_home().is_some()
8649+
&& claurst_core::coven_shared::load_familiars().is_some();
86058650

86068651
if arg.is_empty() {
86078652
// Show current + roster.
86088653
let current = ctx.config.familiar.as_deref().unwrap_or("kitty");
86098654
let auto_hint = infer_familiar_from_env()
86108655
.map(|f| format!(" (auto-detect suggests: {})", f))
86118656
.unwrap_or_default();
8612-
let mut out = format!("Current familiar: {}{}\n\nRoster:\n", current, auto_hint);
8613-
for (name, desc) in FAMILIAR_ROSTER {
8614-
let marker = if *name == current { " ◀" } else { "" };
8657+
let source = if from_daemon {
8658+
" (roster from ~/.coven/familiars.toml)"
8659+
} else {
8660+
""
8661+
};
8662+
let mut out = format!(
8663+
"Current familiar: {}{}\n\nRoster{}:\n",
8664+
current, auto_hint, source
8665+
);
8666+
for (name, desc) in &roster {
8667+
let marker = if name == current { " ◀" } else { "" };
86158668
out.push_str(&format!(" {:8} {}{}", name, desc, marker));
86168669
out.push('\n');
86178670
}
@@ -8624,10 +8677,10 @@ impl SlashCommand for FamiliarCommand {
86248677
None
86258678
} else if arg == "auto" {
86268679
infer_familiar_from_env()
8627-
} else if FAMILIAR_ROSTER.iter().any(|(n, _)| *n == arg) {
8680+
} else if roster.iter().any(|(n, _)| n == &arg) {
86288681
Some(arg.clone())
86298682
} else {
8630-
let names: Vec<&str> = FAMILIAR_ROSTER.iter().map(|(n, _)| *n).collect();
8683+
let names: Vec<&str> = roster.iter().map(|(n, _)| n.as_str()).collect();
86318684
return CommandResult::Error(format!(
86328685
"Unknown familiar '{}'. Choose from: {}",
86338686
arg,
@@ -8649,7 +8702,7 @@ impl SlashCommand for FamiliarCommand {
86498702
Some(name) => format!(
86508703
"Familiar set to {}. {} ",
86518704
name,
8652-
FAMILIAR_ROSTER.iter().find(|(n, _)| n == name).map(|(_, d)| *d).unwrap_or("")
8705+
roster.iter().find(|(n, _)| n == name).map(|(_, d)| d.as_str()).unwrap_or("")
86538706
),
86548707
None => "Familiar reset to default (kitty).".to_string(),
86558708
};

0 commit comments

Comments
 (0)