skillset 0.1.0-beta.9 → 0.13.1

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 (3) hide show
  1. package/dist/cli.js +190 -29
  2. package/dist/create.js +190 -29
  3. package/package.json +2 -3
package/dist/cli.js CHANGED
@@ -7517,12 +7517,12 @@ var skillsetFeatureRegistry = defineFeatureRegistry([
7517
7517
  status: "externally_managed"
7518
7518
  },
7519
7519
  cursor: {
7520
- evidence: [docs("docs/features/runtime-adapters.md"), fixture("fixtures/external/repos/superpowers")],
7520
+ evidence: [docs("docs/features/runtime-adapters.md"), fixture("fixtures/external/repos.yaml")],
7521
7521
  reason: "Cursor support needs target documentation and adapter evidence before Skillset can lower or distribute it.",
7522
7522
  status: "planned"
7523
7523
  },
7524
7524
  "gemini-cli": {
7525
- evidence: [docs("docs/features/runtime-adapters.md"), fixture("fixtures/external/repos/superpowers")],
7525
+ evidence: [docs("docs/features/runtime-adapters.md"), fixture("fixtures/external/repos.yaml")],
7526
7526
  reason: "Gemini support needs target documentation and adapter evidence before Skillset can lower or distribute it.",
7527
7527
  status: "planned"
7528
7528
  },
@@ -7537,7 +7537,7 @@ var skillsetFeatureRegistry = defineFeatureRegistry([
7537
7537
  status: "future"
7538
7538
  },
7539
7539
  opencode: {
7540
- evidence: [docs("docs/features/runtime-adapters.md"), fixture("fixtures/external/repos/superpowers")],
7540
+ evidence: [docs("docs/features/runtime-adapters.md"), fixture("fixtures/external/repos.yaml")],
7541
7541
  reason: "OpenCode support needs target documentation and adapter evidence before Skillset can lower or distribute it.",
7542
7542
  status: "planned"
7543
7543
  }
@@ -7624,7 +7624,7 @@ var skillsetFeatureRegistry = defineFeatureRegistry([
7624
7624
  status: "implemented",
7625
7625
  summary: "Normalizes tool policy intent into Claude allowed-tools fields and Codex metadata sidecars.",
7626
7626
  targetSupport: {
7627
- claude: { status: "native" },
7627
+ claude: { status: "transformed" },
7628
7628
  codex: { status: "metadata_only" }
7629
7629
  },
7630
7630
  title: "Tool Intent",
@@ -7679,6 +7679,9 @@ function defineFeatureRegistry(entries) {
7679
7679
  assertRuntimeSupportVocabulary(entries);
7680
7680
  return [...entries].sort((left, right) => compareStrings(left.id, right.id));
7681
7681
  }
7682
+ function listSkillsetFeatures(registry = skillsetFeatureRegistry) {
7683
+ return registry;
7684
+ }
7682
7685
  function getSkillsetFeature(id, registry = skillsetFeatureRegistry) {
7683
7686
  return registry.find((entry) => entry.id === id);
7684
7687
  }
@@ -14447,6 +14450,9 @@ async function exists6(path) {
14447
14450
  throw error;
14448
14451
  }
14449
14452
  }
14453
+ // packages/core/src/host-leak.ts
14454
+ var textDecoder3 = new TextDecoder;
14455
+
14450
14456
  // packages/core/src/normalized-output-tree.ts
14451
14457
  var DEFAULT_STRUCTURED_JSON_BASENAMES = new Set([".skillset.lock"]);
14452
14458
 
@@ -14454,7 +14460,7 @@ var DEFAULT_STRUCTURED_JSON_BASENAMES = new Set([".skillset.lock"]);
14454
14460
  var textEncoder4 = new TextEncoder;
14455
14461
  // packages/core/src/destination-ownership.ts
14456
14462
  import { basename as basename3 } from "path";
14457
- var textDecoder3 = new TextDecoder;
14463
+ var textDecoder4 = new TextDecoder;
14458
14464
  function classifyDestinationOwnership(args) {
14459
14465
  const file = classifyFileOwnership(args.path, args.target);
14460
14466
  const fields = args.content === undefined ? [] : classifyFieldOwnership(args.path, args.target, args.content);
@@ -14493,7 +14499,7 @@ function classifyFieldOwnership(path, target, content) {
14493
14499
  return [];
14494
14500
  let record;
14495
14501
  try {
14496
- const parsed = JSON.parse(textDecoder3.decode(content));
14502
+ const parsed = JSON.parse(textDecoder4.decode(content));
14497
14503
  if (!isJsonRecord(parsed))
14498
14504
  return [];
14499
14505
  record = parsed;
@@ -17585,7 +17591,7 @@ var CI_WORKFLOW_PATH = ".github/workflows/skillset-ci.yml";
17585
17591
  function renderCiWorkflow() {
17586
17592
  return [
17587
17593
  "# Scaffolded by skillset init --include ci. Skillset does not manage this file; edit freely.",
17588
- "# Pin skillset to an exact version for reproducible CI runs.",
17594
+ "# Consider pinning skillset to an exact version for reproducible CI runs.",
17589
17595
  "# Notes:",
17590
17596
  "# - Mechanical fixes pushed with GITHUB_TOKEN do not retrigger workflows; if your",
17591
17597
  "# branch protection requires checks on the fixed commit, push with a PAT instead.",
@@ -17627,7 +17633,7 @@ function renderCiWorkflow() {
17627
17633
  " - name: Run skillset ci",
17628
17634
  " id: skillset",
17629
17635
  " run: >-",
17630
- " bunx skillset@beta ci",
17636
+ " bunx skillset ci",
17631
17637
  " ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && '--fix' || '' }}",
17632
17638
  ' --report "$RUNNER_TEMP/skillset-ci-report.md"',
17633
17639
  " continue-on-error: true",
@@ -19064,7 +19070,7 @@ async function exists12(path2) {
19064
19070
 
19065
19071
  // apps/skillset/src/authoring.ts
19066
19072
  import { resolve as resolve8, relative as relative15 } from "path";
19067
- var textDecoder4 = new TextDecoder;
19073
+ var textDecoder5 = new TextDecoder;
19068
19074
  async function explainPath(rootPath, inputPath, options = {}) {
19069
19075
  const graph = await loadBuildGraph(rootPath, options);
19070
19076
  const allRendered = await renderBuildGraph(graph);
@@ -19077,33 +19083,39 @@ async function explainPath(rootPath, inputPath, options = {}) {
19077
19083
  const items = collectLockItems(rendered);
19078
19084
  const asSource = items.filter((item) => item.sourcePath === target);
19079
19085
  if (asSource.length > 0) {
19086
+ const matchedLoweringOutcomes = explainLoweringOutcomes(target, asSource, loweringOutcomes);
19080
19087
  return {
19081
19088
  path: target,
19082
19089
  kind: explainSourceKind(graph, target),
19083
19090
  entries: asSource.map((item) => item.entry),
19084
- loweringOutcomes: explainLoweringOutcomes(target, asSource, loweringOutcomes),
19091
+ features: featureCapabilitiesForPath(graph, target, asSource, matchedLoweringOutcomes),
19092
+ loweringOutcomes: matchedLoweringOutcomes,
19085
19093
  notes: sourceNotes(graph, target)
19086
19094
  };
19087
19095
  }
19088
19096
  const asGenerated = items.filter((item) => item.outputPath === target || item.files.some((file) => joinOutputRoot2(item.outputRoot, file) === target));
19089
19097
  if (asGenerated.length > 0) {
19098
+ const matchedLoweringOutcomes = explainLoweringOutcomes(target, asGenerated, loweringOutcomes, {
19099
+ includeSourcePaths: false
19100
+ });
19090
19101
  return {
19091
19102
  path: target,
19092
19103
  kind: "generated",
19093
19104
  entries: asGenerated.map((item) => item.entry),
19094
- loweringOutcomes: explainLoweringOutcomes(target, asGenerated, loweringOutcomes, {
19095
- includeSourcePaths: false
19096
- }),
19105
+ features: featureCapabilitiesForPath(graph, target, asGenerated, matchedLoweringOutcomes),
19106
+ loweringOutcomes: matchedLoweringOutcomes,
19097
19107
  notes: [`Generated output; rebuild with skillset build, verify with skillset check.`]
19098
19108
  };
19099
19109
  }
19100
19110
  const prefixMatch = items.filter((item) => item.sourcePath.startsWith(`${target}/`));
19101
19111
  if (prefixMatch.length > 0) {
19112
+ const matchedLoweringOutcomes = explainLoweringOutcomes(target, prefixMatch, loweringOutcomes);
19102
19113
  return {
19103
19114
  path: target,
19104
19115
  kind: "source-plugin",
19105
19116
  entries: prefixMatch.map((item) => item.entry),
19106
- loweringOutcomes: explainLoweringOutcomes(target, prefixMatch, loweringOutcomes),
19117
+ features: featureCapabilitiesForPath(graph, target, prefixMatch, matchedLoweringOutcomes),
19118
+ loweringOutcomes: matchedLoweringOutcomes,
19107
19119
  notes: [`Matched ${prefixMatch.length} generated entries under this source path.`]
19108
19120
  };
19109
19121
  }
@@ -19113,6 +19125,7 @@ async function explainPath(rootPath, inputPath, options = {}) {
19113
19125
  path: target,
19114
19126
  kind: explainSourceKind(graph, target),
19115
19127
  entries: [],
19128
+ features: featureCapabilitiesForPath(graph, target, [], sourceOnlyOutcomes),
19116
19129
  loweringOutcomes: sourceOnlyOutcomes,
19117
19130
  notes: [`Matched ${sourceOnlyOutcomes.length} lowering outcome(s) under this source path.`]
19118
19131
  };
@@ -19121,6 +19134,7 @@ async function explainPath(rootPath, inputPath, options = {}) {
19121
19134
  path: target,
19122
19135
  kind: "unknown",
19123
19136
  entries: [],
19137
+ features: [],
19124
19138
  loweringOutcomes: [],
19125
19139
  notes: [
19126
19140
  `No lock entry references ${target}. Pass a source path under ${graph.sourceDir}/ or a generated output path.`
@@ -19132,6 +19146,21 @@ async function listGeneratedEntries(rootPath, options = {}) {
19132
19146
  const rendered = scopedRenderedFiles(graph, await renderBuildGraph(graph), options.scopes);
19133
19147
  return collectLockItems(rendered).map((item) => item.entry);
19134
19148
  }
19149
+ function listFeatureCapabilities(featureId) {
19150
+ const features = featureId === undefined ? listSkillsetFeatures() : [getSkillsetFeature(featureId)].filter((feature2) => feature2 !== undefined);
19151
+ return features.map(featureCapability);
19152
+ }
19153
+ function summarizeFeatureCapabilities(features = listFeatureCapabilities()) {
19154
+ return {
19155
+ byFeatureStatus: countBy(features.map((feature2) => feature2.status)),
19156
+ byTargetSupport: {
19157
+ claude: countBy(features.map((feature2) => feature2.targetSupport.claude.status)),
19158
+ codex: countBy(features.map((feature2) => feature2.targetSupport.codex.status))
19159
+ },
19160
+ featureIds: features.map((feature2) => feature2.id).sort(compareStrings),
19161
+ total: features.length
19162
+ };
19163
+ }
19135
19164
  async function doctorSkillset(rootPath, options = {}) {
19136
19165
  let graph;
19137
19166
  try {
@@ -19143,6 +19172,7 @@ async function doctorSkillset(rootPath, options = {}) {
19143
19172
  buildError: errorMessage3(error),
19144
19173
  buildDiagnostics: buildDiagnostics2,
19145
19174
  drift: { added: [], changed: [], missing: [], removed: [] },
19175
+ featureCapabilities: summarizeFeatureCapabilities(),
19146
19176
  lintIssues: [],
19147
19177
  loweringOutcomes: loweringOutcomes2,
19148
19178
  notableLoweringOutcomes: notableLoweringOutcomes(loweringOutcomes2),
@@ -19170,6 +19200,7 @@ async function doctorSkillset(rootPath, options = {}) {
19170
19200
  ...buildError === undefined ? {} : { buildError },
19171
19201
  buildDiagnostics,
19172
19202
  drift,
19203
+ featureCapabilities: summarizeFeatureCapabilities(),
19173
19204
  lintIssues: lint.issues,
19174
19205
  loweringOutcomes,
19175
19206
  notableLoweringOutcomes: notable,
@@ -19230,7 +19261,7 @@ function collectLockItems(rendered) {
19230
19261
  continue;
19231
19262
  let parsed;
19232
19263
  try {
19233
- parsed = JSON.parse(textDecoder4.decode(file.content));
19264
+ parsed = JSON.parse(textDecoder5.decode(file.content));
19234
19265
  } catch {
19235
19266
  continue;
19236
19267
  }
@@ -19331,6 +19362,82 @@ function sourceNotes(graph, target) {
19331
19362
  }
19332
19363
  return notes;
19333
19364
  }
19365
+ function featureCapabilitiesForPath(graph, target, items, outcomes) {
19366
+ const featureIds = new Set;
19367
+ for (const item of items) {
19368
+ if (item.entry.feature !== undefined)
19369
+ featureIds.add(item.entry.feature);
19370
+ }
19371
+ for (const outcome of outcomes) {
19372
+ featureIds.add(outcome.featureId);
19373
+ }
19374
+ for (const featureId of inferredFeatureIdsForSourcePath(graph, target)) {
19375
+ featureIds.add(featureId);
19376
+ }
19377
+ return [...featureIds].map((featureId) => getSkillsetFeature(featureId)).filter((feature2) => feature2 !== undefined).map(featureCapability).sort((left, right) => compareStrings(left.id, right.id));
19378
+ }
19379
+ function inferredFeatureIdsForSourcePath(graph, target) {
19380
+ const featureIds = [];
19381
+ if (graph.rules.some((rule) => relative15(graph.rootPath, rule.sourcePath) === target)) {
19382
+ featureIds.push("project-instructions");
19383
+ }
19384
+ if (graph.projectAgents.some((agent) => relative15(graph.rootPath, agent.sourcePath) === target)) {
19385
+ featureIds.push("project-agents");
19386
+ }
19387
+ if (graph.projectIslands.some((island) => pathMatchesSource(graph, target, island.sourcePath))) {
19388
+ featureIds.push("target-native-islands");
19389
+ }
19390
+ for (const skill of graph.standaloneSkills) {
19391
+ if (!pathMatchesSource(graph, target, skill.sourcePath))
19392
+ continue;
19393
+ featureIds.push("standalone-skills");
19394
+ if (skill.resources.length > 0)
19395
+ featureIds.push("resources");
19396
+ }
19397
+ for (const plugin of graph.plugins) {
19398
+ if (pathMatchesSource(graph, target, plugin.configPath)) {
19399
+ featureIds.push("plugin-manifests");
19400
+ }
19401
+ for (const skill of plugin.skills) {
19402
+ if (!pathMatchesSource(graph, target, skill.sourcePath))
19403
+ continue;
19404
+ featureIds.push("plugin-skills");
19405
+ if (skill.resources.length > 0)
19406
+ featureIds.push("resources");
19407
+ }
19408
+ }
19409
+ return featureIds;
19410
+ }
19411
+ function pathMatchesSource(graph, target, sourcePath) {
19412
+ const relativePath2 = relative15(graph.rootPath, sourcePath).replaceAll("\\", "/");
19413
+ return target === relativePath2 || target.startsWith(`${relativePath2}/`);
19414
+ }
19415
+ function featureCapability(feature2) {
19416
+ return {
19417
+ docs: feature2.docs,
19418
+ id: feature2.id,
19419
+ status: feature2.status,
19420
+ targetSupport: {
19421
+ claude: supportCapability(feature2.targetSupport.claude),
19422
+ codex: supportCapability(feature2.targetSupport.codex)
19423
+ },
19424
+ title: feature2.title
19425
+ };
19426
+ }
19427
+ function supportCapability(support) {
19428
+ return {
19429
+ ...support.note === undefined ? {} : { note: support.note },
19430
+ ...support.reason === undefined ? {} : { reason: support.reason },
19431
+ status: support.status
19432
+ };
19433
+ }
19434
+ function countBy(values) {
19435
+ const counts = {};
19436
+ for (const value of [...values].sort(compareStrings)) {
19437
+ counts[value] = (counts[value] ?? 0) + 1;
19438
+ }
19439
+ return counts;
19440
+ }
19334
19441
  function normalizeRepoPath(rootPath, inputPath) {
19335
19442
  const absolute = resolve8(rootPath, inputPath);
19336
19443
  return relative15(rootPath, absolute).replaceAll("\\", "/");
@@ -19372,14 +19479,14 @@ async function resolveSkillsetCommand(rootPath = process.cwd(), env = process.en
19372
19479
  if (await isLocalSkillsetCheckout(rootPath)) {
19373
19480
  return { argv: ["bun", "./apps/skillset/src/cli.ts"], kind: "argv" };
19374
19481
  }
19375
- if (await commandExists("skillset", rootPath))
19482
+ if (await commandExists("skillset", rootPath, env))
19376
19483
  return { argv: ["skillset"], kind: "argv" };
19377
- if (await commandExists("bunx", rootPath))
19378
- return { argv: ["bunx", "skillset@beta"], kind: "argv" };
19379
- if (await commandExists("bun", rootPath))
19380
- return { argv: ["bun", "x", "skillset@beta"], kind: "argv" };
19381
- if (await commandExists("npx", rootPath))
19382
- return { argv: ["npx", "--yes", "skillset@beta"], kind: "argv" };
19484
+ if (await commandExists("bunx", rootPath, env))
19485
+ return { argv: ["bunx", "skillset"], kind: "argv" };
19486
+ if (await commandExists("bun", rootPath, env))
19487
+ return { argv: ["bun", "x", "skillset"], kind: "argv" };
19488
+ if (await commandExists("npx", rootPath, env))
19489
+ return { argv: ["npx", "--yes", "skillset"], kind: "argv" };
19383
19490
  throw new Error("skillset: could not find a Skillset CLI runner; install skillset or set SKILLSET_HOOK_COMMAND");
19384
19491
  }
19385
19492
  async function runSkillsetCommand(args, options) {
@@ -19410,8 +19517,11 @@ async function runShell(command, args, options) {
19410
19517
  });
19411
19518
  return proc.exited;
19412
19519
  }
19413
- async function commandExists(command, cwd) {
19414
- const result = await capture(["sh", "-lc", `command -v ${shellQuote(command)} >/dev/null 2>&1`], { cwd });
19520
+ async function commandExists(command, cwd, env) {
19521
+ const result = await capture(["/bin/sh", "-lc", `command -v ${shellQuote(command)} >/dev/null 2>&1`], {
19522
+ cwd,
19523
+ env
19524
+ });
19415
19525
  return result.exitCode === 0;
19416
19526
  }
19417
19527
  async function isLocalSkillsetCheckout(rootPath) {
@@ -19430,7 +19540,7 @@ async function capture(argv, options) {
19430
19540
  const proc = Bun.spawn({
19431
19541
  cmd: [...argv],
19432
19542
  cwd: options.cwd,
19433
- env: gitSafeEnv(),
19543
+ env: gitSafeEnv(options.env),
19434
19544
  stderr: "ignore",
19435
19545
  stdout: "ignore"
19436
19546
  });
@@ -20632,6 +20742,7 @@ var USAGE = [
20632
20742
  " skillset release apply [--yes|--dry-run] [--root <path>] [--source <dir>] [--dist <dir>]",
20633
20743
  " skillset restore <backup-id> [--yes|--dry-run] [--root <path>]",
20634
20744
  " skillset distribute plan [name] [--root <path>] [--source <dir>] [--dist <dir>]",
20745
+ " skillset features [feature-id] [--json]",
20635
20746
  " skillset test [name] [--root <path>] [--source <dir>]",
20636
20747
  " skillset hooks print --runner <lefthook|husky|pre-commit|git> [--pre-commit] [--pre-push]",
20637
20748
  " skillset hooks print --target <claude|codex> --agent-runtime",
@@ -20965,6 +21076,25 @@ async function runCli(rawArgs = process.argv.slice(2), invokedName = basename8(p
20965
21076
  console.log(`skillset: listed ${entries.length} generated entries`);
20966
21077
  return;
20967
21078
  }
21079
+ if (command === "features") {
21080
+ const features = listFeatureCapabilities(importPath);
21081
+ if (jsonOutput) {
21082
+ process.stdout.write(renderValidatedJson({ features }, "skillset features"));
21083
+ if (importPath !== undefined && features.length === 0)
21084
+ process.exitCode = 1;
21085
+ return;
21086
+ }
21087
+ if (features.length === 0) {
21088
+ console.log(`skillset: feature ${importPath ?? ""} not found`);
21089
+ process.exitCode = 1;
21090
+ return;
21091
+ }
21092
+ for (const feature2 of features) {
21093
+ printFeatureCapability(feature2);
21094
+ }
21095
+ console.log(`skillset: listed ${features.length} feature${features.length === 1 ? "" : "s"}`);
21096
+ return;
21097
+ }
20968
21098
  if (command === "explain") {
20969
21099
  if (importPath === undefined) {
20970
21100
  throw new Error("skillset: expected a path to explain");
@@ -21007,6 +21137,11 @@ async function runCli(rawArgs = process.argv.slice(2), invokedName = basename8(p
21007
21137
  if (entry.outputHash !== undefined)
21008
21138
  console.log(` output hash: ${entry.outputHash}`);
21009
21139
  }
21140
+ for (const feature2 of result3.features) {
21141
+ console.log(` feature ${feature2.id}: ${feature2.title}`);
21142
+ console.log(` claude: ${feature2.targetSupport.claude.status}`);
21143
+ console.log(` codex: ${feature2.targetSupport.codex.status}`);
21144
+ }
21010
21145
  for (const outcome of result3.loweringOutcomes) {
21011
21146
  printLoweringOutcome(outcome);
21012
21147
  }
@@ -21036,6 +21171,9 @@ async function runCli(rawArgs = process.argv.slice(2), invokedName = basename8(p
21036
21171
  if (driftCount > 0) {
21037
21172
  console.log(` drift: ${added.length} added, ${changed.length} changed, ${missing.length} missing, ${removed.length} removed (run skillset build --yes)`);
21038
21173
  }
21174
+ console.log(` features: ${report.featureCapabilities.total} registry entries; status ${formatCountSummary(report.featureCapabilities.byFeatureStatus)}`);
21175
+ console.log(` feature support: claude ${formatCountSummary(report.featureCapabilities.byTargetSupport.claude)}`);
21176
+ console.log(` feature support: codex ${formatCountSummary(report.featureCapabilities.byTargetSupport.codex)}`);
21039
21177
  for (const outcome of report.notableLoweringOutcomes) {
21040
21178
  printLoweringOutcome(outcome);
21041
21179
  }
@@ -21395,6 +21533,22 @@ function printSkillsetTest(report) {
21395
21533
  if (report.activationPath !== undefined)
21396
21534
  console.log(` activation: ${report.activationPath}`);
21397
21535
  }
21536
+ function printFeatureCapability(feature2) {
21537
+ console.log(`feature ${feature2.id}: ${feature2.title}`);
21538
+ console.log(` status: ${feature2.status}`);
21539
+ console.log(` claude: ${formatFeatureSupport(feature2.targetSupport.claude)}`);
21540
+ console.log(` codex: ${formatFeatureSupport(feature2.targetSupport.codex)}`);
21541
+ if (feature2.docs.length > 0)
21542
+ console.log(` docs: ${feature2.docs.join(", ")}`);
21543
+ }
21544
+ function formatFeatureSupport(support) {
21545
+ const reason = support.reason === undefined ? "" : ` (${support.reason})`;
21546
+ const note = support.note === undefined ? "" : ` note: ${support.note}`;
21547
+ return `${support.status}${reason}${note}`;
21548
+ }
21549
+ function formatCountSummary(counts) {
21550
+ return Object.entries(counts).sort(([left], [right]) => left.localeCompare(right)).map(([key, count]) => `${key} ${count}`).join(", ");
21551
+ }
21398
21552
  function printLoweringOutcome(outcome) {
21399
21553
  const target = outcome.target ?? "workspace";
21400
21554
  const policy = outcome.policy === undefined ? "" : ` policy: ${outcome.policy}`;
@@ -21409,8 +21563,8 @@ function printLoweringOutcome(outcome) {
21409
21563
  }
21410
21564
  function parseArgs(args) {
21411
21565
  const command = args[0];
21412
- if (command !== "adopt" && command !== "build" && command !== "change" && command !== "check" && command !== "ci" && command !== "create" && command !== "diff" && command !== "distribute" && command !== "doctor" && command !== "explain" && command !== "hooks" && command !== "import" && command !== "init" && command !== "lint" && command !== "list" && command !== "release" && command !== "restore" && command !== "test") {
21413
- throw new Error(`skillset: expected command adopt, build, change, check, ci, create, diff, distribute, doctor, explain, hooks, import, init, lint, list, release, restore, or test
21566
+ if (command !== "adopt" && command !== "build" && command !== "change" && command !== "check" && command !== "ci" && command !== "create" && command !== "diff" && command !== "distribute" && command !== "doctor" && command !== "explain" && command !== "features" && command !== "hooks" && command !== "import" && command !== "init" && command !== "lint" && command !== "list" && command !== "release" && command !== "restore" && command !== "test") {
21567
+ throw new Error(`skillset: expected command adopt, build, change, check, ci, create, diff, distribute, doctor, explain, features, hooks, import, init, lint, list, release, restore, or test
21414
21568
  ` + USAGE);
21415
21569
  }
21416
21570
  let changeSubcommand;
@@ -21534,6 +21688,13 @@ function parseArgs(args) {
21534
21688
  importPath = rawPath;
21535
21689
  index += 1;
21536
21690
  }
21691
+ if (command === "features") {
21692
+ const rawFeatureId = args[index];
21693
+ if (rawFeatureId !== undefined && !rawFeatureId.startsWith("--")) {
21694
+ importPath = rawFeatureId;
21695
+ index += 1;
21696
+ }
21697
+ }
21537
21698
  if (command === "restore") {
21538
21699
  const rawBackupId = args[index];
21539
21700
  if (rawBackupId === undefined || rawBackupId.startsWith("--")) {
@@ -21989,9 +22150,9 @@ function validateAdoptFlags(command, adopt) {
21989
22150
  function validateJsonFlags(command, jsonOutput) {
21990
22151
  if (!jsonOutput)
21991
22152
  return;
21992
- if (command === "doctor" || command === "explain")
22153
+ if (command === "doctor" || command === "explain" || command === "features")
21993
22154
  return;
21994
- throw new Error("skillset: --json is only supported with doctor or explain");
22155
+ throw new Error("skillset: --json is only supported with doctor, explain, or features");
21995
22156
  }
21996
22157
  function isImportKind(value) {
21997
22158
  return value === "skill" || value === "skills" || value === "plugin" || value === "plugins";
package/dist/create.js CHANGED
@@ -7517,12 +7517,12 @@ var skillsetFeatureRegistry = defineFeatureRegistry([
7517
7517
  status: "externally_managed"
7518
7518
  },
7519
7519
  cursor: {
7520
- evidence: [docs("docs/features/runtime-adapters.md"), fixture("fixtures/external/repos/superpowers")],
7520
+ evidence: [docs("docs/features/runtime-adapters.md"), fixture("fixtures/external/repos.yaml")],
7521
7521
  reason: "Cursor support needs target documentation and adapter evidence before Skillset can lower or distribute it.",
7522
7522
  status: "planned"
7523
7523
  },
7524
7524
  "gemini-cli": {
7525
- evidence: [docs("docs/features/runtime-adapters.md"), fixture("fixtures/external/repos/superpowers")],
7525
+ evidence: [docs("docs/features/runtime-adapters.md"), fixture("fixtures/external/repos.yaml")],
7526
7526
  reason: "Gemini support needs target documentation and adapter evidence before Skillset can lower or distribute it.",
7527
7527
  status: "planned"
7528
7528
  },
@@ -7537,7 +7537,7 @@ var skillsetFeatureRegistry = defineFeatureRegistry([
7537
7537
  status: "future"
7538
7538
  },
7539
7539
  opencode: {
7540
- evidence: [docs("docs/features/runtime-adapters.md"), fixture("fixtures/external/repos/superpowers")],
7540
+ evidence: [docs("docs/features/runtime-adapters.md"), fixture("fixtures/external/repos.yaml")],
7541
7541
  reason: "OpenCode support needs target documentation and adapter evidence before Skillset can lower or distribute it.",
7542
7542
  status: "planned"
7543
7543
  }
@@ -7624,7 +7624,7 @@ var skillsetFeatureRegistry = defineFeatureRegistry([
7624
7624
  status: "implemented",
7625
7625
  summary: "Normalizes tool policy intent into Claude allowed-tools fields and Codex metadata sidecars.",
7626
7626
  targetSupport: {
7627
- claude: { status: "native" },
7627
+ claude: { status: "transformed" },
7628
7628
  codex: { status: "metadata_only" }
7629
7629
  },
7630
7630
  title: "Tool Intent",
@@ -7679,6 +7679,9 @@ function defineFeatureRegistry(entries) {
7679
7679
  assertRuntimeSupportVocabulary(entries);
7680
7680
  return [...entries].sort((left, right) => compareStrings(left.id, right.id));
7681
7681
  }
7682
+ function listSkillsetFeatures(registry = skillsetFeatureRegistry) {
7683
+ return registry;
7684
+ }
7682
7685
  function getSkillsetFeature(id, registry = skillsetFeatureRegistry) {
7683
7686
  return registry.find((entry) => entry.id === id);
7684
7687
  }
@@ -14447,6 +14450,9 @@ async function exists6(path) {
14447
14450
  throw error;
14448
14451
  }
14449
14452
  }
14453
+ // packages/core/src/host-leak.ts
14454
+ var textDecoder3 = new TextDecoder;
14455
+
14450
14456
  // packages/core/src/normalized-output-tree.ts
14451
14457
  var DEFAULT_STRUCTURED_JSON_BASENAMES = new Set([".skillset.lock"]);
14452
14458
 
@@ -14454,7 +14460,7 @@ var DEFAULT_STRUCTURED_JSON_BASENAMES = new Set([".skillset.lock"]);
14454
14460
  var textEncoder4 = new TextEncoder;
14455
14461
  // packages/core/src/destination-ownership.ts
14456
14462
  import { basename as basename3 } from "path";
14457
- var textDecoder3 = new TextDecoder;
14463
+ var textDecoder4 = new TextDecoder;
14458
14464
  function classifyDestinationOwnership(args) {
14459
14465
  const file = classifyFileOwnership(args.path, args.target);
14460
14466
  const fields = args.content === undefined ? [] : classifyFieldOwnership(args.path, args.target, args.content);
@@ -14493,7 +14499,7 @@ function classifyFieldOwnership(path, target, content) {
14493
14499
  return [];
14494
14500
  let record;
14495
14501
  try {
14496
- const parsed = JSON.parse(textDecoder3.decode(content));
14502
+ const parsed = JSON.parse(textDecoder4.decode(content));
14497
14503
  if (!isJsonRecord(parsed))
14498
14504
  return [];
14499
14505
  record = parsed;
@@ -17585,7 +17591,7 @@ var CI_WORKFLOW_PATH = ".github/workflows/skillset-ci.yml";
17585
17591
  function renderCiWorkflow() {
17586
17592
  return [
17587
17593
  "# Scaffolded by skillset init --include ci. Skillset does not manage this file; edit freely.",
17588
- "# Pin skillset to an exact version for reproducible CI runs.",
17594
+ "# Consider pinning skillset to an exact version for reproducible CI runs.",
17589
17595
  "# Notes:",
17590
17596
  "# - Mechanical fixes pushed with GITHUB_TOKEN do not retrigger workflows; if your",
17591
17597
  "# branch protection requires checks on the fixed commit, push with a PAT instead.",
@@ -17627,7 +17633,7 @@ function renderCiWorkflow() {
17627
17633
  " - name: Run skillset ci",
17628
17634
  " id: skillset",
17629
17635
  " run: >-",
17630
- " bunx skillset@beta ci",
17636
+ " bunx skillset ci",
17631
17637
  " ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && '--fix' || '' }}",
17632
17638
  ' --report "$RUNNER_TEMP/skillset-ci-report.md"',
17633
17639
  " continue-on-error: true",
@@ -19064,7 +19070,7 @@ async function exists12(path2) {
19064
19070
 
19065
19071
  // apps/skillset/src/authoring.ts
19066
19072
  import { resolve as resolve8, relative as relative15 } from "path";
19067
- var textDecoder4 = new TextDecoder;
19073
+ var textDecoder5 = new TextDecoder;
19068
19074
  async function explainPath(rootPath, inputPath, options = {}) {
19069
19075
  const graph = await loadBuildGraph(rootPath, options);
19070
19076
  const allRendered = await renderBuildGraph(graph);
@@ -19077,33 +19083,39 @@ async function explainPath(rootPath, inputPath, options = {}) {
19077
19083
  const items = collectLockItems(rendered);
19078
19084
  const asSource = items.filter((item) => item.sourcePath === target);
19079
19085
  if (asSource.length > 0) {
19086
+ const matchedLoweringOutcomes = explainLoweringOutcomes(target, asSource, loweringOutcomes);
19080
19087
  return {
19081
19088
  path: target,
19082
19089
  kind: explainSourceKind(graph, target),
19083
19090
  entries: asSource.map((item) => item.entry),
19084
- loweringOutcomes: explainLoweringOutcomes(target, asSource, loweringOutcomes),
19091
+ features: featureCapabilitiesForPath(graph, target, asSource, matchedLoweringOutcomes),
19092
+ loweringOutcomes: matchedLoweringOutcomes,
19085
19093
  notes: sourceNotes(graph, target)
19086
19094
  };
19087
19095
  }
19088
19096
  const asGenerated = items.filter((item) => item.outputPath === target || item.files.some((file) => joinOutputRoot2(item.outputRoot, file) === target));
19089
19097
  if (asGenerated.length > 0) {
19098
+ const matchedLoweringOutcomes = explainLoweringOutcomes(target, asGenerated, loweringOutcomes, {
19099
+ includeSourcePaths: false
19100
+ });
19090
19101
  return {
19091
19102
  path: target,
19092
19103
  kind: "generated",
19093
19104
  entries: asGenerated.map((item) => item.entry),
19094
- loweringOutcomes: explainLoweringOutcomes(target, asGenerated, loweringOutcomes, {
19095
- includeSourcePaths: false
19096
- }),
19105
+ features: featureCapabilitiesForPath(graph, target, asGenerated, matchedLoweringOutcomes),
19106
+ loweringOutcomes: matchedLoweringOutcomes,
19097
19107
  notes: [`Generated output; rebuild with skillset build, verify with skillset check.`]
19098
19108
  };
19099
19109
  }
19100
19110
  const prefixMatch = items.filter((item) => item.sourcePath.startsWith(`${target}/`));
19101
19111
  if (prefixMatch.length > 0) {
19112
+ const matchedLoweringOutcomes = explainLoweringOutcomes(target, prefixMatch, loweringOutcomes);
19102
19113
  return {
19103
19114
  path: target,
19104
19115
  kind: "source-plugin",
19105
19116
  entries: prefixMatch.map((item) => item.entry),
19106
- loweringOutcomes: explainLoweringOutcomes(target, prefixMatch, loweringOutcomes),
19117
+ features: featureCapabilitiesForPath(graph, target, prefixMatch, matchedLoweringOutcomes),
19118
+ loweringOutcomes: matchedLoweringOutcomes,
19107
19119
  notes: [`Matched ${prefixMatch.length} generated entries under this source path.`]
19108
19120
  };
19109
19121
  }
@@ -19113,6 +19125,7 @@ async function explainPath(rootPath, inputPath, options = {}) {
19113
19125
  path: target,
19114
19126
  kind: explainSourceKind(graph, target),
19115
19127
  entries: [],
19128
+ features: featureCapabilitiesForPath(graph, target, [], sourceOnlyOutcomes),
19116
19129
  loweringOutcomes: sourceOnlyOutcomes,
19117
19130
  notes: [`Matched ${sourceOnlyOutcomes.length} lowering outcome(s) under this source path.`]
19118
19131
  };
@@ -19121,6 +19134,7 @@ async function explainPath(rootPath, inputPath, options = {}) {
19121
19134
  path: target,
19122
19135
  kind: "unknown",
19123
19136
  entries: [],
19137
+ features: [],
19124
19138
  loweringOutcomes: [],
19125
19139
  notes: [
19126
19140
  `No lock entry references ${target}. Pass a source path under ${graph.sourceDir}/ or a generated output path.`
@@ -19132,6 +19146,21 @@ async function listGeneratedEntries(rootPath, options = {}) {
19132
19146
  const rendered = scopedRenderedFiles(graph, await renderBuildGraph(graph), options.scopes);
19133
19147
  return collectLockItems(rendered).map((item) => item.entry);
19134
19148
  }
19149
+ function listFeatureCapabilities(featureId) {
19150
+ const features = featureId === undefined ? listSkillsetFeatures() : [getSkillsetFeature(featureId)].filter((feature2) => feature2 !== undefined);
19151
+ return features.map(featureCapability);
19152
+ }
19153
+ function summarizeFeatureCapabilities(features = listFeatureCapabilities()) {
19154
+ return {
19155
+ byFeatureStatus: countBy(features.map((feature2) => feature2.status)),
19156
+ byTargetSupport: {
19157
+ claude: countBy(features.map((feature2) => feature2.targetSupport.claude.status)),
19158
+ codex: countBy(features.map((feature2) => feature2.targetSupport.codex.status))
19159
+ },
19160
+ featureIds: features.map((feature2) => feature2.id).sort(compareStrings),
19161
+ total: features.length
19162
+ };
19163
+ }
19135
19164
  async function doctorSkillset(rootPath, options = {}) {
19136
19165
  let graph;
19137
19166
  try {
@@ -19143,6 +19172,7 @@ async function doctorSkillset(rootPath, options = {}) {
19143
19172
  buildError: errorMessage3(error),
19144
19173
  buildDiagnostics: buildDiagnostics2,
19145
19174
  drift: { added: [], changed: [], missing: [], removed: [] },
19175
+ featureCapabilities: summarizeFeatureCapabilities(),
19146
19176
  lintIssues: [],
19147
19177
  loweringOutcomes: loweringOutcomes2,
19148
19178
  notableLoweringOutcomes: notableLoweringOutcomes(loweringOutcomes2),
@@ -19170,6 +19200,7 @@ async function doctorSkillset(rootPath, options = {}) {
19170
19200
  ...buildError === undefined ? {} : { buildError },
19171
19201
  buildDiagnostics,
19172
19202
  drift,
19203
+ featureCapabilities: summarizeFeatureCapabilities(),
19173
19204
  lintIssues: lint.issues,
19174
19205
  loweringOutcomes,
19175
19206
  notableLoweringOutcomes: notable,
@@ -19230,7 +19261,7 @@ function collectLockItems(rendered) {
19230
19261
  continue;
19231
19262
  let parsed;
19232
19263
  try {
19233
- parsed = JSON.parse(textDecoder4.decode(file.content));
19264
+ parsed = JSON.parse(textDecoder5.decode(file.content));
19234
19265
  } catch {
19235
19266
  continue;
19236
19267
  }
@@ -19331,6 +19362,82 @@ function sourceNotes(graph, target) {
19331
19362
  }
19332
19363
  return notes;
19333
19364
  }
19365
+ function featureCapabilitiesForPath(graph, target, items, outcomes) {
19366
+ const featureIds = new Set;
19367
+ for (const item of items) {
19368
+ if (item.entry.feature !== undefined)
19369
+ featureIds.add(item.entry.feature);
19370
+ }
19371
+ for (const outcome of outcomes) {
19372
+ featureIds.add(outcome.featureId);
19373
+ }
19374
+ for (const featureId of inferredFeatureIdsForSourcePath(graph, target)) {
19375
+ featureIds.add(featureId);
19376
+ }
19377
+ return [...featureIds].map((featureId) => getSkillsetFeature(featureId)).filter((feature2) => feature2 !== undefined).map(featureCapability).sort((left, right) => compareStrings(left.id, right.id));
19378
+ }
19379
+ function inferredFeatureIdsForSourcePath(graph, target) {
19380
+ const featureIds = [];
19381
+ if (graph.rules.some((rule) => relative15(graph.rootPath, rule.sourcePath) === target)) {
19382
+ featureIds.push("project-instructions");
19383
+ }
19384
+ if (graph.projectAgents.some((agent) => relative15(graph.rootPath, agent.sourcePath) === target)) {
19385
+ featureIds.push("project-agents");
19386
+ }
19387
+ if (graph.projectIslands.some((island) => pathMatchesSource(graph, target, island.sourcePath))) {
19388
+ featureIds.push("target-native-islands");
19389
+ }
19390
+ for (const skill of graph.standaloneSkills) {
19391
+ if (!pathMatchesSource(graph, target, skill.sourcePath))
19392
+ continue;
19393
+ featureIds.push("standalone-skills");
19394
+ if (skill.resources.length > 0)
19395
+ featureIds.push("resources");
19396
+ }
19397
+ for (const plugin of graph.plugins) {
19398
+ if (pathMatchesSource(graph, target, plugin.configPath)) {
19399
+ featureIds.push("plugin-manifests");
19400
+ }
19401
+ for (const skill of plugin.skills) {
19402
+ if (!pathMatchesSource(graph, target, skill.sourcePath))
19403
+ continue;
19404
+ featureIds.push("plugin-skills");
19405
+ if (skill.resources.length > 0)
19406
+ featureIds.push("resources");
19407
+ }
19408
+ }
19409
+ return featureIds;
19410
+ }
19411
+ function pathMatchesSource(graph, target, sourcePath) {
19412
+ const relativePath2 = relative15(graph.rootPath, sourcePath).replaceAll("\\", "/");
19413
+ return target === relativePath2 || target.startsWith(`${relativePath2}/`);
19414
+ }
19415
+ function featureCapability(feature2) {
19416
+ return {
19417
+ docs: feature2.docs,
19418
+ id: feature2.id,
19419
+ status: feature2.status,
19420
+ targetSupport: {
19421
+ claude: supportCapability(feature2.targetSupport.claude),
19422
+ codex: supportCapability(feature2.targetSupport.codex)
19423
+ },
19424
+ title: feature2.title
19425
+ };
19426
+ }
19427
+ function supportCapability(support) {
19428
+ return {
19429
+ ...support.note === undefined ? {} : { note: support.note },
19430
+ ...support.reason === undefined ? {} : { reason: support.reason },
19431
+ status: support.status
19432
+ };
19433
+ }
19434
+ function countBy(values) {
19435
+ const counts = {};
19436
+ for (const value of [...values].sort(compareStrings)) {
19437
+ counts[value] = (counts[value] ?? 0) + 1;
19438
+ }
19439
+ return counts;
19440
+ }
19334
19441
  function normalizeRepoPath(rootPath, inputPath) {
19335
19442
  const absolute = resolve8(rootPath, inputPath);
19336
19443
  return relative15(rootPath, absolute).replaceAll("\\", "/");
@@ -19372,14 +19479,14 @@ async function resolveSkillsetCommand(rootPath = process.cwd(), env = process.en
19372
19479
  if (await isLocalSkillsetCheckout(rootPath)) {
19373
19480
  return { argv: ["bun", "./apps/skillset/src/cli.ts"], kind: "argv" };
19374
19481
  }
19375
- if (await commandExists("skillset", rootPath))
19482
+ if (await commandExists("skillset", rootPath, env))
19376
19483
  return { argv: ["skillset"], kind: "argv" };
19377
- if (await commandExists("bunx", rootPath))
19378
- return { argv: ["bunx", "skillset@beta"], kind: "argv" };
19379
- if (await commandExists("bun", rootPath))
19380
- return { argv: ["bun", "x", "skillset@beta"], kind: "argv" };
19381
- if (await commandExists("npx", rootPath))
19382
- return { argv: ["npx", "--yes", "skillset@beta"], kind: "argv" };
19484
+ if (await commandExists("bunx", rootPath, env))
19485
+ return { argv: ["bunx", "skillset"], kind: "argv" };
19486
+ if (await commandExists("bun", rootPath, env))
19487
+ return { argv: ["bun", "x", "skillset"], kind: "argv" };
19488
+ if (await commandExists("npx", rootPath, env))
19489
+ return { argv: ["npx", "--yes", "skillset"], kind: "argv" };
19383
19490
  throw new Error("skillset: could not find a Skillset CLI runner; install skillset or set SKILLSET_HOOK_COMMAND");
19384
19491
  }
19385
19492
  async function runSkillsetCommand(args, options) {
@@ -19410,8 +19517,11 @@ async function runShell(command, args, options) {
19410
19517
  });
19411
19518
  return proc.exited;
19412
19519
  }
19413
- async function commandExists(command, cwd) {
19414
- const result = await capture(["sh", "-lc", `command -v ${shellQuote(command)} >/dev/null 2>&1`], { cwd });
19520
+ async function commandExists(command, cwd, env) {
19521
+ const result = await capture(["/bin/sh", "-lc", `command -v ${shellQuote(command)} >/dev/null 2>&1`], {
19522
+ cwd,
19523
+ env
19524
+ });
19415
19525
  return result.exitCode === 0;
19416
19526
  }
19417
19527
  async function isLocalSkillsetCheckout(rootPath) {
@@ -19430,7 +19540,7 @@ async function capture(argv, options) {
19430
19540
  const proc = Bun.spawn({
19431
19541
  cmd: [...argv],
19432
19542
  cwd: options.cwd,
19433
- env: gitSafeEnv(),
19543
+ env: gitSafeEnv(options.env),
19434
19544
  stderr: "ignore",
19435
19545
  stdout: "ignore"
19436
19546
  });
@@ -20632,6 +20742,7 @@ var USAGE = [
20632
20742
  " skillset release apply [--yes|--dry-run] [--root <path>] [--source <dir>] [--dist <dir>]",
20633
20743
  " skillset restore <backup-id> [--yes|--dry-run] [--root <path>]",
20634
20744
  " skillset distribute plan [name] [--root <path>] [--source <dir>] [--dist <dir>]",
20745
+ " skillset features [feature-id] [--json]",
20635
20746
  " skillset test [name] [--root <path>] [--source <dir>]",
20636
20747
  " skillset hooks print --runner <lefthook|husky|pre-commit|git> [--pre-commit] [--pre-push]",
20637
20748
  " skillset hooks print --target <claude|codex> --agent-runtime",
@@ -20965,6 +21076,25 @@ async function runCli(rawArgs = process.argv.slice(2), invokedName = basename8(p
20965
21076
  console.log(`skillset: listed ${entries.length} generated entries`);
20966
21077
  return;
20967
21078
  }
21079
+ if (command === "features") {
21080
+ const features = listFeatureCapabilities(importPath);
21081
+ if (jsonOutput) {
21082
+ process.stdout.write(renderValidatedJson({ features }, "skillset features"));
21083
+ if (importPath !== undefined && features.length === 0)
21084
+ process.exitCode = 1;
21085
+ return;
21086
+ }
21087
+ if (features.length === 0) {
21088
+ console.log(`skillset: feature ${importPath ?? ""} not found`);
21089
+ process.exitCode = 1;
21090
+ return;
21091
+ }
21092
+ for (const feature2 of features) {
21093
+ printFeatureCapability(feature2);
21094
+ }
21095
+ console.log(`skillset: listed ${features.length} feature${features.length === 1 ? "" : "s"}`);
21096
+ return;
21097
+ }
20968
21098
  if (command === "explain") {
20969
21099
  if (importPath === undefined) {
20970
21100
  throw new Error("skillset: expected a path to explain");
@@ -21007,6 +21137,11 @@ async function runCli(rawArgs = process.argv.slice(2), invokedName = basename8(p
21007
21137
  if (entry.outputHash !== undefined)
21008
21138
  console.log(` output hash: ${entry.outputHash}`);
21009
21139
  }
21140
+ for (const feature2 of result3.features) {
21141
+ console.log(` feature ${feature2.id}: ${feature2.title}`);
21142
+ console.log(` claude: ${feature2.targetSupport.claude.status}`);
21143
+ console.log(` codex: ${feature2.targetSupport.codex.status}`);
21144
+ }
21010
21145
  for (const outcome of result3.loweringOutcomes) {
21011
21146
  printLoweringOutcome(outcome);
21012
21147
  }
@@ -21036,6 +21171,9 @@ async function runCli(rawArgs = process.argv.slice(2), invokedName = basename8(p
21036
21171
  if (driftCount > 0) {
21037
21172
  console.log(` drift: ${added.length} added, ${changed.length} changed, ${missing.length} missing, ${removed.length} removed (run skillset build --yes)`);
21038
21173
  }
21174
+ console.log(` features: ${report.featureCapabilities.total} registry entries; status ${formatCountSummary(report.featureCapabilities.byFeatureStatus)}`);
21175
+ console.log(` feature support: claude ${formatCountSummary(report.featureCapabilities.byTargetSupport.claude)}`);
21176
+ console.log(` feature support: codex ${formatCountSummary(report.featureCapabilities.byTargetSupport.codex)}`);
21039
21177
  for (const outcome of report.notableLoweringOutcomes) {
21040
21178
  printLoweringOutcome(outcome);
21041
21179
  }
@@ -21395,6 +21533,22 @@ function printSkillsetTest(report) {
21395
21533
  if (report.activationPath !== undefined)
21396
21534
  console.log(` activation: ${report.activationPath}`);
21397
21535
  }
21536
+ function printFeatureCapability(feature2) {
21537
+ console.log(`feature ${feature2.id}: ${feature2.title}`);
21538
+ console.log(` status: ${feature2.status}`);
21539
+ console.log(` claude: ${formatFeatureSupport(feature2.targetSupport.claude)}`);
21540
+ console.log(` codex: ${formatFeatureSupport(feature2.targetSupport.codex)}`);
21541
+ if (feature2.docs.length > 0)
21542
+ console.log(` docs: ${feature2.docs.join(", ")}`);
21543
+ }
21544
+ function formatFeatureSupport(support) {
21545
+ const reason = support.reason === undefined ? "" : ` (${support.reason})`;
21546
+ const note = support.note === undefined ? "" : ` note: ${support.note}`;
21547
+ return `${support.status}${reason}${note}`;
21548
+ }
21549
+ function formatCountSummary(counts) {
21550
+ return Object.entries(counts).sort(([left], [right]) => left.localeCompare(right)).map(([key, count]) => `${key} ${count}`).join(", ");
21551
+ }
21398
21552
  function printLoweringOutcome(outcome) {
21399
21553
  const target = outcome.target ?? "workspace";
21400
21554
  const policy = outcome.policy === undefined ? "" : ` policy: ${outcome.policy}`;
@@ -21409,8 +21563,8 @@ function printLoweringOutcome(outcome) {
21409
21563
  }
21410
21564
  function parseArgs(args) {
21411
21565
  const command = args[0];
21412
- if (command !== "adopt" && command !== "build" && command !== "change" && command !== "check" && command !== "ci" && command !== "create" && command !== "diff" && command !== "distribute" && command !== "doctor" && command !== "explain" && command !== "hooks" && command !== "import" && command !== "init" && command !== "lint" && command !== "list" && command !== "release" && command !== "restore" && command !== "test") {
21413
- throw new Error(`skillset: expected command adopt, build, change, check, ci, create, diff, distribute, doctor, explain, hooks, import, init, lint, list, release, restore, or test
21566
+ if (command !== "adopt" && command !== "build" && command !== "change" && command !== "check" && command !== "ci" && command !== "create" && command !== "diff" && command !== "distribute" && command !== "doctor" && command !== "explain" && command !== "features" && command !== "hooks" && command !== "import" && command !== "init" && command !== "lint" && command !== "list" && command !== "release" && command !== "restore" && command !== "test") {
21567
+ throw new Error(`skillset: expected command adopt, build, change, check, ci, create, diff, distribute, doctor, explain, features, hooks, import, init, lint, list, release, restore, or test
21414
21568
  ` + USAGE);
21415
21569
  }
21416
21570
  let changeSubcommand;
@@ -21534,6 +21688,13 @@ function parseArgs(args) {
21534
21688
  importPath = rawPath;
21535
21689
  index += 1;
21536
21690
  }
21691
+ if (command === "features") {
21692
+ const rawFeatureId = args[index];
21693
+ if (rawFeatureId !== undefined && !rawFeatureId.startsWith("--")) {
21694
+ importPath = rawFeatureId;
21695
+ index += 1;
21696
+ }
21697
+ }
21537
21698
  if (command === "restore") {
21538
21699
  const rawBackupId = args[index];
21539
21700
  if (rawBackupId === undefined || rawBackupId.startsWith("--")) {
@@ -21989,9 +22150,9 @@ function validateAdoptFlags(command, adopt) {
21989
22150
  function validateJsonFlags(command, jsonOutput) {
21990
22151
  if (!jsonOutput)
21991
22152
  return;
21992
- if (command === "doctor" || command === "explain")
22153
+ if (command === "doctor" || command === "explain" || command === "features")
21993
22154
  return;
21994
- throw new Error("skillset: --json is only supported with doctor or explain");
22155
+ throw new Error("skillset: --json is only supported with doctor, explain, or features");
21995
22156
  }
21996
22157
  function isImportKind(value) {
21997
22158
  return value === "skill" || value === "skills" || value === "plugin" || value === "plugins";
package/package.json CHANGED
@@ -1,14 +1,13 @@
1
1
  {
2
2
  "name": "skillset",
3
- "version": "0.1.0-beta.9",
3
+ "version": "0.13.1",
4
4
  "description": "Source-first compiler for Claude and Codex agent loadouts.",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist/*.js"
8
8
  ],
9
9
  "publishConfig": {
10
- "access": "public",
11
- "tag": "beta"
10
+ "access": "public"
12
11
  },
13
12
  "repository": {
14
13
  "type": "git",