Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions src/commands/arg_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,21 @@ autter config [<key> | set <key> <value> | unset <key>]
summary: "Print support/debug diagnostics",
body: "autter debug\n\n Print support/debug diagnostics.",
},
HelpEntry {
name: "doctor",
aliases: &[],
summary: "Validate the autter setup end-to-end",
body: "autter doctor [--json] [--skip-trace2-checks]

Run setup validation checks: git and config, the background service,
trace2 capture, a real end-to-end checkpoint round-trip, AI agent hooks,
and login/connectivity. Every failed check prints a concrete fix.

Exits 0 when no check fails (warnings allowed), 1 when any check fails.

--json Machine-readable single-line JSON report
--skip-trace2-checks Skip the trace2 event-capture self-check",
},
HelpEntry {
name: "bg",
aliases: &["d", "daemon"],
Expand Down
14 changes: 9 additions & 5 deletions src/commands/autter_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ pub fn handle_autter(args: &[String]) {
// per-PID log files.
//
// Skip for commands that must work without a running background service
// (help, version, config, d management, debug, upgrade) so users can
// always diagnose and recover from a broken state.
// (help, version, config, d management, debug, doctor, upgrade) so users
// can always diagnose and recover from a broken state.
let needs_daemon = !matches!(
args[0].as_str(),
"help"
Expand All @@ -90,6 +90,7 @@ pub fn handle_autter(args: &[String]) {
| "d"
| "daemon"
| "debug"
| "doctor"
| "upgrade"
| "install-hooks"
| "install"
Expand All @@ -107,7 +108,7 @@ pub fn handle_autter(args: &[String]) {
"error: failed to connect to autter background service: {}",
err
);
commands::suggest_autter_debug();
commands::suggest_autter_doctor();
if args[0].as_str() == "checkpoint" {
std::process::exit(0);
}
Expand Down Expand Up @@ -157,6 +158,9 @@ pub fn handle_autter(args: &[String]) {
"debug" => {
commands::debug::handle_debug(&args[1..]);
}
"doctor" => {
commands::doctor::handle_doctor(&args[1..]);
}
"bg" | "d" | "daemon" => {
commands::daemon::handle_daemon(&args[1..]);
}
Expand Down Expand Up @@ -216,7 +220,7 @@ pub fn handle_autter(args: &[String]) {
}
Err(e) => {
eprintln!("Install hooks failed: {}", e);
commands::suggest_autter_debug();
commands::suggest_autter_doctor();
std::process::exit(1);
}
},
Expand All @@ -228,7 +232,7 @@ pub fn handle_autter(args: &[String]) {
}
Err(e) => {
eprintln!("Uninstall hooks failed: {}", e);
commands::suggest_autter_debug();
commands::suggest_autter_doctor();
std::process::exit(1);
}
},
Expand Down
61 changes: 51 additions & 10 deletions src/commands/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -481,10 +481,36 @@ fn collect_agent_capture_info() -> Vec<String> {
lines
}

/// Outcome of walking the VS Code native-hooks chain, shared between the
/// debug report (which prints the lines) and `autter doctor` (which turns the
/// outcome into a pass/warn/fail check).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum VsCodeChainOutcome {
/// No VS Code CLI found on this machine.
NotDetected,
/// VS Code found but its version could not be determined.
VersionUnknown,
/// VS Code predates native agent hooks; the autter extension captures.
LegacyExtensionMode,
/// Native-hooks mode and every link in the chain is in place.
Complete,
/// Native-hooks mode but the hook file or chat settings are missing.
Incomplete,
}

pub(crate) struct VsCodeNativeHooksChain {
pub(crate) outcome: VsCodeChainOutcome,
pub(crate) lines: Vec<String>,
}

fn collect_vscode_native_hooks_chain() -> Vec<String> {
inspect_vscode_native_hooks_chain().lines
}

/// Walk the chain VS Code's built-in Copilot agent needs for AI edits to
/// checkpoint on VS Code >= 1.109.3: the autter extension stands down there
/// and capture only happens if VS Code loads ~/.copilot/hooks/autter.json.
fn collect_vscode_native_hooks_chain() -> Vec<String> {
pub(crate) fn inspect_vscode_native_hooks_chain() -> VsCodeNativeHooksChain {
use crate::mdm::utils::{
MIN_VSCODE_NATIVE_HOOKS_VERSION, VSCODE_USER_COPILOT_HOOKS_LOCATION, get_editor_version,
home_dir, parse_version_triple, resolve_editor_cli, settings_paths_for_products,
Expand All @@ -495,14 +521,20 @@ fn collect_vscode_native_hooks_chain() -> Vec<String> {

let Some(cli) = resolve_editor_cli("code") else {
lines.push(" VS Code CLI not found; skipping chain checks".to_string());
return lines;
return VsCodeNativeHooksChain {
outcome: VsCodeChainOutcome::NotDetected,
lines,
};
};

let version_str = match get_editor_version(&cli) {
Ok(v) => v,
Err(err) => {
lines.push(format!(" VS Code version: <error: {}>", err));
return lines;
return VsCodeNativeHooksChain {
outcome: VsCodeChainOutcome::VersionUnknown,
lines,
};
}
};
let first_line = version_str.lines().next().unwrap_or("").trim().to_string();
Expand All @@ -518,7 +550,10 @@ fn collect_vscode_native_hooks_chain() -> Vec<String> {
" Capture mode: autter extension detection (VS Code predates native agent hooks, added in {}.{}.{})",
min_major, min_minor, min_patch
));
return lines;
return VsCodeNativeHooksChain {
outcome: VsCodeChainOutcome::LegacyExtensionMode,
lines,
};
}

lines.push(format!(
Expand Down Expand Up @@ -567,13 +602,19 @@ fn collect_vscode_native_hooks_chain() -> Vec<String> {
" Chain status: OK — Copilot agent-mode edits should checkpoint (restart VS Code if hooks were just installed)"
.to_string(),
);
} else {
lines.push(
" Chain status: INCOMPLETE — Copilot agent-mode edits are likely NOT being captured; run `autter install-hooks` and restart VS Code"
.to_string(),
);
return VsCodeNativeHooksChain {
outcome: VsCodeChainOutcome::Complete,
lines,
};
}
lines.push(
" Chain status: INCOMPLETE — Copilot agent-mode edits are likely NOT being captured; run `autter install-hooks` and restart VS Code"
.to_string(),
);
VsCodeNativeHooksChain {
outcome: VsCodeChainOutcome::Incomplete,
lines,
}
lines
}

/// Read `chat.useHooks` and the `~/.copilot/hooks` entry of
Expand Down
Loading
Loading