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
4 changes: 3 additions & 1 deletion rust/crates/api/src/providers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,9 @@ pub fn detect_provider_kind(model: &str) -> ProviderKind {
pub const fn model_family_identity_for_kind(kind: ProviderKind) -> runtime::ModelFamilyIdentity {
match kind {
ProviderKind::Anthropic => runtime::ModelFamilyIdentity::Claude,
ProviderKind::Xai | ProviderKind::OpenAi => runtime::ModelFamilyIdentity::Generic,
ProviderKind::Xai | ProviderKind::OpenAi | ProviderKind::Ollama => {
runtime::ModelFamilyIdentity::Generic
}
}
}

Expand Down
5 changes: 4 additions & 1 deletion rust/crates/runtime/src/session_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ impl SessionStore {
/// The on-disk layout becomes `<cwd>/.hackcode/sessions/<workspace_hash>/`.
pub fn from_cwd(cwd: impl AsRef<Path>) -> Result<Self, SessionControlError> {
let cwd = cwd.as_ref();
let sessions_root = cwd
// #151: canonicalize cwd for consistent fingerprinting across
// equivalent path representations.
let canonical_cwd = fs::canonicalize(cwd).unwrap_or_else(|_| cwd.to_path_buf());
let sessions_root = canonical_cwd
.join(".hackcode")
.join("sessions")
.join(workspace_fingerprint(&canonical_cwd));
Expand Down
102 changes: 100 additions & 2 deletions rust/crates/rusty-claude-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ error: {message}
Run `hackcode --help` for usage."
);
}
}
std::process::exit(1);
}
}
Expand Down Expand Up @@ -391,7 +392,10 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
allow_broad_cwd,
)?;
}
CliAction::HelpTopic(topic) => print_help_topic(topic),
CliAction::HelpTopic {
topic,
output_format,
} => print_help_topic(topic, output_format)?,
CliAction::Help { output_format } => print_help(output_format)?,
CliAction::Setup => setup::run_setup()?,
CliAction::Scan => {
Expand Down Expand Up @@ -502,7 +506,10 @@ enum CliAction {
reasoning_effort: Option<String>,
allow_broad_cwd: bool,
},
HelpTopic(LocalHelpTopic),
HelpTopic {
topic: LocalHelpTopic,
output_format: CliOutputFormat,
},
Setup,
Scan,
Update,
Expand Down Expand Up @@ -6131,6 +6138,20 @@ fn collect_sessions_from_dir(
if !directory.exists() {
return Ok(());
}
// #148: classify the workspace lifecycle once (saved-only / idle-shell /
// running-process, plus dirty-worktree and abandoned flags) and share it
// across the sessions in this store — they all belong to the same
// workspace-fingerprinted directory.
let lifecycle = env::current_dir()
.map(|cwd| classify_session_lifecycle_for(&cwd))
.unwrap_or(SessionLifecycleSummary {
kind: SessionLifecycleKind::SavedOnly,
pane_id: None,
pane_command: None,
pane_path: None,
workspace_dirty: false,
abandoned: false,
});
for entry in fs::read_dir(directory)? {
let entry = entry?;
let path = entry.path();
Expand Down Expand Up @@ -6180,6 +6201,7 @@ fn collect_sessions_from_dir(
message_count,
parent_session_id,
branch_name,
lifecycle: lifecycle.clone(),
});
}
Ok(())
Expand Down Expand Up @@ -6426,6 +6448,82 @@ fn print_status_snapshot(
Ok(())
}

/// #148: where the resolved model string originated from.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ModelSource {
Flag,
Env,
Config,
Default,
}

impl ModelSource {
fn as_str(self) -> &'static str {
match self {
ModelSource::Flag => "flag",
ModelSource::Env => "env",
ModelSource::Config => "config",
ModelSource::Default => "default",
}
}
}

/// #148: provenance of the model selection — the resolved (alias-expanded)
/// name, the raw user/env input before resolution, and which source won.
#[derive(Debug, Clone)]
struct ModelProvenance {
resolved: String,
raw: Option<String>,
source: ModelSource,
}

impl ModelProvenance {
/// Probe the model-selection env vars; attribute to env when one is set,
/// otherwise treat the resolved model as a built-in default.
fn from_env_or_config_or_default(model: &str) -> Self {
for key in ["HACKCODE_MODEL", "CLAW_MODEL", "ANTHROPIC_MODEL"] {
if let Ok(value) = std::env::var(key) {
let trimmed = value.trim();
if !trimmed.is_empty() {
return Self {
resolved: model.to_string(),
raw: Some(trimmed.to_string()),
source: ModelSource::Env,
};
}
}
}
Self {
resolved: model.to_string(),
raw: None,
source: ModelSource::Default,
}
}
}

/// #148: compute the stale-base commit state for `cwd`, honoring an optional
/// `--base-commit` flag value (falls back to the `.hackcode-base` file).
fn stale_base_state_for(cwd: &Path, flag_value: Option<&str>) -> BaseCommitState {
let source = resolve_expected_base(flag_value, cwd);
check_base_commit(cwd, source.as_ref())
}

/// #148: render a [`BaseCommitState`] as a JSON object with a stable `status`
/// token and a `fresh` boolean (false only when the worktree has diverged).
fn stale_base_json_value(state: &BaseCommitState) -> serde_json::Value {
match state {
BaseCommitState::Matches => json!({ "status": "matches", "fresh": true }),
BaseCommitState::NoExpectedBase => json!({ "status": "no_expected_base", "fresh": true }),
BaseCommitState::NotAGitRepo => json!({ "status": "not_a_git_repo", "fresh": true }),
BaseCommitState::Diverged { expected, actual } => json!({
"status": "diverged",
"fresh": false,
"expected": expected,
"actual": actual,
}),
}
}

fn status_json_value(
model: Option<&str>,
usage: StatusUsage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fn status_command_applies_model_and_permission_mode_flags() {
fs::create_dir_all(&temp_dir).expect("temp dir should exist");

// when
let output = Command::new(env!("CARGO_BIN_EXE_claw"))
let output = Command::new(env!("CARGO_BIN_EXE_hackcode"))
.current_dir(&temp_dir)
.args([
"--model",
Expand Down Expand Up @@ -45,7 +45,7 @@ fn resume_flag_loads_a_saved_session_and_dispatches_status() {
let session_path = write_session(&temp_dir, "resume-status");

// when
let output = Command::new(env!("CARGO_BIN_EXE_claw"))
let output = Command::new(env!("CARGO_BIN_EXE_hackcode"))
.current_dir(&temp_dir)
.args([
"--resume",
Expand Down Expand Up @@ -73,12 +73,12 @@ fn slash_command_names_match_known_commands_and_suggest_nearby_unknown_ones() {
fs::create_dir_all(&temp_dir).expect("temp dir should exist");

// when
let help_output = Command::new(env!("CARGO_BIN_EXE_claw"))
let help_output = Command::new(env!("CARGO_BIN_EXE_hackcode"))
.current_dir(&temp_dir)
.arg("/help")
.output()
.expect("claw should launch");
let unknown_output = Command::new(env!("CARGO_BIN_EXE_claw"))
let unknown_output = Command::new(env!("CARGO_BIN_EXE_hackcode"))
.current_dir(&temp_dir)
.arg("/zstats")
.output()
Expand Down Expand Up @@ -109,7 +109,7 @@ fn omc_namespaced_slash_commands_surface_a_targeted_compatibility_hint() {
let temp_dir = unique_temp_dir("slash-dispatch-omc");
fs::create_dir_all(&temp_dir).expect("temp dir should exist");

let output = Command::new(env!("CARGO_BIN_EXE_claw"))
let output = Command::new(env!("CARGO_BIN_EXE_hackcode"))
.current_dir(&temp_dir)
.arg("/oh-my-claudecode:hud")
.output()
Expand Down Expand Up @@ -259,7 +259,7 @@ fn local_subcommand_help_does_not_fall_through_to_runtime_or_provider_calls() {
}

fn command_in(cwd: &Path) -> Command {
let mut command = Command::new(env!("CARGO_BIN_EXE_claw"));
let mut command = Command::new(env!("CARGO_BIN_EXE_hackcode"));
command.current_dir(cwd);
command
}
Expand Down
2 changes: 1 addition & 1 deletion rust/crates/rusty-claude-cli/tests/compact_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ fn run_claw(
base_url: &str,
args: &[&str],
) -> Output {
let mut command = Command::new(env!("CARGO_BIN_EXE_claw"));
let mut command = Command::new(env!("CARGO_BIN_EXE_hackcode"));
command
.current_dir(cwd)
.env_clear()
Expand Down
2 changes: 1 addition & 1 deletion rust/crates/rusty-claude-cli/tests/compact_repl_panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ fn run_claw_repl(
home: &std::path::Path,
stdin: &str,
) -> Output {
let mut command = python_pty_command(env!("CARGO_BIN_EXE_claw"));
let mut command = python_pty_command(env!("CARGO_BIN_EXE_hackcode"));
let mut child = command
.current_dir(cwd)
.env_clear()
Expand Down
2 changes: 1 addition & 1 deletion rust/crates/rusty-claude-cli/tests/mock_parity_harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ struct ScenarioReport {
}

fn run_case(case: ScenarioCase, workspace: &HarnessWorkspace, base_url: &str) -> ScenarioRun {
let mut command = Command::new(env!("CARGO_BIN_EXE_claw"));
let mut command = Command::new(env!("CARGO_BIN_EXE_hackcode"));
command
.current_dir(&workspace.root)
.env_clear()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ fn assert_json_command_with_env(current_dir: &Path, args: &[&str], envs: &[(&str
}

fn run_claw(current_dir: &Path, args: &[&str], envs: &[(&str, &str)]) -> Output {
let mut command = Command::new(env!("CARGO_BIN_EXE_claw"));
let mut command = Command::new(env!("CARGO_BIN_EXE_hackcode"));
command.current_dir(current_dir).args(args);
for (key, value) in envs {
command.env(key, value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ fn workspace_session(root: &Path) -> Session {
}

fn run_claw_with_env(current_dir: &Path, args: &[&str], envs: &[(&str, &str)]) -> Output {
let mut command = Command::new(env!("CARGO_BIN_EXE_claw"));
let mut command = Command::new(env!("CARGO_BIN_EXE_hackcode"));
command.current_dir(current_dir).args(args);
for (key, value) in envs {
command.env(key, value);
Expand Down