Skip to content
Draft
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
96 changes: 96 additions & 0 deletions crates/tui/src/commands/groups/skills/skills.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,25 @@ use crate::skills::install::{
self, DEFAULT_MAX_SIZE_BYTES, DEFAULT_REGISTRY_URL, InstallOutcome, InstallSource,
RegistryFetchResult, SkillSyncOutcome, SyncResult, UpdateResult,
};
use crate::skills::probe::ProbeRegistry;
use crate::tui::app::{App, AppAction};
use crate::tui::history::HistoryCell;

use crate::commands::CommandResult;

<<<<<<< Updated upstream
=======
fn skill_readiness_badge(probes: &ProbeRegistry, name: &str) -> String {
let result = match probes.probe(name) {
Some(crate::skills::SkillReadiness::NeedsSetup) => " [needs setup]".to_string(),
Some(crate::skills::SkillReadiness::Partial) => " [partial]".to_string(),
_ => String::new(),
};
eprintln!("skill_readiness_badge({name}) = {result:?}");
result
}

>>>>>>> Stashed changes
#[cfg(test)]
thread_local! {
static TEST_HOME_DIR: std::cell::RefCell<Option<std::path::PathBuf>> =
Expand Down Expand Up @@ -83,6 +97,7 @@ fn visible_skill_directories(app: &App) -> Vec<std::path::PathBuf> {
}

fn inspect_skills(app: &mut App) -> CommandResult {
let probes = ProbeRegistry::new();
let mode = skill_discovery_mode(app);
let dirs = visible_skill_directories(app);
let registry = discover_visible_skills(app);
Expand Down Expand Up @@ -117,9 +132,26 @@ fn inspect_skills(app: &mut App) -> CommandResult {
} else {
for skill in registry.list() {
if skill.description.trim().is_empty() {
<<<<<<< Updated upstream
let _ = writeln!(output, " - {}", skill.name);
} else {
let _ = writeln!(output, " - {} — {}", skill.name, skill.description);
=======
let _ = writeln!(
output,
" - {}{}",
skill.name,
skill_readiness_badge(&probes, &skill.name)
);
} else {
let _ = writeln!(
output,
" - {} — {}{}",
skill.name,
skill.description,
skill_readiness_badge(&probes, &skill.name)
);
>>>>>>> Stashed changes
}
let _ = writeln!(output, " path: {}", skill.path.display());
}
Expand All @@ -135,7 +167,15 @@ fn inspect_skills(app: &mut App) -> CommandResult {
/// local cache (`~/.codewhale/cache/skills/`). Pass `inspect` to show local
/// discovery mode, searched directories, and skill source paths.
fn list_skills(app: &mut App, arg: Option<&str>) -> CommandResult {
let probes = ProbeRegistry::new();
let mut prefix: Option<String> = None;
<<<<<<< Updated upstream
=======
let _ = std::fs::write(
"D:\\codewhale_debug.txt",
format!("list_skills called, arg={arg:?}\n"),
);
>>>>>>> Stashed changes
if let Some(arg) = arg {
let trimmed = arg.trim();
if trimmed == "--remote" || trimmed == "remote" {
Expand Down Expand Up @@ -221,7 +261,17 @@ fn list_skills(app: &mut App, arg: Option<&str>) -> CommandResult {
if idx > 0 {
output.push('\n');
}
<<<<<<< Updated upstream
let _ = writeln!(output, " /{} - {}", skill.name, skill.description);
=======
let _ = writeln!(
output,
" /{} - {}{}",
skill.name,
skill.description,
skill_readiness_badge(&probes, &skill.name)
);
>>>>>>> Stashed changes
}
} else {
// Unfiltered view: partition into user-created and built-in so a
Expand All @@ -239,7 +289,17 @@ fn list_skills(app: &mut App, arg: Option<&str>) -> CommandResult {
if !user_skills.is_empty() {
let _ = writeln!(output, "Your skills ({}):", user_skills.len());
for skill in &user_skills {
<<<<<<< Updated upstream
let _ = writeln!(output, " /{} - {}", skill.name, skill.description);
=======
let _ = writeln!(
output,
" /{} - {}{}",
skill.name,
skill.description,
skill_readiness_badge(&probes, &skill.name)
);
>>>>>>> Stashed changes
}
if !bundled_skills.is_empty() {
output.push('\n');
Expand All @@ -255,7 +315,17 @@ fn list_skills(app: &mut App, arg: Option<&str>) -> CommandResult {
// likely getting their first look at the catalog.
if user_skills.is_empty() {
for skill in &bundled_skills {
<<<<<<< Updated upstream
let _ = writeln!(output, " /{} - {}", skill.name, skill.description);
=======
let _ = writeln!(
output,
" /{} - {}{}",
skill.name,
skill.description,
skill_readiness_badge(&probes, &skill.name)
);
>>>>>>> Stashed changes
}
} else {
let names: Vec<String> = bundled_skills
Expand Down Expand Up @@ -342,6 +412,7 @@ fn activate_skill(app: &mut App, name: &str) -> CommandResult {
// `/skill new` is a friendly alias for `/skill skill-creator`.
let name = if name == "new" { "skill-creator" } else { name };

let probes = ProbeRegistry::new();
let registry = discover_visible_skills(app);

if let Some(skill) = registry.get(name) {
Expand All @@ -350,6 +421,31 @@ fn activate_skill(app: &mut App, name: &str) -> CommandResult {
skill.name, skill.body
);

<<<<<<< Updated upstream
=======
eprintln!(
"activate skill: {name}, readiness probe={:?}",
probes.probe(name)
);
// If skill reports NeedsSetup, append readiness report instruction
if let Some(readiness) = probes.probe(name) {
use crate::skills::SkillReadiness;
if readiness == SkillReadiness::NeedsSetup || readiness == SkillReadiness::Partial {
let status = if readiness == SkillReadiness::NeedsSetup {
"needs setup"
} else {
"partial"
};
let report = format!(
"\n\n## ⚠ Readiness Notice\n\nThis skill reports as **{status}**. Required tools may be missing.\n\
After attempting the task, report whether the skill worked, what dependencies are \
missing, and whether installation was successful.\n\n---\n"
);
instruction.push_str(&report);
}
}

>>>>>>> Stashed changes
app.add_message(HistoryCell::System {
content: format!("Activated skill: {}\n\n{}", skill.name, skill.description),
});
Expand Down
39 changes: 38 additions & 1 deletion crates/tui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;

use std::collections::HashMap;
use crate::skills::probe::ProbeRegistry;
use std::io::{self, IsTerminal, Read, Write};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
Expand Down Expand Up @@ -3408,6 +3409,12 @@ async fn run_doctor(config: &Config, workspace: &Path, config_path_override: Opt
println!();
println!("{}", "Skills:".bold());
let global_skills_dir = config.skills_dir();
<<<<<<< Updated upstream
=======
// Ensure bundled skill probes are registered for doctor --json
let mut probes = crate::skills::probe::ProbeRegistry::new();
let _ = crate::skills::install_system_skills(&global_skills_dir, &mut probes);
>>>>>>> Stashed changes
let agents_skills_dir = workspace.join(".agents").join("skills");
let local_skills_dir = workspace.join("skills");
let agents_global_skills_dir = crate::skills::agents_global_skills_dir();
Expand Down Expand Up @@ -4707,6 +4714,12 @@ fn run_doctor_json(
};

let global_skills_dir = config.skills_dir();
<<<<<<< Updated upstream
=======
// Ensure bundled skill probes are registered for doctor --json
let mut probes = crate::skills::probe::ProbeRegistry::new();
let _ = crate::skills::install_system_skills(&global_skills_dir, &mut probes);
>>>>>>> Stashed changes
let agents_skills_dir = workspace.join(".agents").join("skills");
let local_skills_dir = workspace.join("skills");
let agents_global_skills_dir = crate::skills::agents_global_skills_dir();
Expand Down Expand Up @@ -4779,6 +4792,29 @@ fn run_doctor_json(
let (code_home, legacy_home) = doctor_state_roots();
let legacy_state_report = doctor_legacy_state_report(&code_home, &legacy_home);

<<<<<<< Updated upstream
=======
let skill_items: Vec<serde_json::Value> = {
let registry = crate::skills::discover_for_workspace_and_dir_with_mode(
workspace,
&selected_skills_dir,
crate::skills::SkillDiscoveryMode::Compatible,
);
registry
.list()
.iter()
.map(|skill| {
serde_json::json!({
"name": skill.name,
"readiness": probes.probe(&skill.name)
.unwrap_or(crate::skills::SkillReadiness::Unknown),
"required_tools": probes.query_tools(&skill.name),
})
})
.collect()
};

>>>>>>> Stashed changes
let report = json!({
"version": env!("CARGO_PKG_VERSION"),
"config_path": config_path.display().to_string(),
Expand Down Expand Up @@ -7479,7 +7515,8 @@ async fn run_interactive(
// Auto-install bundled system skills (e.g. skill-creator) on first launch.
// Errors are non-fatal: log a warning and continue.
let skills_dir = config.skills_dir();
if let Err(e) = crate::skills::install_system_skills(&skills_dir) {
let mut probes = ProbeRegistry::new();
if let Err(e) = crate::skills::install_system_skills(&skills_dir, &mut probes) {
logging::warn(format!("Failed to install system skills: {e}"));
}

Expand Down
5 changes: 5 additions & 0 deletions crates/tui/src/runtime_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ use crate::session_manager::default_sessions_dir;
#[cfg(test)]
pub(super) use crate::session_manager::{SavedSession, SessionMetadata};
use crate::skill_state::SkillStateStore;
use crate::skills::probe::ProbeRegistry;
use crate::task_manager::{
NewTaskRequest, SharedTaskManager, TaskManager, TaskManagerConfig, TaskRecord, TaskSummary,
};
Expand Down Expand Up @@ -1382,6 +1383,10 @@ async fn list_skills(
path: skill.path.clone(),
enabled: skill_state.is_enabled(&skill.name),
is_bundled: skill_entry_is_bundled(skill, &skills_dir),
<<<<<<< Updated upstream
=======
readiness: crate::skills::SkillReadiness::Unknown,
>>>>>>> Stashed changes
})
.collect();
Ok(Json(SkillsResponse {
Expand Down
Loading