code_system_graph_hooks/types.rs
1use std::io;
2use std::path::PathBuf;
3use std::str::FromStr;
4
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7use thiserror::Error;
8
9/// Agent host targeted by an installation.
10#[derive(
11 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
12)]
13#[serde(rename_all = "snake_case")]
14#[non_exhaustive]
15pub enum HostKind {
16 /// Anthropic Claude Code.
17 ClaudeCode,
18 /// `OpenAI` Codex CLI.
19 Codex,
20 /// Google Gemini CLI.
21 Gemini,
22 /// Google Antigravity IDE.
23 Antigravity,
24 /// Cursor editor and CLI.
25 Cursor,
26}
27
28impl HostKind {
29 /// Returns the stable command-line and state-file identifier.
30 #[must_use]
31 pub const fn as_str(self) -> &'static str {
32 match self {
33 Self::ClaudeCode => "claude-code",
34 Self::Codex => "codex",
35 Self::Gemini => "gemini",
36 Self::Antigravity => "antigravity",
37 Self::Cursor => "cursor",
38 }
39 }
40}
41
42impl FromStr for HostKind {
43 type Err = HookError;
44
45 fn from_str(value: &str) -> Result<Self, Self::Err> {
46 match value {
47 "claude-code" => Ok(Self::ClaudeCode),
48 "codex" => Ok(Self::Codex),
49 "gemini" => Ok(Self::Gemini),
50 "antigravity" => Ok(Self::Antigravity),
51 "cursor" => Ok(Self::Cursor),
52 other => Err(HookError::UnknownHost(other.to_owned())),
53 }
54 }
55}
56
57/// Failure policy for installed integration.
58#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
59#[serde(rename_all = "snake_case")]
60#[non_exhaustive]
61pub enum HookMode {
62 /// Prompt routing and hook failures never block normal host operation.
63 #[default]
64 Advisory,
65 /// Prompt routing remains advisory, while the Git pre-commit gate fails closed.
66 Strict,
67}
68
69/// Complete request for installing, inspecting, or removing one host integration.
70#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
71pub struct InstallRequest {
72 /// Repository worktree root in which host files are managed.
73 pub root: PathBuf,
74 /// Host whose documented project configuration is targeted.
75 pub host: HostKind,
76 /// Advisory or strict pre-commit behavior.
77 #[serde(default)]
78 pub mode: HookMode,
79 /// `Code System Graph` executable used by the strict gate and to locate its sibling hook runtime.
80 pub code_system_graph_binary: PathBuf,
81 /// `Code System Graph` `SQLite` database passed to strict staged-change analysis.
82 pub database: PathBuf,
83 /// Registered `Code System Graph` workspace name.
84 pub workspace: String,
85 /// Registered repository alias for the selected root.
86 pub repository: String,
87 /// Whether installed guidance may recommend CodeGraph-backed local exploration.
88 #[serde(default)]
89 pub codegraph_enabled: bool,
90}
91
92/// Result of an installation attempt.
93#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
94pub struct InstallReport {
95 /// Whether any managed file changed.
96 pub changed: bool,
97 /// Host configuration or guidance file managed by this installation.
98 pub host_file: PathBuf,
99 /// Managed state file written with restrictive permissions.
100 pub state_file: PathBuf,
101 /// Repository-local Git ignore file when the root belongs to a Git worktree.
102 pub gitignore_path: Option<PathBuf>,
103 /// Whether installation added the generated-state rule.
104 pub gitignore_updated: bool,
105 /// Files whose previous contents were backed up.
106 pub backups: Vec<PathBuf>,
107 /// Non-fatal duplicate or compatibility observations.
108 pub warnings: Vec<String>,
109 /// Host limitation when a stable prompt-hook protocol cannot carry guidance.
110 pub limitation: Option<String>,
111}
112
113/// Current state of one host integration.
114#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
115pub struct HookStatus {
116 /// Whether all files required by the requested mode are present and marker-owned.
117 pub installed: bool,
118 /// Whether the host routing component is installed.
119 pub routing_installed: bool,
120 /// Whether the strict pre-commit component is installed.
121 pub strict_gate_installed: bool,
122 /// Host configuration or guidance path.
123 pub host_file: PathBuf,
124 /// State path used for installation metadata and runtime deduplication.
125 pub state_file: PathBuf,
126 /// Non-fatal duplicate product-hook observations.
127 pub warnings: Vec<String>,
128 /// Host limitation when a guidance file is used instead of a prompt hook.
129 pub limitation: Option<String>,
130}
131
132/// Result of removing one host integration.
133#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
134pub struct UninstallReport {
135 /// Whether any marker-owned content was removed.
136 pub changed: bool,
137 /// Backups created before merging marker-owned content out of existing files.
138 pub backups: Vec<PathBuf>,
139 /// Files deleted because they contained only marker-owned content.
140 pub removed_files: Vec<PathBuf>,
141 /// Non-fatal observations made while uninstalling.
142 pub warnings: Vec<String>,
143}
144
145/// Routing category inferred only from submitted prompt text.
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
147#[serde(rename_all = "snake_case")]
148#[non_exhaustive]
149pub enum RoutingIntent {
150 /// No repository-intelligence routing signal was found.
151 None,
152 /// Work is local to one repository.
153 LocalRepository,
154 /// Work spans repositories, contracts, architecture, impact, diffs, or pull requests.
155 Federated,
156}
157
158/// Input to the host-independent prompt router.
159#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
160pub struct RoutingRequest {
161 /// Host whose documented output shape should be generated.
162 pub host: HostKind,
163 /// Repository root used only as a deduplication scope.
164 pub root: PathBuf,
165 /// Raw host event. Only the top-level `prompt` and session identifier are read.
166 pub event: serde_json::Value,
167 /// Whether guidance may recommend CodeGraph-backed local exploration.
168 #[serde(default)]
169 pub codegraph_enabled: bool,
170 /// Session/repository deduplication lifetime in seconds.
171 #[serde(default = "default_ttl_seconds")]
172 pub ttl_seconds: u64,
173}
174
175/// Host-independent routing result.
176#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
177pub struct RoutingResponse {
178 /// Classified prompt intent.
179 pub intent: RoutingIntent,
180 /// Static guidance, omitted when no routing signal exists or TTL deduplication suppresses it.
181 pub guidance: Option<String>,
182 /// Whether a prior session/repository decision suppressed duplicate guidance.
183 pub deduplicated: bool,
184}
185
186/// Error returned by host installation and routing APIs.
187#[derive(Debug, Error)]
188#[non_exhaustive]
189pub enum HookError {
190 /// Filesystem operation failed.
191 #[error("filesystem operation failed for `{path}`: {source}")]
192 Io {
193 /// Affected path.
194 path: PathBuf,
195 /// Underlying I/O error.
196 #[source]
197 source: io::Error,
198 },
199 /// Host JSON configuration was malformed or had an incompatible shape.
200 #[error("invalid host configuration `{path}`: {message}")]
201 InvalidConfiguration {
202 /// Affected host file.
203 path: PathBuf,
204 /// Validation detail.
205 message: String,
206 },
207 /// Serialization failed.
208 #[error("JSON serialization failed: {0}")]
209 Json(#[from] serde_json::Error),
210 /// The repository root has no final component from which to derive an alias.
211 #[error("repository root `{0}` has no final path component")]
212 MissingRepositoryAlias(PathBuf),
213 /// System time is earlier than the Unix epoch.
214 #[error("system clock is earlier than the Unix epoch")]
215 InvalidSystemTime,
216 /// A command-line host identifier is unknown.
217 #[error("unknown host `{0}`")]
218 UnknownHost(String),
219 /// A routing event omitted a textual top-level prompt.
220 #[error("hook event does not contain a top-level string `prompt`")]
221 MissingPrompt,
222}
223
224pub(crate) const fn default_ttl_seconds() -> u64 {
225 300
226}