skillset 0.14.0 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +615 -110
  2. package/dist/create.js +615 -110
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -7024,8 +7024,8 @@ var require_dist = __commonJS((exports) => {
7024
7024
  });
7025
7025
 
7026
7026
  // apps/skillset/src/cli-core.ts
7027
- import { mkdir as mkdir11, writeFile as writeFile12 } from "fs/promises";
7028
- import { basename as basename9, dirname as dirname16, resolve as resolve10 } from "path";
7027
+ import { mkdir as mkdir12, writeFile as writeFile13 } from "fs/promises";
7028
+ import { basename as basename9, dirname as dirname17, resolve as resolve10 } from "path";
7029
7029
 
7030
7030
  // packages/core/src/authoring.ts
7031
7031
  import { readFile as readFile10, writeFile as writeFile4 } from "fs/promises";
@@ -7206,7 +7206,7 @@ var skillsetFeatureRegistry = defineFeatureRegistry([
7206
7206
  id: "output-safety",
7207
7207
  kind: "workflow",
7208
7208
  renderOwner: "packages/core/src/output-safety.ts",
7209
- sourceShape: "generated .skillset.lock ownership plus current rendered output paths",
7209
+ sourceShape: "generated skillset.lock ownership plus current rendered output paths",
7210
7210
  status: "implemented",
7211
7211
  summary: "Protects unmanaged generated-output collisions and target-side edits with reversible backups.",
7212
7212
  targetSupport: notTargetRuntime(),
@@ -7656,8 +7656,20 @@ var skillsetFeatureRegistry = defineFeatureRegistry([
7656
7656
  validationOwner: "packages/core/src/version-audit.ts"
7657
7657
  }),
7658
7658
  feature({
7659
- docs: ["docs/features/ci.md", "docs/features/build-scopes.md"],
7660
- evidence: [test("apps/skillset/src/__tests__/ci.test.ts", "CI and build-scope coverage")],
7659
+ docs: ["docs/features/ci.md", "docs/features/build-scopes.md", "docs/features/workbench.md"],
7660
+ evidence: [
7661
+ test("apps/skillset/src/__tests__/ci.test.ts", "CI and build-scope coverage"),
7662
+ test("apps/skillset/src/__tests__/contract.test.ts", "CLI check/verify command coverage"),
7663
+ test("packages/workbench/src/__tests__/presets.test.ts", "Workbench presets"),
7664
+ test("packages/workbench/src/__tests__/lint-bridge.test.ts", "Workbench lint bridge"),
7665
+ test("packages/workbench/src/__tests__/parser.test.ts", "Workbench parser diagnostics"),
7666
+ test("packages/workbench/src/__tests__/schema.test.ts", "Workbench source contract diagnostics"),
7667
+ test("packages/workbench/src/__tests__/compatibility.test.ts", "Workbench provider compatibility diagnostics"),
7668
+ test("packages/workbench/src/__tests__/resource-runtime.test.ts", "Workbench resource and runtime diagnostics"),
7669
+ test("packages/workbench/src/__tests__/fixtures.test.ts", "Workbench clean and invalid fixtures"),
7670
+ test("packages/workbench/src/__tests__/ast-grep.test.ts", "Workbench ast-grep adapter proof"),
7671
+ test("packages/workbench/src/__tests__/markdown.test.ts", "Workbench Markdown diagnostics")
7672
+ ],
7661
7673
  id: "workflows",
7662
7674
  kind: "workflow",
7663
7675
  renderOwner: "apps/skillset/src/cli-core.ts",
@@ -8047,13 +8059,14 @@ function readCompileConfig(record, label) {
8047
8059
  if (compile === undefined) {
8048
8060
  return {
8049
8061
  build: "updated",
8062
+ features: { promptArguments: true },
8050
8063
  skillset: { metadata: true },
8051
8064
  targets: [...TARGET_NAMES],
8052
8065
  unsupportedDestination: "error"
8053
8066
  };
8054
8067
  }
8055
8068
  for (const key of Object.keys(compile)) {
8056
- if (key !== "build" && key !== "skillset" && key !== "targets" && key !== "unsupportedDestination") {
8069
+ if (key !== "build" && key !== "features" && key !== "skillset" && key !== "targets" && key !== "unsupportedDestination") {
8057
8070
  throw new Error(`skillset: unsupported compile key ${key} in ${label}`);
8058
8071
  }
8059
8072
  }
@@ -8063,6 +8076,7 @@ function readCompileConfig(record, label) {
8063
8076
  }
8064
8077
  return {
8065
8078
  build: readCompileBuildMode(compile, `${label}.compile.build`),
8079
+ features: readCompileFeatureConfig(compile, `${label}.compile.features`),
8066
8080
  skillset: readCompileSkillsetConfig(compile, `${label}.compile.skillset`),
8067
8081
  targets: readCompileTargetNames(compile, `${label}.compile.targets`),
8068
8082
  unsupportedDestination
@@ -8356,6 +8370,26 @@ function readCompileSkillsetConfig(record, label) {
8356
8370
  }
8357
8371
  return { metadata };
8358
8372
  }
8373
+ function readCompileFeatureConfig(record, label) {
8374
+ const value = record.features;
8375
+ if (value === undefined)
8376
+ return { promptArguments: true };
8377
+ if (!isJsonRecord(value)) {
8378
+ throw new Error(`skillset: expected ${label} to be an object`);
8379
+ }
8380
+ for (const key of Object.keys(value)) {
8381
+ if (key !== "promptArguments") {
8382
+ throw new Error(`skillset: unsupported compile feature key ${key} in ${label}`);
8383
+ }
8384
+ }
8385
+ const promptArguments = value.promptArguments;
8386
+ if (promptArguments === undefined)
8387
+ return { promptArguments: true };
8388
+ if (typeof promptArguments !== "boolean") {
8389
+ throw new Error(`skillset: expected ${label}.promptArguments to be a boolean`);
8390
+ }
8391
+ return { promptArguments };
8392
+ }
8359
8393
  function readUnsupportedDestinationPolicy(record, label) {
8360
8394
  const value = record.unsupportedDestination;
8361
8395
  if (value === undefined)
@@ -9111,7 +9145,7 @@ function targetNativeSurface(relativePath) {
9111
9145
  }
9112
9146
 
9113
9147
  // packages/core/src/render-result-collector.ts
9114
- var LOCK_FILE = ".skillset.lock";
9148
+ var LOCK_FILE = "skillset.lock";
9115
9149
  var TARGETS2 = ["claude", "codex"];
9116
9150
  function collectRenderResults(graph, rendered, options) {
9117
9151
  const mapOutputPath = options.mapOutputPath ?? ((path) => path);
@@ -9705,7 +9739,7 @@ function renderValidatedToml(value, label) {
9705
9739
  function validateGeneratedStructuredOutput(args) {
9706
9740
  const label = structuredOutputLabel(args);
9707
9741
  try {
9708
- if (args.targetPath.endsWith(".json") || args.targetPath.endsWith(".skillset.lock")) {
9742
+ if (args.targetPath.endsWith(".json") || args.targetPath.endsWith("skillset.lock")) {
9709
9743
  validateJson(args.content, label);
9710
9744
  } else if (args.targetPath.endsWith(".yaml") || args.targetPath.endsWith(".yml")) {
9711
9745
  validateYaml(args.content, label);
@@ -9807,7 +9841,7 @@ function structuredOutputLabel(args) {
9807
9841
  }
9808
9842
 
9809
9843
  // packages/core/src/output-safety.ts
9810
- var WORKSPACE_LOCK_FILE = ".skillset.lock";
9844
+ var WORKSPACE_LOCK_FILE = "skillset.lock";
9811
9845
  var OUTPUT_BACKUP_ROOT = ".skillset/build/backups";
9812
9846
  async function readManagedOutputState(rootPath, liveOutputRoots, includeWorkspaceLock, outPath) {
9813
9847
  const paths = new Set;
@@ -10318,7 +10352,7 @@ var dynamicArgumentsEntry = {
10318
10352
  evidence: DYNAMIC_EVIDENCE,
10319
10353
  forms: {
10320
10354
  claude: {
10321
- pattern: /\$ARGUMENTS(?:\b|\[[^\]]+\]|\.[A-Za-z_][A-Za-z0-9_-]*)/gu
10355
+ pattern: /\$ARGUMENTS(?:\[[^\]]+\]|\.[A-Za-z_][A-Za-z0-9_-]*|\b)/gu
10322
10356
  }
10323
10357
  },
10324
10358
  intent: "dynamic.arguments",
@@ -10330,7 +10364,7 @@ var dynamicPositionalEntry = {
10330
10364
  evidence: DYNAMIC_EVIDENCE,
10331
10365
  forms: {
10332
10366
  claude: {
10333
- pattern: /(?<![\w$])\$[0-9]+\b/gu
10367
+ pattern: /(?<![\w$])\$[01]\b(?!\.\d)/gu
10334
10368
  }
10335
10369
  },
10336
10370
  intent: "dynamic.positional",
@@ -11459,8 +11493,25 @@ async function resolveVariable(token, key, context, renderContext) {
11459
11493
  if (value !== undefined)
11460
11494
  return value;
11461
11495
  }
11496
+ if (key.startsWith("$ARGUMENTS")) {
11497
+ return promptArgumentsVariable(token, key, context);
11498
+ }
11462
11499
  throw new Error(`skillset: unknown preprocess variable ${token} in ${relative5(context.rootPath, context.sourcePath)}`);
11463
11500
  }
11501
+ function promptArgumentsVariable(token, key, context) {
11502
+ if (!isPromptArgumentsVariable(key)) {
11503
+ throw new Error(`skillset: invalid prompt arguments variable ${token} in ${relative5(context.rootPath, context.sourcePath)}`);
11504
+ }
11505
+ if (context.promptArguments === false) {
11506
+ throw new Error(`skillset: prompt arguments variable ${token} in ${relative5(context.rootPath, context.sourcePath)} requires compile.features.promptArguments`);
11507
+ }
11508
+ if (context.target === "claude")
11509
+ return key;
11510
+ return `{{${key}}}`;
11511
+ }
11512
+ function isPromptArgumentsVariable(key) {
11513
+ return /^\$ARGUMENTS(?:\b|\[[0-9]+\]|\.[A-Za-z_][A-Za-z0-9_-]*)$/u.test(key);
11514
+ }
11464
11515
  function readPathValue(record, path) {
11465
11516
  let current = record;
11466
11517
  for (const segment of path.split(".")) {
@@ -12110,7 +12161,7 @@ function renderRepositoryReadmes(graph) {
12110
12161
  "",
12111
12162
  "- `.claude-plugin/marketplace.json` indexes the generated plugins.",
12112
12163
  "- `plugins/<plugin-id>/` contains each Claude plugin bundle.",
12113
- "- `.skillset.lock` records deterministic generated-state provenance.",
12164
+ "- `skillset.lock` records deterministic generated-state provenance.",
12114
12165
  ""
12115
12166
  ].join(`
12116
12167
  `)));
@@ -12122,7 +12173,7 @@ function renderRepositoryReadmes(graph) {
12122
12173
  "Generated Codex plugin repository.",
12123
12174
  "",
12124
12175
  "- `plugins/<plugin-id>/` contains each Codex plugin bundle.",
12125
- "- `.skillset.lock` records deterministic generated-state provenance.",
12176
+ "- `skillset.lock` records deterministic generated-state provenance.",
12126
12177
  ""
12127
12178
  ].join(`
12128
12179
  `)));
@@ -12666,10 +12717,17 @@ async function renderSkillMarkdown(graph, plugin, skill, target) {
12666
12717
  rootPath: graph.rootPath,
12667
12718
  sourcePath: skill.sourcePath,
12668
12719
  sourceRoot: graph.sourceRoot,
12720
+ target,
12721
+ promptArguments: graph.root.compile.features.promptArguments,
12669
12722
  ...plugin === undefined ? {} : { pluginPath: plugin.path }
12670
12723
  });
12671
- const dependencyNotice = target === "codex" && plugin !== undefined ? renderCodexDependencyNotice(graph, plugin) : undefined;
12672
- const body = dependencyNotice === undefined ? preprocessedBody : `${dependencyNotice}
12724
+ const notices = [
12725
+ target === "codex" && plugin !== undefined ? renderCodexDependencyNotice(graph, plugin) : undefined,
12726
+ target === "codex" ? renderCodexPromptArgumentsNotice(preprocessedBody) : undefined
12727
+ ].filter((notice) => notice !== undefined);
12728
+ const body = notices.length === 0 ? preprocessedBody : `${notices.join(`
12729
+
12730
+ `)}
12673
12731
 
12674
12732
  ${preprocessedBody}`;
12675
12733
  const linkedBody = rewriteResourceLinks(body, skill.resources, skill.sourcePath);
@@ -12680,6 +12738,12 @@ ${preprocessedBody}`;
12680
12738
  transforms: translated.transforms
12681
12739
  };
12682
12740
  }
12741
+ function renderCodexPromptArgumentsNotice(body) {
12742
+ if (!/\{\{\$ARGUMENTS(?:\}\}|\[[0-9]+\]\}\}|\.[A-Za-z_][A-Za-z0-9_-]*\}\})/u.test(body)) {
12743
+ return;
12744
+ }
12745
+ return "Before using commands, replace `{{$ARGUMENTS...}}` placeholders with the user's supplied arguments.";
12746
+ }
12683
12747
  function renderClaudeSkillPolicy(skill, targetOptions) {
12684
12748
  const label = skill.sourcePath;
12685
12749
  const implicitInvocation = readImplicitInvocation(skill.frontmatter, "claude", label);
@@ -12717,6 +12781,8 @@ async function renderCodexSkillAgentFile(graph, plugin, skill, target, sourceDir
12717
12781
  rootPath: graph.rootPath,
12718
12782
  sourcePath: sourceOpenAiPath,
12719
12783
  sourceRoot: graph.sourceRoot,
12784
+ target,
12785
+ promptArguments: graph.root.compile.features.promptArguments,
12720
12786
  ...plugin === undefined ? {} : { pluginPath: plugin.path }
12721
12787
  }), sourceOpenAiPath) : {};
12722
12788
  const merged = mergeRecords(source2, generated);
@@ -13097,6 +13163,9 @@ function renderLockFiles(graph, lockRoots) {
13097
13163
  for (const [outputRoot, lock] of [...lockRoots.entries()].sort(([left], [right]) => compareStrings(left, right))) {
13098
13164
  const value = {
13099
13165
  buildMode: graph.root.compile.build,
13166
+ features: {
13167
+ promptArguments: graph.root.compile.features.promptArguments
13168
+ },
13100
13169
  generatedBy: GENERATED_BY2,
13101
13170
  items: lock.items.map((item) => stripUndefinedLockItem(item)).sort((left, right) => compareStrings(String(left.outputPath), String(right.outputPath))),
13102
13171
  selectedTargets: [...graph.root.compile.targets],
@@ -13106,7 +13175,7 @@ function renderLockFiles(graph, lockRoots) {
13106
13175
  sourceRoot: graph.sourceRoot,
13107
13176
  target: lock.target
13108
13177
  };
13109
- rendered.push(textFile(join6(outputRoot, ".skillset.lock"), renderValidatedJson(value, `${outputRoot}/.skillset.lock`)));
13178
+ rendered.push(textFile(join6(outputRoot, "skillset.lock"), renderValidatedJson(value, `${outputRoot}/skillset.lock`)));
13110
13179
  }
13111
13180
  return rendered;
13112
13181
  }
@@ -13514,7 +13583,7 @@ async function exists3(path) {
13514
13583
  }
13515
13584
  }
13516
13585
  function validateRenderedFile(file) {
13517
- if (file.sourcePath !== undefined || file.path.endsWith(".skillset.lock")) {
13586
+ if (file.sourcePath !== undefined || file.path.endsWith("skillset.lock")) {
13518
13587
  validateGeneratedStructuredOutput({
13519
13588
  content: textDecoder.decode(file.content),
13520
13589
  targetPath: file.path,
@@ -14413,7 +14482,7 @@ async function outputRootsFor(rootPath, outputs, plugins, standaloneSkills, rule
14413
14482
  for (const outputRoot of configuredOutputRoots(outputs)) {
14414
14483
  if (roots.has(outputRoot.path))
14415
14484
  continue;
14416
- if (await exists5(join8(resolveInside(rootPath, outputRoot.path), ".skillset.lock"))) {
14485
+ if (await exists5(join8(resolveInside(rootPath, outputRoot.path), "skillset.lock"))) {
14417
14486
  roots.set(outputRoot.path, outputRoot);
14418
14487
  }
14419
14488
  }
@@ -14671,7 +14740,7 @@ async function diffSkillsetResult(rootPath, options = {}) {
14671
14740
  }
14672
14741
  };
14673
14742
  }
14674
- async function checkSkillsetResult(rootPath, options = {}) {
14743
+ async function verifySkillsetResult(rootPath, options = {}) {
14675
14744
  const graph = await loadBuildGraph(rootPath, options);
14676
14745
  const diagnostics = [...graph.warnings.map(sourceWarningDiagnostic)];
14677
14746
  const outPath = outPathMapper(options);
@@ -14724,7 +14793,7 @@ async function checkSkillsetResult(rootPath, options = {}) {
14724
14793
  diagnostics: [...diagnostics, ...driftDiagnostics],
14725
14794
  renderResults: renderResultsWithDiagnostics,
14726
14795
  ok: failures.length === 0,
14727
- operation: "check",
14796
+ operation: "verify",
14728
14797
  writes: {
14729
14798
  deletedPaths: [],
14730
14799
  mode: "read",
@@ -14853,12 +14922,12 @@ function outputPathsForLock(outputRoot, lock) {
14853
14922
  return paths;
14854
14923
  }
14855
14924
  function outputRootForLockPath(lockPath) {
14856
- if (lockPath === ".skillset.lock")
14925
+ if (lockPath === "skillset.lock")
14857
14926
  return ".";
14858
14927
  return dirname7(lockPath).replaceAll("\\", "/");
14859
14928
  }
14860
14929
  function isLockFilePath(path) {
14861
- return path === ".skillset.lock" || path.endsWith("/.skillset.lock");
14930
+ return path === "skillset.lock" || path.endsWith("/skillset.lock");
14862
14931
  }
14863
14932
  async function diagnoseMissingManagedOutputs(rootPath, rendered, previousManagedPaths) {
14864
14933
  const diagnostics = [];
@@ -15492,12 +15561,12 @@ var CLAUDE_DYNAMIC_PATTERNS = [
15492
15561
  {
15493
15562
  code: "claude-arguments",
15494
15563
  label: "$ARGUMENTS",
15495
- pattern: /\$ARGUMENTS(?:\b|\[[^\]]+\]|\.[A-Za-z_][A-Za-z0-9_-]*)/
15564
+ pattern: /\$ARGUMENTS(?:\[[^\]]+\]|\.[A-Za-z_][A-Za-z0-9_-]*|\b)/
15496
15565
  },
15497
15566
  {
15498
15567
  code: "claude-positional-argument",
15499
15568
  label: "$0/$1 positional arguments",
15500
- pattern: /(^|[^\w$])\$[0-9]+\b/
15569
+ pattern: /(^|[^\w$])\$[01]\b(?!\.\d)/
15501
15570
  },
15502
15571
  {
15503
15572
  code: "claude-env-substitution",
@@ -15512,6 +15581,7 @@ var CLAUDE_DYNAMIC_PATTERNS = [
15512
15581
  ];
15513
15582
  var FENCE_PATTERN2 = /^\s*(?:```|~~~)/u;
15514
15583
  var INLINE_CODE_PATTERN2 = /`[^`]*`/gu;
15584
+ var SKILLSET_PROMPT_ARGUMENT_PATTERN = /\{\{\s*\$ARGUMENTS(?:\b|\[[0-9]+\]|\.[A-Za-z_][A-Za-z0-9_-]*)\s*\}\}/gu;
15515
15585
  async function lintSkillset(rootPath, options = {}) {
15516
15586
  const graph = await loadBuildGraph(rootPath, options);
15517
15587
  const result = await inspectBuildGraph(graph);
@@ -15696,7 +15766,18 @@ function lintSkill(graph, skill, featureId) {
15696
15766
  if (!skill.targets.codex.enabled)
15697
15767
  return issues;
15698
15768
  issues.push(...lintCodexAllowedTools(graph, skill));
15699
- const searchableBody = maskMarkdownCodeRegions(skill.body);
15769
+ const markdownSearchableBody = maskMarkdownCodeRegions(skill.body);
15770
+ if (!graph.root.compile.features.promptArguments && hasSkillsetPromptArguments(skill.body)) {
15771
+ const path2 = relative10(graph.rootPath, skill.sourcePath);
15772
+ issues.push({
15773
+ code: "prompt-arguments-disabled",
15774
+ featureId,
15775
+ severity: "error",
15776
+ path: path2,
15777
+ message: `${path2} uses Skillset prompt argument placeholders while compile.features.promptArguments is false. ` + "Enable compile.features.promptArguments or remove the {{$ARGUMENTS...}} placeholders."
15778
+ });
15779
+ }
15780
+ const searchableBody = maskSkillsetPromptArguments(markdownSearchableBody);
15700
15781
  const matches = CLAUDE_DYNAMIC_PATTERNS.filter(({ pattern }) => pattern.test(searchableBody));
15701
15782
  if (matches.length === 0)
15702
15783
  return issues;
@@ -15711,6 +15792,14 @@ function lintSkill(graph, skill, featureId) {
15711
15792
  });
15712
15793
  return issues;
15713
15794
  }
15795
+ function maskSkillsetPromptArguments(body) {
15796
+ SKILLSET_PROMPT_ARGUMENT_PATTERN.lastIndex = 0;
15797
+ return body.replaceAll(SKILLSET_PROMPT_ARGUMENT_PATTERN, "");
15798
+ }
15799
+ function hasSkillsetPromptArguments(body) {
15800
+ SKILLSET_PROMPT_ARGUMENT_PATTERN.lastIndex = 0;
15801
+ return SKILLSET_PROMPT_ARGUMENT_PATTERN.test(body);
15802
+ }
15714
15803
  function maskMarkdownCodeRegions(body) {
15715
15804
  let inFence = false;
15716
15805
  return body.split(/\r?\n/u).map((line) => {
@@ -15803,7 +15892,7 @@ async function explainPath(rootPath, inputPath, options = {}) {
15803
15892
  entries: asGenerated.map((item) => item.entry),
15804
15893
  features: featureCapabilitiesForPath(graph, target, asGenerated, matchedRenderResults),
15805
15894
  renderResults: matchedRenderResults,
15806
- notes: [`Generated output; rebuild with skillset build, verify with skillset check.`]
15895
+ notes: [`Generated output; rebuild with skillset build, verify with skillset verify.`]
15807
15896
  };
15808
15897
  }
15809
15898
  const prefixMatch = items.filter((item) => item.sourcePath.startsWith(`${target}/`));
@@ -15902,7 +15991,7 @@ async function suggestSource(rootPath, inputPath, options = {}) {
15902
15991
  generatedPath,
15903
15992
  ...lockPath2 === undefined ? {} : { lockPath: lockPath2 },
15904
15993
  message: "Generated body matches the source body; no source edit is needed.",
15905
- nextSteps: ["Run `skillset diff` or `skillset check` to inspect any remaining generated drift."],
15994
+ nextSteps: ["Run `skillset diff` or `skillset verify` to inspect any remaining generated drift."],
15906
15995
  sourcePath,
15907
15996
  status: "suggestible",
15908
15997
  wouldWrite: false,
@@ -16036,7 +16125,7 @@ function errorMessage(error) {
16036
16125
  function collectLockItems(rendered) {
16037
16126
  const matches = [];
16038
16127
  for (const file of rendered) {
16039
- if (!file.path.endsWith(".skillset.lock"))
16128
+ if (!file.path.endsWith("skillset.lock"))
16040
16129
  continue;
16041
16130
  let parsed;
16042
16131
  try {
@@ -16105,7 +16194,7 @@ function joinOutputRoot2(outputRoot, file) {
16105
16194
  function lockPathForEntry(entry) {
16106
16195
  if (entry === undefined)
16107
16196
  return;
16108
- return joinOutputRoot2(entry.outputRoot, ".skillset.lock");
16197
+ return joinOutputRoot2(entry.outputRoot, "skillset.lock");
16109
16198
  }
16110
16199
  function refusedSourceSuggestion(generatedPath, entries, message, nextSteps, sourcePath) {
16111
16200
  const lockPath = lockPathForEntry(entries[0]);
@@ -16244,7 +16333,7 @@ function normalizeRepoPath(rootPath, inputPath) {
16244
16333
  var textDecoder4 = new TextDecoder;
16245
16334
 
16246
16335
  // packages/core/src/normalized-output-tree.ts
16247
- var DEFAULT_STRUCTURED_JSON_BASENAMES = new Set([".skillset.lock"]);
16336
+ var DEFAULT_STRUCTURED_JSON_BASENAMES = new Set(["skillset.lock"]);
16248
16337
 
16249
16338
  // packages/core/src/deterministic-projection.ts
16250
16339
  var textEncoder4 = new TextEncoder;
@@ -17601,7 +17690,7 @@ function kindForSourceUnitId(id) {
17601
17690
  return "root-config";
17602
17691
  }
17603
17692
  async function sourceInventoryFromLock(rootPath, _options) {
17604
- const lockPath = resolveInside(rootPath, ".skillset.lock");
17693
+ const lockPath = resolveInside(rootPath, "skillset.lock");
17605
17694
  if (!await exists7(lockPath))
17606
17695
  return;
17607
17696
  let parsed;
@@ -17642,7 +17731,7 @@ async function sourceInventoryFromLock(rootPath, _options) {
17642
17731
  if (units.length === 0)
17643
17732
  return;
17644
17733
  return {
17645
- baseline: { hashSchema, kind: "source-inventory", label: ".skillset.lock" },
17734
+ baseline: { hashSchema, kind: "source-inventory", label: "skillset.lock" },
17646
17735
  inventory: { hashSchema, units }
17647
17736
  };
17648
17737
  }
@@ -18377,7 +18466,7 @@ async function importSource(options) {
18377
18466
  nextChecks: [
18378
18467
  "skillset lint",
18379
18468
  "skillset build",
18380
- "skillset check"
18469
+ "skillset verify"
18381
18470
  ],
18382
18471
  preservedTargetNativeFields: classification.targetNative,
18383
18472
  sourcePath,
@@ -19415,7 +19504,7 @@ async function maybeCandidate(candidates, rootPath, path2, kind) {
19415
19504
  candidates.push({ kind, path: relative14(rootPath, absolutePath).replaceAll("\\", "/") });
19416
19505
  }
19417
19506
  async function isManagedCandidate(path2) {
19418
- return await pathExists(join15(path2, ".skillset.lock")) || await pathExists(join15(dirname11(path2), ".skillset.lock"));
19507
+ return await pathExists(join15(path2, "skillset.lock")) || await pathExists(join15(dirname11(path2), "skillset.lock"));
19419
19508
  }
19420
19509
  function compareCandidate(left, right) {
19421
19510
  return `${left.kind}:${left.path}` < `${right.kind}:${right.path}` ? -1 : `${left.kind}:${left.path}` > `${right.kind}:${right.path}` ? 1 : 0;
@@ -19544,6 +19633,7 @@ function createReadme(name, targets) {
19544
19633
  "skillset build --dry-run",
19545
19634
  "skillset build --yes",
19546
19635
  "skillset check",
19636
+ "skillset verify",
19547
19637
  "skillset change status",
19548
19638
  "```",
19549
19639
  "",
@@ -19574,7 +19664,7 @@ function createAgentsGuide(name) {
19574
19664
  "- Treat `changes/` as Skillset-managed change and release state.",
19575
19665
  "- Treat generated target directories as outputs; do not hand-edit them as source truth.",
19576
19666
  "- Run `skillset build --dry-run` before writing generated outputs.",
19577
- "- Run `skillset check` before committing source changes.",
19667
+ "- Run `skillset check` and `skillset verify` before committing source changes.",
19578
19668
  "- Add pending change entries with `skillset change add` when source units change and the repo uses Skillset release tracking.",
19579
19669
  ""
19580
19670
  ].join(`
@@ -19785,6 +19875,7 @@ async function importCandidate(rootPath, acquisition, candidate, cutover, previe
19785
19875
  try {
19786
19876
  const destination = await importInstructionFile(rootPath, candidate.path);
19787
19877
  await writeMarkdownSourceOrigin2(join16(rootPath, destination), sourceOriginFor(acquisition, candidate.path));
19878
+ await normalizeImportedPromptArguments(join16(rootPath, destination));
19788
19879
  cutover.push(candidate.path);
19789
19880
  previewSources.push(destination);
19790
19881
  return { candidate, destination, detail: destination, renderResults: [], ok: true, units: [] };
@@ -19811,6 +19902,7 @@ async function importCandidate(rootPath, acquisition, candidate, cutover, previe
19811
19902
  for (const file of report.copiedFiles) {
19812
19903
  if (basename8(file) !== "SKILL.md")
19813
19904
  continue;
19905
+ await normalizeImportedPromptArguments(join16(report.targetPath, file));
19814
19906
  previewSources.push(relative15(rootPath, join16(report.targetPath, file)).replaceAll("\\", "/"));
19815
19907
  }
19816
19908
  }
@@ -19865,12 +19957,45 @@ function sourceOriginRecord3(origin) {
19865
19957
  ...origin.repo === undefined ? {} : { repo: origin.repo }
19866
19958
  };
19867
19959
  }
19960
+ async function normalizeImportedPromptArguments(path2) {
19961
+ const raw = await readFile17(path2, "utf8");
19962
+ let updated;
19963
+ try {
19964
+ const parts = parseMarkdown(raw, path2);
19965
+ const body = rewriteClaudePromptArguments(parts.body);
19966
+ updated = body === parts.body ? raw : stringifyMarkdown(parts.frontmatter, body);
19967
+ } catch {
19968
+ updated = rewriteClaudePromptArguments(raw);
19969
+ }
19970
+ if (updated !== raw)
19971
+ await writeFile8(path2, updated);
19972
+ }
19973
+ function rewriteClaudePromptArguments(content) {
19974
+ const pattern = /\$ARGUMENTS(?:\[[0-9]+\]|\.[A-Za-z_][A-Za-z0-9_-]*|\b(?![\[.]))/gu;
19975
+ let rewritten = "";
19976
+ let cursor = 0;
19977
+ for (const match of content.matchAll(pattern)) {
19978
+ const token = match[0];
19979
+ const index = match.index;
19980
+ if (isAlreadySkillsetPromptArgument(content, index, index + token.length))
19981
+ continue;
19982
+ rewritten += content.slice(cursor, index);
19983
+ rewritten += `{{${token}}}`;
19984
+ cursor = index + token.length;
19985
+ }
19986
+ return `${rewritten}${content.slice(cursor)}`;
19987
+ }
19988
+ function isAlreadySkillsetPromptArgument(content, start, end) {
19989
+ const before = content.slice(Math.max(0, start - 4), start);
19990
+ const after = content.slice(end, Math.min(content.length, end + 4));
19991
+ return /\{\{\s*$/u.test(before) && /^\s*\}\}/u.test(after);
19992
+ }
19868
19993
  async function buildTransformPreviews(rootPath, paths) {
19869
19994
  const previews = [];
19870
19995
  for (const path2 of paths) {
19871
19996
  const raw = await readFile17(join16(rootPath, path2), "utf8");
19872
19997
  const body = basename8(path2) === "SKILL.md" ? markdownBody(raw, path2) : raw;
19873
- const matches = recognizeTransforms(body, "claude");
19998
+ const matches = recognizeTransforms(maskSkillsetPromptArguments2(body), "claude");
19874
19999
  if (matches.length === 0)
19875
20000
  continue;
19876
20001
  const transformable = matches.some((match) => match.lowering !== "none");
@@ -19892,6 +20017,9 @@ async function buildTransformPreviews(rootPath, paths) {
19892
20017
  }
19893
20018
  return previews;
19894
20019
  }
20020
+ function maskSkillsetPromptArguments2(body) {
20021
+ return body.replaceAll(/\{\{\s*\$ARGUMENTS(?:\[[0-9]+\]|\.[A-Za-z_][A-Za-z0-9_-]*|\b)\s*\}\}/gu, "");
20022
+ }
19895
20023
  async function declareClaudeDialect(path2) {
19896
20024
  const raw = await readFile17(path2, "utf8");
19897
20025
  try {
@@ -19986,7 +20114,7 @@ function renderAdoptReportMarkdown(report, opts) {
19986
20114
  }
19987
20115
  if (report.transformPreviews.length > 0) {
19988
20116
  lines.push("## Transforms (preview)", "");
19989
- lines.push("Bodies are untouched. Files with transformable constructs were marked `dialect: claude`; the build lowers their Codex projection through these forms.", "");
20117
+ lines.push("Claude `$ARGUMENTS` forms are normalized to Skillset prompt argument placeholders. Other bodies are untouched. Files with transformable constructs were marked `dialect: claude`; the build lowers their Codex projection through these forms.", "");
19990
20118
  for (const preview of report.transformPreviews) {
19991
20119
  lines.push(`### \`${preview.path}\``, "");
19992
20120
  if (preview.dialectDeclared) {
@@ -20025,7 +20153,7 @@ function renderAdoptReportMarkdown(report, opts) {
20025
20153
  lines.push(`- ${summary}`);
20026
20154
  }
20027
20155
  lines.push("");
20028
- lines.push("Full structured render results are in `report.json` and in the isolated `.skillset.lock` files when the isolated build completes.", "");
20156
+ lines.push("Full structured render results are in `report.json` and in the isolated `skillset.lock` files when the isolated build completes.", "");
20029
20157
  }
20030
20158
  lines.push("## Cutover", "");
20031
20159
  lines.push(`1. Review the generated mirror under \`${ISOLATED_OUT_ROOT}/\` against the originals.`);
@@ -20038,7 +20166,7 @@ function renderAdoptReportMarkdown(report, opts) {
20038
20166
  lines.push("2. No original paths need to move before a live build.");
20039
20167
  }
20040
20168
  lines.push("3. Run `skillset build --yes` to write live output.", "", "Until the originals move, live builds refuse to overwrite them as unmanaged files \u2014 that protection is the safety net; nothing is lost by building too early.", "");
20041
- lines.push("## Next steps", "", "- `skillset lint`", "- `skillset check --isolated`", "- `skillset ci`", "");
20169
+ lines.push("## Next steps", "", "- `skillset lint`", "- `skillset verify --isolated`", "- `skillset ci`", "");
20042
20170
  return `${lines.join(`
20043
20171
  `).trimEnd()}
20044
20172
  `;
@@ -20803,7 +20931,7 @@ function readHookRunEvent(value) {
20803
20931
  }
20804
20932
  // apps/skillset/src/runtime-hooks/print.ts
20805
20933
  var PRE_COMMIT_COMMAND = "skillset change check --staged";
20806
- var PRE_PUSH_COMMAND = "skillset change check --since origin/main && skillset check && skillset doctor";
20934
+ var PRE_PUSH_COMMAND = "skillset change check --since origin/main && skillset check && skillset verify && skillset doctor";
20807
20935
  var RUNTIME_POST_TOOL_USE_COMMAND = "skillset hooks run post-tool-use";
20808
20936
  var RUNTIME_STOP_COMMAND = "skillset hooks run stop";
20809
20937
  function renderHookPrint(options) {
@@ -21041,7 +21169,12 @@ async function runHookEvent(event, options = {}) {
21041
21169
  const checkArgs = ["check", "--root", "."];
21042
21170
  ranCommands.push(checkArgs.join(" "));
21043
21171
  const check = await runner(checkArgs, commandOptions({ allowFailure: false, options, rootPath }));
21044
- return result({ context, event, exitCode: check, gate, ranCommands });
21172
+ if (check !== 0)
21173
+ return result({ context, event, exitCode: check, gate, ranCommands });
21174
+ const verifyArgs = ["verify", "--root", "."];
21175
+ ranCommands.push(verifyArgs.join(" "));
21176
+ const verify = await runner(verifyArgs, commandOptions({ allowFailure: false, options, rootPath }));
21177
+ return result({ context, event, exitCode: verify, gate, ranCommands });
21045
21178
  }
21046
21179
  function commandOptions(args) {
21047
21180
  return {
@@ -21060,10 +21193,220 @@ function result(args) {
21060
21193
  sourceGateOk: args.gate.ok
21061
21194
  };
21062
21195
  }
21196
+ // apps/skillset/src/new-source.ts
21197
+ import { mkdir as mkdir9, stat as stat16, writeFile as writeFile10 } from "fs/promises";
21198
+ import { dirname as dirname14, join as join19 } from "path";
21199
+ var SKILL_PRESETS = new Set([
21200
+ "assets",
21201
+ "evals",
21202
+ "examples-file",
21203
+ "minimal",
21204
+ "reference-file",
21205
+ "references",
21206
+ "scripts",
21207
+ "support"
21208
+ ]);
21209
+ async function scaffoldSourceUnit(rootPath, options) {
21210
+ if (options.scope !== undefined && options.scope !== "repo") {
21211
+ throw new Error("skillset: new currently supports only --scope repo");
21212
+ }
21213
+ if (options.kind === "hook") {
21214
+ throw new Error("skillset: new hook is not available yet; current hook source is hooks/hooks.json");
21215
+ }
21216
+ const id = resolveSourceId(options);
21217
+ const displayName = resolveDisplayName(options, id);
21218
+ const sourceDir = await detectWorkspaceSourceDir(rootPath, options.skillsetOptions ?? {});
21219
+ await assertWorkspaceInitialized(rootPath, sourceDir);
21220
+ const sourceRoot = sourceDir === "." ? "skillset" : join19(sourceDir, "src");
21221
+ const plans = options.kind === "skill" ? await planSkill(rootPath, sourceRoot, id, displayName, options) : planAgent(sourceRoot, id, displayName, options);
21222
+ for (const plan of plans) {
21223
+ if (await fileExists2(resolveInside(rootPath, plan.path))) {
21224
+ throw new Error(`skillset: refusing to overwrite existing source file ${plan.path}`);
21225
+ }
21226
+ }
21227
+ if (options.write === true) {
21228
+ for (const plan of plans) {
21229
+ const absolutePath = resolveInside(rootPath, plan.path);
21230
+ await mkdir9(dirname14(absolutePath), { recursive: true });
21231
+ await writeFile10(absolutePath, plan.content);
21232
+ }
21233
+ }
21234
+ return {
21235
+ displayName,
21236
+ files: plans.map((plan) => ({ path: plan.path })),
21237
+ id,
21238
+ kind: options.kind,
21239
+ rootPath,
21240
+ sourceRoot,
21241
+ write: options.write === true
21242
+ };
21243
+ }
21244
+ function resolveSourceId(options) {
21245
+ if (options.id !== undefined)
21246
+ return validateSlug(options.id, `${options.kind} id`);
21247
+ const name = options.name ?? options.displayName;
21248
+ if (name === undefined || name.trim().length === 0) {
21249
+ throw new Error(`skillset: new ${options.kind} requires a name or --id`);
21250
+ }
21251
+ return validateSlug(kebabCase(name), `${options.kind} id`);
21252
+ }
21253
+ function resolveDisplayName(options, id) {
21254
+ if (options.displayName !== undefined && options.displayName.trim().length > 0) {
21255
+ return options.displayName.trim();
21256
+ }
21257
+ if (options.name !== undefined && options.name.trim().length > 0)
21258
+ return options.name.trim();
21259
+ return titleFromId(id);
21260
+ }
21261
+ async function planSkill(rootPath, sourceRoot, id, displayName, options) {
21262
+ const container = options.container === undefined ? undefined : validateSlug(options.container, "new --in container");
21263
+ if (container !== undefined)
21264
+ await assertPluginContainer(rootPath, sourceRoot, container);
21265
+ const skillRoot = container === undefined ? join19(sourceRoot, "skills", id) : join19(sourceRoot, "plugins", container, "skills", id);
21266
+ const presets = readSkillPresets(options.presets);
21267
+ const files = [
21268
+ {
21269
+ content: renderSkill(id, displayName),
21270
+ path: join19(skillRoot, "SKILL.md")
21271
+ }
21272
+ ];
21273
+ for (const preset of presets) {
21274
+ if (preset === "minimal")
21275
+ continue;
21276
+ if (preset === "support" || preset === "references") {
21277
+ files.push({ content: "", path: join19(skillRoot, "references", ".gitkeep") });
21278
+ }
21279
+ if (preset === "support" || preset === "assets") {
21280
+ files.push({ content: "", path: join19(skillRoot, "assets", ".gitkeep") });
21281
+ }
21282
+ if (preset === "support" || preset === "scripts") {
21283
+ files.push({ content: "", path: join19(skillRoot, "scripts", ".gitkeep") });
21284
+ }
21285
+ if (preset === "evals") {
21286
+ files.push({ content: `{
21287
+ "evals": []
21288
+ }
21289
+ `, path: join19(skillRoot, "evals", "evals.json") });
21290
+ }
21291
+ if (preset === "reference-file") {
21292
+ files.push({ content: `# ${displayName} Reference
21293
+
21294
+ Add concise reference material here.
21295
+ `, path: join19(skillRoot, "REFERENCE.md") });
21296
+ }
21297
+ if (preset === "examples-file") {
21298
+ files.push({ content: `# ${displayName} Examples
21299
+
21300
+ Add examples here.
21301
+ `, path: join19(skillRoot, "EXAMPLES.md") });
21302
+ }
21303
+ }
21304
+ return uniquePlans(files);
21305
+ }
21306
+ function planAgent(sourceRoot, id, displayName, options) {
21307
+ if (options.container !== undefined) {
21308
+ throw new Error("skillset: new agent does not support --in; project agents live at the repo source root");
21309
+ }
21310
+ if (options.presets !== undefined && options.presets.length > 0) {
21311
+ throw new Error("skillset: new agent does not support --preset");
21312
+ }
21313
+ return [
21314
+ {
21315
+ content: renderAgent(id, displayName),
21316
+ path: join19(sourceRoot, "agents", `${id}.md`)
21317
+ }
21318
+ ];
21319
+ }
21320
+ async function assertPluginContainer(rootPath, sourceRoot, container) {
21321
+ const pluginPath = join19(sourceRoot, "plugins", container);
21322
+ try {
21323
+ const stats = await stat16(resolveInside(rootPath, pluginPath));
21324
+ if (stats.isDirectory() && await hasPluginManifest(rootPath, pluginPath))
21325
+ return;
21326
+ } catch {}
21327
+ throw new Error(`skillset: new --in container does not exist or has no skillset.yaml/config.yaml: ${pluginPath}`);
21328
+ }
21329
+ async function assertWorkspaceInitialized(rootPath, sourceDir) {
21330
+ const manifests = sourceDir === "." ? ["skillset.yaml"] : [join19(sourceDir, "skillset.yaml"), join19(sourceDir, "config.yaml")];
21331
+ for (const manifest of manifests) {
21332
+ if (await fileExists2(resolveInside(rootPath, manifest)))
21333
+ return;
21334
+ }
21335
+ throw new Error("skillset: new requires an initialized Skillset workspace; run skillset init --yes for an existing repo or skillset create for a dedicated repo");
21336
+ }
21337
+ function readSkillPresets(values) {
21338
+ const raw = values === undefined || values.length === 0 ? ["minimal"] : values.flatMap((value) => value.split(",")).map((value) => value.trim()).filter(Boolean);
21339
+ const presets = [];
21340
+ const seen = new Set;
21341
+ for (const value of raw) {
21342
+ if (!SKILL_PRESETS.has(value)) {
21343
+ throw new Error("skillset: expected --preset minimal, support, references, assets, scripts, evals, reference-file, or examples-file");
21344
+ }
21345
+ if (seen.has(value))
21346
+ continue;
21347
+ seen.add(value);
21348
+ presets.push(value);
21349
+ }
21350
+ return presets;
21351
+ }
21352
+ function uniquePlans(plans) {
21353
+ const seen = new Set;
21354
+ const unique = [];
21355
+ for (const plan of plans) {
21356
+ if (seen.has(plan.path))
21357
+ continue;
21358
+ seen.add(plan.path);
21359
+ unique.push(plan);
21360
+ }
21361
+ return unique;
21362
+ }
21363
+ function renderSkill(id, displayName) {
21364
+ const description = `Use when working with ${displayName} workflows.`;
21365
+ return `---
21366
+ name: ${id}
21367
+ title: ${yamlString(displayName)}
21368
+ description: ${yamlString(description)}
21369
+ ---
21370
+
21371
+ # ${displayName}
21372
+
21373
+ Use this skill when working with ${displayName} workflows.
21374
+
21375
+ ## Workflow
21376
+
21377
+ - Add the domain-specific guidance here.
21378
+ `;
21379
+ }
21380
+ function renderAgent(id, displayName) {
21381
+ const description = `Use this agent for ${displayName} work.`;
21382
+ return `---
21383
+ name: ${id}
21384
+ description: ${yamlString(description)}
21385
+ ---
21386
+
21387
+ Use this agent for ${displayName} work.
21388
+ `;
21389
+ }
21390
+ function kebabCase(value) {
21391
+ return value.trim().toLowerCase().replace(/['\u2019]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
21392
+ }
21393
+ function titleFromId(id) {
21394
+ return id.split("-").filter((part) => part.length > 0).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
21395
+ }
21396
+ function yamlString(value) {
21397
+ return JSON.stringify(value);
21398
+ }
21399
+ async function fileExists2(path2) {
21400
+ return Bun.file(path2).exists();
21401
+ }
21402
+ async function hasPluginManifest(rootPath, pluginPath) {
21403
+ return await fileExists2(resolveInside(rootPath, join19(pluginPath, "skillset.yaml"))) || await fileExists2(resolveInside(rootPath, join19(pluginPath, "config.yaml")));
21404
+ }
21405
+
21063
21406
  // apps/skillset/src/release.ts
21064
- import { appendFile as appendFile2, mkdir as mkdir9, readFile as readFile20, rm as rm6, stat as stat16, writeFile as writeFile10 } from "fs/promises";
21407
+ import { appendFile as appendFile2, mkdir as mkdir10, readFile as readFile20, rm as rm6, stat as stat17, writeFile as writeFile11 } from "fs/promises";
21065
21408
  import { createHash as createHash7 } from "crypto";
21066
- import { dirname as dirname14, join as join19 } from "path";
21409
+ import { dirname as dirname15, join as join20 } from "path";
21067
21410
  var HISTORY_FILE3 = "changes/history.jsonl";
21068
21411
  var RELEASES_FILE = "changes/releases.jsonl";
21069
21412
  var RELEASE_AMENDMENTS_FILE = "changes/release-amendments.jsonl";
@@ -21147,9 +21490,9 @@ async function amendReleaseRecord(rootPath, options) {
21147
21490
  const notes = await resolveChangeReason(rootPath, options.reason);
21148
21491
  const now = new Date().toISOString();
21149
21492
  const sourceDir = storageOptions.sourceDir ?? ".skillset";
21150
- const amendmentPath = join19(sourceDir, RELEASE_AMENDMENTS_FILE).replaceAll("\\", "/");
21493
+ const amendmentPath = join20(sourceDir, RELEASE_AMENDMENTS_FILE).replaceAll("\\", "/");
21151
21494
  const absolutePath = resolveInside(rootPath, amendmentPath);
21152
- await mkdir9(dirname14(absolutePath), { recursive: true });
21495
+ await mkdir10(dirname15(absolutePath), { recursive: true });
21153
21496
  await appendFile2(absolutePath, `${JSON.stringify({
21154
21497
  amendedAt: now,
21155
21498
  id: release.id,
@@ -21294,9 +21637,9 @@ function nextReleaseState(state, scopesToWrite, updatedAt) {
21294
21637
  async function appendHistory(rootPath, sourceDir, entries, appliedAt, files) {
21295
21638
  if (entries.length === 0)
21296
21639
  return;
21297
- const relativePath2 = join19(sourceDir, HISTORY_FILE3).replaceAll("\\", "/");
21640
+ const relativePath2 = join20(sourceDir, HISTORY_FILE3).replaceAll("\\", "/");
21298
21641
  const absolutePath = resolveInside(rootPath, relativePath2);
21299
- await mkdir9(dirname14(absolutePath), { recursive: true });
21642
+ await mkdir10(dirname15(absolutePath), { recursive: true });
21300
21643
  const lines = entries.flatMap((entry) => entry.id === undefined || entry.bump === undefined ? [] : [
21301
21644
  JSON.stringify({
21302
21645
  appliedAt,
@@ -21319,9 +21662,9 @@ async function appendHistory(rootPath, sourceDir, entries, appliedAt, files) {
21319
21662
  async function appendReleaseRecord(rootPath, sourceDir, plan, appliedAt, files) {
21320
21663
  if (plan.scopes.length === 0)
21321
21664
  return;
21322
- const relativePath2 = join19(sourceDir, RELEASES_FILE).replaceAll("\\", "/");
21665
+ const relativePath2 = join20(sourceDir, RELEASES_FILE).replaceAll("\\", "/");
21323
21666
  const absolutePath = resolveInside(rootPath, relativePath2);
21324
- await mkdir9(dirname14(absolutePath), { recursive: true });
21667
+ await mkdir10(dirname15(absolutePath), { recursive: true });
21325
21668
  await appendFile2(absolutePath, `${JSON.stringify({
21326
21669
  appliedAt,
21327
21670
  baseline: { hashSchema: SOURCE_HASH_SCHEMA, kind: "source-hashes" },
@@ -21341,12 +21684,12 @@ async function appendReleaseRecord(rootPath, sourceDir, plan, appliedAt, files)
21341
21684
  }
21342
21685
  async function snapshotReleaseFiles(rootPath, sourceDir, entries, includeReleaseState) {
21343
21686
  const paths = new Set([
21344
- join19(sourceDir, HISTORY_FILE3).replaceAll("\\", "/"),
21687
+ join20(sourceDir, HISTORY_FILE3).replaceAll("\\", "/"),
21345
21688
  ...entries.map((entry) => entry.path)
21346
21689
  ]);
21347
21690
  if (includeReleaseState) {
21348
- paths.add(join19(sourceDir, STATE_FILE2).replaceAll("\\", "/"));
21349
- paths.add(join19(sourceDir, RELEASES_FILE).replaceAll("\\", "/"));
21691
+ paths.add(join20(sourceDir, STATE_FILE2).replaceAll("\\", "/"));
21692
+ paths.add(join20(sourceDir, RELEASES_FILE).replaceAll("\\", "/"));
21350
21693
  }
21351
21694
  const snapshots = [];
21352
21695
  for (const path2 of [...paths].sort(compareStrings)) {
@@ -21365,13 +21708,13 @@ async function restoreSnapshots(rootPath, snapshots) {
21365
21708
  await rm6(absolutePath, { force: true });
21366
21709
  continue;
21367
21710
  }
21368
- await mkdir9(dirname14(absolutePath), { recursive: true });
21369
- await writeFile10(absolutePath, snapshot.content);
21711
+ await mkdir10(dirname15(absolutePath), { recursive: true });
21712
+ await writeFile11(absolutePath, snapshot.content);
21370
21713
  }
21371
21714
  }
21372
21715
  async function exists13(path2) {
21373
21716
  try {
21374
- await stat16(path2);
21717
+ await stat17(path2);
21375
21718
  return true;
21376
21719
  } catch (error) {
21377
21720
  if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
@@ -21411,7 +21754,7 @@ function releaseIdFor(scopes) {
21411
21754
  }
21412
21755
  async function readReleaseRecords(rootPath, options = {}) {
21413
21756
  const sourceDir = options.sourceDir ?? ".skillset";
21414
- const path2 = join19(sourceDir, RELEASES_FILE).replaceAll("\\", "/");
21757
+ const path2 = join20(sourceDir, RELEASES_FILE).replaceAll("\\", "/");
21415
21758
  const absolutePath = resolveInside(rootPath, path2);
21416
21759
  if (!await exists13(absolutePath))
21417
21760
  return [];
@@ -21437,7 +21780,7 @@ async function readReleaseRecords(rootPath, options = {}) {
21437
21780
  }
21438
21781
  async function readReleaseAmendments(rootPath, options = {}) {
21439
21782
  const sourceDir = options.sourceDir ?? ".skillset";
21440
- const path2 = join19(sourceDir, RELEASE_AMENDMENTS_FILE).replaceAll("\\", "/");
21783
+ const path2 = join20(sourceDir, RELEASE_AMENDMENTS_FILE).replaceAll("\\", "/");
21441
21784
  const absolutePath = resolveInside(rootPath, path2);
21442
21785
  if (!await exists13(absolutePath))
21443
21786
  return new Map;
@@ -21539,9 +21882,9 @@ function isChangeBump(value) {
21539
21882
  return value === "major" || value === "minor" || value === "patch" || value === "none";
21540
21883
  }
21541
21884
  // apps/skillset/src/test-runner.ts
21542
- import { cp, mkdir as mkdir10, mkdtemp as mkdtemp4, readFile as readFile21, rm as rm7, stat as stat17, writeFile as writeFile11 } from "fs/promises";
21885
+ import { cp, mkdir as mkdir11, mkdtemp as mkdtemp4, readFile as readFile21, rm as rm7, stat as stat18, writeFile as writeFile12 } from "fs/promises";
21543
21886
  import { createHash as createHash8, randomBytes as randomBytes2 } from "crypto";
21544
- import { dirname as dirname15, join as join20, relative as relative16, resolve as resolve9 } from "path";
21887
+ import { dirname as dirname16, join as join21, relative as relative16, resolve as resolve9 } from "path";
21545
21888
  import { tmpdir as tmpdir3 } from "os";
21546
21889
  var TEST_BUILD_DIR = "build/tests";
21547
21890
  var TEST_SCHEMA = 1;
@@ -21559,23 +21902,17 @@ async function runSkillsetTest(rootPath, name, options = {}) {
21559
21902
  targetFilter: declaration.targets
21560
21903
  };
21561
21904
  const runId = makeRunId(declaration.name);
21562
- const buildRoot = resolveInside(rootPath, join20(sourceDir, TEST_BUILD_DIR));
21563
- const runsRoot = join20(buildRoot, "runs");
21564
- const runPath = join20(runsRoot, runId);
21565
- const workspacePath = join20(runPath, "workspace");
21566
- const stagingRoot = await mkdtemp4(join20(tmpdir3(), "skillset-test-"));
21567
- const stagingWorkspacePath = join20(stagingRoot, "workspace");
21568
- const stagingSourcePath = join20(stagingWorkspacePath, sourceDir);
21569
- const sourcePath = resolveInside(rootPath, sourceDir);
21570
- const workspaceLockPath = resolveInside(rootPath, ".skillset.lock");
21571
- const ignoredSourceBuildPath = resolveInside(rootPath, join20(sourceDir, "build"));
21905
+ const buildRoot = resolveInside(rootPath, testBuildRoot(sourceDir));
21906
+ const runsRoot = join21(buildRoot, "runs");
21907
+ const runPath = join21(runsRoot, runId);
21908
+ const workspacePath = join21(runPath, "workspace");
21909
+ const stagingRoot = await mkdtemp4(join21(tmpdir3(), "skillset-test-"));
21910
+ const stagingWorkspacePath = join21(stagingRoot, "workspace");
21911
+ const workspaceLockPath = resolveInside(rootPath, "skillset.lock");
21572
21912
  try {
21573
- await mkdir10(stagingWorkspacePath, { recursive: true });
21574
- await cp(sourcePath, stagingSourcePath, {
21575
- filter: (path2) => !isSameOrInside2(ignoredSourceBuildPath, path2),
21576
- recursive: true
21577
- });
21578
- await copyIfExists(workspaceLockPath, join20(stagingWorkspacePath, ".skillset.lock"));
21913
+ await mkdir11(stagingWorkspacePath, { recursive: true });
21914
+ await copyTestSource(graph, stagingWorkspacePath);
21915
+ await copyIfExists(workspaceLockPath, join21(stagingWorkspacePath, "skillset.lock"));
21579
21916
  await copyWorkspaceManagedFiles(rootPath, stagingWorkspacePath, workspaceLockPath, sourceDir);
21580
21917
  const assertions = [];
21581
21918
  let generatedFiles = 0;
@@ -21595,13 +21932,13 @@ async function runSkillsetTest(rootPath, name, options = {}) {
21595
21932
  if (assertions.every((assertion) => assertion.ok)) {
21596
21933
  await validateActivationExpectations(stagingWorkspacePath, declaration, buildOptions);
21597
21934
  }
21598
- await mkdir10(runPath, { recursive: true });
21935
+ await mkdir11(runPath, { recursive: true });
21599
21936
  await cp(stagingWorkspacePath, workspacePath, { recursive: true });
21600
21937
  const activationPath = await writeActivationProbes(runPath, declaration);
21601
21938
  const ok = assertions.every((assertion) => assertion.ok);
21602
- const reportPath = join20(runPath, "report.json");
21603
- const reportMarkdownPath = join20(runPath, "report.md");
21604
- const latestPath = join20(buildRoot, "latest");
21939
+ const reportPath = join21(runPath, "report.json");
21940
+ const reportMarkdownPath = join21(runPath, "report.md");
21941
+ const latestPath = join21(buildRoot, "latest");
21605
21942
  const activationReport = activationPath === undefined ? {} : {
21606
21943
  activation: {
21607
21944
  path: relative16(rootPath, activationPath),
@@ -21620,8 +21957,8 @@ async function runSkillsetTest(rootPath, name, options = {}) {
21620
21957
  ...activationReport,
21621
21958
  workspacePath: relative16(rootPath, workspacePath)
21622
21959
  };
21623
- await writeFile11(reportPath, renderValidatedJson(report, relative16(rootPath, reportPath)), "utf8");
21624
- await writeFile11(reportMarkdownPath, renderMarkdownReport(report), "utf8");
21960
+ await writeFile12(reportPath, renderValidatedJson(report, relative16(rootPath, reportPath)), "utf8");
21961
+ await writeFile12(reportMarkdownPath, renderMarkdownReport(report), "utf8");
21625
21962
  await refreshLatest(buildRoot, runPath, latestPath, report, rootPath);
21626
21963
  return {
21627
21964
  assertions,
@@ -21866,11 +22203,11 @@ function activationExpectationCandidatePaths(graph, target, expect) {
21866
22203
  }
21867
22204
  if (expect.kind === "agent") {
21868
22205
  const projectRoot = targetProjectRoot4(graph, target);
21869
- return graph.projectAgents.filter((agent) => agent.name === expect.name || agent.outputName === expect.name).map((agent) => join20(projectRoot, "agents", `${agent.outputName}.${target === "claude" ? "md" : "toml"}`));
22206
+ return graph.projectAgents.filter((agent) => agent.name === expect.name || agent.outputName === expect.name).map((agent) => join21(projectRoot, "agents", `${agent.outputName}.${target === "claude" ? "md" : "toml"}`));
21870
22207
  }
21871
22208
  return [
21872
- ...graph.standaloneSkills.filter((skill) => skill.id === expect.name).map((skill) => join20(graph.root.outputs.skills[target], dirname15(skill.relativePath), "SKILL.md")),
21873
- ...graph.plugins.flatMap((plugin) => plugin.skills.filter((skill) => skill.id === expect.name).map((skill) => join20(graph.root.outputs.plugins[target], "plugins", plugin.id, dirname15(skill.relativePath), "SKILL.md")))
22209
+ ...graph.standaloneSkills.filter((skill) => skill.id === expect.name).map((skill) => join21(graph.root.outputs.skills[target], dirname16(skill.relativePath), "SKILL.md")),
22210
+ ...graph.plugins.flatMap((plugin) => plugin.skills.filter((skill) => skill.id === expect.name).map((skill) => join21(graph.root.outputs.plugins[target], "plugins", plugin.id, dirname16(skill.relativePath), "SKILL.md")))
21874
22211
  ];
21875
22212
  }
21876
22213
  function targetProjectRoot4(graph, target) {
@@ -21879,22 +22216,22 @@ function targetProjectRoot4(graph, target) {
21879
22216
  async function writeActivationProbes(runPath, declaration) {
21880
22217
  if (declaration.activationProbes.length === 0)
21881
22218
  return;
21882
- const activationRoot = join20(runPath, "activation");
22219
+ const activationRoot = join21(runPath, "activation");
21883
22220
  for (const target of declaration.targets) {
21884
22221
  const probes = declaration.activationProbes.filter((probe) => probe.targets.includes(target));
21885
22222
  if (probes.length === 0)
21886
22223
  continue;
21887
- const targetRoot = join20(activationRoot, target);
21888
- await mkdir10(targetRoot, { recursive: true });
22224
+ const targetRoot = join21(activationRoot, target);
22225
+ await mkdir11(targetRoot, { recursive: true });
21889
22226
  const records = probes.map((probe) => activationProbeRecord(probe, target));
21890
- await writeFile11(join20(targetRoot, "probes.json"), renderValidatedJson({
22227
+ await writeFile12(join21(targetRoot, "probes.json"), renderValidatedJson({
21891
22228
  probes: records,
21892
22229
  schemaVersion: TEST_SCHEMA,
21893
22230
  target
21894
22231
  }, `activation ${target} probes`), "utf8");
21895
22232
  for (const record of records) {
21896
22233
  const name = typeof record.name === "string" ? record.name : "probe";
21897
- await writeFile11(join20(targetRoot, `${name}.md`), renderActivationProbeMarkdown(record), "utf8");
22234
+ await writeFile12(join21(targetRoot, `${name}.md`), renderActivationProbeMarkdown(record), "utf8");
21898
22235
  }
21899
22236
  }
21900
22237
  return activationRoot;
@@ -21951,14 +22288,14 @@ async function refreshLatest(buildRoot, runPath, latestPath, report, rootPath) {
21951
22288
  const latest = {
21952
22289
  name: report.name,
21953
22290
  ok: report.ok,
21954
- reportPath: relative16(rootPath, join20(latestPath, "report.json")),
22291
+ reportPath: relative16(rootPath, join21(latestPath, "report.json")),
21955
22292
  runId: report.runId,
21956
22293
  runPath: relative16(rootPath, runPath),
21957
22294
  schemaVersion: TEST_SCHEMA,
21958
22295
  source: report.source,
21959
- workspacePath: relative16(rootPath, join20(latestPath, "workspace"))
22296
+ workspacePath: relative16(rootPath, join21(latestPath, "workspace"))
21960
22297
  };
21961
- await writeFile11(join20(buildRoot, "latest.json"), renderValidatedJson(latest, relative16(rootPath, join20(buildRoot, "latest.json"))), "utf8");
22298
+ await writeFile12(join21(buildRoot, "latest.json"), renderValidatedJson(latest, relative16(rootPath, join21(buildRoot, "latest.json"))), "utf8");
21962
22299
  }
21963
22300
  function renderMarkdownReport(report) {
21964
22301
  const assertions = Array.isArray(report.assertions) ? report.assertions : [];
@@ -21995,7 +22332,7 @@ function activationProbeCount(report) {
21995
22332
  }
21996
22333
  async function pathExists2(path2) {
21997
22334
  try {
21998
- await stat17(path2);
22335
+ await stat18(path2);
21999
22336
  return true;
22000
22337
  } catch {
22001
22338
  return false;
@@ -22004,8 +22341,21 @@ async function pathExists2(path2) {
22004
22341
  async function copyIfExists(sourcePath, targetPath) {
22005
22342
  if (!await pathExists2(sourcePath))
22006
22343
  return;
22007
- await mkdir10(dirname15(targetPath), { recursive: true });
22008
- await cp(sourcePath, targetPath);
22344
+ await mkdir11(dirname16(targetPath), { recursive: true });
22345
+ await cp(sourcePath, targetPath, { recursive: true });
22346
+ }
22347
+ async function copyTestSource(graph, stagingWorkspacePath) {
22348
+ const ignoredSourceBuildPath = resolveInside(graph.rootPath, sourceBuildRoot(graph.sourceDir));
22349
+ if (graph.sourceDir !== ".") {
22350
+ await cp(graph.sourcePath, join21(stagingWorkspacePath, graph.sourceDir), {
22351
+ filter: (path2) => !isSameOrInside2(ignoredSourceBuildPath, path2),
22352
+ recursive: true
22353
+ });
22354
+ return;
22355
+ }
22356
+ await copyIfExists(graph.rootConfigPath, join21(stagingWorkspacePath, "skillset.yaml"));
22357
+ await copyIfExists(graph.sourceRootPath, join21(stagingWorkspacePath, graph.sourceRoot));
22358
+ await copyIfExists(resolveInside(graph.rootPath, "changes"), join21(stagingWorkspacePath, "changes"));
22009
22359
  }
22010
22360
  async function copyWorkspaceManagedFiles(rootPath, stagingWorkspacePath, workspaceLockPath, sourceDir) {
22011
22361
  if (!await pathExists2(workspaceLockPath))
@@ -22018,7 +22368,7 @@ async function copyWorkspaceManagedFiles(rootPath, stagingWorkspacePath, workspa
22018
22368
  }
22019
22369
  if (!isJsonRecord(lock) || !Array.isArray(lock.items))
22020
22370
  return;
22021
- const ignoredSourceBuildPath = resolveInside(rootPath, join20(sourceDir, "build"));
22371
+ const ignoredSourceBuildPath = resolveInside(rootPath, sourceBuildRoot(sourceDir));
22022
22372
  for (const item of lock.items) {
22023
22373
  if (!isJsonRecord(item) || !Array.isArray(item.files))
22024
22374
  continue;
@@ -22028,10 +22378,16 @@ async function copyWorkspaceManagedFiles(rootPath, stagingWorkspacePath, workspa
22028
22378
  const sourcePath = resolveInside(rootPath, file);
22029
22379
  if (isSameOrInside2(ignoredSourceBuildPath, sourcePath))
22030
22380
  continue;
22031
- await copyIfExists(sourcePath, join20(stagingWorkspacePath, file));
22381
+ await copyIfExists(sourcePath, join21(stagingWorkspacePath, file));
22032
22382
  }
22033
22383
  }
22034
22384
  }
22385
+ function testBuildRoot(sourceDir) {
22386
+ return sourceDir === "." ? join21(".skillset", TEST_BUILD_DIR) : join21(sourceDir, TEST_BUILD_DIR);
22387
+ }
22388
+ function sourceBuildRoot(sourceDir) {
22389
+ return sourceDir === "." ? ".skillset/build" : join21(sourceDir, "build");
22390
+ }
22035
22391
  function makeRunId(name) {
22036
22392
  const stamp = new Date().toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
22037
22393
  const digest2 = createHash8("sha256").update(`${name}:${stamp}:${randomBytes2(8).toString("hex")}`).digest("hex").slice(0, 8);
@@ -22048,8 +22404,10 @@ function messageFor(error) {
22048
22404
  // apps/skillset/src/cli-core.ts
22049
22405
  var USAGE = [
22050
22406
  "usage: skillset build [--yes|--dry-run] [--updated|--all] [--isolated] [--scope <scope>] [--root <path>] [--source <dir>] [--dist <dir>]",
22051
- " skillset <check|diff> [--updated|--all] [--isolated] [--scope <scope>] [--root <path>] [--source <dir>] [--dist <dir>]",
22052
- " skillset <lint|list> [--updated|--all] [--scope <scope>] [--root <path>] [--source <dir>] [--dist <dir>]",
22407
+ " skillset verify [--updated|--all] [--isolated] [--scope <scope>] [--root <path>] [--source <dir>] [--dist <dir>]",
22408
+ " skillset diff [--updated|--all] [--isolated] [--scope <scope>] [--root <path>] [--source <dir>] [--dist <dir>]",
22409
+ " skillset <check|lint> [--root <path>] [--source <dir>]",
22410
+ " skillset list [--updated|--all] [--scope <scope>] [--root <path>] [--source <dir>] [--dist <dir>]",
22053
22411
  " skillset doctor [--json] [--updated|--all] [--scope <scope>] [--root <path>] [--source <dir>] [--dist <dir>]",
22054
22412
  " skillset ci [--fix] [--since <ref>] [--report <path>] [--root <path>] [--source <dir>] [--dist <dir>]",
22055
22413
  " skillset change status [--since <ref>] [--root <path>] [--source <dir>] [--dist <dir>]",
@@ -22075,6 +22433,7 @@ var USAGE = [
22075
22433
  " skillset adopt <path> [--yes|--dry-run] [--targets claude,codex] [--root <path>]",
22076
22434
  " skillset init [path] [--yes|--dry-run] [--targets claude,codex] [--include ci] [--name <name>] [--root <path>]",
22077
22435
  " skillset create [path|--global] [--yes|--dry-run] [--targets claude,codex] [--include ci] [--name <name>] [--root <path>]",
22436
+ " skillset new <skill|agent|hook> [name] [--id <id>] [--name <name>] [--in <container>] [--scope repo] [--preset <preset>] [--yes|--dry-run] [--root <path>] [--source <dir>]",
22078
22437
  " skillset explain <path> [--json] [--scope <scope>] [--root <path>] [--source <dir>]",
22079
22438
  " skillset import <path> [--kind <skill|skills|plugin|plugins>] [--from <provider>] [--name <name>] [--root <path>] [--source <dir>]",
22080
22439
  " skillset import <claude|codex|agents> [--root <path>] [--source <dir>]"
@@ -22114,6 +22473,12 @@ async function runCli(rawArgs = process.argv.slice(2), invokedName = basename9(p
22114
22473
  importName,
22115
22474
  importProvider,
22116
22475
  jsonOutput,
22476
+ newContainer,
22477
+ newId,
22478
+ newKind,
22479
+ newName,
22480
+ newPresets,
22481
+ newScope,
22117
22482
  options,
22118
22483
  rootPath,
22119
22484
  rootExplicit,
@@ -22156,8 +22521,8 @@ async function runCli(rawArgs = process.argv.slice(2), invokedName = basename9(p
22156
22521
  });
22157
22522
  if (ciReportPath !== undefined) {
22158
22523
  const reportPath = resolve10(ciReportPath);
22159
- await mkdir11(dirname16(reportPath), { recursive: true });
22160
- await writeFile12(reportPath, renderCiReportMarkdown(report));
22524
+ await mkdir12(dirname17(reportPath), { recursive: true });
22525
+ await writeFile13(reportPath, renderCiReportMarkdown(report));
22161
22526
  }
22162
22527
  printCiReport(report);
22163
22528
  if (!report.ok)
@@ -22334,6 +22699,16 @@ async function runCli(rawArgs = process.argv.slice(2), invokedName = basename9(p
22334
22699
  console.log(`skillset: linted ${result3.checkedSkills} source skills`);
22335
22700
  return;
22336
22701
  }
22702
+ if (command === "check") {
22703
+ const result3 = await lintSkillset(rootPath, options);
22704
+ for (const issue of result3.issues) {
22705
+ if (issue.severity !== "warn")
22706
+ continue;
22707
+ console.log(` warn: ${issue.path}: ${issue.code}: ${issue.message}`);
22708
+ }
22709
+ console.log(`skillset: checked ${result3.checkedSkills} source skills`);
22710
+ return;
22711
+ }
22337
22712
  if (command === "adopt") {
22338
22713
  const writeMode = yes && !dryRun;
22339
22714
  const report = await adoptSkillset(importPath === undefined ? rootPath : importPath, {
@@ -22395,6 +22770,25 @@ async function runCli(rawArgs = process.argv.slice(2), invokedName = basename9(p
22395
22770
  console.warn(` warning: ${warning}`);
22396
22771
  return;
22397
22772
  }
22773
+ if (command === "new") {
22774
+ if (newKind === undefined)
22775
+ throw new Error("skillset: expected new kind skill, agent, or hook");
22776
+ const report = await scaffoldSourceUnit(rootPath, {
22777
+ ...newContainer === undefined ? {} : { container: newContainer },
22778
+ ...newId === undefined ? {} : { id: newId },
22779
+ kind: newKind,
22780
+ ...newName === undefined ? {} : { displayName: newName },
22781
+ ...importPath === undefined ? {} : { name: importPath },
22782
+ ...newPresets === undefined ? {} : { presets: newPresets },
22783
+ ...newScope === undefined ? {} : { scope: newScope },
22784
+ skillsetOptions: options,
22785
+ write: yes && !dryRun
22786
+ });
22787
+ printNewSourceReport(report, dryRun ? "dry run" : yes ? "written" : "write confirmation required");
22788
+ if (!yes || dryRun)
22789
+ console.log("skillset: rerun new with --yes to write source files");
22790
+ return;
22791
+ }
22398
22792
  if (command === "diff") {
22399
22793
  const result3 = await diffSkillsetResult(rootPath, options);
22400
22794
  printDiagnostics(result3.diagnostics);
@@ -22563,9 +22957,9 @@ async function runCli(rawArgs = process.argv.slice(2), invokedName = basename9(p
22563
22957
  }
22564
22958
  return;
22565
22959
  }
22566
- const result2 = await checkSkillsetResult(rootPath, options);
22960
+ const result2 = await verifySkillsetResult(rootPath, options);
22567
22961
  printDiagnostics(result2.diagnostics);
22568
- console.log(`skillset: checked ${result2.data.checkedFiles} generated files`);
22962
+ console.log(`skillset: verified ${result2.data.checkedFiles} generated files`);
22569
22963
  if (!result2.ok) {
22570
22964
  console.error(`skillset: generated output is not current`);
22571
22965
  for (const failure of result2.data.failures)
@@ -22930,6 +23324,18 @@ function printSetupReport(result2, reason) {
22930
23324
  console.log(`skillset: ${result2.kind} ${details.join(", ")} (${reason})`);
22931
23325
  console.log(` root: ${result2.rootPath}`);
22932
23326
  }
23327
+ function printNewSourceReport(result2, reason) {
23328
+ for (const file of result2.files)
23329
+ console.log(` + ${file.path}`);
23330
+ const action = result2.write ? "created" : "planned";
23331
+ console.log(`skillset: ${action} ${result2.kind} ${result2.id} (${reason})`);
23332
+ console.log(` source: ${result2.sourceRoot}`);
23333
+ console.log(` name: ${result2.displayName}`);
23334
+ if (result2.write) {
23335
+ console.log(" next: skillset check");
23336
+ console.log(" next: skillset verify");
23337
+ }
23338
+ }
22933
23339
  function printRestoreReport(report) {
22934
23340
  const mode = report.write ? "restored" : "restore preview";
22935
23341
  console.log(`skillset: ${mode} ${report.restoredPaths.length} file${report.restoredPaths.length === 1 ? "" : "s"} from backup ${report.runId}`);
@@ -22984,8 +23390,8 @@ function printRenderResult(outcome) {
22984
23390
  }
22985
23391
  function parseArgs(args) {
22986
23392
  const command = args[0];
22987
- 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 !== "suggest-source" && command !== "test") {
22988
- throw new Error(`skillset: expected command adopt, build, change, check, ci, create, diff, distribute, doctor, explain, features, hooks, import, init, lint, list, release, restore, suggest-source, or test
23393
+ 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 !== "new" && command !== "release" && command !== "restore" && command !== "suggest-source" && command !== "test" && command !== "verify") {
23394
+ throw new Error(`skillset: expected command adopt, build, change, check, ci, create, diff, distribute, doctor, explain, features, hooks, import, init, lint, list, new, release, restore, suggest-source, test, or verify
22989
23395
  ` + USAGE);
22990
23396
  }
22991
23397
  let changeSubcommand;
@@ -23016,6 +23422,12 @@ function parseArgs(args) {
23016
23422
  let importPath;
23017
23423
  let importProvider;
23018
23424
  let jsonOutput = false;
23425
+ let newContainer;
23426
+ let newId;
23427
+ let newKind;
23428
+ let newName;
23429
+ let newPresets;
23430
+ let newScope;
23019
23431
  let rootPath = process.cwd();
23020
23432
  let rootExplicit = false;
23021
23433
  let sourceDir;
@@ -23139,6 +23551,19 @@ function parseArgs(args) {
23139
23551
  index += 1;
23140
23552
  }
23141
23553
  }
23554
+ if (command === "new") {
23555
+ const rawKind = args[index];
23556
+ if (!isNewSourceKind(rawKind)) {
23557
+ throw new Error("skillset: expected new kind skill, agent, or hook");
23558
+ }
23559
+ newKind = rawKind;
23560
+ index += 1;
23561
+ const rawName = args[index];
23562
+ if (rawName !== undefined && !rawName.startsWith("--")) {
23563
+ importPath = rawName;
23564
+ index += 1;
23565
+ }
23566
+ }
23142
23567
  for (;index < args.length; index += 1) {
23143
23568
  const arg = args[index];
23144
23569
  if (arg === undefined)
@@ -23146,7 +23571,7 @@ function parseArgs(args) {
23146
23571
  const equalsIndex = arg.indexOf("=");
23147
23572
  const flag = equalsIndex === -1 ? arg : arg.slice(0, equalsIndex);
23148
23573
  const inlineValue = equalsIndex === -1 ? undefined : arg.slice(equalsIndex + 1);
23149
- if (flag !== "--root" && flag !== "--source" && flag !== "--dist" && flag !== "--name" && flag !== "--kind" && flag !== "--from" && flag !== "--append" && flag !== "--bump" && flag !== "--group" && flag !== "--reason" && flag !== "--reason-file" && flag !== "--ref" && flag !== "--since" && flag !== "--staged" && flag !== "--yes" && flag !== "--dry-run" && flag !== "--updated" && flag !== "--all" && flag !== "--isolated" && flag !== "--scope" && flag !== "--global" && flag !== "--targets" && flag !== "--include" && flag !== "--fix" && flag !== "--report" && flag !== "--json" && flag !== "--runner" && flag !== "--target" && flag !== "--agent-runtime" && flag !== "--pre-commit" && flag !== "--pre-push" && flag !== "--write") {
23574
+ if (flag !== "--root" && flag !== "--source" && flag !== "--dist" && flag !== "--id" && flag !== "--name" && flag !== "--in" && flag !== "--kind" && flag !== "--from" && flag !== "--preset" && flag !== "--append" && flag !== "--bump" && flag !== "--group" && flag !== "--reason" && flag !== "--reason-file" && flag !== "--ref" && flag !== "--since" && flag !== "--staged" && flag !== "--yes" && flag !== "--dry-run" && flag !== "--updated" && flag !== "--all" && flag !== "--isolated" && flag !== "--scope" && flag !== "--global" && flag !== "--targets" && flag !== "--include" && flag !== "--fix" && flag !== "--report" && flag !== "--json" && flag !== "--runner" && flag !== "--target" && flag !== "--agent-runtime" && flag !== "--pre-commit" && flag !== "--pre-push" && flag !== "--write") {
23150
23575
  throw new Error(`skillset: unknown option ${arg}`);
23151
23576
  }
23152
23577
  if (flag === "--yes" || flag === "--dry-run" || flag === "--updated" || flag === "--all" || flag === "--isolated" || flag === "--append" || flag === "--staged" || flag === "--global" || flag === "--fix" || flag === "--json" || flag === "--agent-runtime" || flag === "--pre-commit" || flag === "--pre-push" || flag === "--write") {
@@ -23211,6 +23636,8 @@ function parseArgs(args) {
23211
23636
  throw new Error(`skillset: change ${changeSubcommand} is a whole-source command; --scope is not supported`);
23212
23637
  } else if (command === "change") {
23213
23638
  throw new Error("skillset: --scope is only supported with change add source-unit entries");
23639
+ } else if (command === "new") {
23640
+ newScope = readNewSourceScope(value);
23214
23641
  } else {
23215
23642
  scopes = readBuildScopes(value);
23216
23643
  }
@@ -23243,8 +23670,18 @@ function parseArgs(args) {
23243
23670
  setupTargets = readSetupTargets(value);
23244
23671
  if (flag === "--include")
23245
23672
  setupIncludes = mergeSetupIncludes(setupIncludes, value);
23246
- if (flag === "--name")
23247
- importName = value;
23673
+ if (flag === "--id")
23674
+ newId = value;
23675
+ if (flag === "--in")
23676
+ newContainer = value;
23677
+ if (flag === "--name") {
23678
+ if (command === "new")
23679
+ newName = value;
23680
+ else
23681
+ importName = value;
23682
+ }
23683
+ if (flag === "--preset")
23684
+ newPresets = [...newPresets ?? [], value];
23248
23685
  if (flag === "--kind") {
23249
23686
  if (!isImportKind(value)) {
23250
23687
  throw new Error("skillset: expected --kind skill, skills, plugin, or plugins");
@@ -23307,6 +23744,13 @@ function parseArgs(args) {
23307
23744
  ...scopes === undefined ? {} : { scopes }
23308
23745
  });
23309
23746
  validateJsonFlags(command, jsonOutput);
23747
+ validateSourceDiagnosticFlags(command, {
23748
+ ...buildMode === undefined ? {} : { buildMode },
23749
+ ...distDir === undefined ? {} : { distDir },
23750
+ dryRun,
23751
+ ...scopes === undefined ? {} : { scopes },
23752
+ yes
23753
+ });
23310
23754
  validateIsolatedFlag(command, isolated);
23311
23755
  validateDistributionFlags(command, {
23312
23756
  ...buildMode === undefined ? {} : { buildMode },
@@ -23348,6 +23792,18 @@ function parseArgs(args) {
23348
23792
  write: sourceSuggestionWrite,
23349
23793
  yes
23350
23794
  });
23795
+ validateNewSourceFlags(command, {
23796
+ ...buildMode === undefined ? {} : { buildMode },
23797
+ ...distDir === undefined ? {} : { distDir },
23798
+ ...newContainer === undefined ? {} : { container: newContainer },
23799
+ ...newId === undefined ? {} : { id: newId },
23800
+ ...importKind === undefined ? {} : { importKind },
23801
+ ...importProvider === undefined ? {} : { importProvider },
23802
+ ...newKind === undefined ? {} : { kind: newKind },
23803
+ ...newName === undefined ? {} : { name: newName },
23804
+ ...newPresets === undefined ? {} : { presets: newPresets },
23805
+ ...newScope === undefined ? {} : { scope: newScope }
23806
+ });
23351
23807
  const options = {
23352
23808
  ...buildMode === undefined ? {} : { buildMode },
23353
23809
  ...scopes === undefined ? {} : { scopes },
@@ -23383,6 +23839,12 @@ function parseArgs(args) {
23383
23839
  ...importPath === undefined ? {} : { importPath },
23384
23840
  ...importProvider === undefined ? {} : { importProvider },
23385
23841
  jsonOutput,
23842
+ ...newContainer === undefined ? {} : { newContainer },
23843
+ ...newId === undefined ? {} : { newId },
23844
+ ...newKind === undefined ? {} : { newKind },
23845
+ ...newName === undefined ? {} : { newName },
23846
+ ...newPresets === undefined ? {} : { newPresets },
23847
+ ...newScope === undefined ? {} : { newScope },
23386
23848
  options,
23387
23849
  ...releaseSubcommand === undefined ? {} : { releaseSubcommand },
23388
23850
  ...releaseReason === undefined ? {} : { releaseReason },
@@ -23403,6 +23865,14 @@ function isReleaseSubcommand(value) {
23403
23865
  function isChangeSubcommand(value) {
23404
23866
  return value === "add" || value === "amend" || value === "check" || value === "history" || value === "list" || value === "reason" || value === "show" || value === "status";
23405
23867
  }
23868
+ function isNewSourceKind(value) {
23869
+ return value === "agent" || value === "hook" || value === "skill";
23870
+ }
23871
+ function readNewSourceScope(value) {
23872
+ if (value === "repo")
23873
+ return value;
23874
+ throw new Error("skillset: new currently supports only --scope repo");
23875
+ }
23406
23876
  function readChangeScopes(value) {
23407
23877
  const scopes = value.split(",").map((scope) => scope.trim()).filter((scope) => scope.length > 0);
23408
23878
  if (scopes.length === 0)
@@ -23497,6 +23967,23 @@ function validateTestFlags(command, test2) {
23497
23967
  throw new Error("skillset: build/write options are not supported with test; test output always writes under .skillset/build/tests");
23498
23968
  }
23499
23969
  }
23970
+ function validateSourceDiagnosticFlags(command, sourceCheck) {
23971
+ if (command !== "check" && command !== "lint")
23972
+ return;
23973
+ const label = `skillset ${command}`;
23974
+ if (sourceCheck.buildMode !== undefined) {
23975
+ throw new Error(`${label} does not support --updated or --all; it checks source diagnostics`);
23976
+ }
23977
+ if (sourceCheck.scopes !== undefined) {
23978
+ throw new Error(`${label} does not support --scope; it checks source diagnostics`);
23979
+ }
23980
+ if (sourceCheck.distDir !== undefined) {
23981
+ throw new Error(`${label} does not support --dist; it checks source diagnostics`);
23982
+ }
23983
+ if (sourceCheck.dryRun || sourceCheck.yes) {
23984
+ throw new Error(`${label} is read-only and does not support --yes or --dry-run`);
23985
+ }
23986
+ }
23500
23987
  function validateRestoreFlags(command, restore) {
23501
23988
  if (command !== "restore")
23502
23989
  return;
@@ -23523,6 +24010,24 @@ function validateSuggestSourceFlags(command, suggestion) {
23523
24010
  if (suggestion.write && !suggestion.yes)
23524
24011
  throw new Error("skillset: suggest-source --write requires --yes");
23525
24012
  }
24013
+ function validateNewSourceFlags(command, source2) {
24014
+ const hasNewFlag = source2.container !== undefined || source2.id !== undefined || source2.kind !== undefined || source2.name !== undefined || source2.presets !== undefined || source2.scope !== undefined;
24015
+ if (hasNewFlag && command !== "new") {
24016
+ throw new Error("skillset: new options are only supported with new");
24017
+ }
24018
+ if (command !== "new")
24019
+ return;
24020
+ if (source2.buildMode !== undefined)
24021
+ throw new Error("skillset: --updated and --all are not supported with new");
24022
+ if (source2.distDir !== undefined)
24023
+ throw new Error("skillset: --dist is not supported with new");
24024
+ if (source2.importKind !== undefined)
24025
+ throw new Error("skillset: --kind is only supported with import");
24026
+ if (source2.importProvider !== undefined)
24027
+ throw new Error("skillset: --from is only supported with import");
24028
+ if (source2.kind === undefined)
24029
+ throw new Error("skillset: expected new kind skill, agent, or hook");
24030
+ }
23526
24031
  function setBuildMode(current, next) {
23527
24032
  if (current !== undefined && current !== next) {
23528
24033
  throw new Error(`skillset: conflicting build mode flags --${current} and --${next}`);
@@ -23589,9 +24094,9 @@ function readSetupTargets(value) {
23589
24094
  function validateIsolatedFlag(command, isolated) {
23590
24095
  if (!isolated)
23591
24096
  return;
23592
- if (command === "build" || command === "check" || command === "diff")
24097
+ if (command === "build" || command === "diff" || command === "verify")
23593
24098
  return;
23594
- throw new Error("skillset: --isolated is only supported with build, check, or diff");
24099
+ throw new Error("skillset: --isolated is only supported with build, diff, or verify");
23595
24100
  }
23596
24101
  function validateCiFlags(command, ci) {
23597
24102
  if (command !== "ci") {