@@ -13,7 +13,7 @@ import {
1313 writeSkillLock ,
1414} from "bailian-cli-core" ;
1515import { emitBare , emitResult , formatTable } from "bailian-cli-runtime" ;
16- import { parseSkillNames } from "./shared.ts" ;
16+ import { parseSkillNames , runWithConcurrency } from "./shared.ts" ;
1717
1818interface UpdateOutcome {
1919 name : string ;
@@ -22,6 +22,9 @@ interface UpdateOutcome {
2222 reason ?: string ;
2323}
2424
25+ /** Max number of skills downloading/installing at the same time. */
26+ const UPDATE_CONCURRENCY = 3 ;
27+
2528export default defineCommand ( {
2629 description : "Update installed skills to the latest registry versions" ,
2730 auth : "none" ,
@@ -31,7 +34,7 @@ export default defineCommand({
3134 type : "string" ,
3235 valueHint : "<all|name,...>" ,
3336 description :
34- "Skills to update: all (default, only changed ones) or comma-separated names (force reinstall )" ,
37+ "Skills to update: all (default, only changed ones) or comma-separated names (force update installed skills )" ,
3538 } ,
3639 } ,
3740 exampleArgs : [ "" , "--name spark-video" ] ,
@@ -63,16 +66,25 @@ export default defineCommand({
6366 targets . push ( name ) ;
6467 }
6568 } else {
66- // Explicit names = force reinstall (equivalent to add if not yet installed)
67- targets . push ( ...requested ) ;
69+ // Explicit names: only update skills that are already installed; reject uninstalled ones
70+ for ( const name of requested ) {
71+ if ( ! lock . skills [ name ] ) {
72+ results . push ( {
73+ name,
74+ status : "failed" ,
75+ reason : "not installed; run bl skill add --name " + name + " first" ,
76+ } ) ;
77+ continue ;
78+ }
79+ targets . push ( name ) ;
80+ }
6881 }
6982
7083 const agents = detectInstalledAgents ( ) ;
71- for ( const name of targets ) {
84+ const tasks = targets . map ( ( name ) => async ( ) : Promise < UpdateOutcome > => {
7285 const entry = index . skills [ name ] ;
7386 if ( ! entry ) {
74- results . push ( { name, status : "failed" , reason : "skill not found in registry" } ) ;
75- continue ;
87+ return { name, status : "failed" , reason : "skill not found in registry" } ;
7688 }
7789 try {
7890 await installSkill ( name , entry ) ;
@@ -86,34 +98,36 @@ export default defineCommand({
8698 ...( entry . description ? { description : entry . description } : { } ) ,
8799 links : effective . map ( ( link ) => link . path ) ,
88100 } ;
89- results . push ( { name, status : "updated" , publishedAt : entry . publishedAt } ) ;
101+ return { name, status : "updated" , publishedAt : entry . publishedAt } ;
90102 } catch ( err ) {
91- results . push ( {
103+ return {
92104 name,
93105 status : "failed" ,
94106 reason : err instanceof Error ? err . message : String ( err ) ,
95- } ) ;
107+ } ;
96108 }
97- }
109+ } ) ;
110+ const updateResults = await runWithConcurrency ( tasks , UPDATE_CONCURRENCY ) ;
111+ results . push ( ...updateResults ) ;
98112 writeSkillLock ( lock ) ;
99113
100114 if ( format === "json" ) {
101115 emitResult ( { registry : getSkillRegistryBaseUrl ( ) , skills : results } , format ) ;
102116 } else if ( results . length === 0 ) {
103117 emitBare ( "No skills installed locally; run bl skill add first." ) ;
104118 } else {
105- const rows = results . map ( ( r ) => [
106- r . name ,
107- r . status ,
108- r . publishedAt ? r . publishedAt . slice ( 0 , 10 ) : "-" ,
109- r . reason ?? "-" ,
119+ const rows = results . map ( ( result ) => [
120+ result . name ,
121+ result . status ,
122+ result . publishedAt ? result . publishedAt . slice ( 0 , 10 ) : "-" ,
123+ result . reason ?? "-" ,
110124 ] ) ;
111125 for ( const line of formatTable ( [ "NAME" , "STATUS" , "PUBLISHED" , "REASON" ] , rows ) ) {
112126 emitBare ( line ) ;
113127 }
114128 }
115129
116- const failed = results . filter ( ( r ) => r . status === "failed" ) ;
130+ const failed = results . filter ( ( result ) => result . status === "failed" ) ;
117131 if ( failed . length > 0 ) {
118132 throw new BailianError (
119133 `${ failed . length } skill(s) failed to update` ,
0 commit comments