@@ -95,7 +95,9 @@ fn download_and_extract() -> Result<(), String> {
9595 return Err ( format ! ( "error downloading skill: HTTP {}" , resp. status( ) ) ) ;
9696 }
9797
98- let bytes = resp. bytes ( ) . map_err ( |e| format ! ( "error reading response: {e}" ) ) ?;
98+ let bytes = resp
99+ . bytes ( )
100+ . map_err ( |e| format ! ( "error reading response: {e}" ) ) ?;
99101
100102 // Extract into ~/.hotdata/skills/
101103 let store_dir = home_dir ( ) . join ( ".hotdata" ) . join ( "skills" ) ;
@@ -104,9 +106,15 @@ fn download_and_extract() -> Result<(), String> {
104106 let gz = flate2:: read:: GzDecoder :: new ( std:: io:: Cursor :: new ( bytes) ) ;
105107 let mut archive = tar:: Archive :: new ( gz) ;
106108
107- for entry in archive. entries ( ) . map_err ( |e| format ! ( "error reading archive: {e}" ) ) ? {
109+ for entry in archive
110+ . entries ( )
111+ . map_err ( |e| format ! ( "error reading archive: {e}" ) ) ?
112+ {
108113 let mut entry = entry. map_err ( |e| format ! ( "error reading archive entry: {e}" ) ) ?;
109- let path = entry. path ( ) . map_err ( |e| format ! ( "error reading entry path: {e}" ) ) ?. into_owned ( ) ;
114+ let path = entry
115+ . path ( )
116+ . map_err ( |e| format ! ( "error reading entry path: {e}" ) ) ?
117+ . into_owned ( ) ;
110118
111119 let rel = match path. strip_prefix ( "skills/" ) {
112120 Ok ( r) if !r. as_os_str ( ) . is_empty ( ) => r. to_path_buf ( ) ,
@@ -117,13 +125,14 @@ fn download_and_extract() -> Result<(), String> {
117125 if let Some ( parent) = dest. parent ( ) {
118126 fs:: create_dir_all ( parent) . map_err ( |e| format ! ( "error creating directory: {e}" ) ) ?;
119127 }
120- entry. unpack ( & dest) . map_err ( |e| format ! ( "error extracting {}: {e}" , rel. display( ) ) ) ?;
128+ entry
129+ . unpack ( & dest)
130+ . map_err ( |e| format ! ( "error extracting {}: {e}" , rel. display( ) ) ) ?;
121131 }
122132
123133 Ok ( ( ) )
124134}
125135
126-
127136fn copy_dir_recursive ( src : & PathBuf , dst : & PathBuf ) -> Result < ( ) , String > {
128137 fs:: create_dir_all ( dst) . map_err ( |e| format ! ( "error creating directory: {e}" ) ) ?;
129138 for entry in fs:: read_dir ( src) . map_err ( |e| format ! ( "error reading directory: {e}" ) ) ? {
@@ -147,8 +156,7 @@ fn ensure_symlink_or_copy(src: &PathBuf, link_path: &PathBuf) -> Result<bool, St
147156 // Remove any existing symlink or directory so we can (re)create it
148157 if link_path. symlink_metadata ( ) . is_ok ( ) {
149158 if link_path. is_symlink ( ) {
150- fs:: remove_file ( link_path)
151- . map_err ( |e| format ! ( "error removing old symlink: {e}" ) ) ?;
159+ fs:: remove_file ( link_path) . map_err ( |e| format ! ( "error removing old symlink: {e}" ) ) ?;
152160 } else {
153161 fs:: remove_dir_all ( link_path)
154162 . map_err ( |e| format ! ( "error removing old directory: {e}" ) ) ?;
@@ -198,7 +206,11 @@ pub fn install_project() {
198206 match read_installed_version ( ) {
199207 Some ( ref v) if * v >= current => { }
200208 Some ( ref v) => {
201- println ! ( "{}" , format!( "Global skill is outdated (v{v}), downloading v{current} first..." ) . yellow( ) ) ;
209+ println ! (
210+ "{}" ,
211+ format!( "Global skill is outdated (v{v}), downloading v{current} first..." )
212+ . yellow( )
213+ ) ;
202214 if let Err ( e) = download_and_extract ( ) {
203215 eprintln ! ( "{}" , e. red( ) ) ;
204216 std:: process:: exit ( 1 ) ;
@@ -218,26 +230,43 @@ pub fn install_project() {
218230
219231 // Always copy (not symlink) from store to .agents/skills/hotdata-cli
220232 if project_agents. exists ( ) {
221- fs:: remove_dir_all ( & project_agents)
222- . unwrap_or_else ( |e| { eprintln ! ( "{}" , format!( "error removing existing directory: {e}" ) . red( ) ) ; std:: process:: exit ( 1 ) ; } ) ;
233+ fs:: remove_dir_all ( & project_agents) . unwrap_or_else ( |e| {
234+ eprintln ! (
235+ "{}" ,
236+ format!( "error removing existing directory: {e}" ) . red( )
237+ ) ;
238+ std:: process:: exit ( 1 ) ;
239+ } ) ;
223240 }
224241 if let Some ( parent) = project_agents. parent ( ) {
225- fs:: create_dir_all ( parent) . unwrap_or_else ( |e| { eprintln ! ( "{}" , format!( "error creating directory: {e}" ) . red( ) ) ; std:: process:: exit ( 1 ) ; } ) ;
242+ fs:: create_dir_all ( parent) . unwrap_or_else ( |e| {
243+ eprintln ! ( "{}" , format!( "error creating directory: {e}" ) . red( ) ) ;
244+ std:: process:: exit ( 1 ) ;
245+ } ) ;
226246 }
227- copy_dir_recursive ( & store_path, & project_agents) . unwrap_or_else ( |e| { eprintln ! ( "{}" , e. red( ) ) ; std:: process:: exit ( 1 ) ; } ) ;
247+ copy_dir_recursive ( & store_path, & project_agents) . unwrap_or_else ( |e| {
248+ eprintln ! ( "{}" , e. red( ) ) ;
249+ std:: process:: exit ( 1 ) ;
250+ } ) ;
251+
252+ let rel_agents = project_agents. strip_prefix ( & cwd) . unwrap_or ( & project_agents) ;
228253
229- println ! ( "{}" , format!( "Skill installed to project (v{current})." ) . green( ) ) ;
230- println ! ( "{:<20}{}" , "Location:" , project_agents. display( ) . to_string( ) . cyan( ) ) ;
254+ println ! (
255+ "{}" ,
256+ format!( "Skill installed to project (v{current})." ) . green( )
257+ ) ;
258+ println ! ( "{:<20}{}" , "Location:" , rel_agents. display( ) . to_string( ) . cyan( ) ) ;
231259
232260 // For .claude and .pi in cwd: symlink (fallback copy) from .agents/skills/hotdata-cli
233261 for root in AGENT_ROOTS {
234262 let root_path = cwd. join ( root) ;
235263 if root_path. exists ( ) {
236264 let link_path = root_path. join ( "skills" ) . join ( SKILL_NAME ) ;
265+ let rel_link = link_path. strip_prefix ( & cwd) . unwrap_or ( & link_path) ;
237266 match ensure_symlink_or_copy ( & project_agents, & link_path) {
238- Ok ( true ) => println ! ( "{:<20}{}" , format!( "~ /{root}:" ) , link_path . display( ) . to_string( ) . cyan( ) ) ,
239- Ok ( false ) => println ! ( "{:<20}{} (copied)" , format!( "~ /{root}:" ) , link_path . display( ) . to_string( ) . cyan( ) ) ,
240- Err ( e) => eprintln ! ( "{}" , format!( "~ /{root}: failed: {e}" ) . red( ) ) ,
267+ Ok ( true ) => println ! ( "{:<20}{}" , format!( ". /{root}:" ) , rel_link . display( ) . to_string( ) . cyan( ) ) ,
268+ Ok ( false ) => println ! ( "{:<20}{} (copied)" , format!( ". /{root}:" ) , rel_link . display( ) . to_string( ) . cyan( ) ) ,
269+ Err ( e) => eprintln ! ( "{}" , format!( ". /{root}: failed: {e}" ) . red( ) ) ,
241270 }
242271 }
243272 }
@@ -253,7 +282,11 @@ pub fn install() {
253282 return ;
254283 }
255284 Some ( ref v) => {
256- println ! ( "{}" , format!( "Managed by skills agent — updating from v{v} to v{current}..." ) . yellow( ) ) ;
285+ println ! (
286+ "{}" ,
287+ format!( "Managed by skills agent — updating from v{v} to v{current}..." )
288+ . yellow( )
289+ ) ;
257290 }
258291 None => {
259292 println ! ( "Managed by skills agent — skipping." ) ;
@@ -278,7 +311,10 @@ pub fn install() {
278311
279312 let symlinks = ensure_symlinks ( ) ;
280313
281- println ! ( "{}" , format!( "Skill installed successfully (v{current})." ) . green( ) ) ;
314+ println ! (
315+ "{}" ,
316+ format!( "Skill installed successfully (v{current})." ) . green( )
317+ ) ;
282318 println ! ( "{:<20}{}" , "Location:" , skill_store_path( ) . display( ) ) ;
283319
284320 for ( label, path, result) in & symlinks {
@@ -295,7 +331,6 @@ pub fn status() {
295331 let store_path = skill_store_path ( ) ;
296332 let current = Version :: parse ( CURRENT_VERSION ) . expect ( "invalid package version" ) ;
297333
298- let managed = is_managed_by_skills_agent ( ) ;
299334 let installed_version = read_installed_version ( ) ;
300335 let exists = store_path. exists ( ) ;
301336
@@ -313,33 +348,43 @@ pub fn status() {
313348
314349 match & installed_version {
315350 Some ( v) if * v < current => {
316- row ( "Version" , & format ! ( "{} (outdated, current is v{current})" , v. to_string( ) . yellow( ) ) ) ;
351+ row (
352+ "Version" ,
353+ & format ! (
354+ "{} (outdated, current is v{current})" ,
355+ v. to_string( ) . yellow( )
356+ ) ,
357+ ) ;
317358 }
318359 Some ( v) => row ( "Version" , & v. to_string ( ) . green ( ) . to_string ( ) ) ,
319360 None => row ( "Version" , & "unknown" . dark_grey ( ) . to_string ( ) ) ,
320361 }
321362
322- row ( "Location" , & store_path. display ( ) . to_string ( ) . cyan ( ) . to_string ( ) ) ;
323- row ( "Managed by" , & if managed { "skills agent" . to_string ( ) } else { "direct" . dark_grey ( ) . to_string ( ) } ) ;
324-
325- // Show symlink status for each agent root
326363 let home = home_dir ( ) ;
364+
365+ // Collect installed agent skill paths
366+ let agents_path = agents_skill_path ( ) ;
367+ let mut installed_agents: Vec < String > = Vec :: new ( ) ;
368+
369+ if agents_path. exists ( ) {
370+ installed_agents. push ( "~/.agents" . to_string ( ) ) ;
371+ }
327372 for root in AGENT_ROOTS {
328- let root_path = home. join ( root) ;
329- let link_path = root_path. join ( "skills" ) . join ( SKILL_NAME ) ;
330- let label = format ! ( "~/{root}" ) ;
331-
332- if !root_path. exists ( ) {
333- row ( & label, & "not installed" . dark_grey ( ) . to_string ( ) ) ;
334- } else if link_path. is_symlink ( ) {
335- row ( & label, & link_path. display ( ) . to_string ( ) . cyan ( ) . to_string ( ) ) ;
336- } else if link_path. exists ( ) {
337- row ( & label, & "installed (not symlinked)" . yellow ( ) . to_string ( ) ) ;
338- } else {
339- row ( & label, & "agent detected, not symlinked" . yellow ( ) . to_string ( ) ) ;
373+ let link_path = home. join ( root) . join ( "skills" ) . join ( SKILL_NAME ) ;
374+ if link_path. exists ( ) {
375+ installed_agents. push ( format ! ( "~/{root}" ) ) ;
340376 }
341377 }
342378
379+ if installed_agents. is_empty ( ) {
380+ row ( "Agent Skills" , & "none" . dark_grey ( ) . to_string ( ) ) ;
381+ } else {
382+ row (
383+ "Agent Skills Added" ,
384+ & installed_agents. join ( ", " ) . cyan ( ) . to_string ( ) ,
385+ ) ;
386+ }
387+
343388 if installed_version. map_or ( false , |v| v < current) {
344389 println ! ( "\n Run 'hotdata skill install' to update." ) ;
345390 }
0 commit comments