Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
use anyhow::{bail, Context, Result};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value as JsonValue};
use sha2::{Digest, Sha256};
use std::collections::{BTreeMap, BTreeSet};
use std::ffi::OsStr;
use std::fs;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::time::{SystemTime, UNIX_EPOCH};
pub const PLUGIN_NAME: &str = "konnect-codex";
pub const MARKETPLACE_NAME: &str = "personal";
const ADAPTER_DIR: &str = "codex-companion";
const NATIVE_SKILLS: &[&str] = &[
"konnect",
"kicad-schematic",
"kicad-pcb",
"kicad-manufacture",
"kicad-review",
"kicad-library",
];
const PLUGIN_MANIFEST_TEMPLATE: &str =
include_str!("../template/konnect-codex/.codex-plugin/plugin.json");
const MCP_TEMPLATE: &str = include_str!("../template/konnect-codex/.mcp.json");
const HOOKS_TEMPLATE: &str = include_str!("../template/konnect-codex/hooks/hooks.json");
const COMPATIBILITY_JSON: &str = include_str!("../compatibility.json");
const ENHANCEMENT_POLICY_JSON: &str = include_str!("../policy/enhancements.json");
const UPSTREAM_BASELINE_JSON: &str = include_str!("../policy/upstream-baseline.json");
include!(concat!(env!("OUT_DIR"), "/reviewed_assets.rs"));
#[derive(Clone, Debug)]
pub struct CompanionPaths {
pub home: PathBuf,
pub plugin_dir: PathBuf,
pub agents_dir: PathBuf,
pub marketplace_path: PathBuf,
pub data_dir: PathBuf,
pub manifest_path: PathBuf,
pub generated_config_path: PathBuf,
pub disabled_agents_dir: PathBuf,
pub native_install_guard_path: PathBuf,
}
impl CompanionPaths {
pub fn for_current_user() -> Result<Self> {
let home = dirs::home_dir().context("could not locate the user home directory")?;
Ok(Self::for_home(home))
}
pub fn for_home(home: PathBuf) -> Self {
let data_dir = home.join(".konnect").join(ADAPTER_DIR);
Self {
plugin_dir: home.join("plugins").join(PLUGIN_NAME),
agents_dir: home.join(".codex").join("agents"),
marketplace_path: home
.join(".agents")
.join("plugins")
.join("marketplace.json"),
manifest_path: data_dir.join("manifest.json"),
generated_config_path: data_dir.join("konnect.toml"),
disabled_agents_dir: data_dir.join("disabled-agents"),
native_install_guard_path: data_dir.join("native-install-guard.json"),
home,
data_dir,
}
}
}
#[derive(Clone, Debug)]
pub struct SyncOptions {
pub paths: CompanionPaths,
pub source: Option<PathBuf>,
pub konnect_binary: PathBuf,
pub config_source: Option<PathBuf>,
pub adapter_binary: PathBuf,
pub activate: bool,
pub dry_run: bool,
pub prefer_native_skills: bool,
}
#[derive(Clone, Debug, Default)]
pub struct OperationReport {
pub messages: Vec<String>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PcbPreflightMode {
Live,
Offline,
}
const FREEROUTING_EXPORT_SCRIPT: &str = r#"import pcbnew, sys
board = pcbnew.LoadBoard(sys.argv[1])
if board is None:
raise SystemExit("could not load board")
if not pcbnew.ExportSpecctraDSN(board, sys.argv[2]):
raise SystemExit("KiCad DSN export failed")
"#;
const FREEROUTING_IMPORT_SCRIPT: &str = r#"import pcbnew, sys
board = pcbnew.LoadBoard(sys.argv[1])
if board is None:
raise SystemExit("could not load board")
if not pcbnew.ImportSpecctraSES(board, sys.argv[2]):
raise SystemExit("KiCad SES import failed")
if not pcbnew.SaveBoard(sys.argv[3], board):
raise SystemExit("KiCad board save failed")
"#;
impl OperationReport {
fn push(&mut self, message: impl Into<String>) {
self.messages.push(message.into());
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
enum InstallState {
Enabled,
Disabled,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
enum SkillMode {
#[default]
Reviewed,
NativePreferred,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
enum ManagedRole {
Plugin,
Agent,
Config,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct ManagedFile {
path: PathBuf,
sha256: String,
role: ManagedRole,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct InstallManifest {
schema_version: u32,
adapter_version: String,
source_root: PathBuf,
source_fingerprint: String,
konnect_binary: PathBuf,
config_source: Option<PathBuf>,
adapter_binary: PathBuf,
installed_at_epoch_seconds: u64,
state: InstallState,
marketplace_created: bool,
#[serde(default)]
skill_mode: SkillMode,
#[serde(default)]
supported_konnect_version: String,
#[serde(default)]
hook_contexts: BTreeMap<String, String>,
files: Vec<ManagedFile>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct NativeInstallGuard {
schema_version: u32,
marker_preexisting: bool,
}
#[derive(Clone, Debug)]
struct GeneratedFile {
path: PathBuf,
content: Vec<u8>,
role: ManagedRole,
}
#[derive(Clone, Debug)]
struct SourceAssets {
skills: Vec<(String, PathBuf)>,
agents: Vec<PathBuf>,
}
#[derive(Clone, Debug, Deserialize)]
struct Compatibility {
konnect_version: String,
companion_revision: u32,
konnect_commit: String,
guidance_sha256: String,
hook_sha256: String,
}
#[derive(Clone, Debug, Deserialize)]
struct EnhancementPolicy {
schema_version: u32,
supported_konnect_version: String,
companion_revision: u32,
enhancements: Vec<Enhancement>,
}
#[derive(Clone, Debug, Deserialize)]
struct Enhancement {
id: String,
status: String,
#[serde(default)]
assertions: Vec<EnhancementAssertion>,
}
#[derive(Clone, Debug, Deserialize)]
struct EnhancementAssertion {
target: String,
contains: String,
}
#[derive(Clone, Debug, Deserialize)]
struct UpstreamBaseline {
schema_version: u32,
konnect_version: String,
konnect_commit: String,
files: Vec<UpstreamBaselineFile>,
}
#[derive(Clone, Debug, Deserialize)]
struct UpstreamBaselineFile {
path: String,
sha256: String,
}
fn native_codex_install_marker(paths: &CompanionPaths) -> PathBuf {
paths.home.join(".konnect").join(".installed-codex")
}
fn acquire_native_install_guard(paths: &CompanionPaths) -> Result<()> {
let marker = native_codex_install_marker(paths);
if paths.native_install_guard_path.exists() {
let guard: NativeInstallGuard =
serde_json::from_slice(&fs::read(&paths.native_install_guard_path)?)?;
if guard.schema_version != 1 {
bail!(
"unsupported native install guard schema {}",
guard.schema_version
);
}
if !marker.exists() {
write_atomic(&marker, env!("CARGO_PKG_VERSION").as_bytes())?;
}
return Ok(());
}
let guard = NativeInstallGuard {
schema_version: 1,
marker_preexisting: marker.exists(),
};
if !guard.marker_preexisting {
write_atomic(&marker, env!("CARGO_PKG_VERSION").as_bytes())?;
}
write_atomic(
&paths.native_install_guard_path,
&serde_json::to_vec_pretty(&guard)?,
)
}
fn release_native_install_guard(paths: &CompanionPaths) -> Result<()> {
if !paths.native_install_guard_path.exists() {
return Ok(());
}
let guard: NativeInstallGuard =
serde_json::from_slice(&fs::read(&paths.native_install_guard_path)?)?;
if guard.schema_version != 1 {
bail!(
"unsupported native install guard schema {}",
guard.schema_version
);
}
if !guard.marker_preexisting {
let marker = native_codex_install_marker(paths);
if marker.exists() {
fs::remove_file(marker)?;
}
}
fs::remove_file(&paths.native_install_guard_path)?;
Ok(())
}
pub fn sync(options: SyncOptions) -> Result<OperationReport> {
sync_with_version_probe(options, command_version)
}
fn sync_with_version_probe<F>(options: SyncOptions, version_probe: F) -> Result<OperationReport>
where
F: FnOnce(&Path) -> Result<String>,
{
let compatibility = compatibility()?;
validate_enhancement_policy(&compatibility)?;
validate_executable(&options.konnect_binary, "Konnect")?;
let installed_version = version_probe(&options.konnect_binary)
.context("could not determine the installed Konnect version")?;
ensure_compatible_konnect_version(&installed_version, &compatibility)?;
validate_executable(&options.adapter_binary, "konnect-codex")?;
if let Some(source) = options.source.as_deref() {
verify_guidance_source(source, &options.konnect_binary, &compatibility)?;
}
let native_skills = if options.prefer_native_skills {
discover_native_skills(&options.paths)
} else {
BTreeSet::new()
};
let old_manifest = load_manifest_if_present(&options.paths.manifest_path)?;
let generated_config = render_config(options.config_source.as_deref())?;
let fingerprint = reviewed_fingerprint(&native_skills, generated_config.as_bytes());
let generated = generate_files(
&native_skills,
&options.paths,
&options.adapter_binary,
&fingerprint,
generated_config,
)?;
verify_sync_targets(&generated, old_manifest.as_ref(), &options.paths)?;
let mut report = OperationReport::default();
report.push(format!(
"Reviewed for Konnect v{} ({})",
compatibility.konnect_version, compatibility.konnect_commit
));
report.push(format!("Konnect: {installed_version} (compatible)"));
report.push(format!(
"Skills: {} native, {} reviewed plugin copies",
native_skills.len(),
reviewed_domain_skill_count().saturating_sub(native_skills.len())
));
report.push("Codex profile: complete eager MCP catalogue".to_string());
report.push("Reviewed hooks: 1".to_string());
if options.dry_run {
for file in &generated {
report.push(format!("Would write: {}", file.path.display()));
}
report.push(format!("Would register: {PLUGIN_NAME}@{MARKETPLACE_NAME}"));
return Ok(report);
}
fs::create_dir_all(&options.paths.data_dir)?;
remove_stale_owned_files(&generated, old_manifest.as_ref())?;
for file in &generated {
write_atomic(&file.path, &file.content)?;
}
if old_manifest
.as_ref()
.is_some_and(|manifest| manifest.state == InstallState::Disabled)
{
for file in old_manifest
.as_ref()
.unwrap()
.files
.iter()
.filter(|file| file.role == ManagedRole::Agent)
{
let disabled_path = options
.paths
.disabled_agents_dir
.join(file.path.file_name().unwrap());
if disabled_path.exists() {
fs::remove_file(disabled_path)?;
}
}
}
let marketplace_created =
patch_marketplace(&options.paths.marketplace_path, old_manifest.is_some())?;
let mut manifest = InstallManifest {
schema_version: 1,
adapter_version: env!("CARGO_PKG_VERSION").to_string(),
source_root: options
.source
.as_ref()
.map(|path| canonical_or_original(path))
.unwrap_or_else(|| {
PathBuf::from(format!(
"embedded-konnect-{}",
compatibility.konnect_version
))
}),
source_fingerprint: fingerprint,
konnect_binary: canonical_or_original(&options.konnect_binary),
config_source: options
.config_source
.as_ref()
.map(|p| canonical_or_original(p)),
adapter_binary: canonical_or_original(&options.adapter_binary),
installed_at_epoch_seconds: now_epoch_seconds(),
state: InstallState::Disabled,
marketplace_created: old_manifest
.as_ref()
.map(|old| old.marketplace_created)
.unwrap_or(marketplace_created),
skill_mode: if options.prefer_native_skills {
SkillMode::NativePreferred
} else {
SkillMode::Reviewed
},
supported_konnect_version: compatibility.konnect_version,
hook_contexts: BTreeMap::new(),
files: generated
.iter()
.map(|file| ManagedFile {
path: file.path.clone(),
sha256: sha256_bytes(&file.content),
role: file.role.clone(),
})
.collect(),
};
if !options.activate {
fs::create_dir_all(&options.paths.disabled_agents_dir)?;
for file in manifest
.files
.iter()
.filter(|file| file.role == ManagedRole::Agent)
{
let disabled_path = options
.paths
.disabled_agents_dir
.join(file.path.file_name().unwrap());
fs::copy(&file.path, disabled_path)?;
fs::remove_file(&file.path)?;
}
}
write_manifest(&options.paths.manifest_path, &manifest)?;
if options.activate {
activate_plugin()?;
if let Err(error) = acquire_native_install_guard(&options.paths) {
let _ = remove_plugin_registration();
return Err(error).context("could not arm native guidance suppression");
}
manifest.state = InstallState::Enabled;
write_manifest(&options.paths.manifest_path, &manifest)?;
report.push(format!("Enabled: {PLUGIN_NAME}@{MARKETPLACE_NAME}"));
} else {
report.push("Generated but not enabled (--no-activate).".to_string());
}
report.push(format!("Plugin: {}", options.paths.plugin_dir.display()));
report.push(format!(
"Agents: {}",
manifest
.files
.iter()
.filter(|file| file.role == ManagedRole::Agent)
.count()
));
Ok(report)
}
pub fn disable(paths: &CompanionPaths) -> Result<OperationReport> {
let mut manifest = load_manifest(&paths.manifest_path)?;
let mut report = OperationReport::default();
if manifest.state == InstallState::Disabled {
release_native_install_guard(paths)?;
report.push("Konnect Codex plugin is already disabled.");
return Ok(report);
}
verify_manifest_files(&manifest, paths, false)?;
remove_plugin_registration().context("could not disable the Codex plugin")?;
fs::create_dir_all(&paths.disabled_agents_dir)?;
for file in manifest
.files
.iter()
.filter(|file| file.role == ManagedRole::Agent)
{
if file.path.exists() {
let disabled_path = paths.disabled_agents_dir.join(
file.path
.file_name()
.context("managed agent path has no filename")?,
);
fs::copy(&file.path, &disabled_path)?;
fs::remove_file(&file.path)?;
}
}
manifest.state = InstallState::Disabled;
write_manifest(&paths.manifest_path, &manifest)?;
release_native_install_guard(paths)?;
report.push("Disabled plugin hooks, MCP server, skills, and custom agents.");
report.push("Generated plugin and source manifest were retained for re-enable.");
Ok(report)
}
pub fn enable(paths: &CompanionPaths) -> Result<OperationReport> {
let mut manifest = load_manifest(&paths.manifest_path)?;
let mut report = OperationReport::default();
if manifest.state == InstallState::Enabled {
acquire_native_install_guard(paths)?;
report.push("Konnect Codex plugin is already enabled.");
return Ok(report);
}
for file in manifest
.files
.iter()
.filter(|file| file.role == ManagedRole::Agent)
{
let disabled_path = paths.disabled_agents_dir.join(
file.path
.file_name()
.context("managed agent path has no filename")?,
);
if file.path.exists() {
bail!(
"refusing to replace agent created while disabled: {}",
file.path.display()
);
}
let content = fs::read(&disabled_path).with_context(|| {
format!(
"disabled agent copy is missing: {}",
disabled_path.display()
)
})?;
if sha256_bytes(&content) != file.sha256 {
bail!("disabled agent was modified: {}", disabled_path.display());
}
}
activate_plugin()?;
if let Err(error) = acquire_native_install_guard(paths) {
let _ = remove_plugin_registration();
return Err(error).context("could not arm native guidance suppression");
}
for file in manifest
.files
.iter()
.filter(|file| file.role == ManagedRole::Agent)
{
let disabled_path = paths
.disabled_agents_dir
.join(file.path.file_name().unwrap());
let content = fs::read(&disabled_path)?;
write_atomic(&file.path, &content)?;
fs::remove_file(disabled_path)?;
}
remove_dir_if_empty(&paths.disabled_agents_dir)?;
manifest.state = InstallState::Enabled;
write_manifest(&paths.manifest_path, &manifest)?;
report.push("Enabled plugin skills, eager MCP server, hooks, and custom agents.");
Ok(report)
}
pub fn uninstall(paths: &CompanionPaths, force: bool) -> Result<OperationReport> {
let manifest = load_manifest(&paths.manifest_path)?;
verify_manifest_files(&manifest, paths, force)?;
verify_marketplace_entry(&paths.marketplace_path, force)?;
let mut report = OperationReport::default();
if manifest.state == InstallState::Enabled {
if let Err(error) = remove_plugin_registration() {
report.push(format!(
"Plugin was already absent or could not be removed by Codex: {error:#}"
));
}
}
remove_marketplace_entry(&paths.marketplace_path, manifest.marketplace_created)?;
for file in &manifest.files {
if file.path.exists() {
fs::remove_file(&file.path)
.with_context(|| format!("could not remove {}", file.path.display()))?;
}
if file.role == ManagedRole::Agent {
if let Some(name) = file.path.file_name() {
let disabled_path = paths.disabled_agents_dir.join(name);
if disabled_path.exists() {
fs::remove_file(disabled_path)?;
}
}
}
}
release_native_install_guard(paths)?;
remove_tree_if_empty(&paths.plugin_dir)?;
remove_dir_if_empty(&paths.disabled_agents_dir)?;
if paths.manifest_path.exists() {
fs::remove_file(&paths.manifest_path)?;
}
remove_dir_if_empty(&paths.data_dir)?;
if let Some(parent) = paths.data_dir.parent() {
remove_dir_if_empty(parent)?;
}
remove_dir_if_empty(&paths.agents_dir)?;
report.push("Removed every plugin-owned file and marketplace entry.");
report.push("Native Konnect skills and all Claude files were left untouched.");
Ok(report)
}
pub fn doctor(paths: &CompanionPaths) -> Result<OperationReport> {
let manifest = load_manifest(&paths.manifest_path)?;
let compatibility = compatibility()?;
let mut report = OperationReport::default();
report.push(format!("State: {:?}", manifest.state).to_ascii_lowercase());
report.push(format!("Source: {}", manifest.source_root.display()));
report.push(format!("Skill mode: {:?}", manifest.skill_mode).to_ascii_lowercase());
report.push(format!(
"Supported Konnect: {}",
manifest.supported_konnect_version
));
let active_enhancement_count = validate_enhancement_policy(&compatibility)?;
let policy = enhancement_policy()?;
report.push(format!("Companion revision: {}", policy.companion_revision));
report.push(format!(
"Codex enhancements: {} active",
active_enhancement_count
));
report.push(format!(
"Source fingerprint: {}",
manifest.source_fingerprint
));
let mut healthy_count = 0usize;
let mut healthy_plugin_agents = 0usize;
let plugin_agent_count = manifest
.files
.iter()
.filter(|file| file.role == ManagedRole::Agent)
.count();
let mut unhealthy = Vec::new();
for file in &manifest.files {
let check_path =
if manifest.state == InstallState::Disabled && file.role == ManagedRole::Agent {
paths
.disabled_agents_dir
.join(file.path.file_name().unwrap())
} else {
file.path.clone()
};
match hash_file(&check_path) {
Ok(hash) if hash == file.sha256 => {
healthy_count += 1;
if file.role == ManagedRole::Agent {
healthy_plugin_agents += 1;
}
}
Ok(_) => unhealthy.push(format!("modified: {}", check_path.display())),
Err(_) => unhealthy.push(format!("missing: {}", check_path.display())),
}
}
report.push(format!(
"Owned files: {healthy_count}/{} healthy",
manifest.files.len()
));
report.push(format!(
"Plugin agents: {healthy_plugin_agents}/{plugin_agent_count} installed and healthy"
));
report.messages.extend(unhealthy);
let config_raw = fs::read_to_string(&paths.generated_config_path)?;
let config: toml::Value = toml::from_str(&config_raw)?;
let eager = config
.get("eager_toolsets")
.and_then(toml::Value::as_bool)
.unwrap_or(false);
report.push(format!("Eager toolsets: {eager}"));
let version = Command::new(&manifest.konnect_binary)
.arg("--version")
.output()
.with_context(|| {
format!(
"could not run Konnect at {}",
manifest.konnect_binary.display()
)
})?;
let installed_version = String::from_utf8_lossy(&version.stdout).trim().to_string();
let expected_version = format!("konnect {}", compatibility.konnect_version);
let compatible_version = installed_version == expected_version;
report.push(format!("Konnect: {installed_version}"));
report.push(format!("Version compatibility: {compatible_version}"));
let marketplace_ok = marketplace_has_owned_entry(&paths.marketplace_path)?;
report.push(format!("Marketplace entry: {marketplace_ok}"));
let native_install_suppressed =
paths.native_install_guard_path.exists() && native_codex_install_marker(paths).exists();
report.push(format!(
"Native guidance auto-install suppressed: {native_install_suppressed}"
));
report.push(format!(
"Relevant-prompt hook: {}",
user_prompt_context("Please review this KiCad PCB").is_some()
));
report.messages.extend(native_status(paths)?.messages);
report.push(
if healthy_count == manifest.files.len()
&& eager
&& marketplace_ok
&& compatible_version
&& (manifest.state == InstallState::Disabled || native_install_suppressed)
{
"Health: PASS".to_string()
} else {
"Health: FAIL".to_string()
},
);
Ok(report)
}
pub fn native_status(paths: &CompanionPaths) -> Result<OperationReport> {
let mut report = OperationReport::default();
let native_skills_dir = paths.home.join(".agents").join("skills");
let skill_count = NATIVE_SKILLS
.iter()
.filter(|name| native_skills_dir.join(name).join("SKILL.md").exists())
.count();
let native_agent_count = if paths.agents_dir.exists() {
fs::read_dir(&paths.agents_dir)?
.filter_map(std::result::Result::ok)
.filter(|entry| {
let name = entry.file_name().to_string_lossy().to_ascii_lowercase();
name.ends_with(".toml") && name.contains("kicad") && !name.starts_with("konnect_")
})
.count()
} else {
0
};
let marker = paths.home.join(".konnect").join(".installed-codex");
report.push(format!(
"Upstream-native Konnect coverage: {skill_count}/{} skills, {native_agent_count} agents, installer marker {}, plugin suppression {}",
NATIVE_SKILLS.len(),
marker.exists(),
paths.native_install_guard_path.exists()
));
if skill_count > 0 {
report.push(
"Native skills are installed. The plugin uses its reviewed skills by default; uninstall native Codex guidance to avoid duplicate names, or sync with --prefer-native-skills.",
);
} else {
report.push("The reviewed plugin skills are the active Codex guidance.");
}
Ok(report)
}
pub fn run_mcp(paths: &CompanionPaths) -> Result<i32> {
let manifest = load_manifest(&paths.manifest_path)?;
if manifest.state != InstallState::Enabled {
bail!("Konnect Codex plugin is disabled; run `konnect-codex enable`");
}
acquire_native_install_guard(paths)
.context("could not suppress Konnect's native guidance auto-installer")?;
let status = Command::new(&manifest.konnect_binary)
.arg("--client")
.arg("codex")
.arg("--config")
.arg(&paths.generated_config_path)
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.status()
.with_context(|| {
format!(
"could not start Konnect at {}",
manifest.konnect_binary.display()
)
})?;
Ok(status.code().unwrap_or(1))
}
pub fn pcb_preflight(board: &Path, mode: PcbPreflightMode) -> Result<OperationReport> {
validate_board_path(board)?;
let editors = pcb_editor_process_count()?;
match mode {
PcbPreflightMode::Live if editors != 1 => bail!(
"live PCB mutation requires exactly one pcbnew process; found {editors}. Close duplicate PCB Editors or open the target board before continuing"
),
PcbPreflightMode::Offline if editors != 0 => bail!(
"offline PCB mutation requires PCB Editor to be closed; found {editors} pcbnew process(es)"
),
_ => {}
}
let mut report = OperationReport::default();
report.push(format!("PCB preflight: {:?} mode is ready", mode));
report.push(format!(
"Target board: {}",
canonical_or_original(board).display()
));
report.push(format!("PCB Editor processes: {editors}"));
if mode == PcbPreflightMode::Live {
report.push(
"Process ownership is valid. Confirm Konnect's live active-board path matches this target before the first mutation.",
);
} else {
report.push("Offline ownership is exclusive; no live editor can race the file operation.");
}
Ok(report)
}
pub fn freerouting_status() -> Result<OperationReport> {
let python = locate_kicad_python();
let jar = locate_freerouting_jar();
let java = locate_program(if cfg!(windows) { "java.exe" } else { "java" });
let editors = pcb_editor_process_count()?;
let mut report = OperationReport::default();
match &python {
Some(path) if kicad_python_supports_specctra(path) => {
report.push(format!("KiCad DSN/SES bridge: ready ({})", path.display()))
}
Some(path) => report.push(format!(
"KiCad DSN/SES bridge: unavailable ({} cannot import the required pcbnew API)",
path.display()
)),
None => report
.push("KiCad DSN/SES bridge: unavailable (set KICAD_PYTHON to KiCad's bundled Python)"),
}
report.push(match &jar {
Some(path) => format!("Freerouting engine: ready ({})", path.display()),
None => "Freerouting engine: unavailable (set FREEROUTING_JAR or install the KiCad Freerouting plugin)".to_string(),
});
report.push(match &java {
Some(path) => format!("Java runtime: found ({})", path.display()),
None => "Java runtime: unavailable on PATH".to_string(),
});
report.push(format!("PCB Editor processes: {editors}"));
if python
.as_ref()
.is_some_and(|path| kicad_python_supports_specctra(path))
&& jar.is_some()
&& java.is_some()
{
report
.push("Offline route bridge: ready. Close PCB Editor before export, import, or route.");
}
Ok(report)
}
pub fn freerouting_export(board: &Path, dsn: &Path) -> Result<OperationReport> {
pcb_preflight(board, PcbPreflightMode::Offline)?;
refuse_existing_output(dsn)?;
ensure_parent_exists(dsn)?;
let python = require_kicad_python()?;
if let Err(error) = run_kicad_python(
&python,
FREEROUTING_EXPORT_SCRIPT,
&[board.as_os_str(), dsn.as_os_str()],
"DSN export",
) {
let _ = fs::remove_file(dsn);
return Err(error);
}
if !dsn.is_file() || fs::metadata(dsn)?.len() == 0 {
bail!(
"KiCad reported DSN export success but {} is missing or empty",
dsn.display()
);
}
sanitize_freerouting_dsn(dsn)?;
let mut report = OperationReport::default();
report.push(format!("Exported Freerouting DSN: {}", dsn.display()));
report.push("Source board was not modified.");
Ok(report)
}
pub fn freerouting_import(
board: &Path,
ses: &Path,
output: Option<&Path>,
) -> Result<OperationReport> {
pcb_preflight(board, PcbPreflightMode::Offline)?;
if !ses.is_file() {
bail!("Freerouting session file was not found: {}", ses.display());
}
let output = output
.map(Path::to_path_buf)
.unwrap_or_else(|| default_freerouted_board(board));
refuse_existing_output(&output)?;
ensure_parent_exists(&output)?;
let python = require_kicad_python()?;
if let Err(error) = run_kicad_python(
&python,
FREEROUTING_IMPORT_SCRIPT,
&[board.as_os_str(), ses.as_os_str(), output.as_os_str()],
"SES import",
) {
let _ = fs::remove_file(&output);
return Err(error);
}
if !output.is_file() || fs::metadata(&output)?.len() == 0 {
bail!(
"KiCad reported SES import success but {} is missing or empty",
output.display()
);
}
let mut report = OperationReport::default();
report.push(format!(
"Imported Freerouting SES into: {}",
output.display()
));
report.push(format!("Original board preserved: {}", board.display()));
report.push(
"Open the generated board in one PCB Editor, then run Konnect inventory, unrouted, short, and direct DRC acceptance checks before replacing the original.",
);
Ok(report)
}
pub fn freerouting_route(
board: &Path,
output: Option<&Path>,
passes: u32,
) -> Result<OperationReport> {
pcb_preflight(board, PcbPreflightMode::Offline)?;
let output = output
.map(Path::to_path_buf)
.unwrap_or_else(|| default_freerouted_board(board));
refuse_existing_output(&output)?;
let jar = locate_freerouting_jar().context(
"Freerouting JAR not found; install the KiCad Freerouting plugin or set FREEROUTING_JAR",
)?;
let java = locate_program(if cfg!(windows) { "java.exe" } else { "java" })
.context("Java was not found on PATH")?;
let stamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos();
let work = std::env::temp_dir().join(format!("konnect-codex-freerouting-{stamp}"));
fs::create_dir_all(&work)?;
let dsn = work.join("board.dsn");
let ses = work.join("board.ses");
if let Err(error) = freerouting_export(board, &dsn) {
return Err(error).context(format!("routing workspace retained at {}", work.display()));
}
let status = Command::new(&java)
.arg("-jar")
.arg(&jar)
.arg("-de")
.arg(&dsn)
.arg("-do")
.arg(&ses)
.arg("-mp")
.arg(passes.to_string())
.status()
.with_context(|| format!("could not start Freerouting through {}", java.display()))?;
if !status.success() || !ses.is_file() || fs::metadata(&ses)?.len() == 0 {
bail!(
"Freerouting did not produce a session (status {status}); routing workspace retained at {}",
work.display()
);
}
let mut report = freerouting_import(board, &ses, Some(&output))
.with_context(|| format!("routing workspace retained at {}", work.display()))?;
fs::remove_dir_all(&work)?;
report.messages.insert(
0,
format!(
"Freerouting completed with a {passes}-pass limit via {}",
jar.display()
),
);
Ok(report)
}
fn validate_board_path(board: &Path) -> Result<()> {
if !board.is_file() {
bail!("KiCad board was not found: {}", board.display());
}
let valid_extension = board
.extension()
.and_then(OsStr::to_str)
.is_some_and(|extension| extension.eq_ignore_ascii_case("kicad_pcb"));
if !valid_extension {
bail!("expected a .kicad_pcb file: {}", board.display());
}
Ok(())
}
fn refuse_existing_output(output: &Path) -> Result<()> {
if output.exists() {
bail!(
"refusing to overwrite existing output: {}",
output.display()
);
}
Ok(())
}
fn ensure_parent_exists(output: &Path) -> Result<()> {
if let Some(parent) = output
.parent()
.filter(|parent| !parent.as_os_str().is_empty())
{
if !parent.is_dir() {
bail!("output directory does not exist: {}", parent.display());
}
}
Ok(())
}
fn default_freerouted_board(board: &Path) -> PathBuf {
let stem = board.file_stem().and_then(OsStr::to_str).unwrap_or("board");
board.with_file_name(format!("{stem}.freerouted.kicad_pcb"))
}
fn require_kicad_python() -> Result<PathBuf> {
let python = locate_kicad_python()
.context("KiCad Python not found; set KICAD_PYTHON to KiCad's bundled Python executable")?;
if !kicad_python_supports_specctra(&python) {
bail!(
"{} does not expose pcbnew.ExportSpecctraDSN and pcbnew.ImportSpecctraSES",
python.display()
);
}
Ok(python)
}
fn run_kicad_python(python: &Path, script: &str, args: &[&OsStr], operation: &str) -> Result<()> {
let output = Command::new(python)
.arg("-c")
.arg(script)
.args(args)
.output()
.with_context(|| format!("could not run KiCad Python for {operation}"))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
bail!("KiCad {operation} failed: {stderr}");
}
Ok(())
}
fn sanitize_freerouting_dsn(dsn: &Path) -> Result<()> {
let raw = fs::read_to_string(dsn)?;
let sanitized = raw.replace(['Ω', 'µ', 'Φ'], "");
if sanitized != raw {
write_atomic(dsn, sanitized.as_bytes())?;
}
Ok(())
}
fn kicad_python_supports_specctra(python: &Path) -> bool {
Command::new(python)
.arg("-c")
.arg("import pcbnew; assert hasattr(pcbnew, 'ExportSpecctraDSN') and hasattr(pcbnew, 'ImportSpecctraSES')")
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.is_ok_and(|status| status.success())
}
fn locate_kicad_python() -> Option<PathBuf> {
if let Some(path) = std::env::var_os("KICAD_PYTHON").map(PathBuf::from) {
if path.is_file() {
return Some(path);
}
}
let mut candidates = Vec::new();
if cfg!(windows) {
if let Some(local) = dirs::data_local_dir() {
for version in ["10.0", "9.0"] {
candidates.push(
local
.join("Programs")
.join("KiCad")
.join(version)
.join("bin")
.join("python.exe"),
);
}
}
} else {
for name in ["python3", "python"] {
if let Some(path) = locate_program(name) {
candidates.push(path);
}
}
}
candidates.into_iter().find(|path| path.is_file())
}
fn locate_freerouting_jar() -> Option<PathBuf> {
if let Some(path) = std::env::var_os("FREEROUTING_JAR").map(PathBuf::from) {
if path.is_file() {
return Some(path);
}
}
let documents = dirs::document_dir()?;
for version in ["10.0", "9.0"] {
let jar_dir = documents
.join("KiCad")
.join(version)
.join("3rdparty")
.join("plugins")
.join("app_freerouting_kicad-plugin")
.join("jar");
let Ok(entries) = fs::read_dir(jar_dir) else {
continue;
};
let mut jars: Vec<_> = entries
.filter_map(std::result::Result::ok)
.map(|entry| entry.path())
.filter(|path| {
path.file_name()
.and_then(OsStr::to_str)
.is_some_and(|name| name.starts_with("freerouting-") && name.ends_with(".jar"))
})
.collect();
jars.sort();
if let Some(path) = jars.pop() {
return Some(path);
}
}
None
}
fn locate_program(name: &str) -> Option<PathBuf> {
std::env::var_os("PATH")
.into_iter()
.flat_map(|value| std::env::split_paths(&value).collect::<Vec<_>>())
.map(|directory| directory.join(name))
.find(|path| path.is_file())
}
fn pcb_editor_process_count() -> Result<usize> {
if cfg!(windows) {
let output = Command::new("tasklist")
.args(["/FI", "IMAGENAME eq pcbnew.exe", "/FO", "CSV", "/NH"])
.output()
.context("could not query PCB Editor processes with tasklist")?;
if !output.status.success() {
bail!("tasklist failed while checking PCB Editor ownership");
}
let stdout = String::from_utf8_lossy(&output.stdout).to_ascii_lowercase();
return Ok(stdout
.lines()
.filter(|line| line.trim_start().starts_with("\"pcbnew.exe\""))
.count());
}
let output = Command::new("ps")
.args(["-A", "-o", "comm="])
.output()
.context("could not query PCB Editor processes with ps")?;
if !output.status.success() {
bail!("ps failed while checking PCB Editor ownership");
}
Ok(String::from_utf8_lossy(&output.stdout)
.lines()
.filter(|line| {
Path::new(line.trim())
.file_name()
.and_then(OsStr::to_str)
.is_some_and(|name| name.eq_ignore_ascii_case("pcbnew"))
})
.count())
}
pub fn run_hook(name: &str, argument: Option<&str>, paths: &CompanionPaths) -> Result<()> {
let mut raw = String::new();
std::io::stdin().read_to_string(&mut raw)?;
let input: JsonValue = serde_json::from_str(&raw).unwrap_or_else(|_| json!({}));
let context = match name {
"pre-pcb-ipc" => {
let editors = pcb_editor_process_count().unwrap_or(usize::MAX);
let ownership = if editors == 1 {
"One PCB Editor process is present. Confirm Konnect's active-board path matches the target before mutation."
} else if editors == usize::MAX {
"PCB Editor process ownership could not be determined. Stop and run `konnect-codex pcb-preflight --board <path> --mode live`."
} else {
"PCB ownership is unsafe. Do not run this mutation; open exactly one PCB Editor with the target board and rerun preflight."
};
Some(format!(
"This Konnect PCB operation requires exactly one responsive KiCad PCB Editor with the target board open. Detected pcbnew process count: {editors}. {ownership} Confirm a plausible component/pad inventory before mutation. If IPC is unavailable, the editor closes, or a mutator reports file fallback after live work began, stop the PCB phase, reopen the board, and retry the readiness query once. Never mix live IPC and closed-file fallback in one placement or routing sequence."
))
}
"user-prompt" => input
.get("prompt")
.and_then(JsonValue::as_str)
.and_then(user_prompt_context),
"konnect-skill" => {
let skill_name = argument.context("konnect-skill hook requires a skill name")?;
let manifest = load_manifest(&paths.manifest_path)?;
Some(
manifest
.hook_contexts
.get(skill_name)
.with_context(|| format!("hook guidance '{skill_name}' is not installed"))?
.clone(),
)
}
other => bail!("unknown hook '{other}'"),
};
if let Some(context) = context {
println!(
"{}",
serde_json::to_string(&json!({
"hookSpecificOutput": {
"hookEventName": input
.get("hook_event_name")
.and_then(JsonValue::as_str)
.unwrap_or(if name == "user-prompt" { "UserPromptSubmit" } else { "PreToolUse" }),
"additionalContext": context
}
}))?
);
}
Ok(())
}
fn user_prompt_context(prompt: &str) -> Option<String> {
let lower = prompt.to_ascii_lowercase();
let relevant = [
"kicad",
".kicad_",
"schematic",
"circuit",
"pcb",
"footprint",
"gerber",
"jlcpcb",
"bill of materials",
"bom",
"mpn",
"datasheet",
"component sourcing",
"design rule",
"erc",
"drc",
"freerouting",
"autoroute",
]
.iter()
.any(|term| lower.contains(term));
relevant.then(|| {
"This is a Konnect/KiCad task. Use the konnect-codex router and the matching bundled domain skill. Use kicad-bom for MPN, datasheet, lifecycle, sourcing, DNP, alternate, or assembly-BOM work. Make every KiCad-source change through Konnect MCP tools, use the visible eager tool catalogue directly, and finish with the strongest available validation. When delegation is available, hand custom library work to konnect_library_builder, a complete schematic build to konnect_schematic_builder, substantial PCB transfer/layout work to konnect_pcb_builder, a comprehensive final review to konnect_design_reviewer, and a read-only firmware/first-power handoff to konnect_bringup_planner. Run applicable work sequentially in library -> schematic -> BOM -> PCB -> review -> bring-up order. The PCB builder must close the visible placement gate before routing and use Freerouting by default for a complete board, with route-import inventory and direct DRC acceptance before zones or manufacturing."
.to_string()
})
}
fn discover_assets(source: Option<&Path>, home: &Path) -> Result<SourceAssets> {
let requested = source
.map(Path::to_path_buf)
.unwrap_or_else(|| home.join(".claude"));
let candidates = [
requested.clone(),
requested.join("crates").join("konnect").join("assets"),
];
let root = candidates
.into_iter()
.find(|candidate| candidate.join("skills").is_dir() && candidate.join("agents").is_dir())
.with_context(|| {
format!(
"{} does not contain Konnect skills/ and agents/ assets",
requested.display()
)
})?;
let mut skills = Vec::new();
for entry in fs::read_dir(root.join("skills"))? {
let entry = entry?;
if entry.file_type()?.is_dir() && entry.path().join("SKILL.md").is_file() {
skills.push((
entry.file_name().to_string_lossy().into_owned(),
entry.path(),
));
}
}
skills.sort_by(|a, b| a.0.cmp(&b.0));
let mut agents = Vec::new();
for entry in fs::read_dir(root.join("agents"))? {
let entry = entry?;
let path = entry.path();
if path.extension() == Some(OsStr::new("md")) {
let raw = fs::read_to_string(&path)?;
if raw.contains("mcp__konnect__") {
agents.push(path);
}
}
}
agents.sort();
if skills.is_empty() || agents.is_empty() {
bail!(
"Konnect source at {} must contain at least one skill and one Konnect agent",
root.display()
);
}
Ok(SourceAssets { skills, agents })
}
fn generate_files(
native_skills: &BTreeSet<String>,
paths: &CompanionPaths,
adapter_binary: &Path,
fingerprint: &str,
generated_config: String,
) -> Result<Vec<GeneratedFile>> {
let mut files = Vec::new();
let companion_revision = enhancement_policy()?.companion_revision;
let plugin_version = format!(
"{}+codex.{}.{}",
env!("CARGO_PKG_VERSION"),
companion_revision,
&fingerprint[..12]
);
let mut manifest: JsonValue = serde_json::from_str(PLUGIN_MANIFEST_TEMPLATE)?;
manifest["version"] = json!(plugin_version);
files.push(GeneratedFile {
path: paths.plugin_dir.join(".codex-plugin").join("plugin.json"),
content: serde_json::to_vec_pretty(&manifest)?,
role: ManagedRole::Plugin,
});
let adapter_command = canonical_or_original(adapter_binary)
.to_string_lossy()
.into_owned();
let mut mcp: JsonValue = serde_json::from_str(MCP_TEMPLATE)?;
mcp["mcpServers"]["konnect"]["command"] = json!(adapter_command);
files.push(GeneratedFile {
path: paths.plugin_dir.join(".mcp.json"),
content: serde_json::to_vec_pretty(&mcp)?,
role: ManagedRole::Plugin,
});
let mut hooks: JsonValue = serde_json::from_str(HOOKS_TEMPLATE)?;
if let Some(events) = hooks.get_mut("hooks").and_then(JsonValue::as_object_mut) {
for groups in events.values_mut().filter_map(JsonValue::as_array_mut) {
for group in groups {
if let Some(handlers) = group.get_mut("hooks").and_then(JsonValue::as_array_mut) {
for handler in handlers {
let suffix = handler
.get("command")
.and_then(JsonValue::as_str)
.and_then(|command| command.split_once(" hook ").map(|(_, tail)| tail))
.context("hook template command is missing its hook name")?;
handler["command"] = json!(format!(
"\"{}\" hook {}",
canonical_or_original(adapter_binary).display(),
suffix
));
}
}
}
}
}
files.push(GeneratedFile {
path: paths.plugin_dir.join("hooks").join("hooks.json"),
content: serde_json::to_vec_pretty(&hooks)?,
role: ManagedRole::Plugin,
});
for (relative, content) in REVIEWED_SKILL_FILES {
let name = Path::new(relative)
.components()
.next()
.context("reviewed skill path has no skill name")?
.as_os_str()
.to_string_lossy();
if name != PLUGIN_NAME && native_skills.contains(name.as_ref()) {
continue;
}
files.push(GeneratedFile {
path: paths.plugin_dir.join("skills").join(relative),
content: content.to_vec(),
role: ManagedRole::Plugin,
});
}
for (file_name, content) in REVIEWED_AGENT_FILES {
files.push(GeneratedFile {
path: paths.agents_dir.join(file_name),
content: content.to_vec(),
role: ManagedRole::Agent,
});
}
files.push(GeneratedFile {
path: paths.generated_config_path.clone(),
content: generated_config.into_bytes(),
role: ManagedRole::Config,
});
Ok(files)
}
fn discover_native_skills(paths: &CompanionPaths) -> BTreeSet<String> {
let marker = paths.home.join(".konnect").join(".installed-codex");
if !marker.is_file() {
return BTreeSet::new();
}
let skills_dir = paths.home.join(".agents").join("skills");
reviewed_skill_names()
.into_iter()
.filter(|name| name != PLUGIN_NAME)
.filter(|name| skills_dir.join(name).join("SKILL.md").is_file())
.collect()
}
fn render_config(source: Option<&Path>) -> Result<String> {
let mut value = if let Some(path) = source {
let raw = fs::read_to_string(path)
.with_context(|| format!("could not read Konnect config {}", path.display()))?;
if path.extension() == Some(OsStr::new("json")) {
let json: JsonValue = serde_json::from_str(&raw)?;
toml::Value::try_from(json)?
} else {
toml::from_str::<toml::Value>(&raw)?
}
} else {
toml::Value::Table(toml::map::Map::new())
};
let table = value
.as_table_mut()
.context("Konnect config root must be a table/object")?;
table.insert("eager_toolsets".to_string(), toml::Value::Boolean(true));
table.insert(
"transport".to_string(),
toml::Value::String("stdio".to_string()),
);
let serialized = toml::to_string_pretty(&value)?;
Ok(format!(
"# Generated by konnect-codex. Edit the source config and run sync again.\n{serialized}"
))
}
fn compatibility() -> Result<Compatibility> {
Ok(serde_json::from_str(COMPATIBILITY_JSON)?)
}
fn enhancement_policy() -> Result<EnhancementPolicy> {
Ok(serde_json::from_str(ENHANCEMENT_POLICY_JSON)?)
}
fn upstream_baseline() -> Result<UpstreamBaseline> {
Ok(serde_json::from_str(UPSTREAM_BASELINE_JSON)?)
}
fn reviewed_asset_content(target: &str) -> Option<&'static [u8]> {
if let Some(path) = target.strip_prefix("skills/") {
REVIEWED_SKILL_FILES
.iter()
.find(|(candidate, _)| *candidate == path)
.map(|(_, content)| *content)
} else if let Some(path) = target.strip_prefix("agents/") {
REVIEWED_AGENT_FILES
.iter()
.find(|(candidate, _)| *candidate == path)
.map(|(_, content)| *content)
} else {
None
}
}
fn validate_enhancement_policy(compatibility: &Compatibility) -> Result<usize> {
let policy = enhancement_policy()?;
if policy.schema_version != 1
|| policy.supported_konnect_version != compatibility.konnect_version
|| policy.companion_revision != compatibility.companion_revision
{
bail!("Codex enhancement policy metadata does not match compatibility.json");
}
let mut ids = BTreeSet::new();
let mut active_count = 0usize;
for enhancement in policy.enhancements {
if enhancement.id.trim().is_empty() || !ids.insert(enhancement.id.clone()) {
bail!("Codex enhancement policy contains a missing or duplicate id");
}
if enhancement.status == "active" {
active_count += 1;
} else if enhancement.status != "retired" {
bail!(
"Codex enhancement {} has unsupported status {}",
enhancement.id,
enhancement.status
);
}
for assertion in enhancement.assertions {
let content = reviewed_asset_content(&assertion.target).with_context(|| {
format!(
"Codex enhancement {} targets missing asset {}",
enhancement.id, assertion.target
)
})?;
let raw = std::str::from_utf8(content)?;
if !raw.contains(&assertion.contains) {
bail!(
"Codex enhancement {} lost assertion {:?} in {}",
enhancement.id,
assertion.contains,
assertion.target
);
}
}
}
Ok(active_count)
}
pub fn audit_guidance(
source: Option<&Path>,
konnect_binary: &Path,
home: &Path,
) -> Result<OperationReport> {
validate_executable(konnect_binary, "Konnect")?;
let compatibility = compatibility()?;
let assets = discover_assets(source, home)?;
verify_upstream_baseline(&assets, &compatibility)?;
let guidance = guidance_fingerprint(&assets)?;
let hook = hook_fingerprint(konnect_binary)?;
let version = command_version(konnect_binary)?;
let expected_version = format!("konnect {}", compatibility.konnect_version);
if version != expected_version
|| guidance != compatibility.guidance_sha256
|| hook != compatibility.hook_sha256
{
bail!(
"Konnect guidance drift detected.\n version: {version} (expected {expected_version})\n guidance: {guidance} (expected {})\n hook: {hook} (expected {})\nReview the upstream changes before publishing a matching konnect-codex release.",
compatibility.guidance_sha256,
compatibility.hook_sha256
);
}
let mut report = OperationReport::default();
report.push(format!("Konnect version: {version}"));
report.push(format!("Reviewed commit: {}", compatibility.konnect_commit));
report.push(format!(
"Upstream baseline: {} files verified",
upstream_baseline()?.files.len()
));
report.push(format!("Guidance fingerprint: {guidance}"));
report.push(format!("Hook fingerprint: {hook}"));
report.push("Compatibility audit: PASS");
Ok(report)
}
fn verify_guidance_source(
source: &Path,
konnect_binary: &Path,
compatibility: &Compatibility,
) -> Result<()> {
let assets = discover_assets(Some(source), Path::new("."))?;
verify_upstream_baseline(&assets, compatibility)?;
let guidance = guidance_fingerprint(&assets)?;
let hook = hook_fingerprint(konnect_binary)?;
if guidance != compatibility.guidance_sha256 || hook != compatibility.hook_sha256 {
bail!(
"the requested source does not match reviewed Konnect v{}; run `konnect-codex audit --source {}` for details",
compatibility.konnect_version,
source.display()
);
}
Ok(())
}
fn command_version(konnect_binary: &Path) -> Result<String> {
let output = Command::new(konnect_binary).arg("--version").output()?;
if !output.status.success() {
bail!(
"Konnect --version failed: {}",
String::from_utf8_lossy(&output.stderr)
);
}
Ok(String::from_utf8(output.stdout)?.trim().to_string())
}
fn ensure_compatible_konnect_version(
installed_version: &str,
compatibility: &Compatibility,
) -> Result<()> {
let expected_version = format!("konnect {}", compatibility.konnect_version);
if installed_version != expected_version {
bail!(
"Konnect version mismatch: found `{installed_version}`, but konnect-codex v{} requires `{expected_version}`. Install Konnect v{} and run sync again; no plugin files were changed.",
env!("CARGO_PKG_VERSION"),
compatibility.konnect_version
);
}
Ok(())
}
fn hook_fingerprint(konnect_binary: &Path) -> Result<String> {
let output = Command::new(konnect_binary)
.args(["skill", "pre-pcb-ipc"])
.output()?;
if !output.status.success() {
bail!(
"Konnect could not provide pre-pcb-ipc guidance: {}",
String::from_utf8_lossy(&output.stderr)
);
}
Ok(sha256_bytes(&normalize_newlines(&output.stdout)))
}
fn guidance_fingerprint(assets: &SourceAssets) -> Result<String> {
let mut hasher = Sha256::new();
for (name, dir) in &assets.skills {
hasher.update(name.as_bytes());
for (relative, path) in walk_files(dir)? {
let relative = relative.to_string_lossy().replace('\\', "/");
hasher.update(relative.as_bytes());
hasher.update(normalize_newlines(&fs::read(path)?));
}
}
for path in &assets.agents {
hasher.update(path.file_name().unwrap().to_string_lossy().as_bytes());
hasher.update(normalize_newlines(&fs::read(path)?));
}
Ok(format!("{:x}", hasher.finalize()))
}
fn upstream_file_hashes(assets: &SourceAssets) -> Result<BTreeMap<String, String>> {
let mut files = BTreeMap::new();
for (name, dir) in &assets.skills {
for (relative, path) in walk_files(dir)? {
let relative = relative.to_string_lossy().replace('\\', "/");
files.insert(
format!("skills/{name}/{relative}"),
sha256_bytes(&normalize_newlines(&fs::read(path)?)),
);
}
}
for path in &assets.agents {
files.insert(
format!("agents/{}", path.file_name().unwrap().to_string_lossy()),
sha256_bytes(&normalize_newlines(&fs::read(path)?)),
);
}
Ok(files)
}
fn verify_upstream_baseline(assets: &SourceAssets, compatibility: &Compatibility) -> Result<()> {
let baseline = upstream_baseline()?;
if baseline.schema_version != 1
|| baseline.konnect_version != compatibility.konnect_version
|| baseline.konnect_commit != compatibility.konnect_commit
{
bail!("upstream baseline metadata does not match compatibility.json");
}
let expected: BTreeMap<_, _> = baseline
.files
.into_iter()
.map(|file| (file.path, file.sha256))
.collect();
let actual = upstream_file_hashes(assets)?;
let mut differences = Vec::new();
for (path, expected_hash) in &expected {
match actual.get(path) {
None => differences.push(format!("removed: {path}")),
Some(actual_hash) if actual_hash != expected_hash => {
differences.push(format!("modified: {path}"));
}
Some(_) => {}
}
}
for path in actual.keys() {
if !expected.contains_key(path) {
differences.push(format!("added: {path}"));
}
}
if !differences.is_empty() {
bail!(
"Konnect upstream baseline drift detected:\n {}\nReview every changed asset and update the baseline plus active enhancement decisions before release.",
differences.join("\n ")
);
}
Ok(())
}
fn reviewed_fingerprint(native_skills: &BTreeSet<String>, config: &[u8]) -> String {
let mut hasher = Sha256::new();
for (path, content) in REVIEWED_SKILL_FILES {
hasher.update(path.as_bytes());
hasher.update(normalize_newlines(content));
}
for (path, content) in REVIEWED_AGENT_FILES {
hasher.update(path.as_bytes());
hasher.update(normalize_newlines(content));
}
for name in native_skills {
hasher.update(b"native-skill\0");
hasher.update(name.as_bytes());
}
hasher.update(normalize_newlines(config));
format!("{:x}", hasher.finalize())
}
fn reviewed_skill_names() -> BTreeSet<String> {
REVIEWED_SKILL_FILES
.iter()
.filter_map(|(path, _)| Path::new(path).components().next())
.map(|component| component.as_os_str().to_string_lossy().into_owned())
.collect()
}
fn reviewed_domain_skill_count() -> usize {
reviewed_skill_names()
.into_iter()
.filter(|name| name != PLUGIN_NAME)
.count()
}
fn walk_files(root: &Path) -> Result<Vec<(PathBuf, PathBuf)>> {
fn visit(root: &Path, current: &Path, files: &mut Vec<(PathBuf, PathBuf)>) -> Result<()> {
for entry in fs::read_dir(current)? {
let entry = entry?;
let path = entry.path();
if entry.file_type()?.is_dir() {
visit(root, &path, files)?;
} else if entry.file_type()?.is_file() {
files.push((path.strip_prefix(root)?.to_path_buf(), path));
}
}
Ok(())
}
let mut files = Vec::new();
visit(root, root, &mut files)?;
files.sort_by(|a, b| a.0.cmp(&b.0));
Ok(files)
}
fn verify_sync_targets(
generated: &[GeneratedFile],
old: Option<&InstallManifest>,
paths: &CompanionPaths,
) -> Result<()> {
if let Some(old) = old.filter(|manifest| manifest.state == InstallState::Disabled) {
for file in old
.files
.iter()
.filter(|file| file.role == ManagedRole::Agent)
{
let disabled_path = paths
.disabled_agents_dir
.join(file.path.file_name().unwrap());
if disabled_path.exists() && hash_file(&disabled_path)? != file.sha256 {
bail!(
"disabled managed agent was modified; preserve it before sync: {}",
disabled_path.display()
);
}
}
}
for file in generated {
if !file.path.exists() {
continue;
}
let Some(old_file) =
old.and_then(|manifest| manifest.files.iter().find(|owned| owned.path == file.path))
else {
bail!(
"refusing to overwrite an unowned path: {}",
file.path.display()
);
};
let current_hash = hash_file(&file.path)?;
if current_hash != old_file.sha256 {
bail!(
"managed file was modified; preserve or remove it before sync: {}",
file.path.display()
);
}
}
Ok(())
}
fn remove_stale_owned_files(
generated: &[GeneratedFile],
old: Option<&InstallManifest>,
) -> Result<()> {
let generated_paths: BTreeSet<&Path> =
generated.iter().map(|file| file.path.as_path()).collect();
if let Some(old) = old {
for file in &old.files {
if !generated_paths.contains(file.path.as_path()) && file.path.exists() {
if hash_file(&file.path)? != file.sha256 {
bail!("stale managed file was modified: {}", file.path.display());
}
fs::remove_file(&file.path)?;
}
}
}
Ok(())
}
fn verify_manifest_files(
manifest: &InstallManifest,
paths: &CompanionPaths,
force: bool,
) -> Result<()> {
if force {
return Ok(());
}
let mut modified = Vec::new();
for file in &manifest.files {
let path = if manifest.state == InstallState::Disabled && file.role == ManagedRole::Agent {
paths
.disabled_agents_dir
.join(file.path.file_name().unwrap())
} else {
file.path.clone()
};
if path.exists() && hash_file(&path)? != file.sha256 {
modified.push(path);
}
}
if !modified.is_empty() {
bail!(
"managed files were modified; rerun with --force only if they may be discarded:\n{}",
modified
.iter()
.map(|path| format!(" {}", path.display()))
.collect::<Vec<_>>()
.join("\n")
);
}
Ok(())
}
fn patch_marketplace(path: &Path, is_update: bool) -> Result<bool> {
let created = !path.exists();
let mut root = if created {
json!({
"name": MARKETPLACE_NAME,
"interface": { "displayName": "Personal" },
"plugins": []
})
} else {
serde_json::from_str(&fs::read_to_string(path)?)?
};
let name = root
.get("name")
.and_then(JsonValue::as_str)
.context("personal marketplace is missing its name")?;
if name != MARKETPLACE_NAME {
bail!(
"marketplace at {} is named '{name}', expected '{MARKETPLACE_NAME}'",
path.display()
);
}
let plugins = root
.get_mut("plugins")
.and_then(JsonValue::as_array_mut)
.context("personal marketplace plugins field is not an array")?;
if let Some(existing) = plugins
.iter_mut()
.find(|entry| entry.get("name").and_then(JsonValue::as_str) == Some(PLUGIN_NAME))
{
if !is_update {
bail!("marketplace already contains an unowned '{PLUGIN_NAME}' entry");
}
let existing_path = existing
.pointer("/source/path")
.and_then(JsonValue::as_str)
.unwrap_or_default();
if existing_path != "./plugins/konnect-codex" {
bail!("existing '{PLUGIN_NAME}' marketplace entry points elsewhere");
}
*existing = marketplace_entry();
} else {
plugins.push(marketplace_entry());
}
write_atomic(path, &serde_json::to_vec_pretty(&root)?)?;
Ok(created)
}
fn marketplace_entry() -> JsonValue {
json!({
"name": PLUGIN_NAME,
"source": {
"source": "local",
"path": "./plugins/konnect-codex"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Productivity"
})
}
fn marketplace_has_owned_entry(path: &Path) -> Result<bool> {
if !path.exists() {
return Ok(false);
}
let root: JsonValue = serde_json::from_str(&fs::read_to_string(path)?)?;
Ok(root
.get("plugins")
.and_then(JsonValue::as_array)
.is_some_and(|plugins| plugins.iter().any(|entry| *entry == marketplace_entry())))
}
fn verify_marketplace_entry(path: &Path, force: bool) -> Result<()> {
if force || marketplace_has_owned_entry(path)? {
return Ok(());
}
bail!(
"the '{PLUGIN_NAME}' marketplace entry was removed or modified; use --force only if the plugin entry may be discarded"
)
}
fn remove_marketplace_entry(path: &Path, remove_empty_marketplace: bool) -> Result<()> {
if !path.exists() {
return Ok(());
}
let mut root: JsonValue = serde_json::from_str(&fs::read_to_string(path)?)?;
let plugins = root
.get_mut("plugins")
.and_then(JsonValue::as_array_mut)
.context("personal marketplace plugins field is not an array")?;
plugins.retain(|entry| entry.get("name").and_then(JsonValue::as_str) != Some(PLUGIN_NAME));
if remove_empty_marketplace && plugins.is_empty() {
fs::remove_file(path)?;
if let Some(parent) = path.parent() {
remove_dir_if_empty(parent)?;
}
} else {
write_atomic(path, &serde_json::to_vec_pretty(&root)?)?;
}
Ok(())
}
fn activate_plugin() -> Result<()> {
run_codex_plugin_command("add")
}
fn remove_plugin_registration() -> Result<()> {
run_codex_plugin_command("remove")
}
fn run_codex_plugin_command(action: &str) -> Result<()> {
let output = Command::new("codex")
.args(["plugin", action, "konnect-codex@personal", "--json"])
.output()
.with_context(|| "could not run the Codex CLI; ensure `codex` is on PATH")?;
if !output.status.success() {
bail!(
"`codex plugin {action}` failed: {}{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
}
Ok(())
}
fn load_manifest_if_present(path: &Path) -> Result<Option<InstallManifest>> {
if path.exists() {
Ok(Some(load_manifest(path)?))
} else {
Ok(None)
}
}
fn load_manifest(path: &Path) -> Result<InstallManifest> {
let raw = fs::read_to_string(path).with_context(|| {
format!(
"Konnect Codex plugin is not installed (missing {})",
path.display()
)
})?;
let manifest: InstallManifest = serde_json::from_str(&raw)?;
if manifest.schema_version != 1 {
bail!(
"unsupported plugin manifest schema {}",
manifest.schema_version
);
}
Ok(manifest)
}
fn write_manifest(path: &Path, manifest: &InstallManifest) -> Result<()> {
write_atomic(path, &serde_json::to_vec_pretty(manifest)?)
}
fn write_atomic(path: &Path, content: &[u8]) -> Result<()> {
let parent = path
.parent()
.with_context(|| format!("path has no parent: {}", path.display()))?;
fs::create_dir_all(parent)?;
let temp_path = parent.join(format!(
".{}.konnect-codex-{}.tmp",
path.file_name()
.unwrap_or_else(|| OsStr::new("file"))
.to_string_lossy(),
std::process::id()
));
{
let mut file = fs::File::create(&temp_path)?;
file.write_all(content)?;
file.sync_all()?;
}
if path.exists() {
fs::remove_file(path)?;
}
fs::rename(&temp_path, path)?;
Ok(())
}
fn validate_executable(path: &Path, label: &str) -> Result<()> {
if !path.is_file() {
bail!("{label} executable was not found: {}", path.display());
}
Ok(())
}
fn canonical_or_original(path: &Path) -> PathBuf {
let canonical = path.canonicalize().unwrap_or_else(|_| path.to_path_buf());
#[cfg(windows)]
{
let rendered = canonical.to_string_lossy();
if let Some(rest) = rendered.strip_prefix(r"\\?\UNC\") {
return PathBuf::from(format!(r"\\{rest}"));
}
if let Some(rest) = rendered.strip_prefix(r"\\?\") {
return PathBuf::from(rest);
}
}
canonical
}
fn sha256_bytes(content: &[u8]) -> String {
format!("{:x}", Sha256::digest(content))
}
fn normalize_newlines(content: &[u8]) -> Vec<u8> {
let mut normalized = Vec::with_capacity(content.len());
let mut index = 0;
while index < content.len() {
if content[index] == b'\r' && content.get(index + 1) == Some(&b'\n') {
normalized.push(b'\n');
index += 2;
} else {
normalized.push(content[index]);
index += 1;
}
}
normalized
}
fn hash_file(path: &Path) -> Result<String> {
Ok(sha256_bytes(&fs::read(path)?))
}
fn now_epoch_seconds() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
}
fn remove_tree_if_empty(root: &Path) -> Result<()> {
if !root.exists() {
return Ok(());
}
let mut dirs = Vec::new();
fn collect(path: &Path, dirs: &mut Vec<PathBuf>) -> Result<()> {
for entry in fs::read_dir(path)? {
let entry = entry?;
if entry.file_type()?.is_dir() {
collect(&entry.path(), dirs)?;
dirs.push(entry.path());
}
}
Ok(())
}
collect(root, &mut dirs)?;
dirs.sort_by_key(|path| std::cmp::Reverse(path.components().count()));
for dir in dirs {
remove_dir_if_empty(&dir)?;
}
remove_dir_if_empty(root)
}
fn remove_dir_if_empty(path: &Path) -> Result<()> {
if path.is_dir() && fs::read_dir(path)?.next().is_none() {
fs::remove_dir(path)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn release_versions_are_synchronized() {
let compatibility = compatibility().unwrap();
let policy = enhancement_policy().unwrap();
let baseline = upstream_baseline().unwrap();
assert_eq!(compatibility.konnect_version, env!("CARGO_PKG_VERSION"));
assert_eq!(compatibility.companion_revision, policy.companion_revision);
assert_eq!(policy.schema_version, 1);
assert_eq!(
policy.supported_konnect_version,
compatibility.konnect_version
);
assert_eq!(baseline.schema_version, 1);
assert_eq!(baseline.konnect_version, compatibility.konnect_version);
assert_eq!(baseline.konnect_commit, compatibility.konnect_commit);
let manifest: JsonValue = serde_json::from_str(PLUGIN_MANIFEST_TEMPLATE).unwrap();
assert_eq!(
manifest.get("version").and_then(JsonValue::as_str),
Some(env!("CARGO_PKG_VERSION"))
);
assert_eq!(compatibility.guidance_sha256.len(), 64);
assert_eq!(compatibility.hook_sha256.len(), 64);
}
#[test]
fn active_enhancements_are_present_in_reviewed_assets() {
let policy = enhancement_policy().unwrap();
let active: Vec<_> = policy
.enhancements
.iter()
.filter(|enhancement| enhancement.status == "active")
.collect();
assert_eq!(active.len(), 19);
let expected_ids = BTreeSet::from([
"agent-delegation",
"schematic-evidence-and-collision-gate",
"pcb-transfer-integrity",
"contradictory-verifier-gate",
"requirements-based-review-defaults",
"doctor-agent-reporting",
"native-auto-install-suppression",
"pcb-builder-delegation",
"freerouting-first-routing",
"pcb-live-state-and-placement-gates",
"custom-part-physical-pin-acceptance",
"visual-placement-checkpoint",
"offline-freerouting-bridge",
"pcb-ownership-preflight",
"eco-and-power-layout-branches",
"firmware-bringup-handoff",
"legacy-sourcing-and-review-evidence",
"evidence-grounded-review-methodology",
"bom-lifecycle-workflow",
]);
let actual_ids: BTreeSet<_> = active
.iter()
.map(|enhancement| enhancement.id.as_str())
.collect();
assert_eq!(actual_ids, expected_ids);
for enhancement in active {
for assertion in &enhancement.assertions {
let content = reviewed_asset_content(&assertion.target)
.unwrap_or_else(|| panic!("missing enhancement target {}", assertion.target));
let raw = std::str::from_utf8(content).unwrap();
assert!(
raw.contains(&assertion.contains),
"enhancement {} lost assertion {:?} in {}",
enhancement.id,
assertion.contains,
assertion.target
);
}
}
}
#[test]
fn upstream_baseline_has_unique_normalized_hashes_for_every_asset() {
let baseline = upstream_baseline().unwrap();
assert_eq!(baseline.files.len(), 17);
let mut paths = BTreeSet::new();
for file in baseline.files {
assert!(paths.insert(file.path));
assert_eq!(file.sha256.len(), 64);
assert!(file
.sha256
.chars()
.all(|character| character.is_ascii_hexdigit()));
}
}
#[test]
fn native_status_distinguishes_upstream_agents_from_plugin_agents() {
let temp = TempDir::new().unwrap();
let paths = CompanionPaths::for_home(temp.path().join("home"));
fs::create_dir_all(&paths.agents_dir).unwrap();
fs::write(
paths.agents_dir.join("konnect_design_reviewer.toml"),
"managed plugin agent",
)
.unwrap();
let report = native_status(&paths).unwrap().messages.join("\n");
assert!(report.contains("Upstream-native Konnect coverage"));
assert!(report.contains("0 agents"));
assert!(!report.contains("recognizable agents"));
}
#[test]
fn native_install_guard_restores_an_absent_marker_after_plugin_use() {
let temp = TempDir::new().unwrap();
let paths = CompanionPaths::for_home(temp.path().join("home"));
acquire_native_install_guard(&paths).unwrap();
assert_eq!(
fs::read_to_string(native_codex_install_marker(&paths)).unwrap(),
env!("CARGO_PKG_VERSION")
);
assert!(paths.native_install_guard_path.exists());
release_native_install_guard(&paths).unwrap();
assert!(!native_codex_install_marker(&paths).exists());
assert!(!paths.native_install_guard_path.exists());
}
#[test]
fn mcp_launch_repairs_the_native_install_guard_before_starting_konnect() {
let temp = TempDir::new().unwrap();
let paths = CompanionPaths::for_home(temp.path().join("home"));
let executable = std::env::current_exe().unwrap();
sync_with_version_probe(
SyncOptions {
paths: paths.clone(),
source: None,
konnect_binary: executable.clone(),
config_source: None,
adapter_binary: executable,
activate: false,
dry_run: false,
prefer_native_skills: false,
},
|_| Ok(format!("konnect {}", env!("CARGO_PKG_VERSION"))),
)
.unwrap();
let mut manifest = load_manifest(&paths.manifest_path).unwrap();
manifest.state = InstallState::Enabled;
write_manifest(&paths.manifest_path, &manifest).unwrap();
assert!(!native_codex_install_marker(&paths).exists());
assert!(!paths.native_install_guard_path.exists());
let _ = run_mcp(&paths).unwrap();
assert!(native_codex_install_marker(&paths).exists());
assert!(paths.native_install_guard_path.exists());
}
#[test]
fn compatibility_fingerprints_ignore_platform_newlines() {
assert_eq!(normalize_newlines(b"one\r\ntwo\r\n"), b"one\ntwo\n");
assert_eq!(normalize_newlines(b"one\ntwo\n"), b"one\ntwo\n");
}
#[test]
fn freerouting_outputs_never_replace_the_source_board_by_default() {
let board = Path::new("clock.kicad_pcb");
assert_eq!(
default_freerouted_board(board),
PathBuf::from("clock.freerouted.kicad_pcb")
);
}
#[test]
fn freerouting_dsn_sanitizer_removes_known_specctra_incompatible_characters() {
let temp = TempDir::new().unwrap();
let dsn = temp.path().join("board.dsn");
fs::write(&dsn, "(net \"5V_ΩµΦ\")\n").unwrap();
sanitize_freerouting_dsn(&dsn).unwrap();
assert_eq!(fs::read_to_string(dsn).unwrap(), "(net \"5V_\")\n");
}
#[test]
fn sync_rejects_a_missing_konnect_before_writing_files() {
let temp = TempDir::new().unwrap();
let paths = CompanionPaths::for_home(temp.path().join("home"));
let error = sync_with_version_probe(
SyncOptions {
paths: paths.clone(),
source: None,
konnect_binary: temp.path().join(exe_name_for_test("missing-konnect")),
config_source: None,
adapter_binary: std::env::current_exe().unwrap(),
activate: false,
dry_run: false,
prefer_native_skills: false,
},
|_| panic!("the version probe must not run for a missing executable"),
)
.unwrap_err();
assert!(error
.to_string()
.contains("Konnect executable was not found"));
assert!(!paths.plugin_dir.exists());
assert!(!paths.data_dir.exists());
}
#[test]
fn sync_rejects_a_mismatched_konnect_before_writing_files() {
let temp = TempDir::new().unwrap();
let paths = CompanionPaths::for_home(temp.path().join("home"));
let fake_konnect = temp.path().join(exe_name_for_test("konnect"));
fs::write(&fake_konnect, "fake").unwrap();
let existing_plugin = paths.plugin_dir.join("existing-plugin.txt");
let existing_state = paths.data_dir.join("existing-state.txt");
fs::create_dir_all(existing_plugin.parent().unwrap()).unwrap();
fs::create_dir_all(existing_state.parent().unwrap()).unwrap();
fs::write(&existing_plugin, "keep plugin").unwrap();
fs::write(&existing_state, "keep state").unwrap();
let error = sync_with_version_probe(
SyncOptions {
paths: paths.clone(),
source: None,
konnect_binary: fake_konnect,
config_source: None,
adapter_binary: std::env::current_exe().unwrap(),
activate: false,
dry_run: false,
prefer_native_skills: false,
},
|_| Ok("konnect 0.0.0".to_string()),
)
.unwrap_err();
assert!(error.to_string().contains("Konnect version mismatch"));
assert!(error.to_string().contains("no plugin files were changed"));
assert_eq!(fs::read_to_string(existing_plugin).unwrap(), "keep plugin");
assert_eq!(fs::read_to_string(existing_state).unwrap(), "keep state");
}
fn exe_name_for_test(stem: &str) -> String {
if cfg!(windows) {
format!("{stem}.exe")
} else {
stem.to_string()
}
}
#[test]
fn reviewed_skills_are_codex_native_and_complete() {
let names = reviewed_skill_names();
assert_eq!(names.len(), NATIVE_SKILLS.len() + 3);
assert!(names.contains(PLUGIN_NAME));
assert!(names.contains("kicad-bringup"));
assert!(names.contains("kicad-bom"));
for name in NATIVE_SKILLS {
assert!(names.contains(*name));
}
for (path, content) in REVIEWED_SKILL_FILES {
let raw = std::str::from_utf8(content).unwrap();
if path.ends_with("SKILL.md") {
assert!(raw.starts_with("---"), "{path} is missing frontmatter");
assert!(raw.contains("\nname:"), "{path} is missing name");
assert!(
raw.contains("\ndescription:"),
"{path} is missing description"
);
assert!(
!raw.contains("argument-hint:"),
"{path} has Claude-only frontmatter"
);
assert!(!raw.contains("Claude"), "{path} contains Claude wording");
}
}
}
#[test]
fn reviewed_agents_are_valid_codex_toml() {
assert_eq!(REVIEWED_AGENT_FILES.len(), 5);
let names: BTreeSet<_> = REVIEWED_AGENT_FILES.iter().map(|(path, _)| *path).collect();
assert_eq!(
names,
BTreeSet::from([
"konnect_design_reviewer.toml",
"konnect_bringup_planner.toml",
"konnect_library_builder.toml",
"konnect_pcb_builder.toml",
"konnect_schematic_builder.toml",
])
);
for (path, content) in REVIEWED_AGENT_FILES {
let raw = std::str::from_utf8(content).unwrap();
let value: toml::Value = toml::from_str(raw).unwrap();
assert!(
value.get("name").and_then(toml::Value::as_str).is_some(),
"{path}"
);
assert!(
value
.get("developer_instructions")
.and_then(toml::Value::as_str)
.is_some(),
"{path}"
);
assert!(!raw.contains("sonnet"), "{path} pins a Claude model");
assert!(!raw.contains("maxTurns"), "{path} has Claude-only limits");
}
}
#[test]
fn generated_config_preserves_values_and_enables_eager_tools() {
let temp = tempfile::NamedTempFile::new().unwrap();
fs::write(
temp.path(),
"kicad_cli = \"custom-kicad\"\neager_toolsets = false\ntransport = \"http\"\n",
)
.unwrap();
let rendered = render_config(Some(temp.path())).unwrap();
let value: toml::Value = toml::from_str(&rendered).unwrap();
assert_eq!(value["kicad_cli"].as_str(), Some("custom-kicad"));
assert_eq!(value["eager_toolsets"].as_bool(), Some(true));
assert_eq!(value["transport"].as_str(), Some("stdio"));
}
#[test]
fn prompt_hook_only_adds_context_for_relevant_work() {
let context = user_prompt_context("Build this KiCad schematic and PCB").unwrap();
assert!(context.contains("konnect_schematic_builder"));
assert!(context.contains("konnect_library_builder"));
assert!(context.contains("konnect_pcb_builder"));
assert!(context.contains("konnect_design_reviewer"));
assert!(context.contains("konnect_bringup_planner"));
assert!(context.contains("kicad-bom"));
assert!(context.contains("library -> schematic -> BOM -> PCB -> review -> bring-up"));
assert!(context.contains("Freerouting"));
assert!(context.contains("placement gate"));
assert!(user_prompt_context("Use Freerouting for this board").is_some());
assert!(user_prompt_context("Check MPN and BOM lifecycle risk").is_some());
assert!(user_prompt_context("Refactor my web API").is_none());
}
#[test]
fn pcb_hook_covers_whole_board_routing_and_state_changes() {
let hooks: JsonValue = serde_json::from_str(HOOKS_TEMPLATE).unwrap();
let matcher = hooks["hooks"]["PreToolUse"][0]["matcher"].as_str().unwrap();
for tool in [
"autoroute",
"update_pcb_from_schematic",
"delete_trace",
"add_zone",
"refill_zones",
] {
assert!(matcher.contains(tool), "PCB hook misses {tool}");
}
}
#[test]
fn marketplace_round_trip_preserves_unrelated_entries() {
let temp = TempDir::new().unwrap();
let path = temp.path().join("marketplace.json");
fs::write(
&path,
serde_json::to_vec_pretty(&json!({
"name": "personal",
"interface": {"displayName": "Mine"},
"plugins": [{"name": "other", "source": {"source": "local", "path": "./plugins/other"}}]
}))
.unwrap(),
)
.unwrap();
assert!(!patch_marketplace(&path, false).unwrap());
assert!(marketplace_has_owned_entry(&path).unwrap());
remove_marketplace_entry(&path, false).unwrap();
let root: JsonValue = serde_json::from_str(&fs::read_to_string(path).unwrap()).unwrap();
assert_eq!(root["interface"]["displayName"], "Mine");
assert_eq!(root["plugins"].as_array().unwrap().len(), 1);
assert_eq!(root["plugins"][0]["name"], "other");
}
#[test]
fn sync_and_uninstall_are_reversible_without_touching_native_skills() {
let temp = TempDir::new().unwrap();
let home = temp.path().join("home");
let native_skill = home
.join(".agents")
.join("skills")
.join("native-test")
.join("SKILL.md");
fs::create_dir_all(native_skill.parent().unwrap()).unwrap();
fs::write(&native_skill, "keep me").unwrap();
let fake_konnect = temp.path().join(if cfg!(windows) {
"konnect.exe"
} else {
"konnect"
});
fs::write(&fake_konnect, "fake").unwrap();
let config = temp.path().join("konnect.toml");
fs::write(&config, "log_level = \"debug\"\n").unwrap();
let paths = CompanionPaths::for_home(home);
let adapter = std::env::current_exe().unwrap();
sync_with_version_probe(
SyncOptions {
paths: paths.clone(),
source: None,
konnect_binary: fake_konnect,
config_source: Some(config),
adapter_binary: adapter,
activate: false,
dry_run: false,
prefer_native_skills: false,
},
|_| Ok(format!("konnect {}", env!("CARGO_PKG_VERSION"))),
)
.unwrap();
assert!(paths
.plugin_dir
.join("skills")
.join("konnect")
.join("SKILL.md")
.exists());
assert!(paths
.disabled_agents_dir
.join("konnect_schematic_builder.toml")
.exists());
assert!(paths
.disabled_agents_dir
.join("konnect_pcb_builder.toml")
.exists());
assert!(!paths
.agents_dir
.join("konnect_schematic_builder.toml")
.exists());
assert!(!paths.agents_dir.join("konnect_pcb_builder.toml").exists());
assert!(marketplace_has_owned_entry(&paths.marketplace_path).unwrap());
let native_konnect_skill = paths
.home
.join(".agents")
.join("skills")
.join("konnect")
.join("SKILL.md");
fs::create_dir_all(native_konnect_skill.parent().unwrap()).unwrap();
fs::write(&native_konnect_skill, "native Konnect skill").unwrap();
fs::create_dir_all(paths.home.join(".konnect")).unwrap();
fs::write(
paths.home.join(".konnect").join(".installed-codex"),
env!("CARGO_PKG_VERSION"),
)
.unwrap();
sync_with_version_probe(
SyncOptions {
paths: paths.clone(),
source: None,
konnect_binary: temp.path().join(if cfg!(windows) {
"konnect.exe"
} else {
"konnect"
}),
config_source: Some(temp.path().join("konnect.toml")),
adapter_binary: std::env::current_exe().unwrap(),
activate: false,
dry_run: false,
prefer_native_skills: true,
},
|_| Ok(format!("konnect {}", env!("CARGO_PKG_VERSION"))),
)
.unwrap();
assert!(!paths
.plugin_dir
.join("skills")
.join("konnect")
.join("SKILL.md")
.exists());
assert_eq!(
fs::read_to_string(&native_konnect_skill).unwrap(),
"native Konnect skill"
);
acquire_native_install_guard(&paths).unwrap();
assert!(paths.native_install_guard_path.exists());
uninstall(&paths, false).unwrap();
assert!(!paths.plugin_dir.exists());
assert!(!paths.data_dir.exists());
assert!(!paths.marketplace_path.exists());
assert_eq!(fs::read_to_string(native_skill).unwrap(), "keep me");
assert!(native_codex_install_marker(&paths).exists());
}
#[cfg(windows)]
#[test]
fn command_paths_do_not_use_windows_verbatim_prefixes() {
let current = std::env::current_exe().unwrap();
let normalized = canonical_or_original(¤t);
assert!(!normalized.to_string_lossy().starts_with(r"\\?\"));
assert!(normalized.is_absolute());
}
}
8388cccf6This file is pinned to the selected release. Download the original to open it in your design tool.