@aiwg/cli 2026.8.7 → 2026.8.10

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 (76) hide show
  1. package/README.md +23 -8
  2. package/THIRD_PARTY_NOTICES.md +35 -0
  3. package/agentic/code/providers/capability-matrix.yaml +3 -3
  4. package/bin/aiwg.mjs +125 -0
  5. package/dist/src/artifacts/backends/graphology-backend.js +4 -3
  6. package/dist/src/artifacts/backends/sqlite-backend.js +4 -5
  7. package/dist/src/artifacts/cli.js +2 -2
  8. package/dist/src/artifacts/corpus-tools/cli.js +27 -0
  9. package/dist/src/artifacts/corpus-tools/profile-embed.js +3 -2
  10. package/dist/src/artifacts/corpus-tools/retrieval-lab.js +356 -0
  11. package/dist/src/artifacts/discover-facets.js +2 -2
  12. package/dist/src/artifacts/embedding-index.js +9 -8
  13. package/dist/src/artifacts/graph-backend.js +2 -2
  14. package/dist/src/artifacts/move.js +40 -2
  15. package/dist/src/artifacts/query-engine.js +21 -7
  16. package/dist/src/artifacts/repair.js +47 -0
  17. package/dist/src/artifacts/types.js +3 -3
  18. package/dist/src/cli/command-log.js +2 -2
  19. package/dist/src/cli/handlers/artifacts.js +50 -1
  20. package/dist/src/cli/handlers/cost-report.js +71 -0
  21. package/dist/src/cli/handlers/evidence.js +78 -0
  22. package/dist/src/cli/handlers/help.js +9 -0
  23. package/dist/src/cli/handlers/index.js +8 -3
  24. package/dist/src/cli/handlers/local-executor.js +4 -3
  25. package/dist/src/cli/handlers/refresh.js +20 -8
  26. package/dist/src/cli/handlers/regenerate.js +22 -12
  27. package/dist/src/cli/handlers/serve.js +15 -36
  28. package/dist/src/cli/handlers/sessions.js +5 -4
  29. package/dist/src/cli/handlers/setup-manifest.js +8 -1
  30. package/dist/src/cli/handlers/subcommands.js +30 -0
  31. package/dist/src/cli/handlers/use.js +272 -89
  32. package/dist/src/cli/handlers/utilities.js +149 -0
  33. package/dist/src/cli/handlers/workspace.js +10 -0
  34. package/dist/src/cli/help-generator.js +2 -1
  35. package/dist/src/cli/regenerate-selector.js +94 -0
  36. package/dist/src/cli/router.js +4 -1
  37. package/dist/src/cli/services/deployment-verification.js +596 -0
  38. package/dist/src/cli/skill-usage.js +2 -2
  39. package/dist/src/cli/workflow-orchestrator.js +1 -1
  40. package/dist/src/cli/workspace-signals.js +2 -2
  41. package/dist/src/config/aiwg-config.js +54 -27
  42. package/dist/src/config/cli.js +3 -3
  43. package/dist/src/config/project-artifacts-health.js +2 -0
  44. package/dist/src/config/project-artifacts-health.mjs +123 -0
  45. package/dist/src/config/project-artifacts-runtime.mjs +16 -0
  46. package/dist/src/config/project-artifacts.js +2 -1
  47. package/dist/src/cost/fleet-report.js +329 -0
  48. package/dist/src/evidence/bundle.js +256 -0
  49. package/dist/src/extensions/commands/definitions.js +77 -25
  50. package/dist/src/extensions/deployment-registration.js +6 -4
  51. package/dist/src/extensions/project-local-doctor.js +10 -6
  52. package/dist/src/extensions/project-local-gitignore.js +8 -4
  53. package/dist/src/extensions/project-quickref.js +197 -10
  54. package/dist/src/features/catalog.js +26 -0
  55. package/dist/src/features/cli.js +1 -3
  56. package/dist/src/features/runtime.js +17 -1
  57. package/dist/src/issues/cli.js +91 -7
  58. package/dist/src/mcp/server.mjs +1 -1
  59. package/dist/src/ops/registry.js +2 -2
  60. package/dist/src/policy/authorization.js +2 -2
  61. package/dist/src/providers/capability-matrix.yaml +3 -3
  62. package/dist/src/providers/provider-definitions.js +7 -5
  63. package/dist/src/providers/provider-definitions.mjs +1 -1
  64. package/dist/src/serve/pty-bridge.js +2 -8
  65. package/dist/src/serve/screen-reader.js +3 -6
  66. package/dist/src/smiths/context-pipeline/aiwg-md.js +2 -2
  67. package/dist/src/smiths/context-pipeline/finalization.js +18 -5
  68. package/dist/src/smiths/context-pipeline/generator.js +2 -2
  69. package/dist/src/smiths/context-pipeline/workspace-context.js +16 -17
  70. package/package.json +2 -1
  71. package/tools/agents/deploy-agents.mjs +10 -11
  72. package/tools/agents/providers/base.mjs +47 -5
  73. package/tools/agents/providers/openclaw.mjs +5 -2
  74. package/tools/agents/providers/windsurf.mjs +13 -24
  75. package/tools/plugin/package-plugins.mjs +53 -0
  76. package/tools/skills/deploy-skills-codex.mjs +21 -5
@@ -232,6 +232,7 @@ function installerConsistencyChecks(manifest, manifestDir) {
232
232
  const recoveryIds = new Set((manifest.spec.recovery ?? []).map((recovery) => recovery.id));
233
233
  const osConfigIds = new Set((manifest.spec.os_config ?? []).map((entry) => entry.id));
234
234
  const installType = manifest.metadata.install_type ?? 'user';
235
+ const executionMode = manifest.metadata.execution_mode ?? 'deterministic';
235
236
  for (const [index, step] of manifest.spec.steps.entries()) {
236
237
  if (allStepIds.has(step.id)) {
237
238
  findings.push({ severity: 'error', path: `/spec/steps/${index}/id`, rule: 'uniqueStepId', message: `duplicate step id '${step.id}'` });
@@ -260,7 +261,7 @@ function installerConsistencyChecks(manifest, manifestDir) {
260
261
  if (!step.instruction) {
261
262
  findings.push({ severity: 'error', path: `${pointer}/instruction`, rule: 'agenticInstruction', message: 'agentic step requires instruction' });
262
263
  }
263
- else {
264
+ else if (executionMode !== 'provider-orchestrated') {
264
265
  findings.push({ severity: 'warning', path: pointer, rule: 'agenticStep', message: 'agentic steps are exception handling only and require manual intervention during setup-run' });
265
266
  }
266
267
  }
@@ -687,6 +688,12 @@ export function runSetupManifest(options) {
687
688
  return { exitCode: 1, message: 'setup-run: manifest validation failed before execution' };
688
689
  }
689
690
  const manifest = validation.manifest;
691
+ if (manifest.metadata.execution_mode === 'provider-orchestrated') {
692
+ return {
693
+ exitCode: 2,
694
+ message: 'setup-run: this manifest is provider-orchestrated; give its URL or contents to a supported AI provider instead of executing it as a deterministic CLI manifest',
695
+ };
696
+ }
690
697
  const target = detectPlatform(options);
691
698
  if (!manifest.spec.platforms.some((candidate) => platformMatches(target, candidate))) {
692
699
  return { exitCode: 1, message: `setup-run: platform ${target.os}${target.distro ? `/${target.distro}` : ''}/${target.arch}/${target.shell} is not declared in the manifest` };
@@ -47,6 +47,8 @@ export const quickrefHandler = {
47
47
  console.log(`${verb} ${result.skillName}`);
48
48
  console.log(` Source: ${result.sourcePath}`);
49
49
  console.log(` Output: ${result.outputPath}`);
50
+ for (const warning of result.warnings)
51
+ console.log(` Warning: ${warning}`);
50
52
  if (ctx.dryRun) {
51
53
  console.log('\n--- preview ---\n');
52
54
  console.log(result.content);
@@ -850,6 +852,26 @@ export const promoteHandler = {
850
852
  console.log(`✓ Promoted '${positional}' → ${result.plan?.destination}`);
851
853
  if (cleanup) {
852
854
  console.log(' Source removed from .aiwg/');
855
+ try {
856
+ const { deployProjectQuickref, generateProjectQuickref, hasProjectQuickref } = await import('../../extensions/project-quickref.js');
857
+ if (await hasProjectQuickref(projectDir)) {
858
+ if (config.providers.length > 0) {
859
+ for (const provider of config.providers)
860
+ await deployProjectQuickref(projectDir, provider);
861
+ console.log(` Managed project quickref refreshed for: ${config.providers.join(', ')}.`);
862
+ }
863
+ else {
864
+ await generateProjectQuickref(projectDir);
865
+ console.log(' Managed project quickref generated; no providers are configured for deployment.');
866
+ }
867
+ }
868
+ else {
869
+ console.log(' Managed project quickref is now empty; run `aiwg doctor --project-local` to inspect deployed stale copies.');
870
+ }
871
+ }
872
+ catch (error) {
873
+ console.log(` Managed project quickref refresh failed: ${error.message}`);
874
+ }
853
875
  }
854
876
  return { exitCode: 0 };
855
877
  }
@@ -956,6 +978,14 @@ export const newBundleHandler = {
956
978
  catch {
957
979
  // .gitignore management is best-effort; don't fail the scaffold
958
980
  }
981
+ try {
982
+ const { generateProjectQuickref } = await import('../../extensions/project-quickref.js');
983
+ await generateProjectQuickref(ctx.cwd);
984
+ console.log(' → Managed project quickref refreshed from discovered capabilities.');
985
+ }
986
+ catch (error) {
987
+ console.log(` → Managed project quickref refresh deferred: ${error.message}`);
988
+ }
959
989
  // #1235 / #1758 — auto-rebuild the project graph and refresh the
960
990
  // Fortemi Core static cache so the new bundle is immediately
961
991
  // discoverable via top-level `aiwg discover` / `aiwg show`.
@@ -36,16 +36,22 @@ import { installAiwgHooks } from '../../extensions/claude-hooks-installer.js';
36
36
  import { detectScope, mirrorToUserScope, rejectOpenClawProjectScope, USER_SCOPE_PATHS, } from '../scope-resolver.js';
37
37
  import { maybeWarnProjectIsolation } from '../project-isolation/index.js';
38
38
  import { formatWorkspaceSignalPlan, includedBundleIds, resolveWorkspaceSignalPlan, writeWorkspaceSignalPlan, } from '../workspace-signals.js';
39
- import { getProviderArtifactPathStrings, getProviderKernelSkillPath, normalizeProviderDefinitionId, } from '../../providers/provider-definitions.js';
39
+ import { getProviderArtifactPathStrings, getProviderDefinition, getProviderKernelSkillPath, normalizeProviderDefinitionId, } from '../../providers/provider-definitions.js';
40
40
  // Module-level guard so the iteration loops further down (which re-enter
41
41
  // execute() per framework/provider) don't re-emit the warning each pass.
42
42
  // Reset is not needed: a single CLI process is one user invocation.
43
43
  let projectIsolationChecked = false;
44
+ // Non-zero only while the outer `aiwg use --json` orchestration wrapper is
45
+ // collecting child-process output. The CLI is single-command-per-process;
46
+ // recursive provider expansion shares this guard intentionally.
47
+ let machineReadableUseDepth = 0;
44
48
  // Context-pipeline: emits WORKSPACE.md + AIWG.md + provider adapters last.
45
49
  // for non-Claude providers per ADR-1 (.aiwg/architecture/adr-agents-md-aggregation.md).
46
50
  // Distinct from agentsmith (which creates subagent personas).
47
51
  import { generate as generateContextFiles, discoverDeployedArtifacts, } from '../../smiths/context-pipeline/index.js';
48
52
  import { verifyModelWrapperDeployment } from '../../models/wrapper-deployment.js';
53
+ import { loadGraphIndexFile } from '../../artifacts/index-reader.js';
54
+ import { aggregateUseDeploymentResult, buildDryRunUseResult, renderUseDeploymentResult, verifyProviderDeployment, } from '../services/deployment-verification.js';
49
55
  /**
50
56
  * Valid framework identifiers
51
57
  */
@@ -517,11 +523,13 @@ async function runPreDeployCollisionCheck(opts) {
517
523
  sourceSkillsDir,
518
524
  });
519
525
  const report = formatCollisionReport(results, { verbose });
520
- if (report) {
526
+ if (report && machineReadableUseDepth === 0) {
521
527
  process.stderr.write(report + '\n');
522
528
  }
523
529
  if (hasBlockingCollisions(results) && !force) {
524
- process.stderr.write('\nDeployment blocked. Use --force to override.\n');
530
+ if (machineReadableUseDepth === 0) {
531
+ process.stderr.write('\nDeployment blocked. Use --force to override.\n');
532
+ }
525
533
  return false;
526
534
  }
527
535
  return true;
@@ -559,7 +567,7 @@ const NEXT_STEPS = {
559
567
  'warp/sdlc': agenticNextSteps('Open Warp: Start a Warp session in this project root.'),
560
568
  'copilot/sdlc': agenticNextSteps('Open VS Code: Open this workspace and use Copilot Chat.'),
561
569
  'codex/sdlc': agenticNextSteps('Open Codex: Restart Codex in this project root.'),
562
- 'windsurf/sdlc': agenticNextSteps('Open Windsurf: Open this project in Windsurf and ask Cascade for AIWG status.'),
570
+ 'windsurf/sdlc': agenticNextSteps('Open Devin Desktop: Open this project in Devin Desktop and ask Devin for AIWG status.'),
563
571
  'openclaw/sdlc': agenticNextSteps('Start OpenClaw: Open OpenClaw with this project workspace.'),
564
572
  'openclaw/marketing': agenticNextSteps('Start OpenClaw: Open OpenClaw with this project workspace.'),
565
573
  'openclaw/all': agenticNextSteps('Start OpenClaw: Open OpenClaw with this project workspace.'),
@@ -575,9 +583,6 @@ export function nextStepsFor(framework, provider = 'claude') {
575
583
  const steps = NEXT_STEPS[providerKey] ?? NEXT_STEPS[framework] ?? NEXT_STEPS.sdlc;
576
584
  return steps.map((step) => step.replace('{{aiwg-regenerate}}', regenerateInvocation));
577
585
  }
578
- function printNextSteps(framework, provider = 'claude') {
579
- ui.section('Next steps:', nextStepsFor(framework, provider));
580
- }
581
586
  /**
582
587
  * Per-provider session-reload requirement after `aiwg use`.
583
588
  *
@@ -613,8 +618,8 @@ const SESSION_RELOAD_NOTICE = {
613
618
  rationale: 'Warp aggregates context from WARP.md when a new tab spawns; existing tabs keep the prior version.',
614
619
  },
615
620
  windsurf: {
616
- action: 'Restart Windsurf or reload the workspace so the aggregated AGENTS.md is re-parsed.',
617
- rationale: 'Windsurf reads AGENTS.md once per workspace session.',
621
+ action: 'Restart Devin Desktop or reload the workspace so the aggregated AGENTS.md is re-parsed.',
622
+ rationale: 'Devin Desktop reads the Windsurf-compatible AGENTS.md once per workspace session.',
618
623
  },
619
624
  factory: {
620
625
  action: 'Restart your Factory droid runtime to pick up new entries in .factory/droids/.',
@@ -723,18 +728,6 @@ async function countDeployedArtifacts(target, paths, provider) {
723
728
  behaviors: await countDirs(paths.behaviors),
724
729
  };
725
730
  }
726
- async function countDiscoverableSkills(aiwgRoot) {
727
- try {
728
- const { loadGraphIndexFile } = await import('../../artifacts/index-reader.js');
729
- const index = loadGraphIndexFile(aiwgRoot, 'metadata.json', 'framework');
730
- if (!index?.entries)
731
- return null;
732
- return Object.values(index.entries).filter(entry => entry.type === 'skill').length;
733
- }
734
- catch {
735
- return null;
736
- }
737
- }
738
731
  /**
739
732
  * Detect forge targets from .git/config remote URLs.
740
733
  * Returns a list of forge types found: 'github' | 'gitea'
@@ -1079,18 +1072,17 @@ async function deployProjectLocalBundles(opts) {
1079
1072
  : discovery.bundles.filter(b => b.type !== 'provider');
1080
1073
  if (targetBundles.length === 0) {
1081
1074
  if (!onlyBundleId) {
1082
- const { loadProjectQuickref, deployProjectQuickref } = await import('../../extensions/project-quickref.js');
1083
- const quickref = await loadProjectQuickref(projectDir);
1084
- if (quickref.exists) {
1085
- try {
1075
+ const { hasProjectQuickref, deployProjectQuickref } = await import('../../extensions/project-quickref.js');
1076
+ try {
1077
+ if (await hasProjectQuickref(projectDir)) {
1086
1078
  await deployProjectQuickref(projectDir, provider, { dryRun });
1087
1079
  if (verbose || dryRun)
1088
1080
  ui.dim(` + project quickref -> ${provider}`);
1089
1081
  }
1090
- catch (error) {
1091
- ui.warn(`Project quickref deployment failed: ${error.message}`);
1092
- return { deployed: 0, failed: 1, bundles: [] };
1093
- }
1082
+ }
1083
+ catch (error) {
1084
+ ui.warn(`Project quickref deployment failed: ${error.message}`);
1085
+ return { deployed: 0, failed: 1, bundles: [] };
1094
1086
  }
1095
1087
  }
1096
1088
  return { deployed: 0, failed: 0, bundles: [] };
@@ -1107,7 +1099,7 @@ async function deployProjectLocalBundles(opts) {
1107
1099
  const upstream = await buildUpstreamRegistry({ frameworkRoot });
1108
1100
  const shadowResult = await resolveShadows(targetBundles, upstream);
1109
1101
  const report = formatShadowReport(shadowResult);
1110
- if (report) {
1102
+ if (report && machineReadableUseDepth === 0) {
1111
1103
  process.stderr.write(report + '\n');
1112
1104
  }
1113
1105
  // #1037/#1049 — Activity log per shadow resolution
@@ -1204,22 +1196,20 @@ async function deployProjectLocalBundles(opts) {
1204
1196
  }
1205
1197
  }
1206
1198
  }
1207
- // A committed `.aiwg/quickref.json` is the canonical orientation source.
1208
- // Refresh its provider kernel copy whenever project-local bundles deploy so
1209
- // `aiwg use <bundle>` keeps the always-visible surface in sync.
1210
- const { loadProjectQuickref, deployProjectQuickref } = await import('../../extensions/project-quickref.js');
1211
- const quickref = await loadProjectQuickref(projectDir);
1212
- if (quickref.exists) {
1213
- try {
1199
+ // Refresh the project kernel quickref from either legacy operator input or
1200
+ // managed project-local discovery whenever bundles deploy.
1201
+ const { hasProjectQuickref, deployProjectQuickref } = await import('../../extensions/project-quickref.js');
1202
+ try {
1203
+ if (await hasProjectQuickref(projectDir)) {
1214
1204
  const quickrefResult = await deployProjectQuickref(projectDir, provider, { dryRun });
1215
1205
  if (verbose || dryRun) {
1216
1206
  ui.dim(` + project quickref -> ${quickrefResult.provider}${quickrefResult.emulated ? ' (emulated)' : ''}`);
1217
1207
  }
1218
1208
  }
1219
- catch (error) {
1220
- failed++;
1221
- ui.warn(`Project quickref deployment failed: ${error.message}`);
1222
- }
1209
+ }
1210
+ catch (error) {
1211
+ failed++;
1212
+ ui.warn(`Project quickref deployment failed: ${error.message}`);
1223
1213
  }
1224
1214
  return { deployed, failed, bundles: targetBundles };
1225
1215
  }
@@ -1240,15 +1230,12 @@ function resolveBuiltInProviderForUse(provider) {
1240
1230
  }
1241
1231
  function unsupportedProviderMessage(provider) {
1242
1232
  const normalized = provider.trim().toLowerCase();
1243
- if (normalized === 'devin' || normalized === 'devin-cli') {
1233
+ if (normalized === 'devin-cli') {
1244
1234
  return [
1245
1235
  `Unsupported provider: ${provider}`,
1246
1236
  '',
1247
- 'Devin Desktop is supported through the Windsurf compatibility adapter:',
1248
- ' aiwg use sdlc --provider windsurf',
1249
- ' aiwg use sdlc --provider devin-desktop',
1250
- '',
1251
- 'Devin CLI has distinct rules/skills surfaces and is recorded as future-provider metadata; AIWG does not emit .devin/ provider output yet.',
1237
+ 'Devin CLI has distinct rules/skills surfaces and is not a deployable AIWG provider yet.',
1238
+ 'Use --provider devin for Devin Desktop deployments.',
1252
1239
  ].join('\n');
1253
1240
  }
1254
1241
  return null;
@@ -1673,6 +1660,46 @@ async function generateGlobalProjectContext(opts) {
1673
1660
  },
1674
1661
  });
1675
1662
  }
1663
+ async function ensurePostDeployPhases(opts) {
1664
+ const currentIndex = loadGraphIndexFile(opts.frameworkRoot, 'metadata.json', 'framework');
1665
+ const builtAt = currentIndex ? Date.parse(currentIndex.builtAt) : Number.NaN;
1666
+ const startedAt = Date.parse(opts.invocationStartedAt);
1667
+ if (!Number.isFinite(builtAt) || builtAt + 2_000 < startedAt) {
1668
+ try {
1669
+ const { buildIndex } = await import('../../artifacts/index-builder.js');
1670
+ await buildIndex(opts.frameworkRoot, { graph: 'framework', explicit: false });
1671
+ }
1672
+ catch {
1673
+ // The shared verifier reports the index failure with stable remediation.
1674
+ }
1675
+ }
1676
+ if (opts.args.includes('--no-context-files'))
1677
+ return;
1678
+ const paths = getProviderPaths(opts.provider);
1679
+ const sections = await discoverDeployedArtifacts(opts.projectPath, {
1680
+ agents: paths.agents,
1681
+ rules: paths.rules,
1682
+ skills: paths.skills,
1683
+ behaviors: paths.behaviors,
1684
+ });
1685
+ try {
1686
+ await generateContextFiles({
1687
+ provider: opts.provider,
1688
+ projectPath: opts.projectPath,
1689
+ sections,
1690
+ detectExistingFiles: true,
1691
+ force: opts.args.includes('--force-context-files'),
1692
+ skip: {
1693
+ workspaceMd: opts.args.includes('--no-workspace-md'),
1694
+ aiwgMd: opts.args.includes('--no-aiwg-md'),
1695
+ agentsMd: opts.args.includes('--no-agents-md'),
1696
+ },
1697
+ });
1698
+ }
1699
+ catch {
1700
+ // The shared verifier reports context or provider-wiring failures.
1701
+ }
1702
+ }
1676
1703
  async function deploySourceDirectory(opts) {
1677
1704
  const args = [
1678
1705
  '--source', opts.source,
@@ -1823,7 +1850,158 @@ export class UseHandler {
1823
1850
  description = 'Deploy AIWG framework to project or user scope';
1824
1851
  category = 'framework';
1825
1852
  aliases = [];
1853
+ orchestrationDepth = 0;
1826
1854
  async execute(ctx) {
1855
+ const requestedBundle = firstUsePositional(ctx.args)
1856
+ ?? (ctx.args[0] === '--profile' ? 'all' : undefined);
1857
+ const bypassOrchestration = this.orchestrationDepth > 0
1858
+ || !requestedBundle
1859
+ || requestedBundle === 'cockpit'
1860
+ || ctx.args.includes('--workspace-signals');
1861
+ if (bypassOrchestration)
1862
+ return this.executeCore(ctx);
1863
+ this.orchestrationDepth += 1;
1864
+ try {
1865
+ return await this.executeOrchestrated(ctx, requestedBundle);
1866
+ }
1867
+ finally {
1868
+ this.orchestrationDepth -= 1;
1869
+ }
1870
+ }
1871
+ async executeOrchestrated(ctx, requestedBundle) {
1872
+ const startedAt = new Date().toISOString();
1873
+ const json = ctx.args.includes('--json');
1874
+ const coreArgs = ctx.args.filter((arg) => arg !== '--json');
1875
+ const remainingArgs = removeFirstPositional(coreArgs);
1876
+ const projectDir = getProjectDir(ctx, remainingArgs);
1877
+ const frameworkRoot = ctx.frameworkRoot || await getFrameworkRoot();
1878
+ const config = await readAiwgConfig(projectDir);
1879
+ const requestedProviders = configuredGlobalProviders(remainingArgs, config);
1880
+ const providers = [];
1881
+ for (const requestedProvider of requestedProviders) {
1882
+ const local = await resolveProjectLocalProviderAdapter(projectDir, requestedProvider);
1883
+ const builtIn = local.requestedProvider
1884
+ ? local.provider
1885
+ : resolveBuiltInProviderForUse(local.provider).provider;
1886
+ if (!providers.includes(builtIn))
1887
+ providers.push(builtIn);
1888
+ }
1889
+ const dryRun = remainingArgs.includes('--dry-run');
1890
+ const requestedScope = remainingArgs.includes('--global')
1891
+ ? 'user'
1892
+ : detectScope(remainingArgs);
1893
+ const contextOptOut = [
1894
+ '--no-context-files',
1895
+ '--no-workspace-md',
1896
+ '--no-aiwg-md',
1897
+ '--no-agents-md',
1898
+ ].some((flag) => remainingArgs.includes(flag));
1899
+ const originalConsole = {
1900
+ log: console.log,
1901
+ info: console.info,
1902
+ warn: console.warn,
1903
+ error: console.error,
1904
+ };
1905
+ if (json) {
1906
+ machineReadableUseDepth += 1;
1907
+ console.log = () => { };
1908
+ console.info = () => { };
1909
+ console.warn = () => { };
1910
+ console.error = () => { };
1911
+ }
1912
+ let coreResult;
1913
+ try {
1914
+ coreResult = await this.executeCore({ ...ctx, args: coreArgs });
1915
+ }
1916
+ finally {
1917
+ if (json) {
1918
+ machineReadableUseDepth -= 1;
1919
+ console.log = originalConsole.log;
1920
+ console.info = originalConsole.info;
1921
+ console.warn = originalConsole.warn;
1922
+ console.error = originalConsole.error;
1923
+ }
1924
+ }
1925
+ let result;
1926
+ if (dryRun && coreResult.exitCode === 0) {
1927
+ result = buildDryRunUseResult({
1928
+ projectRoot: projectDir,
1929
+ frameworkRoot,
1930
+ providers,
1931
+ scope: requestedScope,
1932
+ requestedBundles: [requestedBundle],
1933
+ contextOptOut,
1934
+ });
1935
+ }
1936
+ else {
1937
+ if (coreResult.exitCode === 0 && !dryRun && !VALID_FRAMEWORKS.includes(requestedBundle)) {
1938
+ for (const provider of providers) {
1939
+ await ensurePostDeployPhases({
1940
+ frameworkRoot,
1941
+ projectPath: projectDir,
1942
+ provider,
1943
+ args: remainingArgs,
1944
+ invocationStartedAt: startedAt,
1945
+ });
1946
+ }
1947
+ }
1948
+ const providerResults = [];
1949
+ for (const provider of providers) {
1950
+ const effectiveScope = provider === 'openclaw' || provider === 'openhuman'
1951
+ ? 'user'
1952
+ : requestedScope;
1953
+ providerResults.push(await verifyProviderDeployment({
1954
+ projectRoot: projectDir,
1955
+ frameworkRoot,
1956
+ provider,
1957
+ scope: effectiveScope,
1958
+ requestedBundles: [requestedBundle],
1959
+ contextOptOut,
1960
+ invocationStartedAt: dryRun ? undefined : startedAt,
1961
+ deploymentExitCode: coreResult.exitCode,
1962
+ deploymentMessage: coreResult.message,
1963
+ }));
1964
+ }
1965
+ result = aggregateUseDeploymentResult({
1966
+ projectRoot: projectDir,
1967
+ frameworkRoot,
1968
+ scope: requestedScope,
1969
+ requestedBundles: [requestedBundle],
1970
+ providers: providerResults,
1971
+ });
1972
+ if (dryRun) {
1973
+ result.dryRun = true;
1974
+ result.exitClassification = 'failure';
1975
+ }
1976
+ }
1977
+ if (json) {
1978
+ return { exitCode: result.exitCode, message: JSON.stringify(result, null, 2), rawOutput: true };
1979
+ }
1980
+ const verbose = coreArgs.includes('--verbose') || coreArgs.includes('-v');
1981
+ const versionInfo = await getVersionInfo().catch(() => null);
1982
+ const widthFromEnvironment = Number(process.env.COLUMNS);
1983
+ const width = Number.isFinite(process.stdout.columns) && process.stdout.columns > 0
1984
+ ? process.stdout.columns
1985
+ : Number.isFinite(widthFromEnvironment) && widthFromEnvironment > 0
1986
+ ? widthFromEnvironment
1987
+ : 100;
1988
+ const canonicalProvider = result.providers[0]?.provider ?? 'claude';
1989
+ const rendered = renderUseDeploymentResult(result, {
1990
+ verbose,
1991
+ width,
1992
+ version: versionInfo
1993
+ ? { version: versionInfo.version, repository: versionInfo.repoUrl || 'aiwg.io' }
1994
+ : undefined,
1995
+ nextSteps: verbose && result.outcome !== 'failed' && VALID_FRAMEWORKS.includes(requestedBundle)
1996
+ ? nextStepsFor(requestedBundle, canonicalProvider)
1997
+ : undefined,
1998
+ });
1999
+ return {
2000
+ exitCode: result.exitCode,
2001
+ message: [coreResult.message, rendered].filter(Boolean).join('\n'),
2002
+ };
2003
+ }
2004
+ async executeCore(ctx) {
1827
2005
  const explicitTarget = firstUsePositional(ctx.args);
1828
2006
  if (ctx.args.includes('--workspace-signals')) {
1829
2007
  const signalArgs = ctx.args.filter((a) => a !== '--workspace-signals');
@@ -2065,7 +2243,7 @@ export class UseHandler {
2065
2243
  const verbose = remainingArgs.includes('--verbose') || remainingArgs.includes('-v');
2066
2244
  const force = remainingArgs.includes('--force');
2067
2245
  const copyAll = remainingArgs.includes('--copy-all') || remainingArgs.includes('--copy-standard-skills');
2068
- const quiet = !verbose && !dryRun;
2246
+ const quiet = machineReadableUseDepth > 0 || (!verbose && !dryRun);
2069
2247
  ui.blank();
2070
2248
  ui.header(` Workspace-aware deployment (${plan.profile})`);
2071
2249
  ui.dim(` Included frameworks: ${selectedFrameworks.join(', ') || '(none)'}`);
@@ -2138,7 +2316,7 @@ export class UseHandler {
2138
2316
  target,
2139
2317
  dryRun,
2140
2318
  verbose,
2141
- quiet: !verbose && !dryRun,
2319
+ quiet: machineReadableUseDepth > 0 || (!verbose && !dryRun),
2142
2320
  modelArgs: modelDeployArgs,
2143
2321
  });
2144
2322
  if (plResult.failed > 0) {
@@ -2329,13 +2507,19 @@ export class UseHandler {
2329
2507
  const dryRunAddon = remainingArgs.includes('--dry-run');
2330
2508
  const runner = createScriptRunner(frameworkRoot);
2331
2509
  const addonBaseArgs = ['--deploy-commands', '--deploy-skills', '--deploy-rules'];
2510
+ // An explicitly selected upstream addon must be self-contained in the
2511
+ // project. Unlike a full framework deploy, its standard skills cannot be
2512
+ // left index-only: the user asked to install this specific bundle and
2513
+ // its supporting scripts must travel with the skill directory.
2514
+ addonBaseArgs.push('--copy-all');
2332
2515
  addonBaseArgs.push(...modelDeployArgs);
2333
2516
  if (provider)
2334
2517
  addonBaseArgs.push('--provider', provider);
2335
2518
  if (target)
2336
2519
  addonBaseArgs.push('--target', target);
2337
2520
  // Forward --copy-all (#1219) so addon-only deploys also honor it.
2338
- if (remainingArgs.includes('--copy-all') || remainingArgs.includes('--copy-standard-skills')) {
2521
+ if ((remainingArgs.includes('--copy-all') || remainingArgs.includes('--copy-standard-skills'))
2522
+ && !addonBaseArgs.includes('--copy-all')) {
2339
2523
  addonBaseArgs.push('--copy-all');
2340
2524
  }
2341
2525
  if (dryRunAddon)
@@ -2427,6 +2611,30 @@ export class UseHandler {
2427
2611
  message: `Failed to register CLI commands: ${error instanceof Error ? error.message : String(error)}`,
2428
2612
  };
2429
2613
  }
2614
+ // Persist the same lifecycle record frameworks and project-local bundles
2615
+ // receive so status, refresh, doctor, and remove can account for this
2616
+ // upstream addon and every provider artifact it actually deployed.
2617
+ if (!dryRunAddon && config) {
2618
+ try {
2619
+ const manifestPath = path.join(addonSource, 'manifest.json');
2620
+ const manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8'));
2621
+ // Provider rule aggregation may rename many source rules into one
2622
+ // managed index. Record this addon's contributed artifact counts,
2623
+ // matching framework registry semantics, rather than trying to
2624
+ // attribute shared aggregate filenames after deployment.
2625
+ const counts = await countBundleSourceArtifacts(addonSource);
2626
+ const updated = updateInstalled(config, framework, provider, counts, {
2627
+ version: manifest.version ?? (await getVersionInfo()).version,
2628
+ source: 'bundled',
2629
+ manifestHash: await hashManifest(manifestPath),
2630
+ });
2631
+ await writeAiwgConfig(projectDir, updated);
2632
+ config = updated;
2633
+ }
2634
+ catch (error) {
2635
+ ui.warn(`Addon registry update failed for '${framework}': ${error instanceof Error ? error.message : String(error)}`);
2636
+ }
2637
+ }
2430
2638
  // Profile picker for addons with memory topology and multiple templates
2431
2639
  try {
2432
2640
  const profileManifestPath = path.join(addonSource, 'manifest.json');
@@ -2545,8 +2753,9 @@ export class UseHandler {
2545
2753
  const filteredArgs = deployArgs.filter(a => a !== '--no-utils' && a !== '--no-project-local' && a !== '--ci-hooks-enabled' && a !== '--force' && a !== '--skip-conflicts' && a !== '--no-harness-agents');
2546
2754
  const deployFilteredArgs = removeFlagWithOptionalValue(filteredArgs, '--harness-agents');
2547
2755
  // Pass --quiet to suppress deploy-agents.mjs header/footer in default mode (#460)
2548
- // Dry-run must not capture output its purpose is to show what would happen
2549
- if (!verbose && !dryRun)
2756
+ // Human dry-run remains verbose; machine-readable dry-run captures the
2757
+ // preview so stdout stays a single JSON document.
2758
+ if (machineReadableUseDepth > 0 || (!verbose && !dryRun))
2550
2759
  deployFilteredArgs.push('--quiet');
2551
2760
  // Extract provider and target from remainingArgs to pass to addon deployments
2552
2761
  // Config-first resolution (#621): explicit --provider overrides config, config overrides default 'claude'
@@ -2586,6 +2795,9 @@ export class UseHandler {
2586
2795
  if (unsupportedMessage) {
2587
2796
  return { exitCode: 1, message: unsupportedMessage };
2588
2797
  }
2798
+ if (requestedProvider.trim().toLowerCase() === 'windsurf') {
2799
+ ui.warn("Provider id 'windsurf' is deprecated; use '--provider devin' for Devin Desktop. Existing .windsurf/ output paths remain supported.");
2800
+ }
2589
2801
  const provider = builtInProviderResolution.provider;
2590
2802
  const providerDeployArgs = builtInProviderResolution.requestedProvider
2591
2803
  ? withProviderOverride(deployFilteredArgs, provider)
@@ -2648,14 +2860,15 @@ export class UseHandler {
2648
2860
  }
2649
2861
  }
2650
2862
  // Deploy main framework
2651
- const quiet = !verbose && !dryRun;
2863
+ const quiet = machineReadableUseDepth > 0 || (!verbose && !dryRun);
2652
2864
  const captureOpts = quiet ? { capture: true } : {};
2653
2865
  if (quiet) {
2654
2866
  const installLabel = framework === 'all'
2655
2867
  ? 'Installing complete AIWG surface'
2656
2868
  : `Installing ${framework} framework`;
2869
+ const providerLabel = getProviderDefinition(provider)?.displayName ?? provider;
2657
2870
  ui.blank();
2658
- console.log(` ${ui.brandMark()} ${ui.bold(installLabel)} ${ui.dimText(`for ${provider === 'claude' ? 'Claude Code' : provider}`)}`);
2871
+ console.log(` ${ui.brandMark()} ${ui.bold(installLabel)} ${ui.dimText(`for ${providerLabel}`)}`);
2659
2872
  ui.blank();
2660
2873
  }
2661
2874
  const runner = createScriptRunner(ctx.frameworkRoot);
@@ -2889,6 +3102,7 @@ export class UseHandler {
2889
3102
  behaviorsPath: paths.behaviors,
2890
3103
  provider,
2891
3104
  cwd: target,
3105
+ quiet: !verbose,
2892
3106
  });
2893
3107
  if (verbose)
2894
3108
  console.log('Extension registration complete');
@@ -2908,7 +3122,6 @@ export class UseHandler {
2908
3122
  // (e.g., test fixtures, deploy from npm install rather than the
2909
3123
  // source repo). buildIndex() calls `process.exit(1)` on missing
2910
3124
  // scan dirs which would short-circuit our catch.
2911
- let discoverableSkillCount = null;
2912
3125
  if (!dryRun) {
2913
3126
  // Build the framework graph against $AIWG_ROOT, not the project's
2914
3127
  // target dir (#1217). The framework source is user-global at
@@ -2940,9 +3153,8 @@ export class UseHandler {
2940
3153
  // regardless of build cwd.
2941
3154
  await buildIndex(aiwgRootForIndex, { graph: 'framework', explicit: false });
2942
3155
  console.log = origLog;
2943
- discoverableSkillCount = await countDiscoverableSkills(aiwgRootForIndex);
2944
3156
  const indexElapsedSec = ((Date.now() - indexStart) / 1000).toFixed(1);
2945
- ui.success(`Capability index ready (${indexElapsedSec}s) — agents can search the installed capability set.`);
3157
+ ui.success(`Capability index ready (${indexElapsedSec}s).`);
2946
3158
  }
2947
3159
  catch (error) {
2948
3160
  console.log = origLog;
@@ -2954,42 +3166,13 @@ export class UseHandler {
2954
3166
  console.log('Framework source not found; skipping capability index rebuild');
2955
3167
  }
2956
3168
  }
2957
- // Show completion summary and next steps (default mode only)
3169
+ // Collect deployment counts for registry persistence and the final
3170
+ // orchestrated report. Presentation happens once, after verification, so
3171
+ // users do not see a second competing summary.
2958
3172
  let counts = { agents: 0, commands: 0, skills: 0, rules: 0, behaviors: 0 };
2959
3173
  if (quiet) {
2960
- // Count deployed artifacts
2961
3174
  const paths = getProviderPaths(provider);
2962
3175
  counts = await countDeployedArtifacts(target, paths, provider);
2963
- if (counts.agents > 0)
2964
- ui.deployCount('Agents', counts.agents);
2965
- if (counts.commands > 0)
2966
- ui.deployCount('Commands', counts.commands);
2967
- if (counts.skills > 0)
2968
- ui.deployCount('Skills', counts.skills);
2969
- if (discoverableSkillCount !== null)
2970
- ui.deployCount('Discoverable skills', discoverableSkillCount);
2971
- if (counts.rules > 0)
2972
- ui.deployCount('Rules', counts.rules);
2973
- if (counts.behaviors > 0)
2974
- ui.deployCount('Behaviors', counts.behaviors);
2975
- ui.blank();
2976
- printNextSteps(framework, provider);
2977
- // #1240: warn the operator that the running session can't see the newly
2978
- // deployed agents until reloaded. Skipping this notice is what produced
2979
- // the "Agent type 'software-implementer' not found" symptom on a stale
2980
- // Claude Code session.
2981
- ui.blank();
2982
- printSessionReloadNotice(provider);
2983
- // Append version confirmation line (#719)
2984
- try {
2985
- const versionInfo = await getVersionInfo();
2986
- ui.blank();
2987
- const repoStamp = versionInfo.repoUrl || 'aiwg.io';
2988
- ui.dim(` AIWG v${versionInfo.version} — ${repoStamp}`);
2989
- }
2990
- catch {
2991
- // Graceful fallback: omit version line if versionInfo unavailable
2992
- }
2993
3176
  }
2994
3177
  // Deploy CI workflow files when --ci-hooks-enabled is set (#661)
2995
3178
  if (ciHooksEnabled) {