Skip to content
Open
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
27 changes: 19 additions & 8 deletions crates/path-cli/src/cmd_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ use clap::Subcommand;
use std::path::PathBuf;

use crate::cache::{list_cached, remove_cached};
use crate::config::Config;
#[cfg(not(target_os = "emscripten"))]
use crate::{
artifact::{ArtifactRef, ArtifactType},
harness::HarnessBundle,
providers,
sync::{SyncObserver, SyncOutcome, sync_bundle},
};

Expand Down Expand Up @@ -43,15 +44,15 @@ pub enum CacheOp {
},
}

pub fn run(op: CacheOp) -> Result<()> {
pub fn run(op: CacheOp, config: &Config) -> Result<()> {
match op {
CacheOp::Ls => run_ls(),
CacheOp::Rm { id } => run_rm(&id),
CacheOp::Rm { id } => run_rm(&id, config),
#[cfg(not(target_os = "emscripten"))]
CacheOp::Sync {
types,
project_under,
} => run_sync(types, project_under),
} => run_sync(types, project_under, config),
}
}

Expand All @@ -67,24 +68,34 @@ fn run_ls() -> Result<()> {
Ok(())
}

fn run_rm(id: &str) -> Result<()> {
#[cfg_attr(target_os = "emscripten", expect(unused_variables))]
fn run_rm(id: &str, config: &Config) -> Result<()> {
remove_cached(id)?;
// The artifact is still real — downgrade its manifest record to
// "known, not cached" so the next sync can re-materialize it.
#[cfg(not(target_os = "emscripten"))]
if let Err(e) = crate::sync::evict_cache_id(id) {
if let Err(e) = config
.config_dir()
.and_then(|dir| crate::sync::evict_cache_id(&dir, id))
{
eprintln!("warning: sync manifest not updated: {e}");
}
eprintln!("Removed {id}");
Ok(())
}

#[cfg(not(target_os = "emscripten"))]
fn run_sync(types: Vec<ArtifactType>, project_under: Option<PathBuf>) -> Result<()> {
fn run_sync(
types: Vec<ArtifactType>,
project_under: Option<PathBuf>,
config: &Config,
) -> Result<()> {
let explicit = !types.is_empty();
let types = resolve_types(&types);
let bundle = HarnessBundle::from_environment();
let config_dir = config.config_dir()?;
let bundle = providers::harness_bundle(config);
let outcomes = sync_bundle(
&config_dir,
&bundle,
&types,
project_under.as_deref(),
Expand Down
6 changes: 4 additions & 2 deletions crates/path-cli/src/cmd_p.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use anyhow::Result;
use clap::Subcommand;
use std::path::PathBuf;

use crate::config::Config;

#[derive(Subcommand, Debug)]
pub enum PCommand {
/// List available sources (branches, projects, sessions)
Expand Down Expand Up @@ -93,7 +95,7 @@ pub enum PCommand {
},
}

pub fn run(command: PCommand, pretty: bool, config: &crate::config::Config) -> Result<()> {
pub fn run(command: PCommand, pretty: bool, config: &Config) -> Result<()> {
match command {
PCommand::List {
source,
Expand All @@ -102,7 +104,7 @@ pub fn run(command: PCommand, pretty: bool, config: &crate::config::Config) -> R
} => crate::cmd_list::run(source, format, json, config),
PCommand::Import { args } => crate::cmd_import::run(args, pretty),
PCommand::Export { target } => crate::cmd_export::run(target),
PCommand::Cache { op } => crate::cmd_cache::run(op),
PCommand::Cache { op } => crate::cmd_cache::run(op, config),
PCommand::Render { format } => crate::cmd_render::run(format),
PCommand::Merge { inputs, title } => crate::cmd_merge::run(inputs, title, pretty),
PCommand::Validate { input } => crate::cmd_validate::run(input),
Expand Down
17 changes: 16 additions & 1 deletion crates/path-cli/src/cmd_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,23 @@ fn sync_query_scope(args: &QueryArgs) {
if types.is_empty() {
return;
}
// Transitional: `query` does not take `&Config` yet; load one for
// the config directory. A load failure degrades like a sync failure.
let config_dir = match crate::config::Config::load().and_then(|c| c.config_dir()) {
Ok(dir) => dir,
Err(e) => {
eprintln!("warning: cache sync skipped: {e}");
return;
}
};
let bundle = crate::harness::HarnessBundle::from_environment();
match crate::sync::sync_bundle(&bundle, &types, args.project_under.as_deref(), &mut ()) {
match crate::sync::sync_bundle(
&config_dir,
&bundle,
&types,
args.project_under.as_deref(),
&mut (),
) {
Ok(outcomes) => {
for (t, o) in outcomes {
if o.new + o.updated + o.failed > 0 {
Expand Down
32 changes: 32 additions & 0 deletions crates/path-cli/src/providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
//! against `with_home`. The injected directory wins against both.

use crate::config::Config;
#[cfg(not(target_os = "emscripten"))]
use crate::harness::HarnessBundle;
use std::path::Path;

pub(crate) fn claude_convo(config: &Config) -> toolpath_claude::ClaudeConvo {
Expand Down Expand Up @@ -87,6 +89,23 @@ pub(crate) fn pi_convo(config: &Config, base: Option<&Path>) -> toolpath_pi::PiC
toolpath_pi::PiConvo::with_resolver(resolver)
}

/// The production [`HarnessBundle`], every provider built from
/// `config`. Each provider is included unconditionally (construction
/// does not fail on a missing home dir); consumers skip the ones whose
/// listing returns empty/NotFound.
#[cfg(not(target_os = "emscripten"))]
pub(crate) fn harness_bundle(config: &Config) -> HarnessBundle {
HarnessBundle {
claude: Some(claude_convo(config)),
gemini: Some(gemini_convo(config)),
codex: Some(codex_convo(config)),
copilot: Some(copilot_convo(config)),
opencode: Some(opencode_convo(config)),
cursor: Some(cursor_convo(config)),
pi: Some(pi_convo(config, None)),
}
}

#[cfg(all(test, not(target_os = "emscripten")))]
mod tests {
use super::*;
Expand Down Expand Up @@ -210,4 +229,17 @@ mod tests {
let manager = pi_convo(&config_with_home(), Some(Path::new("/pi/base")));
assert_eq!(manager.resolver().sessions_dir(), PathBuf::from("/pi/base"));
}

#[test]
fn harness_bundle_roots_providers_at_config_home() {
let bundle = harness_bundle(&config_with_home());
assert_eq!(
bundle.claude.unwrap().resolver().projects_dir().unwrap(),
PathBuf::from("/home/jailed/.claude/projects")
);
assert_eq!(
bundle.pi.unwrap().resolver().sessions_dir(),
PathBuf::from("/home/jailed/.pi/agent/sessions")
);
}
}
Loading
Loading