@aiwg/cli 2026.10.8 → 2026.10.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 (104) hide show
  1. package/agentic/code/providers/model-capabilities.v1.json +197 -3
  2. package/bin/aiwg.mjs +8 -0
  3. package/dist/src/artifacts/cli.js +9 -5
  4. package/dist/src/auth/config.js +3 -8
  5. package/dist/src/auth/resource-credentials.js +35 -2
  6. package/dist/src/cli/handlers/diagnose.js +13 -0
  7. package/dist/src/cli/handlers/mc-admission.js +39 -0
  8. package/dist/src/cli/handlers/mc.js +2 -31
  9. package/dist/src/cli/handlers/models-help.js +52 -0
  10. package/dist/src/cli/handlers/models.js +11 -81
  11. package/dist/src/cli/handlers/resource-versions.js +8 -5
  12. package/dist/src/cli/handlers/script-runner.js +13 -1
  13. package/dist/src/cli/handlers/use-addon-profile.js +72 -0
  14. package/dist/src/cli/handlers/use.js +209 -233
  15. package/dist/src/cli/ui.js +1 -0
  16. package/dist/src/config/aiwg-config.js +38 -184
  17. package/dist/src/config/cli-help.js +54 -0
  18. package/dist/src/config/cli.js +5 -69
  19. package/dist/src/config/config-normalization.js +10 -0
  20. package/dist/src/config/worker-activation-gate.js +26 -0
  21. package/dist/src/config/worker-adoption-cli.js +102 -0
  22. package/dist/src/config/worker-adoption.js +368 -0
  23. package/dist/src/config/worker-config-digest.js +14 -0
  24. package/dist/src/config/worker-group-templates.js +150 -0
  25. package/dist/src/config/worker-guidance-warning.js +28 -0
  26. package/dist/src/config/worker-owner-lock.js +152 -0
  27. package/dist/src/config/worker-parallelism.js +154 -0
  28. package/dist/src/config/worker-write.js +75 -0
  29. package/dist/src/decision/telemetry/exporter.js +2 -1
  30. package/dist/src/decision/telemetry/index.js +1 -0
  31. package/dist/src/decision/telemetry/mapping.js +43 -0
  32. package/dist/src/decision/telemetry/otlp-http.js +5 -2
  33. package/dist/src/decision/telemetry/redaction.js +6 -1
  34. package/dist/src/decision/telemetry/types.js +3 -0
  35. package/dist/src/decision/telemetry/worker.js +69 -0
  36. package/dist/src/lint/rule-activation.js +15 -7
  37. package/dist/src/lint/runner.js +2 -2
  38. package/dist/src/mcp/server.mjs +2 -11
  39. package/dist/src/mcp/stdio-lifecycle.mjs +27 -0
  40. package/dist/src/mcp/tools/worker-failure-diagnostics.mjs +9 -0
  41. package/dist/src/mcp/tools/worker-invoke.mjs +6 -6
  42. package/dist/src/mission-protocol/codecs.js +8 -38
  43. package/dist/src/mission-protocol/worker-contract-diagnostics.js +62 -0
  44. package/dist/src/mission-protocol/worker-invocation-types.js +2 -0
  45. package/dist/src/models/conductor-data-policy.d.mts +8 -0
  46. package/dist/src/models/conductor-data-policy.mjs +190 -0
  47. package/dist/src/models/config-loader.js +19 -3
  48. package/dist/src/models/config-writer.js +27 -0
  49. package/dist/src/models/model-capabilities.v1.json +197 -3
  50. package/dist/src/models/provider-policy.js +7 -155
  51. package/dist/src/models/resolver.js +2 -293
  52. package/dist/src/models/worker-artifact-binding.mjs +12 -0
  53. package/dist/src/models/worker-direct-readiness-policy.js +155 -0
  54. package/dist/src/models/worker-direct-readiness.js +9 -218
  55. package/dist/src/models/worker-health-readiness.js +105 -26
  56. package/dist/src/models/worker-installation.mjs +12 -14
  57. package/dist/src/models/worker-member-references.js +64 -0
  58. package/dist/src/models/worker-plan.js +317 -0
  59. package/dist/src/models/worker-selection-policy.js +217 -0
  60. package/dist/src/models/wrapper-deployment.js +1 -1
  61. package/dist/src/resources/web-release.js +76 -65
  62. package/dist/src/serve/shared-host-admission-checks.js +116 -0
  63. package/dist/src/serve/shared-host-scheduler.js +25 -107
  64. package/dist/src/serve/shared-host-starvation.js +73 -0
  65. package/dist/src/serve/worker-artifacts.d.mts +4 -0
  66. package/dist/src/serve/worker-artifacts.mjs +56 -0
  67. package/dist/src/serve/worker-command-request.js +68 -0
  68. package/dist/src/serve/worker-invocation-paths.js +144 -0
  69. package/dist/src/serve/worker-invocation-preparation.js +169 -0
  70. package/dist/src/serve/worker-invocation.js +24 -291
  71. package/dist/src/serve/worker-stdin-frame.d.mts +2 -2
  72. package/dist/src/serve/worker-stdin-frame.mjs +6 -6
  73. package/dist/src/smiths/context-pipeline/parallelism-section.js +19 -7
  74. package/dist/src/storage/backends/fortemi.js +33 -29
  75. package/dist/src/storage/backends/logseq.js +16 -3
  76. package/dist/src/storage/backends/postgres.js +74 -33
  77. package/dist/src/storage/backends/postgrest.js +37 -13
  78. package/dist/src/storage/config.js +6 -1
  79. package/dist/src/uhp/config.js +4 -0
  80. package/package.json +9 -1
  81. package/schemas/mission-v1.schema.json +20 -19
  82. package/schemas/models/model-config-compatibility.v1.schema.json +16 -0
  83. package/tools/agents/providers/claude-post-deploy.mjs +35 -0
  84. package/tools/agents/providers/claude.mjs +3 -30
  85. package/tools/agents/providers/codex-post-deploy.mjs +13 -0
  86. package/tools/agents/providers/codex.mjs +2 -8
  87. package/tools/agents/providers/conductor-data-guard.mjs +61 -0
  88. package/tools/agents/providers/rule-delivery.mjs +3 -16
  89. package/tools/agents/providers/rule-presentation.mjs +38 -0
  90. package/tools/plugin/package-plugins.mjs +1 -1
  91. package/tools/ralph-external/lib/claude-adapter.mjs +2 -2
  92. package/tools/ralph-external/lib/codex-adapter.mjs +3 -3
  93. package/tools/ralph-external/lib/managed-worker-attempt.mjs +86 -0
  94. package/tools/ralph-external/lib/provider-adapter.mjs +11 -260
  95. package/tools/ralph-external/lib/session-attempt.mjs +85 -0
  96. package/tools/ralph-external/lib/strict-worker-json.mjs +190 -0
  97. package/tools/ralph-external/lib/worker-artifacts.mjs +209 -0
  98. package/tools/ralph-external/lib/worker-final.mjs +143 -0
  99. package/tools/ralph-external/lib/worker-inputs.mjs +55 -0
  100. package/tools/ralph-external/lib/worker-isolation.mjs +2 -44
  101. package/tools/ralph-external/lib/worker-launch-cli.mjs +47 -0
  102. package/tools/ralph-external/lib/worker-native-sandbox.mjs +83 -0
  103. package/tools/ralph-external/session-launcher.mjs +73 -217
  104. package/vscode-extension/schemas/aiwg.config.v1.json +161 -2
@@ -12,6 +12,7 @@ import { trackTemporaryDirectory } from '../../utils/temporary-directory.js';
12
12
  * @issue #33
13
13
  */
14
14
  import fs from 'fs/promises';
15
+ import { existsSync, rmSync, rmdirSync } from 'fs';
15
16
  import path from 'path';
16
17
  import os from 'os';
17
18
  import { pathToFileURL } from 'node:url';
@@ -27,11 +28,12 @@ import { registerDeployedExtensions } from '../../extensions/deployment-registra
27
28
  import { loadCliCommandsContribution, registerCliCommands, registerHooks, } from '../cli-extension-loader.js';
28
29
  import { translateSkillsToCommands, providerNeedsCommands } from '../../plugin/skill-command-translator.js';
29
30
  import * as ui from '../ui.js';
30
- import { readAiwgConfig, writeAiwgConfig, updateInstalled, hashManifest, emptyConfig, getProjectDir } from '../../config/aiwg-config.js';
31
+ import { readAiwgConfig, writeAiwgConfig, updateInstalled, hashManifest, emptyConfig, getProjectDir, getConfigPath } from '../../config/aiwg-config.js';
31
32
  import { appendGitignore } from '../../config/gitignore.js';
32
33
  import { getLogger } from '../log.js';
33
34
  import { installCockpit } from './cockpit.js';
34
35
  import { initHandler } from './init.js';
36
+ import { selectAddonProfile } from './use-addon-profile.js';
35
37
  import { checkCollisions, formatCollisionReport, hasBlockingCollisions, } from '../../smiths/skillsmith/collision-detector.js';
36
38
  import { discoverProjectLocalBundles, } from '../../extensions/project-local-discovery.js';
37
39
  import { PROJECT_LOCAL_TYPE_TO_DIR } from '../../extensions/project-local-paths.js';
@@ -65,7 +67,7 @@ import { verifyModelWrapperDeployment } from '../../models/wrapper-deployment.js
65
67
  import { aggregateUseDeploymentResult, buildDryRunUseResult, renderUseDeploymentResult, verifyProviderDeployment, } from '../services/deployment-verification.js';
66
68
  import { finalizeProviderTransformationReceipt, providerReceiptHasLocalSources, sourceVerificationsFromSignedWebRelease, } from '../../providers/transformation-receipt-integration.js';
67
69
  import { loadResourceTrustRootFile, resolveWebRelease, } from '../../resources/web-release.js';
68
- import { createResourceCredentialProvider } from '../../auth/resource-credentials.js';
70
+ import { createResourceCredentialProvider, prepareResourceCredentials } from '../../auth/resource-credentials.js';
69
71
  /**
70
72
  * Valid framework identifiers
71
73
  */
@@ -102,7 +104,7 @@ export async function resolveProviderReceiptSource(options) {
102
104
  release = await resolveWebRelease({ ...releaseOptions, selector: versionInfo.version, offline: true });
103
105
  }
104
106
  catch {
105
- const credentialProvider = createResourceCredentialProvider(process.env);
107
+ const credentialProvider = options.credentialProvider ?? createResourceCredentialProvider(process.env);
106
108
  const token = await credentialProvider();
107
109
  // Protected production resources require the authenticated release
108
110
  // credential. A configured alternate endpoint may intentionally be public.
@@ -1697,6 +1699,7 @@ function createUseScriptRunner(frameworkRoot) {
1697
1699
  const runner = createScriptRunner(frameworkRoot);
1698
1700
  return {
1699
1701
  async run(script, args, options = {}) {
1702
+ options = { ...options, omitEnv: useDeployment?.resourceCredentials?.omitEnv ?? ['AIWG_RESOURCE_TOKEN'] };
1700
1703
  if (script === 'tools/agents/deploy-agents.mjs')
1701
1704
  args = useDeploymentArgs(args);
1702
1705
  if (!usePreviewActions)
@@ -2472,6 +2475,9 @@ Bundles:
2472
2475
  <addon> | <extension> Any installed addon or extension name
2473
2476
 
2474
2477
  Options:
2478
+ --resource-token-stdin | --resource-token-file <path> | --resource-token-store <name>
2479
+ --resource-token-env <NAME> | --resource-token-interactive
2480
+ Select the optional protected release bearer
2475
2481
  --provider <name> Target provider (default: .aiwg/aiwg.config providers)
2476
2482
  --target <dir> Deploy into <dir> instead of the current directory
2477
2483
  --scope project|user Deploy to the project (default) or the user scope
@@ -2543,11 +2549,22 @@ export class UseHandler {
2543
2549
  return { exitCode: 0, message: USE_HELP, rawOutput: true };
2544
2550
  }
2545
2551
  async execute(ctx) {
2552
+ let resourceCredentials = useDeployment?.resourceCredentials;
2553
+ try {
2554
+ if (!resourceCredentials) {
2555
+ resourceCredentials = await prepareResourceCredentials(ctx.args);
2556
+ ctx = { ...ctx, args: resourceCredentials.args };
2557
+ }
2558
+ }
2559
+ catch (error) {
2560
+ return { exitCode: 2, message: error instanceof Error ? error.message : String(error) };
2561
+ }
2546
2562
  const invalidOption = validateUseOptions(ctx.args, this.preparedProviderContinuations);
2547
2563
  if (invalidOption)
2548
2564
  return { exitCode: 2, message: invalidOption };
2549
2565
  const previousDeployment = useDeployment;
2550
2566
  useDeployment = {
2567
+ resourceCredentials,
2551
2568
  version: previousDeployment?.version ?? resolveDeployVersion(ctx.frameworkRoot || await getFrameworkRoot()),
2552
2569
  skipContextFiles: previousDeployment?.skipContextFiles || ctx.args.includes('--no-context-files'),
2553
2570
  contextFiles: previousDeployment?.contextFiles ?? [],
@@ -2672,7 +2689,7 @@ export class UseHandler {
2672
2689
  }
2673
2690
  let coreResult;
2674
2691
  try {
2675
- coreResult = await this.executeCore({ ...ctx, args: coreArgs });
2692
+ coreResult = await this.executeCore({ ...ctx, args: coreArgs }, providers);
2676
2693
  }
2677
2694
  catch (error) {
2678
2695
  coreResult = { exitCode: 1, message: error instanceof Error ? error.message : String(error) };
@@ -2730,7 +2747,7 @@ export class UseHandler {
2730
2747
  scope: effectiveScope,
2731
2748
  requestedBundles: [requestedBundle],
2732
2749
  };
2733
- const sourceResolution = await resolveProviderReceiptSource(receiptOptions);
2750
+ const sourceResolution = await resolveProviderReceiptSource({ ...receiptOptions, credentialProvider: useDeployment?.resourceCredentials?.credentialProvider });
2734
2751
  await finalizeProviderTransformationReceipt({ ...receiptOptions, ...sourceResolution });
2735
2752
  }
2736
2753
  catch (error) {
@@ -2807,7 +2824,7 @@ export class UseHandler {
2807
2824
  message: [coreResult.message, rendered, ...(previewActions ? [...previewActions].sort() : [])].filter(Boolean).join('\n'),
2808
2825
  };
2809
2826
  }
2810
- async executeCore(ctx) {
2827
+ async executeCore(ctx, resolvedProviders) {
2811
2828
  const explicitTarget = firstUsePositional(ctx.args);
2812
2829
  if (ctx.args.includes('--workspace-signals')) {
2813
2830
  const signalArgs = ctx.args.filter((a) => a !== '--workspace-signals');
@@ -2974,6 +2991,13 @@ export class UseHandler {
2974
2991
  || remainingArgs.includes('--yes')
2975
2992
  || remainingArgs.includes('-y')
2976
2993
  || remainingArgs.includes('--non-interactive');
2994
+ // An auto-created config is removed again if the target turns out to be unknown,
2995
+ // so a mistyped `aiwg use <name>` leaves the project untouched.
2996
+ let autoCreatedConfig = null;
2997
+ if (!config && !_isDryRun) {
2998
+ const configPath = getConfigPath(projectDir);
2999
+ autoCreatedConfig = { path: configPath, controlDirExisted: existsSync(path.dirname(configPath)) };
3000
+ }
2977
3001
  if (!config) {
2978
3002
  if (_providersValue) {
2979
3003
  // --providers shorthand: write config without wizard
@@ -3291,6 +3315,15 @@ export class UseHandler {
3291
3315
  message: totalFailed > 0 ? `${totalFailed} project-local deploy(s) failed` : '',
3292
3316
  };
3293
3317
  }
3318
+ if (autoCreatedConfig) {
3319
+ rmSync(autoCreatedConfig.path, { force: true });
3320
+ if (!autoCreatedConfig.controlDirExisted) {
3321
+ try {
3322
+ rmdirSync(path.dirname(autoCreatedConfig.path));
3323
+ }
3324
+ catch { /* not empty or already gone */ }
3325
+ }
3326
+ }
3294
3327
  return {
3295
3328
  exitCode: 1,
3296
3329
  message: `Error: Unknown target '${framework}'\nFrameworks: ${VALID_FRAMEWORKS.join(', ')}\n\n'all' deploys the kernel surface (kernel skills, kernel and core rules, behaviors).\nFor a single addon, run 'aiwg list' to see available addons.\nFor extensions (sys, net, it, sec, stream, dev), see $AIWG_ROOT/agentic/code/extensions/.\nFor project-local artifacts, run 'aiwg list --project-local'.\nRun 'aiwg help' for usage information.`,
@@ -3301,7 +3334,8 @@ export class UseHandler {
3301
3334
  if (isAddon || isExtension) {
3302
3335
  const providerIdx = remainingArgs.findIndex(a => a === '--provider' || a === '--platform');
3303
3336
  const explicitAddonProvider = providerIdx >= 0 && remainingArgs[providerIdx + 1] ? remainingArgs[providerIdx + 1] : null;
3304
- const provider = resolveBuiltInProviderForUse(explicitAddonProvider ?? (config?.providers?.[0] ?? 'claude')).provider;
3337
+ const providers = resolvedProviders ?? [...new Set(configuredGlobalProviders(remainingArgs, config)
3338
+ .map(provider => resolveBuiltInProviderForUse(provider).provider))];
3305
3339
  const targetIdx = remainingArgs.findIndex(a => a === '--target');
3306
3340
  const target = targetIdx >= 0 && remainingArgs[targetIdx + 1] ? remainingArgs[targetIdx + 1] : process.cwd();
3307
3341
  const dryRunAddon = remainingArgs.includes('--dry-run');
@@ -3314,264 +3348,206 @@ export class UseHandler {
3314
3348
  catch (error) {
3315
3349
  return { exitCode: 1, message: `Error: ${error instanceof Error ? error.message : String(error)}` };
3316
3350
  }
3317
- if (addonScope === 'user' && !USER_SCOPE_PATHS[provider]) {
3318
- return {
3319
- exitCode: 1,
3320
- message: `--scope user not supported for provider '${provider}' — see docs/customization/user-scope-deployment.md for the supported list`,
3321
- };
3322
- }
3323
- const runner = createUseScriptRunner(frameworkRoot);
3324
- const addonBaseArgs = ['--deploy-commands', '--deploy-skills', '--deploy-rules'];
3325
- if (remainingArgs.includes('--allow-over-budget'))
3326
- addonBaseArgs.push('--allow-over-budget');
3327
- if (addonScope === 'user')
3328
- addonBaseArgs.push('--scope', 'user');
3329
- // An explicitly selected upstream addon must be self-contained in the
3330
- // project. Unlike a full framework deploy, its standard skills cannot be
3331
- // left index-only: the user asked to install this specific bundle and
3332
- // its supporting scripts must travel with the skill directory.
3333
- addonBaseArgs.push('--copy-all');
3334
- addonBaseArgs.push(...modelDeployArgs);
3335
- if (provider)
3336
- addonBaseArgs.push('--provider', provider);
3337
- if (target)
3338
- addonBaseArgs.push('--target', target);
3339
- // Forward --copy-all (#1219) so addon-only deploys also honor it.
3340
- if ((remainingArgs.includes('--copy-all') || remainingArgs.includes('--copy-standard-skills'))
3341
- && !addonBaseArgs.includes('--copy-all')) {
3342
- addonBaseArgs.push('--copy-all');
3343
- }
3344
- if (dryRunAddon)
3345
- addonBaseArgs.push('--dry-run');
3346
- if (remainingArgs.includes('--force'))
3347
- addonBaseArgs.push('--force');
3348
3351
  const kind = isExtension ? 'extension' : 'addon';
3349
- const activationOrder = isAddon
3350
- ? await resolveRequiredAddonActivationOrder(frameworkRoot, framework)
3351
- : [framework];
3352
- const requiredAddons = activationOrder.slice(0, -1);
3353
- for (const dependency of requiredAddons) {
3352
+ const addonSource = isExtension
3353
+ ? extensionPath(frameworkRoot, framework)
3354
+ : addonPath(frameworkRoot, framework);
3355
+ // Deploy, reconcile, and record exactly the provider set verification checks.
3356
+ for (const provider of providers) {
3357
+ if (addonScope === 'user' && !USER_SCOPE_PATHS[provider]) {
3358
+ return {
3359
+ exitCode: 1,
3360
+ message: `--scope user not supported for provider '${provider}' — see docs/customization/user-scope-deployment.md for the supported list`,
3361
+ };
3362
+ }
3363
+ const runner = createUseScriptRunner(frameworkRoot);
3364
+ const addonBaseArgs = ['--deploy-commands', '--deploy-skills', '--deploy-rules'];
3365
+ if (remainingArgs.includes('--allow-over-budget'))
3366
+ addonBaseArgs.push('--allow-over-budget');
3367
+ if (addonScope === 'user')
3368
+ addonBaseArgs.push('--scope', 'user');
3369
+ // An explicitly selected upstream addon must be self-contained in the
3370
+ // project. Unlike a full framework deploy, its standard skills cannot be
3371
+ // left index-only: the user asked to install this specific bundle and
3372
+ // its supporting scripts must travel with the skill directory.
3373
+ addonBaseArgs.push('--copy-all');
3374
+ addonBaseArgs.push(...modelDeployArgs);
3375
+ if (provider)
3376
+ addonBaseArgs.push('--provider', provider);
3377
+ if (target)
3378
+ addonBaseArgs.push('--target', target);
3379
+ // Forward --copy-all (#1219) so addon-only deploys also honor it.
3380
+ if ((remainingArgs.includes('--copy-all') || remainingArgs.includes('--copy-standard-skills'))
3381
+ && !addonBaseArgs.includes('--copy-all')) {
3382
+ addonBaseArgs.push('--copy-all');
3383
+ }
3384
+ if (dryRunAddon)
3385
+ addonBaseArgs.push('--dry-run');
3386
+ if (remainingArgs.includes('--force'))
3387
+ addonBaseArgs.push('--force');
3388
+ const activationOrder = isAddon
3389
+ ? await resolveRequiredAddonActivationOrder(frameworkRoot, framework)
3390
+ : [framework];
3391
+ const requiredAddons = activationOrder.slice(0, -1);
3392
+ for (const dependency of requiredAddons) {
3393
+ ui.blank();
3394
+ ui.header(` Deploying required ${dependency} addon...`);
3395
+ const dependencySource = addonPath(frameworkRoot, dependency);
3396
+ const dependencyResult = await runner.run('tools/agents/deploy-agents.mjs', [
3397
+ '--quiet', '--source', dependencySource,
3398
+ ...addonBaseArgs,
3399
+ ], { capture: true });
3400
+ if (dependencyResult.exitCode !== 0) {
3401
+ return {
3402
+ ...dependencyResult,
3403
+ message: dependencyResult.message
3404
+ || `Failed to deploy required addon '${dependency}'`,
3405
+ };
3406
+ }
3407
+ if (!dryRunAddon) {
3408
+ try {
3409
+ await reconcileDeployedSkillAssets(dependencySource, target, provider, { strictReferences: false, force: remainingArgs.includes('--force') });
3410
+ }
3411
+ catch (error) {
3412
+ return {
3413
+ exitCode: 1,
3414
+ message: `Required addon '${dependency}' skill asset deployment failed: ${error instanceof Error ? error.message : String(error)}`,
3415
+ };
3416
+ }
3417
+ }
3418
+ try {
3419
+ await registerSourceCliCommands({
3420
+ source: dependencySource,
3421
+ target,
3422
+ provider,
3423
+ dryRun: dryRunAddon,
3424
+ fallbackDescription: `${dependency} addon commands`,
3425
+ });
3426
+ }
3427
+ catch (error) {
3428
+ return {
3429
+ exitCode: 1,
3430
+ message: `Failed to register required addon '${dependency}' CLI commands: ${error instanceof Error ? error.message : String(error)}`,
3431
+ };
3432
+ }
3433
+ ui.success(dryRunAddon
3434
+ ? `Required ${dependency} addon activation previewed`
3435
+ : `Required ${dependency} addon deployed`);
3436
+ }
3354
3437
  ui.blank();
3355
- ui.header(` Deploying required ${dependency} addon...`);
3356
- const dependencySource = addonPath(frameworkRoot, dependency);
3357
- const dependencyResult = await runner.run('tools/agents/deploy-agents.mjs', [
3358
- '--quiet', '--source', dependencySource,
3438
+ ui.header(` Deploying ${framework} ${kind}...`);
3439
+ const addonResult = await runner.run('tools/agents/deploy-agents.mjs', [
3440
+ '--quiet', '--source', addonSource,
3359
3441
  ...addonBaseArgs,
3360
3442
  ], { capture: true });
3361
- if (dependencyResult.exitCode !== 0) {
3362
- return {
3363
- ...dependencyResult,
3364
- message: dependencyResult.message
3365
- || `Failed to deploy required addon '${dependency}'`,
3366
- };
3443
+ if (addonResult.exitCode !== 0) {
3444
+ return addonResult;
3367
3445
  }
3368
3446
  if (!dryRunAddon) {
3369
3447
  try {
3370
- await reconcileDeployedSkillAssets(dependencySource, target, provider, { strictReferences: false, force: remainingArgs.includes('--force') });
3448
+ await reconcileDeployedSkillAssets(addonSource, target, provider, { strictReferences: false, force: remainingArgs.includes('--force') });
3371
3449
  }
3372
3450
  catch (error) {
3373
3451
  return {
3374
3452
  exitCode: 1,
3375
- message: `Required addon '${dependency}' skill asset deployment failed: ${error instanceof Error ? error.message : String(error)}`,
3453
+ message: `${kind} '${framework}' skill asset deployment failed: ${error instanceof Error ? error.message : String(error)}`,
3376
3454
  };
3377
3455
  }
3378
3456
  }
3457
+ if (!dryRunAddon && provider === 'omp' && (detectScope(remainingArgs) === 'user' || remainingArgs.includes('--user'))) {
3458
+ try {
3459
+ await deployOmpUserSource({ frameworkRoot, source: addonSource, target, bundle: framework, copyAll: true });
3460
+ }
3461
+ catch (error) {
3462
+ return { exitCode: 1, message: `OMP user deployment failed: ${error instanceof Error ? error.message : String(error)}` };
3463
+ }
3464
+ }
3465
+ // Register only artifacts actually written by a confirmed deployment.
3466
+ if (!dryRunAddon) {
3467
+ try {
3468
+ const registry = getRegistry();
3469
+ const paths = getProviderPaths(provider);
3470
+ await registerDeployedExtensions(registry, {
3471
+ agentsPath: paths.agents,
3472
+ skillsPath: paths.skills,
3473
+ commandsPath: paths.commands,
3474
+ rulesPath: paths.rules,
3475
+ behaviorsPath: paths.behaviors,
3476
+ provider,
3477
+ cwd: target,
3478
+ });
3479
+ ui.success('Extension registration complete');
3480
+ }
3481
+ catch (error) {
3482
+ ui.warn(`Failed to register extensions: ${error instanceof Error ? error.message : String(error)}`);
3483
+ }
3484
+ }
3485
+ // Register CLI commands if addon declares them
3379
3486
  try {
3380
3487
  await registerSourceCliCommands({
3381
- source: dependencySource,
3488
+ source: addonSource,
3382
3489
  target,
3383
3490
  provider,
3384
3491
  dryRun: dryRunAddon,
3385
- fallbackDescription: `${dependency} addon commands`,
3492
+ fallbackDescription: `${framework} addon commands`,
3386
3493
  });
3387
3494
  }
3388
3495
  catch (error) {
3389
3496
  return {
3390
3497
  exitCode: 1,
3391
- message: `Failed to register required addon '${dependency}' CLI commands: ${error instanceof Error ? error.message : String(error)}`,
3392
- };
3393
- }
3394
- ui.success(dryRunAddon
3395
- ? `Required ${dependency} addon activation previewed`
3396
- : `Required ${dependency} addon deployed`);
3397
- }
3398
- ui.blank();
3399
- ui.header(` Deploying ${framework} ${kind}...`);
3400
- const addonSource = isExtension
3401
- ? extensionPath(frameworkRoot, framework)
3402
- : addonPath(frameworkRoot, framework);
3403
- const addonResult = await runner.run('tools/agents/deploy-agents.mjs', [
3404
- '--quiet', '--source', addonSource,
3405
- ...addonBaseArgs,
3406
- ], { capture: true });
3407
- if (addonResult.exitCode !== 0) {
3408
- return addonResult;
3409
- }
3410
- if (!dryRunAddon) {
3411
- try {
3412
- await reconcileDeployedSkillAssets(addonSource, target, provider, { strictReferences: false, force: remainingArgs.includes('--force') });
3413
- }
3414
- catch (error) {
3415
- return {
3416
- exitCode: 1,
3417
- message: `${kind} '${framework}' skill asset deployment failed: ${error instanceof Error ? error.message : String(error)}`,
3498
+ message: `Failed to register CLI commands: ${error instanceof Error ? error.message : String(error)}`,
3418
3499
  };
3419
3500
  }
3420
- }
3421
- if (!dryRunAddon && provider === 'omp' && (detectScope(remainingArgs) === 'user' || remainingArgs.includes('--user'))) {
3422
- try {
3423
- await deployOmpUserSource({ frameworkRoot, source: addonSource, target, bundle: framework, copyAll: true });
3424
- }
3425
- catch (error) {
3426
- return { exitCode: 1, message: `OMP user deployment failed: ${error instanceof Error ? error.message : String(error)}` };
3427
- }
3428
- }
3429
- // Register only artifacts actually written by a confirmed deployment.
3430
- if (!dryRunAddon) {
3431
- try {
3432
- const registry = getRegistry();
3433
- const paths = getProviderPaths(provider);
3434
- await registerDeployedExtensions(registry, {
3435
- agentsPath: paths.agents,
3436
- skillsPath: paths.skills,
3437
- commandsPath: paths.commands,
3438
- rulesPath: paths.rules,
3439
- behaviorsPath: paths.behaviors,
3440
- provider,
3441
- cwd: target,
3442
- });
3443
- ui.success('Extension registration complete');
3444
- }
3445
- catch (error) {
3446
- ui.warn(`Failed to register extensions: ${error instanceof Error ? error.message : String(error)}`);
3447
- }
3448
- }
3449
- // Register CLI commands if addon declares them
3450
- try {
3451
- await registerSourceCliCommands({
3452
- source: addonSource,
3453
- target,
3454
- provider,
3455
- dryRun: dryRunAddon,
3456
- fallbackDescription: `${framework} addon commands`,
3457
- });
3458
- }
3459
- catch (error) {
3460
- return {
3461
- exitCode: 1,
3462
- message: `Failed to register CLI commands: ${error instanceof Error ? error.message : String(error)}`,
3463
- };
3464
- }
3465
- if (!dryRunAddon && (provider === 'openclaw' || (provider === 'grokbot' && addonScope === 'user'))) {
3466
- try {
3467
- await recordNativeAddonDeploy(addonSource, target, provider, framework);
3468
- }
3469
- catch (error) {
3470
- return { exitCode: 1, message: `Addon user registry update failed: ${error instanceof Error ? error.message : String(error)}` };
3471
- }
3472
- }
3473
- // Persist the same lifecycle record frameworks and project-local bundles
3474
- // receive so status, refresh, doctor, and remove can account for this
3475
- // upstream addon and every provider artifact it actually deployed.
3476
- if (!dryRunAddon && config) {
3477
- try {
3478
- const manifestPath = path.join(addonSource, 'manifest.json');
3479
- const manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8'));
3480
- // Provider rule aggregation may rename many source rules into one
3481
- // managed index. Record this addon's contributed artifact counts,
3482
- // matching framework registry semantics, rather than trying to
3483
- // attribute shared aggregate filenames after deployment.
3484
- const counts = await countBundleSourceArtifacts(addonSource);
3485
- const updated = updateInstalled(config, framework, provider, counts, {
3486
- version: manifest.version ?? (await getVersionInfo()).version,
3487
- source: 'bundled',
3488
- manifestHash: await hashManifest(manifestPath),
3489
- asPrimary: Boolean(explicitAddonProvider),
3490
- });
3491
- await writeAiwgConfig(projectDir, updated);
3492
- config = updated;
3493
- }
3494
- catch (error) {
3495
- ui.warn(`Addon registry update failed for '${framework}': ${error instanceof Error ? error.message : String(error)}`);
3496
- }
3497
- }
3498
- // Profile picker for addons with memory topology and multiple templates
3499
- try {
3500
- const profileManifestPath = path.join(addonSource, 'manifest.json');
3501
- const profileManifestContent = await fs.readFile(profileManifestPath, 'utf-8');
3502
- const profileManifest = JSON.parse(profileManifestContent);
3503
- const topology = profileManifest.memory?.topology;
3504
- const templates = profileManifest.templates;
3505
- if (topology && templates && templates.length > 1) {
3506
- const profileIdx = remainingArgs.findIndex(a => a === '--profile');
3507
- let selectedProfile;
3508
- if (profileIdx >= 0 && remainingArgs[profileIdx + 1]) {
3509
- // Explicit --profile flag
3510
- selectedProfile = remainingArgs[profileIdx + 1];
3511
- const templateFile = templates.find((t) => t.replace('.md', '') === selectedProfile || t === selectedProfile);
3512
- if (!templateFile) {
3513
- ui.warn(`Unknown profile "${selectedProfile}". Available: ${templates.map((t) => t.replace('.md', '')).join(', ')}`);
3514
- selectedProfile = undefined;
3515
- }
3501
+ if (!dryRunAddon && (provider === 'openclaw' || (provider === 'grokbot' && addonScope === 'user'))) {
3502
+ try {
3503
+ await recordNativeAddonDeploy(addonSource, target, provider, framework);
3516
3504
  }
3517
- else if (_isBulkIntent || !process.stdin.isTTY) {
3518
- // Bulk/automation or non-TTY: silently pick 'generic' default.
3519
- // The profile picker is annoying during `aiwg use all`.
3520
- selectedProfile = 'generic';
3505
+ catch (error) {
3506
+ return { exitCode: 1, message: `Addon user registry update failed: ${error instanceof Error ? error.message : String(error)}` };
3521
3507
  }
3522
- else if (process.stdin.isTTY) {
3523
- // Interactive profile selection via the shared `listSelect` helper
3524
- // (POC for spike #926). One call renders the option list, handles
3525
- // number-or-name matching, threads `ctx.signal` for Ctrl-C
3526
- // cancellation, and resolves to the fallback on timeout or empty
3527
- // input. The hand-rolled parse-and-branch that used to live here
3528
- // is now a one-liner.
3529
- const { createPromptInterface, listSelect } = await import('../prompt-utils.js');
3530
- ui.blank();
3531
- ui.header(' Select a topology profile:');
3532
- const templateNames = templates.map((t) => t.replace('.md', ''));
3533
- const options = templateNames.map((name) => ({
3534
- label: name === 'generic' ? `${name} (default)` : name,
3535
- value: name,
3536
- }));
3537
- const rl = createPromptInterface();
3538
- try {
3539
- selectedProfile = await listSelect(rl, ' Enter number or name [generic]: ', options, 'generic', ctx.signal);
3540
- }
3541
- finally {
3542
- rl.close();
3543
- }
3508
+ }
3509
+ // Persist the same lifecycle record frameworks and project-local bundles
3510
+ // receive so status, refresh, doctor, and remove can account for this
3511
+ // upstream addon and every provider artifact it actually deployed.
3512
+ if (!dryRunAddon && config) {
3513
+ try {
3514
+ const manifestPath = path.join(addonSource, 'manifest.json');
3515
+ const manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8'));
3516
+ // Provider rule aggregation may rename many source rules into one
3517
+ // managed index. Record this addon's contributed artifact counts,
3518
+ // matching framework registry semantics, rather than trying to
3519
+ // attribute shared aggregate filenames after deployment.
3520
+ const counts = await countBundleSourceArtifacts(addonSource);
3521
+ const updated = updateInstalled(config, framework, provider, counts, {
3522
+ version: manifest.version ?? (await getVersionInfo()).version,
3523
+ source: 'bundled',
3524
+ manifestHash: await hashManifest(manifestPath),
3525
+ asPrimary: Boolean(explicitAddonProvider),
3526
+ });
3527
+ await writeAiwgConfig(projectDir, updated);
3528
+ config = updated;
3544
3529
  }
3545
- // Write profile config to project namespace
3546
- if (selectedProfile && !dryRunAddon) {
3547
- const namespace = topology.namespace || `.aiwg/${framework}`;
3548
- const configDir = path.join(target, namespace);
3549
- await fs.mkdir(configDir, { recursive: true });
3550
- const profileConfig = {
3551
- profile: selectedProfile,
3552
- pageTemplate: `templates/${selectedProfile}.md`,
3553
- selectedAt: new Date().toISOString(),
3554
- };
3555
- await fs.writeFile(path.join(configDir, 'config.json'), JSON.stringify(profileConfig, null, 2) + '\n');
3556
- ui.success(`Profile "${selectedProfile}" selected → ${namespace}/config.json`);
3530
+ catch (error) {
3531
+ ui.warn(`Addon registry update failed for '${framework}': ${error instanceof Error ? error.message : String(error)}`);
3557
3532
  }
3558
3533
  }
3534
+ if (framework === 'aiwg-utils' && !['pi', 'omp'].includes(provider) && !remainingArgs.includes('--dry-run')) {
3535
+ const wrapperValidation = await validateDeployedModelWrappers({
3536
+ provider: normalizeProviderDefinitionId(provider) ?? provider,
3537
+ target,
3538
+ frameworkRoot,
3539
+ modelDeployArgs,
3540
+ filtered: remainingArgs.includes('--filter') || remainingArgs.includes('--filter-role'),
3541
+ verbose: remainingArgs.includes('--verbose') || remainingArgs.includes('-v'),
3542
+ });
3543
+ if (wrapperValidation)
3544
+ return wrapperValidation;
3545
+ }
3559
3546
  }
3560
- catch {
3561
- // Profile selection is optional — don't fail deployment
3562
- }
3563
- if (framework === 'aiwg-utils' && !['pi', 'omp'].includes(provider) && !remainingArgs.includes('--dry-run')) {
3564
- const wrapperValidation = await validateDeployedModelWrappers({
3565
- provider: normalizeProviderDefinitionId(provider) ?? provider,
3566
- target,
3567
- frameworkRoot,
3568
- modelDeployArgs,
3569
- filtered: remainingArgs.includes('--filter') || remainingArgs.includes('--filter-role'),
3570
- verbose: remainingArgs.includes('--verbose') || remainingArgs.includes('-v'),
3571
- });
3572
- if (wrapperValidation)
3573
- return wrapperValidation;
3574
- }
3547
+ await selectAddonProfile({
3548
+ addonSource, framework, target, args: remainingArgs,
3549
+ dryRunAddon, bulkIntent: _isBulkIntent, signal: ctx.signal,
3550
+ });
3575
3551
  ui.blank();
3576
3552
  ui.success(dryRunAddon
3577
3553
  ? `${framework} addon activation preview complete`
@@ -44,6 +44,7 @@ export function success(msg) {
44
44
  console.log(isTTY ? chalk.hex(SUCCESS_HEX)(` ✓ ${msg}`) : ` OK ${msg}`);
45
45
  }
46
46
  export function error(msg) {
47
+ msg = msg.trimStart();
47
48
  console.error(isTTY ? chalk.red(` ✗ ${msg}`) : ` ERROR ${msg}`);
48
49
  }
49
50
  export function warn(msg) {