skillset 0.1.0-beta.9 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +175 -17
  2. package/dist/create.js +175 -17
  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;
@@ -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("\\", "/");
@@ -20632,6 +20739,7 @@ var USAGE = [
20632
20739
  " skillset release apply [--yes|--dry-run] [--root <path>] [--source <dir>] [--dist <dir>]",
20633
20740
  " skillset restore <backup-id> [--yes|--dry-run] [--root <path>]",
20634
20741
  " skillset distribute plan [name] [--root <path>] [--source <dir>] [--dist <dir>]",
20742
+ " skillset features [feature-id] [--json]",
20635
20743
  " skillset test [name] [--root <path>] [--source <dir>]",
20636
20744
  " skillset hooks print --runner <lefthook|husky|pre-commit|git> [--pre-commit] [--pre-push]",
20637
20745
  " skillset hooks print --target <claude|codex> --agent-runtime",
@@ -20965,6 +21073,25 @@ async function runCli(rawArgs = process.argv.slice(2), invokedName = basename8(p
20965
21073
  console.log(`skillset: listed ${entries.length} generated entries`);
20966
21074
  return;
20967
21075
  }
21076
+ if (command === "features") {
21077
+ const features = listFeatureCapabilities(importPath);
21078
+ if (jsonOutput) {
21079
+ process.stdout.write(renderValidatedJson({ features }, "skillset features"));
21080
+ if (importPath !== undefined && features.length === 0)
21081
+ process.exitCode = 1;
21082
+ return;
21083
+ }
21084
+ if (features.length === 0) {
21085
+ console.log(`skillset: feature ${importPath ?? ""} not found`);
21086
+ process.exitCode = 1;
21087
+ return;
21088
+ }
21089
+ for (const feature2 of features) {
21090
+ printFeatureCapability(feature2);
21091
+ }
21092
+ console.log(`skillset: listed ${features.length} feature${features.length === 1 ? "" : "s"}`);
21093
+ return;
21094
+ }
20968
21095
  if (command === "explain") {
20969
21096
  if (importPath === undefined) {
20970
21097
  throw new Error("skillset: expected a path to explain");
@@ -21007,6 +21134,11 @@ async function runCli(rawArgs = process.argv.slice(2), invokedName = basename8(p
21007
21134
  if (entry.outputHash !== undefined)
21008
21135
  console.log(` output hash: ${entry.outputHash}`);
21009
21136
  }
21137
+ for (const feature2 of result3.features) {
21138
+ console.log(` feature ${feature2.id}: ${feature2.title}`);
21139
+ console.log(` claude: ${feature2.targetSupport.claude.status}`);
21140
+ console.log(` codex: ${feature2.targetSupport.codex.status}`);
21141
+ }
21010
21142
  for (const outcome of result3.loweringOutcomes) {
21011
21143
  printLoweringOutcome(outcome);
21012
21144
  }
@@ -21036,6 +21168,9 @@ async function runCli(rawArgs = process.argv.slice(2), invokedName = basename8(p
21036
21168
  if (driftCount > 0) {
21037
21169
  console.log(` drift: ${added.length} added, ${changed.length} changed, ${missing.length} missing, ${removed.length} removed (run skillset build --yes)`);
21038
21170
  }
21171
+ console.log(` features: ${report.featureCapabilities.total} registry entries; status ${formatCountSummary(report.featureCapabilities.byFeatureStatus)}`);
21172
+ console.log(` feature support: claude ${formatCountSummary(report.featureCapabilities.byTargetSupport.claude)}`);
21173
+ console.log(` feature support: codex ${formatCountSummary(report.featureCapabilities.byTargetSupport.codex)}`);
21039
21174
  for (const outcome of report.notableLoweringOutcomes) {
21040
21175
  printLoweringOutcome(outcome);
21041
21176
  }
@@ -21395,6 +21530,22 @@ function printSkillsetTest(report) {
21395
21530
  if (report.activationPath !== undefined)
21396
21531
  console.log(` activation: ${report.activationPath}`);
21397
21532
  }
21533
+ function printFeatureCapability(feature2) {
21534
+ console.log(`feature ${feature2.id}: ${feature2.title}`);
21535
+ console.log(` status: ${feature2.status}`);
21536
+ console.log(` claude: ${formatFeatureSupport(feature2.targetSupport.claude)}`);
21537
+ console.log(` codex: ${formatFeatureSupport(feature2.targetSupport.codex)}`);
21538
+ if (feature2.docs.length > 0)
21539
+ console.log(` docs: ${feature2.docs.join(", ")}`);
21540
+ }
21541
+ function formatFeatureSupport(support) {
21542
+ const reason = support.reason === undefined ? "" : ` (${support.reason})`;
21543
+ const note = support.note === undefined ? "" : ` note: ${support.note}`;
21544
+ return `${support.status}${reason}${note}`;
21545
+ }
21546
+ function formatCountSummary(counts) {
21547
+ return Object.entries(counts).sort(([left], [right]) => left.localeCompare(right)).map(([key, count]) => `${key} ${count}`).join(", ");
21548
+ }
21398
21549
  function printLoweringOutcome(outcome) {
21399
21550
  const target = outcome.target ?? "workspace";
21400
21551
  const policy = outcome.policy === undefined ? "" : ` policy: ${outcome.policy}`;
@@ -21409,8 +21560,8 @@ function printLoweringOutcome(outcome) {
21409
21560
  }
21410
21561
  function parseArgs(args) {
21411
21562
  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
21563
+ 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") {
21564
+ 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
21565
  ` + USAGE);
21415
21566
  }
21416
21567
  let changeSubcommand;
@@ -21534,6 +21685,13 @@ function parseArgs(args) {
21534
21685
  importPath = rawPath;
21535
21686
  index += 1;
21536
21687
  }
21688
+ if (command === "features") {
21689
+ const rawFeatureId = args[index];
21690
+ if (rawFeatureId !== undefined && !rawFeatureId.startsWith("--")) {
21691
+ importPath = rawFeatureId;
21692
+ index += 1;
21693
+ }
21694
+ }
21537
21695
  if (command === "restore") {
21538
21696
  const rawBackupId = args[index];
21539
21697
  if (rawBackupId === undefined || rawBackupId.startsWith("--")) {
@@ -21989,9 +22147,9 @@ function validateAdoptFlags(command, adopt) {
21989
22147
  function validateJsonFlags(command, jsonOutput) {
21990
22148
  if (!jsonOutput)
21991
22149
  return;
21992
- if (command === "doctor" || command === "explain")
22150
+ if (command === "doctor" || command === "explain" || command === "features")
21993
22151
  return;
21994
- throw new Error("skillset: --json is only supported with doctor or explain");
22152
+ throw new Error("skillset: --json is only supported with doctor, explain, or features");
21995
22153
  }
21996
22154
  function isImportKind(value) {
21997
22155
  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;
@@ -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("\\", "/");
@@ -20632,6 +20739,7 @@ var USAGE = [
20632
20739
  " skillset release apply [--yes|--dry-run] [--root <path>] [--source <dir>] [--dist <dir>]",
20633
20740
  " skillset restore <backup-id> [--yes|--dry-run] [--root <path>]",
20634
20741
  " skillset distribute plan [name] [--root <path>] [--source <dir>] [--dist <dir>]",
20742
+ " skillset features [feature-id] [--json]",
20635
20743
  " skillset test [name] [--root <path>] [--source <dir>]",
20636
20744
  " skillset hooks print --runner <lefthook|husky|pre-commit|git> [--pre-commit] [--pre-push]",
20637
20745
  " skillset hooks print --target <claude|codex> --agent-runtime",
@@ -20965,6 +21073,25 @@ async function runCli(rawArgs = process.argv.slice(2), invokedName = basename8(p
20965
21073
  console.log(`skillset: listed ${entries.length} generated entries`);
20966
21074
  return;
20967
21075
  }
21076
+ if (command === "features") {
21077
+ const features = listFeatureCapabilities(importPath);
21078
+ if (jsonOutput) {
21079
+ process.stdout.write(renderValidatedJson({ features }, "skillset features"));
21080
+ if (importPath !== undefined && features.length === 0)
21081
+ process.exitCode = 1;
21082
+ return;
21083
+ }
21084
+ if (features.length === 0) {
21085
+ console.log(`skillset: feature ${importPath ?? ""} not found`);
21086
+ process.exitCode = 1;
21087
+ return;
21088
+ }
21089
+ for (const feature2 of features) {
21090
+ printFeatureCapability(feature2);
21091
+ }
21092
+ console.log(`skillset: listed ${features.length} feature${features.length === 1 ? "" : "s"}`);
21093
+ return;
21094
+ }
20968
21095
  if (command === "explain") {
20969
21096
  if (importPath === undefined) {
20970
21097
  throw new Error("skillset: expected a path to explain");
@@ -21007,6 +21134,11 @@ async function runCli(rawArgs = process.argv.slice(2), invokedName = basename8(p
21007
21134
  if (entry.outputHash !== undefined)
21008
21135
  console.log(` output hash: ${entry.outputHash}`);
21009
21136
  }
21137
+ for (const feature2 of result3.features) {
21138
+ console.log(` feature ${feature2.id}: ${feature2.title}`);
21139
+ console.log(` claude: ${feature2.targetSupport.claude.status}`);
21140
+ console.log(` codex: ${feature2.targetSupport.codex.status}`);
21141
+ }
21010
21142
  for (const outcome of result3.loweringOutcomes) {
21011
21143
  printLoweringOutcome(outcome);
21012
21144
  }
@@ -21036,6 +21168,9 @@ async function runCli(rawArgs = process.argv.slice(2), invokedName = basename8(p
21036
21168
  if (driftCount > 0) {
21037
21169
  console.log(` drift: ${added.length} added, ${changed.length} changed, ${missing.length} missing, ${removed.length} removed (run skillset build --yes)`);
21038
21170
  }
21171
+ console.log(` features: ${report.featureCapabilities.total} registry entries; status ${formatCountSummary(report.featureCapabilities.byFeatureStatus)}`);
21172
+ console.log(` feature support: claude ${formatCountSummary(report.featureCapabilities.byTargetSupport.claude)}`);
21173
+ console.log(` feature support: codex ${formatCountSummary(report.featureCapabilities.byTargetSupport.codex)}`);
21039
21174
  for (const outcome of report.notableLoweringOutcomes) {
21040
21175
  printLoweringOutcome(outcome);
21041
21176
  }
@@ -21395,6 +21530,22 @@ function printSkillsetTest(report) {
21395
21530
  if (report.activationPath !== undefined)
21396
21531
  console.log(` activation: ${report.activationPath}`);
21397
21532
  }
21533
+ function printFeatureCapability(feature2) {
21534
+ console.log(`feature ${feature2.id}: ${feature2.title}`);
21535
+ console.log(` status: ${feature2.status}`);
21536
+ console.log(` claude: ${formatFeatureSupport(feature2.targetSupport.claude)}`);
21537
+ console.log(` codex: ${formatFeatureSupport(feature2.targetSupport.codex)}`);
21538
+ if (feature2.docs.length > 0)
21539
+ console.log(` docs: ${feature2.docs.join(", ")}`);
21540
+ }
21541
+ function formatFeatureSupport(support) {
21542
+ const reason = support.reason === undefined ? "" : ` (${support.reason})`;
21543
+ const note = support.note === undefined ? "" : ` note: ${support.note}`;
21544
+ return `${support.status}${reason}${note}`;
21545
+ }
21546
+ function formatCountSummary(counts) {
21547
+ return Object.entries(counts).sort(([left], [right]) => left.localeCompare(right)).map(([key, count]) => `${key} ${count}`).join(", ");
21548
+ }
21398
21549
  function printLoweringOutcome(outcome) {
21399
21550
  const target = outcome.target ?? "workspace";
21400
21551
  const policy = outcome.policy === undefined ? "" : ` policy: ${outcome.policy}`;
@@ -21409,8 +21560,8 @@ function printLoweringOutcome(outcome) {
21409
21560
  }
21410
21561
  function parseArgs(args) {
21411
21562
  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
21563
+ 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") {
21564
+ 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
21565
  ` + USAGE);
21415
21566
  }
21416
21567
  let changeSubcommand;
@@ -21534,6 +21685,13 @@ function parseArgs(args) {
21534
21685
  importPath = rawPath;
21535
21686
  index += 1;
21536
21687
  }
21688
+ if (command === "features") {
21689
+ const rawFeatureId = args[index];
21690
+ if (rawFeatureId !== undefined && !rawFeatureId.startsWith("--")) {
21691
+ importPath = rawFeatureId;
21692
+ index += 1;
21693
+ }
21694
+ }
21537
21695
  if (command === "restore") {
21538
21696
  const rawBackupId = args[index];
21539
21697
  if (rawBackupId === undefined || rawBackupId.startsWith("--")) {
@@ -21989,9 +22147,9 @@ function validateAdoptFlags(command, adopt) {
21989
22147
  function validateJsonFlags(command, jsonOutput) {
21990
22148
  if (!jsonOutput)
21991
22149
  return;
21992
- if (command === "doctor" || command === "explain")
22150
+ if (command === "doctor" || command === "explain" || command === "features")
21993
22151
  return;
21994
- throw new Error("skillset: --json is only supported with doctor or explain");
22152
+ throw new Error("skillset: --json is only supported with doctor, explain, or features");
21995
22153
  }
21996
22154
  function isImportKind(value) {
21997
22155
  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.0",
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",