agents-united 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -368,7 +368,7 @@ agents doctor --host cline
368
368
  ---
369
369
  ## 🌐 One Library, Every Assistant
370
370
 
371
- The `.agents/` folder is the **main library** — the *one* folder you edit. **Antigravity reads it directly**; every other assistant (Claude Code, Cursor, Cline, OpenCode, Codex) can't read it natively, so Agents United writes **translated copies** it keeps in sync for you.
371
+ The `.agents/` folder is the **main library** — the *one* folder you edit. **Antigravity reads it directly** for interactive sessions (CLI TUI and desktop); see [ADR 0009](./docs/adr/0009-host-conformance-targets.md). Every other assistant (Claude Code, Cursor, Cline, OpenCode, Codex) can't read it natively, so Agents United writes **translated copies** it keeps in sync for you.
372
372
 
373
373
  - **Edit only `.agents/`.** That's your source of truth — the `agents-united.json` lockfile tracks it.
374
374
  - **Other assistants get their own translated copies**, in their own folders (`.claude/agents/`, `.cline/agents/`, …). Don't edit those — they're machine-managed and rewritten on every `agents update`.
package/dist/cli.js CHANGED
@@ -419,8 +419,10 @@ ${bodyStr}
419
419
  }
420
420
  /**
421
421
  * Render Team Manifest YAML matching ClineTeamManifest schema.
422
+ * @param excludeAddons optional addon names to omit from `recommendedAddons`
423
+ * (used to stop advertising bundles already present in the workspace).
422
424
  */
423
- static renderTeamManifest(bundle, scope) {
425
+ static renderTeamManifest(bundle, scope, excludeAddons = []) {
424
426
  const coordinatorFile = bundle.orchestrator || `${bundle.name}.md`;
425
427
  const coordinatorName = coordinatorFile.replace(/\.md$/, "");
426
428
  const coordinatorCanonical = `agents/${coordinatorFile}`;
@@ -430,7 +432,7 @@ ${bodyStr}
430
432
  }));
431
433
  const sortedSkills = [...bundle.skills || []].sort();
432
434
  const sortedWorkflows = [...bundle.workflows || []].sort();
433
- const recommendedAddons = bundle.recommendedAddons || [];
435
+ const recommendedAddons = (bundle.recommendedAddons || []).filter((a) => !excludeAddons.includes(a));
434
436
  const manifest = {
435
437
  schemaVersion: 1,
436
438
  bundle: bundle.name,
@@ -452,8 +454,10 @@ ${bodyStr}
452
454
  }
453
455
  /**
454
456
  * Render the coordinator rule file (.cline/rules/agents-united-<bundle>.md).
457
+ * @param excludeAddons optional addon names to omit from the "Recommended Addon
458
+ * Policy" list (used to stop advertising bundles already installed).
455
459
  */
456
- static renderCoordinatorRule(bundle, scope) {
460
+ static renderCoordinatorRule(bundle, scope, excludeAddons = []) {
457
461
  const marker = `<!-- managed-by: agents-united | profile: cline | bundle: ${bundle.name} | do not edit -->`;
458
462
  const manifestRelPath = scope === "global" ? `~/.cline/agents-united/teams/${bundle.name}.yaml` : `.cline/agents-united/teams/${bundle.name}.yaml`;
459
463
  const coordinatorFile = bundle.orchestrator || `${bundle.name}.md`;
@@ -463,7 +467,7 @@ ${bodyStr}
463
467
  const name = agentFile.replace(/\.md$/, "");
464
468
  return `- **${name}**: \`.agents/agents/${agentFile}\``;
465
469
  });
466
- const addons = bundle.recommendedAddons || [];
470
+ const addons = (bundle.recommendedAddons || []).filter((a) => !excludeAddons.includes(a));
467
471
  const addonSection = addons.length > 0 ? `
468
472
  ### Recommended Addon Policy
469
473
  When user tasks require capabilities from: ${addons.join(", ")}, explain the capability and request user confirmation to install via \`agents add <addon> -t cline -y\` before running the installation.` : "";
@@ -487,8 +491,10 @@ ${addonSection}
487
491
  }
488
492
  /**
489
493
  * Plan all compound artifacts for a bundle installation into Cline.
494
+ * @param excludeAddons optional addon names forwarded to the coordinator rule and
495
+ * team manifest renderers so already-installed bundles are not advertised.
490
496
  */
491
- static async planCompoundProjection(bundle, scope, resolved, registryDir) {
497
+ static async planCompoundProjection(bundle, scope, resolved, registryDir, excludeAddons = []) {
492
498
  const artifacts = [];
493
499
  const baseDir = scope === "global" ? ".cline" : ".cline";
494
500
  for (const agentFile of resolved.agents) {
@@ -539,14 +545,14 @@ ${addonSection}
539
545
  }
540
546
  }
541
547
  }
542
- const ruleContent = this.renderCoordinatorRule(bundle, scope);
548
+ const ruleContent = this.renderCoordinatorRule(bundle, scope, excludeAddons);
543
549
  artifacts.push({
544
550
  kind: "rule",
545
551
  relPath: `${baseDir}/rules/agents-united-${bundle.name}.md`.replace(/\\/g, "/"),
546
552
  content: ruleContent,
547
553
  managedMarker: true
548
554
  });
549
- const manifestContent = this.renderTeamManifest(bundle, scope);
555
+ const manifestContent = this.renderTeamManifest(bundle, scope, excludeAddons);
550
556
  artifacts.push({
551
557
  kind: "team-manifest",
552
558
  relPath: `${baseDir}/agents-united/teams/${bundle.name}.yaml`.replace(/\\/g, "/"),
@@ -948,6 +954,11 @@ var InstallEngine = class _InstallEngine {
948
954
  if (host === "cline") {
949
955
  const bundleDef = resolved.targetBundle ? await this.registry.getBundle(resolved.targetBundle) : void 0;
950
956
  if (bundleDef) {
957
+ const declared = /* @__PURE__ */ new Set();
958
+ if (bundleDef.orchestrator) declared.add(`agents/${bundleDef.orchestrator}`);
959
+ (bundleDef.agents || []).forEach((a) => declared.add(`agents/${a}`));
960
+ (bundleDef.skills || []).forEach((s) => declared.add(`skills/${s}/SKILL.md`));
961
+ (bundleDef.workflows || []).forEach((w) => declared.add(`workflows/${w}`));
951
962
  const artifacts = await ClineProjector.planCompoundProjection(bundleDef, scope, resolved, registryDir);
952
963
  for (const artifact of artifacts) {
953
964
  const dest = path5.join(root, artifact.relPath);
@@ -978,14 +989,16 @@ var InstallEngine = class _InstallEngine {
978
989
  lockfile.projections = lockfile.projections || {};
979
990
  const existingProj = lockfile.projections[artifact.relPath];
980
991
  const bundleName = bundleDef.name;
981
- const owners = existingProj?.owners ? Array.from(/* @__PURE__ */ new Set([...existingProj.owners, bundleName])) : [bundleName];
992
+ const declares = artifact.kind === "rule" || artifact.kind === "team-manifest" || (artifact.canonical ? declared.has(artifact.canonical) : false);
993
+ const priorOwners = existingProj?.owners ?? [];
994
+ const owners = declares ? Array.from(/* @__PURE__ */ new Set([...priorOwners, bundleName])) : priorOwners;
982
995
  lockfile.projections[artifact.relPath] = {
983
996
  host: "cline",
984
997
  kind: artifact.kind,
985
998
  canonical: artifact.canonical,
986
999
  owners,
987
1000
  hash: deployedHash,
988
- installedAt: now,
1001
+ installedAt: existingProj?.installedAt ?? now,
989
1002
  managedMarker: artifact.managedMarker
990
1003
  };
991
1004
  projections.push({ host, path: artifact.relPath, kind: artifact.kind, warnings: [] });
@@ -993,6 +1006,38 @@ var InstallEngine = class _InstallEngine {
993
1006
  this.recordProjectedTo(lockfile, artifact.canonical, artifact.relPath);
994
1007
  }
995
1008
  }
1009
+ if (bundleDef.parentBundle) {
1010
+ const parentDef = await this.registry.getBundle(bundleDef.parentBundle);
1011
+ if (parentDef) {
1012
+ const parentProjection = await ClineProjector.planCompoundProjection(
1013
+ parentDef,
1014
+ scope,
1015
+ resolved,
1016
+ registryDir,
1017
+ lockfile.installed.bundles
1018
+ );
1019
+ for (const artifact of parentProjection) {
1020
+ if (artifact.kind !== "rule" && artifact.kind !== "team-manifest") continue;
1021
+ if (artifact.content === void 0) continue;
1022
+ const dest = path5.join(root, artifact.relPath);
1023
+ if (!await fs3.pathExists(dest)) continue;
1024
+ await this.deployProjection(dest, artifact.content);
1025
+ const deployedHash = await this.calculateHash(dest);
1026
+ lockfile.projections = lockfile.projections || {};
1027
+ const existingProj = lockfile.projections[artifact.relPath];
1028
+ lockfile.projections[artifact.relPath] = {
1029
+ host: "cline",
1030
+ kind: artifact.kind,
1031
+ canonical: artifact.canonical,
1032
+ // Preserve existing ownership; the parent remains the sole owner.
1033
+ owners: existingProj?.owners ?? [],
1034
+ hash: deployedHash,
1035
+ installedAt: existingProj?.installedAt ?? now,
1036
+ managedMarker: artifact.managedMarker
1037
+ };
1038
+ }
1039
+ }
1040
+ }
996
1041
  continue;
997
1042
  }
998
1043
  }
@@ -1048,6 +1093,16 @@ var InstallEngine = class _InstallEngine {
1048
1093
  if (options.fanout !== void 0) {
1049
1094
  lockfile.fanout = effectiveFanout;
1050
1095
  }
1096
+ const declared = /* @__PURE__ */ new Set();
1097
+ if (resolved.targetBundle) {
1098
+ const bundleDef = await this.registry.getBundle(resolved.targetBundle);
1099
+ if (bundleDef) {
1100
+ if (bundleDef.orchestrator) declared.add(`agents/${bundleDef.orchestrator}`);
1101
+ (bundleDef.agents || []).forEach((a) => declared.add(`agents/${a}`));
1102
+ (bundleDef.skills || []).forEach((s) => declared.add(`skills/${s}/SKILL.md`));
1103
+ (bundleDef.workflows || []).forEach((w) => declared.add(`workflows/${w}`));
1104
+ }
1105
+ }
1051
1106
  for (const agentFile of resolved.agents) {
1052
1107
  const src = path5.join(registryDir, "agents", agentFile);
1053
1108
  const dest = path5.join(subPaths.agentsDir, agentFile);
@@ -1060,14 +1115,18 @@ var InstallEngine = class _InstallEngine {
1060
1115
  }
1061
1116
  const actualMethod = await this.deployFile(src, dest, method, options.force);
1062
1117
  const hash = await this.calculateHash(src);
1063
- const relPath = path5.relative(targetDir, dest);
1064
- const previousProjections = lockfile.files[relPath]?.projectedTo;
1118
+ const relPath = this.toPosix(path5.relative(targetDir, dest));
1119
+ const existing = lockfile.files[relPath];
1120
+ const existingOwners = existing?.owners ?? (existing?.bundle ? [existing.bundle] : []);
1121
+ const declaresAsset = declared.has(`agents/${agentFile}`);
1122
+ const owners = resolved.targetBundle && declaresAsset ? Array.from(/* @__PURE__ */ new Set([...existingOwners, resolved.targetBundle])) : existingOwners;
1065
1123
  lockfile.files[relPath] = {
1066
1124
  hash,
1067
- bundle: resolved.targetBundle,
1125
+ bundle: existing?.bundle ?? resolved.targetBundle,
1126
+ owners,
1068
1127
  method: actualMethod,
1069
- installedAt: now,
1070
- ...previousProjections ? { projectedTo: previousProjections } : {}
1128
+ installedAt: existing?.installedAt ?? now,
1129
+ ...existing?.projectedTo ? { projectedTo: existing.projectedTo } : {}
1071
1130
  };
1072
1131
  if (!lockfile.installed.agents.includes(agentFile)) {
1073
1132
  lockfile.installed.agents.push(agentFile);
@@ -1080,14 +1139,18 @@ var InstallEngine = class _InstallEngine {
1080
1139
  const skillFile = path5.join(src, "SKILL.md");
1081
1140
  if (await fs3.pathExists(skillFile)) {
1082
1141
  const hash = await this.calculateHash(skillFile);
1083
- const relPath = path5.relative(targetDir, path5.join(subPaths.skillsDir, skillName, "SKILL.md"));
1084
- const previousProjections = lockfile.files[relPath]?.projectedTo;
1142
+ const relPath = this.toPosix(path5.relative(targetDir, path5.join(subPaths.skillsDir, skillName, "SKILL.md")));
1143
+ const existing = lockfile.files[relPath];
1144
+ const existingOwners = existing?.owners ?? (existing?.bundle ? [existing.bundle] : []);
1145
+ const declaresAsset = declared.has(`skills/${skillName}/SKILL.md`);
1146
+ const owners = resolved.targetBundle && declaresAsset ? Array.from(/* @__PURE__ */ new Set([...existingOwners, resolved.targetBundle])) : existingOwners;
1085
1147
  lockfile.files[relPath] = {
1086
1148
  hash,
1087
- bundle: resolved.targetBundle,
1149
+ bundle: existing?.bundle ?? resolved.targetBundle,
1150
+ owners,
1088
1151
  method: actualMethod,
1089
- installedAt: now,
1090
- ...previousProjections ? { projectedTo: previousProjections } : {}
1152
+ installedAt: existing?.installedAt ?? now,
1153
+ ...existing?.projectedTo ? { projectedTo: existing.projectedTo } : {}
1091
1154
  };
1092
1155
  }
1093
1156
  if (!lockfile.installed.skills.includes(skillName)) {
@@ -1099,14 +1162,18 @@ var InstallEngine = class _InstallEngine {
1099
1162
  const dest = path5.join(subPaths.workflowsDir, workflowFile);
1100
1163
  const actualMethod = await this.deployFile(src, dest, method, options.force);
1101
1164
  const hash = await this.calculateHash(src);
1102
- const relPath = path5.relative(targetDir, dest);
1103
- const previousProjections = lockfile.files[relPath]?.projectedTo;
1165
+ const relPath = this.toPosix(path5.relative(targetDir, dest));
1166
+ const existing = lockfile.files[relPath];
1167
+ const existingOwners = existing?.owners ?? (existing?.bundle ? [existing.bundle] : []);
1168
+ const declaresAsset = declared.has(`workflows/${workflowFile}`);
1169
+ const owners = resolved.targetBundle && declaresAsset ? Array.from(/* @__PURE__ */ new Set([...existingOwners, resolved.targetBundle])) : existingOwners;
1104
1170
  lockfile.files[relPath] = {
1105
1171
  hash,
1106
- bundle: resolved.targetBundle,
1172
+ bundle: existing?.bundle ?? resolved.targetBundle,
1173
+ owners,
1107
1174
  method: actualMethod,
1108
- installedAt: now,
1109
- ...previousProjections ? { projectedTo: previousProjections } : {}
1175
+ installedAt: existing?.installedAt ?? now,
1176
+ ...existing?.projectedTo ? { projectedTo: existing.projectedTo } : {}
1110
1177
  };
1111
1178
  if (!lockfile.installed.workflows.includes(workflowFile)) {
1112
1179
  lockfile.installed.workflows.push(workflowFile);
@@ -1118,12 +1185,18 @@ var InstallEngine = class _InstallEngine {
1118
1185
  if (await fs3.pathExists(src)) {
1119
1186
  const actualMethod = await this.deployFile(src, dest, method, options.force);
1120
1187
  const hash = await this.calculateHash(src);
1121
- const relPath = path5.relative(targetDir, dest);
1188
+ const relPath = this.toPosix(path5.relative(targetDir, dest));
1189
+ const existing = lockfile.files[relPath];
1190
+ const existingOwners = existing?.owners ?? (existing?.bundle ? [existing.bundle] : []);
1191
+ const declaresAsset = true;
1192
+ const owners = resolved.targetBundle && declaresAsset ? Array.from(/* @__PURE__ */ new Set([...existingOwners, resolved.targetBundle])) : existingOwners;
1122
1193
  lockfile.files[relPath] = {
1123
1194
  hash,
1124
- bundle: resolved.targetBundle,
1195
+ bundle: existing?.bundle ?? resolved.targetBundle,
1196
+ owners,
1125
1197
  method: actualMethod,
1126
- installedAt: now
1198
+ installedAt: existing?.installedAt ?? now,
1199
+ ...existing?.projectedTo ? { projectedTo: existing.projectedTo } : {}
1127
1200
  };
1128
1201
  }
1129
1202
  }
@@ -1223,6 +1296,42 @@ var UninstallEngine = class {
1223
1296
  }
1224
1297
  await fs4.remove(absPath);
1225
1298
  }
1299
+ /**
1300
+ * Installed-addon freshness (plan 003): after a child bundle (one with a
1301
+ * `parentBundle`) is removed, re-render the parent's coordinator rule + team
1302
+ * manifest excluding every remaining installed bundle in the lockfile, so the
1303
+ * removed addon returns to the parent's recommendedAddons. Presentation-free:
1304
+ * the two projection files carry the managed marker trustees inside (rule) and
1305
+ * are overwritten directly via fs-extra. If a parent artifact does not exist in
1306
+ * the workspace (parent not installed as its own bundle), it is skipped.
1307
+ */
1308
+ async refreshParentCoordination(bundleName, workspaceRoot, lockfile, scope) {
1309
+ const bundleDef = await this.registry.getBundle(bundleName);
1310
+ if (!bundleDef?.parentBundle) return;
1311
+ const parentDef = await this.registry.getBundle(bundleDef.parentBundle);
1312
+ if (!parentDef) return;
1313
+ const parentResolved = await this.registry.resolve(parentDef.name).catch(() => null);
1314
+ if (!parentResolved) return;
1315
+ const registryDir = this.registry.getRegistryDir();
1316
+ const projection = await ClineProjector.planCompoundProjection(
1317
+ parentDef,
1318
+ scope,
1319
+ parentResolved,
1320
+ registryDir,
1321
+ lockfile.installed.bundles
1322
+ );
1323
+ for (const artifact of projection) {
1324
+ if (artifact.kind !== "rule" && artifact.kind !== "team-manifest") continue;
1325
+ if (artifact.content === void 0) continue;
1326
+ const dest = path6.join(workspaceRoot, artifact.relPath);
1327
+ if (!await fs4.pathExists(dest)) continue;
1328
+ await fs4.writeFile(dest, artifact.content, "utf8");
1329
+ const hash = await this.calculateHash(dest);
1330
+ if (lockfile.projections?.[artifact.relPath]) {
1331
+ lockfile.projections[artifact.relPath].hash = hash;
1332
+ }
1333
+ }
1334
+ }
1226
1335
  parseHosts(options) {
1227
1336
  if (options.hosts && options.hosts.length > 0) return options.hosts;
1228
1337
  if (options.target) {
@@ -1281,6 +1390,11 @@ var UninstallEngine = class {
1281
1390
  if (lockfile.installed.bundles.includes(bundleName)) {
1282
1391
  if (!options.dryRun) {
1283
1392
  const workspaceRoot = path6.resolve(path6.dirname(targetDir));
1393
+ const ownedFiles = Object.entries(lockfile.files).filter(([, m]) => (m.owners ?? (m.bundle ? [m.bundle] : [])).includes(bundleName));
1394
+ const ownedProjections = lockfile.projections ? Object.values(lockfile.projections).filter((p) => p.owners.includes(bundleName)).length : 0;
1395
+ if (ownedFiles.length === 0 && ownedProjections === 0) {
1396
+ throw new Error(`No installed assets found matching "${bundleName}".`);
1397
+ }
1284
1398
  if (lockfile.projections) {
1285
1399
  for (const [projRelPath, proj] of Object.entries(lockfile.projections)) {
1286
1400
  if (proj.owners.includes(bundleName)) {
@@ -1308,7 +1422,10 @@ var UninstallEngine = class {
1308
1422
  }
1309
1423
  }
1310
1424
  for (const [relPath, assetMeta] of Object.entries(lockfile.files)) {
1311
- if (assetMeta.bundle === bundleName) {
1425
+ const assetOwners = assetMeta.owners ?? (assetMeta.bundle ? [assetMeta.bundle] : []);
1426
+ if (!assetOwners.includes(bundleName)) continue;
1427
+ const newOwners = assetOwners.filter((o) => o !== bundleName);
1428
+ if (newOwners.length === 0) {
1312
1429
  if (assetMeta.projectedTo && assetMeta.projectedTo.length > 0) {
1313
1430
  await this.removeProjections(workspaceRoot, assetMeta.projectedTo, options.force);
1314
1431
  }
@@ -1324,21 +1441,28 @@ var UninstallEngine = class {
1324
1441
  removedFiles.push(relPath);
1325
1442
  }
1326
1443
  delete lockfile.files[relPath];
1444
+ } else {
1445
+ lockfile.files[relPath] = { ...assetMeta, owners: newOwners };
1327
1446
  }
1328
1447
  }
1329
1448
  lockfile.installed.bundles = lockfile.installed.bundles.filter((b) => b !== bundleName);
1330
1449
  if (lockfile.bundleVersions) {
1331
1450
  delete lockfile.bundleVersions[bundleName];
1332
1451
  }
1333
- if (resolved.agents) {
1334
- lockfile.installed.agents = lockfile.installed.agents.filter((a) => !resolved.agents.includes(a));
1335
- }
1336
- if (resolved.skills) {
1337
- lockfile.installed.skills = lockfile.installed.skills.filter((s) => !resolved.skills.includes(s));
1338
- }
1339
- if (resolved.workflows) {
1340
- lockfile.installed.workflows = lockfile.installed.workflows.filter((w) => !resolved.workflows.includes(w));
1452
+ const surviving = lockfile.installed.bundles.filter((b) => b !== bundleName);
1453
+ const survival = /* @__PURE__ */ new Set();
1454
+ for (const b of surviving) {
1455
+ const bdef = await this.registry.getBundle(b);
1456
+ if (!bdef) continue;
1457
+ if (bdef.orchestrator) survival.add(`agents/${bdef.orchestrator}`);
1458
+ (bdef.agents || []).forEach((a) => survival.add(`agents/${a}`));
1459
+ (bdef.skills || []).forEach((s) => survival.add(`skills/${s}/SKILL.md`));
1460
+ (bdef.workflows || []).forEach((w) => survival.add(`workflows/${w}`));
1341
1461
  }
1462
+ lockfile.installed.agents = lockfile.installed.agents.filter((a) => survival.has(`agents/${a}`));
1463
+ lockfile.installed.skills = lockfile.installed.skills.filter((s) => survival.has(`skills/${s}/SKILL.md`));
1464
+ lockfile.installed.workflows = lockfile.installed.workflows.filter((w) => survival.has(`workflows/${w}`));
1465
+ await this.refreshParentCoordination(bundleName, workspaceRoot, lockfile, scope);
1342
1466
  await fs4.writeJson(subPaths.lockfile, lockfile, { spaces: 2 });
1343
1467
  } else {
1344
1468
  removedFiles.push(...resolved.agents, ...resolved.skills, ...resolved.workflows);
@@ -2020,6 +2144,30 @@ var DoctorEngine = class {
2020
2144
  }
2021
2145
  }
2022
2146
  }
2147
+ const installedBundles = manifest.installed?.bundles ?? [];
2148
+ const fileOwnersOf = (rec) => rec.owners ?? (rec.bundle ? [rec.bundle] : []);
2149
+ if (manifest.projections) {
2150
+ for (const [projRelPath, proj] of Object.entries(manifest.projections)) {
2151
+ for (const owner of proj.owners) {
2152
+ if (!installedBundles.includes(owner)) {
2153
+ warnings.push(`Projection ${projRelPath} is owned by bundle "${owner}" which is not in installed.bundles.`);
2154
+ }
2155
+ }
2156
+ }
2157
+ }
2158
+ for (const [relPath, rec] of Object.entries(manifest.files)) {
2159
+ for (const owner of fileOwnersOf(rec)) {
2160
+ if (!installedBundles.includes(owner)) {
2161
+ warnings.push(`File record ${relPath} is owned by bundle "${owner}" which is not in installed.bundles.`);
2162
+ }
2163
+ }
2164
+ }
2165
+ for (const bundleName of installedBundles) {
2166
+ const fileCount = Object.values(manifest.files).filter((m) => fileOwnersOf(m).includes(bundleName)).length;
2167
+ if (fileCount === 0) {
2168
+ warnings.push(`Installed bundle "${bundleName}" owns zero file records.`);
2169
+ }
2170
+ }
2023
2171
  }
2024
2172
  if (host === "cline") {
2025
2173
  const probe = new ClineCapabilityProbe();