Install Atomic's hook group into the plugin hooks file, write the plugin manifest, and register the plugin in agy's import manifest. `config_dir` is agy's global config directory (`~/.gemini/config`); it is a parameter so tests can point at a temporary directory.
(config_dir: &Path, force: bool)
| 404 | /// `config_dir` is agy's global config directory (`~/.gemini/config`); |
| 405 | /// it is a parameter so tests can point at a temporary directory. |
| 406 | fn install_to(config_dir: &Path, force: bool) -> AgentResult<usize> { |
| 407 | let hooks_path = Self::hooks_path(config_dir); |
| 408 | let mut groups = Self::read_hooks_file(&hooks_path)?; |
| 409 | |
| 410 | // Get or create the "atomic" group as an object. |
| 411 | let group = groups |
| 412 | .entry(ATOMIC_GROUP.to_string()) |
| 413 | .or_insert_with(|| Value::Object(Map::new())); |
| 414 | if !group.is_object() { |
| 415 | // A non-object "atomic" group isn't ours — leave it untouched. |
| 416 | return Ok(0); |
| 417 | } |
| 418 | |
| 419 | if force { |
| 420 | remove_atomic_commands(group); |
| 421 | } |
| 422 | |
| 423 | let mut count = 0; |
| 424 | // (event key, verb) — lifecycle events take a direct handler list. |
| 425 | for (event, verb) in [("PreInvocation", "pre-invocation"), ("Stop", "stop")] { |
| 426 | let command = format!("{} {} || true", ATOMIC_HOOK_PREFIX, verb); |
| 427 | if add_direct_handler(group, event, &command) { |
| 428 | count += 1; |
| 429 | } |
| 430 | } |
| 431 | |
| 432 | // Tool events use matcher groups ("" matches all tools). |
| 433 | let command = format!("{} post-tool-use || true", ATOMIC_HOOK_PREFIX); |
| 434 | if add_matcher_handler(group, "PostToolUse", &command) { |
| 435 | count += 1; |
| 436 | } |
| 437 | |
| 438 | Self::write_json(&hooks_path, &Value::Object(groups))?; |
| 439 | |
| 440 | // Write the plugin manifest if missing (never clobber a user's). |
| 441 | let manifest_path = Self::plugin_dir(config_dir).join(PLUGIN_MANIFEST_FILE); |
| 442 | if !manifest_path.exists() { |
| 443 | std::fs::write(&manifest_path, PLUGIN_MANIFEST).map_err(|e| { |
| 444 | AgentError::ConfigError { |
| 445 | operation: "write plugin manifest".to_string(), |
| 446 | path: manifest_path.clone(), |
| 447 | reason: e.to_string(), |
| 448 | } |
| 449 | })?; |
| 450 | } |
| 451 | |
| 452 | // Bundle the code-intelligence skills so the agent learns the |
| 453 | // knowledge-graph-first discovery workflow. |
| 454 | Self::install_skills(config_dir)?; |
| 455 | |
| 456 | Self::register_import(config_dir)?; |
| 457 | |
| 458 | Ok(count) |
| 459 | } |
| 460 | |
| 461 | /// Write the bundled skills into the plugin's `skills/` directory. |
| 462 | fn install_skills(config_dir: &Path) -> AgentResult<()> { |
nothing calls this directly
no test coverage detected