agentic-workflow-manager 3.1.0 → 3.2.1

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 (92) hide show
  1. package/dist/src/commands/add.js +84 -0
  2. package/dist/src/commands/agent.js +95 -0
  3. package/dist/src/commands/backup.js +46 -0
  4. package/dist/src/commands/doctor.js +80 -4
  5. package/dist/src/commands/hooks/claude.js +192 -0
  6. package/dist/src/commands/hooks/codex.js +191 -0
  7. package/dist/src/commands/hooks/index.js +23 -8
  8. package/dist/src/commands/hooks/install.js +11 -107
  9. package/dist/src/commands/hooks/resync.js +28 -18
  10. package/dist/src/commands/hooks/shared.js +94 -0
  11. package/dist/src/commands/hooks/status.js +13 -64
  12. package/dist/src/commands/hooks/uninstall.js +11 -47
  13. package/dist/src/commands/init.js +90 -28
  14. package/dist/src/commands/registry/index.js +1 -1
  15. package/dist/src/commands/sync.js +113 -0
  16. package/dist/src/commands/update.js +107 -0
  17. package/dist/src/core/agent-targets.js +50 -0
  18. package/dist/src/core/artifact-state.js +65 -0
  19. package/dist/src/core/atomic-file.js +72 -0
  20. package/dist/src/core/bundle-install.js +60 -38
  21. package/dist/src/core/bundles.js +21 -0
  22. package/dist/src/core/context/managed-block.js +190 -0
  23. package/dist/src/core/context/orchestrator.js +3 -1
  24. package/dist/src/core/context/project-constitution-inject.js +1 -1
  25. package/dist/src/core/context/regenerate.js +3 -3
  26. package/dist/src/core/context/strategies/codex-agents.js +100 -0
  27. package/dist/src/core/diagnostics/checks.js +16 -0
  28. package/dist/src/core/diagnostics/context.js +37 -8
  29. package/dist/src/core/diagnostics/provider-checks.js +219 -0
  30. package/dist/src/core/executor.js +30 -9
  31. package/dist/src/core/init/mutation-targets.js +139 -0
  32. package/dist/src/core/init/provider-facts.js +179 -0
  33. package/dist/src/core/init/steps.js +65 -9
  34. package/dist/src/core/install-planner.js +206 -0
  35. package/dist/src/core/install-transaction.js +405 -0
  36. package/dist/src/core/profile.js +1 -1
  37. package/dist/src/core/provider-artifacts.js +54 -0
  38. package/dist/src/core/provider-version.js +36 -0
  39. package/dist/src/core/reconciliation.js +61 -0
  40. package/dist/src/core/registries.js +10 -0
  41. package/dist/src/core/renderers/canonical-agent.js +27 -0
  42. package/dist/src/core/renderers/codex-agent.js +60 -0
  43. package/dist/src/core/skill-integrity.js +4 -3
  44. package/dist/src/index.js +97 -289
  45. package/dist/src/providers/index.js +143 -39
  46. package/dist/src/ui/provider-preflight.js +26 -0
  47. package/dist/src/utils/config.js +101 -9
  48. package/dist/tests/commands/agent.test.js +103 -0
  49. package/dist/tests/commands/backup.test.js +109 -0
  50. package/dist/tests/commands/doctor.test.js +65 -4
  51. package/dist/tests/commands/hooks/codex.test.js +230 -0
  52. package/dist/tests/commands/hooks/install.test.js +20 -1
  53. package/dist/tests/commands/hooks/resync.test.js +53 -6
  54. package/dist/tests/commands/hooks/status.test.js +10 -0
  55. package/dist/tests/commands/hooks/uninstall.test.js +12 -0
  56. package/dist/tests/commands/init.test.js +195 -8
  57. package/dist/tests/commands/multi-agent-targeting.test.js +231 -0
  58. package/dist/tests/core/agent-targets.test.js +45 -0
  59. package/dist/tests/core/artifact-state.test.js +144 -0
  60. package/dist/tests/core/atomic-file.test.js +96 -0
  61. package/dist/tests/core/bundle-install.test.js +208 -17
  62. package/dist/tests/core/bundles.test.js +12 -0
  63. package/dist/tests/core/context/managed-block.test.js +90 -0
  64. package/dist/tests/core/context/orchestrator.test.js +48 -3
  65. package/dist/tests/core/context/regenerate.test.js +1 -1
  66. package/dist/tests/core/context/strategies/codex-agents.test.js +185 -0
  67. package/dist/tests/core/context/strategies/config-instructions.test.js +1 -1
  68. package/dist/tests/core/diagnostics/checks.test.js +19 -0
  69. package/dist/tests/core/diagnostics/context.test.js +74 -0
  70. package/dist/tests/core/diagnostics/provider-checks.test.js +252 -0
  71. package/dist/tests/core/executor.test.js +20 -0
  72. package/dist/tests/core/export/engine.test.js +3 -2
  73. package/dist/tests/core/init/mutation-targets.test.js +235 -0
  74. package/dist/tests/core/init/orchestrator.test.js +1 -1
  75. package/dist/tests/core/init/provider-facts.test.js +177 -0
  76. package/dist/tests/core/init/steps.test.js +106 -2
  77. package/dist/tests/core/install-planner.test.js +224 -0
  78. package/dist/tests/core/install-transaction.test.js +257 -0
  79. package/dist/tests/core/provider-artifacts.test.js +64 -0
  80. package/dist/tests/core/provider-version.test.js +53 -0
  81. package/dist/tests/core/reconciliation.test.js +165 -0
  82. package/dist/tests/core/registries-sync.test.js +4 -4
  83. package/dist/tests/core/renderers/canonical-agent.test.js +58 -0
  84. package/dist/tests/core/renderers/codex-agent.test.js +128 -0
  85. package/dist/tests/core/versioning.test.js +1 -1
  86. package/dist/tests/integration/codex-provider-isolated.test.js +235 -0
  87. package/dist/tests/providers/hooks-config.test.js +43 -25
  88. package/dist/tests/providers/index.test.js +144 -39
  89. package/dist/tests/structural/codex-agent-escaping-completeness.test.js +77 -0
  90. package/dist/tests/ui/provider-preflight.test.js +25 -0
  91. package/dist/tests/utils/config.test.js +178 -18
  92. package/package.json +1 -1
@@ -88,10 +88,11 @@ function repairGlobalSkills(skillsDir, registryContentDirs) {
88
88
  * único agente target. Cada provider en su propio path; un dir ausente se omite. */
89
89
  function reconcileAllSkillLinks(registryContentDirs) {
90
90
  const out = [];
91
- for (const agent of Object.keys(providers_1.PROVIDERS)) {
92
- if (!fs_1.default.existsSync(providers_1.PROVIDERS[agent].skill.global))
91
+ for (const agent of providers_1.AGENT_TARGETS) {
92
+ const skillsDir = (0, providers_1.providerFor)(agent).skill.global;
93
+ if (!fs_1.default.existsSync(skillsDir))
93
94
  continue;
94
- out.push({ agent, result: repairGlobalSkills(providers_1.PROVIDERS[agent].skill.global, registryContentDirs) });
95
+ out.push({ agent, result: repairGlobalSkills(skillsDir, registryContentDirs) });
95
96
  }
96
97
  return out;
97
98
  }
package/dist/src/index.js CHANGED
@@ -13,31 +13,33 @@ const tty_1 = require("./ui/tty");
13
13
  const picker_1 = require("./ui/picker");
14
14
  const providers_1 = require("./providers");
15
15
  const executor_1 = require("./core/executor");
16
- const regenerate_1 = require("./core/context/regenerate");
16
+ const provider_artifacts_1 = require("./core/provider-artifacts");
17
+ const provider_preflight_1 = require("./ui/provider-preflight");
17
18
  const discovery_1 = require("./core/discovery");
18
19
  const bundles_1 = require("./core/bundles");
19
- const skill_integrity_1 = require("./core/skill-integrity");
20
20
  const registries_1 = require("./core/registries");
21
- const cli_version_1 = require("./core/cli-version");
22
- const bundle_install_1 = require("./core/bundle-install");
23
- const profile_1 = require("./core/profile");
24
21
  const path_1 = __importDefault(require("path"));
25
22
  const picocolors_1 = __importDefault(require("picocolors"));
26
23
  const fs_1 = __importDefault(require("fs"));
27
24
  const story_map_parser_1 = require("./core/story-map-parser");
28
25
  const miro_1 = require("./core/miro");
29
26
  const hooks_1 = require("./commands/hooks");
30
- const resync_1 = require("./commands/hooks/resync");
31
27
  const sensors_1 = require("./commands/sensors");
32
28
  const ledger_1 = require("./commands/ledger");
33
29
  const doctor_1 = require("./commands/doctor");
30
+ const backup_1 = require("./commands/backup");
34
31
  const init_1 = require("./commands/init");
35
32
  const registry_1 = require("./commands/registry");
36
33
  const pin_1 = require("./commands/pin");
37
34
  const export_1 = require("./commands/export");
38
- const profile_pins_1 = require("./core/profile-pins");
35
+ const agent_1 = require("./commands/agent");
36
+ const add_1 = require("./commands/add");
37
+ const sync_1 = require("./commands/sync");
38
+ const update_1 = require("./commands/update");
39
+ const agent_targets_1 = require("./core/agent-targets");
39
40
  const update_check_1 = require("./core/update-check");
40
41
  const paths_1 = require("./core/paths");
42
+ const cli_version_1 = require("./core/cli-version");
41
43
  const program = new commander_1.Command();
42
44
  program.name('awm').description('Agentic Workflow Manager').version((0, cli_version_1.cliVersion)());
43
45
  program.hook('postAction', () => {
@@ -46,6 +48,17 @@ program.hook('postAction', () => {
46
48
  }
47
49
  catch { /* el aviso nunca rompe un comando */ }
48
50
  });
51
+ /** Shared resolve-or-exit for the 3 index.ts call sites that need `--agent` targeting
52
+ * outside an already-testable `run*Core` function (which returns `{ code: 1, ... }`
53
+ * instead — see `core/agent-targets.ts`'s `resolveAgentTargetsOrError`). */
54
+ function resolveTargetsOrExit(prefs, explicit) {
55
+ const resolved = (0, agent_targets_1.resolveAgentTargetsOrError)({ prefs, explicit });
56
+ if (!resolved.ok) {
57
+ console.error(picocolors_1.default.red(resolved.error));
58
+ process.exit(1);
59
+ }
60
+ return resolved.targets;
61
+ }
49
62
  function handleCancel(value) {
50
63
  if ((0, prompts_1.isCancel)(value)) {
51
64
  (0, prompts_1.outro)('Operation cancelled.');
@@ -72,7 +85,7 @@ function resolveSelectedArtifacts(selections) {
72
85
  program.command('add [name]')
73
86
  .description('Add a skill, workflow, or process interactively (or non-interactively with flags)')
74
87
  .option('-t, --type <type>', 'Artifact type: skill, workflow, or process')
75
- .option('-a, --agent <agent>', `Target agent: ${Object.keys(providers_1.PROVIDERS).join(', ')}`)
88
+ .option('-a, --agent <agent>', `Target agent: ${providers_1.AGENT_TARGETS.join(', ')}`)
76
89
  .option('-s, --scope <scope>', 'Scope: local or global')
77
90
  .option('-m, --method <method>', 'Install method: symlink or copy')
78
91
  .option('-y, --yes', 'Skip confirmation prompts')
@@ -91,65 +104,12 @@ program.command('add [name]')
91
104
  // 1b. If `name` matches a bundle, run the bundle-activation flow and exit.
92
105
  if (name) {
93
106
  const allBundles = (0, bundles_1.discoverAllBundles)();
94
- const matchedBundle = allBundles.find((b) => b.name === name);
95
- if (matchedBundle) {
96
- const prefs = (0, config_1.getPreferences)();
97
- let bundleAgents;
98
- if (options.agent) {
99
- const valid = Object.keys(providers_1.PROVIDERS);
100
- const parsed = options.agent.split(',').map((a) => a.trim());
101
- for (const a of parsed) {
102
- if (!valid.includes(a)) {
103
- console.error(picocolors_1.default.red(`Invalid agent "${a}". Use: ${valid.join(', ')}.`));
104
- process.exit(1);
105
- }
106
- }
107
- bundleAgents = parsed;
108
- }
109
- else {
110
- bundleAgents = [prefs.defaultAgent];
111
- }
112
- let scopeOverride;
113
- if (options.scope) {
114
- if (!['local', 'global'].includes(options.scope)) {
115
- console.error(picocolors_1.default.red(`Invalid scope "${options.scope}". Use: local or global.`));
116
- process.exit(1);
117
- }
118
- scopeOverride = options.scope;
119
- }
120
- const effective = scopeOverride ?? (0, bundles_1.defaultScopeForBundle)(matchedBundle.scope);
121
- const projectRoot = (0, profile_1.findProjectRoot)(process.cwd());
122
- if (effective === 'local' && !projectRoot) {
123
- console.error(picocolors_1.default.red('No project root found (need a .git/, package.json, or .awm/profile.json here). Run inside a project, or pass --global.'));
124
- process.exit(1);
125
- }
126
- const result = (0, bundle_install_1.addBundle)({
127
- bundleName: matchedBundle.name,
128
- bundles: allBundles,
129
- agents: bundleAgents,
130
- method: 'symlink',
131
- projectRoot: projectRoot ?? process.cwd(),
132
- scopeOverride,
133
- });
134
- if (result.skipped.length > 0) {
135
- for (const s of result.skipped)
136
- console.log(picocolors_1.default.yellow(` ⚠ Skipped: ${s}`));
137
- }
138
- if (result.installed.length === 0) {
139
- (0, prompts_1.outro)(picocolors_1.default.yellow(`Nothing installed for bundle "${matchedBundle.name}".`));
140
- return;
141
- }
142
- const lines = result.installed.map((n) => picocolors_1.default.green(n)).join('\n ');
143
- const recordNote = result.recordedExtension
144
- ? `\n\n${picocolors_1.default.dim('Recorded as a project extension in .awm/profile.json (commit it; symlinks are gitignored).')}`
145
- : '';
146
- (0, prompts_1.outro)(`✅ Installed bundle ${picocolors_1.default.cyan(matchedBundle.name)}:\n ${lines}${recordNote}`);
147
- return;
148
- }
149
- // name given but no matching bundle found — report clearly regardless of TTY
150
- console.error(picocolors_1.default.red(`Bundle "${name}" not found in registry.`));
151
- console.error(picocolors_1.default.dim('Run `awm list` to see available packages.'));
152
- process.exit(1);
107
+ const prefs = (0, config_1.getPreferences)();
108
+ const outcome = (0, add_1.runAddBundleCore)({ name, agent: options.agent, scope: options.scope }, prefs, allBundles);
109
+ if (outcome.code !== 0)
110
+ process.exit(outcome.code);
111
+ (0, prompts_1.outro)('Done.');
112
+ return;
153
113
  }
154
114
  // 2. Discover artifacts
155
115
  const skills = (0, discovery_1.discoverSkills)();
@@ -162,21 +122,7 @@ program.command('add [name]')
162
122
  const prefs = (0, config_1.getPreferences)();
163
123
  // --all: install everything from every package headlessly
164
124
  if (options.all) {
165
- let targetAgents;
166
- if (options.agent) {
167
- const validAgents = Object.keys(providers_1.PROVIDERS);
168
- const parsed = options.agent.split(',').map((a) => a.trim());
169
- for (const a of parsed) {
170
- if (!validAgents.includes(a)) {
171
- console.error(picocolors_1.default.red(`Invalid agent "${a}". Use: ${validAgents.join(', ')}.`));
172
- process.exit(1);
173
- }
174
- }
175
- targetAgents = parsed;
176
- }
177
- else {
178
- targetAgents = [prefs.defaultAgent];
179
- }
125
+ const targetAgents = resolveTargetsOrExit(prefs, options.agent);
180
126
  let scopeVal;
181
127
  if (options.scope) {
182
128
  if (!['local', 'global'].includes(options.scope)) {
@@ -199,8 +145,8 @@ program.command('add [name]')
199
145
  else {
200
146
  methodVal = scopeVal === 'local' ? 'copy' : 'symlink';
201
147
  }
202
- const includeWorkflows = targetAgents.some((a) => providers_1.PROVIDERS[a].workflow !== null);
203
- const includeAgents = targetAgents.some((a) => providers_1.PROVIDERS[a].agent !== null);
148
+ const includeWorkflows = targetAgents.some((a) => (0, providers_1.providerFor)(a).workflow !== null);
149
+ const includeAgents = targetAgents.some((a) => (0, providers_1.providerFor)(a).agent !== null);
204
150
  const allView = (0, registry_view_1.buildPackageView)(skills, includeWorkflows ? workflows : [], includeAgents ? agents : [], (0, bundles_1.discoverAllBundles)());
205
151
  const allDedup = new Map();
206
152
  for (const pkg of allView) {
@@ -217,6 +163,9 @@ program.command('add [name]')
217
163
  (0, prompts_1.outro)(picocolors_1.default.yellow('No artifacts available.'));
218
164
  return;
219
165
  }
166
+ const preflightOk = (0, provider_preflight_1.preflightLinkArtifactsForCli)(targetAgents.flatMap((agent) => artifactsToInstall.map((artifact) => ({ agent, artifact }))));
167
+ if (!preflightOk)
168
+ return;
220
169
  const installSpinner = (0, prompts_1.spinner)();
221
170
  installSpinner.start('Installing artifacts...');
222
171
  try {
@@ -224,17 +173,24 @@ program.command('add [name]')
224
173
  const skipped = [];
225
174
  for (const currentAgent of targetAgents) {
226
175
  for (const artifact of artifactsToInstall) {
227
- if (providers_1.PROVIDERS[currentAgent][artifact.type] === null) {
176
+ const config = (0, providers_1.assertLinkRenderer)(artifact.type, currentAgent);
177
+ if (config === null) {
228
178
  skipped.push(`${artifact.name} (${currentAgent})`);
229
179
  continue;
230
180
  }
231
- const targetDir = (0, providers_1.getTargetPath)(artifact.type, currentAgent, scopeVal);
181
+ const targetDir = config[scopeVal];
232
182
  const finalDest = path_1.default.join(targetDir, artifact.name);
233
183
  (0, executor_1.installArtifact)(artifact.sourcePath, finalDest, methodVal);
234
184
  installed.push(`${artifact.name} → ${currentAgent} (${scopeVal})`);
235
185
  }
236
186
  }
237
- (0, config_1.savePreferences)({ defaultAgent: targetAgents[0], defaultScope: scopeVal, installMethod: methodVal });
187
+ const updatedPrefs = targetAgents.reduce((current, agent) => (0, config_1.enableAgent)(current, agent), prefs);
188
+ (0, config_1.savePreferences)({
189
+ ...updatedPrefs,
190
+ defaultAgent: targetAgents[0],
191
+ defaultScope: scopeVal,
192
+ installMethod: methodVal,
193
+ });
238
194
  installSpinner.stop('Installation complete!');
239
195
  if (skipped.length > 0) {
240
196
  for (const sk of skipped) {
@@ -260,24 +216,16 @@ program.command('add [name]')
260
216
  // 3. Agent & Scope Prompts (Moved up)
261
217
  let targetAgents;
262
218
  if (options.agent) {
263
- const validAgents = Object.keys(providers_1.PROVIDERS);
264
- const parsed = options.agent.split(',').map(a => a.trim());
265
- for (const a of parsed) {
266
- if (!validAgents.includes(a)) {
267
- console.error(picocolors_1.default.red(`Invalid agent "${a}". Use: ${validAgents.join(', ')}.`));
268
- process.exit(1);
269
- }
270
- }
271
- targetAgents = parsed;
219
+ targetAgents = resolveTargetsOrExit(prefs, options.agent);
272
220
  }
273
221
  else {
274
222
  const agentChoice = await (0, prompts_1.multiselect)({
275
223
  message: 'Which agent(s) do you want to install to?',
276
- options: Object.entries(providers_1.PROVIDERS).map(([key, config]) => ({
224
+ options: providers_1.AGENT_TARGETS.map((key) => ({
277
225
  value: key,
278
- label: config.label
226
+ label: (0, providers_1.providerFor)(key).label
279
227
  })),
280
- initialValues: [prefs.defaultAgent],
228
+ initialValues: [...prefs.enabledAgents],
281
229
  required: true
282
230
  });
283
231
  handleCancel(agentChoice);
@@ -304,8 +252,8 @@ program.command('add [name]')
304
252
  scopeVal = scopeChoice;
305
253
  }
306
254
  // 4. Build the package view, filtered to artifact types the target agent(s) support
307
- const includeWorkflows = targetAgents.some(a => providers_1.PROVIDERS[a].workflow !== null);
308
- const includeAgents = targetAgents.some(a => providers_1.PROVIDERS[a].agent !== null);
255
+ const includeWorkflows = targetAgents.some(a => (0, providers_1.providerFor)(a).workflow !== null);
256
+ const includeAgents = targetAgents.some(a => (0, providers_1.providerFor)(a).agent !== null);
309
257
  const view = (0, registry_view_1.buildPackageView)(skills, includeWorkflows ? workflows : [], includeAgents ? agents : [], (0, bundles_1.discoverAllBundles)());
310
258
  if (view.length === 0) {
311
259
  (0, prompts_1.outro)(picocolors_1.default.yellow('No artifacts available for the selected agent(s).'));
@@ -349,6 +297,9 @@ program.command('add [name]')
349
297
  (0, prompts_1.outro)(picocolors_1.default.yellow('No artifacts selected.'));
350
298
  return;
351
299
  }
300
+ const preflightOk = (0, provider_preflight_1.preflightLinkArtifactsForCli)(targetAgents.flatMap((agent) => artifactsToInstall.map((artifact) => ({ agent, artifact }))));
301
+ if (!preflightOk)
302
+ return;
352
303
  // 6. Installation Method
353
304
  let methodVal;
354
305
  if (options.method) {
@@ -389,17 +340,24 @@ program.command('add [name]')
389
340
  for (const currentAgent of targetAgents) {
390
341
  for (const artifact of artifactsToInstall) {
391
342
  // Skip artifacts not supported by this agent
392
- if (providers_1.PROVIDERS[currentAgent][artifact.type] === null) {
343
+ const config = (0, providers_1.assertLinkRenderer)(artifact.type, currentAgent);
344
+ if (config === null) {
393
345
  skipped.push(`${artifact.name} (${currentAgent})`);
394
346
  continue;
395
347
  }
396
- const targetDir = (0, providers_1.getTargetPath)(artifact.type, currentAgent, scopeVal);
348
+ const targetDir = config[scopeVal];
397
349
  const finalDest = path_1.default.join(targetDir, artifact.name);
398
350
  (0, executor_1.installArtifact)(artifact.sourcePath, finalDest, methodVal);
399
351
  installed.push(`${artifact.name} → ${currentAgent} (${scopeVal})`);
400
352
  }
401
353
  }
402
- (0, config_1.savePreferences)({ defaultAgent: targetAgents[0], defaultScope: scopeVal, installMethod: methodVal });
354
+ const updatedPrefs = targetAgents.reduce((current, agent) => (0, config_1.enableAgent)(current, agent), prefs);
355
+ (0, config_1.savePreferences)({
356
+ ...updatedPrefs,
357
+ defaultAgent: targetAgents[0],
358
+ defaultScope: scopeVal,
359
+ installMethod: methodVal,
360
+ });
403
361
  installSpinner.stop('Installation complete!');
404
362
  if (skipped.length > 0) {
405
363
  for (const s of skipped) {
@@ -416,149 +374,25 @@ program.command('add [name]')
416
374
  }
417
375
  });
418
376
  program.command('update')
419
- .description('Sync all configured registries with their remotes')
420
- .action(async () => {
377
+ .description('Sync all configured registries, reconcile artifacts and re-sync hooks')
378
+ .option('-a, --agent <agent>', `Target agent(s), comma-separated: ${providers_1.AGENT_TARGETS.join(', ')} (defaults to every enabled agent)`)
379
+ .action(async (options) => {
421
380
  (0, prompts_1.intro)(picocolors_1.default.bgCyan(picocolors_1.default.black(' AWM - Update Registries ')));
422
- const s = (0, prompts_1.spinner)();
423
- s.start('Syncing registries...');
424
- const results = await (0, registries_1.syncRegistries)();
425
- s.stop('Registries synced.');
426
- if (results.length === 0) {
427
- console.log(picocolors_1.default.yellow(' No registries configured — run `awm init` (seeds baseline) or `awm registry add <remote>`.'));
428
- }
429
- for (const r of results) {
430
- if (r.action === 'error') {
431
- console.warn(picocolors_1.default.yellow(` ⚠ registry ${r.name}: ${r.error}`));
432
- }
433
- else {
434
- console.log(picocolors_1.default.green(` ✓ Registry ${r.name} ${r.action === 'pulled' ? 'updated' : 're-cloned'} @ ${r.version}`));
435
- }
436
- }
437
- for (const f of (0, registries_1.verifyMinCliVersions)()) {
438
- console.warn(picocolors_1.default.yellow(` ⚠ Registry ${f.name} requires CLI ≥ ${f.min} (you have ${(0, cli_version_1.cliVersion)()}) — run: npm i -g agentic-workflow-manager`));
439
- }
440
- try {
441
- const regen = (0, regenerate_1.regenerateGlobalContext)();
442
- const refreshed = regen.filter((r) => r.action === 'refreshed').map((r) => r.agent);
443
- if (refreshed.length > 0)
444
- console.log(picocolors_1.default.green(` ✓ Regenerated AWM context for: ${refreshed.join(', ')}`));
445
- }
446
- catch { /* no aborta */ }
447
- try {
448
- for (const { agent, result } of (0, skill_integrity_1.reconcileAllSkillLinks)((0, registries_1.contentRoots)())) {
449
- const touched = result.relinked.length + result.pruned.length;
450
- if (touched > 0)
451
- console.log(picocolors_1.default.green(` ✓ Reconciled ${agent} skill links: re-linked ${result.relinked.length}, pruned ${result.pruned.length}`));
452
- }
453
- }
454
- catch { /* no aborta */ }
455
- try {
456
- const hooksRoot = (0, registries_1.capabilityRoot)('hooks');
457
- if (hooksRoot) {
458
- for (const r of (0, resync_1.resyncInstalledHooks)(hooksRoot)) {
459
- if (r.action === 'resynced')
460
- console.log(picocolors_1.default.green(` ✓ Re-synced ${r.agent} hook scripts`));
461
- else if (r.action === 'registry-missing')
462
- console.warn(picocolors_1.default.yellow(` ⚠ ${r.agent} hook installed but registry hooks missing — run 'awm hooks install'`));
463
- }
464
- }
465
- }
466
- catch { /* no aborta */ }
467
- await (0, update_check_1.offerSelfUpdate)(); // capa 2 — Task 13
468
- (0, prompts_1.outro)('✅ Registries, skills and hooks updated.');
381
+ (0, paths_1.warnIfUnsupportedPlatform)((m) => console.warn(picocolors_1.default.yellow(`⚠ ${m}`)));
382
+ const result = await (0, update_1.runUpdateCore)(options);
383
+ (0, prompts_1.outro)(result.code === 0 ? '✅ Registries, skills and hooks updated.' : picocolors_1.default.red('Update failed — see errors above.'));
384
+ process.exitCode = result.code;
469
385
  });
470
386
  program.command('sync')
471
387
  .description('Rebuild local skill symlinks from .awm/profile.json (e.g. after cloning on a new machine)')
472
- .option('-a, --agent <agent>', `Target agent: ${Object.keys(providers_1.PROVIDERS).join(', ')}`)
388
+ .option('-a, --agent <agent>', `Target agent(s), comma-separated: ${providers_1.AGENT_TARGETS.join(', ')} (defaults to every enabled agent)`)
473
389
  .option('-m, --method <method>', 'Install method: symlink or copy', 'symlink')
474
390
  .action(async (options) => {
475
391
  (0, prompts_1.intro)(picocolors_1.default.bgCyan(picocolors_1.default.black(' AWM - Sync Project Profile ')));
476
392
  (0, paths_1.warnIfUnsupportedPlatform)((m) => console.warn(picocolors_1.default.yellow(`⚠ ${m}`)));
477
- const projectRoot = (0, profile_1.findProjectRoot)(process.cwd());
478
- if (!projectRoot) {
479
- console.error(picocolors_1.default.red('No project root found (need a .git/, package.json, or .awm/profile.json here).'));
480
- process.exit(1);
481
- }
482
- let profile;
483
- try {
484
- profile = (0, profile_1.readProfile)(projectRoot);
485
- }
486
- catch (e) {
487
- console.error(picocolors_1.default.red(e.message));
488
- process.exit(1);
489
- }
490
- const s = (0, prompts_1.spinner)();
491
- s.start('Syncing registries...');
492
- const syncResults = await (0, registries_1.syncRegistries)();
493
- s.stop('Registries synced.');
494
- for (const r of syncResults) {
495
- if (r.action === 'error')
496
- console.warn(picocolors_1.default.yellow(` ⚠ registry ${r.name}: ${r.error}`));
497
- }
498
- // Gate minCliVersion (WS-4): un registry puede exigir una versión mínima del CLI.
499
- // Corre ANTES del gate de pins — gates de contrato primero (CONSTITUTION).
500
- const cliFailures = (0, registries_1.verifyMinCliVersions)();
501
- if (cliFailures.length > 0) {
502
- for (const f of cliFailures) {
503
- console.error(picocolors_1.default.red(`Registry ${f.name} requires CLI ≥ ${f.min} (you have ${(0, cli_version_1.cliVersion)()}).`));
504
- console.error(picocolors_1.default.red(' Run: npm i -g agentic-workflow-manager'));
505
- }
506
- process.exit(1);
507
- }
508
- // Gate de versión (WS-3): el pin del profile es el lock del proyecto.
509
- // Runs before the empty-extensions early-exit so pinned projects always fail fast.
510
- const pins = profile.registries ?? {};
511
- if (Object.keys(pins).length > 0) {
512
- const failures = await (0, profile_pins_1.verifyProjectPins)(pins);
513
- if (failures.length > 0) {
514
- for (const f of failures) {
515
- if (f.reason === 'missing-registry') {
516
- const registriesConfig = (0, registries_1.readRegistriesConfig)();
517
- const isConfigured = registriesConfig.some((r) => r.name === f.name);
518
- if (isConfigured) {
519
- console.error(picocolors_1.default.red(`The registry "${f.name}" is configured but not yet synced on this machine. Run: awm update`));
520
- }
521
- else {
522
- console.error(picocolors_1.default.red(`The registry "${f.name}" is not configured on this machine. Run: awm registry add <remote>`));
523
- }
524
- }
525
- else {
526
- console.error(picocolors_1.default.red(`This machine has ${f.name} @ ${f.actual ? `v${f.actual}` : 'HEAD (no tag)'} but the project requires v${f.required}.`));
527
- console.error(picocolors_1.default.red(` Run: awm pin ${f.name} ${f.required} && awm update`));
528
- }
529
- }
530
- process.exit(1);
531
- }
532
- }
533
- if (profile.extensions.length === 0) {
534
- (0, prompts_1.outro)(picocolors_1.default.yellow('No extensions in .awm/profile.json — nothing to sync. Use `awm add <bundle>` first.'));
535
- return;
536
- }
537
- const prefs = (0, config_1.getPreferences)();
538
- let agents;
539
- if (options.agent) {
540
- const valid = Object.keys(providers_1.PROVIDERS);
541
- const parsed = options.agent.split(',').map((a) => a.trim());
542
- for (const a of parsed) {
543
- if (!valid.includes(a)) {
544
- console.error(picocolors_1.default.red(`Invalid agent "${a}". Use: ${valid.join(', ')}.`));
545
- process.exit(1);
546
- }
547
- }
548
- agents = parsed;
549
- }
550
- else {
551
- agents = [prefs.defaultAgent];
552
- }
553
- const method = options.method === 'copy' ? 'copy' : 'symlink';
554
- const result = (0, bundle_install_1.syncProfile)({ projectRoot, bundles: (0, bundles_1.discoverAllBundles)(), agents, method });
555
- if (result.skipped.length > 0) {
556
- for (const sk of result.skipped)
557
- console.log(picocolors_1.default.yellow(` ⚠ Skipped: ${sk}`));
558
- }
559
- const lines = result.installed.map((n) => picocolors_1.default.green(n)).join('\n ');
560
- const installedNote = lines ? `\n ${lines}` : picocolors_1.default.dim(' (all up to date)');
561
- (0, prompts_1.outro)(`✅ Synced extensions [${result.extensions.join(', ')}]:${installedNote}`);
393
+ const { code } = await (0, sync_1.runSyncCore)(options);
394
+ (0, prompts_1.outro)(code === 0 ? 'Done.' : picocolors_1.default.red('Sync failed — see errors above.'));
395
+ process.exitCode = code;
562
396
  });
563
397
  program.command('list [package]')
564
398
  .description('List available artifacts. With no argument shows a package summary; pass a package name or --all for detail.')
@@ -621,21 +455,29 @@ program.command('list [package]')
621
455
  });
622
456
  program.command('remove')
623
457
  .description('Remove an installed skill or workflow')
624
- .action(async () => {
458
+ .option('-a, --agent <agent>', `Target agent(s), comma-separated: ${providers_1.AGENT_TARGETS.join(', ')} (defaults to every enabled agent)`)
459
+ .action(async (options) => {
625
460
  (0, prompts_1.intro)(picocolors_1.default.bgCyan(picocolors_1.default.black(' AWM - Remove Artifact ')));
626
461
  const prefs = (0, config_1.getPreferences)();
627
- // Multi-agent selection (matching the add command flow)
628
- const agentChoice = await (0, prompts_1.multiselect)({
629
- message: 'From which agent(s)?',
630
- options: Object.entries(providers_1.PROVIDERS).map(([key, config]) => ({
631
- value: key,
632
- label: config.label
633
- })),
634
- initialValues: [prefs.defaultAgent],
635
- required: true
636
- });
637
- handleCancel(agentChoice);
638
- const targetAgents = agentChoice;
462
+ // Multi-agent selection (matching the add command flow). --agent skips
463
+ // the interactive prompt and resolves the same way as add/sync/update/doctor (R12/R13).
464
+ let targetAgents;
465
+ if (options.agent) {
466
+ targetAgents = resolveTargetsOrExit(prefs, options.agent);
467
+ }
468
+ else {
469
+ const agentChoice = await (0, prompts_1.multiselect)({
470
+ message: 'From which agent(s)?',
471
+ options: providers_1.AGENT_TARGETS.map((key) => ({
472
+ value: key,
473
+ label: (0, providers_1.providerFor)(key).label
474
+ })),
475
+ initialValues: [...prefs.enabledAgents],
476
+ required: true
477
+ });
478
+ handleCancel(agentChoice);
479
+ targetAgents = agentChoice;
480
+ }
639
481
  const scopeChoice = await (0, prompts_1.select)({
640
482
  message: 'Scope?',
641
483
  options: [
@@ -646,43 +488,7 @@ program.command('remove')
646
488
  });
647
489
  handleCancel(scopeChoice);
648
490
  const scopeVal = scopeChoice;
649
- // Scan installed artifacts across all selected agents, aggregating by name
650
- const artifactMap = new Map();
651
- const scanDir = (dir, type, agent) => {
652
- if (!fs_1.default.existsSync(dir))
653
- return;
654
- const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
655
- for (const entry of entries) {
656
- const existing = artifactMap.get(entry.name);
657
- if (existing) {
658
- existing.installedIn.push(agent);
659
- existing.fullPaths.push(path_1.default.join(dir, entry.name));
660
- }
661
- else {
662
- artifactMap.set(entry.name, {
663
- name: entry.name,
664
- type,
665
- installedIn: [agent],
666
- fullPaths: [path_1.default.join(dir, entry.name)]
667
- });
668
- }
669
- }
670
- };
671
- for (const targetAgent of targetAgents) {
672
- try {
673
- scanDir((0, providers_1.getTargetPath)('skill', targetAgent, scopeVal), 'skill', targetAgent);
674
- }
675
- catch { /* ok */ }
676
- try {
677
- scanDir((0, providers_1.getTargetPath)('workflow', targetAgent, scopeVal), 'workflow', targetAgent);
678
- }
679
- catch { /* ok */ }
680
- try {
681
- scanDir((0, providers_1.getTargetPath)('agent', targetAgent, scopeVal), 'agent', targetAgent);
682
- }
683
- catch { /* ok */ }
684
- }
685
- const installed = Array.from(artifactMap.values());
491
+ const installed = (0, provider_artifacts_1.scanLegacyArtifacts)(targetAgents, scopeVal);
686
492
  if (installed.length === 0) {
687
493
  (0, prompts_1.outro)(picocolors_1.default.yellow('No installed artifacts found for the selected agents/scope.'));
688
494
  process.exit(0);
@@ -799,8 +605,10 @@ miroCmd.command('sync <storyMapPath>')
799
605
  (0, sensors_1.registerSensorsCommand)(program);
800
606
  (0, ledger_1.registerLedgerCommand)(program);
801
607
  (0, doctor_1.registerDoctorCommand)(program);
608
+ (0, backup_1.registerBackupCommand)(program);
802
609
  (0, init_1.registerInitCommand)(program);
803
610
  (0, registry_1.registerRegistryCommand)(program);
804
611
  (0, pin_1.registerPinCommands)(program);
805
612
  (0, export_1.registerExportCommand)(program);
613
+ (0, agent_1.registerAgentCommand)(program);
806
614
  program.parse();