plugnz 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/plugnz.mjs +119 -28
  2. package/package.json +1 -1
package/dist/plugnz.mjs CHANGED
@@ -12622,12 +12622,12 @@ function assertOutcome(outcome, snapshots, dryRun, command) {
12622
12622
  if (outcome["action"] === "not-attempted" && (outcome["route"] !== "none" || outcome["changed"] !== false || outcome["result"] === "succeeded" || outcome["result"] === "pending")) {
12623
12623
  throw contradiction(`preflight refusal '${outcome["operationId"]}' must be a non-successful, non-mutating no-route action`);
12624
12624
  }
12625
- if (outcome["action"] === "not-attempted" && (outcome["resourceState"] !== "unknown" || outcome["activationState"] !== "unknown")) {
12625
+ if (!dryRun && outcome["action"] === "not-attempted" && (outcome["resourceState"] !== "unknown" || outcome["activationState"] !== "unknown")) {
12626
12626
  throw contradiction(`preflight refusal '${outcome["operationId"]}' cannot claim a terminal resource or activation state`);
12627
12627
  }
12628
12628
  const containment = outcome["action"] === "retain-prior" || outcome["action"] === "disable-nonconforming";
12629
12629
  const appliedDisablement = outcome["action"] === "disable-nonconforming" && outcome["changed"] === true;
12630
- if (containment) {
12630
+ if (containment && !dryRun) {
12631
12631
  const expectedResourceState = "retained";
12632
12632
  const expectedActivationState = outcome["action"] === "retain-prior" ? "retained-prior" : appliedDisablement ? "inactive" : "active-nonconforming";
12633
12633
  if (outcome["result"] !== "failed" || outcome["resourceState"] !== expectedResourceState || outcome["activationState"] !== expectedActivationState) {
@@ -12654,9 +12654,12 @@ function assertOutcome(outcome, snapshots, dryRun, command) {
12654
12654
  throw contradiction(`successful Desired outcome '${outcome["operationId"]}' needs an exact native identity`);
12655
12655
  }
12656
12656
  const terminalStateMatches = outcome["coverage"] === "retirement" ? outcome["resourceState"] === "absent" && outcome["activationState"] === "inactive" : outcome["resourceState"] === "present" && outcome["activationState"] === "active-conforming";
12657
- if (!terminalStateMatches)
12657
+ if (!dryRun && !terminalStateMatches)
12658
12658
  throw contradiction(`successful outcome '${outcome["operationId"]}' contradicts its terminal resource or activation state`);
12659
12659
  }
12660
+ if (dryRun && (outcome["resourceState"] === "potentially-changed" || outcome["resourceState"] === "absent" && outcome["activationState"] !== "inactive" || outcome["resourceState"] === "unknown" && outcome["activationState"] !== "unknown")) {
12661
+ throw contradiction(`dry-run outcome '${outcome["operationId"]}' contradicts its observed resource or activation state`);
12662
+ }
12660
12663
  if (outcome["result"] === "not-attempted" && outcome["resourceState"] === "potentially-changed") {
12661
12664
  throw contradiction(`not-attempted outcome '${outcome["operationId"]}' cannot be potentially changed`);
12662
12665
  }
@@ -25043,7 +25046,7 @@ async function planLifecycle(input) {
25043
25046
  scopes.push(planned.scope);
25044
25047
  operations.push(...planned.operations);
25045
25048
  }
25046
- return frozenPlan(input, command, attemptId, scopes, operations, prepared);
25049
+ return frozenPlan(input, command, attemptId, scopes, operations, prepared, loaded.state);
25047
25050
  }
25048
25051
  async function prepareEntry(entry, hosts, state, command, transportSource, transportCache) {
25049
25052
  switch (entry.operation) {
@@ -25202,7 +25205,7 @@ async function planSync(item, attemptId, validatedAt, state, command) {
25202
25205
  requiredCapabilities: [],
25203
25206
  adoptionRequested: selected.adoptionRequested
25204
25207
  });
25205
- classified.push(uninventoriedUpdate(item, selected.plugin.name, nativeId));
25208
+ classified.push(uninventoriedUpdate(item, selected.plugin.name, nativeId, attemptId));
25206
25209
  continue;
25207
25210
  }
25208
25211
  const nativeId = item.host === null ? selected.plugin.name : item.host.plannedNativeId(selected.plugin);
@@ -25213,7 +25216,7 @@ async function planSync(item, attemptId, validatedAt, state, command) {
25213
25216
  requiredCapabilities: [...requiredSemanticsForOperation(inventory, "install")].sort(),
25214
25217
  adoptionRequested: selected.adoptionRequested
25215
25218
  });
25216
- const row = await classifyDesired(item, selected, inventory, nativeId, attemptId, state, command === "add" || command === "update", command === "update");
25219
+ const row = await classifyDesired(item, selected, inventory, nativeId, attemptId, state, command === "update");
25217
25220
  if (row.kind === "defect")
25218
25221
  return row;
25219
25222
  classified.push(row);
@@ -25233,8 +25236,8 @@ async function planSync(item, attemptId, validatedAt, state, command) {
25233
25236
  operations: [...classified.map((row) => row.operation), ...retirements.operations]
25234
25237
  };
25235
25238
  }
25236
- async function classifyDesired(item, selected, inventory, nativeId, attemptId, state, applyUpdates, reapplyMatchingUpdate) {
25237
- const operationId = operationIdentity("desired-pair", item.scope.id, selected.plugin.name, nativeId, item.frozen.snapshot.fingerprint);
25239
+ async function classifyDesired(item, selected, inventory, nativeId, attemptId, state, reapplyMatchingUpdate) {
25240
+ const operationId = operationIdentity("desired-pair", item.scope.id, selected.plugin.name, nativeId, `${item.frozen.snapshot.fingerprint}\x00${attemptId}`);
25238
25241
  const snapshotId = sourceSnapshotId(item.frozen);
25239
25242
  const activation = findActivation(state, item.scope.id, selected.plugin.name, nativeId);
25240
25243
  if (item.host === null || item.observation === null) {
@@ -25274,13 +25277,12 @@ async function classifyDesired(item, selected, inventory, nativeId, attemptId, s
25274
25277
  return classified(operationId, "desired-pair", item.scope, snapshotId, selected.plugin.name, nativeId, "not-attempted", "none", decision.reason);
25275
25278
  }
25276
25279
  const recordedRoute = activation?.route.kind === "managed" || activation?.route.kind === "native" ? activation.route.kind : null;
25277
- const matchingBytes = sameBytes(activation, installation, selected.plugin);
25280
+ const matchingBytes = sameBytes(activation, installation, selected.plugin) && installation?.activation === "active" && installation.enablement === "enabled";
25278
25281
  const action = chosenAction(replacing, recordedRoute, decision.route, matchingBytes, reapplyMatchingUpdate);
25279
- const plannedOperationId = action === "update" && matchingBytes && reapplyMatchingUpdate ? operationIdentity("desired-pair", item.scope.id, selected.plugin.name, nativeId, `${item.frozen.snapshot.fingerprint}\x00${state.stateGeneration}`) : operationId;
25280
- if (action === "route-migrate" || action === "update" && !applyUpdates) {
25282
+ if (action === "route-migrate") {
25281
25283
  return classified(operationId, "desired-pair", item.scope, snapshotId, selected.plugin.name, nativeId, "retain-prior", recordedRoute ?? decision.route, createLifecycleReason("capability", "capability.unsupported", `${action} of '${selected.plugin.name}' is not applied by this execution slice`, action));
25282
25284
  }
25283
- return classified(plannedOperationId, "desired-pair", item.scope, snapshotId, selected.plugin.name, nativeId, action, decision.route, null);
25285
+ return classified(operationId, "desired-pair", item.scope, snapshotId, selected.plugin.name, nativeId, action, decision.route, null);
25284
25286
  }
25285
25287
  async function planOmissions(item, attemptId, state, desired) {
25286
25288
  const packageIds = new Set(desired.map((row) => row.packageId));
@@ -25417,7 +25419,7 @@ async function selectDisable(item, nativeId, operationId, attemptId, version, ac
25417
25419
  return decision;
25418
25420
  }
25419
25421
  async function selectRetirement(host, observation, scope, activation, attemptId, source) {
25420
- const operationId = operationIdentity("retirement", scope.id, activation.packageId, activation.nativeId, activation.sourceRevision ?? "unrecorded");
25422
+ const operationId = operationIdentity("retirement", scope.id, activation.packageId, activation.nativeId, `${activation.sourceRevision ?? "unrecorded"}\x00${attemptId}`);
25421
25423
  const installation = observation.installations.find((row) => row.nativeId === activation.nativeId);
25422
25424
  if (installation === undefined) {
25423
25425
  return { kind: "planned", operation: notAttemptedRetirement(operationId, scope, activation, "ownership was not revalidated") };
@@ -25500,8 +25502,9 @@ async function decideRecordedRoute(host, observation, recorded, operationId, att
25500
25502
  function recordedActivation(scope, source, activation, installation, target) {
25501
25503
  return ownedActivation(scope, source, activation, installation, target);
25502
25504
  }
25503
- function frozenPlan(input, command, attemptId, scopes, operations, prepared) {
25505
+ function frozenPlan(input, command, attemptId, scopes, operations, prepared, state) {
25504
25506
  const snapshots = [];
25507
+ const frozenSources = new Map;
25505
25508
  const seen = new Set;
25506
25509
  for (const item of prepared) {
25507
25510
  if (item.kind !== "sync")
@@ -25511,8 +25514,12 @@ function frozenPlan(input, command, attemptId, scopes, operations, prepared) {
25511
25514
  continue;
25512
25515
  seen.add(id);
25513
25516
  snapshots.push({ id, reference: item.frozen.snapshot });
25517
+ frozenSources.set(id, item.frozen);
25514
25518
  }
25515
- const outcomes = operations.map((row) => outcomeFor(row));
25519
+ const outcomes = operations.map((row) => {
25520
+ const outcome = outcomeFor(row);
25521
+ return input.dryRun ? { ...outcome, ...observedStates(row.operation, prepared, state) } : outcome;
25522
+ });
25516
25523
  const failed = outcomes.filter((outcome) => outcome.result !== "succeeded");
25517
25524
  const converged = failed.length === 0;
25518
25525
  const failureCategory = converged ? null : failed[0]?.reason?.category ?? "internal";
@@ -25536,12 +25543,31 @@ function frozenPlan(input, command, attemptId, scopes, operations, prepared) {
25536
25543
  kind: "frozen",
25537
25544
  requestedDryRun: input.dryRun,
25538
25545
  attemptId,
25546
+ frozenSources,
25539
25547
  report,
25540
25548
  scopes,
25541
25549
  operations,
25542
25550
  ...input.transportSource === undefined ? {} : { transportSource: input.transportSource }
25543
25551
  };
25544
25552
  }
25553
+ function observedStates(operation, prepared, state) {
25554
+ const item = prepared.find((candidate) => candidate.scope.id === operation.scope.id);
25555
+ if (item?.observation === null || item === undefined)
25556
+ return { resourceState: "unknown", activationState: "unknown" };
25557
+ const installation = item.observation.installations.find((row) => row.nativeId === operation.nativeId);
25558
+ if (installation === undefined || installation.presence === "absent")
25559
+ return { resourceState: "absent", activationState: "inactive" };
25560
+ if (installation.enablement === "disabled" || installation.activation === "inactive")
25561
+ return { resourceState: "present", activationState: "inactive" };
25562
+ if (installation.enablement === "unknown" || installation.activation === "unknown")
25563
+ return { resourceState: "present", activationState: "unknown" };
25564
+ if (installation.activation === "nonconforming")
25565
+ return { resourceState: "present", activationState: "active-nonconforming" };
25566
+ const activation = findActivation(state, operation.scope.id, operation.package, operation.nativeId ?? "");
25567
+ const plugin = item.kind === "sync" ? item.selected.find((selected) => selected.plugin.name === operation.package)?.plugin : undefined;
25568
+ const conforming = operation.coverage === "retirement" ? activation?.fingerprints.installed === installation.installedFingerprint : plugin !== undefined && sameBytes(activation, installation, plugin);
25569
+ return { resourceState: "present", activationState: conforming ? "active-conforming" : "active-nonconforming" };
25570
+ }
25545
25571
  function outcomeFor(row) {
25546
25572
  const operation = row.operation;
25547
25573
  switch (operation.action) {
@@ -25580,8 +25606,8 @@ function plannedNativeIdFor(item, plugin) {
25580
25606
  return error instanceof Error ? error : new Error(unknownErrorDiagnostic(error));
25581
25607
  }
25582
25608
  }
25583
- function uninventoriedUpdate(item, packageName, nativeId) {
25584
- return classified(operationIdentity("desired-pair", item.scope.id, packageName, nativeId, item.frozen.snapshot.fingerprint), "desired-pair", item.scope, sourceSnapshotId(item.frozen), packageName, nativeId, "update", "managed", null);
25609
+ function uninventoriedUpdate(item, packageName, nativeId, attemptId) {
25610
+ return classified(operationIdentity("desired-pair", item.scope.id, packageName, nativeId, `${item.frozen.snapshot.fingerprint}\x00${attemptId}`), "desired-pair", item.scope, sourceSnapshotId(item.frozen), packageName, nativeId, "update", "managed", null);
25585
25611
  }
25586
25612
  function classified(operationId, coverage, scope, sourceSnapshotId, packageName, nativeId, action, route, reason) {
25587
25613
  return {
@@ -25748,9 +25774,7 @@ function commandName(manifest) {
25748
25774
  return manifest.entries.every((entry) => entry.operation === "retire-source") ? "retire-source" : "sync";
25749
25775
  }
25750
25776
  function resolvePlannedSource(binding, transportSource, cache) {
25751
- const argument = transportArgument(binding, transportSource);
25752
- if (argument === undefined)
25753
- return resolveSource(sourceArgument(binding));
25777
+ const argument = transportArgument(binding, transportSource) ?? sourceArgument(binding);
25754
25778
  const cached = cache.get(argument);
25755
25779
  if (cached !== undefined)
25756
25780
  return cached;
@@ -26022,6 +26046,15 @@ async function executeFrozen(plan, hosts, now) {
26022
26046
  const refusal = ledger.generationRefusal(plan);
26023
26047
  if (refusal !== null)
26024
26048
  return refusalReport(plan, refusal);
26049
+ try {
26050
+ for (const frozen of plan.frozenSources.values()) {
26051
+ if (fingerprintTree(frozen.snapshotDir) !== frozen.snapshot.fingerprint) {
26052
+ return refusalReport(plan, createLifecycleReason("internal", "internal.invariant", "retained frozen source bytes drifted before apply"));
26053
+ }
26054
+ }
26055
+ } catch (error) {
26056
+ return refusalReport(plan, thrownReason(error));
26057
+ }
26025
26058
  const outcomes = [];
26026
26059
  let stopped = null;
26027
26060
  for (const row of plan.operations) {
@@ -26186,8 +26219,10 @@ async function prepareActivation(plan, operation, host, nativeId, action, record
26186
26219
  if (context === undefined) {
26187
26220
  return refused(createLifecycleReason("internal", "internal.invariant", `${action} '${operation.package}' has no frozen source snapshot`));
26188
26221
  }
26189
- const frozen = resolvePlannedSource(operation.scope.source, plan.transportSource, new Map);
26190
- if (frozen.snapshot.fingerprint !== context.reference.fingerprint || frozen.snapshot.revision !== context.reference.revision) {
26222
+ const frozen = operation.sourceSnapshotId === null ? undefined : plan.frozenSources.get(operation.sourceSnapshotId);
26223
+ if (frozen === undefined)
26224
+ return refused(createLifecycleReason("internal", "internal.invariant", `${action} '${operation.package}' lost its retained frozen source`));
26225
+ if (frozen.snapshot.fingerprint !== context.reference.fingerprint || frozen.snapshot.revision !== context.reference.revision || fingerprintTree(frozen.snapshotDir) !== context.reference.fingerprint) {
26191
26226
  return refused(createLifecycleReason("internal", "internal.invariant", `${action} '${operation.package}' source bytes drifted from the frozen snapshot`));
26192
26227
  }
26193
26228
  const plugin = frozen.plugins.find((candidate) => candidate.name === operation.package);
@@ -26201,6 +26236,9 @@ async function prepareActivation(plan, operation, host, nativeId, action, record
26201
26236
  if (version.kind !== "detected") {
26202
26237
  return refused(createLifecycleReason("runtime", "runtime.operation-failed", `${action} '${operation.package}' lost its detected target version`));
26203
26238
  }
26239
+ if (fingerprintTree(frozen.snapshotDir) !== context.reference.fingerprint) {
26240
+ return refused(createLifecycleReason("internal", "internal.invariant", `${action} '${operation.package}' retained snapshot changed during target observation`));
26241
+ }
26204
26242
  const inventory = inventoryPackageSemantics(plugin);
26205
26243
  const resolvedPins = resolveRecordedPinExecutables(plugin.dir, recordedPins);
26206
26244
  if (resolvedPins.kind === "refused")
@@ -27634,7 +27672,7 @@ function legacyCommandAction(command) {
27634
27672
  // package.json
27635
27673
  var package_default = {
27636
27674
  name: "plugnz",
27637
- version: "0.0.7",
27675
+ version: "0.0.8",
27638
27676
  description: "Install, diagnose and update agent plugins (Agent Plugins spec) and MCP configs across Claude Code, Codex, Cursor, Kimi and more",
27639
27677
  license: "MIT",
27640
27678
  type: "module",
@@ -27877,10 +27915,17 @@ function withoutRetiredActivation(state, scopeId, packageId, nativeId, retiredAt
27877
27915
  if (activation === undefined)
27878
27916
  throw new Error(`activation '${packageId}' is not recorded for manual removal`);
27879
27917
  const dropped = state.activations.filter((row) => row !== activation);
27918
+ const attempts = state.attempts.map((attempt) => attempt.id !== activation.pending?.attemptId ? attempt : {
27919
+ ...attempt,
27920
+ phase: "completed",
27921
+ completedAt: retiredAt,
27922
+ updatedAt: retiredAt,
27923
+ journal: attempt.journal.map((entry) => ({ ...entry, state: "completed", updatedAt: retiredAt }))
27924
+ });
27880
27925
  const tombstone = retirementTombstone(activation, retiredAt);
27881
27926
  if (tombstone === null) {
27882
27927
  if (activation.ownership.kind === "legacy-claim" && activation.route.kind === "legacy-unverified" && (activation.ownership.prior === "plgnz" || activation.ownership.prior === "unrecorded")) {
27883
- return { ...state, stateGeneration: state.stateGeneration + 1, activations: dropped };
27928
+ return { ...state, stateGeneration: state.stateGeneration + 1, activations: dropped, attempts };
27884
27929
  }
27885
27930
  throw new Error(`activation '${packageId}' cannot retain a tombstone`);
27886
27931
  }
@@ -27888,9 +27933,47 @@ function withoutRetiredActivation(state, scopeId, packageId, nativeId, retiredAt
27888
27933
  ...state,
27889
27934
  stateGeneration: state.stateGeneration + 1,
27890
27935
  activations: dropped,
27936
+ attempts,
27891
27937
  tombstones: state.tombstones.some((row) => row.id === tombstone.id) ? state.tombstones : [...state.tombstones, tombstone]
27892
27938
  };
27893
27939
  }
27940
+ function withPendingRemoval(state, operation, now) {
27941
+ const activation = state.activations.find((row) => row.scopeId === operation.scope.id && row.nativeId === operation.nativeId && (row.packageId === operation.package || row.packageId === operation.nativeId));
27942
+ if (activation === undefined)
27943
+ throw new Error(`activation '${operation.package}' is not recorded for manual removal`);
27944
+ if (activation.pending?.operation === "remove")
27945
+ return { ...state, stateGeneration: state.stateGeneration + 1 };
27946
+ const attemptId = `remove-attempt-${state.stateGeneration}-${operation.operationId}`;
27947
+ return {
27948
+ ...state,
27949
+ stateGeneration: state.stateGeneration + 1,
27950
+ scopes: state.scopes.map((scope) => scope.id === operation.scope.id ? { ...scope, lastAttemptId: attemptId } : scope),
27951
+ activations: state.activations.map((row) => row !== activation ? row : {
27952
+ ...row,
27953
+ pending: { operation: "remove", phase: "applying", attemptId, startedAt: now }
27954
+ }),
27955
+ attempts: [...state.attempts, {
27956
+ id: attemptId,
27957
+ command: "remove",
27958
+ phase: "pruning",
27959
+ mutationStarted: true,
27960
+ scopeIds: [operation.scope.id],
27961
+ startedAt: now,
27962
+ updatedAt: now,
27963
+ journal: [{
27964
+ operationId: operation.operationId,
27965
+ scopeId: operation.scope.id,
27966
+ packageId: activation.packageId,
27967
+ nativeId: activation.nativeId,
27968
+ action: "remove",
27969
+ state: "applying",
27970
+ route: activation.route,
27971
+ startedAt: now,
27972
+ updatedAt: now
27973
+ }]
27974
+ }]
27975
+ };
27976
+ }
27894
27977
  function reportFor(command, dryRun, plan, outcomes, options = {}) {
27895
27978
  const failure = options.reason ?? outcomes.find((candidate) => candidate.result !== "succeeded")?.reason ?? null;
27896
27979
  const result = options.result ?? (failure === null && outcomes.every((candidate) => candidate.result === "succeeded") ? "converged" : "incomplete");
@@ -29175,20 +29258,22 @@ async function main(argv) {
29175
29258
  preflightFailure = createLifecycleReason("internal", "internal.invariant", "install is pending; refusing removal");
29176
29259
  break;
29177
29260
  }
29261
+ const pendingOperationId = claimed?.pending?.operation === "remove" ? lifecycleState.attempts.find((attempt) => attempt.id === claimed.pending.attemptId)?.journal.find((entry) => entry.scopeId === claimed.scopeId && entry.packageId === claimed.packageId && entry.nativeId === claimed.nativeId)?.operationId : undefined;
29178
29262
  pairs.push({
29179
29263
  writer,
29180
29264
  record,
29181
29265
  nativeId: identity.nativeId,
29182
29266
  legacyNativeIds: identity.legacyNativeIds,
29183
29267
  failure: null,
29184
- operation: planOperation2({
29268
+ operation: { ...planOperation2({
29185
29269
  command: "remove",
29186
29270
  scope: selected.scope,
29187
29271
  package: identity.package,
29188
29272
  nativeId: identity.nativeId,
29189
29273
  action: "retire-orphan",
29190
- route: "managed"
29191
- })
29274
+ route: "managed",
29275
+ discriminator: lifecycleVersion === 2 ? String(lifecycleState.stateGeneration) : undefined
29276
+ }), ...pendingOperationId === undefined ? {} : { operationId: pendingOperationId } }
29192
29277
  });
29193
29278
  }
29194
29279
  if (preflightFailure !== null) {
@@ -29244,7 +29329,7 @@ async function main(argv) {
29244
29329
  throw new LifecycleCommandError(createLifecycleReason("internal", "internal.invariant", `state generation moved before removal of '${nativeId}'`));
29245
29330
  }
29246
29331
  try {
29247
- lifecycleState = withoutRetiredActivation(fresh.state, operation.scope.id, operation.package, nativeId, new Date().toISOString());
29332
+ lifecycleState = withPendingRemoval(fresh.state, operation, new Date().toISOString());
29248
29333
  } catch (error) {
29249
29334
  throw new LifecycleCommandError(createLifecycleReason("internal", "internal.invariant", unknownErrorDiagnostic(error)));
29250
29335
  }
@@ -29276,6 +29361,12 @@ async function main(argv) {
29276
29361
  }
29277
29362
  pairTerminalPhase = "finalize";
29278
29363
  if (lifecycleVersion === 2) {
29364
+ const fresh = readLifecycleState();
29365
+ if (fresh.sourceVersion !== 2 || fresh.state.stateGeneration !== lifecycleState.stateGeneration) {
29366
+ throw new LifecycleCommandError(createLifecycleReason("internal", "internal.invariant", `state generation moved before retirement finalization of '${nativeId}'`));
29367
+ }
29368
+ lifecycleState = withoutRetiredActivation(fresh.state, operation.scope.id, operation.package, nativeId, new Date().toISOString());
29369
+ writeLifecycleState(lifecycleState, { globalPreflight: "succeeded" });
29279
29370
  state = readState();
29280
29371
  } else {
29281
29372
  const finalized = state.filter((candidate) => candidate !== pending);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plugnz",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "Install, diagnose and update agent plugins (Agent Plugins spec) and MCP configs across Claude Code, Codex, Cursor, Kimi and more",
5
5
  "license": "MIT",
6
6
  "type": "module",