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.
- package/dist/cli.js +190 -29
- package/dist/create.js +190 -29
- 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
|
|
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
|
|
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
|
|
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: "
|
|
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
|
|
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(
|
|
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
|
-
"#
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
19095
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
19379
|
-
if (await commandExists("bun", rootPath))
|
|
19380
|
-
return { argv: ["bun", "x", "skillset
|
|
19381
|
-
if (await commandExists("npx", rootPath))
|
|
19382
|
-
return { argv: ["npx", "--yes", "skillset
|
|
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`], {
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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: "
|
|
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
|
|
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(
|
|
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
|
-
"#
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
19095
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
19379
|
-
if (await commandExists("bun", rootPath))
|
|
19380
|
-
return { argv: ["bun", "x", "skillset
|
|
19381
|
-
if (await commandExists("npx", rootPath))
|
|
19382
|
-
return { argv: ["npx", "--yes", "skillset
|
|
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`], {
|
|
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
|
|
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
|
|
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",
|