@promptscript/cli 1.12.1 → 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
  */
@@ -16365,6 +16373,24 @@ ${lines.join("\n")}
16365
16373
  }
16366
16374
  return filtered;
16367
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
+ }
16368
16394
  };
16369
16395
  }
16370
16396
  });
@@ -20847,9 +20873,9 @@ var require_rate_limit = __commonJS({
20847
20873
  let ttl = 0;
20848
20874
  let ttlInSeconds = 0;
20849
20875
  try {
20850
- const res = await new Promise((resolve21, reject) => {
20876
+ const res = await new Promise((resolve22, reject) => {
20851
20877
  store.incr(key, (err, res2) => {
20852
- err ? reject(err) : resolve21(res2);
20878
+ err ? reject(err) : resolve22(res2);
20853
20879
  }, timeWindow, max);
20854
20880
  });
20855
20881
  current = res.current;
@@ -20956,12 +20982,12 @@ var init_health = __esm({
20956
20982
 
20957
20983
  // packages/server/src/source-dirs.ts
20958
20984
  import { readFile as readFile24 } from "fs/promises";
20959
- import { join as join21, dirname as dirname12 } from "path";
20985
+ import { join as join23, dirname as dirname14 } from "path";
20960
20986
  import { parse as parseYaml13 } from "yaml";
20961
20987
  async function readProjectConfig(workspace) {
20962
20988
  for (const filename of CONFIG_FILENAMES) {
20963
20989
  try {
20964
- const raw = await readFile24(join21(workspace, filename), "utf-8");
20990
+ const raw = await readFile24(join23(workspace, filename), "utf-8");
20965
20991
  const config = parseYaml13(raw);
20966
20992
  if (config) return config;
20967
20993
  } catch {
@@ -20972,10 +20998,10 @@ async function readProjectConfig(workspace) {
20972
20998
  async function resolveSourceDir(workspace) {
20973
20999
  for (const filename of CONFIG_FILENAMES) {
20974
21000
  try {
20975
- const raw = await readFile24(join21(workspace, filename), "utf-8");
21001
+ const raw = await readFile24(join23(workspace, filename), "utf-8");
20976
21002
  const config = parseYaml13(raw);
20977
21003
  if (config?.input?.entry) {
20978
- return dirname12(config.input.entry);
21004
+ return dirname14(config.input.entry);
20979
21005
  }
20980
21006
  } catch {
20981
21007
  }
@@ -20989,9 +21015,9 @@ async function resolveFileGlobs(workspace) {
20989
21015
  async function resolveWatchPaths(workspace) {
20990
21016
  const sourceDir = await resolveSourceDir(workspace);
20991
21017
  return [
20992
- join21(workspace, sourceDir),
20993
- join21(workspace, "promptscript.yaml"),
20994
- join21(workspace, "promptscript.yml")
21018
+ join23(workspace, sourceDir),
21019
+ join23(workspace, "promptscript.yaml"),
21020
+ join23(workspace, "promptscript.yml")
20995
21021
  ];
20996
21022
  }
20997
21023
  var DEFAULT_SOURCE_DIR, CONFIG_FILENAMES;
@@ -21018,13 +21044,13 @@ var init_config2 = __esm({
21018
21044
  });
21019
21045
 
21020
21046
  // packages/server/src/path-guard.ts
21021
- 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";
21022
21048
  function resolveSafePath(workspace, requestedPath) {
21023
21049
  const decoded = decodeURIComponent(requestedPath);
21024
21050
  if (isAbsolute6(decoded)) {
21025
21051
  throw new PathTraversalError(requestedPath);
21026
21052
  }
21027
- const resolved = resolve20(workspace, decoded);
21053
+ const resolved = resolve21(workspace, decoded);
21028
21054
  const rel = relative4(workspace, resolved);
21029
21055
  if (rel.startsWith("..") || isAbsolute6(rel)) {
21030
21056
  throw new PathTraversalError(requestedPath);
@@ -21046,7 +21072,7 @@ var init_path_guard = __esm({
21046
21072
 
21047
21073
  // packages/server/src/routes/files.ts
21048
21074
  import { readFile as readFile25, writeFile as writeFile13, unlink as unlink2, stat as stat3, mkdir as mkdir10 } from "fs/promises";
21049
- import { dirname as dirname13 } from "path";
21075
+ import { dirname as dirname15 } from "path";
21050
21076
  import fg from "fast-glob";
21051
21077
  function registerRoutes(app, workspace, readOnly) {
21052
21078
  app.get("/api/files", async () => {
@@ -21106,7 +21132,7 @@ function registerRoutes(app, workspace, readOnly) {
21106
21132
  return reply.status(409).send({ error: "File already exists" });
21107
21133
  } catch {
21108
21134
  }
21109
- await mkdir10(dirname13(resolved), { recursive: true });
21135
+ await mkdir10(dirname15(resolved), { recursive: true });
21110
21136
  await writeFile13(resolved, request.body.content, "utf-8");
21111
21137
  return reply.status(201).send({ path: filePath, status: "created" });
21112
21138
  } catch (err) {
@@ -21332,14 +21358,14 @@ init_src();
21332
21358
  init_init();
21333
21359
  import { Command } from "commander";
21334
21360
  import { fileURLToPath as fileURLToPath4 } from "url";
21335
- import { dirname as dirname14 } from "path";
21361
+ import { dirname as dirname16 } from "path";
21336
21362
 
21337
21363
  // packages/cli/src/commands/compile.ts
21338
21364
  init_src();
21339
- 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";
21340
21366
  import { fileURLToPath as fileURLToPath2 } from "url";
21341
21367
  import { writeFile as writeFile4, mkdir as mkdir4, readFile as readFile12 } from "fs/promises";
21342
- import { existsSync as existsSync11 } from "fs";
21368
+ import { existsSync as existsSync12 } from "fs";
21343
21369
  import chokidar from "chokidar";
21344
21370
 
21345
21371
  // packages/cli/src/config/loader.ts
@@ -21403,6 +21429,56 @@ async function loadConfig(customPath) {
21403
21429
 
21404
21430
  // packages/cli/src/commands/compile.ts
21405
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
21406
21482
  init_console();
21407
21483
 
21408
21484
  // packages/compiler/src/compiler.ts
@@ -21419,7 +21495,7 @@ import { basename as basename11, dirname as dirname7, join as join8 } from "path
21419
21495
  // packages/resolver/src/loader.ts
21420
21496
  init_src();
21421
21497
  import { readFile as readFile7 } from "fs/promises";
21422
- 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";
21423
21499
 
21424
21500
  // packages/resolver/src/alias-resolver.ts
21425
21501
  init_src();
@@ -21509,8 +21585,8 @@ var FileLoader = class {
21509
21585
  options;
21510
21586
  constructor(options) {
21511
21587
  this.registryPath = options.registryPath;
21512
- this.localPath = resolve4(options.localPath ?? process.cwd());
21513
- this.projectRoot = resolve4(
21588
+ this.localPath = resolve5(options.localPath ?? process.cwd());
21589
+ this.projectRoot = resolve5(
21514
21590
  options.projectRoot ?? (basename7(this.localPath) === ".promptscript" ? dirname5(this.localPath) : this.localPath)
21515
21591
  );
21516
21592
  this.options = options;
@@ -21553,11 +21629,11 @@ var FileLoader = class {
21553
21629
  const namespace = match[1];
21554
21630
  const segments = match[2];
21555
21631
  const fileName2 = segments.endsWith(".prs") || segments.endsWith(".md") ? segments : `${segments}.prs`;
21556
- return resolve4(this.registryPath, `@${namespace}`, fileName2);
21632
+ return resolve5(this.registryPath, `@${namespace}`, fileName2);
21557
21633
  }
21558
21634
  }
21559
21635
  const fileName = path.endsWith(".prs") || path.endsWith(".md") ? path : `${path}.prs`;
21560
- return resolve4(this.localPath, fileName);
21636
+ return resolve5(this.localPath, fileName);
21561
21637
  }
21562
21638
  /**
21563
21639
  * Resolve a PathReference to an absolute path.
@@ -21570,7 +21646,7 @@ var FileLoader = class {
21570
21646
  if (ref.isRelative) {
21571
21647
  const dir = dirname5(fromFile);
21572
21648
  const rawPath = ref.raw.endsWith(".prs") || ref.raw.endsWith(".md") ? ref.raw : `${ref.raw}.prs`;
21573
- const resolved = resolve4(dir, rawPath);
21649
+ const resolved = resolve5(dir, rawPath);
21574
21650
  const rel = relative(this.projectRoot, resolved);
21575
21651
  if (rel.startsWith("..") || isAbsolute2(rel)) {
21576
21652
  throw new Error(`Path traversal outside project root is not allowed: ${ref.raw}`);
@@ -22511,7 +22587,7 @@ function uniqueConcat3(parent, child) {
22511
22587
  // packages/resolver/src/skills.ts
22512
22588
  init_src();
22513
22589
  import { readFile as readFile8, readdir as readdir2, access, lstat, realpath } from "fs/promises";
22514
- 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";
22515
22591
  function parseSkillMd(content) {
22516
22592
  const lines = content.split("\n");
22517
22593
  let inFrontmatter = false;
@@ -22957,10 +23033,10 @@ function parseSkillIgnore(content) {
22957
23033
  return { patterns };
22958
23034
  }
22959
23035
  function isIgnoredByRules(relPath, rules) {
22960
- const basename14 = relPath.split("/").pop() ?? relPath;
23036
+ const basename16 = relPath.split("/").pop() ?? relPath;
22961
23037
  let ignored = false;
22962
23038
  for (const { regex, matchPath, negated } of rules.patterns) {
22963
- const target = matchPath ? relPath : basename14;
23039
+ const target = matchPath ? relPath : basename16;
22964
23040
  if (regex.test(target)) {
22965
23041
  ignored = !negated;
22966
23042
  }
@@ -22968,7 +23044,7 @@ function isIgnoredByRules(relPath, rules) {
22968
23044
  return ignored;
22969
23045
  }
22970
23046
  async function loadSkillIgnore(skillDir) {
22971
- const ignorePath = resolve5(skillDir, ".skillignore");
23047
+ const ignorePath = resolve6(skillDir, ".skillignore");
22972
23048
  try {
22973
23049
  const content = await readFile8(ignorePath, "utf-8");
22974
23050
  return parseSkillIgnore(content);
@@ -23014,7 +23090,7 @@ async function discoverSkillResources(skillDir, logger = noopLogger2) {
23014
23090
  }
23015
23091
  if (entry.isSymbolicLink()) continue;
23016
23092
  if (SKIP_FILES.has(entry.name)) continue;
23017
- const fullPath = resolve5(entry.parentPath, entry.name);
23093
+ const fullPath = resolve6(entry.parentPath, entry.name);
23018
23094
  const relPath = relative2(skillDir, fullPath);
23019
23095
  const relPathNormalized = relPath.split(sep2).join("/");
23020
23096
  const pathSegments = relPath.split(sep2);
@@ -23073,7 +23149,7 @@ async function resolveSkillReferences(references, basePath, logger) {
23073
23149
  column: 0
23074
23150
  });
23075
23151
  }
23076
- const fullPath = resolve5(basePath, ref);
23152
+ const fullPath = resolve6(basePath, ref);
23077
23153
  let content;
23078
23154
  try {
23079
23155
  content = await readFile8(fullPath, "utf-8");
@@ -23145,8 +23221,8 @@ async function discoverSkillDirs(basePath) {
23145
23221
  if (!entry.isDirectory()) continue;
23146
23222
  if (SKIP_DIRS.has(entry.name)) continue;
23147
23223
  if (!isSafeSkillName(entry.name)) continue;
23148
- const subDir = resolve5(dir, entry.name);
23149
- const skillMd = resolve5(subDir, "SKILL.md");
23224
+ const subDir = resolve6(dir, entry.name);
23225
+ const skillMd = resolve6(subDir, "SKILL.md");
23150
23226
  if (await fileExists(skillMd)) {
23151
23227
  if (!result.has(entry.name)) {
23152
23228
  result.set(entry.name, subDir);
@@ -23173,9 +23249,9 @@ async function resolveNativeSkills(ast, registryPath, sourceFile, localPath, opt
23173
23249
  const isSkillsDir = sourceDir.includes("@skills") || sourceDir.endsWith("/skills");
23174
23250
  const discoveredSkillPaths = /* @__PURE__ */ new Map();
23175
23251
  if (!isSkillsDir && localPath) {
23176
- const discoveryDirs = [resolve5(localPath, "skills")];
23252
+ const discoveryDirs = [resolve6(localPath, "skills")];
23177
23253
  if (options?.universalDir && localPath) {
23178
- discoveryDirs.push(resolve5(localPath, "..", options.universalDir, "skills"));
23254
+ discoveryDirs.push(resolve6(localPath, "..", options.universalDir, "skills"));
23179
23255
  }
23180
23256
  for (const dir of discoveryDirs) {
23181
23257
  const discovered = await discoverSkillDirs(dir);
@@ -23196,7 +23272,7 @@ async function resolveNativeSkills(ast, registryPath, sourceFile, localPath, opt
23196
23272
  }
23197
23273
  let sharedResources = [];
23198
23274
  if (localPath) {
23199
- const sharedDir = resolve5(localPath, "shared");
23275
+ const sharedDir = resolve6(localPath, "shared");
23200
23276
  if (await fileExists(sharedDir)) {
23201
23277
  try {
23202
23278
  sharedResources = await discoverSkillResources(sharedDir, logger);
@@ -23217,12 +23293,12 @@ async function resolveNativeSkills(ast, registryPath, sourceFile, localPath, opt
23217
23293
  continue;
23218
23294
  }
23219
23295
  const skillObj = skillValue;
23220
- const skillMdPath = isSkillsDir ? resolve5(sourceDir, skillName, "SKILL.md") : await (async () => {
23296
+ const skillMdPath = isSkillsDir ? resolve6(sourceDir, skillName, "SKILL.md") : await (async () => {
23221
23297
  const discoveredDir = discoveredSkillPaths.get(skillName);
23222
- if (discoveredDir) return resolve5(discoveredDir, "SKILL.md");
23223
- const localCandidate = localPath ? resolve5(localPath, "skills", skillName, "SKILL.md") : null;
23224
- const universalCandidate = options?.universalDir && localPath ? resolve5(localPath, "..", options.universalDir, "skills", skillName, "SKILL.md") : null;
23225
- 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");
23226
23302
  if (localCandidate && await fileExists(localCandidate)) return localCandidate;
23227
23303
  if (universalCandidate && await fileExists(universalCandidate))
23228
23304
  return universalCandidate;
@@ -23312,7 +23388,7 @@ async function discoverCommandFiles(dir, logger = noopLogger2) {
23312
23388
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
23313
23389
  if (entry.isSymbolicLink()) continue;
23314
23390
  const cmdName = "/" + entry.name.replace(/\.md$/, "");
23315
- const fullPath = resolve5(dir, entry.name);
23391
+ const fullPath = resolve6(dir, entry.name);
23316
23392
  try {
23317
23393
  const fileStat = await lstat(fullPath);
23318
23394
  if (fileStat.isSymbolicLink()) continue;
@@ -23343,11 +23419,11 @@ async function resolveNativeCommands(ast, sourceFile, localPath, options) {
23343
23419
  if (!localPath) return ast;
23344
23420
  const logger = options?.logger ?? noopLogger2;
23345
23421
  const allCommands = {};
23346
- const localCommands = await discoverCommandFiles(resolve5(localPath, "commands"), logger);
23422
+ const localCommands = await discoverCommandFiles(resolve6(localPath, "commands"), logger);
23347
23423
  Object.assign(allCommands, localCommands);
23348
23424
  if (options?.universalDir) {
23349
23425
  const universalCommands = await discoverCommandFiles(
23350
- resolve5(localPath, "..", options.universalDir, "commands"),
23426
+ resolve6(localPath, "..", options.universalDir, "commands"),
23351
23427
  logger
23352
23428
  );
23353
23429
  for (const [name, value] of Object.entries(universalCommands)) {
@@ -23428,7 +23504,7 @@ async function discoverAgentFiles(dir, logger = noopLogger2) {
23428
23504
  for (const entry of entries) {
23429
23505
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
23430
23506
  if (entry.isSymbolicLink()) continue;
23431
- const fullPath = resolve5(dir, entry.name);
23507
+ const fullPath = resolve6(dir, entry.name);
23432
23508
  try {
23433
23509
  const fileStat = await lstat(fullPath);
23434
23510
  if (fileStat.isSymbolicLink()) continue;
@@ -23492,11 +23568,11 @@ async function resolveNativeAgents(ast, sourceFile, localPath, options) {
23492
23568
  if (!localPath) return ast;
23493
23569
  const logger = options?.logger ?? noopLogger2;
23494
23570
  const allAgents = {};
23495
- const localAgents = await discoverAgentFiles(resolve5(localPath, "agents"), logger);
23571
+ const localAgents = await discoverAgentFiles(resolve6(localPath, "agents"), logger);
23496
23572
  Object.assign(allAgents, localAgents);
23497
23573
  if (options?.universalDir) {
23498
23574
  const universalAgents = await discoverAgentFiles(
23499
- resolve5(localPath, "..", options.universalDir, "agents"),
23575
+ resolve6(localPath, "..", options.universalDir, "agents"),
23500
23576
  logger
23501
23577
  );
23502
23578
  for (const [name, value] of Object.entries(universalAgents)) {
@@ -25124,7 +25200,7 @@ var RegistryCache = class {
25124
25200
 
25125
25201
  // packages/resolver/src/auto-discovery.ts
25126
25202
  import { readFile as readFile10, readdir as readdir3, access as access2, lstat as lstat2 } from "fs/promises";
25127
- import { resolve as resolve6, basename as basename10 } from "path";
25203
+ import { resolve as resolve7, basename as basename10 } from "path";
25128
25204
  var CONTEXT_FILES = ["CLAUDE.md", ".clinerules", ".cursorrules"];
25129
25205
  async function fileExists2(path) {
25130
25206
  try {
@@ -25160,9 +25236,9 @@ async function discoverSkills(dir) {
25160
25236
  const properties = {};
25161
25237
  for (const entry of entries) {
25162
25238
  if (!entry.isDirectory()) continue;
25163
- let skillMdPath = resolve6(dir, entry.name, "SKILL.md");
25239
+ let skillMdPath = resolve7(dir, entry.name, "SKILL.md");
25164
25240
  if (!await fileExists2(skillMdPath)) {
25165
- const dirnameMdPath = resolve6(dir, entry.name, `${entry.name}.md`);
25241
+ const dirnameMdPath = resolve7(dir, entry.name, `${entry.name}.md`);
25166
25242
  if (await fileExists2(dirnameMdPath)) {
25167
25243
  skillMdPath = dirnameMdPath;
25168
25244
  } else {
@@ -25189,7 +25265,7 @@ async function discoverSkills(dir) {
25189
25265
  return Object.keys(properties).length > 0 ? properties : null;
25190
25266
  }
25191
25267
  async function discoverRootSkill(dir) {
25192
- const skillMdPath = resolve6(dir, "SKILL.md");
25268
+ const skillMdPath = resolve7(dir, "SKILL.md");
25193
25269
  if (!await fileExists2(skillMdPath)) return null;
25194
25270
  try {
25195
25271
  const raw = await readFile10(skillMdPath, "utf-8");
@@ -25220,7 +25296,7 @@ async function discoverAgents(dir) {
25220
25296
  const properties = {};
25221
25297
  for (const entry of entries) {
25222
25298
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
25223
- const fullPath = resolve6(dir, entry.name);
25299
+ const fullPath = resolve7(dir, entry.name);
25224
25300
  try {
25225
25301
  const stat4 = await lstat2(fullPath);
25226
25302
  if (stat4.isSymbolicLink()) continue;
@@ -25256,7 +25332,7 @@ async function discoverCommands(dir) {
25256
25332
  for (const entry of entries) {
25257
25333
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
25258
25334
  if (entry.name === "SKILL.md") continue;
25259
- const fullPath = resolve6(dir, entry.name);
25335
+ const fullPath = resolve7(dir, entry.name);
25260
25336
  try {
25261
25337
  const stat4 = await lstat2(fullPath);
25262
25338
  if (stat4.isSymbolicLink()) continue;
@@ -25273,7 +25349,7 @@ async function discoverCommands(dir) {
25273
25349
  }
25274
25350
  async function discoverContext(dir) {
25275
25351
  for (const fileName of CONTEXT_FILES) {
25276
- const fullPath = resolve6(dir, fileName);
25352
+ const fullPath = resolve7(dir, fileName);
25277
25353
  if (!await fileExists2(fullPath)) continue;
25278
25354
  try {
25279
25355
  const content = await readFile10(fullPath, "utf-8");
@@ -25303,7 +25379,7 @@ async function discoverNativeContent(dir) {
25303
25379
  const subSkills = await discoverSkills(dir);
25304
25380
  const wrappedSkillResults = await Promise.all(
25305
25381
  SKILL_WRAPPER_DIRS.map(async (name) => {
25306
- const candidate = resolve6(dir, name);
25382
+ const candidate = resolve7(dir, name);
25307
25383
  if (!await safeIsDirectory(candidate)) return null;
25308
25384
  return discoverSkills(candidate);
25309
25385
  })
@@ -25324,7 +25400,7 @@ async function discoverNativeContent(dir) {
25324
25400
  const rootAgents = await discoverAgents(dir) ?? {};
25325
25401
  const wrappedAgents = {};
25326
25402
  for (const name of AGENT_WRAPPER_DIRS) {
25327
- const candidate = resolve6(dir, name);
25403
+ const candidate = resolve7(dir, name);
25328
25404
  if (await safeIsDirectory(candidate)) {
25329
25405
  const found = await discoverAgents(candidate);
25330
25406
  if (found) Object.assign(wrappedAgents, found);
@@ -25337,7 +25413,7 @@ async function discoverNativeContent(dir) {
25337
25413
  const rootCommands = await discoverCommands(dir) ?? {};
25338
25414
  const wrappedCommands = {};
25339
25415
  for (const name of COMMAND_WRAPPER_DIRS) {
25340
- const candidate = resolve6(dir, name);
25416
+ const candidate = resolve7(dir, name);
25341
25417
  if (await safeIsDirectory(candidate)) {
25342
25418
  const found = await discoverCommands(candidate);
25343
25419
  if (found) Object.assign(wrappedCommands, found);
@@ -25838,6 +25914,12 @@ var Resolver = class {
25838
25914
  errors.push(new ResolveError(e.message, e.location));
25839
25915
  }
25840
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
+ );
25841
25923
  } else {
25842
25924
  const discoverDir = isRoot ? cachePath : join8(cachePath, subPath);
25843
25925
  this.logger.debug(`No .prs found, trying auto-discovery: ${discoverDir}`);
@@ -26080,7 +26162,7 @@ var HttpRegistry = class {
26080
26162
  * Sleep for specified milliseconds.
26081
26163
  */
26082
26164
  sleep(ms) {
26083
- return new Promise((resolve21) => setTimeout(resolve21, ms));
26165
+ return new Promise((resolve22) => setTimeout(resolve22, ms));
26084
26166
  }
26085
26167
  /**
26086
26168
  * Fetch with timeout.
@@ -26224,9 +26306,251 @@ function createHttpRegistry(options) {
26224
26306
  // packages/resolver/src/vendor-registry.ts
26225
26307
  init_src();
26226
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
+
26227
26544
  // packages/resolver/src/index.ts
26228
26545
  init_src();
26229
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
+
26230
26554
  // packages/validator/src/validator.ts
26231
26555
  init_src();
26232
26556
 
@@ -29679,7 +30003,7 @@ var Compiler = class _Compiler {
29679
30003
  outputPathOwners.set(skillPath, `${formatter.name}:promptscript-skill`);
29680
30004
  const skillOutput = {
29681
30005
  path: skillPath,
29682
- content: this.options.skillContent
30006
+ content: formatter.transformInjectedSkillContent ? formatter.transformInjectedSkillContent(this.options.skillContent) : this.options.skillContent
29683
30007
  };
29684
30008
  outputs.set(skillPath, addMarkerToOutput(skillOutput, sourceLabel, "promptscript"));
29685
30009
  this.logger.verbose(` \u2192 ${skillPath} (auto-injected promptscript skill)`);
@@ -29760,12 +30084,12 @@ var Compiler = class _Compiler {
29760
30084
  */
29761
30085
  async watch(entryPath, options = {}) {
29762
30086
  const { default: chokidar2 } = await import("chokidar");
29763
- const { dirname: dirname15, resolve: resolve21 } = await import("path");
29764
- const baseDir = dirname15(resolve21(entryPath));
30087
+ const { dirname: dirname17, resolve: resolve22 } = await import("path");
30088
+ const baseDir = dirname17(resolve22(entryPath));
29765
30089
  const includePatterns = options.include ?? ["**/*.prs"];
29766
30090
  const excludePatterns = options.exclude ?? ["**/node_modules/**"];
29767
30091
  const debounceMs = options.debounce ?? 300;
29768
- const watchPatterns = includePatterns.map((p) => resolve21(baseDir, p));
30092
+ const watchPatterns = includePatterns.map((p) => resolve22(baseDir, p));
29769
30093
  let debounceTimer = null;
29770
30094
  let pendingChanges = [];
29771
30095
  const handleChange = async (changedFiles) => {
@@ -29940,7 +30264,7 @@ var Pager = class {
29940
30264
  }
29941
30265
  return;
29942
30266
  }
29943
- return new Promise((resolve21) => {
30267
+ return new Promise((resolve22) => {
29944
30268
  const pagerCmd = getPagerCommand();
29945
30269
  const [cmd, ...args] = pagerCmd.split(" ");
29946
30270
  if (cmd === "less" && !args.includes("-R")) {
@@ -29948,7 +30272,7 @@ var Pager = class {
29948
30272
  }
29949
30273
  if (!cmd) {
29950
30274
  console.log(content);
29951
- resolve21();
30275
+ resolve22();
29952
30276
  return;
29953
30277
  }
29954
30278
  const pager = spawn(cmd, args, {
@@ -29961,17 +30285,17 @@ var Pager = class {
29961
30285
  });
29962
30286
  pager.on("error", (_err) => {
29963
30287
  console.log(content);
29964
- resolve21();
30288
+ resolve22();
29965
30289
  });
29966
30290
  pager.on("close", () => {
29967
- resolve21();
30291
+ resolve22();
29968
30292
  });
29969
30293
  if (pager.stdin) {
29970
30294
  pager.stdin.write(content);
29971
30295
  pager.stdin.end();
29972
30296
  } else {
29973
30297
  console.log(content);
29974
- resolve21();
30298
+ resolve22();
29975
30299
  }
29976
30300
  });
29977
30301
  }
@@ -29984,7 +30308,7 @@ function createPager(enabled = true) {
29984
30308
  init_services();
29985
30309
 
29986
30310
  // packages/cli/src/utils/registry-resolver.ts
29987
- import { join as join10 } from "path";
30311
+ import { join as join11 } from "path";
29988
30312
  async function resolveRegistryPath(config) {
29989
30313
  if (config.registry?.git) {
29990
30314
  const gitConfig = config.registry.git;
@@ -29998,7 +30322,7 @@ async function resolveRegistryPath(config) {
29998
30322
  if (isValid) {
29999
30323
  const subPath2 = gitConfig.path ?? "";
30000
30324
  return {
30001
- path: subPath2 ? join10(cachePath, subPath2) : cachePath,
30325
+ path: subPath2 ? join11(cachePath, subPath2) : cachePath,
30002
30326
  isRemote: true,
30003
30327
  source: "git"
30004
30328
  };
@@ -30027,7 +30351,7 @@ async function resolveRegistryPath(config) {
30027
30351
  }
30028
30352
  const subPath = gitConfig.path ?? "";
30029
30353
  return {
30030
- path: subPath ? join10(cachePath, subPath) : cachePath,
30354
+ path: subPath ? join11(cachePath, subPath) : cachePath,
30031
30355
  isRemote: true,
30032
30356
  source: "git"
30033
30357
  };
@@ -30077,17 +30401,17 @@ function detectOutputConflicts(targets) {
30077
30401
 
30078
30402
  // packages/cli/src/commands/compile.ts
30079
30403
  var __filename2 = fileURLToPath2(import.meta.url);
30080
- var __dirname2 = dirname8(__filename2);
30404
+ var __dirname2 = dirname9(__filename2);
30081
30405
  async function loadBundledSkillContent(logger) {
30082
30406
  const skillRelPath = "skills/promptscript/SKILL.md";
30083
30407
  const candidates = [
30084
- resolve7(__dirname2, skillRelPath),
30408
+ resolve8(__dirname2, skillRelPath),
30085
30409
  // bundled: dist/packages/cli/skills/...
30086
- resolve7(__dirname2, "..", "..", skillRelPath)
30410
+ resolve8(__dirname2, "..", "..", skillRelPath)
30087
30411
  // dev: packages/cli/skills/...
30088
30412
  ];
30089
30413
  for (const candidate of candidates) {
30090
- if (existsSync11(candidate)) {
30414
+ if (existsSync12(candidate)) {
30091
30415
  try {
30092
30416
  const content = await readFile12(candidate, "utf-8");
30093
30417
  logger.debug(
@@ -30104,8 +30428,8 @@ async function loadBundledSkillContent(logger) {
30104
30428
  }
30105
30429
  function findConfigInDir(dir) {
30106
30430
  for (const file of CONFIG_FILES) {
30107
- const fullPath = resolve7(dir, file);
30108
- if (existsSync11(fullPath)) {
30431
+ const fullPath = resolve8(dir, file);
30432
+ if (existsSync12(fullPath)) {
30109
30433
  return fullPath;
30110
30434
  }
30111
30435
  }
@@ -30161,7 +30485,7 @@ function getBuildProfile(config, buildName) {
30161
30485
  }
30162
30486
  function resolveOutputBase(projectRoot, output) {
30163
30487
  if (!output) return projectRoot;
30164
- return isAbsolute5(output) ? output : resolve7(projectRoot, output);
30488
+ return isAbsolute5(output) ? output : resolve8(projectRoot, output);
30165
30489
  }
30166
30490
  async function isPromptScriptGenerated2(filePath) {
30167
30491
  try {
@@ -30205,13 +30529,13 @@ async function writeOutputs(outputs, options, _config, services) {
30205
30529
  const targetErrors = [];
30206
30530
  async function safeWrite(path, content) {
30207
30531
  try {
30208
- await mkdir4(dirname8(path), { recursive: true });
30532
+ await mkdir4(dirname9(path), { recursive: true });
30209
30533
  await writeFile4(path, content, "utf-8");
30210
30534
  return true;
30211
30535
  } catch (err) {
30212
30536
  const nodeErr = err;
30213
30537
  if (nodeErr.code === "ENOTDIR") {
30214
- const conflictPath = nodeErr.path ?? dirname8(path);
30538
+ const conflictPath = nodeErr.path ?? dirname9(path);
30215
30539
  const msg = `Cannot create directory because '${conflictPath}' already exists as a file. Remove it or disable this target.`;
30216
30540
  targetErrors.push(msg);
30217
30541
  ConsoleOutput.error(msg);
@@ -30227,8 +30551,8 @@ async function writeOutputs(outputs, options, _config, services) {
30227
30551
  }
30228
30552
  }
30229
30553
  for (const output of outputs.values()) {
30230
- const outputPath = options.output ? resolve7(options.output, output.path) : resolve7(output.path);
30231
- const fileExists3 = existsSync11(outputPath);
30554
+ const outputPath = options.output ? resolve8(options.output, output.path) : resolve8(output.path);
30555
+ const fileExists3 = existsSync12(outputPath);
30232
30556
  if (options.dryRun) {
30233
30557
  if (fileExists3) {
30234
30558
  const isGenerated2 = await isPromptScriptGenerated2(outputPath);
@@ -30361,7 +30685,7 @@ async function compileCommand(options, services = createDefaultServices()) {
30361
30685
  const spinner = createSpinner("Loading configuration...").start();
30362
30686
  const logger = createCliLogger();
30363
30687
  try {
30364
- const projectRoot = options.cwd ? resolve7(options.cwd) : process.cwd();
30688
+ const projectRoot = options.cwd ? resolve8(options.cwd) : process.cwd();
30365
30689
  logger.verbose("Loading configuration...");
30366
30690
  const configPath = options.config ? options.config : options.cwd ? findConfigInDir(projectRoot) : void 0;
30367
30691
  const config = await loadConfig(configPath);
@@ -30401,7 +30725,7 @@ Disable one of the conflicting targets or assign a custom output path.`
30401
30725
  }
30402
30726
  }
30403
30727
  let lockfile;
30404
- const lockfilePath = resolve7(projectRoot, "promptscript.lock");
30728
+ const lockfilePath = resolve8(projectRoot, "promptscript.lock");
30405
30729
  try {
30406
30730
  const lockfileContent = await readFile12(lockfilePath, "utf-8");
30407
30731
  const parsed = parseYaml5(lockfileContent, { maxAliasCount: 100 });
@@ -30421,7 +30745,7 @@ Disable one of the conflicting targets or assign a custom output path.`
30421
30745
  if (config.includePromptScriptSkill !== false) {
30422
30746
  skillContent = await loadBundledSkillContent(logger);
30423
30747
  }
30424
- const localPath = resolve7(projectRoot, ".promptscript");
30748
+ const localPath = resolve8(projectRoot, ".promptscript");
30425
30749
  const compiler = new Compiler({
30426
30750
  resolver: {
30427
30751
  registryPath,
@@ -30441,8 +30765,8 @@ Disable one of the conflicting targets or assign a custom output path.`
30441
30765
  ignoreHashes: options.ignoreHashes
30442
30766
  });
30443
30767
  const entry = buildProfile?.entry ?? config.input?.entry;
30444
- const entryPath = entry ? resolve7(projectRoot, entry) : resolve7(localPath, "project.prs");
30445
- if (!existsSync11(entryPath)) {
30768
+ const entryPath = entry ? resolve8(projectRoot, entry) : resolve8(localPath, "project.prs");
30769
+ if (!existsSync12(entryPath)) {
30446
30770
  spinner.fail("Entry file not found");
30447
30771
  ConsoleOutput.error(`File not found: ${entryPath}`);
30448
30772
  ConsoleOutput.muted("Run: prs init");
@@ -30467,6 +30791,7 @@ Disable one of the conflicting targets or assign a custom output path.`
30467
30791
  ...options,
30468
30792
  output: resolveOutputBase(projectRoot, configuredOutput)
30469
30793
  };
30794
+ await postFormatWithPrettier(result.outputs, projectRoot, logger);
30470
30795
  const writeResult = await writeOutputs(result.outputs, effectiveOptions, config, services);
30471
30796
  if (writeResult.unchanged.length > 0) {
30472
30797
  ConsoleOutput.muted(`Unchanged ${writeResult.unchanged.length} file(s)`);
@@ -30480,7 +30805,7 @@ Disable one of the conflicting targets or assign a custom output path.`
30480
30805
  ConsoleOutput.newline();
30481
30806
  ConsoleOutput.info("Watching for changes... (Ctrl+C to stop)");
30482
30807
  watchForChanges(
30483
- resolve7(projectRoot, ".promptscript"),
30808
+ resolve8(projectRoot, ".promptscript"),
30484
30809
  () => compileCommand({ ...options, watch: false }, services)
30485
30810
  );
30486
30811
  }
@@ -30531,8 +30856,8 @@ function watchForChanges(dir, callback) {
30531
30856
  }
30532
30857
 
30533
30858
  // packages/cli/src/commands/validate.ts
30534
- import { resolve as resolve8, join as join11 } from "path";
30535
- 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";
30536
30861
  init_console();
30537
30862
  init_src();
30538
30863
  init_src2();
@@ -30641,7 +30966,7 @@ function discoverPrsFiles(dir) {
30641
30966
  try {
30642
30967
  const entries = readdirSync(dir, { withFileTypes: true });
30643
30968
  for (const entry of entries) {
30644
- const fullPath = join11(dir, entry.name);
30969
+ const fullPath = join12(dir, entry.name);
30645
30970
  if (entry.isDirectory()) {
30646
30971
  results.push(...discoverPrsFiles(fullPath));
30647
30972
  } else if (entry.name.endsWith(".prs")) {
@@ -30681,8 +31006,8 @@ async function validateCommand(options) {
30681
31006
  formatters: []
30682
31007
  // No formatters needed for validation only
30683
31008
  });
30684
- const entryPath = resolve8("./.promptscript/project.prs");
30685
- if (!existsSync12(entryPath)) {
31009
+ const entryPath = resolve9("./.promptscript/project.prs");
31010
+ if (!existsSync13(entryPath)) {
30686
31011
  handleEntryNotFound(entryPath, isJsonFormat, spinner);
30687
31012
  return;
30688
31013
  }
@@ -30764,8 +31089,8 @@ ${fixedCount} file(s) fixed.`);
30764
31089
 
30765
31090
  // packages/cli/src/commands/pull.ts
30766
31091
  import { mkdir as mkdir5, writeFile as writeFile5 } from "fs/promises";
30767
- import { existsSync as existsSync13 } from "fs";
30768
- 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";
30769
31094
  init_console();
30770
31095
  async function pullCommand(options) {
30771
31096
  const spinner = createSpinner("Loading configuration...").start();
@@ -30790,8 +31115,8 @@ async function pullCommand(options) {
30790
31115
  }
30791
31116
  spinner.text = `Fetching ${inheritPath}...`;
30792
31117
  const content = await registry2.fetch(inheritPath);
30793
- const destPath = resolve9("./.promptscript/.inherited", inheritPath);
30794
- if (existsSync13(destPath) && !options.force && !options.dryRun) {
31118
+ const destPath = resolve10("./.promptscript/.inherited", inheritPath);
31119
+ if (existsSync14(destPath) && !options.force && !options.dryRun) {
30795
31120
  spinner.warn("File already exists (use --force to overwrite)");
30796
31121
  ConsoleOutput.muted(destPath);
30797
31122
  return;
@@ -30801,12 +31126,12 @@ async function pullCommand(options) {
30801
31126
  ConsoleOutput.newline();
30802
31127
  ConsoleOutput.dryRun(`Would fetch: ${config.inherit}`);
30803
31128
  ConsoleOutput.dryRun(` to: ${destPath}`);
30804
- if (existsSync13(destPath)) {
31129
+ if (existsSync14(destPath)) {
30805
31130
  ConsoleOutput.dryRun("(would overwrite existing file)");
30806
31131
  }
30807
31132
  return;
30808
31133
  }
30809
- await mkdir5(dirname9(destPath), { recursive: true });
31134
+ await mkdir5(dirname10(destPath), { recursive: true });
30810
31135
  await writeFile5(destPath, content, "utf-8");
30811
31136
  spinner.succeed("Pulled from registry");
30812
31137
  ConsoleOutput.success(destPath);
@@ -30849,10 +31174,10 @@ async function createRegistry(config, options, spinner) {
30849
31174
  }
30850
31175
  const registryPath = config.registry?.path ?? "./registry";
30851
31176
  spinner.text = `Using local registry: ${registryPath}...`;
30852
- if (!existsSync13(registryPath)) {
31177
+ if (!existsSync14(registryPath)) {
30853
31178
  throw new Error(`Local registry path not found: ${registryPath}`);
30854
31179
  }
30855
- return createFileSystemRegistry(resolve9(registryPath));
31180
+ return createFileSystemRegistry(resolve10(registryPath));
30856
31181
  }
30857
31182
  function parseInheritPath(inheritPath) {
30858
31183
  const versionIndex = inheritPath.lastIndexOf("@");
@@ -30899,9 +31224,9 @@ function handlePullError(error, spinner) {
30899
31224
  }
30900
31225
 
30901
31226
  // packages/cli/src/commands/diff.ts
30902
- import { resolve as resolve10 } from "path";
31227
+ import { resolve as resolve11 } from "path";
30903
31228
  import { readFile as readFile13 } from "fs/promises";
30904
- import { existsSync as existsSync14 } from "fs";
31229
+ import { existsSync as existsSync15 } from "fs";
30905
31230
  init_console();
30906
31231
  import chalk2 from "chalk";
30907
31232
  function configureColors(options) {
@@ -30936,9 +31261,9 @@ function printNewFilePreview(content, showFull, pager) {
30936
31261
  }
30937
31262
  }
30938
31263
  async function compareOutput(_name, output, _config, showFull, pager) {
30939
- const outputPath = resolve10(output.path);
31264
+ const outputPath = resolve11(output.path);
30940
31265
  const newContent = output.content;
30941
- if (!existsSync14(outputPath)) {
31266
+ if (!existsSync15(outputPath)) {
30942
31267
  pager.write(chalk2.green(`+ ${outputPath} (new file)`));
30943
31268
  pager.write("");
30944
31269
  printNewFilePreview(newContent, showFull, pager);
@@ -30978,8 +31303,8 @@ async function diffCommand(options) {
30978
31303
  formatters: targets,
30979
31304
  customConventions: config.customConventions
30980
31305
  });
30981
- const entryPath = resolve10("./.promptscript/project.prs");
30982
- if (!existsSync14(entryPath)) {
31306
+ const entryPath = resolve11("./.promptscript/project.prs");
31307
+ if (!existsSync15(entryPath)) {
30983
31308
  spinner.fail("Entry file not found");
30984
31309
  ConsoleOutput.error(`File not found: ${entryPath}`);
30985
31310
  ConsoleOutput.muted("Run: prs init");
@@ -31047,8 +31372,8 @@ function printSimpleDiff(existingLines, newLines, showFull, pager) {
31047
31372
  }
31048
31373
 
31049
31374
  // packages/cli/src/commands/check.ts
31050
- import { existsSync as existsSync15 } from "fs";
31051
- import { resolve as resolve11 } from "path";
31375
+ import { existsSync as existsSync16 } from "fs";
31376
+ import { resolve as resolve12 } from "path";
31052
31377
  init_console();
31053
31378
  async function checkCommand(_options) {
31054
31379
  const spinner = createSpinner("Checking project health...").start();
@@ -31111,7 +31436,7 @@ async function checkCommand(_options) {
31111
31436
  });
31112
31437
  }
31113
31438
  const entryPath = config.input?.entry ?? ".promptscript/project.prs";
31114
- if (existsSync15(entryPath)) {
31439
+ if (existsSync16(entryPath)) {
31115
31440
  results.push({
31116
31441
  name: "Entry file",
31117
31442
  status: "ok",
@@ -31126,8 +31451,8 @@ async function checkCommand(_options) {
31126
31451
  hasErrors = true;
31127
31452
  }
31128
31453
  if (config.registry?.path) {
31129
- const registryPath = resolve11(config.registry.path);
31130
- if (existsSync15(registryPath)) {
31454
+ const registryPath = resolve12(config.registry.path);
31455
+ if (existsSync16(registryPath)) {
31131
31456
  results.push({
31132
31457
  name: "Registry path",
31133
31458
  status: "ok",
@@ -31212,21 +31537,21 @@ function printResults(results) {
31212
31537
  init_src();
31213
31538
  init_console();
31214
31539
  import { fileURLToPath as fileURLToPath3 } from "url";
31215
- import { dirname as dirname10 } from "path";
31540
+ import { dirname as dirname11 } from "path";
31216
31541
 
31217
31542
  // packages/cli/src/utils/version-check.ts
31218
31543
  init_console();
31219
31544
  import { homedir as homedir3 } from "os";
31220
- import { join as join12 } from "path";
31545
+ import { join as join13 } from "path";
31221
31546
  import { readFile as readFile14, writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
31222
31547
  var NPM_REGISTRY_URL = "https://registry.npmjs.org/@promptscript/cli/latest";
31223
31548
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
31224
31549
  var FETCH_TIMEOUT_MS = 3e3;
31225
31550
  function getCacheDir() {
31226
- return join12(homedir3(), ".promptscript", ".cache");
31551
+ return join13(homedir3(), ".promptscript", ".cache");
31227
31552
  }
31228
31553
  function getCachePath() {
31229
- return join12(getCacheDir(), "version.json");
31554
+ return join13(getCacheDir(), "version.json");
31230
31555
  }
31231
31556
  async function readCache() {
31232
31557
  try {
@@ -31365,7 +31690,7 @@ function printUpdateNotification(info) {
31365
31690
 
31366
31691
  // packages/cli/src/commands/update-check.ts
31367
31692
  var __filename3 = fileURLToPath3(import.meta.url);
31368
- var __dirname3 = dirname10(__filename3);
31693
+ var __dirname3 = dirname11(__filename3);
31369
31694
  async function updateCheckCommand() {
31370
31695
  const currentVersion = getPackageVersion(__dirname3, "./package.json");
31371
31696
  console.log(`@promptscript/cli v${currentVersion}`);
@@ -31393,7 +31718,7 @@ init_services();
31393
31718
  init_console();
31394
31719
 
31395
31720
  // packages/cli/src/utils/registry-scaffolder.ts
31396
- import { join as join13 } from "path";
31721
+ import { join as join14 } from "path";
31397
31722
  var SEED_BASE = `@meta {
31398
31723
  id: "@core/base"
31399
31724
  syntax: "1.0.0"
@@ -31494,14 +31819,14 @@ async function scaffoldRegistry(options, services) {
31494
31819
  const createdFiles = [];
31495
31820
  await fs4.mkdir(directory, { recursive: true });
31496
31821
  const manifest = generateManifest(name, description, namespaces, seed);
31497
- const manifestPath = join13(directory, "registry-manifest.yaml");
31822
+ const manifestPath = join14(directory, "registry-manifest.yaml");
31498
31823
  await fs4.writeFile(manifestPath, manifest, "utf-8");
31499
31824
  createdFiles.push(manifestPath);
31500
31825
  const readme = generateReadme(name, description, namespaces);
31501
- const readmePath = join13(directory, "README.md");
31826
+ const readmePath = join14(directory, "README.md");
31502
31827
  await fs4.writeFile(readmePath, readme, "utf-8");
31503
31828
  createdFiles.push(readmePath);
31504
- const gitignorePath = join13(directory, ".gitignore");
31829
+ const gitignorePath = join14(directory, ".gitignore");
31505
31830
  await fs4.writeFile(
31506
31831
  gitignorePath,
31507
31832
  ".DS_Store\nnode_modules/\n*.log\n*.swp\n*.bak\n.env*\n",
@@ -31509,20 +31834,20 @@ async function scaffoldRegistry(options, services) {
31509
31834
  );
31510
31835
  createdFiles.push(gitignorePath);
31511
31836
  for (const ns of namespaces) {
31512
- const nsDir = join13(directory, ns);
31837
+ const nsDir = join14(directory, ns);
31513
31838
  await fs4.mkdir(nsDir, { recursive: true });
31514
31839
  if (seed && ns === "@core") {
31515
- const basePath = join13(nsDir, "base.prs");
31840
+ const basePath = join14(nsDir, "base.prs");
31516
31841
  await fs4.writeFile(basePath, SEED_BASE, "utf-8");
31517
31842
  createdFiles.push(basePath);
31518
- const qualityPath = join13(nsDir, "quality.prs");
31843
+ const qualityPath = join14(nsDir, "quality.prs");
31519
31844
  await fs4.writeFile(qualityPath, SEED_QUALITY, "utf-8");
31520
31845
  createdFiles.push(qualityPath);
31521
- const securityPath = join13(nsDir, "security.prs");
31846
+ const securityPath = join14(nsDir, "security.prs");
31522
31847
  await fs4.writeFile(securityPath, SEED_SECURITY, "utf-8");
31523
31848
  createdFiles.push(securityPath);
31524
31849
  } else {
31525
- const gitkeepPath = join13(nsDir, ".gitkeep");
31850
+ const gitkeepPath = join14(nsDir, ".gitkeep");
31526
31851
  await fs4.writeFile(gitkeepPath, "", "utf-8");
31527
31852
  createdFiles.push(gitkeepPath);
31528
31853
  }
@@ -31631,7 +31956,7 @@ Then reference configs with \`@inherit\` or \`@use\`:
31631
31956
  }
31632
31957
 
31633
31958
  // packages/cli/src/utils/resolve-target-directory.ts
31634
- import { join as join14, resolve as resolve12 } from "path";
31959
+ import { join as join15, resolve as resolve13 } from "path";
31635
31960
 
31636
31961
  // packages/cli/src/utils/slugify.ts
31637
31962
  function slugify(name) {
@@ -31642,7 +31967,7 @@ function slugify(name) {
31642
31967
  async function resolveTargetDirectory(options, services) {
31643
31968
  const { cwd, directoryArg, registryName, nonInteractive } = options;
31644
31969
  if (directoryArg) {
31645
- return resolve12(directoryArg);
31970
+ return resolve13(directoryArg);
31646
31971
  }
31647
31972
  const empty = await isDirectoryEmpty(cwd, services.fs);
31648
31973
  if (empty && nonInteractive) {
@@ -31656,9 +31981,9 @@ async function resolveTargetDirectory(options, services) {
31656
31981
  { name: `Subdirectory (./${slugify(registryName)})`, value: "subdirectory" }
31657
31982
  ]
31658
31983
  });
31659
- return choice === "here" ? cwd : join14(cwd, slugify(registryName));
31984
+ return choice === "here" ? cwd : join15(cwd, slugify(registryName));
31660
31985
  }
31661
- return join14(cwd, slugify(registryName));
31986
+ return join15(cwd, slugify(registryName));
31662
31987
  }
31663
31988
  async function isDirectoryEmpty(dir, fs4) {
31664
31989
  const entries = await fs4.readdir(dir);
@@ -31749,15 +32074,15 @@ async function registryInitCommand(directory, options, services = createDefaultS
31749
32074
  // packages/cli/src/commands/registry/validate.ts
31750
32075
  init_services();
31751
32076
  init_console();
31752
- import { resolve as resolve13 } from "path";
32077
+ import { resolve as resolve14 } from "path";
31753
32078
 
31754
32079
  // packages/cli/src/utils/registry-validator.ts
31755
- import { join as join15 } from "path";
32080
+ import { join as join16 } from "path";
31756
32081
  import { parse as parseYaml6 } from "yaml";
31757
32082
  async function validateRegistry(registryPath, services) {
31758
32083
  const { fs: fs4 } = services;
31759
32084
  const issues = [];
31760
- const manifestPath = join15(registryPath, "registry-manifest.yaml");
32085
+ const manifestPath = join16(registryPath, "registry-manifest.yaml");
31761
32086
  if (!fs4.existsSync(manifestPath)) {
31762
32087
  return {
31763
32088
  valid: false,
@@ -31849,7 +32174,7 @@ async function validateRegistry(registryPath, services) {
31849
32174
  });
31850
32175
  }
31851
32176
  if (entry.path) {
31852
- const prsPath = join15(registryPath, entry.path);
32177
+ const prsPath = join16(registryPath, entry.path);
31853
32178
  if (!fs4.existsSync(prsPath)) {
31854
32179
  issues.push({
31855
32180
  severity: "error",
@@ -31884,7 +32209,7 @@ async function validateRegistry(registryPath, services) {
31884
32209
  });
31885
32210
  }
31886
32211
  for (const ns of namespaceNames) {
31887
- const nsDir = join15(registryPath, ns);
32212
+ const nsDir = join16(registryPath, ns);
31888
32213
  if (fs4.existsSync(nsDir)) {
31889
32214
  try {
31890
32215
  const files = await fs4.readdir(nsDir);
@@ -31898,7 +32223,7 @@ async function validateRegistry(registryPath, services) {
31898
32223
  issues.push({
31899
32224
  severity: "warning",
31900
32225
  message: `Orphaned .prs file not in catalog: ${relativePath}`,
31901
- path: join15(nsDir, file)
32226
+ path: join16(nsDir, file)
31902
32227
  });
31903
32228
  }
31904
32229
  }
@@ -31949,7 +32274,7 @@ function detectCircularDependencies(catalog) {
31949
32274
 
31950
32275
  // packages/cli/src/commands/registry/validate.ts
31951
32276
  async function registryValidateCommand(path, options, services = createDefaultServices()) {
31952
- const registryPath = resolve13(path ?? ".");
32277
+ const registryPath = resolve14(path ?? ".");
31953
32278
  const spinner = createSpinner("Validating registry...").start();
31954
32279
  try {
31955
32280
  const result = await validateRegistry(registryPath, services);
@@ -32001,10 +32326,10 @@ async function registryValidateCommand(path, options, services = createDefaultSe
32001
32326
  // packages/cli/src/commands/registry/publish.ts
32002
32327
  init_services();
32003
32328
  init_console();
32004
- import { resolve as resolve14, join as join16 } from "path";
32329
+ import { resolve as resolve15, join as join17 } from "path";
32005
32330
  import { execFileSync as execFileSync2 } from "child_process";
32006
32331
  async function registryPublishCommand(path, options, services = createDefaultServices()) {
32007
- const registryPath = resolve14(path ?? ".");
32332
+ const registryPath = resolve15(path ?? ".");
32008
32333
  try {
32009
32334
  if (!options.force) {
32010
32335
  const spinner2 = createSpinner("Validating registry...").start();
@@ -32024,7 +32349,7 @@ async function registryPublishCommand(path, options, services = createDefaultSer
32024
32349
  `Registry valid (${result.stats.catalogEntries} entries, ${result.stats.prsFiles} files)`
32025
32350
  );
32026
32351
  }
32027
- const manifestPath = join16(registryPath, "registry-manifest.yaml");
32352
+ const manifestPath = join17(registryPath, "registry-manifest.yaml");
32028
32353
  if (services.fs.existsSync(manifestPath)) {
32029
32354
  const content = await services.fs.readFile(manifestPath, "utf-8");
32030
32355
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -32152,8 +32477,8 @@ async function registryListCommand() {
32152
32477
  // packages/cli/src/commands/registry/add.ts
32153
32478
  init_console();
32154
32479
  import { readFile as readFile16, writeFile as writeFile7, mkdir as mkdir7 } from "fs/promises";
32155
- import { existsSync as existsSync16 } from "fs";
32156
- import { dirname as dirname11 } from "path";
32480
+ import { existsSync as existsSync17 } from "fs";
32481
+ import { dirname as dirname12 } from "path";
32157
32482
  import { parse as parseYaml8, stringify as stringifyYaml } from "yaml";
32158
32483
  init_user_config();
32159
32484
  async function registryAddCommand(alias, url, options) {
@@ -32183,12 +32508,12 @@ async function registryAddCommand(alias, url, options) {
32183
32508
  }
32184
32509
  }
32185
32510
  async function addToGlobalConfig(alias, url) {
32186
- const configDir = dirname11(USER_CONFIG_PATH);
32187
- if (!existsSync16(configDir)) {
32511
+ const configDir = dirname12(USER_CONFIG_PATH);
32512
+ if (!existsSync17(configDir)) {
32188
32513
  await mkdir7(configDir, { recursive: true });
32189
32514
  }
32190
32515
  let config = { version: "1" };
32191
- if (existsSync16(USER_CONFIG_PATH)) {
32516
+ if (existsSync17(USER_CONFIG_PATH)) {
32192
32517
  const content = await readFile16(USER_CONFIG_PATH, "utf-8");
32193
32518
  const parsed = parseYaml8(content);
32194
32519
  if (parsed && typeof parsed === "object") {
@@ -32238,8 +32563,8 @@ function registerRegistryCommands(registry2) {
32238
32563
 
32239
32564
  // packages/cli/src/commands/lock.ts
32240
32565
  import { writeFile as writeFile8, readFile as readFile18 } from "fs/promises";
32241
- import { existsSync as existsSync18 } from "fs";
32242
- import { resolve as resolve15 } from "path";
32566
+ import { existsSync as existsSync19 } from "fs";
32567
+ import { resolve as resolve16 } from "path";
32243
32568
  import { parse as parseYaml9, stringify as stringifyYaml2 } from "yaml";
32244
32569
  init_console();
32245
32570
  init_src();
@@ -32247,9 +32572,9 @@ init_src();
32247
32572
  // packages/cli/src/commands/lock-scanner.ts
32248
32573
  init_src2();
32249
32574
  import { readFile as readFile17 } from "fs/promises";
32250
- import { existsSync as existsSync17 } from "fs";
32575
+ import { existsSync as existsSync18 } from "fs";
32251
32576
  async function collectRemoteImports(entryPath, options) {
32252
- if (!existsSync17(entryPath)) {
32577
+ if (!existsSync18(entryPath)) {
32253
32578
  return [];
32254
32579
  }
32255
32580
  const loader = new FileLoader({
@@ -32286,7 +32611,7 @@ async function collectRemoteImports(entryPath, options) {
32286
32611
  results.push(marker);
32287
32612
  }
32288
32613
  } else {
32289
- if (existsSync17(resolved)) {
32614
+ if (existsSync18(resolved)) {
32290
32615
  await scan(resolved);
32291
32616
  }
32292
32617
  }
@@ -32312,8 +32637,8 @@ async function lockCommand(options) {
32312
32637
  const aliases = config.registries ?? {};
32313
32638
  const aliasEntries = Object.entries(aliases);
32314
32639
  const projectRoot = process.cwd();
32315
- const localPath = resolve15(projectRoot, ".promptscript");
32316
- 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");
32317
32642
  const scannedImports = await collectRemoteImports(entryPath, {
32318
32643
  localPath,
32319
32644
  registries: config.registries
@@ -32324,7 +32649,7 @@ async function lockCommand(options) {
32324
32649
  return;
32325
32650
  }
32326
32651
  let existing = { version: LOCKFILE_VERSION, dependencies: {} };
32327
- if (existsSync18(LOCKFILE_PATH)) {
32652
+ if (existsSync19(LOCKFILE_PATH)) {
32328
32653
  try {
32329
32654
  const raw = await readFile18(LOCKFILE_PATH, "utf-8");
32330
32655
  const parsed = parseYaml9(raw, { maxAliasCount: 100 });
@@ -32393,7 +32718,7 @@ async function lockCommand(options) {
32393
32718
 
32394
32719
  // packages/cli/src/commands/update.ts
32395
32720
  import { writeFile as writeFile9, readFile as readFile19 } from "fs/promises";
32396
- import { existsSync as existsSync19 } from "fs";
32721
+ import { existsSync as existsSync20 } from "fs";
32397
32722
  import { parse as parseYaml10, stringify as stringifyYaml3 } from "yaml";
32398
32723
  init_console();
32399
32724
  init_src();
@@ -32416,7 +32741,7 @@ async function updateCommand(packageArg, options) {
32416
32741
  return;
32417
32742
  }
32418
32743
  let existing = { version: LOCKFILE_VERSION, dependencies: {} };
32419
- if (existsSync19(LOCKFILE_PATH)) {
32744
+ if (existsSync20(LOCKFILE_PATH)) {
32420
32745
  try {
32421
32746
  const raw = await readFile19(LOCKFILE_PATH, "utf-8");
32422
32747
  const parsed = parseYaml10(raw, { maxAliasCount: 100 });
@@ -32480,12 +32805,12 @@ async function updateCommand(packageArg, options) {
32480
32805
  init_console();
32481
32806
  init_src();
32482
32807
  import { readFile as readFile20, writeFile as writeFile10, mkdir as mkdir8, readdir as readdir5 } from "fs/promises";
32483
- import { existsSync as existsSync20 } from "fs";
32484
- 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";
32485
32810
  import { homedir as homedir4 } from "os";
32486
32811
  import { parse as parseYaml11 } from "yaml";
32487
32812
  var VENDOR_DIR = ".promptscript/vendor";
32488
- var CACHE_BASE = join17(homedir4(), ".promptscript", "cache", "registries");
32813
+ var CACHE_BASE = join18(homedir4(), ".promptscript", "cache", "registries");
32489
32814
  async function vendorSyncCommand(options) {
32490
32815
  const spinner = createSpinner("Syncing vendor directory...").start();
32491
32816
  try {
@@ -32503,13 +32828,13 @@ async function vendorSyncCommand(options) {
32503
32828
  }
32504
32829
  const results = [];
32505
32830
  for (const [repoUrl, dep] of deps) {
32506
- const cacheDir = join17(CACHE_BASE, repoUrl.replace(/[/:]/g, "_"), dep.version);
32507
- if (!existsSync20(cacheDir)) {
32831
+ const cacheDir = join18(CACHE_BASE, repoUrl.replace(/[/:]/g, "_"), dep.version);
32832
+ if (!existsSync21(cacheDir)) {
32508
32833
  results.push({ repoUrl, status: "missing" });
32509
32834
  continue;
32510
32835
  }
32511
32836
  if (!options.dryRun) {
32512
- const vendorTarget = resolve16(VENDOR_DIR, repoUrl.replace(/[/:]/g, "_"));
32837
+ const vendorTarget = resolve17(VENDOR_DIR, repoUrl.replace(/[/:]/g, "_"));
32513
32838
  await mkdir8(vendorTarget, { recursive: true });
32514
32839
  await copyDirectory(cacheDir, vendorTarget);
32515
32840
  }
@@ -32555,7 +32880,7 @@ async function vendorCheckCommand(_options) {
32555
32880
  spinner.warn("No dependencies in lockfile");
32556
32881
  return;
32557
32882
  }
32558
- if (!existsSync20(VENDOR_DIR)) {
32883
+ if (!existsSync21(VENDOR_DIR)) {
32559
32884
  spinner.fail("Vendor directory does not exist");
32560
32885
  ConsoleOutput.muted("Run: prs vendor sync");
32561
32886
  process.exitCode = 1;
@@ -32563,8 +32888,8 @@ async function vendorCheckCommand(_options) {
32563
32888
  }
32564
32889
  const issues = [];
32565
32890
  for (const [repoUrl] of deps) {
32566
- const vendorTarget = resolve16(VENDOR_DIR, repoUrl.replace(/[/:]/g, "_"));
32567
- if (!existsSync20(vendorTarget)) {
32891
+ const vendorTarget = resolve17(VENDOR_DIR, repoUrl.replace(/[/:]/g, "_"));
32892
+ if (!existsSync21(vendorTarget)) {
32568
32893
  issues.push({ repoUrl, issue: "missing from vendor directory" });
32569
32894
  }
32570
32895
  }
@@ -32587,7 +32912,7 @@ async function vendorCheckCommand(_options) {
32587
32912
  }
32588
32913
  }
32589
32914
  async function loadLockfile() {
32590
- if (!existsSync20(LOCKFILE_PATH)) {
32915
+ if (!existsSync21(LOCKFILE_PATH)) {
32591
32916
  return null;
32592
32917
  }
32593
32918
  try {
@@ -32601,8 +32926,8 @@ async function loadLockfile() {
32601
32926
  async function copyDirectory(src, dest) {
32602
32927
  const entries = await readdir5(src, { withFileTypes: true });
32603
32928
  for (const entry of entries) {
32604
- const srcPath = join17(src, entry.name);
32605
- const destPath = join17(dest, entry.name);
32929
+ const srcPath = join18(src, entry.name);
32930
+ const destPath = join18(dest, entry.name);
32606
32931
  if (entry.isDirectory()) {
32607
32932
  await mkdir8(destPath, { recursive: true });
32608
32933
  await copyDirectory(srcPath, destPath);
@@ -32614,12 +32939,12 @@ async function copyDirectory(src, dest) {
32614
32939
  }
32615
32940
 
32616
32941
  // packages/cli/src/commands/resolve-cmd.ts
32617
- import { existsSync as existsSync21 } from "fs";
32618
- import { join as join18 } from "path";
32942
+ import { existsSync as existsSync22 } from "fs";
32943
+ import { join as join19 } from "path";
32619
32944
  import { homedir as homedir5 } from "os";
32620
32945
  init_user_config();
32621
32946
  init_console();
32622
- var CACHE_BASE2 = join18(homedir5(), ".promptscript", "cache", "registries");
32947
+ var CACHE_BASE2 = join19(homedir5(), ".promptscript", "cache", "registries");
32623
32948
  async function resolveCommand(importPath, _options) {
32624
32949
  try {
32625
32950
  const configFile = findConfigFile();
@@ -32661,11 +32986,11 @@ async function resolveCommand(importPath, _options) {
32661
32986
  }
32662
32987
  const { repoUrl, path, version } = expanded;
32663
32988
  const cacheVersion = version ?? "latest";
32664
- const cacheDir = join18(CACHE_BASE2, repoUrl.replace(/[/:]/g, "_"), cacheVersion);
32665
- const cacheExists = existsSync21(cacheDir);
32989
+ const cacheDir = join19(CACHE_BASE2, repoUrl.replace(/[/:]/g, "_"), cacheVersion);
32990
+ const cacheExists = existsSync22(cacheDir);
32666
32991
  const filePath = path.endsWith(".prs") ? path : `${path}.prs`;
32667
- const cachedFile = join18(cacheDir, filePath);
32668
- const fileExists3 = cacheExists && existsSync21(cachedFile);
32992
+ const cachedFile = join19(cacheDir, filePath);
32993
+ const fileExists3 = cacheExists && existsSync22(cachedFile);
32669
32994
  ConsoleOutput.newline();
32670
32995
  console.log(` Alias: ${aliasName} \u2192 ${repoUrl}`);
32671
32996
  console.log(` Repo: ${repoUrl}`);
@@ -32682,7 +33007,7 @@ async function resolveCommand(importPath, _options) {
32682
33007
  ConsoleOutput.newline();
32683
33008
  } else {
32684
33009
  const resolvedPath = importPath.endsWith(".prs") ? importPath : `${importPath}.prs`;
32685
- const exists = existsSync21(resolvedPath);
33010
+ const exists = existsSync22(resolvedPath);
32686
33011
  ConsoleOutput.newline();
32687
33012
  console.log(` Import: ${importPath}`);
32688
33013
  console.log(` Type: local file`);
@@ -32699,8 +33024,8 @@ async function resolveCommand(importPath, _options) {
32699
33024
  }
32700
33025
 
32701
33026
  // packages/cli/src/commands/inspect.ts
32702
- import { existsSync as existsSync22 } from "fs";
32703
- 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";
32704
33029
  init_console();
32705
33030
  async function inspectCommand(skillName, options) {
32706
33031
  const isJson = options.format === "json";
@@ -32713,8 +33038,8 @@ async function inspectCommand(skillName, options) {
32713
33038
  localPath: "./.promptscript",
32714
33039
  registries: config.registries
32715
33040
  });
32716
- const entryPath = resolve17("./.promptscript/project.prs");
32717
- if (!existsSync22(entryPath)) {
33041
+ const entryPath = resolve18("./.promptscript/project.prs");
33042
+ if (!existsSync23(entryPath)) {
32718
33043
  spinner.stop();
32719
33044
  ConsoleOutput.error(`Entry file not found: ${entryPath}`);
32720
33045
  process.exitCode = 1;
@@ -32786,7 +33111,7 @@ function summarizeValue(val) {
32786
33111
  return String(val);
32787
33112
  }
32788
33113
  function shortPath(source) {
32789
- return basename12(source);
33114
+ return basename13(source);
32790
33115
  }
32791
33116
  function getPropertySource(propName, trace, baseSource) {
32792
33117
  for (let i = trace.length - 1; i >= 0; i--) {
@@ -32894,8 +33219,8 @@ init_console();
32894
33219
  // packages/cli/src/commands/import.ts
32895
33220
  init_src3();
32896
33221
  init_console();
32897
- import { resolve as resolve18, join as join19, basename as basename13 } from "path";
32898
- 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";
32899
33224
  import { stat as stat2, readdir as readdir6, readFile as readFile21, writeFile as writeFile11, mkdir as mkdir9 } from "fs/promises";
32900
33225
  function parseInstructionFrontmatter(content) {
32901
33226
  const match = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/.exec(content);
@@ -32923,10 +33248,10 @@ async function importDirectory(dirPath) {
32923
33248
  }
32924
33249
  const lines = ["@meta {", ' version: "1.0"', "}", "", "@guards {"];
32925
33250
  for (const filename of instructionFiles) {
32926
- const filePath = join19(dirPath, filename);
33251
+ const filePath = join20(dirPath, filename);
32927
33252
  const content = await readFile21(filePath, "utf-8");
32928
33253
  const frontmatter = parseInstructionFrontmatter(content);
32929
- const entryName = basename13(filename).replace(/\.instructions\.md$/i, "");
33254
+ const entryName = basename14(filename).replace(/\.instructions\.md$/i, "");
32930
33255
  if (frontmatter) {
32931
33256
  const globPatterns = frontmatter.applyTo.map((p) => `"${p}"`).join(", ");
32932
33257
  lines.push(` ${entryName}(${globPatterns}) {`);
@@ -32949,7 +33274,7 @@ async function importDirectory(dirPath) {
32949
33274
  return lines.join("\n");
32950
33275
  }
32951
33276
  async function importCommand(file, options) {
32952
- const filepath = resolve18(file);
33277
+ const filepath = resolve19(file);
32953
33278
  const inputStat = await stat2(filepath).catch(() => null);
32954
33279
  if (inputStat?.isDirectory()) {
32955
33280
  const prsContent = await importDirectory(filepath);
@@ -32959,7 +33284,7 @@ async function importCommand(file, options) {
32959
33284
  }
32960
33285
  const outputDir = options.output ?? ".promptscript";
32961
33286
  await mkdir9(outputDir, { recursive: true });
32962
- const outputPath = join19(outputDir, "imported.prs");
33287
+ const outputPath = join20(outputDir, "imported.prs");
32963
33288
  await writeFile11(outputPath, prsContent);
32964
33289
  console.log(`\u2714 Imported directory to ${outputPath}`);
32965
33290
  return;
@@ -32981,11 +33306,11 @@ async function importCommand(file, options) {
32981
33306
  console.log(result.prsContent);
32982
33307
  return;
32983
33308
  }
32984
- const outputDir = resolve18(options.output ?? ".promptscript");
32985
- if (!existsSync23(outputDir)) {
33309
+ const outputDir = resolve19(options.output ?? ".promptscript");
33310
+ if (!existsSync24(outputDir)) {
32986
33311
  mkdirSync(outputDir, { recursive: true });
32987
33312
  }
32988
- const outputPath = resolve18(outputDir, "imported.prs");
33313
+ const outputPath = resolve19(outputDir, "imported.prs");
32989
33314
  writeFileSync2(outputPath, result.prsContent, "utf-8");
32990
33315
  ConsoleOutput.success(`Written to ${outputPath}`);
32991
33316
  if (options.validate) {
@@ -33100,18 +33425,18 @@ async function detectMarker(filePath) {
33100
33425
  }
33101
33426
 
33102
33427
  // packages/cli/src/hooks/debounce-lock.ts
33103
- import { createHash as createHash2 } from "node:crypto";
33104
- 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";
33105
33430
  import { tmpdir } from "node:os";
33106
- import { join as join20 } from "node:path";
33431
+ import { join as join21 } from "node:path";
33107
33432
  var STALE_THRESHOLD_MS = 3e4;
33108
33433
  function getLockPath(projectDir) {
33109
- const hash = createHash2("md5").update(projectDir).digest("hex").slice(0, 12);
33110
- 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`);
33111
33436
  }
33112
33437
  function acquireLock(projectDir) {
33113
33438
  const lockPath = getLockPath(projectDir);
33114
- if (existsSync24(lockPath)) {
33439
+ if (existsSync25(lockPath)) {
33115
33440
  try {
33116
33441
  const stat4 = statSync(lockPath);
33117
33442
  const age = Date.now() - stat4.mtimeMs;
@@ -33219,9 +33544,10 @@ Usage: echo '{"file_path":"..."}' | prs hook ${action}
33219
33544
  init_hooks();
33220
33545
 
33221
33546
  // packages/cli/src/commands/skills.ts
33222
- import { writeFile as writeFile12, readFile as readFile23, readdir as readdir7 } from "fs/promises";
33223
- import { resolve as resolve19 } from "path";
33224
- 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";
33225
33551
  import { parse as parseYaml12, stringify as stringifyYaml4 } from "yaml";
33226
33552
  init_console();
33227
33553
  init_src();
@@ -33241,6 +33567,77 @@ function normalizeSkillSource(input2) {
33241
33567
  }
33242
33568
  return source;
33243
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
+ }
33244
33641
  var HEADER_DIRECTIVES = ["@use ", "@inherit ", "@meta "];
33245
33642
  function extractRepoUrl(source) {
33246
33643
  const parts = source.replace(/^https?:\/\//, "").split("/");
@@ -33268,8 +33665,8 @@ function validateRemoteSource(source) {
33268
33665
  }
33269
33666
  async function resolveEntryFile(fileFlag) {
33270
33667
  if (fileFlag) {
33271
- const resolved = resolve19(fileFlag);
33272
- if (!existsSync25(resolved)) {
33668
+ const resolved = resolve20(fileFlag);
33669
+ if (!existsSync26(resolved)) {
33273
33670
  throw new Error(`File not found: ${resolved}`);
33274
33671
  }
33275
33672
  return resolved;
@@ -33279,24 +33676,24 @@ async function resolveEntryFile(fileFlag) {
33279
33676
  try {
33280
33677
  const config = await loadConfig();
33281
33678
  if (config.input?.entry) {
33282
- const entryPath = resolve19(config.input.entry);
33283
- if (existsSync25(entryPath)) {
33679
+ const entryPath = resolve20(config.input.entry);
33680
+ if (existsSync26(entryPath)) {
33284
33681
  return entryPath;
33285
33682
  }
33286
33683
  }
33287
33684
  } catch {
33288
33685
  }
33289
33686
  }
33290
- const promptscriptDir = resolve19(".promptscript");
33291
- if (existsSync25(promptscriptDir)) {
33687
+ const promptscriptDir = resolve20(".promptscript");
33688
+ if (existsSync26(promptscriptDir)) {
33292
33689
  const entries = await readdir7(promptscriptDir);
33293
33690
  const prsFiles = entries.filter((f) => f.endsWith(".prs"));
33294
33691
  if (prsFiles.length === 1) {
33295
- return resolve19(promptscriptDir, prsFiles[0]);
33692
+ return resolve20(promptscriptDir, prsFiles[0]);
33296
33693
  }
33297
33694
  if (prsFiles.length > 1) {
33298
33695
  if (prsFiles.includes("project.prs")) {
33299
- return resolve19(promptscriptDir, "project.prs");
33696
+ return resolve20(promptscriptDir, "project.prs");
33300
33697
  }
33301
33698
  throw new Error(
33302
33699
  `Multiple .prs files found in .promptscript/. Use --file to specify which one.`
@@ -33340,7 +33737,7 @@ function extractUsePath(line) {
33340
33737
  }
33341
33738
  async function loadLockfile2() {
33342
33739
  const defaultLockfile = { version: LOCKFILE_VERSION, dependencies: {} };
33343
- if (!existsSync25(LOCKFILE_PATH)) {
33740
+ if (!existsSync26(LOCKFILE_PATH)) {
33344
33741
  return defaultLockfile;
33345
33742
  }
33346
33743
  try {
@@ -33359,6 +33756,14 @@ async function saveLockfile(lockfile) {
33359
33756
  async function skillsAddCommand(rawSource, options) {
33360
33757
  const spinner = createSpinner("Adding skill...").start();
33361
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
+ }
33362
33767
  const source = normalizeSkillSource(rawSource);
33363
33768
  const sourceError = validateRemoteSource(source);
33364
33769
  if (sourceError) {
@@ -33387,15 +33792,52 @@ async function skillsAddCommand(rawSource, options) {
33387
33792
  process.exitCode = 1;
33388
33793
  return;
33389
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;
33390
33833
  const insertionPoint = findInsertionPoint(lines);
33391
33834
  const newLine = `@use ${source}`;
33392
33835
  lines.splice(insertionPoint, 0, newLine);
33393
33836
  const updatedContent = lines.join("\n");
33394
- const lockfile = await loadLockfile2();
33395
33837
  const lockEntry = {
33396
33838
  version: "latest",
33397
33839
  commit: validation.headCommit ?? "0000000000000000000000000000000000000000",
33398
- integrity: "sha256-pending",
33840
+ integrity,
33399
33841
  source: "md"
33400
33842
  };
33401
33843
  lockfile.dependencies[source] = lockEntry;
@@ -33523,10 +33965,39 @@ async function skillsUpdateCommand(name, options) {
33523
33965
  });
33524
33966
  continue;
33525
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
+ }
33526
33997
  lockfile.dependencies[key] = {
33527
33998
  version: dep.version ?? "latest",
33528
33999
  commit: validation.headCommit,
33529
- integrity: dep.integrity ?? "sha256-pending",
34000
+ integrity,
33530
34001
  source: "md",
33531
34002
  fetchedAt
33532
34003
  };
@@ -33573,7 +34044,7 @@ async function skillsUpdateCommand(name, options) {
33573
34044
 
33574
34045
  // packages/cli/src/cli.ts
33575
34046
  var __filename4 = fileURLToPath4(import.meta.url);
33576
- var __dirname4 = dirname14(__filename4);
34047
+ var __dirname4 = dirname16(__filename4);
33577
34048
  var program = new Command();
33578
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) => {
33579
34050
  const opts = thisCommand.opts();
@@ -33632,10 +34103,10 @@ program.command("resolve <import>").description("Show full resolution chain for
33632
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);
33633
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);
33634
34105
  var skills = program.command("skills").description("Manage PromptScript skills");
33635
- 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));
33636
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));
33637
34108
  skills.command("list").description("List skills imported in the current project").action(() => skillsListCommand());
33638
- 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));
33639
34110
  var registry = program.command("registry").description("Manage PromptScript registries");
33640
34111
  registerRegistryCommands(registry);
33641
34112
  function run(args = process.argv) {