@acidicsoil/portable-capabilities 0.1.8 → 0.1.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.
@@ -3594,10 +3594,81 @@ import { parse } from "yaml";
3594
3594
  import { parseDocument } from "yaml";
3595
3595
 
3596
3596
  // packages/compiler/src/evidence/runtime-claims.ts
3597
+ function parseVersion(value) {
3598
+ const match = value.match(/(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?/u);
3599
+ if (!match) return void 0;
3600
+ return {
3601
+ major: Number(match[1]),
3602
+ minor: Number(match[2]),
3603
+ patch: Number(match[3]),
3604
+ prerelease: match[4] ? match[4].split(".").map((part) => /^\d+$/u.test(part) ? Number(part) : part) : []
3605
+ };
3606
+ }
3607
+ function compareVersions(left, right) {
3608
+ for (const key of ["major", "minor", "patch"]) {
3609
+ if (left[key] !== right[key]) return left[key] > right[key] ? 1 : -1;
3610
+ }
3611
+ if (left.prerelease.length === 0 && right.prerelease.length === 0) return 0;
3612
+ if (left.prerelease.length === 0) return 1;
3613
+ if (right.prerelease.length === 0) return -1;
3614
+ for (let index = 0; index < Math.max(left.prerelease.length, right.prerelease.length); index += 1) {
3615
+ const leftPart = left.prerelease[index];
3616
+ const rightPart = right.prerelease[index];
3617
+ if (leftPart === void 0) return -1;
3618
+ if (rightPart === void 0) return 1;
3619
+ if (leftPart === rightPart) continue;
3620
+ if (typeof leftPart === "number" && typeof rightPart === "string") return -1;
3621
+ if (typeof leftPart === "string" && typeof rightPart === "number") return 1;
3622
+ return leftPart > rightPart ? 1 : -1;
3623
+ }
3624
+ return 0;
3625
+ }
3626
+ function caretUpperBound(version) {
3627
+ if (version.major > 0) return { major: version.major + 1, minor: 0, patch: 0, prerelease: [] };
3628
+ if (version.minor > 0) return { major: 0, minor: version.minor + 1, patch: 0, prerelease: [] };
3629
+ return { major: 0, minor: 0, patch: version.patch + 1, prerelease: [] };
3630
+ }
3631
+ function tildeUpperBound(version) {
3632
+ return { major: version.major, minor: version.minor + 1, patch: 0, prerelease: [] };
3633
+ }
3634
+ function matchesConstraint(actual, operator, expected) {
3635
+ if (actual.prerelease.length > 0 && expected.prerelease.length === 0) return false;
3636
+ const comparison = compareVersions(actual, expected);
3637
+ switch (operator ?? "=") {
3638
+ case ">=":
3639
+ return comparison >= 0;
3640
+ case "<=":
3641
+ return comparison <= 0;
3642
+ case ">":
3643
+ return comparison > 0;
3644
+ case "<":
3645
+ return comparison < 0;
3646
+ case "^":
3647
+ return comparison >= 0 && compareVersions(actual, caretUpperBound(expected)) < 0;
3648
+ case "~":
3649
+ return comparison >= 0 && compareVersions(actual, tildeUpperBound(expected)) < 0;
3650
+ default:
3651
+ return comparison === 0;
3652
+ }
3653
+ }
3654
+ function isRuntimeVersionInRange(range, runtimeVersion) {
3655
+ const normalizedRange = range.trim();
3656
+ if (normalizedRange === "*") return true;
3657
+ const actual = parseVersion(runtimeVersion);
3658
+ if (!actual) return false;
3659
+ const expression = normalizedRange.includes(";") ? normalizedRange.slice(normalizedRange.lastIndexOf(";") + 1).trim() : normalizedRange;
3660
+ const constraints = expression.split(/\s+/u).map((token) => {
3661
+ const match = token.match(/^(>=|<=|>|<|=|\^|~)?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/u);
3662
+ if (!match || !match[2]) return void 0;
3663
+ const version = parseVersion(match[2]);
3664
+ return version ? { operator: match[1], version } : void 0;
3665
+ });
3666
+ return constraints.length > 0 && constraints.every(
3667
+ (constraint) => constraint?.version && matchesConstraint(actual, constraint.operator, constraint.version)
3668
+ );
3669
+ }
3597
3670
  function resolveRuntimeEvidence(claim4, runtimeVersion) {
3598
- const rangeMajor = claim4.versionRange.match(/\d+/)?.[0];
3599
- const runtimeMajor = runtimeVersion.match(/^\d+/)?.[0];
3600
- const applicable = claim4.versionRange === "*" || rangeMajor !== void 0 && rangeMajor === runtimeMajor;
3671
+ const applicable = isRuntimeVersionInRange(claim4.versionRange, runtimeVersion);
3601
3672
  const sufficientState = ["documented", "source-observed", "runtime-observed"].includes(
3602
3673
  claim4.state
3603
3674
  );
@@ -3615,85 +3686,26 @@ function resolveRuntimeEvidence(claim4, runtimeVersion) {
3615
3686
  }
3616
3687
 
3617
3688
  // packages/compiler/src/negotiation/adapter-registry.ts
3618
- var entries = [
3619
- {
3620
- id: "materialize-markdown",
3621
- operationKinds: ["MaterializeArtifact"],
3622
- purityClass: "pure",
3623
- effectClass: "filesystem",
3624
- requiredEvidenceCategories: ["location", "discovery"]
3625
- },
3626
- {
3627
- id: "patch-structured-config",
3628
- operationKinds: ["PatchStructuredConfig"],
3629
- purityClass: "planned-effect",
3630
- effectClass: "configuration",
3631
- requiredEvidenceCategories: ["configuration"]
3632
- },
3633
- {
3634
- id: "pi-extension-registration",
3635
- operationKinds: ["InstallExtension"],
3636
- purityClass: "planned-effect",
3637
- effectClass: "registration",
3638
- requiredEvidenceCategories: ["configuration"]
3639
- },
3640
- {
3641
- id: "oh-my-pi-code-as-hooks",
3642
- operationKinds: ["InstallExtension"],
3643
- purityClass: "planned-effect",
3644
- effectClass: "registration",
3645
- requiredEvidenceCategories: ["hooks", "configuration"]
3646
- },
3647
- {
3648
- id: "antigravity-plugin-registration",
3649
- operationKinds: ["RegisterPlugin"],
3650
- purityClass: "planned-effect",
3651
- effectClass: "registration",
3652
- requiredEvidenceCategories: ["plugin", "location"]
3653
- },
3654
- {
3655
- id: "antigravity-hook-registration",
3656
- operationKinds: ["RegisterHook"],
3657
- purityClass: "planned-effect",
3658
- effectClass: "registration",
3659
- requiredEvidenceCategories: ["hooks", "location"]
3660
- },
3661
- {
3662
- id: "antigravity-mcp-registration",
3663
- operationKinds: ["RegisterMcpServer"],
3664
- purityClass: "planned-effect",
3665
- effectClass: "registration",
3666
- requiredEvidenceCategories: ["mcp", "location"]
3667
- },
3668
- {
3669
- id: "opencode-custom-tool",
3670
- operationKinds: ["InstallExtension"],
3671
- purityClass: "planned-effect",
3672
- effectClass: "registration",
3673
- requiredEvidenceCategories: ["location", "discovery"]
3674
- },
3675
- {
3676
- id: "opencode-plugin",
3677
- operationKinds: ["RegisterPlugin"],
3678
- purityClass: "planned-effect",
3679
- effectClass: "registration",
3680
- requiredEvidenceCategories: ["plugin", "discovery"]
3681
- }
3682
- ];
3683
- var adapterRegistry = new Map(
3684
- entries.map((x) => [x.id, x])
3685
- );
3689
+ var adapterIds = Object.freeze([
3690
+ "materialize-markdown",
3691
+ "patch-structured-config",
3692
+ "pi-extension-registration",
3693
+ "oh-my-pi-code-as-hooks",
3694
+ "antigravity-plugin-registration",
3695
+ "antigravity-hook-registration",
3696
+ "antigravity-mcp-registration",
3697
+ "opencode-custom-tool",
3698
+ "opencode-plugin"
3699
+ ]);
3686
3700
  function requireAdapter(id) {
3687
- const item = adapterRegistry.get(id);
3688
- if (!item) throw new Error(`Unknown adapter ID: ${id}`);
3689
- return item;
3701
+ if (!adapterIds.includes(id)) throw new Error(`Unknown adapter ID: ${id}`);
3690
3702
  }
3691
3703
 
3692
3704
  // packages/compiler/src/negotiation/negotiate-capability.ts
3693
3705
  function negotiateCapability(artifact, descriptor, claims, version) {
3694
3706
  return artifact.invocationIntents.map((intent) => {
3695
3707
  const surface = descriptor.surfaces.find(
3696
- (x) => x.semanticKind === intent && x.evidenceClaimIds.some((claimId) => {
3708
+ (x) => x.semanticKind === intent && (x.versionRange === void 0 || isRuntimeVersionInRange(x.versionRange, version)) && x.evidenceClaimIds.some((claimId) => {
3697
3709
  const claim4 = claims.find((candidate) => candidate.id === claimId);
3698
3710
  return claim4 !== void 0 && resolveRuntimeEvidence(claim4, version).reason !== "version-inapplicable";
3699
3711
  })
@@ -3749,8 +3761,8 @@ var operationalSectionNames = Object.freeze([
3749
3761
  ]);
3750
3762
  function validateOperationalSkillContract(contract) {
3751
3763
  for (const section of operationalSectionNames) {
3752
- const entries2 = contract[section];
3753
- if (!Array.isArray(entries2) || entries2.length === 0 || entries2.some((entry) => !entry.trim())) {
3764
+ const entries = contract[section];
3765
+ if (!Array.isArray(entries) || entries.length === 0 || entries.some((entry) => !entry.trim())) {
3754
3766
  throw new Error(`Missing operational section: ${section}`);
3755
3767
  }
3756
3768
  }
@@ -3770,13 +3782,26 @@ var headings = {
3770
3782
  function renderOperationalSkillContract(contract) {
3771
3783
  validateOperationalSkillContract(contract);
3772
3784
  return operationalSectionNames.map((section) => {
3773
- const entries2 = contract[section].map((entry, index) => `${index + 1}. ${entry}`).join("\n");
3785
+ const entries = contract[section].map((entry, index) => `${index + 1}. ${entry}`).join("\n");
3774
3786
  return `## ${headings[section]}
3775
3787
 
3776
- ${entries2}`;
3788
+ ${entries}`;
3777
3789
  }).join("\n\n");
3778
3790
  }
3779
3791
 
3792
+ // packages/contracts/src/skill-document.ts
3793
+ function renderSkillDocument(input) {
3794
+ const description = input.description.replace(/[\r\n\t]+/gu, " ").replace(/\s{2,}/gu, " ").trim();
3795
+ if (!description) throw new Error(`Skill ${input.name} requires a non-empty description`);
3796
+ return `---
3797
+ name: ${JSON.stringify(input.name)}
3798
+ description: ${JSON.stringify(description)}
3799
+ ---
3800
+ ${input.marker}
3801
+ ${input.content}
3802
+ `;
3803
+ }
3804
+
3780
3805
  // packages/contracts/src/structured-config.ts
3781
3806
  import { existsSync, readFileSync as readFileSync2 } from "node:fs";
3782
3807
  import { parse as parse2, stringify } from "yaml";
@@ -3809,6 +3834,7 @@ function planProjection(artifact, runtimeId, scope, negotiations) {
3809
3834
  id: `materialize:${n4.intent}`,
3810
3835
  artifactId: artifact.id,
3811
3836
  logicalDestination: `${n4.intent}/${artifact.id}`,
3837
+ description: artifact.title,
3812
3838
  content: artifact.operationalContract ? renderOperationalSkillContract(artifact.operationalContract) : artifact.title
3813
3839
  });
3814
3840
  for (const resource2 of artifact.resourceFiles ?? [])
@@ -3844,14 +3870,14 @@ function planProjection(artifact, runtimeId, scope, negotiations) {
3844
3870
 
3845
3871
  // packages/compiler/src/planning/plan-installation.ts
3846
3872
  import { createHash as createHash3 } from "node:crypto";
3847
- function planInstallation(plan, descriptor) {
3873
+ function planInstallation(plan, descriptor, runtimeVersion) {
3848
3874
  const operations = [];
3849
3875
  for (const op of plan.operations) {
3850
3876
  if (op.kind === "MaterializeArtifact") {
3851
3877
  const location = descriptor.locations.find(
3852
- (x) => x.scope === plan.scope && (x.artifactKind === "skill" || x.artifactKind === op.logicalDestination.split("/")[0])
3878
+ (x) => x.scope === plan.scope && (x.artifactKind === "skill" || x.artifactKind === op.logicalDestination.split("/")[0]) && (!x.versionRange || isRuntimeVersionInRange(x.versionRange, runtimeVersion))
3853
3879
  );
3854
- if (!location || !location.pathTemplate || location.installation === "manual") {
3880
+ if (!location?.pathTemplate || location.installation === "manual") {
3855
3881
  operations.push({
3856
3882
  kind: "EmitManualStep",
3857
3883
  id: `manual:${op.id}`,
@@ -3879,14 +3905,15 @@ function planInstallation(plan, descriptor) {
3879
3905
  uninstall: "remove-owned-only",
3880
3906
  ownershipIntent: "own",
3881
3907
  destination: `${location.pathTemplate}/${op.logicalDestination}`,
3908
+ description: op.description,
3882
3909
  content: op.content
3883
3910
  });
3884
3911
  }
3885
3912
  if (op.kind === "IncludeResource") {
3886
3913
  const location = descriptor.locations.find(
3887
- (candidate) => candidate.scope === plan.scope && candidate.artifactKind === "skill"
3914
+ (candidate) => candidate.scope === plan.scope && candidate.artifactKind === "skill" && (!candidate.versionRange || isRuntimeVersionInRange(candidate.versionRange, runtimeVersion))
3888
3915
  );
3889
- if (!location || !location.pathTemplate || location.installation === "manual") {
3916
+ if (!location?.pathTemplate || location.installation === "manual") {
3890
3917
  operations.push({
3891
3918
  kind: "EmitManualStep",
3892
3919
  id: `manual:${op.id}`,
@@ -3958,7 +3985,7 @@ function planSetup(request, artifact, descriptor, claims, ownershipManifest) {
3958
3985
  request.runtimeVersion
3959
3986
  );
3960
3987
  const projectionPlan = planProjection(artifact, descriptor.id, request.scope, negotiations);
3961
- const baseInstallationPlan = planInstallation(projectionPlan, descriptor);
3988
+ const baseInstallationPlan = planInstallation(projectionPlan, descriptor, request.runtimeVersion);
3962
3989
  const mutatingIntent = request.intent === "setup" || request.intent === "update";
3963
3990
  let installationPlan = mutatingIntent ? baseInstallationPlan : { ...baseInstallationPlan, operations: [] };
3964
3991
  if (request.intent === "remove") {
@@ -4029,7 +4056,7 @@ function planSetup(request, artifact, descriptor, claims, ownershipManifest) {
4029
4056
  approvals: request.approvalPolicy === "preapproved" ? [] : approvals,
4030
4057
  manualSteps: installationPlan.operations.filter((operation) => operation.kind === "EmitManualStep").map((operation) => operation.id),
4031
4058
  verificationExpectations: projectionPlan.operations.filter((operation) => operation.kind === "ExpectVerification").map((operation) => operation.expectation),
4032
- nativeUsageInputs: negotiations.filter((outcome) => outcome.surfaceId).map((outcome) => outcome.surfaceId)
4059
+ nativeUsageInputs: negotiations.flatMap(({ surfaceId }) => surfaceId ? [surfaceId] : [])
4033
4060
  };
4034
4061
  }
4035
4062
 
@@ -11406,8 +11433,8 @@ async function assertNoSymlinks(current) {
11406
11433
  async function treeDigest(root) {
11407
11434
  const hash = createHash5("sha256");
11408
11435
  async function visit(current, relative4 = "") {
11409
- const entries2 = await readdir(current, { withFileTypes: true });
11410
- for (const entry of entries2.sort((left, right) => left.name.localeCompare(right.name))) {
11436
+ const entries = await readdir(current, { withFileTypes: true });
11437
+ for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
11411
11438
  if ([".git", "node_modules", ".turbo"].includes(entry.name)) continue;
11412
11439
  const path9 = join(current, entry.name);
11413
11440
  const child = relative4 ? `${relative4}/${entry.name}` : entry.name;
@@ -11706,9 +11733,9 @@ function writeEnvelope(envelope, format2) {
11706
11733
  }
11707
11734
 
11708
11735
  // packages/cli/src/release.ts
11709
- var cliVersion = true ? "0.1.8" : createRequire(import.meta.url)("../../../package.json").version;
11736
+ var cliVersion = true ? "0.1.10" : createRequire(import.meta.url)("../../../package.json").version;
11710
11737
 
11711
- // packages/adapters/antigravity/src/materialize.ts
11738
+ // packages/adapters/src/antigravity/materialize.ts
11712
11739
  import { resolve as resolve2 } from "node:path";
11713
11740
  var marker = "<!-- portable-capabilities:owned antigravity -->";
11714
11741
  function destination(value) {
@@ -11739,15 +11766,15 @@ function diagnostic(operation, kind, message) {
11739
11766
  }
11740
11767
  function render(operation) {
11741
11768
  const skill = operation.destination.match(/skills\/skill\/([^/]+)$/);
11742
- if (!skill) return `${marker}
11769
+ const name = skill?.[1];
11770
+ if (!name) return `${marker}
11743
11771
  ${operation.content}`;
11744
- return `---
11745
- name: ${skill[1]}
11746
- description: ${operation.content.replace(/[\r\n]+/g, " ").trim()}
11747
- ---
11748
- ${marker}
11749
- ${operation.content}
11750
- `;
11772
+ return renderSkillDocument({
11773
+ name,
11774
+ description: operation.description,
11775
+ marker,
11776
+ content: operation.content
11777
+ });
11751
11778
  }
11752
11779
  function registration(operation) {
11753
11780
  return `${JSON.stringify({ adapterId: operation.adapterId, kind: operation.kind, target: operation.target }, null, 2)}
@@ -11856,7 +11883,7 @@ function prepareAntigravityMaterialization(plan, root) {
11856
11883
  });
11857
11884
  }
11858
11885
 
11859
- // packages/adapters/antigravity/src/runtime-descriptor.ts
11886
+ // packages/adapters/src/antigravity/runtime-descriptor.ts
11860
11887
  var markdown = "materialize-markdown";
11861
11888
  var plugin = "antigravity-plugin-registration";
11862
11889
  var hook = "antigravity-hook-registration";
@@ -12049,7 +12076,7 @@ var antigravityRuntimeDescriptor = {
12049
12076
  ]
12050
12077
  };
12051
12078
 
12052
- // packages/adapters/antigravity/src/runtime-evidence.ts
12079
+ // packages/adapters/src/antigravity/runtime-evidence.ts
12053
12080
  import { createHash as createHash7 } from "node:crypto";
12054
12081
  var digest2 = (value) => createHash7("sha256").update(value).digest("hex");
12055
12082
  var retrievedAt = "2026-08-10T00:00:00.000Z";
@@ -12197,7 +12224,7 @@ var antigravityRuntimeEvidence = [
12197
12224
  )
12198
12225
  ];
12199
12226
 
12200
- // packages/adapters/claude-code/src/materialize.ts
12227
+ // packages/adapters/src/claude-code/materialize.ts
12201
12228
  import { resolve as resolve3 } from "node:path";
12202
12229
  var marker2 = "<!-- portable-capabilities:owned claude-code -->";
12203
12230
  function destination2(value) {
@@ -12232,15 +12259,15 @@ function diagnostic2(operation, kind, message) {
12232
12259
  }
12233
12260
  function render2(operation) {
12234
12261
  const skill = operation.destination.match(/skills\/skill\/([^/]+)$/);
12235
- if (!skill) return `${marker2}
12262
+ const name = skill?.[1];
12263
+ if (!name) return `${marker2}
12236
12264
  ${operation.content}`;
12237
- return `---
12238
- name: ${skill[1]}
12239
- description: ${operation.content.replace(/[\r\n]+/g, " ").trim()}
12240
- ---
12241
- ${marker2}
12242
- ${operation.content}
12243
- `;
12265
+ return renderSkillDocument({
12266
+ name,
12267
+ description: operation.description,
12268
+ marker: marker2,
12269
+ content: operation.content
12270
+ });
12244
12271
  }
12245
12272
  function config(operation, root, path9) {
12246
12273
  if (operation.format !== "json")
@@ -12354,7 +12381,7 @@ function prepareClaudeCodeMaterialization(plan, root) {
12354
12381
  });
12355
12382
  }
12356
12383
 
12357
- // packages/adapters/claude-code/src/runtime-descriptor.ts
12384
+ // packages/adapters/src/claude-code/runtime-descriptor.ts
12358
12385
  var markdown2 = "materialize-markdown";
12359
12386
  var claudeCodeRuntimeDescriptor = {
12360
12387
  schemaVersion: "1.0",
@@ -12556,7 +12583,7 @@ var claudeCodeRuntimeDescriptor = {
12556
12583
  ]
12557
12584
  };
12558
12585
 
12559
- // packages/adapters/claude-code/src/runtime-evidence.ts
12586
+ // packages/adapters/src/claude-code/runtime-evidence.ts
12560
12587
  import { createHash as createHash8 } from "node:crypto";
12561
12588
  var digest3 = (value) => createHash8("sha256").update(value).digest("hex");
12562
12589
  var retrievedAt2 = "2026-08-10T00:00:00.000Z";
@@ -12727,18 +12754,20 @@ var claudeCodeRuntimeEvidence = Object.freeze([
12727
12754
  }))
12728
12755
  ]);
12729
12756
 
12730
- // packages/adapters/codex/src/materialize.ts
12757
+ // packages/adapters/src/codex/materialize.ts
12731
12758
  import { resolve as resolve4 } from "node:path";
12732
12759
  var marker3 = "<!-- portable-capabilities:owned codex -->";
12733
12760
  function destination3(value) {
12734
12761
  const project = value.match(/^\.codex\/skills\/skill\/(.+)$/);
12735
12762
  if (project) return `.codex/skills/${project[1]}/SKILL.md`;
12763
+ const currentProject = value.match(/^\.agents\/skills\/skill\/(.+)$/);
12764
+ if (currentProject) return `.agents/skills/${currentProject[1]}/SKILL.md`;
12736
12765
  const user = value.match(/^\$CODEX_HOME\/skills\/skill\/(.+)$/);
12737
12766
  if (user) return `.codex-user/skills/${user[1]}/SKILL.md`;
12738
12767
  return value.replace(/^\$CODEX_HOME\//, ".codex-user/");
12739
12768
  }
12740
12769
  function resourceDestination3(value) {
12741
- return value.replace(/^\.codex\/skills\/skill\//u, ".codex/skills/").replace(/^\$CODEX_HOME\/skills\/skill\//u, ".codex-user/skills/");
12770
+ return value.replace(/^\.codex\/skills\/skill\//u, ".codex/skills/").replace(/^\.agents\/skills\/skill\//u, ".agents/skills/").replace(/^\$CODEX_HOME\/skills\/skill\//u, ".codex-user/skills/");
12742
12771
  }
12743
12772
  function confined3(root, path9) {
12744
12773
  const target = resolve4(root, path9);
@@ -12748,15 +12777,15 @@ function confined3(root, path9) {
12748
12777
  }
12749
12778
  function render3(operation) {
12750
12779
  const skill = operation.destination.match(/skills\/skill\/([^/]+)$/);
12751
- if (!skill) return `${marker3}
12780
+ const name = skill?.[1];
12781
+ if (!name) return `${marker3}
12752
12782
  ${operation.content}`;
12753
- return `---
12754
- name: ${skill[1]}
12755
- description: ${operation.content.replace(/[\r\n]+/g, " ").trim()}
12756
- ---
12757
- ${marker3}
12758
- ${operation.content}
12759
- `;
12783
+ return renderSkillDocument({
12784
+ name,
12785
+ description: operation.description,
12786
+ marker: marker3,
12787
+ content: operation.content
12788
+ });
12760
12789
  }
12761
12790
  function transform(operation) {
12762
12791
  if (operation.format !== "json")
@@ -12820,25 +12849,48 @@ function prepareCodexMaterialization(plan, root, ..._options) {
12820
12849
  });
12821
12850
  }
12822
12851
 
12823
- // packages/adapters/codex/src/runtime-descriptor.ts
12852
+ // packages/adapters/src/codex/runtime-descriptor.ts
12824
12853
  var codexRuntimeDescriptor = {
12825
12854
  schemaVersion: "1.0",
12826
12855
  id: "codex",
12827
12856
  aliases: ["codex-cli"],
12828
- supportedVersions: [">=0.146.0"],
12857
+ supportedVersions: ["=0.146.0", ">=0.147.0 <0.148.0"],
12829
12858
  locations: [
12830
12859
  {
12831
- id: "codex-project-skills",
12860
+ id: "codex-project-skills-0146",
12832
12861
  artifactKind: "skill",
12833
12862
  scope: "project",
12834
12863
  pathTemplate: ".codex/skills",
12835
12864
  evidenceClaimIds: ["codex.location.project-skills"],
12865
+ versionRange: "=0.146.0",
12866
+ installation: "automatic",
12867
+ registration: "discovery",
12868
+ uninstall: "automatic"
12869
+ },
12870
+ {
12871
+ id: "codex-project-skills-0147",
12872
+ artifactKind: "skill",
12873
+ scope: "project",
12874
+ pathTemplate: ".agents/skills",
12875
+ evidenceClaimIds: ["codex.location.project-skills-agents"],
12876
+ versionRange: ">=0.147.0 <0.148.0",
12877
+ installation: "automatic",
12878
+ registration: "discovery",
12879
+ uninstall: "automatic"
12880
+ },
12881
+ {
12882
+ id: "codex-user-skills-0147",
12883
+ artifactKind: "skill",
12884
+ scope: "user",
12885
+ pathTemplate: ".agents/skills",
12886
+ evidenceClaimIds: ["codex.location.user-skills-agents"],
12887
+ versionRange: ">=0.147.0 <0.148.0",
12836
12888
  installation: "automatic",
12837
12889
  registration: "discovery",
12838
12890
  uninstall: "automatic"
12839
12891
  },
12840
12892
  {
12841
- id: "codex-user-skills",
12893
+ id: "codex-user-skills-0146",
12842
12894
  artifactKind: "skill",
12843
12895
  scope: "user",
12844
12896
  pathTemplate: "$CODEX_HOME/skills",
@@ -12851,16 +12903,30 @@ var codexRuntimeDescriptor = {
12851
12903
  ],
12852
12904
  surfaces: [
12853
12905
  {
12854
- id: "codex-direct-skill",
12906
+ id: "codex-direct-skill-0146",
12855
12907
  semanticKind: "skill",
12856
12908
  integrationMode: "declarative",
12857
12909
  adapterId: "materialize-markdown",
12858
- discovery: "Codex discovers project and user skills from its native skills roots.",
12859
- invocation: "Invoke the installed skill through Codex's native skill mechanism.",
12910
+ discovery: "Codex 0.146.x discovers project skills from .codex/skills.",
12911
+ invocation: "Invoke the installed skill through Codex 0.146.x native skill selection.",
12860
12912
  precedence: ["project", "user"],
12861
12913
  evidenceClaimIds: ["codex.discovery.skills", "codex.invocation.skills"],
12862
- stability: "stable",
12914
+ versionRange: "=0.146.0",
12915
+ stability: "version-gated",
12863
12916
  evidenceState: "runtime-observed"
12917
+ },
12918
+ {
12919
+ id: "codex-direct-skill-0147",
12920
+ semanticKind: "skill",
12921
+ integrationMode: "declarative",
12922
+ adapterId: "materialize-markdown",
12923
+ discovery: "Codex 0.147.x discovers project skills from .agents/skills.",
12924
+ invocation: "Invoke the installed skill through Codex 0.147.x native skill selection.",
12925
+ precedence: ["project", "user"],
12926
+ evidenceClaimIds: ["codex.discovery.skills-0147", "codex.invocation.skills-0147"],
12927
+ versionRange: ">=0.147.0 <0.148.0",
12928
+ stability: "version-gated",
12929
+ evidenceState: "source-observed"
12864
12930
  }
12865
12931
  ],
12866
12932
  configuration: [],
@@ -12870,10 +12936,37 @@ var codexRuntimeDescriptor = {
12870
12936
  degradation: []
12871
12937
  };
12872
12938
 
12873
- // packages/adapters/codex/src/runtime-evidence.ts
12939
+ // packages/adapters/src/codex/runtime-evidence.ts
12874
12940
  import { createHash as createHash9 } from "node:crypto";
12875
12941
  var digest4 = (value) => createHash9("sha256").update(value).digest("hex");
12876
12942
  var observedAt = "2026-08-08T00:00:00.000Z";
12943
+ var sourceObservedAt = "2026-08-15T00:00:00.000Z";
12944
+ var codexSource = "https://github.com/openai/codex";
12945
+ var codexSkillRootsSource = "https://github.com/openai/codex/blob/53f3fa749659498fa24c81da8fde5440fa7bba7f/codex-rs/ext/skills/src/host_roots.rs";
12946
+ var codexCurrentRange = ">=0.147.0 <0.148.0";
12947
+ var projectSkillRootExcerpt = 'const AGENTS_DIR_NAME: &str = ".agents";\nconst SKILLS_DIR_NAME: &str = "skills";';
12948
+ var userSkillRootExcerpt = "home_dir.join(AGENTS_DIR_NAME).join(SKILLS_DIR_NAME), SkillScope::User";
12949
+ var discoveryExcerpt = "let agents_skills = directory.join(AGENTS_DIR_NAME).join(SKILLS_DIR_NAME);";
12950
+ var invocationExcerpt = 'UserInput::Skill {\n name: "demo".to_string(),';
12951
+ var sourceClaim2 = (id, subject, assertion, sourceLocator, sourceExcerpt) => ({
12952
+ schemaVersion: "1.0",
12953
+ id,
12954
+ runtimeId: "codex",
12955
+ category: id.includes("location") ? "location" : id.includes("discovery") ? "discovery" : "invocation",
12956
+ subject,
12957
+ assertion,
12958
+ versionRange: codexCurrentRange,
12959
+ sourceType: "official-source",
12960
+ source: codexSource,
12961
+ sourceLocator,
12962
+ sourceExcerpt,
12963
+ sourceDigest: digest4(sourceExcerpt),
12964
+ state: "source-observed",
12965
+ confidence: "high",
12966
+ verificationStatus: "verified",
12967
+ stability: "version-gated",
12968
+ verifiedAt: sourceObservedAt
12969
+ });
12877
12970
  var codexRuntimeEvidence = Object.freeze([
12878
12971
  {
12879
12972
  schemaVersion: "1.0",
@@ -12881,54 +12974,82 @@ var codexRuntimeEvidence = Object.freeze([
12881
12974
  runtimeId: "codex",
12882
12975
  category: "location",
12883
12976
  subject: "project skill root",
12884
- assertion: "Codex project skills are materialized beneath .codex/skills.",
12885
- versionRange: ">=0.146.0",
12977
+ assertion: "Codex 0.146.x project skills are materialized beneath .codex/skills.",
12978
+ versionRange: "=0.146.0",
12886
12979
  sourceType: "runtime-probe",
12887
12980
  source: "codex-cli",
12888
- sourceLocator: "native vertical tracer",
12981
+ sourceLocator: "packages/conformance/src/tracer/codex-native-evidence.json",
12889
12982
  sourceExcerpt: ".codex/skills",
12890
- sourceDigest: digest4("codex project skills .codex/skills"),
12983
+ sourceDigest: digest4("codex 0.146 project skills .codex/skills"),
12891
12984
  state: "runtime-observed",
12892
12985
  confidence: "high",
12893
12986
  verificationStatus: "verified",
12894
- stability: "stable",
12987
+ stability: "version-gated",
12895
12988
  verifiedAt: observedAt
12896
12989
  },
12990
+ sourceClaim2(
12991
+ "codex.location.project-skills-agents",
12992
+ "project skill root",
12993
+ "Codex 0.147.x project skills are discovered beneath .agents/skills.",
12994
+ `${codexSkillRootsSource}#L92-L101`,
12995
+ projectSkillRootExcerpt
12996
+ ),
12997
+ sourceClaim2(
12998
+ "codex.location.user-skills-agents",
12999
+ "user skill root",
13000
+ "Codex 0.147.x user skills are discovered beneath ~/.agents/skills.",
13001
+ `${codexSkillRootsSource}#L107-L119`,
13002
+ userSkillRootExcerpt
13003
+ ),
12897
13004
  {
12898
13005
  schemaVersion: "1.0",
12899
13006
  id: "codex.location.user-skills",
12900
13007
  runtimeId: "codex",
12901
13008
  category: "location",
12902
13009
  subject: "user skill root",
12903
- assertion: "Codex user skills are materialized beneath CODEX_HOME/skills.",
12904
- versionRange: ">=0.146.0",
13010
+ assertion: "Codex 0.146.x user skills are materialized beneath CODEX_HOME/skills.",
13011
+ versionRange: "=0.146.0",
12905
13012
  sourceType: "runtime-probe",
12906
13013
  source: "codex-cli",
12907
13014
  sourceLocator: "native vertical tracer",
12908
13015
  sourceExcerpt: "CODEX_HOME/skills",
12909
- sourceDigest: digest4("codex user skills CODEX_HOME/skills"),
13016
+ sourceDigest: digest4("codex 0.146 user skills CODEX_HOME/skills"),
12910
13017
  state: "runtime-observed",
12911
13018
  confidence: "high",
12912
13019
  verificationStatus: "verified",
12913
- stability: "stable",
13020
+ stability: "version-gated",
12914
13021
  verifiedAt: observedAt
12915
13022
  },
13023
+ sourceClaim2(
13024
+ "codex.discovery.skills-0147",
13025
+ "native skill discovery",
13026
+ "Codex 0.147.x discovers Agent Skills from .agents/skills.",
13027
+ `${codexSkillRootsSource}#L139-L161`,
13028
+ discoveryExcerpt
13029
+ ),
13030
+ sourceClaim2(
13031
+ "codex.invocation.skills-0147",
13032
+ "native skill invocation",
13033
+ "Codex 0.147.x invokes a discovered Agent Skill through its native skill selection.",
13034
+ "https://github.com/openai/codex/blob/53f3fa749659498fa24c81da8fde5440fa7bba7f/codex-rs/core/tests/suite/skills.rs",
13035
+ invocationExcerpt
13036
+ ),
12916
13037
  {
12917
13038
  schemaVersion: "1.0",
12918
13039
  id: "codex.discovery.skills",
12919
13040
  runtimeId: "codex",
12920
13041
  category: "discovery",
12921
13042
  subject: "native skill discovery",
12922
- assertion: "Codex natively discovers a skill installed in its project skill root.",
12923
- versionRange: ">=0.146.0",
13043
+ assertion: "Codex 0.146.x natively discovers a skill installed in its project skill root.",
13044
+ versionRange: "=0.146.0",
12924
13045
  sourceType: "runtime-probe",
12925
13046
  source: "codex-cli",
12926
13047
  sourceLocator: "packages/conformance/src/tracer/codex-native-evidence.json",
12927
- sourceDigest: digest4("codex native discovery"),
13048
+ sourceDigest: digest4("codex 0.146 native discovery"),
12928
13049
  state: "runtime-observed",
12929
13050
  confidence: "high",
12930
13051
  verificationStatus: "verified",
12931
- stability: "stable",
13052
+ stability: "version-gated",
12932
13053
  verifiedAt: observedAt
12933
13054
  },
12934
13055
  {
@@ -12937,21 +13058,22 @@ var codexRuntimeEvidence = Object.freeze([
12937
13058
  runtimeId: "codex",
12938
13059
  category: "invocation",
12939
13060
  subject: "native skill invocation",
12940
- assertion: "Codex can invoke an installed skill through the native client.",
12941
- versionRange: ">=0.146.0",
13061
+ assertion: "Codex 0.146.x can invoke an installed skill through the native client.",
13062
+ versionRange: "=0.146.0",
12942
13063
  sourceType: "runtime-probe",
12943
13064
  source: "codex-cli",
12944
13065
  sourceLocator: "packages/conformance/src/tracer/codex-native-evidence.json",
12945
- sourceDigest: digest4("codex native invocation"),
13066
+ sourceDigest: digest4("codex 0.146 native invocation"),
12946
13067
  state: "runtime-observed",
12947
13068
  confidence: "high",
12948
13069
  verificationStatus: "verified",
12949
- stability: "stable",
13070
+ stability: "version-gated",
12950
13071
  verifiedAt: observedAt
12951
13072
  }
12952
13073
  ]);
12953
13074
 
12954
- // packages/adapters/dcode/src/materialize.ts
13075
+ // packages/adapters/src/dcode/materialize.ts
13076
+ import { readFile as readFile3 } from "node:fs/promises";
12955
13077
  import { resolve as resolve5 } from "node:path";
12956
13078
  var marker4 = "<!-- portable-capabilities:owned dcode -->";
12957
13079
  function destination4(value) {
@@ -12983,21 +13105,46 @@ function diagnostic3(operation, kind, message) {
12983
13105
  }
12984
13106
  function render4(operation) {
12985
13107
  const skill = operation.destination.match(/skills\/skill\/([^/]+)$/);
12986
- if (!skill) return `${marker4}
13108
+ const name = skill?.[1];
13109
+ if (!name) return `${marker4}
12987
13110
  ${operation.content}`;
12988
- return `---
12989
- name: ${skill[1]}
12990
- description: ${operation.content.replace(/[\r\n]+/g, " ").trim()}
12991
- ---
12992
- ${marker4}
12993
- ${operation.content}
12994
- `;
13111
+ return renderSkillDocument({
13112
+ name,
13113
+ description: operation.description,
13114
+ marker: marker4,
13115
+ content: operation.content
13116
+ });
12995
13117
  }
12996
13118
  function registration3(operation) {
12997
13119
  return `${JSON.stringify({ adapterId: operation.adapterId, kind: operation.kind, target: operation.target }, null, 2)}
12998
13120
  `;
12999
13121
  }
13000
- function prepareDCodeMaterialization(plan, root) {
13122
+ async function mergedInstructions(root, path9, operations, removedIds) {
13123
+ let existing;
13124
+ try {
13125
+ existing = await readFile3(resolve5(root, path9), "utf8");
13126
+ } catch (error) {
13127
+ if (error.code !== "ENOENT") throw error;
13128
+ }
13129
+ if (!existing && operations.length === 0) return void 0;
13130
+ const selectedIds = new Set(operations.map((operation) => operation.id));
13131
+ const existingText = existing ?? "";
13132
+ const firstHeading = existingText.search(/^## .+$/mu);
13133
+ const preamble = firstHeading >= 0 ? existingText.slice(0, firstHeading).trim() : existingText.trim();
13134
+ const sections = firstHeading >= 0 ? existingText.slice(firstHeading).split(/(?=^## .+$)/mu) : [];
13135
+ const retained = sections.filter((section) => {
13136
+ const id = section.match(/^## ([^\n]+)/u)?.[1] ?? "";
13137
+ return !selectedIds.has(id) && !removedIds.some((removedId) => id.startsWith(`${removedId}:`));
13138
+ }).map((section) => section.trim());
13139
+ const generated = operations.slice().sort((left, right) => left.id.localeCompare(right.id)).map((operation) => `## ${operation.id}
13140
+
13141
+ ${operation.content.trim()}`);
13142
+ const sectionsToWrite = [...retained, ...generated];
13143
+ return sectionsToWrite.length > 0 ? `${preamble || marker4}
13144
+ ${sectionsToWrite.join("\n\n")}
13145
+ ` : void 0;
13146
+ }
13147
+ async function prepareDCodeMaterialization(plan, root) {
13001
13148
  if (plan.runtimeId !== "dcode") throw new Error(`DCode adapter cannot prepare ${plan.runtimeId}`);
13002
13149
  const mutations = [];
13003
13150
  const diagnostics = [];
@@ -13099,28 +13246,39 @@ function prepareDCodeMaterialization(plan, root) {
13099
13246
  );
13100
13247
  }
13101
13248
  }
13102
- if (instructionOperations.length > 0) {
13249
+ const instructionRemovals = plan.operations.filter(
13250
+ (operation) => operation.kind === "RemoveOwnedArtifact" && /^\.deepagents\/skills\/[^/]+\/SKILL\.md$/u.test(destination4(operation.detail))
13251
+ );
13252
+ if (instructionOperations.length > 0 || instructionRemovals.length > 0) {
13103
13253
  const instructionPath = ".deepagents/AGENTS.md";
13104
13254
  confined4(root, instructionPath);
13105
- const content = instructionOperations.sort((left, right) => left.id.localeCompare(right.id)).map((operation) => `## ${operation.id}
13106
-
13107
- ${operation.content.trim()}`).join("\n\n");
13108
- const first = instructionOperations[0];
13109
- mutations.push({
13110
- path: instructionPath,
13111
- kind: "write",
13112
- content: `<!-- portable-capabilities:owned dcode -->
13113
- ${content}
13114
- `,
13115
- mode: 420,
13116
- operationId: "dcode:shared-instructions",
13117
- sourceProjectionOperationId: first.sourceProjectionOperationId,
13118
- ownershipIntent: first.ownershipIntent,
13119
- approval: first.approval,
13120
- rollback: first.rollback,
13121
- upgrade: first.upgrade,
13122
- uninstall: first.uninstall
13255
+ const first = instructionOperations[0] ?? instructionRemovals[0];
13256
+ if (!first) throw new Error("DCode shared instruction operation is missing");
13257
+ const removedIds = instructionRemovals.flatMap((operation) => {
13258
+ const match = operation.detail.match(/^\.deepagents\/skills\/([^/]+)\/SKILL\.md$/u);
13259
+ return match?.[1] ? [match[1]] : [];
13123
13260
  });
13261
+ const content = await mergedInstructions(
13262
+ root,
13263
+ instructionPath,
13264
+ instructionOperations,
13265
+ removedIds
13266
+ );
13267
+ mutations.push(
13268
+ content ? {
13269
+ path: instructionPath,
13270
+ kind: "write",
13271
+ content,
13272
+ mode: 420,
13273
+ operationId: "dcode:shared-instructions",
13274
+ sourceProjectionOperationId: first.sourceProjectionOperationId,
13275
+ ownershipIntent: first.ownershipIntent,
13276
+ approval: first.approval,
13277
+ rollback: first.rollback,
13278
+ upgrade: first.upgrade,
13279
+ uninstall: first.uninstall
13280
+ } : { path: instructionPath, kind: "remove", ...metadata3(first) }
13281
+ );
13124
13282
  }
13125
13283
  return Object.freeze({
13126
13284
  runtimeId: plan.runtimeId,
@@ -13130,7 +13288,7 @@ ${content}
13130
13288
  });
13131
13289
  }
13132
13290
 
13133
- // packages/adapters/dcode/src/runtime-descriptor.ts
13291
+ // packages/adapters/src/dcode/runtime-descriptor.ts
13134
13292
  var markdown3 = "materialize-markdown";
13135
13293
  var source2 = "source-observed";
13136
13294
  var dcodeRuntimeDescriptor = {
@@ -13335,7 +13493,7 @@ var dcodeRuntimeDescriptor = {
13335
13493
  ]
13336
13494
  };
13337
13495
 
13338
- // packages/adapters/dcode/src/runtime-evidence.ts
13496
+ // packages/adapters/src/dcode/runtime-evidence.ts
13339
13497
  import { createHash as createHash10 } from "node:crypto";
13340
13498
  var digest5 = (value) => createHash10("sha256").update(value).digest("hex");
13341
13499
  var verifiedAt = "2026-08-10T00:00:00.000Z";
@@ -13518,7 +13676,7 @@ var dcodeRuntimeEvidence = Object.freeze([
13518
13676
  })
13519
13677
  ]);
13520
13678
 
13521
- // packages/adapters/oh-my-pi/src/materialize.ts
13679
+ // packages/adapters/src/oh-my-pi/materialize.ts
13522
13680
  import { resolve as resolve6 } from "node:path";
13523
13681
  var marker5 = "<!-- portable-capabilities:owned oh-my-pi -->";
13524
13682
  function resolveOhMyPiProfile(input) {
@@ -13576,13 +13734,13 @@ function diagnostic4(operation, kind, message) {
13576
13734
  }
13577
13735
  function render5(operation) {
13578
13736
  const skill = operation.destination.match(/skills\/skill\/([^/]+)$/);
13579
- return skill ? `---
13580
- name: ${skill[1]}
13581
- description: ${operation.content.replace(/[\r\n]+/g, " ").trim()}
13582
- ---
13583
- ${marker5}
13584
- ${operation.content}
13585
- ` : `${marker5}
13737
+ const name = skill?.[1];
13738
+ return name ? renderSkillDocument({
13739
+ name,
13740
+ description: operation.description,
13741
+ marker: marker5,
13742
+ content: operation.content
13743
+ }) : `${marker5}
13586
13744
  ${operation.content}`;
13587
13745
  }
13588
13746
  function registration4(operation) {
@@ -13691,7 +13849,7 @@ async function prepareOhMyPiMaterialization(plan, root, profile = { home: root }
13691
13849
  });
13692
13850
  }
13693
13851
 
13694
- // packages/adapters/oh-my-pi/src/runtime-descriptor.ts
13852
+ // packages/adapters/src/oh-my-pi/runtime-descriptor.ts
13695
13853
  var markdown4 = "materialize-markdown";
13696
13854
  var hooks = "oh-my-pi-code-as-hooks";
13697
13855
  var config2 = "patch-structured-config";
@@ -13939,7 +14097,7 @@ var ohMyPiRuntimeDescriptor = {
13939
14097
  ]
13940
14098
  };
13941
14099
 
13942
- // packages/adapters/oh-my-pi/src/runtime-evidence.ts
14100
+ // packages/adapters/src/oh-my-pi/runtime-evidence.ts
13943
14101
  import { createHash as createHash11 } from "node:crypto";
13944
14102
  var digest6 = (value) => createHash11("sha256").update(value).digest("hex");
13945
14103
  var verifiedAt2 = "2026-08-10T00:00:00.000Z";
@@ -14089,7 +14247,7 @@ var ohMyPiRuntimeEvidence = [
14089
14247
  }
14090
14248
  ];
14091
14249
 
14092
- // packages/adapters/opencode/src/materialize.ts
14250
+ // packages/adapters/src/opencode/materialize.ts
14093
14251
  import { resolve as resolve7 } from "node:path";
14094
14252
  var marker6 = "<!-- portable-capabilities:owned opencode -->";
14095
14253
  function destination5(value) {
@@ -14126,15 +14284,15 @@ function diagnostic5(operation, kind, message) {
14126
14284
  }
14127
14285
  function render6(operation) {
14128
14286
  const skill = operation.destination.match(/skills\/skill\/([^/]+)$/);
14129
- if (!skill) return `${marker6}
14287
+ const name = skill?.[1];
14288
+ if (!name) return `${marker6}
14130
14289
  ${operation.content}`;
14131
- return `---
14132
- name: ${skill[1]}
14133
- description: ${operation.content.replace(/[\r\n]+/g, " ").trim()}
14134
- ---
14135
- ${marker6}
14136
- ${operation.content}
14137
- `;
14290
+ return renderSkillDocument({
14291
+ name,
14292
+ description: operation.description,
14293
+ marker: marker6,
14294
+ content: operation.content
14295
+ });
14138
14296
  }
14139
14297
  function config3(operation, root, path9) {
14140
14298
  if (operation.format !== "json")
@@ -14242,7 +14400,7 @@ async function prepareOpencodeMaterialization(plan, root) {
14242
14400
  });
14243
14401
  }
14244
14402
 
14245
- // packages/adapters/opencode/src/runtime-descriptor.ts
14403
+ // packages/adapters/src/opencode/runtime-descriptor.ts
14246
14404
  var native = "opencode.stable";
14247
14405
  var beta = "opencode.v2-beta";
14248
14406
  var opencodeRuntimeDescriptor = {
@@ -14524,7 +14682,7 @@ var opencodeRuntimeDescriptor = {
14524
14682
  ]
14525
14683
  };
14526
14684
 
14527
- // packages/adapters/opencode/src/runtime-evidence.ts
14685
+ // packages/adapters/src/opencode/runtime-evidence.ts
14528
14686
  import { createHash as createHash12 } from "node:crypto";
14529
14687
  var retrievedAt3 = "2026-08-10T00:00:00.000Z";
14530
14688
  var docs2 = "docs/RFC/runtime-capability-profiles-revised.md";
@@ -14608,14 +14766,14 @@ var sourceClaims = [
14608
14766
  "discovery",
14609
14767
  "stable skill discovery",
14610
14768
  "OpenCode discovers native project and user skills.",
14611
- "runtime-observed"
14769
+ "source-observed"
14612
14770
  ],
14613
14771
  [
14614
14772
  "invocation.skills",
14615
14773
  "invocation",
14616
14774
  "stable skill invocation",
14617
14775
  "OpenCode invokes a discovered skill through its native skill tool.",
14618
- "runtime-observed"
14776
+ "source-observed"
14619
14777
  ],
14620
14778
  [
14621
14779
  "discovery.agents",
@@ -14753,7 +14911,7 @@ var opencodeRuntimeEvidence = Object.freeze([
14753
14911
  ...betaClaims
14754
14912
  ]);
14755
14913
 
14756
- // packages/adapters/pi/src/materialize.ts
14914
+ // packages/adapters/src/pi/materialize.ts
14757
14915
  import { resolve as resolve8 } from "node:path";
14758
14916
  var marker7 = "<!-- portable-capabilities:owned pi -->";
14759
14917
  function destination6(value) {
@@ -14774,15 +14932,15 @@ function confined7(root, path9) {
14774
14932
  }
14775
14933
  function render7(operation) {
14776
14934
  const skill = operation.destination.match(/skills\/skill\/([^/]+)$/);
14777
- if (!skill) return `${marker7}
14935
+ const name = skill?.[1];
14936
+ if (!name) return `${marker7}
14778
14937
  ${operation.content}`;
14779
- return `---
14780
- name: ${skill[1]}
14781
- description: ${operation.content.replace(/[\r\n]+/g, " ").trim()}
14782
- ---
14783
- ${marker7}
14784
- ${operation.content}
14785
- `;
14938
+ return renderSkillDocument({
14939
+ name,
14940
+ description: operation.description,
14941
+ marker: marker7,
14942
+ content: operation.content
14943
+ });
14786
14944
  }
14787
14945
  function transform2(operation) {
14788
14946
  if (operation.format !== "json")
@@ -14846,7 +15004,7 @@ function preparePiMaterialization(plan, root, ..._options) {
14846
15004
  });
14847
15005
  }
14848
15006
 
14849
- // packages/adapters/pi/src/runtime-descriptor.ts
15007
+ // packages/adapters/src/pi/runtime-descriptor.ts
14850
15008
  var piRuntimeDescriptor = {
14851
15009
  schemaVersion: "1.0",
14852
15010
  id: "pi",
@@ -14924,7 +15082,7 @@ var piRuntimeDescriptor = {
14924
15082
  ]
14925
15083
  };
14926
15084
 
14927
- // packages/adapters/pi/src/runtime-evidence.ts
15085
+ // packages/adapters/src/pi/runtime-evidence.ts
14928
15086
  import { createHash as createHash13 } from "node:crypto";
14929
15087
  var digest8 = (value) => createHash13("sha256").update(value).digest("hex");
14930
15088
  var observedAt2 = "2026-08-08T04:24:00-05:00";
@@ -14990,7 +15148,7 @@ var piRuntimeEvidence = [
14990
15148
  category: "discovery",
14991
15149
  subject: "native skill discovery",
14992
15150
  assertion: "Pi natively discovers a project skill with extensions disabled.",
14993
- versionRange: ">=0.64.0",
15151
+ versionRange: "=0.64.0",
14994
15152
  sourceType: "runtime-probe",
14995
15153
  source: "pi",
14996
15154
  sourceLocator: "packages/conformance/src/tracer/pi-native-evidence.json",
@@ -15008,7 +15166,7 @@ var piRuntimeEvidence = [
15008
15166
  category: "invocation",
15009
15167
  subject: "native skill invocation",
15010
15168
  assertion: "Pi invokes a discovered project skill with extensions disabled.",
15011
- versionRange: ">=0.64.0",
15169
+ versionRange: "=0.64.0",
15012
15170
  sourceType: "runtime-probe",
15013
15171
  source: "pi",
15014
15172
  sourceLocator: "packages/conformance/src/tracer/pi-native-evidence.json",
@@ -15132,19 +15290,19 @@ function normalizeSetupRequest(input) {
15132
15290
  // packages/cli/src/setup/service.ts
15133
15291
  import { createHash as createHash16 } from "node:crypto";
15134
15292
  import { existsSync as existsSync2 } from "node:fs";
15135
- import { readdir as readdir3, readFile as readFile5 } from "node:fs/promises";
15293
+ import { readdir as readdir3, readFile as readFile6 } from "node:fs/promises";
15136
15294
  import { dirname as dirname4, isAbsolute as isAbsolute3, relative as relative3, resolve as resolve11 } from "node:path";
15137
15295
  import { fileURLToPath as fileURLToPath4 } from "node:url";
15138
15296
  import { parse as parse3 } from "yaml";
15139
15297
 
15140
15298
  // packages/cli/src/installation/lifecycle.ts
15141
15299
  import { createHash as createHash15 } from "node:crypto";
15142
- import { chmod, copyFile, lstat as lstat2, mkdir as mkdir3, readFile as readFile4, rename as rename2, rm, writeFile as writeFile3 } from "node:fs/promises";
15300
+ import { chmod, copyFile, lstat as lstat2, mkdir as mkdir3, readFile as readFile5, rename as rename2, rm, writeFile as writeFile3 } from "node:fs/promises";
15143
15301
  import { dirname as dirname3, isAbsolute as isAbsolute2, join as join3, relative as relative2, resolve as resolve10, sep } from "node:path";
15144
15302
 
15145
15303
  // packages/cli/src/installation/manifest.ts
15146
15304
  import { createHash as createHash14 } from "node:crypto";
15147
- import { mkdir as mkdir2, readFile as readFile3, rename, stat, writeFile as writeFile2 } from "node:fs/promises";
15305
+ import { mkdir as mkdir2, readFile as readFile4, rename, stat, writeFile as writeFile2 } from "node:fs/promises";
15148
15306
  import { dirname as dirname2, join as join2 } from "node:path";
15149
15307
  var installationManifestName = ".portable-capabilities-manifest.json";
15150
15308
  var installationJournalName = ".portable-capabilities-journal.json";
@@ -15163,9 +15321,9 @@ function normalizeDigestValue(value) {
15163
15321
  function stableManifestDigest(value) {
15164
15322
  return digestBytes(new TextEncoder().encode(JSON.stringify(normalizeDigestValue(value))));
15165
15323
  }
15166
- function packageEntriesDigest(entries2, packageIdentities = []) {
15324
+ function packageEntriesDigest(entries, packageIdentities = []) {
15167
15325
  return stableManifestDigest({
15168
- entries: [...entries2].sort((left, right) => left.path.localeCompare(right.path)).map((entry) => ({ path: entry.path, digest: entry.digest, mode: entry.mode })),
15326
+ entries: [...entries].sort((left, right) => left.path.localeCompare(right.path)).map((entry) => ({ path: entry.path, digest: entry.digest, mode: entry.mode })),
15169
15327
  packageIdentities: [...packageIdentities].sort(
15170
15328
  (left, right) => left.lockPath.localeCompare(right.lockPath)
15171
15329
  )
@@ -15198,7 +15356,7 @@ function verifyInstallationManifestDigest(manifest) {
15198
15356
  }
15199
15357
  }
15200
15358
  async function digestFile(path9) {
15201
- return digestBytes(await readFile3(path9));
15359
+ return digestBytes(await readFile4(path9));
15202
15360
  }
15203
15361
  function manifestPath(targetRoot) {
15204
15362
  return join2(targetRoot, installationManifestName);
@@ -15209,7 +15367,7 @@ function journalPath(targetRoot) {
15209
15367
  async function readInstallationManifest(targetRoot) {
15210
15368
  try {
15211
15369
  const parsed = JSON.parse(
15212
- await readFile3(manifestPath(targetRoot), "utf8")
15370
+ await readFile4(manifestPath(targetRoot), "utf8")
15213
15371
  );
15214
15372
  if (parsed.schemaVersion !== "1.1.0" || !Array.isArray(parsed.entries)) {
15215
15373
  throw new Error("Invalid installation manifest shape");
@@ -15486,6 +15644,19 @@ async function assertPlanPathsSafe(targetRoot, plan) {
15486
15644
  }
15487
15645
  async function executePreparedInstallation(options) {
15488
15646
  const manifest = await readInstallationManifest(options.targetRoot);
15647
+ const operation = options.preparedOperation ?? "install";
15648
+ if (options.preparedDiagnostics?.length) {
15649
+ return result(operation, "degraded", {
15650
+ ok: false,
15651
+ diagnostics: options.preparedDiagnostics
15652
+ });
15653
+ }
15654
+ if (options.preparedMutations.length === 0) {
15655
+ return result(operation, "degraded", {
15656
+ ok: false,
15657
+ diagnostics: ["No prepared mutations can be installed"]
15658
+ });
15659
+ }
15489
15660
  const duplicatePaths = /* @__PURE__ */ new Set();
15490
15661
  for (const mutation of options.preparedMutations) {
15491
15662
  if (duplicatePaths.has(mutation.path)) {
@@ -15514,7 +15685,7 @@ async function executePreparedInstallation(options) {
15514
15685
  const exists = await pathExists(target);
15515
15686
  if (mutation.ownershipIntent === "preserve-user" && exists) continue;
15516
15687
  if (exists && mutation.kind === "write" && mutation.ownershipIntent === "own") {
15517
- const current = await readFile4(target, "utf8");
15688
+ const current = await readFile5(target, "utf8");
15518
15689
  const ownedEntry = manifest?.entries.find((entry) => entry.path === mutation.path);
15519
15690
  const isManifestOwned = ownedEntry !== void 0 && await digestFile(target) === ownedEntry.digest;
15520
15691
  if (!current.includes("portable-capabilities:owned") && !isManifestOwned)
@@ -15560,7 +15731,6 @@ async function executePreparedInstallation(options) {
15560
15731
  packageIdentities,
15561
15732
  entries: nextEntries
15562
15733
  };
15563
- const operation = options.preparedOperation ?? "install";
15564
15734
  if (options.dryRun)
15565
15735
  return result(operation, manifest ? "healthy" : "missing", {
15566
15736
  dryRun: true,
@@ -15595,63 +15765,6 @@ async function statusInstallation(targetRoot) {
15595
15765
  manifest
15596
15766
  });
15597
15767
  }
15598
- async function removeInstallation(targetRoot, faultInjector, capabilityIds) {
15599
- const manifest = await readInstallationManifest(targetRoot);
15600
- if (!manifest) return result("remove", "missing", { ok: false });
15601
- const selected = new Set(capabilityIds ?? []);
15602
- const scoped = (path9) => {
15603
- if (selected.size === 0) return true;
15604
- const normalized = path9.replaceAll("\\", "/");
15605
- return [...selected].some(
15606
- (id) => normalized.includes(`/skills/${id}/`) || normalized.endsWith(`/skills/${id}`)
15607
- );
15608
- };
15609
- const classifications = await classifyManifestEntries(manifest);
15610
- const unchanged = new Set(
15611
- classifications.unchanged.filter((entry) => scoped(entry.path)).map((entry) => entry.path)
15612
- );
15613
- const removes = manifest.entries.filter((entry) => unchanged.has(entry.path)).map((entry) => entry.path);
15614
- const preserved = manifest.entries.filter((entry) => !unchanged.has(entry.path));
15615
- const nextManifest = preserved.length ? (() => {
15616
- const entries2 = Object.freeze(preserved);
15617
- const preservedPaths = new Set(entries2.map((entry) => entry.path));
15618
- const packageIdentities = Object.freeze(
15619
- manifest.packageIdentities.filter((identity3) => preservedPaths.has(identity3.lockPath))
15620
- );
15621
- const packageDigest = packageEntriesDigest(entries2, packageIdentities);
15622
- const base = {
15623
- runtimeId: manifest.runtimeId,
15624
- packageVersion: manifest.packageVersion,
15625
- targetRoot: manifest.targetRoot,
15626
- journalVersion: manifest.journalVersion,
15627
- lifecycleState: "modified",
15628
- packageIdentities,
15629
- entries: entries2
15630
- };
15631
- return {
15632
- ...manifest,
15633
- ...base,
15634
- packageDigest,
15635
- installationDigest: installationOwnershipDigest(packageDigest, base)
15636
- };
15637
- })() : void 0;
15638
- return commitPlan(
15639
- "remove",
15640
- {
15641
- sourceRoot: targetRoot,
15642
- targetRoot,
15643
- runtimeId: manifest.runtimeId,
15644
- packageVersion: manifest.packageVersion,
15645
- ...faultInjector ? { faultInjector } : {}
15646
- },
15647
- {
15648
- writes: [],
15649
- removes,
15650
- preserved: preserved.map((entry) => entry.path),
15651
- ...nextManifest ? { nextManifest } : {}
15652
- }
15653
- );
15654
- }
15655
15768
 
15656
15769
  // packages/cli/src/setup/service.ts
15657
15770
  function roleFacet(role, name) {
@@ -15712,7 +15825,7 @@ async function loadCanonicalArtifact(catalogRoot, capabilityId) {
15712
15825
  if (!capabilityId || /[\\/]/u.test(capabilityId))
15713
15826
  throw new Error("capability IDs must be single path segments");
15714
15827
  const file = resolve11(catalogRoot, "roles", `${capabilityId}.yaml`);
15715
- const role = parse3(await readFile5(file, "utf8"));
15828
+ const role = parse3(await readFile6(file, "utf8"));
15716
15829
  if (role.schemaVersion !== "1.0" || role.id !== capabilityId || !role.purpose || !Array.isArray(role.inputs) || !role.output || !role.family || !role.taxonomy || !Array.isArray(role.precedence) || !Array.isArray(role.requiredBehavior) || !Array.isArray(role.forbiddenBehavior) || !Array.isArray(role.evaluation?.criteria)) {
15717
15830
  throw new Error(`Invalid canonical capability: ${file}`);
15718
15831
  }
@@ -15721,7 +15834,7 @@ async function loadCanonicalArtifact(catalogRoot, capabilityId) {
15721
15834
  const schemaRelativePath = relative3(resolve11(catalogRoot), schemaFile);
15722
15835
  if (isAbsolute3(schemaRelativePath) || schemaRelativePath.startsWith(".."))
15723
15836
  throw new Error(`Canonical output schema escapes catalog: ${role.output.schema}`);
15724
- const schemaContent = await readFile5(schemaFile, "utf8");
15837
+ const schemaContent = await readFile6(schemaFile, "utf8");
15725
15838
  const contract = operationalContract(role);
15726
15839
  const contractContent = `${JSON.stringify(
15727
15840
  { ...role, output: { ...role.output, schema: "resources/output.schema.json" } },
@@ -15794,6 +15907,27 @@ function previewSetup(request, artifact, descriptor, claims, ownershipManifest)
15794
15907
  const body = { ...plan, selectedSurfaces, conversions, conflicts };
15795
15908
  return { ...body, digest: stableDigest(body) };
15796
15909
  }
15910
+ function composeInstallationPlan(previews, artifacts, runtimeId) {
15911
+ const first = previews[0]?.installationPlan;
15912
+ if (!first) throw new Error("at least one installation plan is required");
15913
+ const operations = previews.flatMap((preview, index) => {
15914
+ const capabilityId = artifacts[index]?.id;
15915
+ if (!capabilityId) throw new Error("installation plan is missing its capability");
15916
+ return preview.installationPlan.operations.map((operation) => ({
15917
+ ...operation,
15918
+ id: `${capabilityId}:${operation.id}`,
15919
+ sourceProjectionOperationId: `${capabilityId}:${operation.sourceProjectionOperationId}`
15920
+ }));
15921
+ });
15922
+ const base = {
15923
+ schemaVersion: first.schemaVersion,
15924
+ id: `installation:${runtimeId}:${artifacts.map((artifact) => artifact.id).join(",")}`,
15925
+ runtimeId,
15926
+ scope: first.scope,
15927
+ operations
15928
+ };
15929
+ return { ...base, digest: stableDigest(base) };
15930
+ }
15797
15931
  function defaultCatalogRoot() {
15798
15932
  const moduleDir = dirname4(fileURLToPath4(import.meta.url));
15799
15933
  const packagedCatalog = [resolve11(moduleDir, "catalog"), resolve11(moduleDir, "../catalog")].find(
@@ -15852,23 +15986,33 @@ async function executeSetupSelection(request, artifacts, descriptor, claims, con
15852
15986
  ...context.faultInjector ? { faultInjector: context.faultInjector } : {}
15853
15987
  };
15854
15988
  const runtime = resolveProductionRuntime(executionRequest.runtimeId);
15855
- const lifecycle = executionRequest.intent === "setup" || executionRequest.intent === "update" ? await executePreparedInstallation({
15856
- ...lifecycleOptions,
15857
- preparedOperation: executionRequest.intent === "update" ? "upgrade" : "install",
15858
- preparedMutations: (await Promise.all(
15859
- previews.map((preview) => runtime.prepare(preview.installationPlan, targetRoot))
15860
- )).flatMap((prepared) => prepared.mutations)
15861
- }) : executionRequest.intent === "remove" ? await removeInstallation(
15862
- targetRoot,
15863
- context.faultInjector,
15864
- executionRequest.capabilityIds
15865
- ) : await statusInstallation(targetRoot);
15989
+ const lifecycle = executionRequest.intent === "setup" || executionRequest.intent === "update" || executionRequest.intent === "remove" ? await (async () => {
15990
+ const installationPlan = composeInstallationPlan(
15991
+ previews,
15992
+ planningArtifacts,
15993
+ executionRequest.runtimeId
15994
+ );
15995
+ const prepared = await runtime.prepare(installationPlan, targetRoot);
15996
+ if (prepared.runtimeId !== installationPlan.runtimeId || prepared.planId !== installationPlan.id) {
15997
+ throw new Error(`Materializer returned an invalid ${executionRequest.runtimeId} plan`);
15998
+ }
15999
+ const duplicatePath = prepared.mutations.map((mutation) => mutation.path).find((path9, index, paths) => paths.indexOf(path9) !== index);
16000
+ if (duplicatePath)
16001
+ throw new Error(`Selection produced duplicate prepared path: ${duplicatePath}`);
16002
+ return executePreparedInstallation({
16003
+ ...lifecycleOptions,
16004
+ preparedOperation: executionRequest.intent === "update" ? "upgrade" : executionRequest.intent === "remove" ? "remove" : "install",
16005
+ preparedMutations: prepared.mutations,
16006
+ ...prepared.diagnostics ? {
16007
+ preparedDiagnostics: prepared.diagnostics.map(
16008
+ (diagnostic6) => `${diagnostic6.kind}: ${diagnostic6.message}`
16009
+ )
16010
+ } : {}
16011
+ });
16012
+ })() : await statusInstallation(targetRoot);
15866
16013
  return { request, previews, lifecycle };
15867
16014
  }
15868
16015
 
15869
- // packages/cli/src/wizard.ts
15870
- import { execFileSync } from "node:child_process";
15871
-
15872
16016
  // node_modules/.pnpm/@clack+core@1.4.3/node_modules/@clack/core/dist/index.mjs
15873
16017
  import { styleText } from "node:util";
15874
16018
  import { stdout, stdin } from "node:process";
@@ -17076,6 +17220,73 @@ ${r2}
17076
17220
  }
17077
17221
  }).prompt();
17078
17222
 
17223
+ // packages/cli/src/setup/probe.ts
17224
+ import { execFileSync } from "node:child_process";
17225
+ var executables = Object.freeze({
17226
+ antigravity: "agy",
17227
+ "claude-code": "claude",
17228
+ codex: "codex",
17229
+ dcode: "dcode",
17230
+ "oh-my-pi": "omp",
17231
+ opencode: "opencode",
17232
+ pi: "pi"
17233
+ });
17234
+ function resolveRuntimeProbe(runtimeId) {
17235
+ const runtime = resolveProductionRuntime(runtimeId);
17236
+ const canonicalId = runtime.descriptor.id;
17237
+ const executable = executables[canonicalId];
17238
+ if (!executable) throw new Error(`No runtime probe configured for ${canonicalId}`);
17239
+ return {
17240
+ runtimeId: canonicalId,
17241
+ executable,
17242
+ args: ["--version"],
17243
+ supportedVersions: runtime.descriptor.supportedVersions
17244
+ };
17245
+ }
17246
+ function parseVersion2(output) {
17247
+ return output.match(/\bv?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)\b/u)?.[1];
17248
+ }
17249
+ function defaultRun(probe) {
17250
+ return execFileSync(probe.executable, probe.args, {
17251
+ encoding: "utf8",
17252
+ stdio: ["ignore", "pipe", "pipe"],
17253
+ timeout: 5e3
17254
+ });
17255
+ }
17256
+ function probeRuntimeVersion(runtimeId, options = {}) {
17257
+ const probe = resolveRuntimeProbe(runtimeId);
17258
+ let output;
17259
+ try {
17260
+ output = (options.run ?? defaultRun)(probe);
17261
+ } catch (error) {
17262
+ const code = error.code;
17263
+ const unavailable = code === "ENOENT" || code === "EACCES";
17264
+ return {
17265
+ status: unavailable ? "unavailable" : "failed",
17266
+ runtimeId: probe.runtimeId,
17267
+ executable: probe.executable,
17268
+ reason: unavailable ? `${probe.executable} is not installed or is not executable` : error instanceof Error ? error.message : String(error)
17269
+ };
17270
+ }
17271
+ const version = parseVersion2(output);
17272
+ if (!version)
17273
+ return {
17274
+ status: "unknown-version",
17275
+ runtimeId: probe.runtimeId,
17276
+ executable: probe.executable,
17277
+ reason: "version probe returned no parseable semantic version"
17278
+ };
17279
+ if (!probe.supportedVersions.some((range) => isRuntimeVersionInRange(range, version)))
17280
+ return {
17281
+ status: "unsupported-version",
17282
+ runtimeId: probe.runtimeId,
17283
+ executable: probe.executable,
17284
+ version,
17285
+ reason: `version ${version} is outside the supported runtime ranges`
17286
+ };
17287
+ return { status: "detected", runtimeId: probe.runtimeId, executable: probe.executable, version };
17288
+ }
17289
+
17079
17290
  // packages/cli/src/wizard.ts
17080
17291
  var supportedRuntimes = [...productionRuntimeRegistry.keys()];
17081
17292
  function unwrap(value) {
@@ -17086,17 +17297,6 @@ function unwrap(value) {
17086
17297
  }
17087
17298
  return value;
17088
17299
  }
17089
- function detectRuntimeVersion(runtime) {
17090
- try {
17091
- const output = execFileSync(runtime, ["--version"], {
17092
- encoding: "utf8",
17093
- stdio: ["ignore", "pipe", "pipe"]
17094
- });
17095
- return output.match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/)?.[0] ?? "unknown";
17096
- } catch {
17097
- return "unknown";
17098
- }
17099
- }
17100
17300
  async function runInstallWizard() {
17101
17301
  intro("Portable Capabilities setup");
17102
17302
  try {
@@ -17116,6 +17316,12 @@ async function runInstallWizard() {
17116
17316
  options: supportedRuntimes.map((value) => ({ value, label: value }))
17117
17317
  })
17118
17318
  );
17319
+ const probe = probeRuntimeVersion(runtime);
17320
+ if (probe.status !== "detected" || !probe.version) {
17321
+ log.error(`${runtime}: ${probe.reason ?? probe.status}`);
17322
+ process.exitCode = 4;
17323
+ return;
17324
+ }
17119
17325
  const target = String(
17120
17326
  unwrap(
17121
17327
  await text({
@@ -17133,7 +17339,7 @@ async function runInstallWizard() {
17133
17339
  );
17134
17340
  const request = normalizeSetupRequest({
17135
17341
  runtimeId: runtime,
17136
- runtimeVersion: detectRuntimeVersion(runtime),
17342
+ runtimeVersion: probe.version,
17137
17343
  scope: "project",
17138
17344
  projectDirectory: target,
17139
17345
  capabilityIds,
@@ -17155,7 +17361,7 @@ async function runInstallWizard() {
17155
17361
  runtimeEntry.claims,
17156
17362
  { sourceRoot: process.cwd(), packageVersion: cliVersion }
17157
17363
  );
17158
- if (!result2.lifecycle || !result2.lifecycle.ok) {
17364
+ if (!result2.lifecycle?.ok) {
17159
17365
  for (const diagnostic6 of result2.lifecycle?.diagnostics ?? ["Approval required."])
17160
17366
  log.error(diagnostic6);
17161
17367
  process.exitCode = 4;