ma-agents 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/cli.js +98 -28
  2. package/package.json +1 -1
package/bin/cli.js CHANGED
@@ -120,60 +120,113 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath, fo
120
120
  const skills = listSkills();
121
121
  const agents = listAgents();
122
122
 
123
+ let selectedSkillIds = preselectedSkill ? [preselectedSkill] : [];
124
+ let selectedAgentIds = preselectedAgents || [];
125
+ let installScope = 'project';
126
+ let installPath = customPath || '';
127
+
128
+ const existingStatus = getStatus([], '', 'project');
129
+ let isUpdate = false;
130
+
131
+ // Step 0: Check for existing installation ONLY in interactive mode (no preselected skill)
132
+ if (!preselectedSkill && !customPath && existingStatus.length > 0) {
133
+ console.log(chalk.cyan('\n Existing installation detected in project.'));
134
+
135
+ const { action } = await prompts({
136
+ type: 'select',
137
+ name: 'action',
138
+ message: 'What would you like to do?',
139
+ choices: [
140
+ { title: 'Update (add/remove skills)', value: 'update' },
141
+ { title: 'Clean reinstall (remove all first)', value: 'reinstall' },
142
+ { title: 'Uninstall all skills', value: 'uninstall' },
143
+ { title: 'Cancel', value: 'cancel' }
144
+ ]
145
+ });
146
+
147
+ if (!action || action === 'cancel') process.exit(0);
148
+
149
+ if (action === 'uninstall') {
150
+ const { confirmed } = await prompts({
151
+ type: 'confirm',
152
+ name: 'confirmed',
153
+ message: 'This will remove all ma-agents skills from this project. Are you sure?',
154
+ initial: false
155
+ });
156
+ if (!confirmed) process.exit(0);
157
+
158
+ for (const entry of existingStatus) {
159
+ for (const skillId of Object.keys(entry.skills)) {
160
+ await uninstallSkill(skillId, [entry.agent.id], '', 'project');
161
+ }
162
+ }
163
+ console.log(chalk.bold.green('\n All skills successfully removed!\n'));
164
+ return;
165
+ }
166
+
167
+ if (action === 'reinstall') {
168
+ for (const entry of existingStatus) {
169
+ for (const skillId of Object.keys(entry.skills)) {
170
+ await uninstallSkill(skillId, [entry.agent.id], '', 'project');
171
+ }
172
+ }
173
+ console.log(chalk.gray(' Clean slate prepared.'));
174
+ }
175
+
176
+ if (action === 'update') {
177
+ isUpdate = true;
178
+ // Pre-populate selections from existing status
179
+ const existingSkillIds = new Set();
180
+ const existingAgentIds = new Set();
181
+ existingStatus.forEach(entry => {
182
+ existingAgentIds.add(entry.agent.id);
183
+ Object.keys(entry.skills).forEach(id => existingSkillIds.add(id));
184
+ });
185
+ selectedSkillIds = Array.from(existingSkillIds);
186
+ selectedAgentIds = Array.from(existingAgentIds);
187
+ }
188
+ }
189
+
123
190
  // Step 1: Select skills
124
- let selectedSkillIds;
125
- if (preselectedSkill) {
126
- selectedSkillIds = [preselectedSkill];
127
- } else {
191
+ if (selectedSkillIds.length === 0 || isUpdate) {
128
192
  const { skills: chosen } = await prompts({
129
193
  type: 'multiselect',
130
194
  name: 'skills',
131
- message: 'Which skills do you want to install?',
195
+ message: 'Select the skills you want to have installed:',
132
196
  choices: skills.map(s => ({
133
197
  title: chalk.white(s.name) + chalk.gray(` v${s.version} - ${s.description}`),
134
198
  value: s.id,
135
- selected: false
199
+ selected: selectedSkillIds.includes(s.id)
136
200
  })),
137
201
  instructions: chalk.gray(' Use space to select, enter to confirm'),
138
202
  min: 1
139
203
  });
140
204
 
141
- if (!chosen || chosen.length === 0) {
142
- console.log(chalk.yellow('No skills selected. Exiting.'));
143
- process.exit(0);
144
- }
205
+ if (!chosen) process.exit(0);
145
206
  selectedSkillIds = chosen;
146
207
  }
147
208
 
148
209
  // Step 2: Select agents
149
- let selectedAgentIds;
150
- if (preselectedAgents && preselectedAgents.length > 0) {
151
- selectedAgentIds = preselectedAgents;
152
- } else {
210
+ if (selectedAgentIds.length === 0 || isUpdate) {
153
211
  const { agents: chosen } = await prompts({
154
212
  type: 'multiselect',
155
213
  name: 'agents',
156
- message: 'Which coding agents do you want to install to?',
214
+ message: 'Select which coding agents to include:',
157
215
  choices: agents.map(a => ({
158
216
  title: chalk.white(a.name) + chalk.gray(` v${a.version} - ${a.description}`),
159
217
  value: a.id,
160
- selected: false
218
+ selected: selectedAgentIds.includes(a.id)
161
219
  })),
162
220
  instructions: chalk.gray(' Use space to select, enter to confirm'),
163
221
  min: 1
164
222
  });
165
223
 
166
- if (!chosen || chosen.length === 0) {
167
- console.log(chalk.yellow('No agents selected. Exiting.'));
168
- process.exit(0);
169
- }
224
+ if (!chosen) process.exit(0);
170
225
  selectedAgentIds = chosen;
171
226
  }
172
227
 
173
- // Step 3: Installation scope
174
- let installPath = customPath || '';
175
- let installScope = 'project';
176
- if (!installPath) {
228
+ // Step 3: Scope (Skip if update, we already know it's project)
229
+ if (!isUpdate && !installPath) {
177
230
  const { pathChoice } = await prompts({
178
231
  type: 'select',
179
232
  name: 'pathChoice',
@@ -209,16 +262,33 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath, fo
209
262
  const { confirmed } = await prompts({
210
263
  type: 'confirm',
211
264
  name: 'confirmed',
212
- message: 'Proceed with installation?',
265
+ message: isUpdate ? 'Apply changes?' : 'Proceed with installation?',
213
266
  initial: true
214
267
  });
215
268
 
216
269
  if (!confirmed) {
217
- console.log(chalk.yellow('Installation cancelled.'));
270
+ console.log(chalk.yellow('Operation cancelled.'));
218
271
  process.exit(0);
219
272
  }
220
273
 
221
- // Step 5: Install each skill (with upgrade detection)
274
+ // Step 5: Execution
275
+ if (isUpdate) {
276
+ // Identify removals
277
+ const currentlyInstalled = new Set();
278
+ existingStatus.forEach(entry => Object.keys(entry.skills).forEach(id => currentlyInstalled.add(id)));
279
+
280
+ const finalSkills = new Set(selectedSkillIds);
281
+ const toRemove = [...currentlyInstalled].filter(id => !finalSkills.has(id));
282
+
283
+ if (toRemove.length > 0) {
284
+ console.log(chalk.gray(`\n Cleaning up ${toRemove.length} removed skills...`));
285
+ for (const skillId of toRemove) {
286
+ await uninstallSkill(skillId, selectedAgentIds, installPath, 'project');
287
+ }
288
+ }
289
+ }
290
+
291
+ // Install requested skills
222
292
  for (const skillId of selectedSkillIds) {
223
293
  try {
224
294
  await installSkill(skillId, selectedAgentIds, installPath, installScope, { force: !!forceFlag });
@@ -227,7 +297,7 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath, fo
227
297
  }
228
298
  }
229
299
 
230
- console.log(chalk.bold.green('\n Installation complete!\n'));
300
+ console.log(chalk.bold.green('\n Done!\n'));
231
301
  }
232
302
 
233
303
  // --- Command handlers ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ma-agents",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "NPX tool to install skills for AI coding agents (Claude Code, Gemini, Copilot, Kilocode, Cline, Cursor)",
5
5
  "main": "index.js",
6
6
  "bin": {