docs(comments): rewrite comments across all crates to the guidelines
Sweep every first-party crate source (1956 .rs files) to the project comment guidelines: delete redundant restatements, decorative banners, change narration, and end-of-line comments; keep and tighten the crucial ones (invariants, bug rationale, SAFETY blocks, ported-source attribution). No functional code changed. Every edit is proven comment-only against the prior tree by a comment-stripping lexer (string/char/raw-string aware) plus a separate doctest-fence check. Where removing a comment made rustfmt or clippy want to re-lay-out adjacent code, the minimal triggering comment is restored so code tokens stay byte-identical. Gates green: cargo fmt --all --check (0 diffs), cargo check and cargo clippy --workspace --all-targets (0 warnings). Adds scripts/check_codegen_comment_guidelines.py — the enforcement gate for these guidelines (flags banners, end-of-line comments, change narration, and commented-out code).
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
//! Strongly-typed notification metadata.
|
||||
//!
|
||||
//! Parses the `_meta` JSON from `SessionNotification` into a struct with
|
||||
//! typed fields. All fields are `Option` — gracefully degrades when
|
||||
//! kigi-shell hasn't been updated or meta is absent.
|
||||
//! Parses the `_meta` JSON from `SessionNotification` into typed fields that
|
||||
//! gracefully degrade when kigi-shell hasn't been updated or meta is absent.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
||||
@@ -54,7 +54,6 @@ pub struct AcpConnection {
|
||||
pub rx: AcpClientRx,
|
||||
/// Available models and current selection.
|
||||
pub models: ModelState,
|
||||
/// Whether the agent is a kigi-shell instance.
|
||||
pub is_kigi_shell: bool,
|
||||
/// Auth methods advertised by the agent.
|
||||
pub auth_methods: Vec<acp::AuthMethod>,
|
||||
@@ -147,7 +146,6 @@ pub struct ConnectFlags {
|
||||
/// This is the main entry point for establishing an ACP connection.
|
||||
/// After this returns, the agent is ready to create sessions and receive prompts.
|
||||
pub async fn connect(cancel: &CancellationToken, flags: ConnectFlags) -> Result<AcpConnection> {
|
||||
// Load agent config from disk
|
||||
let raw_config = kigi_shell::config::load_effective_config()
|
||||
.map_err(|e| anyhow::anyhow!("Failed to load config: {}", e))?;
|
||||
let mut agent_config = AgentConfig::new_from_toml_cfg(&raw_config)
|
||||
@@ -181,13 +179,11 @@ pub async fn connect(cancel: &CancellationToken, flags: ConnectFlags) -> Result<
|
||||
|
||||
apply_config_writes(&flags);
|
||||
|
||||
// Spawn the agent
|
||||
let memory_config = agent_config.memory_config.clone();
|
||||
let spawned = spawn::spawn_kigi_shell(agent_config, cancel, memory_config).await?;
|
||||
let auth_manager = spawned.auth_manager.clone();
|
||||
let (tx, rx) = (spawned.channel.tx, spawned.channel.rx);
|
||||
|
||||
// Initialize
|
||||
let (
|
||||
models,
|
||||
is_kigi_shell,
|
||||
@@ -198,7 +194,6 @@ pub async fn connect(cancel: &CancellationToken, flags: ConnectFlags) -> Result<
|
||||
session_recap_available,
|
||||
) = initialize(&tx, &flags).await?;
|
||||
|
||||
// Determine whether interactive login is needed.
|
||||
let (needs_login, login_label, login_method_id, auth_start_mode) =
|
||||
startup_auth_metadata(&auth_methods);
|
||||
|
||||
@@ -482,7 +477,6 @@ async fn initialize(
|
||||
|
||||
let resp: acp::InitializeResponse = acp_send(req, tx).await?;
|
||||
|
||||
// Check if this is a kigi-shell agent
|
||||
let is_kigi_shell = resp
|
||||
.meta
|
||||
.as_ref()
|
||||
@@ -490,7 +484,6 @@ async fn initialize(
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or(false);
|
||||
|
||||
// Parse model state from response meta
|
||||
let models: ModelState = resp
|
||||
.meta
|
||||
.as_ref()
|
||||
@@ -566,7 +559,8 @@ pub fn startup_auth_metadata(
|
||||
return (false, None, None, AuthStartMode::Pending);
|
||||
}
|
||||
|
||||
let method = first_method.unwrap(); // safe: needs_login == true implies first_method.is_some()
|
||||
// safe: needs_login == true implies first_method.is_some()
|
||||
let method = first_method.unwrap();
|
||||
let login_label = Some(method.name().to_string());
|
||||
let login_method_id = Some(method.id().clone());
|
||||
|
||||
@@ -812,8 +806,6 @@ mod tests {
|
||||
assert!(!parse_session_recap_available(meta.as_object()));
|
||||
}
|
||||
|
||||
// ── startup_auth_metadata ──────────────────────────────────────
|
||||
|
||||
fn make_auth_method(id: &str, name: &str, meta: Option<serde_json::Value>) -> acp::AuthMethod {
|
||||
let mut agent = acp::AuthMethodAgent::new(acp::AuthMethodId::new(id), name.to_string());
|
||||
if let Some(m) = meta.and_then(|v| v.as_object().cloned()) {
|
||||
@@ -1001,8 +993,6 @@ mod tests {
|
||||
assert_eq!(mode, AuthStartMode::Pending);
|
||||
}
|
||||
|
||||
// ── unsupported_leader_flags ──────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn unsupported_leader_flags_empty_when_none_set() {
|
||||
let flags = ConnectFlags::default();
|
||||
|
||||
@@ -64,7 +64,6 @@ impl ModelState {
|
||||
self.available.is_empty()
|
||||
}
|
||||
|
||||
/// Display name for the current model.
|
||||
pub fn current_model_name(&self) -> Option<String> {
|
||||
let current = self.current.as_ref()?;
|
||||
if let Some(model_info) = self.available.get(current) {
|
||||
@@ -74,12 +73,10 @@ impl ModelState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Machine-readable model ID string for the current model (e.g. "kigi-4.5").
|
||||
pub fn current_model_id_str(&self) -> Option<&str> {
|
||||
Some(self.current.as_ref()?.0.as_ref())
|
||||
}
|
||||
|
||||
/// Total context window tokens for the current model (if available).
|
||||
fn current_context_window_tokens(&self) -> Option<u64> {
|
||||
let meta = self.available.get(self.current.as_ref()?)?.meta.as_ref()?;
|
||||
meta.get("totalContextTokens")
|
||||
@@ -118,20 +115,16 @@ impl ModelState {
|
||||
true
|
||||
}
|
||||
|
||||
/// Get the effective context window size (tokens).
|
||||
///
|
||||
/// Returns the override if set, otherwise reads from the current model's
|
||||
/// metadata. The override is set by `override_context_window()` when an
|
||||
/// external source (e.g., SubagentProgress) reports the actual window size.
|
||||
/// Effective context window (tokens): the override if set, otherwise the
|
||||
/// current model's metadata.
|
||||
pub fn get_context_window(&self) -> Option<u64> {
|
||||
self.context_window_override
|
||||
.or_else(|| self.current_context_window_tokens())
|
||||
}
|
||||
|
||||
/// Override the context window size.
|
||||
///
|
||||
/// Used for subagent views where the actual context window is reported
|
||||
/// via SubagentProgress and may differ from the inherited model's metadata.
|
||||
/// Set the context-window override for subagent views, where the real size
|
||||
/// comes from SubagentProgress and can differ from the inherited model's
|
||||
/// metadata.
|
||||
pub fn override_context_window(&mut self, tokens: u64) {
|
||||
self.context_window_override = Some(tokens);
|
||||
}
|
||||
@@ -288,7 +281,7 @@ impl ModelState {
|
||||
})
|
||||
}
|
||||
|
||||
/// Resolve a user-supplied name to a `ModelId` via case-insensitive
|
||||
/// Resolve a user-supplied name or id to a `ModelId` via case-insensitive
|
||||
/// ASCII match against the catalog.
|
||||
pub fn resolve_by_name_or_id(&self, query: &str) -> Option<acp::ModelId> {
|
||||
self.available.iter().find_map(|(id, info)| {
|
||||
@@ -300,7 +293,6 @@ impl ModelState {
|
||||
})
|
||||
}
|
||||
|
||||
/// Look up the display name for a `ModelId` in the catalog.
|
||||
pub fn display_name_for(&self, id: &acp::ModelId) -> String {
|
||||
self.available
|
||||
.get(id)
|
||||
@@ -308,7 +300,6 @@ impl ModelState {
|
||||
.unwrap_or_else(|| id.0.to_string())
|
||||
}
|
||||
|
||||
/// Cycle to the next model.
|
||||
pub fn next_model(&self) -> Option<acp::ModelId> {
|
||||
if self.available.is_empty() {
|
||||
None
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Agent spawning — creates the agent process and ACP channels.
|
||||
//!
|
||||
//! Simplified to only support KigiShell (in-process) mode.
|
||||
//! Subprocess and remote modes can be added later if needed.
|
||||
//! Only KigiShell (in-process) mode is supported; subprocess and remote modes
|
||||
//! can be added later if needed.
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::thread;
|
||||
@@ -25,14 +25,12 @@ pub struct SpawnedAgent {
|
||||
pub _thread_handle: thread::JoinHandle<Result<()>>,
|
||||
pub channel: AcpClientChannel,
|
||||
pub cancel: CancellationToken,
|
||||
/// The agent's `AuthManager`, shared so pager-side consumers
|
||||
/// channel) resolve the same refreshing bearer as chat traffic.
|
||||
/// The agent's `AuthManager`, shared so pager-side consumers resolve the
|
||||
/// same refreshing bearer as chat traffic.
|
||||
pub auth_manager: std::sync::Arc<AuthManager>,
|
||||
}
|
||||
|
||||
/// Spawn a KigiShell agent in a background thread.
|
||||
///
|
||||
/// Returns the ACP client channel for communication and a cancellation token.
|
||||
pub async fn spawn_kigi_shell(
|
||||
agent_config: AgentConfig,
|
||||
cancel: &CancellationToken,
|
||||
@@ -82,7 +80,6 @@ pub async fn spawn_kigi_shell(
|
||||
})
|
||||
};
|
||||
|
||||
// Spawn the agent thread with direct dispatch
|
||||
let handle = spawn_agent_thread_direct(spawn_fn, acp_agent, agent_cancel.clone())?;
|
||||
|
||||
Ok(SpawnedAgent {
|
||||
@@ -113,12 +110,10 @@ fn spawn_agent_thread_direct(
|
||||
let client_tx = channel.tx.clone();
|
||||
let agent_rc = spawn_agent(client_tx)?;
|
||||
|
||||
// Direct dispatch: RPC requests go straight to the agent
|
||||
let gw_rx = AcpGatewayReceiver::new(channel.rx, agent_rc).with_tracing(true);
|
||||
tokio::task::spawn_local(gw_rx.run());
|
||||
tokio::task::yield_now().await;
|
||||
|
||||
// Keep running until cancelled
|
||||
cancel.cancelled().await;
|
||||
anyhow::Result::Ok(())
|
||||
})
|
||||
|
||||
@@ -28,7 +28,6 @@ use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tracing::debug;
|
||||
/// Convert a UTC millisecond timestamp to local time.
|
||||
fn utc_ms_to_local(ms: i64) -> DateTime<Local> {
|
||||
chrono::Utc
|
||||
.timestamp_millis_opt(ms)
|
||||
@@ -36,22 +35,14 @@ fn utc_ms_to_local(ms: i64) -> DateTime<Local> {
|
||||
.map(|utc| utc.with_timezone(&Local))
|
||||
.unwrap_or_else(Local::now)
|
||||
}
|
||||
/// What the agent is currently doing within a turn.
|
||||
///
|
||||
/// Derived from the tracker's internal state by [`AcpUpdateTracker::activity()`].
|
||||
/// Used by the turn status line widget to show context-appropriate indicators.
|
||||
///
|
||||
/// Note: `Idle` here means "the tracker has no in-flight work". The caller
|
||||
/// should check `TurnState` to distinguish true idle (no turn) from waiting
|
||||
/// (turn started, but no chunks received yet).
|
||||
/// Why a turn is open but nothing is streaming right now.
|
||||
///
|
||||
/// Replaces the old single, opaque "Waiting…" placeholder: instead of treating
|
||||
/// the absence of activity as one undifferentiated state, the turn-status line
|
||||
/// names *what* the agent is blocked on. Resolved partly by the tracker (the
|
||||
/// blocking tool waits it suppresses — see [`AcpUpdateTracker::activity`]) and
|
||||
/// partly at the view boundary (`Model`/`Subagent`, which need turn-state and
|
||||
/// the subagent registry the tracker doesn't own).
|
||||
/// Rather than treating the absence of activity as one undifferentiated
|
||||
/// "Waiting…" state, the turn-status line names *what* the agent is blocked on.
|
||||
/// Resolved partly by the tracker (the blocking tool waits it suppresses — see
|
||||
/// [`AcpUpdateTracker::activity`]) and partly at the view boundary
|
||||
/// (`Model`/`Subagent`, which need turn-state and the subagent registry the
|
||||
/// tracker doesn't own).
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum WaitingReason {
|
||||
/// Waiting for the model to (re)start streaming — the first token after the
|
||||
@@ -183,7 +174,7 @@ pub enum TurnActivity {
|
||||
reason: String,
|
||||
},
|
||||
/// Turn is open but nothing is streaming; `reason` says what we're waiting
|
||||
/// on. Replaces the implicit "no activity == generic Waiting…" placeholder.
|
||||
/// on.
|
||||
Waiting(WaitingReason),
|
||||
}
|
||||
impl TurnActivity {
|
||||
@@ -1404,8 +1395,8 @@ fn extract_skill_header_command(text: &str) -> Option<String> {
|
||||
/// 2. `SessionNotification._meta.promptId` classified via
|
||||
/// [`PromptOrigin::from_prompt_id`]
|
||||
///
|
||||
/// Legacy fallback (pre-meta sessions only): bare auto-wake text that used to
|
||||
/// be gated by the system-reminder prefix. Cron is handled earlier by
|
||||
/// Legacy fallback (pre-meta sessions only): bare auto-wake text gated by the
|
||||
/// system-reminder prefix. Cron is handled earlier by
|
||||
/// [`extract_cron_prompt_body`].
|
||||
fn user_message_hidden_from_scrollback(
|
||||
chunk: &acp::ContentChunk,
|
||||
@@ -2000,7 +1991,6 @@ fn tool_call_title(tc: &acp::ToolCall) -> Cow<'_, str> {
|
||||
Cow::Borrowed(&tc.title)
|
||||
}
|
||||
}
|
||||
/// Extract text content from a ContentBlock.
|
||||
fn extract_text_from_content(content: &acp::ContentBlock) -> String {
|
||||
match content {
|
||||
acp::ContentBlock::Text(t) => t.text.clone(),
|
||||
@@ -2863,9 +2853,9 @@ mod tests {
|
||||
}
|
||||
/// Regression test: two turns should create separate agent message entries.
|
||||
///
|
||||
/// Previously, handle_user_message() didn't reset current_agent_msg,
|
||||
/// so the second turn's agent message chunks got appended to the first
|
||||
/// turn's entry, producing concatenated text.
|
||||
/// Without resetting current_agent_msg in handle_user_message(), the second
|
||||
/// turn's agent message chunks append to the first turn's entry, producing
|
||||
/// concatenated text.
|
||||
#[test]
|
||||
fn two_turns_separate_agent_messages() {
|
||||
crate::appearance::cache::set_show_thinking_blocks(true);
|
||||
@@ -3767,8 +3757,8 @@ mod tests {
|
||||
/// 2. ToolCallUpdate in-progress with kind=search, title="fn main", rawInput
|
||||
/// 3. ToolCallUpdate completed with rawOutput containing GrepSearchOutput
|
||||
///
|
||||
/// This was broken: kind from in-progress update was lost, so the completed
|
||||
/// block rendered as "Other" with no search results.
|
||||
/// Without carrying the kind from the in-progress update, the completed
|
||||
/// block renders as "Other" with no search results.
|
||||
#[test]
|
||||
fn test_search_tool_call_flow() {
|
||||
use kigi_tools::types::output::{GrepFileMatch, GrepLineMatch, GrepSearchOutput};
|
||||
@@ -4836,7 +4826,7 @@ mod tests {
|
||||
assert_eq!(tracker.activity(), None);
|
||||
}
|
||||
/// The blocking bg-plumbing tools are kept out of scrollback but the turn
|
||||
/// IS blocked on them — `activity()` must name the wait instead of the old
|
||||
/// IS blocked on them — `activity()` must name the wait instead of a
|
||||
/// generic `None` (→ "Waiting…"). Task-output tools only advertise once
|
||||
/// raw_input proves them blocking (`timeout_ms > 0`); before that the
|
||||
/// wait is not shown (display mirrors interject eligibility).
|
||||
|
||||
Reference in New Issue
Block a user