Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
7770f9f
feat(plugins): Phase 1 plugin manifest discovery and loader infrastru…
Zetkolink Apr 9, 2026
f94c966
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 9, 2026
9b78b8c
feat(plugins): Phase 2-10 implementation (partial) and plumbing
Zetkolink Apr 9, 2026
864fafa
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 9, 2026
e5e92f6
[autofix.ci] apply automated fixes (attempt 2/3)
autofix-ci[bot] Apr 9, 2026
8eed374
feat(plugins): Wave A — Phase 9.5 :plugin install subcommand
Zetkolink Apr 9, 2026
73adb8a
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 9, 2026
9864980
feat(plugins): Wave B — Phase 6A Notification + Phase 6B Setup
Zetkolink Apr 9, 2026
af4e7e0
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 9, 2026
1993ddc
feat(plugins): Wave C — Phase 6C ConfigWatcher with notify-debouncer-…
Zetkolink Apr 9, 2026
af533c3
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 9, 2026
3b57cb1
feat(plugins): Wave D Pass 1 — Phase 6D InstructionsLoaded minimal fire
Zetkolink Apr 9, 2026
91d67c1
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 9, 2026
5bf3352
feat(plugins): Wave E-1a — Phase 7A Subagent fire sites (SubagentStar…
Zetkolink Apr 9, 2026
4f22a97
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 9, 2026
5a9ca5e
feat(plugins): Wave E-1b — Phase 7B Permission fire sites + aggregate…
Zetkolink Apr 9, 2026
c6725fe
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 9, 2026
de48157
feat(plugins): Wave E-2a — Phase 7C FileChangedWatcher + fs_watcher_c…
Zetkolink Apr 9, 2026
de17342
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 9, 2026
159dd26
feat(plugins): Wave E-2b — Phase 7C dynamic watch_paths from SessionS…
Zetkolink Apr 9, 2026
da4373f
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 9, 2026
dd8021b
feat(plugins): Wave E-2c-i — Phase 7D minimal WorktreeCreate fire site
Zetkolink Apr 9, 2026
5e61a15
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 9, 2026
9f045c4
feat(plugins): Wave F-1 — Phase 8 ElicitationDispatcher infrastructure
Zetkolink Apr 9, 2026
0a7ce5f
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 9, 2026
8422944
feat(plugins): Wave F-2 — Phase 8 rmcp ClientHandler + UI layer wiring
Zetkolink Apr 9, 2026
b33451b
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 9, 2026
e3a685c
feat(plugins): Wave G-1 — 8 fixture plugins + 14 discovery tests (Pha…
Zetkolink Apr 10, 2026
3aad783
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 10, 2026
d568863
test(plugins): Wave G-2 — 13 e2e hook execution tests (Phase 11.1.3)
Zetkolink Apr 10, 2026
5623cbf
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 10, 2026
68093db
[autofix.ci] apply automated fixes (attempt 2/3)
autofix-ci[bot] Apr 10, 2026
1951293
test(plugins): Wave G-3 — 10 multi-plugin + hot-reload + error path t…
Zetkolink Apr 10, 2026
f090928
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 10, 2026
1695ee9
test(plugins): Wave G-4 — 4 performance smoke tests (Phase 11.3)
Zetkolink Apr 10, 2026
181bf10
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 10, 2026
5527432
[autofix.ci] apply automated fixes (attempt 2/3)
autofix-ci[bot] Apr 10, 2026
c6b1d9f
fix: resolve compiler warnings in hook_runtime module
Zetkolink Apr 10, 2026
cb52d97
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 10, 2026
2dd659d
fix: remove unused new_without_plugins in CommandLoaderService
Zetkolink Apr 10, 2026
f348930
chore: remove lcov.info from tracking, add to .gitignore
Zetkolink Apr 10, 2026
5db7937
fix: increase hook perf test budget on CI runners (10s vs 2s local)
Zetkolink Apr 10, 2026
effe433
fix(hooks): wire env vars, fix once semantics, dedup agent resolution…
Zetkolink Apr 10, 2026
5239845
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 10, 2026
9e17fb5
[autofix.ci] apply automated fixes (attempt 2/3)
autofix-ci[bot] Apr 10, 2026
6939b08
feat(hooks): async hook queue, session hooks, plugin refactor, and li…
Zetkolink Apr 11, 2026
c7d02a1
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 11, 2026
3516352
[autofix.ci] apply automated fixes (attempt 2/3)
autofix-ci[bot] Apr 11, 2026
42f74a8
feat(plugins): complete plugin integration cleanup and fixes
Zetkolink Apr 11, 2026
f15705a
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 11, 2026
fb861b0
feat: finalize plugin system - hot-reload pipeline, CLI subcommand, Z…
Zetkolink Apr 11, 2026
f30e64e
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 11, 2026
cc3c411
Fix
Zetkolink Apr 11, 2026
ba7a16f
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 11, 2026
2558144
Fix
Zetkolink Apr 11, 2026
eea7026
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 11, 2026
8a98d98
fix: explicitly shutdown stdin in hook test to prevent Linux pipe hang
Zetkolink Apr 11, 2026
29ea6d4
feat: scan ~/.claude/plugins/ and .claude/plugins/ for Claude Code pl…
Zetkolink Apr 11, 2026
89d73f0
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 11, 2026
0ee63b9
chore: add self-improvement loop to forge agent prompt
Zetkolink Apr 11, 2026
d89dee7
fix: ignore stdin write errors in hook test for early-exit commands
Zetkolink Apr 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ Cargo.lock
**/.forge/request.body.json
node_modules/
bench/__pycache__
lcov.info
33 changes: 33 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# CLAUDE.md

## Testing

Use `cargo nextest run` instead of `cargo test`. The project is configured for nextest (see `.config/nextest.toml`).

Always pass `--no-input-handler` to avoid a crossterm panic in non-interactive environments (e.g. when run by an LLM agent).

```bash
# Only unit tests (fast feedback loop during development)
cargo nextest run --no-input-handler --lib

# Specific crate
cargo nextest run --no-input-handler -p forge_domain

# Integration tests only
cargo nextest run --no-input-handler --test '*'

# Watch mode (auto-rerun on file changes)
cargo watch -x "nextest run --no-input-handler --lib"
```

### Final verification

Before considering any task complete, run the **full** workspace test suite **once** at the very end. This is the same command CI uses and catches issues that crate-scoped runs miss (feature-flag interactions, integration tests, cross-crate breakage):

```bash
cargo nextest run --no-input-handler --all-features --workspace
```

Do NOT run this command repeatedly during development — use the crate-scoped commands above for iteration. Run it exactly once as the last step.

Do NOT silently skip work. If a task is out of scope for the current change, place the TODO and mention it in your response summary.
112 changes: 109 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ codegen-units = 1
opt-level = 3
strip = true

[profile.test]
opt-level = 0
debug = false # skip debug-info generation — noticeably speeds up linking

[workspace.dependencies]
anyhow = "1.0.102"
async-recursion = "1.1.1"
Expand Down
2 changes: 2 additions & 0 deletions crates/forge_api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ futures.workspace = true
forge_app.workspace = true
serde_json.workspace = true
forge_config.workspace = true
tokio.workspace = true
tracing.workspace = true

[dev-dependencies]

Expand Down
79 changes: 76 additions & 3 deletions crates/forge_api/src/api.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use anyhow::Result;
use forge_app::dto::ToolsOverview;
use forge_app::{User, UserUsage};
use forge_domain::{AgentId, Effort, ModelId, ProviderModels};
use forge_app::{NotificationService, User, UserUsage};
use forge_domain::{AgentId, Effort, ModelId, ProviderModels, SetupTrigger};
use forge_stream::MpscStream;
use futures::stream::BoxStream;
use url::Url;
Expand Down Expand Up @@ -250,4 +251,76 @@ pub trait API: Sync + Send {

/// Check the OAuth authentication status of an MCP server
async fn mcp_auth_status(&self, server_url: &str) -> Result<String>;

/// List all discovered plugins alongside any load errors encountered
/// during discovery.
///
/// This is the Phase 9 entry point for the `/plugin list` and
/// `/plugin info` slash commands. The result is cloned from the
/// [`PluginLoader`](forge_app::PluginLoader) cache, so repeated calls
/// cost a single filesystem scan per session. Call
/// [`API::reload_plugins`] to force a re-scan.
async fn list_plugins_with_errors(&self) -> Result<forge_domain::PluginLoadResult>;

/// Persist a plugin `enabled` override to the user's `.forge.toml`
/// under the `[plugins.<name>]` table.
///
/// Used by `/plugin enable <name>` and `/plugin disable <name>`. The
/// write is lossy with respect to other config fields — it round-trips
/// [`ForgeConfig`] through [`ForgeConfig::read`] + [`ForgeConfig::write`]
/// so unrelated fields are preserved. Callers are expected to follow
/// up with [`API::reload_plugins`] to apply the change.
async fn set_plugin_enabled(&self, name: &str, enabled: bool) -> Result<()>;

/// Invalidate the plugin cache and reload every plugin-provided
/// component (skills, commands, agents). Mirrors
/// [`forge_app::PluginComponentsReloader::reload_plugin_components`].
///
/// Used by `/plugin reload`, `/plugin enable`, and `/plugin disable`
/// to apply plugin state changes mid-session without restarting
/// Forge.
async fn reload_plugins(&self) -> Result<()>;

/// Returns a handle to the notification service for emitting
/// user-facing notifications (REPL idle, OAuth success, elicitation,
/// ...). Calling [`NotificationService::emit`] fires the
/// `Notification` lifecycle event through the plugin hook
/// dispatcher (observability only — hook errors never propagate)
/// and, on non-VS-Code TTY terminals, emits a best-effort terminal
/// bell.
///
/// Construction is cheap: the returned handle holds only an `Arc`
/// to the services aggregate, so callers can either cache the
/// handle or construct one per emit.
fn notification_service(&self) -> Arc<dyn NotificationService>;

/// Fires the `Setup` lifecycle event with the given trigger.
///
/// Plugin hooks can observe or log the event, but blocking errors
/// returned by hooks are intentionally ignored per Claude Code
/// semantics (`hooksConfigManager.ts:175`) — Setup runs before a
/// conversation exists, so there is nothing to block.
///
/// Called by `UI::run_inner` when the user invokes
/// `forge --init` / `forge --init-only` / `forge --maintenance`.
/// Safe to call even when no plugins are configured.
async fn fire_setup_hook(&self, trigger: SetupTrigger) -> Result<()>;

/// Notifies the background `ConfigWatcher` that Forge itself is
/// about to write `path`, so the filesystem event that the
/// resulting save produces can be suppressed within the 5-second
/// internal-write window (see
/// [`forge_services::config_watcher`](https://docs.rs/forge_services)).
///
/// This prevents Forge's own config writes (e.g. `/plugin enable`
/// updating `.forge.toml`) from round-tripping through the
/// `ConfigChange` plugin hook, which would otherwise see a
/// spurious "external" change every time the user flipped a
/// setting through the UI.
///
/// Call sites should invoke this **immediately before** the
/// `fc.write()?` that persists the new config. The default impl
/// is a no-op so API implementations that don't own a watcher
/// (e.g. test doubles) can simply inherit it.
fn mark_config_write(&self, _path: &Path) {}
}
Loading
Loading