@promptscript/cli 1.12.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -11760,6 +11760,14 @@ var init_base_formatter = __esm({
11760
11760
  referencesMode() {
11761
11761
  return "none";
11762
11762
  }
11763
+ /**
11764
+ * Default pass-through for injected skill content. Formatters whose target
11765
+ * tools restrict skill frontmatter (e.g. Factory AI) override this hook to
11766
+ * filter unsupported fields before the compiler writes the file.
11767
+ */
11768
+ transformInjectedSkillContent(content) {
11769
+ return content;
11770
+ }
11763
11771
  /**
11764
11772
  * Generate a provenance comment for a reference file.
11765
11773
  */
@@ -15878,7 +15886,7 @@ var init_factory = __esm({
15878
15886
  outputPath: "AGENTS.md"
15879
15887
  }
15880
15888
  };
15881
- FactoryFormatter = class extends MarkdownInstructionFormatter {
15889
+ FactoryFormatter = class _FactoryFormatter extends MarkdownInstructionFormatter {
15882
15890
  constructor() {
15883
15891
  super({
15884
15892
  name: "factory",
@@ -16022,7 +16030,7 @@ var init_factory = __esm({
16022
16030
  const skillName = factoryConfig2.name.replace(/\./g, "-");
16023
16031
  lines.push("---");
16024
16032
  if (factoryConfig2.rawFrontmatter) {
16025
- lines.push(factoryConfig2.rawFrontmatter);
16033
+ lines.push(...this.filterFactoryFrontmatter(factoryConfig2.rawFrontmatter));
16026
16034
  } else {
16027
16035
  lines.push(`name: ${skillName}`);
16028
16036
  lines.push(`description: ${this.yamlString(factoryConfig2.description)}`);
@@ -16307,6 +16315,82 @@ ${lines.join("\n")}
16307
16315
  }
16308
16316
  return handoffs;
16309
16317
  }
16318
+ // ============================================================
16319
+ // Frontmatter Filtering (Factory-specific)
16320
+ // ============================================================
16321
+ /**
16322
+ * Factory AI supported frontmatter fields for skills.
16323
+ * Fields not in this set are stripped to avoid parsing failures.
16324
+ *
16325
+ * @see https://docs.factory.ai/cli/configuration/skills
16326
+ */
16327
+ static FACTORY_SKILL_FRONTMATTER_FIELDS = /* @__PURE__ */ new Set([
16328
+ "name",
16329
+ "description",
16330
+ "user-invocable",
16331
+ "disable-model-invocation"
16332
+ ]);
16333
+ /**
16334
+ * Filter raw YAML frontmatter to only include fields that Factory AI
16335
+ * recognizes in skill SKILL.md files. Strips unsupported fields like
16336
+ * `license`, `metadata`, `compatibility`, `allowed-tools`, and comments,
16337
+ * which cause Factory to fail to load the skill.
16338
+ *
16339
+ * @param rawFrontmatter - Raw YAML frontmatter string (between --- markers)
16340
+ * @returns Array of filtered frontmatter lines
16341
+ */
16342
+ filterFactoryFrontmatter(rawFrontmatter) {
16343
+ const lines = rawFrontmatter.split("\n");
16344
+ const filtered = [];
16345
+ let skipBlock = false;
16346
+ let blockIndent = -1;
16347
+ for (const line of lines) {
16348
+ const trimmed = line.trim();
16349
+ if (trimmed.startsWith("#")) continue;
16350
+ if (skipBlock) {
16351
+ if (trimmed === "") {
16352
+ skipBlock = false;
16353
+ blockIndent = -1;
16354
+ continue;
16355
+ }
16356
+ const currentIndent = line.length - line.trimStart().length;
16357
+ if (currentIndent > blockIndent) continue;
16358
+ skipBlock = false;
16359
+ blockIndent = -1;
16360
+ }
16361
+ if (trimmed === "") continue;
16362
+ const keyMatch = trimmed.match(/^([a-zA-Z][a-zA-Z0-9_-]*):/);
16363
+ if (keyMatch) {
16364
+ const key = keyMatch[1];
16365
+ const indent = line.length - line.trimStart().length;
16366
+ if (!_FactoryFormatter.FACTORY_SKILL_FRONTMATTER_FIELDS.has(key)) {
16367
+ skipBlock = true;
16368
+ blockIndent = indent;
16369
+ continue;
16370
+ }
16371
+ }
16372
+ filtered.push(line);
16373
+ }
16374
+ return filtered;
16375
+ }
16376
+ /**
16377
+ * Filter the frontmatter of an externally-supplied SKILL.md file (such as
16378
+ * the bundled PromptScript skill injected by the compiler) so it only
16379
+ * contains fields Factory AI recognises. The body is preserved verbatim.
16380
+ * Files without a YAML frontmatter block are returned unchanged.
16381
+ */
16382
+ transformInjectedSkillContent(content) {
16383
+ if (!content.startsWith("---")) return content;
16384
+ const closing = content.indexOf("\n---", 3);
16385
+ if (closing === -1) return content;
16386
+ const rawFrontmatter = content.slice(4, closing);
16387
+ const body = content.slice(closing + 4);
16388
+ const filteredLines = this.filterFactoryFrontmatter(rawFrontmatter);
16389
+ const filteredFrontmatter = filteredLines.join("\n");
16390
+ return `---
16391
+ ${filteredFrontmatter}
16392
+ ---${body}`;
16393
+ }
16310
16394
  };
16311
16395
  }
16312
16396
  });
@@ -20789,9 +20873,9 @@ var require_rate_limit = __commonJS({
20789
20873
  let ttl = 0;
20790
20874
  let ttlInSeconds = 0;
20791
20875
  try {
20792
- const res = await new Promise((resolve21, reject) => {
20876
+ const res = await new Promise((resolve22, reject) => {
20793
20877
  store.incr(key, (err, res2) => {
20794
- err ? reject(err) : resolve21(res2);
20878
+ err ? reject(err) : resolve22(res2);
20795
20879
  }, timeWindow, max);
20796
20880
  });
20797
20881
  current = res.current;
@@ -20898,12 +20982,12 @@ var init_health = __esm({
20898
20982
 
20899
20983
  // packages/server/src/source-dirs.ts
20900
20984
  import { readFile as readFile24 } from "fs/promises";
20901
- import { join as join21, dirname as dirname12 } from "path";
20985
+ import { join as join23, dirname as dirname14 } from "path";
20902
20986
  import { parse as parseYaml13 } from "yaml";
20903
20987
  async function readProjectConfig(workspace) {
20904
20988
  for (const filename of CONFIG_FILENAMES) {
20905
20989
  try {
20906
- const raw = await readFile24(join21(workspace, filename), "utf-8");
20990
+ const raw = await readFile24(join23(workspace, filename), "utf-8");
20907
20991
  const config = parseYaml13(raw);
20908
20992
  if (config) return config;
20909
20993
  } catch {
@@ -20914,10 +20998,10 @@ async function readProjectConfig(workspace) {
20914
20998
  async function resolveSourceDir(workspace) {
20915
20999
  for (const filename of CONFIG_FILENAMES) {
20916
21000
  try {
20917
- const raw = await readFile24(join21(workspace, filename), "utf-8");
21001
+ const raw = await readFile24(join23(workspace, filename), "utf-8");
20918
21002
  const config = parseYaml13(raw);
20919
21003
  if (config?.input?.entry) {
20920
- return dirname12(config.input.entry);
21004
+ return dirname14(config.input.entry);
20921
21005
  }
20922
21006
  } catch {
20923
21007
  }
@@ -20931,9 +21015,9 @@ async function resolveFileGlobs(workspace) {
20931
21015
  async function resolveWatchPaths(workspace) {
20932
21016
  const sourceDir = await resolveSourceDir(workspace);
20933
21017
  return [
20934
- join21(workspace, sourceDir),
20935
- join21(workspace, "promptscript.yaml"),
20936
- join21(workspace, "promptscript.yml")
21018
+ join23(workspace, sourceDir),
21019
+ join23(workspace, "promptscript.yaml"),
21020
+ join23(workspace, "promptscript.yml")
20937
21021
  ];
20938
21022
  }
20939
21023
  var DEFAULT_SOURCE_DIR, CONFIG_FILENAMES;
@@ -20960,13 +21044,13 @@ var init_config2 = __esm({
20960
21044
  });
20961
21045
 
20962
21046
  // packages/server/src/path-guard.ts
20963
- import { resolve as resolve20, relative as relative4, isAbsolute as isAbsolute6 } from "path";
21047
+ import { resolve as resolve21, relative as relative4, isAbsolute as isAbsolute6 } from "path";
20964
21048
  function resolveSafePath(workspace, requestedPath) {
20965
21049
  const decoded = decodeURIComponent(requestedPath);
20966
21050
  if (isAbsolute6(decoded)) {
20967
21051
  throw new PathTraversalError(requestedPath);
20968
21052
  }
20969
- const resolved = resolve20(workspace, decoded);
21053
+ const resolved = resolve21(workspace, decoded);
20970
21054
  const rel = relative4(workspace, resolved);
20971
21055
  if (rel.startsWith("..") || isAbsolute6(rel)) {
20972
21056
  throw new PathTraversalError(requestedPath);
@@ -20988,7 +21072,7 @@ var init_path_guard = __esm({
20988
21072
 
20989
21073
  // packages/server/src/routes/files.ts
20990
21074
  import { readFile as readFile25, writeFile as writeFile13, unlink as unlink2, stat as stat3, mkdir as mkdir10 } from "fs/promises";
20991
- import { dirname as dirname13 } from "path";
21075
+ import { dirname as dirname15 } from "path";
20992
21076
  import fg from "fast-glob";
20993
21077
  function registerRoutes(app, workspace, readOnly) {
20994
21078
  app.get("/api/files", async () => {
@@ -21048,7 +21132,7 @@ function registerRoutes(app, workspace, readOnly) {
21048
21132
  return reply.status(409).send({ error: "File already exists" });
21049
21133
  } catch {
21050
21134
  }
21051
- await mkdir10(dirname13(resolved), { recursive: true });
21135
+ await mkdir10(dirname15(resolved), { recursive: true });
21052
21136
  await writeFile13(resolved, request.body.content, "utf-8");
21053
21137
  return reply.status(201).send({ path: filePath, status: "created" });
21054
21138
  } catch (err) {
@@ -21274,14 +21358,14 @@ init_src();
21274
21358
  init_init();
21275
21359
  import { Command } from "commander";
21276
21360
  import { fileURLToPath as fileURLToPath4 } from "url";
21277
- import { dirname as dirname14 } from "path";
21361
+ import { dirname as dirname16 } from "path";
21278
21362
 
21279
21363
  // packages/cli/src/commands/compile.ts
21280
21364
  init_src();
21281
- import { resolve as resolve7, dirname as dirname8, isAbsolute as isAbsolute5 } from "path";
21365
+ import { resolve as resolve8, dirname as dirname9, isAbsolute as isAbsolute5 } from "path";
21282
21366
  import { fileURLToPath as fileURLToPath2 } from "url";
21283
21367
  import { writeFile as writeFile4, mkdir as mkdir4, readFile as readFile12 } from "fs/promises";
21284
- import { existsSync as existsSync11 } from "fs";
21368
+ import { existsSync as existsSync12 } from "fs";
21285
21369
  import chokidar from "chokidar";
21286
21370
 
21287
21371
  // packages/cli/src/config/loader.ts
@@ -21345,6 +21429,56 @@ async function loadConfig(customPath) {
21345
21429
 
21346
21430
  // packages/cli/src/commands/compile.ts
21347
21431
  init_loader();
21432
+
21433
+ // packages/cli/src/prettier/post-format.ts
21434
+ import { resolve as resolve4 } from "path";
21435
+ var MARKDOWN_RE = /\.(md|mdx|markdown)$/i;
21436
+ async function postFormatWithPrettier(outputs, projectRoot, logger) {
21437
+ let prettier;
21438
+ try {
21439
+ prettier = await import("prettier");
21440
+ } catch {
21441
+ logger.verbose("Prettier not available; skipping markdown post-format.");
21442
+ return;
21443
+ }
21444
+ const queue = [];
21445
+ for (const output of outputs.values()) {
21446
+ collectOutputs(output, queue);
21447
+ }
21448
+ for (const output of queue) {
21449
+ if (!MARKDOWN_RE.test(output.path)) continue;
21450
+ const absPath = resolve4(projectRoot, output.path);
21451
+ let config = null;
21452
+ try {
21453
+ config = await prettier.resolveConfig(absPath);
21454
+ } catch (err) {
21455
+ logger.verbose(
21456
+ `Could not resolve Prettier config for ${output.path}: ${err instanceof Error ? err.message : String(err)}`
21457
+ );
21458
+ }
21459
+ try {
21460
+ output.content = await prettier.format(output.content, {
21461
+ ...config ?? {},
21462
+ filepath: absPath,
21463
+ parser: "markdown"
21464
+ });
21465
+ } catch (err) {
21466
+ logger.verbose(
21467
+ `Prettier rejected ${output.path}: ${err instanceof Error ? err.message : String(err)}`
21468
+ );
21469
+ }
21470
+ }
21471
+ }
21472
+ function collectOutputs(output, into) {
21473
+ into.push(output);
21474
+ if (output.additionalFiles) {
21475
+ for (const child of output.additionalFiles) {
21476
+ collectOutputs(child, into);
21477
+ }
21478
+ }
21479
+ }
21480
+
21481
+ // packages/cli/src/commands/compile.ts
21348
21482
  init_console();
21349
21483
 
21350
21484
  // packages/compiler/src/compiler.ts
@@ -21361,7 +21495,7 @@ import { basename as basename11, dirname as dirname7, join as join8 } from "path
21361
21495
  // packages/resolver/src/loader.ts
21362
21496
  init_src();
21363
21497
  import { readFile as readFile7 } from "fs/promises";
21364
- import { resolve as resolve4, dirname as dirname5, basename as basename7, isAbsolute as isAbsolute2, relative } from "path";
21498
+ import { resolve as resolve5, dirname as dirname5, basename as basename7, isAbsolute as isAbsolute2, relative } from "path";
21365
21499
 
21366
21500
  // packages/resolver/src/alias-resolver.ts
21367
21501
  init_src();
@@ -21451,8 +21585,8 @@ var FileLoader = class {
21451
21585
  options;
21452
21586
  constructor(options) {
21453
21587
  this.registryPath = options.registryPath;
21454
- this.localPath = resolve4(options.localPath ?? process.cwd());
21455
- this.projectRoot = resolve4(
21588
+ this.localPath = resolve5(options.localPath ?? process.cwd());
21589
+ this.projectRoot = resolve5(
21456
21590
  options.projectRoot ?? (basename7(this.localPath) === ".promptscript" ? dirname5(this.localPath) : this.localPath)
21457
21591
  );
21458
21592
  this.options = options;
@@ -21495,11 +21629,11 @@ var FileLoader = class {
21495
21629
  const namespace = match[1];
21496
21630
  const segments = match[2];
21497
21631
  const fileName2 = segments.endsWith(".prs") || segments.endsWith(".md") ? segments : `${segments}.prs`;
21498
- return resolve4(this.registryPath, `@${namespace}`, fileName2);
21632
+ return resolve5(this.registryPath, `@${namespace}`, fileName2);
21499
21633
  }
21500
21634
  }
21501
21635
  const fileName = path.endsWith(".prs") || path.endsWith(".md") ? path : `${path}.prs`;
21502
- return resolve4(this.localPath, fileName);
21636
+ return resolve5(this.localPath, fileName);
21503
21637
  }
21504
21638
  /**
21505
21639
  * Resolve a PathReference to an absolute path.
@@ -21512,7 +21646,7 @@ var FileLoader = class {
21512
21646
  if (ref.isRelative) {
21513
21647
  const dir = dirname5(fromFile);
21514
21648
  const rawPath = ref.raw.endsWith(".prs") || ref.raw.endsWith(".md") ? ref.raw : `${ref.raw}.prs`;
21515
- const resolved = resolve4(dir, rawPath);
21649
+ const resolved = resolve5(dir, rawPath);
21516
21650
  const rel = relative(this.projectRoot, resolved);
21517
21651
  if (rel.startsWith("..") || isAbsolute2(rel)) {
21518
21652
  throw new Error(`Path traversal outside project root is not allowed: ${ref.raw}`);
@@ -22453,7 +22587,7 @@ function uniqueConcat3(parent, child) {
22453
22587
  // packages/resolver/src/skills.ts
22454
22588
  init_src();
22455
22589
  import { readFile as readFile8, readdir as readdir2, access, lstat, realpath } from "fs/promises";
22456
- import { basename as basename8, resolve as resolve5, dirname as dirname6, relative as relative2, normalize as normalize2, isAbsolute as isAbsolute3, sep as sep2 } from "path";
22590
+ import { basename as basename8, resolve as resolve6, dirname as dirname6, relative as relative2, normalize as normalize2, isAbsolute as isAbsolute3, sep as sep2 } from "path";
22457
22591
  function parseSkillMd(content) {
22458
22592
  const lines = content.split("\n");
22459
22593
  let inFrontmatter = false;
@@ -22899,10 +23033,10 @@ function parseSkillIgnore(content) {
22899
23033
  return { patterns };
22900
23034
  }
22901
23035
  function isIgnoredByRules(relPath, rules) {
22902
- const basename14 = relPath.split("/").pop() ?? relPath;
23036
+ const basename16 = relPath.split("/").pop() ?? relPath;
22903
23037
  let ignored = false;
22904
23038
  for (const { regex, matchPath, negated } of rules.patterns) {
22905
- const target = matchPath ? relPath : basename14;
23039
+ const target = matchPath ? relPath : basename16;
22906
23040
  if (regex.test(target)) {
22907
23041
  ignored = !negated;
22908
23042
  }
@@ -22910,7 +23044,7 @@ function isIgnoredByRules(relPath, rules) {
22910
23044
  return ignored;
22911
23045
  }
22912
23046
  async function loadSkillIgnore(skillDir) {
22913
- const ignorePath = resolve5(skillDir, ".skillignore");
23047
+ const ignorePath = resolve6(skillDir, ".skillignore");
22914
23048
  try {
22915
23049
  const content = await readFile8(ignorePath, "utf-8");
22916
23050
  return parseSkillIgnore(content);
@@ -22956,7 +23090,7 @@ async function discoverSkillResources(skillDir, logger = noopLogger2) {
22956
23090
  }
22957
23091
  if (entry.isSymbolicLink()) continue;
22958
23092
  if (SKIP_FILES.has(entry.name)) continue;
22959
- const fullPath = resolve5(entry.parentPath, entry.name);
23093
+ const fullPath = resolve6(entry.parentPath, entry.name);
22960
23094
  const relPath = relative2(skillDir, fullPath);
22961
23095
  const relPathNormalized = relPath.split(sep2).join("/");
22962
23096
  const pathSegments = relPath.split(sep2);
@@ -23015,7 +23149,7 @@ async function resolveSkillReferences(references, basePath, logger) {
23015
23149
  column: 0
23016
23150
  });
23017
23151
  }
23018
- const fullPath = resolve5(basePath, ref);
23152
+ const fullPath = resolve6(basePath, ref);
23019
23153
  let content;
23020
23154
  try {
23021
23155
  content = await readFile8(fullPath, "utf-8");
@@ -23087,8 +23221,8 @@ async function discoverSkillDirs(basePath) {
23087
23221
  if (!entry.isDirectory()) continue;
23088
23222
  if (SKIP_DIRS.has(entry.name)) continue;
23089
23223
  if (!isSafeSkillName(entry.name)) continue;
23090
- const subDir = resolve5(dir, entry.name);
23091
- const skillMd = resolve5(subDir, "SKILL.md");
23224
+ const subDir = resolve6(dir, entry.name);
23225
+ const skillMd = resolve6(subDir, "SKILL.md");
23092
23226
  if (await fileExists(skillMd)) {
23093
23227
  if (!result.has(entry.name)) {
23094
23228
  result.set(entry.name, subDir);
@@ -23115,9 +23249,9 @@ async function resolveNativeSkills(ast, registryPath, sourceFile, localPath, opt
23115
23249
  const isSkillsDir = sourceDir.includes("@skills") || sourceDir.endsWith("/skills");
23116
23250
  const discoveredSkillPaths = /* @__PURE__ */ new Map();
23117
23251
  if (!isSkillsDir && localPath) {
23118
- const discoveryDirs = [resolve5(localPath, "skills")];
23252
+ const discoveryDirs = [resolve6(localPath, "skills")];
23119
23253
  if (options?.universalDir && localPath) {
23120
- discoveryDirs.push(resolve5(localPath, "..", options.universalDir, "skills"));
23254
+ discoveryDirs.push(resolve6(localPath, "..", options.universalDir, "skills"));
23121
23255
  }
23122
23256
  for (const dir of discoveryDirs) {
23123
23257
  const discovered = await discoverSkillDirs(dir);
@@ -23138,7 +23272,7 @@ async function resolveNativeSkills(ast, registryPath, sourceFile, localPath, opt
23138
23272
  }
23139
23273
  let sharedResources = [];
23140
23274
  if (localPath) {
23141
- const sharedDir = resolve5(localPath, "shared");
23275
+ const sharedDir = resolve6(localPath, "shared");
23142
23276
  if (await fileExists(sharedDir)) {
23143
23277
  try {
23144
23278
  sharedResources = await discoverSkillResources(sharedDir, logger);
@@ -23159,12 +23293,12 @@ async function resolveNativeSkills(ast, registryPath, sourceFile, localPath, opt
23159
23293
  continue;
23160
23294
  }
23161
23295
  const skillObj = skillValue;
23162
- const skillMdPath = isSkillsDir ? resolve5(sourceDir, skillName, "SKILL.md") : await (async () => {
23296
+ const skillMdPath = isSkillsDir ? resolve6(sourceDir, skillName, "SKILL.md") : await (async () => {
23163
23297
  const discoveredDir = discoveredSkillPaths.get(skillName);
23164
- if (discoveredDir) return resolve5(discoveredDir, "SKILL.md");
23165
- const localCandidate = localPath ? resolve5(localPath, "skills", skillName, "SKILL.md") : null;
23166
- const universalCandidate = options?.universalDir && localPath ? resolve5(localPath, "..", options.universalDir, "skills", skillName, "SKILL.md") : null;
23167
- const registryCandidate = resolve5(registryPath, "@skills", skillName, "SKILL.md");
23298
+ if (discoveredDir) return resolve6(discoveredDir, "SKILL.md");
23299
+ const localCandidate = localPath ? resolve6(localPath, "skills", skillName, "SKILL.md") : null;
23300
+ const universalCandidate = options?.universalDir && localPath ? resolve6(localPath, "..", options.universalDir, "skills", skillName, "SKILL.md") : null;
23301
+ const registryCandidate = resolve6(registryPath, "@skills", skillName, "SKILL.md");
23168
23302
  if (localCandidate && await fileExists(localCandidate)) return localCandidate;
23169
23303
  if (universalCandidate && await fileExists(universalCandidate))
23170
23304
  return universalCandidate;
@@ -23254,7 +23388,7 @@ async function discoverCommandFiles(dir, logger = noopLogger2) {
23254
23388
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
23255
23389
  if (entry.isSymbolicLink()) continue;
23256
23390
  const cmdName = "/" + entry.name.replace(/\.md$/, "");
23257
- const fullPath = resolve5(dir, entry.name);
23391
+ const fullPath = resolve6(dir, entry.name);
23258
23392
  try {
23259
23393
  const fileStat = await lstat(fullPath);
23260
23394
  if (fileStat.isSymbolicLink()) continue;
@@ -23285,11 +23419,11 @@ async function resolveNativeCommands(ast, sourceFile, localPath, options) {
23285
23419
  if (!localPath) return ast;
23286
23420
  const logger = options?.logger ?? noopLogger2;
23287
23421
  const allCommands = {};
23288
- const localCommands = await discoverCommandFiles(resolve5(localPath, "commands"), logger);
23422
+ const localCommands = await discoverCommandFiles(resolve6(localPath, "commands"), logger);
23289
23423
  Object.assign(allCommands, localCommands);
23290
23424
  if (options?.universalDir) {
23291
23425
  const universalCommands = await discoverCommandFiles(
23292
- resolve5(localPath, "..", options.universalDir, "commands"),
23426
+ resolve6(localPath, "..", options.universalDir, "commands"),
23293
23427
  logger
23294
23428
  );
23295
23429
  for (const [name, value] of Object.entries(universalCommands)) {
@@ -23370,7 +23504,7 @@ async function discoverAgentFiles(dir, logger = noopLogger2) {
23370
23504
  for (const entry of entries) {
23371
23505
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
23372
23506
  if (entry.isSymbolicLink()) continue;
23373
- const fullPath = resolve5(dir, entry.name);
23507
+ const fullPath = resolve6(dir, entry.name);
23374
23508
  try {
23375
23509
  const fileStat = await lstat(fullPath);
23376
23510
  if (fileStat.isSymbolicLink()) continue;
@@ -23434,11 +23568,11 @@ async function resolveNativeAgents(ast, sourceFile, localPath, options) {
23434
23568
  if (!localPath) return ast;
23435
23569
  const logger = options?.logger ?? noopLogger2;
23436
23570
  const allAgents = {};
23437
- const localAgents = await discoverAgentFiles(resolve5(localPath, "agents"), logger);
23571
+ const localAgents = await discoverAgentFiles(resolve6(localPath, "agents"), logger);
23438
23572
  Object.assign(allAgents, localAgents);
23439
23573
  if (options?.universalDir) {
23440
23574
  const universalAgents = await discoverAgentFiles(
23441
- resolve5(localPath, "..", options.universalDir, "agents"),
23575
+ resolve6(localPath, "..", options.universalDir, "agents"),
23442
23576
  logger
23443
23577
  );
23444
23578
  for (const [name, value] of Object.entries(universalAgents)) {
@@ -25066,7 +25200,7 @@ var RegistryCache = class {
25066
25200
 
25067
25201
  // packages/resolver/src/auto-discovery.ts
25068
25202
  import { readFile as readFile10, readdir as readdir3, access as access2, lstat as lstat2 } from "fs/promises";
25069
- import { resolve as resolve6, basename as basename10 } from "path";
25203
+ import { resolve as resolve7, basename as basename10 } from "path";
25070
25204
  var CONTEXT_FILES = ["CLAUDE.md", ".clinerules", ".cursorrules"];
25071
25205
  async function fileExists2(path) {
25072
25206
  try {
@@ -25102,9 +25236,9 @@ async function discoverSkills(dir) {
25102
25236
  const properties = {};
25103
25237
  for (const entry of entries) {
25104
25238
  if (!entry.isDirectory()) continue;
25105
- let skillMdPath = resolve6(dir, entry.name, "SKILL.md");
25239
+ let skillMdPath = resolve7(dir, entry.name, "SKILL.md");
25106
25240
  if (!await fileExists2(skillMdPath)) {
25107
- const dirnameMdPath = resolve6(dir, entry.name, `${entry.name}.md`);
25241
+ const dirnameMdPath = resolve7(dir, entry.name, `${entry.name}.md`);
25108
25242
  if (await fileExists2(dirnameMdPath)) {
25109
25243
  skillMdPath = dirnameMdPath;
25110
25244
  } else {
@@ -25131,7 +25265,7 @@ async function discoverSkills(dir) {
25131
25265
  return Object.keys(properties).length > 0 ? properties : null;
25132
25266
  }
25133
25267
  async function discoverRootSkill(dir) {
25134
- const skillMdPath = resolve6(dir, "SKILL.md");
25268
+ const skillMdPath = resolve7(dir, "SKILL.md");
25135
25269
  if (!await fileExists2(skillMdPath)) return null;
25136
25270
  try {
25137
25271
  const raw = await readFile10(skillMdPath, "utf-8");
@@ -25162,7 +25296,7 @@ async function discoverAgents(dir) {
25162
25296
  const properties = {};
25163
25297
  for (const entry of entries) {
25164
25298
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
25165
- const fullPath = resolve6(dir, entry.name);
25299
+ const fullPath = resolve7(dir, entry.name);
25166
25300
  try {
25167
25301
  const stat4 = await lstat2(fullPath);
25168
25302
  if (stat4.isSymbolicLink()) continue;
@@ -25198,7 +25332,7 @@ async function discoverCommands(dir) {
25198
25332
  for (const entry of entries) {
25199
25333
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
25200
25334
  if (entry.name === "SKILL.md") continue;
25201
- const fullPath = resolve6(dir, entry.name);
25335
+ const fullPath = resolve7(dir, entry.name);
25202
25336
  try {
25203
25337
  const stat4 = await lstat2(fullPath);
25204
25338
  if (stat4.isSymbolicLink()) continue;
@@ -25215,7 +25349,7 @@ async function discoverCommands(dir) {
25215
25349
  }
25216
25350
  async function discoverContext(dir) {
25217
25351
  for (const fileName of CONTEXT_FILES) {
25218
- const fullPath = resolve6(dir, fileName);
25352
+ const fullPath = resolve7(dir, fileName);
25219
25353
  if (!await fileExists2(fullPath)) continue;
25220
25354
  try {
25221
25355
  const content = await readFile10(fullPath, "utf-8");
@@ -25245,7 +25379,7 @@ async function discoverNativeContent(dir) {
25245
25379
  const subSkills = await discoverSkills(dir);
25246
25380
  const wrappedSkillResults = await Promise.all(
25247
25381
  SKILL_WRAPPER_DIRS.map(async (name) => {
25248
- const candidate = resolve6(dir, name);
25382
+ const candidate = resolve7(dir, name);
25249
25383
  if (!await safeIsDirectory(candidate)) return null;
25250
25384
  return discoverSkills(candidate);
25251
25385
  })
@@ -25266,7 +25400,7 @@ async function discoverNativeContent(dir) {
25266
25400
  const rootAgents = await discoverAgents(dir) ?? {};
25267
25401
  const wrappedAgents = {};
25268
25402
  for (const name of AGENT_WRAPPER_DIRS) {
25269
- const candidate = resolve6(dir, name);
25403
+ const candidate = resolve7(dir, name);
25270
25404
  if (await safeIsDirectory(candidate)) {
25271
25405
  const found = await discoverAgents(candidate);
25272
25406
  if (found) Object.assign(wrappedAgents, found);
@@ -25279,7 +25413,7 @@ async function discoverNativeContent(dir) {
25279
25413
  const rootCommands = await discoverCommands(dir) ?? {};
25280
25414
  const wrappedCommands = {};
25281
25415
  for (const name of COMMAND_WRAPPER_DIRS) {
25282
- const candidate = resolve6(dir, name);
25416
+ const candidate = resolve7(dir, name);
25283
25417
  if (await safeIsDirectory(candidate)) {
25284
25418
  const found = await discoverCommands(candidate);
25285
25419
  if (found) Object.assign(wrappedCommands, found);
@@ -25780,6 +25914,12 @@ var Resolver = class {
25780
25914
  errors.push(new ResolveError(e.message, e.location));
25781
25915
  }
25782
25916
  }
25917
+ } else if (isMdPath) {
25918
+ errors.push(
25919
+ new ResolveError(
25920
+ `Cannot resolve registry import: file '${subPath}' not found in ${repoUrl} (looked for ${resolvedFullPath}). Verify the path inside the repository.`
25921
+ )
25922
+ );
25783
25923
  } else {
25784
25924
  const discoverDir = isRoot ? cachePath : join8(cachePath, subPath);
25785
25925
  this.logger.debug(`No .prs found, trying auto-discovery: ${discoverDir}`);
@@ -26022,7 +26162,7 @@ var HttpRegistry = class {
26022
26162
  * Sleep for specified milliseconds.
26023
26163
  */
26024
26164
  sleep(ms) {
26025
- return new Promise((resolve21) => setTimeout(resolve21, ms));
26165
+ return new Promise((resolve22) => setTimeout(resolve22, ms));
26026
26166
  }
26027
26167
  /**
26028
26168
  * Fetch with timeout.
@@ -26166,9 +26306,251 @@ function createHttpRegistry(options) {
26166
26306
  // packages/resolver/src/vendor-registry.ts
26167
26307
  init_src();
26168
26308
 
26309
+ // packages/resolver/src/skill-validation.ts
26310
+ import { basename as basename12, dirname as dirname8, join as join10 } from "path";
26311
+ import { existsSync as existsSync11 } from "fs";
26312
+ function validateSkillFrontmatter(rawContent, options = {}) {
26313
+ const issues = [];
26314
+ if (!hasFrontmatterDelimiters(rawContent)) {
26315
+ issues.push({
26316
+ severity: "error",
26317
+ code: "SK001",
26318
+ message: "SKILL.md must start with a YAML frontmatter block delimited by `---` lines."
26319
+ });
26320
+ return { valid: false, issues };
26321
+ }
26322
+ const parsed = parseSkillMd(rawContent);
26323
+ validateName(parsed, options, issues);
26324
+ validateDescription(parsed, issues);
26325
+ validateCompatibility(parsed.rawFrontmatter, issues);
26326
+ validateLicense(parsed.rawFrontmatter, issues);
26327
+ validateBody(parsed.content, issues);
26328
+ validateReferences(parsed.references, options.filePath, issues);
26329
+ validateAllowedTools(parsed.rawFrontmatter, issues);
26330
+ const hasErrors = issues.some((i) => i.severity === "error");
26331
+ return { valid: !hasErrors, issues };
26332
+ }
26333
+ var NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
26334
+ var MAX_NAME_LEN = 64;
26335
+ var MIN_DESC_LEN = 1;
26336
+ var MAX_DESC_LEN = 1024;
26337
+ var MAX_COMPATIBILITY_LEN = 500;
26338
+ var SHORT_DESC_WARN_LEN = 40;
26339
+ var MAX_BODY_LINES = 500;
26340
+ function validateName(parsed, options, issues) {
26341
+ const name = parsed.name;
26342
+ if (!name || name.length === 0) {
26343
+ issues.push({
26344
+ severity: "error",
26345
+ code: "SK002",
26346
+ field: "name",
26347
+ message: "`name` is required in SKILL.md frontmatter."
26348
+ });
26349
+ return;
26350
+ }
26351
+ if (name.length > MAX_NAME_LEN) {
26352
+ issues.push({
26353
+ severity: "error",
26354
+ code: "SK003",
26355
+ field: "name",
26356
+ message: `\`name\` must be at most ${MAX_NAME_LEN} characters (got ${name.length}).`
26357
+ });
26358
+ }
26359
+ if (!NAME_PATTERN.test(name)) {
26360
+ issues.push({
26361
+ severity: "error",
26362
+ code: "SK004",
26363
+ field: "name",
26364
+ message: `\`name\` "${name}" is invalid. Use lowercase letters, digits, and single hyphens only. Cannot start or end with a hyphen or contain consecutive hyphens.`
26365
+ });
26366
+ }
26367
+ if (options.filePath) {
26368
+ const parentDir = basename12(dirname8(options.filePath));
26369
+ if (parentDir && parentDir !== name) {
26370
+ issues.push({
26371
+ severity: "error",
26372
+ code: "SK005",
26373
+ field: "name",
26374
+ message: `\`name\` "${name}" must match the parent directory name "${parentDir}".`
26375
+ });
26376
+ }
26377
+ }
26378
+ if (options.existingNames?.has(name)) {
26379
+ issues.push({
26380
+ severity: "error",
26381
+ code: "SK006",
26382
+ field: "name",
26383
+ message: `Skill name "${name}" collides with an already-installed skill. Two skills cannot share the same name (they would be written to the same output folder).`
26384
+ });
26385
+ }
26386
+ }
26387
+ function validateDescription(parsed, issues) {
26388
+ const fromRaw = parsed.rawFrontmatter ? extractScalarField(parsed.rawFrontmatter, "description") : void 0;
26389
+ const description = fromRaw !== void 0 ? fromRaw : parsed.description;
26390
+ if (!description || description.trim().length < MIN_DESC_LEN) {
26391
+ issues.push({
26392
+ severity: "error",
26393
+ code: "SK010",
26394
+ field: "description",
26395
+ message: "`description` is required and must be non-empty."
26396
+ });
26397
+ return;
26398
+ }
26399
+ if (description.length > MAX_DESC_LEN) {
26400
+ issues.push({
26401
+ severity: "error",
26402
+ code: "SK011",
26403
+ field: "description",
26404
+ message: `\`description\` must be at most ${MAX_DESC_LEN} characters (got ${description.length}).`
26405
+ });
26406
+ }
26407
+ if (description.length < SHORT_DESC_WARN_LEN) {
26408
+ issues.push({
26409
+ severity: "warning",
26410
+ code: "SK012",
26411
+ field: "description",
26412
+ message: `\`description\` is very short (${description.length} chars). The spec recommends describing both what the skill does AND when to use it so agents can match it to tasks.`
26413
+ });
26414
+ } else if (!/\bwhen\b|\buse\b/i.test(description)) {
26415
+ issues.push({
26416
+ severity: "warning",
26417
+ code: "SK013",
26418
+ field: "description",
26419
+ message: '`description` should mention WHEN to use the skill (e.g. "Use when \u2026"). This improves agent trigger accuracy.'
26420
+ });
26421
+ }
26422
+ }
26423
+ function validateCompatibility(rawFrontmatter, issues) {
26424
+ if (!rawFrontmatter) return;
26425
+ const value = extractScalarField(rawFrontmatter, "compatibility");
26426
+ if (value === void 0) return;
26427
+ if (value.length > MAX_COMPATIBILITY_LEN) {
26428
+ issues.push({
26429
+ severity: "error",
26430
+ code: "SK020",
26431
+ field: "compatibility",
26432
+ message: `\`compatibility\` must be at most ${MAX_COMPATIBILITY_LEN} characters (got ${value.length}).`
26433
+ });
26434
+ }
26435
+ }
26436
+ function validateLicense(rawFrontmatter, issues) {
26437
+ if (!rawFrontmatter) {
26438
+ issues.push({
26439
+ severity: "warning",
26440
+ code: "SK030",
26441
+ field: "license",
26442
+ message: "`license` is missing. Enterprises typically require an explicit license declaration before adopting a third-party skill."
26443
+ });
26444
+ return;
26445
+ }
26446
+ const value = extractScalarField(rawFrontmatter, "license");
26447
+ if (value === void 0 || value.length === 0) {
26448
+ issues.push({
26449
+ severity: "warning",
26450
+ code: "SK030",
26451
+ field: "license",
26452
+ message: "`license` is missing. Enterprises typically require an explicit license declaration before adopting a third-party skill."
26453
+ });
26454
+ }
26455
+ }
26456
+ function validateBody(body, issues) {
26457
+ const lines = body.split("\n").length;
26458
+ if (lines > MAX_BODY_LINES) {
26459
+ issues.push({
26460
+ severity: "warning",
26461
+ code: "SK040",
26462
+ message: `SKILL.md body has ${lines} lines (>${MAX_BODY_LINES}). The spec recommends splitting long content into separate files under references/ for progressive disclosure.`
26463
+ });
26464
+ }
26465
+ }
26466
+ function validateReferences(references, filePath, issues) {
26467
+ if (!references || references.length === 0) return;
26468
+ if (!filePath) return;
26469
+ const skillDir = dirname8(filePath);
26470
+ for (const ref of references) {
26471
+ if (ref.startsWith("/") || ref.startsWith("..")) {
26472
+ issues.push({
26473
+ severity: "error",
26474
+ code: "SK050",
26475
+ field: "references",
26476
+ message: `Reference "${ref}" must be a relative path inside the skill directory (no leading "/" or "..").`
26477
+ });
26478
+ continue;
26479
+ }
26480
+ const target = join10(skillDir, ref);
26481
+ if (!existsSync11(target)) {
26482
+ issues.push({
26483
+ severity: "error",
26484
+ code: "SK051",
26485
+ field: "references",
26486
+ message: `Reference "${ref}" listed in frontmatter does not exist (looked for ${target}).`
26487
+ });
26488
+ }
26489
+ }
26490
+ }
26491
+ function validateAllowedTools(rawFrontmatter, issues) {
26492
+ if (!rawFrontmatter) return;
26493
+ const value = extractScalarField(rawFrontmatter, "allowed-tools");
26494
+ if (value === void 0) return;
26495
+ const tokens = value.split(/\s+/).filter((t) => t.length > 0);
26496
+ for (const token of tokens) {
26497
+ if (!/^[A-Za-z][A-Za-z0-9_-]*(\([^)]*\))?$/.test(token)) {
26498
+ issues.push({
26499
+ severity: "warning",
26500
+ code: "SK060",
26501
+ field: "allowed-tools",
26502
+ message: `\`allowed-tools\` entry "${token}" does not match the expected \`Tool\` or \`Tool(scope:pattern)\` shape.`
26503
+ });
26504
+ }
26505
+ }
26506
+ }
26507
+ function hasFrontmatterDelimiters(content) {
26508
+ const lines = content.split("\n");
26509
+ let first2 = -1;
26510
+ for (let i = 0; i < lines.length; i++) {
26511
+ if ((lines[i] ?? "").trim() === "---") {
26512
+ first2 = i;
26513
+ break;
26514
+ }
26515
+ }
26516
+ if (first2 === -1) return false;
26517
+ for (let i = first2 + 1; i < lines.length; i++) {
26518
+ if ((lines[i] ?? "").trim() === "---") return true;
26519
+ }
26520
+ return false;
26521
+ }
26522
+ function extractScalarField(rawFrontmatter, field) {
26523
+ const lines = rawFrontmatter.split("\n");
26524
+ const pattern = new RegExp(`^${escapeRegex(field)}:\\s*(?:"([^"]*)"|'([^']*)'|(.*))\\s*$`);
26525
+ for (const line of lines) {
26526
+ const match = line.match(pattern);
26527
+ if (match) {
26528
+ return (match[1] ?? match[2] ?? match[3] ?? "").trim();
26529
+ }
26530
+ }
26531
+ return void 0;
26532
+ }
26533
+ function escapeRegex(input2) {
26534
+ return input2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26535
+ }
26536
+ function formatSkillValidationIssues(issues) {
26537
+ return issues.map((i) => {
26538
+ const tag = i.severity === "error" ? "\u2717" : "\u26A0";
26539
+ const field = i.field ? ` [${i.field}]` : "";
26540
+ return ` ${tag} ${i.code}${field}: ${i.message}`;
26541
+ }).join("\n");
26542
+ }
26543
+
26169
26544
  // packages/resolver/src/index.ts
26170
26545
  init_src();
26171
26546
 
26547
+ // packages/resolver/src/reference-hasher.ts
26548
+ import { createHash as createHash2 } from "crypto";
26549
+ function hashContent(content) {
26550
+ const hex = createHash2("sha256").update(content).digest("hex");
26551
+ return `sha256-${hex}`;
26552
+ }
26553
+
26172
26554
  // packages/validator/src/validator.ts
26173
26555
  init_src();
26174
26556
 
@@ -29621,7 +30003,7 @@ var Compiler = class _Compiler {
29621
30003
  outputPathOwners.set(skillPath, `${formatter.name}:promptscript-skill`);
29622
30004
  const skillOutput = {
29623
30005
  path: skillPath,
29624
- content: this.options.skillContent
30006
+ content: formatter.transformInjectedSkillContent ? formatter.transformInjectedSkillContent(this.options.skillContent) : this.options.skillContent
29625
30007
  };
29626
30008
  outputs.set(skillPath, addMarkerToOutput(skillOutput, sourceLabel, "promptscript"));
29627
30009
  this.logger.verbose(` \u2192 ${skillPath} (auto-injected promptscript skill)`);
@@ -29702,12 +30084,12 @@ var Compiler = class _Compiler {
29702
30084
  */
29703
30085
  async watch(entryPath, options = {}) {
29704
30086
  const { default: chokidar2 } = await import("chokidar");
29705
- const { dirname: dirname15, resolve: resolve21 } = await import("path");
29706
- const baseDir = dirname15(resolve21(entryPath));
30087
+ const { dirname: dirname17, resolve: resolve22 } = await import("path");
30088
+ const baseDir = dirname17(resolve22(entryPath));
29707
30089
  const includePatterns = options.include ?? ["**/*.prs"];
29708
30090
  const excludePatterns = options.exclude ?? ["**/node_modules/**"];
29709
30091
  const debounceMs = options.debounce ?? 300;
29710
- const watchPatterns = includePatterns.map((p) => resolve21(baseDir, p));
30092
+ const watchPatterns = includePatterns.map((p) => resolve22(baseDir, p));
29711
30093
  let debounceTimer = null;
29712
30094
  let pendingChanges = [];
29713
30095
  const handleChange = async (changedFiles) => {
@@ -29882,7 +30264,7 @@ var Pager = class {
29882
30264
  }
29883
30265
  return;
29884
30266
  }
29885
- return new Promise((resolve21) => {
30267
+ return new Promise((resolve22) => {
29886
30268
  const pagerCmd = getPagerCommand();
29887
30269
  const [cmd, ...args] = pagerCmd.split(" ");
29888
30270
  if (cmd === "less" && !args.includes("-R")) {
@@ -29890,7 +30272,7 @@ var Pager = class {
29890
30272
  }
29891
30273
  if (!cmd) {
29892
30274
  console.log(content);
29893
- resolve21();
30275
+ resolve22();
29894
30276
  return;
29895
30277
  }
29896
30278
  const pager = spawn(cmd, args, {
@@ -29903,17 +30285,17 @@ var Pager = class {
29903
30285
  });
29904
30286
  pager.on("error", (_err) => {
29905
30287
  console.log(content);
29906
- resolve21();
30288
+ resolve22();
29907
30289
  });
29908
30290
  pager.on("close", () => {
29909
- resolve21();
30291
+ resolve22();
29910
30292
  });
29911
30293
  if (pager.stdin) {
29912
30294
  pager.stdin.write(content);
29913
30295
  pager.stdin.end();
29914
30296
  } else {
29915
30297
  console.log(content);
29916
- resolve21();
30298
+ resolve22();
29917
30299
  }
29918
30300
  });
29919
30301
  }
@@ -29926,7 +30308,7 @@ function createPager(enabled = true) {
29926
30308
  init_services();
29927
30309
 
29928
30310
  // packages/cli/src/utils/registry-resolver.ts
29929
- import { join as join10 } from "path";
30311
+ import { join as join11 } from "path";
29930
30312
  async function resolveRegistryPath(config) {
29931
30313
  if (config.registry?.git) {
29932
30314
  const gitConfig = config.registry.git;
@@ -29940,7 +30322,7 @@ async function resolveRegistryPath(config) {
29940
30322
  if (isValid) {
29941
30323
  const subPath2 = gitConfig.path ?? "";
29942
30324
  return {
29943
- path: subPath2 ? join10(cachePath, subPath2) : cachePath,
30325
+ path: subPath2 ? join11(cachePath, subPath2) : cachePath,
29944
30326
  isRemote: true,
29945
30327
  source: "git"
29946
30328
  };
@@ -29969,7 +30351,7 @@ async function resolveRegistryPath(config) {
29969
30351
  }
29970
30352
  const subPath = gitConfig.path ?? "";
29971
30353
  return {
29972
- path: subPath ? join10(cachePath, subPath) : cachePath,
30354
+ path: subPath ? join11(cachePath, subPath) : cachePath,
29973
30355
  isRemote: true,
29974
30356
  source: "git"
29975
30357
  };
@@ -30019,17 +30401,17 @@ function detectOutputConflicts(targets) {
30019
30401
 
30020
30402
  // packages/cli/src/commands/compile.ts
30021
30403
  var __filename2 = fileURLToPath2(import.meta.url);
30022
- var __dirname2 = dirname8(__filename2);
30404
+ var __dirname2 = dirname9(__filename2);
30023
30405
  async function loadBundledSkillContent(logger) {
30024
30406
  const skillRelPath = "skills/promptscript/SKILL.md";
30025
30407
  const candidates = [
30026
- resolve7(__dirname2, skillRelPath),
30408
+ resolve8(__dirname2, skillRelPath),
30027
30409
  // bundled: dist/packages/cli/skills/...
30028
- resolve7(__dirname2, "..", "..", skillRelPath)
30410
+ resolve8(__dirname2, "..", "..", skillRelPath)
30029
30411
  // dev: packages/cli/skills/...
30030
30412
  ];
30031
30413
  for (const candidate of candidates) {
30032
- if (existsSync11(candidate)) {
30414
+ if (existsSync12(candidate)) {
30033
30415
  try {
30034
30416
  const content = await readFile12(candidate, "utf-8");
30035
30417
  logger.debug(
@@ -30046,8 +30428,8 @@ async function loadBundledSkillContent(logger) {
30046
30428
  }
30047
30429
  function findConfigInDir(dir) {
30048
30430
  for (const file of CONFIG_FILES) {
30049
- const fullPath = resolve7(dir, file);
30050
- if (existsSync11(fullPath)) {
30431
+ const fullPath = resolve8(dir, file);
30432
+ if (existsSync12(fullPath)) {
30051
30433
  return fullPath;
30052
30434
  }
30053
30435
  }
@@ -30103,7 +30485,7 @@ function getBuildProfile(config, buildName) {
30103
30485
  }
30104
30486
  function resolveOutputBase(projectRoot, output) {
30105
30487
  if (!output) return projectRoot;
30106
- return isAbsolute5(output) ? output : resolve7(projectRoot, output);
30488
+ return isAbsolute5(output) ? output : resolve8(projectRoot, output);
30107
30489
  }
30108
30490
  async function isPromptScriptGenerated2(filePath) {
30109
30491
  try {
@@ -30147,13 +30529,13 @@ async function writeOutputs(outputs, options, _config, services) {
30147
30529
  const targetErrors = [];
30148
30530
  async function safeWrite(path, content) {
30149
30531
  try {
30150
- await mkdir4(dirname8(path), { recursive: true });
30532
+ await mkdir4(dirname9(path), { recursive: true });
30151
30533
  await writeFile4(path, content, "utf-8");
30152
30534
  return true;
30153
30535
  } catch (err) {
30154
30536
  const nodeErr = err;
30155
30537
  if (nodeErr.code === "ENOTDIR") {
30156
- const conflictPath = nodeErr.path ?? dirname8(path);
30538
+ const conflictPath = nodeErr.path ?? dirname9(path);
30157
30539
  const msg = `Cannot create directory because '${conflictPath}' already exists as a file. Remove it or disable this target.`;
30158
30540
  targetErrors.push(msg);
30159
30541
  ConsoleOutput.error(msg);
@@ -30169,8 +30551,8 @@ async function writeOutputs(outputs, options, _config, services) {
30169
30551
  }
30170
30552
  }
30171
30553
  for (const output of outputs.values()) {
30172
- const outputPath = options.output ? resolve7(options.output, output.path) : resolve7(output.path);
30173
- const fileExists3 = existsSync11(outputPath);
30554
+ const outputPath = options.output ? resolve8(options.output, output.path) : resolve8(output.path);
30555
+ const fileExists3 = existsSync12(outputPath);
30174
30556
  if (options.dryRun) {
30175
30557
  if (fileExists3) {
30176
30558
  const isGenerated2 = await isPromptScriptGenerated2(outputPath);
@@ -30303,7 +30685,7 @@ async function compileCommand(options, services = createDefaultServices()) {
30303
30685
  const spinner = createSpinner("Loading configuration...").start();
30304
30686
  const logger = createCliLogger();
30305
30687
  try {
30306
- const projectRoot = options.cwd ? resolve7(options.cwd) : process.cwd();
30688
+ const projectRoot = options.cwd ? resolve8(options.cwd) : process.cwd();
30307
30689
  logger.verbose("Loading configuration...");
30308
30690
  const configPath = options.config ? options.config : options.cwd ? findConfigInDir(projectRoot) : void 0;
30309
30691
  const config = await loadConfig(configPath);
@@ -30343,7 +30725,7 @@ Disable one of the conflicting targets or assign a custom output path.`
30343
30725
  }
30344
30726
  }
30345
30727
  let lockfile;
30346
- const lockfilePath = resolve7(projectRoot, "promptscript.lock");
30728
+ const lockfilePath = resolve8(projectRoot, "promptscript.lock");
30347
30729
  try {
30348
30730
  const lockfileContent = await readFile12(lockfilePath, "utf-8");
30349
30731
  const parsed = parseYaml5(lockfileContent, { maxAliasCount: 100 });
@@ -30363,7 +30745,7 @@ Disable one of the conflicting targets or assign a custom output path.`
30363
30745
  if (config.includePromptScriptSkill !== false) {
30364
30746
  skillContent = await loadBundledSkillContent(logger);
30365
30747
  }
30366
- const localPath = resolve7(projectRoot, ".promptscript");
30748
+ const localPath = resolve8(projectRoot, ".promptscript");
30367
30749
  const compiler = new Compiler({
30368
30750
  resolver: {
30369
30751
  registryPath,
@@ -30383,8 +30765,8 @@ Disable one of the conflicting targets or assign a custom output path.`
30383
30765
  ignoreHashes: options.ignoreHashes
30384
30766
  });
30385
30767
  const entry = buildProfile?.entry ?? config.input?.entry;
30386
- const entryPath = entry ? resolve7(projectRoot, entry) : resolve7(localPath, "project.prs");
30387
- if (!existsSync11(entryPath)) {
30768
+ const entryPath = entry ? resolve8(projectRoot, entry) : resolve8(localPath, "project.prs");
30769
+ if (!existsSync12(entryPath)) {
30388
30770
  spinner.fail("Entry file not found");
30389
30771
  ConsoleOutput.error(`File not found: ${entryPath}`);
30390
30772
  ConsoleOutput.muted("Run: prs init");
@@ -30409,6 +30791,7 @@ Disable one of the conflicting targets or assign a custom output path.`
30409
30791
  ...options,
30410
30792
  output: resolveOutputBase(projectRoot, configuredOutput)
30411
30793
  };
30794
+ await postFormatWithPrettier(result.outputs, projectRoot, logger);
30412
30795
  const writeResult = await writeOutputs(result.outputs, effectiveOptions, config, services);
30413
30796
  if (writeResult.unchanged.length > 0) {
30414
30797
  ConsoleOutput.muted(`Unchanged ${writeResult.unchanged.length} file(s)`);
@@ -30422,7 +30805,7 @@ Disable one of the conflicting targets or assign a custom output path.`
30422
30805
  ConsoleOutput.newline();
30423
30806
  ConsoleOutput.info("Watching for changes... (Ctrl+C to stop)");
30424
30807
  watchForChanges(
30425
- resolve7(projectRoot, ".promptscript"),
30808
+ resolve8(projectRoot, ".promptscript"),
30426
30809
  () => compileCommand({ ...options, watch: false }, services)
30427
30810
  );
30428
30811
  }
@@ -30473,8 +30856,8 @@ function watchForChanges(dir, callback) {
30473
30856
  }
30474
30857
 
30475
30858
  // packages/cli/src/commands/validate.ts
30476
- import { resolve as resolve8, join as join11 } from "path";
30477
- import { existsSync as existsSync12, readFileSync as readFileSync4, writeFileSync, readdirSync } from "fs";
30859
+ import { resolve as resolve9, join as join12 } from "path";
30860
+ import { existsSync as existsSync13, readFileSync as readFileSync4, writeFileSync, readdirSync } from "fs";
30478
30861
  init_console();
30479
30862
  init_src();
30480
30863
  init_src2();
@@ -30583,7 +30966,7 @@ function discoverPrsFiles(dir) {
30583
30966
  try {
30584
30967
  const entries = readdirSync(dir, { withFileTypes: true });
30585
30968
  for (const entry of entries) {
30586
- const fullPath = join11(dir, entry.name);
30969
+ const fullPath = join12(dir, entry.name);
30587
30970
  if (entry.isDirectory()) {
30588
30971
  results.push(...discoverPrsFiles(fullPath));
30589
30972
  } else if (entry.name.endsWith(".prs")) {
@@ -30623,8 +31006,8 @@ async function validateCommand(options) {
30623
31006
  formatters: []
30624
31007
  // No formatters needed for validation only
30625
31008
  });
30626
- const entryPath = resolve8("./.promptscript/project.prs");
30627
- if (!existsSync12(entryPath)) {
31009
+ const entryPath = resolve9("./.promptscript/project.prs");
31010
+ if (!existsSync13(entryPath)) {
30628
31011
  handleEntryNotFound(entryPath, isJsonFormat, spinner);
30629
31012
  return;
30630
31013
  }
@@ -30706,8 +31089,8 @@ ${fixedCount} file(s) fixed.`);
30706
31089
 
30707
31090
  // packages/cli/src/commands/pull.ts
30708
31091
  import { mkdir as mkdir5, writeFile as writeFile5 } from "fs/promises";
30709
- import { existsSync as existsSync13 } from "fs";
30710
- import { resolve as resolve9, dirname as dirname9 } from "path";
31092
+ import { existsSync as existsSync14 } from "fs";
31093
+ import { resolve as resolve10, dirname as dirname10 } from "path";
30711
31094
  init_console();
30712
31095
  async function pullCommand(options) {
30713
31096
  const spinner = createSpinner("Loading configuration...").start();
@@ -30732,8 +31115,8 @@ async function pullCommand(options) {
30732
31115
  }
30733
31116
  spinner.text = `Fetching ${inheritPath}...`;
30734
31117
  const content = await registry2.fetch(inheritPath);
30735
- const destPath = resolve9("./.promptscript/.inherited", inheritPath);
30736
- if (existsSync13(destPath) && !options.force && !options.dryRun) {
31118
+ const destPath = resolve10("./.promptscript/.inherited", inheritPath);
31119
+ if (existsSync14(destPath) && !options.force && !options.dryRun) {
30737
31120
  spinner.warn("File already exists (use --force to overwrite)");
30738
31121
  ConsoleOutput.muted(destPath);
30739
31122
  return;
@@ -30743,12 +31126,12 @@ async function pullCommand(options) {
30743
31126
  ConsoleOutput.newline();
30744
31127
  ConsoleOutput.dryRun(`Would fetch: ${config.inherit}`);
30745
31128
  ConsoleOutput.dryRun(` to: ${destPath}`);
30746
- if (existsSync13(destPath)) {
31129
+ if (existsSync14(destPath)) {
30747
31130
  ConsoleOutput.dryRun("(would overwrite existing file)");
30748
31131
  }
30749
31132
  return;
30750
31133
  }
30751
- await mkdir5(dirname9(destPath), { recursive: true });
31134
+ await mkdir5(dirname10(destPath), { recursive: true });
30752
31135
  await writeFile5(destPath, content, "utf-8");
30753
31136
  spinner.succeed("Pulled from registry");
30754
31137
  ConsoleOutput.success(destPath);
@@ -30791,10 +31174,10 @@ async function createRegistry(config, options, spinner) {
30791
31174
  }
30792
31175
  const registryPath = config.registry?.path ?? "./registry";
30793
31176
  spinner.text = `Using local registry: ${registryPath}...`;
30794
- if (!existsSync13(registryPath)) {
31177
+ if (!existsSync14(registryPath)) {
30795
31178
  throw new Error(`Local registry path not found: ${registryPath}`);
30796
31179
  }
30797
- return createFileSystemRegistry(resolve9(registryPath));
31180
+ return createFileSystemRegistry(resolve10(registryPath));
30798
31181
  }
30799
31182
  function parseInheritPath(inheritPath) {
30800
31183
  const versionIndex = inheritPath.lastIndexOf("@");
@@ -30841,9 +31224,9 @@ function handlePullError(error, spinner) {
30841
31224
  }
30842
31225
 
30843
31226
  // packages/cli/src/commands/diff.ts
30844
- import { resolve as resolve10 } from "path";
31227
+ import { resolve as resolve11 } from "path";
30845
31228
  import { readFile as readFile13 } from "fs/promises";
30846
- import { existsSync as existsSync14 } from "fs";
31229
+ import { existsSync as existsSync15 } from "fs";
30847
31230
  init_console();
30848
31231
  import chalk2 from "chalk";
30849
31232
  function configureColors(options) {
@@ -30878,9 +31261,9 @@ function printNewFilePreview(content, showFull, pager) {
30878
31261
  }
30879
31262
  }
30880
31263
  async function compareOutput(_name, output, _config, showFull, pager) {
30881
- const outputPath = resolve10(output.path);
31264
+ const outputPath = resolve11(output.path);
30882
31265
  const newContent = output.content;
30883
- if (!existsSync14(outputPath)) {
31266
+ if (!existsSync15(outputPath)) {
30884
31267
  pager.write(chalk2.green(`+ ${outputPath} (new file)`));
30885
31268
  pager.write("");
30886
31269
  printNewFilePreview(newContent, showFull, pager);
@@ -30920,8 +31303,8 @@ async function diffCommand(options) {
30920
31303
  formatters: targets,
30921
31304
  customConventions: config.customConventions
30922
31305
  });
30923
- const entryPath = resolve10("./.promptscript/project.prs");
30924
- if (!existsSync14(entryPath)) {
31306
+ const entryPath = resolve11("./.promptscript/project.prs");
31307
+ if (!existsSync15(entryPath)) {
30925
31308
  spinner.fail("Entry file not found");
30926
31309
  ConsoleOutput.error(`File not found: ${entryPath}`);
30927
31310
  ConsoleOutput.muted("Run: prs init");
@@ -30989,8 +31372,8 @@ function printSimpleDiff(existingLines, newLines, showFull, pager) {
30989
31372
  }
30990
31373
 
30991
31374
  // packages/cli/src/commands/check.ts
30992
- import { existsSync as existsSync15 } from "fs";
30993
- import { resolve as resolve11 } from "path";
31375
+ import { existsSync as existsSync16 } from "fs";
31376
+ import { resolve as resolve12 } from "path";
30994
31377
  init_console();
30995
31378
  async function checkCommand(_options) {
30996
31379
  const spinner = createSpinner("Checking project health...").start();
@@ -31053,7 +31436,7 @@ async function checkCommand(_options) {
31053
31436
  });
31054
31437
  }
31055
31438
  const entryPath = config.input?.entry ?? ".promptscript/project.prs";
31056
- if (existsSync15(entryPath)) {
31439
+ if (existsSync16(entryPath)) {
31057
31440
  results.push({
31058
31441
  name: "Entry file",
31059
31442
  status: "ok",
@@ -31068,8 +31451,8 @@ async function checkCommand(_options) {
31068
31451
  hasErrors = true;
31069
31452
  }
31070
31453
  if (config.registry?.path) {
31071
- const registryPath = resolve11(config.registry.path);
31072
- if (existsSync15(registryPath)) {
31454
+ const registryPath = resolve12(config.registry.path);
31455
+ if (existsSync16(registryPath)) {
31073
31456
  results.push({
31074
31457
  name: "Registry path",
31075
31458
  status: "ok",
@@ -31154,21 +31537,21 @@ function printResults(results) {
31154
31537
  init_src();
31155
31538
  init_console();
31156
31539
  import { fileURLToPath as fileURLToPath3 } from "url";
31157
- import { dirname as dirname10 } from "path";
31540
+ import { dirname as dirname11 } from "path";
31158
31541
 
31159
31542
  // packages/cli/src/utils/version-check.ts
31160
31543
  init_console();
31161
31544
  import { homedir as homedir3 } from "os";
31162
- import { join as join12 } from "path";
31545
+ import { join as join13 } from "path";
31163
31546
  import { readFile as readFile14, writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
31164
31547
  var NPM_REGISTRY_URL = "https://registry.npmjs.org/@promptscript/cli/latest";
31165
31548
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
31166
31549
  var FETCH_TIMEOUT_MS = 3e3;
31167
31550
  function getCacheDir() {
31168
- return join12(homedir3(), ".promptscript", ".cache");
31551
+ return join13(homedir3(), ".promptscript", ".cache");
31169
31552
  }
31170
31553
  function getCachePath() {
31171
- return join12(getCacheDir(), "version.json");
31554
+ return join13(getCacheDir(), "version.json");
31172
31555
  }
31173
31556
  async function readCache() {
31174
31557
  try {
@@ -31307,7 +31690,7 @@ function printUpdateNotification(info) {
31307
31690
 
31308
31691
  // packages/cli/src/commands/update-check.ts
31309
31692
  var __filename3 = fileURLToPath3(import.meta.url);
31310
- var __dirname3 = dirname10(__filename3);
31693
+ var __dirname3 = dirname11(__filename3);
31311
31694
  async function updateCheckCommand() {
31312
31695
  const currentVersion = getPackageVersion(__dirname3, "./package.json");
31313
31696
  console.log(`@promptscript/cli v${currentVersion}`);
@@ -31335,7 +31718,7 @@ init_services();
31335
31718
  init_console();
31336
31719
 
31337
31720
  // packages/cli/src/utils/registry-scaffolder.ts
31338
- import { join as join13 } from "path";
31721
+ import { join as join14 } from "path";
31339
31722
  var SEED_BASE = `@meta {
31340
31723
  id: "@core/base"
31341
31724
  syntax: "1.0.0"
@@ -31436,14 +31819,14 @@ async function scaffoldRegistry(options, services) {
31436
31819
  const createdFiles = [];
31437
31820
  await fs4.mkdir(directory, { recursive: true });
31438
31821
  const manifest = generateManifest(name, description, namespaces, seed);
31439
- const manifestPath = join13(directory, "registry-manifest.yaml");
31822
+ const manifestPath = join14(directory, "registry-manifest.yaml");
31440
31823
  await fs4.writeFile(manifestPath, manifest, "utf-8");
31441
31824
  createdFiles.push(manifestPath);
31442
31825
  const readme = generateReadme(name, description, namespaces);
31443
- const readmePath = join13(directory, "README.md");
31826
+ const readmePath = join14(directory, "README.md");
31444
31827
  await fs4.writeFile(readmePath, readme, "utf-8");
31445
31828
  createdFiles.push(readmePath);
31446
- const gitignorePath = join13(directory, ".gitignore");
31829
+ const gitignorePath = join14(directory, ".gitignore");
31447
31830
  await fs4.writeFile(
31448
31831
  gitignorePath,
31449
31832
  ".DS_Store\nnode_modules/\n*.log\n*.swp\n*.bak\n.env*\n",
@@ -31451,20 +31834,20 @@ async function scaffoldRegistry(options, services) {
31451
31834
  );
31452
31835
  createdFiles.push(gitignorePath);
31453
31836
  for (const ns of namespaces) {
31454
- const nsDir = join13(directory, ns);
31837
+ const nsDir = join14(directory, ns);
31455
31838
  await fs4.mkdir(nsDir, { recursive: true });
31456
31839
  if (seed && ns === "@core") {
31457
- const basePath = join13(nsDir, "base.prs");
31840
+ const basePath = join14(nsDir, "base.prs");
31458
31841
  await fs4.writeFile(basePath, SEED_BASE, "utf-8");
31459
31842
  createdFiles.push(basePath);
31460
- const qualityPath = join13(nsDir, "quality.prs");
31843
+ const qualityPath = join14(nsDir, "quality.prs");
31461
31844
  await fs4.writeFile(qualityPath, SEED_QUALITY, "utf-8");
31462
31845
  createdFiles.push(qualityPath);
31463
- const securityPath = join13(nsDir, "security.prs");
31846
+ const securityPath = join14(nsDir, "security.prs");
31464
31847
  await fs4.writeFile(securityPath, SEED_SECURITY, "utf-8");
31465
31848
  createdFiles.push(securityPath);
31466
31849
  } else {
31467
- const gitkeepPath = join13(nsDir, ".gitkeep");
31850
+ const gitkeepPath = join14(nsDir, ".gitkeep");
31468
31851
  await fs4.writeFile(gitkeepPath, "", "utf-8");
31469
31852
  createdFiles.push(gitkeepPath);
31470
31853
  }
@@ -31573,7 +31956,7 @@ Then reference configs with \`@inherit\` or \`@use\`:
31573
31956
  }
31574
31957
 
31575
31958
  // packages/cli/src/utils/resolve-target-directory.ts
31576
- import { join as join14, resolve as resolve12 } from "path";
31959
+ import { join as join15, resolve as resolve13 } from "path";
31577
31960
 
31578
31961
  // packages/cli/src/utils/slugify.ts
31579
31962
  function slugify(name) {
@@ -31584,7 +31967,7 @@ function slugify(name) {
31584
31967
  async function resolveTargetDirectory(options, services) {
31585
31968
  const { cwd, directoryArg, registryName, nonInteractive } = options;
31586
31969
  if (directoryArg) {
31587
- return resolve12(directoryArg);
31970
+ return resolve13(directoryArg);
31588
31971
  }
31589
31972
  const empty = await isDirectoryEmpty(cwd, services.fs);
31590
31973
  if (empty && nonInteractive) {
@@ -31598,9 +31981,9 @@ async function resolveTargetDirectory(options, services) {
31598
31981
  { name: `Subdirectory (./${slugify(registryName)})`, value: "subdirectory" }
31599
31982
  ]
31600
31983
  });
31601
- return choice === "here" ? cwd : join14(cwd, slugify(registryName));
31984
+ return choice === "here" ? cwd : join15(cwd, slugify(registryName));
31602
31985
  }
31603
- return join14(cwd, slugify(registryName));
31986
+ return join15(cwd, slugify(registryName));
31604
31987
  }
31605
31988
  async function isDirectoryEmpty(dir, fs4) {
31606
31989
  const entries = await fs4.readdir(dir);
@@ -31691,15 +32074,15 @@ async function registryInitCommand(directory, options, services = createDefaultS
31691
32074
  // packages/cli/src/commands/registry/validate.ts
31692
32075
  init_services();
31693
32076
  init_console();
31694
- import { resolve as resolve13 } from "path";
32077
+ import { resolve as resolve14 } from "path";
31695
32078
 
31696
32079
  // packages/cli/src/utils/registry-validator.ts
31697
- import { join as join15 } from "path";
32080
+ import { join as join16 } from "path";
31698
32081
  import { parse as parseYaml6 } from "yaml";
31699
32082
  async function validateRegistry(registryPath, services) {
31700
32083
  const { fs: fs4 } = services;
31701
32084
  const issues = [];
31702
- const manifestPath = join15(registryPath, "registry-manifest.yaml");
32085
+ const manifestPath = join16(registryPath, "registry-manifest.yaml");
31703
32086
  if (!fs4.existsSync(manifestPath)) {
31704
32087
  return {
31705
32088
  valid: false,
@@ -31791,7 +32174,7 @@ async function validateRegistry(registryPath, services) {
31791
32174
  });
31792
32175
  }
31793
32176
  if (entry.path) {
31794
- const prsPath = join15(registryPath, entry.path);
32177
+ const prsPath = join16(registryPath, entry.path);
31795
32178
  if (!fs4.existsSync(prsPath)) {
31796
32179
  issues.push({
31797
32180
  severity: "error",
@@ -31826,7 +32209,7 @@ async function validateRegistry(registryPath, services) {
31826
32209
  });
31827
32210
  }
31828
32211
  for (const ns of namespaceNames) {
31829
- const nsDir = join15(registryPath, ns);
32212
+ const nsDir = join16(registryPath, ns);
31830
32213
  if (fs4.existsSync(nsDir)) {
31831
32214
  try {
31832
32215
  const files = await fs4.readdir(nsDir);
@@ -31840,7 +32223,7 @@ async function validateRegistry(registryPath, services) {
31840
32223
  issues.push({
31841
32224
  severity: "warning",
31842
32225
  message: `Orphaned .prs file not in catalog: ${relativePath}`,
31843
- path: join15(nsDir, file)
32226
+ path: join16(nsDir, file)
31844
32227
  });
31845
32228
  }
31846
32229
  }
@@ -31891,7 +32274,7 @@ function detectCircularDependencies(catalog) {
31891
32274
 
31892
32275
  // packages/cli/src/commands/registry/validate.ts
31893
32276
  async function registryValidateCommand(path, options, services = createDefaultServices()) {
31894
- const registryPath = resolve13(path ?? ".");
32277
+ const registryPath = resolve14(path ?? ".");
31895
32278
  const spinner = createSpinner("Validating registry...").start();
31896
32279
  try {
31897
32280
  const result = await validateRegistry(registryPath, services);
@@ -31943,10 +32326,10 @@ async function registryValidateCommand(path, options, services = createDefaultSe
31943
32326
  // packages/cli/src/commands/registry/publish.ts
31944
32327
  init_services();
31945
32328
  init_console();
31946
- import { resolve as resolve14, join as join16 } from "path";
32329
+ import { resolve as resolve15, join as join17 } from "path";
31947
32330
  import { execFileSync as execFileSync2 } from "child_process";
31948
32331
  async function registryPublishCommand(path, options, services = createDefaultServices()) {
31949
- const registryPath = resolve14(path ?? ".");
32332
+ const registryPath = resolve15(path ?? ".");
31950
32333
  try {
31951
32334
  if (!options.force) {
31952
32335
  const spinner2 = createSpinner("Validating registry...").start();
@@ -31966,7 +32349,7 @@ async function registryPublishCommand(path, options, services = createDefaultSer
31966
32349
  `Registry valid (${result.stats.catalogEntries} entries, ${result.stats.prsFiles} files)`
31967
32350
  );
31968
32351
  }
31969
- const manifestPath = join16(registryPath, "registry-manifest.yaml");
32352
+ const manifestPath = join17(registryPath, "registry-manifest.yaml");
31970
32353
  if (services.fs.existsSync(manifestPath)) {
31971
32354
  const content = await services.fs.readFile(manifestPath, "utf-8");
31972
32355
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -32094,8 +32477,8 @@ async function registryListCommand() {
32094
32477
  // packages/cli/src/commands/registry/add.ts
32095
32478
  init_console();
32096
32479
  import { readFile as readFile16, writeFile as writeFile7, mkdir as mkdir7 } from "fs/promises";
32097
- import { existsSync as existsSync16 } from "fs";
32098
- import { dirname as dirname11 } from "path";
32480
+ import { existsSync as existsSync17 } from "fs";
32481
+ import { dirname as dirname12 } from "path";
32099
32482
  import { parse as parseYaml8, stringify as stringifyYaml } from "yaml";
32100
32483
  init_user_config();
32101
32484
  async function registryAddCommand(alias, url, options) {
@@ -32125,12 +32508,12 @@ async function registryAddCommand(alias, url, options) {
32125
32508
  }
32126
32509
  }
32127
32510
  async function addToGlobalConfig(alias, url) {
32128
- const configDir = dirname11(USER_CONFIG_PATH);
32129
- if (!existsSync16(configDir)) {
32511
+ const configDir = dirname12(USER_CONFIG_PATH);
32512
+ if (!existsSync17(configDir)) {
32130
32513
  await mkdir7(configDir, { recursive: true });
32131
32514
  }
32132
32515
  let config = { version: "1" };
32133
- if (existsSync16(USER_CONFIG_PATH)) {
32516
+ if (existsSync17(USER_CONFIG_PATH)) {
32134
32517
  const content = await readFile16(USER_CONFIG_PATH, "utf-8");
32135
32518
  const parsed = parseYaml8(content);
32136
32519
  if (parsed && typeof parsed === "object") {
@@ -32180,8 +32563,8 @@ function registerRegistryCommands(registry2) {
32180
32563
 
32181
32564
  // packages/cli/src/commands/lock.ts
32182
32565
  import { writeFile as writeFile8, readFile as readFile18 } from "fs/promises";
32183
- import { existsSync as existsSync18 } from "fs";
32184
- import { resolve as resolve15 } from "path";
32566
+ import { existsSync as existsSync19 } from "fs";
32567
+ import { resolve as resolve16 } from "path";
32185
32568
  import { parse as parseYaml9, stringify as stringifyYaml2 } from "yaml";
32186
32569
  init_console();
32187
32570
  init_src();
@@ -32189,9 +32572,9 @@ init_src();
32189
32572
  // packages/cli/src/commands/lock-scanner.ts
32190
32573
  init_src2();
32191
32574
  import { readFile as readFile17 } from "fs/promises";
32192
- import { existsSync as existsSync17 } from "fs";
32575
+ import { existsSync as existsSync18 } from "fs";
32193
32576
  async function collectRemoteImports(entryPath, options) {
32194
- if (!existsSync17(entryPath)) {
32577
+ if (!existsSync18(entryPath)) {
32195
32578
  return [];
32196
32579
  }
32197
32580
  const loader = new FileLoader({
@@ -32228,7 +32611,7 @@ async function collectRemoteImports(entryPath, options) {
32228
32611
  results.push(marker);
32229
32612
  }
32230
32613
  } else {
32231
- if (existsSync17(resolved)) {
32614
+ if (existsSync18(resolved)) {
32232
32615
  await scan(resolved);
32233
32616
  }
32234
32617
  }
@@ -32254,8 +32637,8 @@ async function lockCommand(options) {
32254
32637
  const aliases = config.registries ?? {};
32255
32638
  const aliasEntries = Object.entries(aliases);
32256
32639
  const projectRoot = process.cwd();
32257
- const localPath = resolve15(projectRoot, ".promptscript");
32258
- const entryPath = config.input?.entry ? resolve15(projectRoot, config.input.entry) : resolve15(localPath, "project.prs");
32640
+ const localPath = resolve16(projectRoot, ".promptscript");
32641
+ const entryPath = config.input?.entry ? resolve16(projectRoot, config.input.entry) : resolve16(localPath, "project.prs");
32259
32642
  const scannedImports = await collectRemoteImports(entryPath, {
32260
32643
  localPath,
32261
32644
  registries: config.registries
@@ -32266,7 +32649,7 @@ async function lockCommand(options) {
32266
32649
  return;
32267
32650
  }
32268
32651
  let existing = { version: LOCKFILE_VERSION, dependencies: {} };
32269
- if (existsSync18(LOCKFILE_PATH)) {
32652
+ if (existsSync19(LOCKFILE_PATH)) {
32270
32653
  try {
32271
32654
  const raw = await readFile18(LOCKFILE_PATH, "utf-8");
32272
32655
  const parsed = parseYaml9(raw, { maxAliasCount: 100 });
@@ -32335,7 +32718,7 @@ async function lockCommand(options) {
32335
32718
 
32336
32719
  // packages/cli/src/commands/update.ts
32337
32720
  import { writeFile as writeFile9, readFile as readFile19 } from "fs/promises";
32338
- import { existsSync as existsSync19 } from "fs";
32721
+ import { existsSync as existsSync20 } from "fs";
32339
32722
  import { parse as parseYaml10, stringify as stringifyYaml3 } from "yaml";
32340
32723
  init_console();
32341
32724
  init_src();
@@ -32358,7 +32741,7 @@ async function updateCommand(packageArg, options) {
32358
32741
  return;
32359
32742
  }
32360
32743
  let existing = { version: LOCKFILE_VERSION, dependencies: {} };
32361
- if (existsSync19(LOCKFILE_PATH)) {
32744
+ if (existsSync20(LOCKFILE_PATH)) {
32362
32745
  try {
32363
32746
  const raw = await readFile19(LOCKFILE_PATH, "utf-8");
32364
32747
  const parsed = parseYaml10(raw, { maxAliasCount: 100 });
@@ -32422,12 +32805,12 @@ async function updateCommand(packageArg, options) {
32422
32805
  init_console();
32423
32806
  init_src();
32424
32807
  import { readFile as readFile20, writeFile as writeFile10, mkdir as mkdir8, readdir as readdir5 } from "fs/promises";
32425
- import { existsSync as existsSync20 } from "fs";
32426
- import { join as join17, resolve as resolve16 } from "path";
32808
+ import { existsSync as existsSync21 } from "fs";
32809
+ import { join as join18, resolve as resolve17 } from "path";
32427
32810
  import { homedir as homedir4 } from "os";
32428
32811
  import { parse as parseYaml11 } from "yaml";
32429
32812
  var VENDOR_DIR = ".promptscript/vendor";
32430
- var CACHE_BASE = join17(homedir4(), ".promptscript", "cache", "registries");
32813
+ var CACHE_BASE = join18(homedir4(), ".promptscript", "cache", "registries");
32431
32814
  async function vendorSyncCommand(options) {
32432
32815
  const spinner = createSpinner("Syncing vendor directory...").start();
32433
32816
  try {
@@ -32445,13 +32828,13 @@ async function vendorSyncCommand(options) {
32445
32828
  }
32446
32829
  const results = [];
32447
32830
  for (const [repoUrl, dep] of deps) {
32448
- const cacheDir = join17(CACHE_BASE, repoUrl.replace(/[/:]/g, "_"), dep.version);
32449
- if (!existsSync20(cacheDir)) {
32831
+ const cacheDir = join18(CACHE_BASE, repoUrl.replace(/[/:]/g, "_"), dep.version);
32832
+ if (!existsSync21(cacheDir)) {
32450
32833
  results.push({ repoUrl, status: "missing" });
32451
32834
  continue;
32452
32835
  }
32453
32836
  if (!options.dryRun) {
32454
- const vendorTarget = resolve16(VENDOR_DIR, repoUrl.replace(/[/:]/g, "_"));
32837
+ const vendorTarget = resolve17(VENDOR_DIR, repoUrl.replace(/[/:]/g, "_"));
32455
32838
  await mkdir8(vendorTarget, { recursive: true });
32456
32839
  await copyDirectory(cacheDir, vendorTarget);
32457
32840
  }
@@ -32497,7 +32880,7 @@ async function vendorCheckCommand(_options) {
32497
32880
  spinner.warn("No dependencies in lockfile");
32498
32881
  return;
32499
32882
  }
32500
- if (!existsSync20(VENDOR_DIR)) {
32883
+ if (!existsSync21(VENDOR_DIR)) {
32501
32884
  spinner.fail("Vendor directory does not exist");
32502
32885
  ConsoleOutput.muted("Run: prs vendor sync");
32503
32886
  process.exitCode = 1;
@@ -32505,8 +32888,8 @@ async function vendorCheckCommand(_options) {
32505
32888
  }
32506
32889
  const issues = [];
32507
32890
  for (const [repoUrl] of deps) {
32508
- const vendorTarget = resolve16(VENDOR_DIR, repoUrl.replace(/[/:]/g, "_"));
32509
- if (!existsSync20(vendorTarget)) {
32891
+ const vendorTarget = resolve17(VENDOR_DIR, repoUrl.replace(/[/:]/g, "_"));
32892
+ if (!existsSync21(vendorTarget)) {
32510
32893
  issues.push({ repoUrl, issue: "missing from vendor directory" });
32511
32894
  }
32512
32895
  }
@@ -32529,7 +32912,7 @@ async function vendorCheckCommand(_options) {
32529
32912
  }
32530
32913
  }
32531
32914
  async function loadLockfile() {
32532
- if (!existsSync20(LOCKFILE_PATH)) {
32915
+ if (!existsSync21(LOCKFILE_PATH)) {
32533
32916
  return null;
32534
32917
  }
32535
32918
  try {
@@ -32543,8 +32926,8 @@ async function loadLockfile() {
32543
32926
  async function copyDirectory(src, dest) {
32544
32927
  const entries = await readdir5(src, { withFileTypes: true });
32545
32928
  for (const entry of entries) {
32546
- const srcPath = join17(src, entry.name);
32547
- const destPath = join17(dest, entry.name);
32929
+ const srcPath = join18(src, entry.name);
32930
+ const destPath = join18(dest, entry.name);
32548
32931
  if (entry.isDirectory()) {
32549
32932
  await mkdir8(destPath, { recursive: true });
32550
32933
  await copyDirectory(srcPath, destPath);
@@ -32556,12 +32939,12 @@ async function copyDirectory(src, dest) {
32556
32939
  }
32557
32940
 
32558
32941
  // packages/cli/src/commands/resolve-cmd.ts
32559
- import { existsSync as existsSync21 } from "fs";
32560
- import { join as join18 } from "path";
32942
+ import { existsSync as existsSync22 } from "fs";
32943
+ import { join as join19 } from "path";
32561
32944
  import { homedir as homedir5 } from "os";
32562
32945
  init_user_config();
32563
32946
  init_console();
32564
- var CACHE_BASE2 = join18(homedir5(), ".promptscript", "cache", "registries");
32947
+ var CACHE_BASE2 = join19(homedir5(), ".promptscript", "cache", "registries");
32565
32948
  async function resolveCommand(importPath, _options) {
32566
32949
  try {
32567
32950
  const configFile = findConfigFile();
@@ -32603,11 +32986,11 @@ async function resolveCommand(importPath, _options) {
32603
32986
  }
32604
32987
  const { repoUrl, path, version } = expanded;
32605
32988
  const cacheVersion = version ?? "latest";
32606
- const cacheDir = join18(CACHE_BASE2, repoUrl.replace(/[/:]/g, "_"), cacheVersion);
32607
- const cacheExists = existsSync21(cacheDir);
32989
+ const cacheDir = join19(CACHE_BASE2, repoUrl.replace(/[/:]/g, "_"), cacheVersion);
32990
+ const cacheExists = existsSync22(cacheDir);
32608
32991
  const filePath = path.endsWith(".prs") ? path : `${path}.prs`;
32609
- const cachedFile = join18(cacheDir, filePath);
32610
- const fileExists3 = cacheExists && existsSync21(cachedFile);
32992
+ const cachedFile = join19(cacheDir, filePath);
32993
+ const fileExists3 = cacheExists && existsSync22(cachedFile);
32611
32994
  ConsoleOutput.newline();
32612
32995
  console.log(` Alias: ${aliasName} \u2192 ${repoUrl}`);
32613
32996
  console.log(` Repo: ${repoUrl}`);
@@ -32624,7 +33007,7 @@ async function resolveCommand(importPath, _options) {
32624
33007
  ConsoleOutput.newline();
32625
33008
  } else {
32626
33009
  const resolvedPath = importPath.endsWith(".prs") ? importPath : `${importPath}.prs`;
32627
- const exists = existsSync21(resolvedPath);
33010
+ const exists = existsSync22(resolvedPath);
32628
33011
  ConsoleOutput.newline();
32629
33012
  console.log(` Import: ${importPath}`);
32630
33013
  console.log(` Type: local file`);
@@ -32641,8 +33024,8 @@ async function resolveCommand(importPath, _options) {
32641
33024
  }
32642
33025
 
32643
33026
  // packages/cli/src/commands/inspect.ts
32644
- import { existsSync as existsSync22 } from "fs";
32645
- import { resolve as resolve17, basename as basename12 } from "path";
33027
+ import { existsSync as existsSync23 } from "fs";
33028
+ import { resolve as resolve18, basename as basename13 } from "path";
32646
33029
  init_console();
32647
33030
  async function inspectCommand(skillName, options) {
32648
33031
  const isJson = options.format === "json";
@@ -32655,8 +33038,8 @@ async function inspectCommand(skillName, options) {
32655
33038
  localPath: "./.promptscript",
32656
33039
  registries: config.registries
32657
33040
  });
32658
- const entryPath = resolve17("./.promptscript/project.prs");
32659
- if (!existsSync22(entryPath)) {
33041
+ const entryPath = resolve18("./.promptscript/project.prs");
33042
+ if (!existsSync23(entryPath)) {
32660
33043
  spinner.stop();
32661
33044
  ConsoleOutput.error(`Entry file not found: ${entryPath}`);
32662
33045
  process.exitCode = 1;
@@ -32728,7 +33111,7 @@ function summarizeValue(val) {
32728
33111
  return String(val);
32729
33112
  }
32730
33113
  function shortPath(source) {
32731
- return basename12(source);
33114
+ return basename13(source);
32732
33115
  }
32733
33116
  function getPropertySource(propName, trace, baseSource) {
32734
33117
  for (let i = trace.length - 1; i >= 0; i--) {
@@ -32836,8 +33219,8 @@ init_console();
32836
33219
  // packages/cli/src/commands/import.ts
32837
33220
  init_src3();
32838
33221
  init_console();
32839
- import { resolve as resolve18, join as join19, basename as basename13 } from "path";
32840
- import { writeFileSync as writeFileSync2, mkdirSync, existsSync as existsSync23 } from "fs";
33222
+ import { resolve as resolve19, join as join20, basename as basename14 } from "path";
33223
+ import { writeFileSync as writeFileSync2, mkdirSync, existsSync as existsSync24 } from "fs";
32841
33224
  import { stat as stat2, readdir as readdir6, readFile as readFile21, writeFile as writeFile11, mkdir as mkdir9 } from "fs/promises";
32842
33225
  function parseInstructionFrontmatter(content) {
32843
33226
  const match = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/.exec(content);
@@ -32865,10 +33248,10 @@ async function importDirectory(dirPath) {
32865
33248
  }
32866
33249
  const lines = ["@meta {", ' version: "1.0"', "}", "", "@guards {"];
32867
33250
  for (const filename of instructionFiles) {
32868
- const filePath = join19(dirPath, filename);
33251
+ const filePath = join20(dirPath, filename);
32869
33252
  const content = await readFile21(filePath, "utf-8");
32870
33253
  const frontmatter = parseInstructionFrontmatter(content);
32871
- const entryName = basename13(filename).replace(/\.instructions\.md$/i, "");
33254
+ const entryName = basename14(filename).replace(/\.instructions\.md$/i, "");
32872
33255
  if (frontmatter) {
32873
33256
  const globPatterns = frontmatter.applyTo.map((p) => `"${p}"`).join(", ");
32874
33257
  lines.push(` ${entryName}(${globPatterns}) {`);
@@ -32891,7 +33274,7 @@ async function importDirectory(dirPath) {
32891
33274
  return lines.join("\n");
32892
33275
  }
32893
33276
  async function importCommand(file, options) {
32894
- const filepath = resolve18(file);
33277
+ const filepath = resolve19(file);
32895
33278
  const inputStat = await stat2(filepath).catch(() => null);
32896
33279
  if (inputStat?.isDirectory()) {
32897
33280
  const prsContent = await importDirectory(filepath);
@@ -32901,7 +33284,7 @@ async function importCommand(file, options) {
32901
33284
  }
32902
33285
  const outputDir = options.output ?? ".promptscript";
32903
33286
  await mkdir9(outputDir, { recursive: true });
32904
- const outputPath = join19(outputDir, "imported.prs");
33287
+ const outputPath = join20(outputDir, "imported.prs");
32905
33288
  await writeFile11(outputPath, prsContent);
32906
33289
  console.log(`\u2714 Imported directory to ${outputPath}`);
32907
33290
  return;
@@ -32923,11 +33306,11 @@ async function importCommand(file, options) {
32923
33306
  console.log(result.prsContent);
32924
33307
  return;
32925
33308
  }
32926
- const outputDir = resolve18(options.output ?? ".promptscript");
32927
- if (!existsSync23(outputDir)) {
33309
+ const outputDir = resolve19(options.output ?? ".promptscript");
33310
+ if (!existsSync24(outputDir)) {
32928
33311
  mkdirSync(outputDir, { recursive: true });
32929
33312
  }
32930
- const outputPath = resolve18(outputDir, "imported.prs");
33313
+ const outputPath = resolve19(outputDir, "imported.prs");
32931
33314
  writeFileSync2(outputPath, result.prsContent, "utf-8");
32932
33315
  ConsoleOutput.success(`Written to ${outputPath}`);
32933
33316
  if (options.validate) {
@@ -33042,18 +33425,18 @@ async function detectMarker(filePath) {
33042
33425
  }
33043
33426
 
33044
33427
  // packages/cli/src/hooks/debounce-lock.ts
33045
- import { createHash as createHash2 } from "node:crypto";
33046
- import { existsSync as existsSync24, writeFileSync as writeFileSync4, unlinkSync, statSync } from "node:fs";
33428
+ import { createHash as createHash3 } from "node:crypto";
33429
+ import { existsSync as existsSync25, writeFileSync as writeFileSync4, unlinkSync, statSync } from "node:fs";
33047
33430
  import { tmpdir } from "node:os";
33048
- import { join as join20 } from "node:path";
33431
+ import { join as join21 } from "node:path";
33049
33432
  var STALE_THRESHOLD_MS = 3e4;
33050
33433
  function getLockPath(projectDir) {
33051
- const hash = createHash2("md5").update(projectDir).digest("hex").slice(0, 12);
33052
- return join20(tmpdir(), `prs-compile-${hash}.lock`);
33434
+ const hash = createHash3("md5").update(projectDir).digest("hex").slice(0, 12);
33435
+ return join21(tmpdir(), `prs-compile-${hash}.lock`);
33053
33436
  }
33054
33437
  function acquireLock(projectDir) {
33055
33438
  const lockPath = getLockPath(projectDir);
33056
- if (existsSync24(lockPath)) {
33439
+ if (existsSync25(lockPath)) {
33057
33440
  try {
33058
33441
  const stat4 = statSync(lockPath);
33059
33442
  const age = Date.now() - stat4.mtimeMs;
@@ -33161,9 +33544,10 @@ Usage: echo '{"file_path":"..."}' | prs hook ${action}
33161
33544
  init_hooks();
33162
33545
 
33163
33546
  // packages/cli/src/commands/skills.ts
33164
- import { writeFile as writeFile12, readFile as readFile23, readdir as readdir7 } from "fs/promises";
33165
- import { resolve as resolve19 } from "path";
33166
- import { existsSync as existsSync25 } from "fs";
33547
+ import { writeFile as writeFile12, readFile as readFile23, readdir as readdir7, mkdtemp, rm } from "fs/promises";
33548
+ import { resolve as resolve20, join as join22, basename as basename15, dirname as dirname13 } from "path";
33549
+ import { existsSync as existsSync26 } from "fs";
33550
+ import { tmpdir as tmpdir2 } from "os";
33167
33551
  import { parse as parseYaml12, stringify as stringifyYaml4 } from "yaml";
33168
33552
  init_console();
33169
33553
  init_src();
@@ -33183,6 +33567,77 @@ function normalizeSkillSource(input2) {
33183
33567
  }
33184
33568
  return source;
33185
33569
  }
33570
+ function extractSubPath(source) {
33571
+ const parts = source.replace(/^https?:\/\//, "").split("/");
33572
+ return parts.length > 3 ? parts.slice(3).join("/") : "";
33573
+ }
33574
+ function deriveSkillFolderName(source) {
33575
+ const subPath = extractSubPath(source);
33576
+ if (!subPath) return void 0;
33577
+ if (!subPath.toLowerCase().endsWith(".md")) return void 0;
33578
+ const parent = dirname13(subPath);
33579
+ if (parent === "." || parent === "") return void 0;
33580
+ return basename15(parent);
33581
+ }
33582
+ function collectExistingSkillNames(lockfile, exclude) {
33583
+ const names = /* @__PURE__ */ new Set();
33584
+ for (const [key, dep] of Object.entries(lockfile.dependencies)) {
33585
+ if (dep.source !== "md") continue;
33586
+ if (exclude && key === exclude) continue;
33587
+ const name = deriveSkillFolderName(key);
33588
+ if (name) names.add(name);
33589
+ }
33590
+ return names;
33591
+ }
33592
+ async function fetchAndValidateRemoteSkill(source, options) {
33593
+ const subPath = extractSubPath(source);
33594
+ if (!subPath || !subPath.toLowerCase().endsWith(".md")) {
33595
+ return void 0;
33596
+ }
33597
+ const repoUrl = extractRepoUrl(source);
33598
+ const tmp = await mkdtemp(join22(tmpdir2(), "prs-skill-validate-"));
33599
+ try {
33600
+ const gitRegistry = createGitRegistry({ url: repoUrl });
33601
+ await gitRegistry.cloneAtTag(repoUrl, options.version, tmp);
33602
+ const filePath = join22(tmp, subPath);
33603
+ if (!existsSync26(filePath)) {
33604
+ return {
33605
+ content: "",
33606
+ integrity: "",
33607
+ valid: false,
33608
+ issues: [
33609
+ {
33610
+ severity: "error",
33611
+ code: "SK000",
33612
+ message: `File '${subPath}' does not exist in ${repoUrl} at ${options.version}.`
33613
+ }
33614
+ ]
33615
+ };
33616
+ }
33617
+ const content = await readFile23(filePath, "utf-8");
33618
+ const integrity = hashContent(Buffer.from(content, "utf-8"));
33619
+ const result = validateSkillFrontmatter(content, {
33620
+ filePath,
33621
+ existingNames: options.existingNames
33622
+ });
33623
+ return { content, integrity, valid: result.valid, issues: result.issues };
33624
+ } finally {
33625
+ await rm(tmp, { recursive: true, force: true }).catch(() => {
33626
+ });
33627
+ }
33628
+ }
33629
+ function printValidationIssues(issues) {
33630
+ if (issues.length === 0) return;
33631
+ ConsoleOutput.newline();
33632
+ const formatted = formatSkillValidationIssues(issues);
33633
+ for (const line of formatted.split("\n")) {
33634
+ if (line.includes("\u2717")) {
33635
+ ConsoleOutput.error(line);
33636
+ } else {
33637
+ ConsoleOutput.warn(line);
33638
+ }
33639
+ }
33640
+ }
33186
33641
  var HEADER_DIRECTIVES = ["@use ", "@inherit ", "@meta "];
33187
33642
  function extractRepoUrl(source) {
33188
33643
  const parts = source.replace(/^https?:\/\//, "").split("/");
@@ -33210,8 +33665,8 @@ function validateRemoteSource(source) {
33210
33665
  }
33211
33666
  async function resolveEntryFile(fileFlag) {
33212
33667
  if (fileFlag) {
33213
- const resolved = resolve19(fileFlag);
33214
- if (!existsSync25(resolved)) {
33668
+ const resolved = resolve20(fileFlag);
33669
+ if (!existsSync26(resolved)) {
33215
33670
  throw new Error(`File not found: ${resolved}`);
33216
33671
  }
33217
33672
  return resolved;
@@ -33221,24 +33676,24 @@ async function resolveEntryFile(fileFlag) {
33221
33676
  try {
33222
33677
  const config = await loadConfig();
33223
33678
  if (config.input?.entry) {
33224
- const entryPath = resolve19(config.input.entry);
33225
- if (existsSync25(entryPath)) {
33679
+ const entryPath = resolve20(config.input.entry);
33680
+ if (existsSync26(entryPath)) {
33226
33681
  return entryPath;
33227
33682
  }
33228
33683
  }
33229
33684
  } catch {
33230
33685
  }
33231
33686
  }
33232
- const promptscriptDir = resolve19(".promptscript");
33233
- if (existsSync25(promptscriptDir)) {
33687
+ const promptscriptDir = resolve20(".promptscript");
33688
+ if (existsSync26(promptscriptDir)) {
33234
33689
  const entries = await readdir7(promptscriptDir);
33235
33690
  const prsFiles = entries.filter((f) => f.endsWith(".prs"));
33236
33691
  if (prsFiles.length === 1) {
33237
- return resolve19(promptscriptDir, prsFiles[0]);
33692
+ return resolve20(promptscriptDir, prsFiles[0]);
33238
33693
  }
33239
33694
  if (prsFiles.length > 1) {
33240
33695
  if (prsFiles.includes("project.prs")) {
33241
- return resolve19(promptscriptDir, "project.prs");
33696
+ return resolve20(promptscriptDir, "project.prs");
33242
33697
  }
33243
33698
  throw new Error(
33244
33699
  `Multiple .prs files found in .promptscript/. Use --file to specify which one.`
@@ -33282,7 +33737,7 @@ function extractUsePath(line) {
33282
33737
  }
33283
33738
  async function loadLockfile2() {
33284
33739
  const defaultLockfile = { version: LOCKFILE_VERSION, dependencies: {} };
33285
- if (!existsSync25(LOCKFILE_PATH)) {
33740
+ if (!existsSync26(LOCKFILE_PATH)) {
33286
33741
  return defaultLockfile;
33287
33742
  }
33288
33743
  try {
@@ -33301,6 +33756,14 @@ async function saveLockfile(lockfile) {
33301
33756
  async function skillsAddCommand(rawSource, options) {
33302
33757
  const spinner = createSpinner("Adding skill...").start();
33303
33758
  try {
33759
+ if (/^http:\/\//i.test(rawSource.trim())) {
33760
+ spinner.fail("Plain HTTP sources are not allowed (security)");
33761
+ ConsoleOutput.error(
33762
+ "Use https:// or git@host:owner/repo. Cleartext git transport is rejected to prevent man-in-the-middle attacks."
33763
+ );
33764
+ process.exitCode = 1;
33765
+ return;
33766
+ }
33304
33767
  const source = normalizeSkillSource(rawSource);
33305
33768
  const sourceError = validateRemoteSource(source);
33306
33769
  if (sourceError) {
@@ -33329,15 +33792,52 @@ async function skillsAddCommand(rawSource, options) {
33329
33792
  process.exitCode = 1;
33330
33793
  return;
33331
33794
  }
33795
+ const lockfile = await loadLockfile2();
33796
+ let integrity = "sha256-pending";
33797
+ let validationFailed = false;
33798
+ if (!options.skipValidation) {
33799
+ spinner.text = "Validating SKILL.md frontmatter...";
33800
+ try {
33801
+ const fetched = await fetchAndValidateRemoteSkill(source, {
33802
+ existingNames: collectExistingSkillNames(lockfile),
33803
+ version: "main"
33804
+ });
33805
+ if (fetched) {
33806
+ integrity = fetched.integrity || integrity;
33807
+ const hasErrors = !fetched.valid;
33808
+ const hasWarnings = fetched.issues.some((i) => i.severity === "warning");
33809
+ if (hasErrors || options.strict && hasWarnings) {
33810
+ spinner.fail("SKILL.md failed validation");
33811
+ printValidationIssues(fetched.issues);
33812
+ ConsoleOutput.newline();
33813
+ ConsoleOutput.muted(
33814
+ "Re-run with --skip-validation to bypass (not recommended) or fix the upstream SKILL.md."
33815
+ );
33816
+ process.exitCode = 1;
33817
+ validationFailed = true;
33818
+ } else if (fetched.issues.length > 0) {
33819
+ spinner.warn("SKILL.md has validation warnings");
33820
+ printValidationIssues(fetched.issues);
33821
+ spinner.start("Adding skill...");
33822
+ }
33823
+ }
33824
+ } catch (err) {
33825
+ spinner.warn("Could not validate SKILL.md ahead of time");
33826
+ ConsoleOutput.muted(
33827
+ `Reason: ${err instanceof Error ? err.message : String(err)}. Proceeding without frontmatter check.`
33828
+ );
33829
+ spinner.start("Adding skill...");
33830
+ }
33831
+ }
33832
+ if (validationFailed) return;
33332
33833
  const insertionPoint = findInsertionPoint(lines);
33333
33834
  const newLine = `@use ${source}`;
33334
33835
  lines.splice(insertionPoint, 0, newLine);
33335
33836
  const updatedContent = lines.join("\n");
33336
- const lockfile = await loadLockfile2();
33337
33837
  const lockEntry = {
33338
33838
  version: "latest",
33339
33839
  commit: validation.headCommit ?? "0000000000000000000000000000000000000000",
33340
- integrity: "sha256-pending",
33840
+ integrity,
33341
33841
  source: "md"
33342
33842
  };
33343
33843
  lockfile.dependencies[source] = lockEntry;
@@ -33465,10 +33965,39 @@ async function skillsUpdateCommand(name, options) {
33465
33965
  });
33466
33966
  continue;
33467
33967
  }
33968
+ let integrity = dep.integrity ?? "sha256-pending";
33969
+ if (!options.skipValidation) {
33970
+ try {
33971
+ const fetched = await fetchAndValidateRemoteSkill(key, {
33972
+ existingNames: collectExistingSkillNames(lockfile, key),
33973
+ version: dep.version === "latest" ? "main" : dep.version
33974
+ });
33975
+ if (fetched) {
33976
+ integrity = fetched.integrity || integrity;
33977
+ const hasErrors = !fetched.valid;
33978
+ const hasWarnings = fetched.issues.some((i) => i.severity === "warning");
33979
+ if (hasErrors || options.strict && hasWarnings) {
33980
+ skipped.push({
33981
+ key,
33982
+ reason: "SKILL.md failed validation after update"
33983
+ });
33984
+ printValidationIssues(fetched.issues);
33985
+ continue;
33986
+ } else if (hasWarnings) {
33987
+ ConsoleOutput.warn(`${key}: validation warnings`);
33988
+ printValidationIssues(fetched.issues);
33989
+ }
33990
+ }
33991
+ } catch (err) {
33992
+ ConsoleOutput.warn(
33993
+ `${key}: could not re-validate (${err instanceof Error ? err.message : String(err)})`
33994
+ );
33995
+ }
33996
+ }
33468
33997
  lockfile.dependencies[key] = {
33469
33998
  version: dep.version ?? "latest",
33470
33999
  commit: validation.headCommit,
33471
- integrity: dep.integrity ?? "sha256-pending",
34000
+ integrity,
33472
34001
  source: "md",
33473
34002
  fetchedAt
33474
34003
  };
@@ -33515,7 +34044,7 @@ async function skillsUpdateCommand(name, options) {
33515
34044
 
33516
34045
  // packages/cli/src/cli.ts
33517
34046
  var __filename4 = fileURLToPath4(import.meta.url);
33518
- var __dirname4 = dirname14(__filename4);
34047
+ var __dirname4 = dirname16(__filename4);
33519
34048
  var program = new Command();
33520
34049
  program.name("prs").description("PromptScript CLI - Standardize AI instructions").version(getPackageVersion(__dirname3, "./package.json")).option("--verbose", "Enable verbose output").option("--debug", "Enable debug output (includes verbose)").option("--quiet", "Suppress non-error output").hook("preAction", async (thisCommand, actionCommand) => {
33521
34050
  const opts = thisCommand.opts();
@@ -33574,10 +34103,10 @@ program.command("resolve <import>").description("Show full resolution chain for
33574
34103
  program.command("hook").argument("<action>", "Hook action: pre-edit or post-edit").description("Hook handler invoked by AI tools (not for direct use)").action(hookCommand);
33575
34104
  program.command("hooks").argument("<action>", "Action: install or uninstall").argument("[tool]", "Tool: claude, gemini, copilot, cursor, windsurf, factory, cline").option("--all", "Install/uninstall for all detected tools").description("Manage PromptScript hooks for AI tools").action(hooksCommand);
33576
34105
  var skills = program.command("skills").description("Manage PromptScript skills");
33577
- skills.command("add <source>").description("Add a remote skill to the project").option("-f, --file <file>", "Target .prs file to modify").option("--dry-run", "Preview changes without writing").action((source, opts) => skillsAddCommand(source, opts));
34106
+ skills.command("add <source>").description("Add a remote skill to the project").option("-f, --file <file>", "Target .prs file to modify").option("--dry-run", "Preview changes without writing").option("--skip-validation", "Skip SKILL.md frontmatter validation (not recommended)").option("--strict", "Treat validation warnings as errors").action((source, opts) => skillsAddCommand(source, opts));
33578
34107
  skills.command("remove <name>").description("Remove a skill from the project").option("--dry-run", "Preview changes without writing").action((name, opts) => skillsRemoveCommand(name, opts));
33579
34108
  skills.command("list").description("List skills imported in the current project").action(() => skillsListCommand());
33580
- skills.command("update [name]").description("Update lock entries for markdown-sourced skills").option("--dry-run", "Preview changes without writing").action((name, opts) => skillsUpdateCommand(name, opts));
34109
+ skills.command("update [name]").description("Update lock entries for markdown-sourced skills").option("--dry-run", "Preview changes without writing").option("--skip-validation", "Skip SKILL.md frontmatter validation (not recommended)").option("--strict", "Treat validation warnings as errors").action((name, opts) => skillsUpdateCommand(name, opts));
33581
34110
  var registry = program.command("registry").description("Manage PromptScript registries");
33582
34111
  registerRegistryCommands(registry);
33583
34112
  function run(args = process.argv) {