1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// SPDX-License-Identifier: Apache-2.0
//! Claude Code host facts the engine reads without the hook installers.
//!
//! - the governor setup arms and the rule block each writes (measured by
//! `context_overhead`, installed by `hooks::agents::claude_governor`) and
//! whether that setup is installed (checked by the standard installers);
//! - recognising lean-ctx's own status line command (HUD, statusline installer);
//! - the Claude Code mod sources the binary embeds (`rule_artifacts` mirrors
//! them, `hooks::agents::claude_mod` installs them).
/// The governor setup variant (owner decision O-2: both are measured).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GovernorArm {
/// No MCP server: 0 schema tokens, strong tools through Bash.
NoMcp,
/// MCP server without instructions, schemas deferred behind Tool Search.
McpDeferred,
}
impl GovernorArm {
/// `--mode` value → arm. `None` for every non-governor mode.
pub fn from_mode(raw: &str) -> Option<Self> {
match raw.trim().to_ascii_lowercase().as_str() {
"governor" | "governor-nomcp" => Some(Self::NoMcp),
"governor-mcp" => Some(Self::McpDeferred),
_ => None,
}
}
pub fn label(self) -> &'static str {
match self {
Self::NoMcp => "governor",
Self::McpDeferred => "governor-mcp",
}
}
}
const RULES_MCP: &str = "\
<!-- lean-ctx -->
<!-- lean-ctx-governor-v1 -->
lean-ctx keeps native Read/Bash results lossless; an identical repeat may show `[unchanged …]` (the full result is earlier in this conversation).
Optional `ctx_*` tools (e.g. ctx_compose for ranked files with source) load on demand via tool search.
<!-- /lean-ctx -->";
/// The rule block the arm writes — the only per-turn text it adds. The no-MCP
/// arm writes none.
pub fn rules_block(arm: GovernorArm) -> Option<&'static str> {
match arm {
GovernorArm::NoMcp => None,
GovernorArm::McpDeferred => Some(RULES_MCP),
}
}
/// True when the settings carry the governor's PostToolUse hook. Standard
/// installers and refreshes check this so they never fight the profile.
pub fn is_governor_installed(home: &std::path::Path) -> bool {
std::fs::read_to_string(
crate::core::editor_registry::claude_state_dir(home).join("settings.json"),
)
.is_ok_and(|c| c.contains(" hook govern\""))
}
/// `… lean-ctx … statusline [args]` — the binary may be quoted or portable.
pub fn is_lean_ctx_statusline(cmd: &str) -> bool {
statusline_args(cmd).is_some()
}
/// The part after the `statusline` subcommand (with its leading space), when
/// `cmd` runs lean-ctx's status line.
pub fn statusline_args(cmd: &str) -> Option<&str> {
let idx = cmd.find(" statusline")?;
let (bin, rest) = cmd.split_at(idx);
let rest = &rest[" statusline".len()..];
let ends_token = rest.is_empty() || rest.starts_with(' ');
(bin.contains("lean-ctx") && ends_token).then_some(rest)
}
pub const REGISTER_TS: &str = include_str!("../templates/claude_mod/register.ts");
pub const HOOKS_JSON: &str = include_str!("../templates/claude_mod/hooks.json");
/// The cockpit (sidebar, pulse line, overlays), imported by `register.ts`.
pub const COCKPIT_TSX: &str = include_str!("../templates/claude_mod/cockpit.tsx");
/// ctx_patch / ctx_read / ctx_search results drawn as diffs and code,
/// imported by `register.ts`.
pub const RENDER_TSX: &str = include_str!("../templates/claude_mod/render.tsx");
/// The `$.state` contract the manifest names as `types`.
pub const TYPES_DTS: &str = include_str!("../templates/claude_mod/cockpit.d.ts");
/// Source manifest; `hooks::agents::claude_mod::plugin_manifest` stamps the engine version into it.
pub const PLUGIN_JSON: &str = include_str!("../templates/claude_mod/plugin.json");