@promptscript/cli 1.14.1 → 1.15.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
@@ -840,6 +840,30 @@ var init_levenshtein = __esm({
840
840
  }
841
841
  });
842
842
 
843
+ // packages/core/src/utils/markers.ts
844
+ function stripPromptScriptMarkers(content) {
845
+ return content.replace(new RegExp(`${HTML_MARKER}\\n*`, "g"), "").replace(new RegExp(`${YAML_MARKER}[^\\n]+\\n*`, "g"), "");
846
+ }
847
+ function isGeneratedByPromptScript(content) {
848
+ const head = new RegExp(`^\\s*(?:${YAML_MARKER}|${HTML_MARKER})`);
849
+ return content.split("\n", 5).some((line) => head.test(line));
850
+ }
851
+ function stripFrontmatterMarkerLines(frontmatter) {
852
+ const markerLine = new RegExp(`^\\s*${YAML_MARKER}`);
853
+ return frontmatter.split("\n").filter((line) => !markerLine.test(line)).join("\n");
854
+ }
855
+ function stripLeadingHtmlMarker(body) {
856
+ return body.replace(new RegExp(`^\\s*${HTML_MARKER}\\n*`), "");
857
+ }
858
+ var HTML_MARKER, YAML_MARKER;
859
+ var init_markers = __esm({
860
+ "packages/core/src/utils/markers.ts"() {
861
+ "use strict";
862
+ HTML_MARKER = "<!--\\s*PromptScript [^>]+-->";
863
+ YAML_MARKER = "#\\s*promptscript-generated:";
864
+ }
865
+ });
866
+
843
867
  // packages/core/src/utils/index.ts
844
868
  var init_utils = __esm({
845
869
  "packages/core/src/utils/index.ts"() {
@@ -850,6 +874,7 @@ var init_utils = __esm({
850
874
  init_path();
851
875
  init_version();
852
876
  init_levenshtein();
877
+ init_markers();
853
878
  }
854
879
  });
855
880
 
@@ -12714,6 +12739,7 @@ var init_markdown_instruction_formatter = __esm({
12714
12739
  this.addSection(sections, this.project(ast, renderer));
12715
12740
  this.addSection(sections, this.techStack(ast, renderer));
12716
12741
  this.addSection(sections, this.architecture(ast, renderer));
12742
+ this.addSection(sections, this.context(ast, renderer));
12717
12743
  this.addSection(sections, this.codeStandards(ast, renderer));
12718
12744
  this.addSection(sections, this.gitCommits(ast, renderer));
12719
12745
  this.addSection(sections, this.configFiles(ast, renderer));
@@ -12806,6 +12832,30 @@ var init_markdown_instruction_formatter = __esm({
12806
12832
  const normalizedContent = this.normalizeMarkdownForPrettier(content);
12807
12833
  return renderer.renderSection("Architecture", normalizedContent.trim()) + "\n";
12808
12834
  }
12835
+ /**
12836
+ * Render @context block text content as a "## Context" section.
12837
+ *
12838
+ * The "## Architecture" subsection (with code block) is stripped because
12839
+ * it is already rendered separately by {@link architecture}. Remaining
12840
+ * "## " headings are downgraded to "### " to avoid clashing with the
12841
+ * formatter's own h2 section headings. When no @identity block exists,
12842
+ * the project() fallback already consumes the full @context text, so
12843
+ * this method returns null to avoid duplication.
12844
+ */
12845
+ context(ast, renderer) {
12846
+ const identity = this.findBlock(ast, "identity");
12847
+ if (!identity) return null;
12848
+ const contextBlock = this.findBlock(ast, "context");
12849
+ if (!contextBlock) return null;
12850
+ const text = this.extractText(contextBlock.content);
12851
+ if (!text) return null;
12852
+ const archMatch = this.extractSectionWithCodeBlock(text, "## Architecture");
12853
+ const remainingText = archMatch ? text.replace(archMatch, "").trim() : text.trim();
12854
+ if (!remainingText) return null;
12855
+ const downgradedText = remainingText.replace(/^(\s*)## /gm, "$1### ");
12856
+ const normalizedContent = this.normalizeMarkdownForPrettier(downgradedText);
12857
+ return renderer.renderSection("Context", normalizedContent.trim()) + "\n";
12858
+ }
12809
12859
  codeStandards(ast, renderer) {
12810
12860
  const standards = this.findBlock(ast, "standards");
12811
12861
  if (!standards) return null;
@@ -13844,6 +13894,8 @@ ${f.content}`
13844
13894
  if (techStack) sections.push(techStack);
13845
13895
  const architecture = this.architecture(ast, renderer);
13846
13896
  if (architecture) sections.push(architecture);
13897
+ const contextSection = this.contextSection(ast, renderer);
13898
+ if (contextSection) sections.push(contextSection);
13847
13899
  const codeStandards = this.codeStandards(ast, renderer);
13848
13900
  if (codeStandards) sections.push(codeStandards);
13849
13901
  const shortcuts = this.shortcutsSection(ast, renderer);
@@ -13954,6 +14006,19 @@ ${f.content}`
13954
14006
  const content = archMatch.replace("## Architecture", "").trim();
13955
14007
  return renderer.renderSection("architecture", this.stripAllIndent(content));
13956
14008
  }
14009
+ contextSection(ast, renderer) {
14010
+ const identity = this.findBlock(ast, "identity");
14011
+ if (!identity) return null;
14012
+ const contextBlock = this.findBlock(ast, "context");
14013
+ if (!contextBlock) return null;
14014
+ const text = this.extractText(contextBlock.content);
14015
+ if (!text) return null;
14016
+ const archMatch = this.extractSectionWithCodeBlock(text, "## Architecture");
14017
+ const remainingText = archMatch ? text.replace(archMatch, "").trim() : text.trim();
14018
+ if (!remainingText) return null;
14019
+ const downgradedText = remainingText.replace(/^(\s*)## /gm, "$1### ");
14020
+ return renderer.renderSection("Context", this.stripAllIndent(downgradedText));
14021
+ }
13957
14022
  codeStandards(ast, renderer) {
13958
14023
  const standards = this.findBlock(ast, "standards");
13959
14024
  if (!standards) return null;
@@ -15046,6 +15111,7 @@ ${f.content}`
15046
15111
  this.addSection(sections, this.project(ast, renderer));
15047
15112
  this.addSection(sections, this.techStack(ast, renderer));
15048
15113
  this.addSection(sections, this.architecture(ast, renderer));
15114
+ this.addSection(sections, this.contextSection(ast, renderer));
15049
15115
  this.addSection(sections, this.codeStandards(ast, renderer));
15050
15116
  this.addSection(sections, this.gitCommits(ast, renderer));
15051
15117
  this.addSection(sections, this.configFiles(ast, renderer));
@@ -15138,6 +15204,20 @@ ${f.content}`
15138
15204
  const normalizedContent = this.normalizeMarkdownForPrettier(content);
15139
15205
  return renderer.renderSection("Architecture", normalizedContent.trim()) + "\n";
15140
15206
  }
15207
+ contextSection(ast, renderer) {
15208
+ const identity = this.findBlock(ast, "identity");
15209
+ if (!identity) return null;
15210
+ const contextBlock = this.findBlock(ast, "context");
15211
+ if (!contextBlock) return null;
15212
+ const text = this.extractText(contextBlock.content);
15213
+ if (!text) return null;
15214
+ const archMatch = this.extractSectionWithCodeBlock(text, "## Architecture");
15215
+ const remainingText = archMatch ? text.replace(archMatch, "").trim() : text.trim();
15216
+ if (!remainingText) return null;
15217
+ const downgradedText = remainingText.replace(/^(\s*)## /gm, "$1### ");
15218
+ const normalizedContent = this.normalizeMarkdownForPrettier(downgradedText);
15219
+ return renderer.renderSection("Context", normalizedContent.trim()) + "\n";
15220
+ }
15141
15221
  codeStandards(ast, renderer) {
15142
15222
  const standards = this.findBlock(ast, "standards");
15143
15223
  if (!standards) return null;
@@ -16345,6 +16425,8 @@ ${content}`);
16345
16425
  if (techStack) sections.push(techStack);
16346
16426
  const architecture = this.architecture(ast, renderer);
16347
16427
  if (architecture) sections.push(architecture);
16428
+ const contextSection = this.contextSection(ast, renderer);
16429
+ if (contextSection) sections.push(contextSection);
16348
16430
  const codeStandards = this.codeStandards(ast, renderer);
16349
16431
  if (codeStandards) sections.push(codeStandards);
16350
16432
  const gitCommits = this.gitCommits(ast, renderer);
@@ -16672,6 +16754,21 @@ ${this.stripAllIndent(content)}`;
16672
16754
  }
16673
16755
  return null;
16674
16756
  }
16757
+ contextSection(ast, _renderer) {
16758
+ const identity = this.findBlock(ast, "identity");
16759
+ if (!identity) return null;
16760
+ const contextBlock = this.findBlock(ast, "context");
16761
+ if (!contextBlock) return null;
16762
+ const text = this.extractText(contextBlock.content);
16763
+ if (!text) return null;
16764
+ const archMatch = this.extractSectionWithCodeBlock(text, "## Architecture");
16765
+ const remainingText = archMatch ? text.replace(archMatch, "").trim() : text.trim();
16766
+ if (!remainingText) return null;
16767
+ const downgradedText = remainingText.replace(/^(\s*)## /gm, "$1### ");
16768
+ return `## Context
16769
+
16770
+ ${this.stripAllIndent(downgradedText)}`;
16771
+ }
16675
16772
  /**
16676
16773
  * Extract code standards from @standards block.
16677
16774
  * Uses shared extractor for dynamic key iteration (parity with GitHub/Claude/Cursor).
@@ -17418,6 +17515,7 @@ ${links.join("\n")}
17418
17515
  this.addSection(sections, this.project(ast, renderer));
17419
17516
  this.addSection(sections, this.techStack(ast, renderer));
17420
17517
  this.addSection(sections, this.architecture(ast, renderer));
17518
+ this.addSection(sections, this.context(ast, renderer));
17421
17519
  this.addSection(sections, this.commands(ast, renderer));
17422
17520
  this.addSection(sections, this.postWork(ast, renderer));
17423
17521
  }
@@ -26768,16 +26866,22 @@ function extractReservedParams(params) {
26768
26866
  const remaining = [];
26769
26867
  let only;
26770
26868
  let exclude;
26869
+ let includes;
26870
+ let excludes;
26771
26871
  for (const param of params) {
26772
26872
  if (param.name === "only" && Array.isArray(param.value)) {
26773
26873
  only = param.value.filter((v) => typeof v === "string");
26774
26874
  } else if (param.name === "exclude" && Array.isArray(param.value)) {
26775
26875
  exclude = param.value.filter((v) => typeof v === "string");
26876
+ } else if (param.name === "includes" && Array.isArray(param.value)) {
26877
+ includes = param.value.filter((v) => typeof v === "string");
26878
+ } else if (param.name === "excludes" && Array.isArray(param.value)) {
26879
+ excludes = param.value.filter((v) => typeof v === "string");
26776
26880
  } else {
26777
26881
  remaining.push(param);
26778
26882
  }
26779
26883
  }
26780
- return { only, exclude, remaining };
26884
+ return { only, exclude, includes, excludes, remaining };
26781
26885
  }
26782
26886
  function filterBlocks(blocks, options) {
26783
26887
  if (options.only) {
@@ -26790,6 +26894,44 @@ function filterBlocks(blocks, options) {
26790
26894
  }
26791
26895
  return blocks;
26792
26896
  }
26897
+ function filterSkillsBlock(program2, options) {
26898
+ if (!options.includes && !options.excludes) {
26899
+ return program2;
26900
+ }
26901
+ const skillsBlock = program2.blocks.find((b) => b.name === "skills");
26902
+ if (!skillsBlock || skillsBlock.content.type !== "ObjectContent") {
26903
+ return program2;
26904
+ }
26905
+ const props = skillsBlock.content.properties;
26906
+ const skillNames = Object.keys(props);
26907
+ let filteredNames;
26908
+ if (options.includes) {
26909
+ const allowSet = new Set(options.includes);
26910
+ filteredNames = skillNames.filter((name) => allowSet.has(name));
26911
+ } else {
26912
+ const denySet = new Set(options.excludes);
26913
+ filteredNames = skillNames.filter((name) => !denySet.has(name));
26914
+ }
26915
+ if (filteredNames.length === skillNames.length) {
26916
+ return program2;
26917
+ }
26918
+ const filteredProps = {};
26919
+ for (const name of filteredNames) {
26920
+ filteredProps[name] = deepCloneValue2(props[name]);
26921
+ }
26922
+ const newSkillsBlock = {
26923
+ ...deepClone(skillsBlock),
26924
+ content: {
26925
+ type: "ObjectContent",
26926
+ properties: filteredProps,
26927
+ loc: skillsBlock.content.loc
26928
+ }
26929
+ };
26930
+ return {
26931
+ ...program2,
26932
+ blocks: program2.blocks.map((b) => b.name === "skills" ? newSkillsBlock : b)
26933
+ };
26934
+ }
26793
26935
 
26794
26936
  // packages/resolver/src/extensions.ts
26795
26937
  init_src();
@@ -27407,7 +27549,9 @@ function parseSkillMd(content) {
27407
27549
  let rawFrontmatter;
27408
27550
  let bodyContent;
27409
27551
  if (frontmatterStart >= 0 && frontmatterEnd > frontmatterStart) {
27410
- const frontmatterLines = lines.slice(frontmatterStart + 1, frontmatterEnd);
27552
+ const frontmatterLines = stripFrontmatterMarkerLines(
27553
+ lines.slice(frontmatterStart + 1, frontmatterEnd).join("\n")
27554
+ ).split("\n");
27411
27555
  const parsed = parseFrontmatterFields(frontmatterLines);
27412
27556
  name = parsed.name;
27413
27557
  description = parsed.description;
@@ -27418,9 +27562,9 @@ function parseSkillMd(content) {
27418
27562
  scripts = parsed.scripts;
27419
27563
  license = parsed.license;
27420
27564
  rawFrontmatter = frontmatterLines.join("\n");
27421
- bodyContent = lines.slice(frontmatterEnd + 1).join("\n").trim();
27565
+ bodyContent = stripLeadingHtmlMarker(lines.slice(frontmatterEnd + 1).join("\n")).trim();
27422
27566
  } else {
27423
- bodyContent = content.trim();
27567
+ bodyContent = stripLeadingHtmlMarker(content).trim();
27424
27568
  }
27425
27569
  return {
27426
27570
  name,
@@ -28092,6 +28236,13 @@ async function fileExists(path2) {
28092
28236
  return false;
28093
28237
  }
28094
28238
  }
28239
+ async function isGeneratedOutput(filePath) {
28240
+ try {
28241
+ return isGeneratedByPromptScript(await readFile8(filePath, "utf-8"));
28242
+ } catch {
28243
+ return false;
28244
+ }
28245
+ }
28095
28246
  async function discoverSkillDirs(basePath) {
28096
28247
  const result = /* @__PURE__ */ new Map();
28097
28248
  const queue = [{ dir: basePath, depth: 0 }];
@@ -28109,7 +28260,7 @@ async function discoverSkillDirs(basePath) {
28109
28260
  if (!isSafeSkillName(entry.name)) continue;
28110
28261
  const subDir = resolve9(dir, entry.name);
28111
28262
  const skillMd = resolve9(subDir, "SKILL.md");
28112
- if (await fileExists(skillMd)) {
28263
+ if (await fileExists(skillMd) && !await isGeneratedOutput(skillMd)) {
28113
28264
  if (!result.has(entry.name)) {
28114
28265
  result.set(entry.name, subDir);
28115
28266
  }
@@ -28193,6 +28344,10 @@ async function resolveNativeSkills(ast, registryPath, sourceFile, localPath, opt
28193
28344
  if (skillMdPath && await fileExists(skillMdPath)) {
28194
28345
  try {
28195
28346
  const rawContent = await readFile8(skillMdPath, "utf-8");
28347
+ if (isGeneratedByPromptScript(rawContent)) {
28348
+ logger.verbose(`Ignoring generated SKILL.md for skill '${skillName}': ${skillMdPath}`);
28349
+ continue;
28350
+ }
28196
28351
  const parsed = parseSkillMd(rawContent);
28197
28352
  const updatedSkill = { ...skillObj };
28198
28353
  const skillArgs = extractSkillArgs(skillObj);
@@ -28323,6 +28478,10 @@ async function discoverCommandFiles(dir, logger = noopLogger2) {
28323
28478
  logger.verbose(`Skipping binary command file: ${entry.name}`);
28324
28479
  continue;
28325
28480
  }
28481
+ if (isGeneratedByPromptScript(content)) {
28482
+ logger.verbose(`Skipping generated command file: ${entry.name}`);
28483
+ continue;
28484
+ }
28326
28485
  commands[cmdName] = {
28327
28486
  type: "TextContent",
28328
28487
  value: content.trim(),
@@ -28401,8 +28560,8 @@ function parseAgentFrontmatter(content) {
28401
28560
  searchFrom = idx + 1;
28402
28561
  }
28403
28562
  if (endIdx === -1) return null;
28404
- const yamlStr = trimmed.slice(3, endIdx).trim();
28405
- const body = trimmed.slice(endIdx + 3).trim();
28563
+ const yamlStr = stripFrontmatterMarkerLines(trimmed.slice(3, endIdx)).trim();
28564
+ const body = stripLeadingHtmlMarker(trimmed.slice(endIdx + 3)).trim();
28406
28565
  const frontmatter = {};
28407
28566
  for (const line of yamlStr.split("\n")) {
28408
28567
  const colonIdx = line.indexOf(":");
@@ -28439,6 +28598,10 @@ async function discoverAgentFiles(dir, logger = noopLogger2) {
28439
28598
  logger.verbose(`Skipping binary agent file: ${entry.name}`);
28440
28599
  continue;
28441
28600
  }
28601
+ if (isGeneratedByPromptScript(fileContent)) {
28602
+ logger.verbose(`Skipping generated agent file: ${entry.name}`);
28603
+ continue;
28604
+ }
28442
28605
  const parsed = parseAgentFrontmatter(fileContent);
28443
28606
  if (!parsed) {
28444
28607
  logger.verbose(`Skipping agent file without frontmatter: ${entry.name}`);
@@ -29926,6 +30089,8 @@ var GitRegistry = class {
29926
30089
  options.baseDir = baseDir;
29927
30090
  }
29928
30091
  const git = simpleGit(options);
30092
+ git.env("GIT_TERMINAL_PROMPT", "0");
30093
+ git.env("GCM_INTERACTIVE", "never");
29929
30094
  if (this.auth?.type === "token") {
29930
30095
  const token = this.resolveToken();
29931
30096
  if (token) {
@@ -30098,13 +30263,13 @@ function createGitRegistry(options) {
30098
30263
  return new GitRegistry(options);
30099
30264
  }
30100
30265
  async function validateRemoteAccess(repoUrl, ref) {
30101
- const git = simpleGit();
30266
+ const git = simpleGit().env("GIT_TERMINAL_PROMPT", "0").env("GCM_INTERACTIVE", "never");
30102
30267
  try {
30103
30268
  const isCommit = ref !== void 0 && /^[0-9a-f]{40}$/i.test(ref);
30104
30269
  if (isCommit) {
30105
30270
  const probeDir = await fs2.mkdtemp(join7(tmpdir(), "promptscript-ref-"));
30106
30271
  try {
30107
- const probe = simpleGit(probeDir);
30272
+ const probe = simpleGit(probeDir).env("GIT_TERMINAL_PROMPT", "0").env("GCM_INTERACTIVE", "never");
30108
30273
  await probe.init();
30109
30274
  await probe.addRemote("origin", repoUrl);
30110
30275
  await probe.fetch(["origin", ref, "--depth=1"]);
@@ -31103,7 +31268,9 @@ var Resolver = class {
31103
31268
  errors.push(...imported.errors);
31104
31269
  if (imported.ast) {
31105
31270
  let resolvedImport = imported.ast;
31106
- const { only, exclude, remaining } = extractReservedParams(use.params);
31271
+ const { only, exclude, includes, excludes, remaining } = extractReservedParams(
31272
+ use.params
31273
+ );
31107
31274
  if (imported.ast.meta?.params || remaining.length > 0) {
31108
31275
  this.logger.debug(`Binding template parameters for ${importPath}`);
31109
31276
  try {
@@ -31134,6 +31301,12 @@ var Resolver = class {
31134
31301
  blocks: filterBlocks(resolvedImport.blocks, { only, exclude })
31135
31302
  };
31136
31303
  }
31304
+ if (includes || excludes) {
31305
+ this.logger.debug(
31306
+ `Filtering skills: ${includes ? `includes=[${includes.join(",")}]` : `excludes=[${excludes.join(",")}]`}`
31307
+ );
31308
+ resolvedImport = filterSkillsBlock(resolvedImport, { includes, excludes });
31309
+ }
31137
31310
  let effectiveUse = use;
31138
31311
  const fromConfig = this.options.skillTargets?.[use.path.raw];
31139
31312
  if (use.outputDir && fromConfig && use.outputDir !== fromConfig) {
@@ -34309,18 +34482,21 @@ var duplicateSkills = {
34309
34482
  // packages/validator/src/rules/use-block-filter.ts
34310
34483
  init_src();
34311
34484
  var BLOCK_TYPE_SET = new Set(BLOCK_TYPES);
34312
- var RESERVED_USE_PARAMS = ["only", "exclude"];
34485
+ var RESERVED_BLOCK_PARAMS = ["only", "exclude"];
34486
+ var RESERVED_SKILL_PARAMS = ["includes", "excludes"];
34313
34487
  var useBlockFilter = {
34314
34488
  id: "PS021",
34315
34489
  name: "use-block-filter",
34316
- description: "Validate only/exclude block filter parameters on @use directives",
34490
+ description: "Validate only/exclude/includes/excludes filter parameters on @use directives",
34317
34491
  defaultSeverity: "error",
34318
34492
  validate: (ctx) => {
34319
34493
  for (const use of ctx.ast.uses) {
34320
34494
  if (!use.params || use.params.length === 0) continue;
34321
34495
  const onlyParam = use.params.find((p) => p.name === "only");
34322
34496
  const excludeParam = use.params.find((p) => p.name === "exclude");
34323
- if (!onlyParam && !excludeParam) continue;
34497
+ const includesParam = use.params.find((p) => p.name === "includes");
34498
+ const excludesParam = use.params.find((p) => p.name === "excludes");
34499
+ if (!onlyParam && !excludeParam && !includesParam && !excludesParam) continue;
34324
34500
  if (onlyParam && excludeParam) {
34325
34501
  ctx.report({
34326
34502
  message: '"only" and "exclude" are mutually exclusive in @use',
@@ -34329,7 +34505,15 @@ var useBlockFilter = {
34329
34505
  });
34330
34506
  continue;
34331
34507
  }
34332
- for (const paramName of RESERVED_USE_PARAMS) {
34508
+ if (includesParam && excludesParam) {
34509
+ ctx.report({
34510
+ message: '"includes" and "excludes" are mutually exclusive in @use',
34511
+ location: use.loc,
34512
+ suggestion: "Use either includes: [...] or excludes: [...], not both"
34513
+ });
34514
+ continue;
34515
+ }
34516
+ for (const paramName of RESERVED_BLOCK_PARAMS) {
34333
34517
  const param = paramName === "only" ? onlyParam : excludeParam;
34334
34518
  if (!param) continue;
34335
34519
  if (!Array.isArray(param.value)) {
@@ -34358,6 +34542,25 @@ var useBlockFilter = {
34358
34542
  }
34359
34543
  }
34360
34544
  }
34545
+ for (const paramName of RESERVED_SKILL_PARAMS) {
34546
+ const param = paramName === "includes" ? includesParam : excludesParam;
34547
+ if (!param) continue;
34548
+ if (!Array.isArray(param.value)) {
34549
+ ctx.report({
34550
+ message: `"${paramName}" expects an array, got ${typeof param.value}. Use ${paramName}: ["skillName"]`,
34551
+ location: param.loc ?? use.loc
34552
+ });
34553
+ continue;
34554
+ }
34555
+ if (param.value.length === 0) {
34556
+ ctx.report({
34557
+ message: paramName === "includes" ? 'Empty "includes" filter imports nothing \u2014 consider removing this @use' : 'Empty "excludes" filter has no effect \u2014 consider removing the parameter',
34558
+ location: param.loc ?? use.loc,
34559
+ severity: "warning"
34560
+ });
34561
+ continue;
34562
+ }
34563
+ }
34361
34564
  }
34362
34565
  }
34363
34566
  };
@@ -36703,9 +36906,7 @@ async function resolveRegistryPath(config, options = {}) {
36703
36906
  import { parse as parseYaml5 } from "yaml";
36704
36907
 
36705
36908
  // packages/cli/src/utils/markers.ts
36706
- function stripMarkers(content) {
36707
- return content.replace(/<!-- PromptScript [^>]+ -->\n*/g, "").replace(/# promptscript-generated: [^\n]+\n*/g, "");
36708
- }
36909
+ init_src();
36709
36910
 
36710
36911
  // packages/cli/src/utils/conflict-detector.ts
36711
36912
  init_src();
@@ -37113,13 +37314,15 @@ function resolveCompileConfigPath(projectRoot, options) {
37113
37314
  }
37114
37315
  return configPath;
37115
37316
  }
37116
- async function isPromptScriptGenerated3(filePath) {
37317
+ function hasPromptScriptMarker(content) {
37318
+ const lines = content.split("\n").slice(0, 50);
37319
+ return lines.some((line) => PROMPTSCRIPT_MARKERS2.some((marker) => line.includes(marker)));
37320
+ }
37321
+ async function readFileOrUndefined(filePath) {
37117
37322
  try {
37118
- const content = await readFile14(filePath, "utf-8");
37119
- const lines = content.split("\n").slice(0, 50);
37120
- return lines.some((line) => PROMPTSCRIPT_MARKERS2.some((marker) => line.includes(marker)));
37323
+ return await readFile14(filePath, "utf-8");
37121
37324
  } catch {
37122
- return false;
37325
+ return void 0;
37123
37326
  }
37124
37327
  }
37125
37328
  async function promptForOverwrite(filePath, services) {
@@ -37182,69 +37385,32 @@ async function writeOutputs(outputs, options, _config, services) {
37182
37385
  for (const output of outputs.values()) {
37183
37386
  const outputPath = options.output ? resolve17(options.output, output.path) : resolve17(output.path);
37184
37387
  const fileExists3 = existsSync13(outputPath);
37388
+ const existingContent = fileExists3 ? await readFileOrUndefined(outputPath) : void 0;
37389
+ const contentMatches = existingContent !== void 0 && stripPromptScriptMarkers(existingContent) === stripPromptScriptMarkers(output.content);
37185
37390
  if (options.dryRun) {
37186
- if (fileExists3) {
37187
- const isGenerated2 = await isPromptScriptGenerated3(outputPath);
37188
- if (isGenerated2) {
37189
- const existingContent = await readFile14(outputPath, "utf-8");
37190
- const existingWithoutMarker = stripMarkers(existingContent);
37191
- const newWithoutMarker = stripMarkers(output.content);
37192
- if (existingWithoutMarker === newWithoutMarker) {
37193
- ConsoleOutput.dryRun(`Unchanged: ${outputPath}`);
37194
- result.unchanged.push(outputPath);
37195
- } else {
37196
- ConsoleOutput.dryRun(`Would overwrite: ${outputPath}`);
37197
- result.written.push(outputPath);
37198
- }
37199
- } else {
37200
- const existingContent = await readFile14(outputPath, "utf-8");
37201
- if (existingContent === output.content) {
37202
- ConsoleOutput.dryRun(`Unchanged: ${outputPath}`);
37203
- result.unchanged.push(outputPath);
37204
- } else {
37205
- ConsoleOutput.warning(`Would conflict: ${outputPath} (not generated by PromptScript)`);
37206
- result.written.push(outputPath);
37207
- }
37208
- }
37209
- } else {
37391
+ if (!fileExists3) {
37210
37392
  ConsoleOutput.dryRun(`Would write: ${outputPath}`);
37211
37393
  result.written.push(outputPath);
37212
- }
37213
- continue;
37214
- }
37215
- if (!fileExists3) {
37216
- if (await safeWrite(outputPath, output.content, output.mode)) {
37217
- ConsoleOutput.success(outputPath);
37394
+ } else if (contentMatches) {
37395
+ ConsoleOutput.dryRun(`Unchanged: ${outputPath}`);
37396
+ result.unchanged.push(outputPath);
37397
+ } else if (existingContent !== void 0 && hasPromptScriptMarker(existingContent)) {
37398
+ ConsoleOutput.dryRun(`Would overwrite: ${outputPath}`);
37399
+ result.written.push(outputPath);
37400
+ } else {
37401
+ ConsoleOutput.warning(`Would conflict: ${outputPath} (not generated by PromptScript)`);
37218
37402
  result.written.push(outputPath);
37219
37403
  }
37220
37404
  continue;
37221
37405
  }
37222
- const isGenerated = await isPromptScriptGenerated3(outputPath);
37223
- if (isGenerated) {
37224
- const existingContent = await readFile14(outputPath, "utf-8");
37225
- const existingWithoutMarker = stripMarkers(existingContent);
37226
- const newWithoutMarker = stripMarkers(output.content);
37227
- if (!options.force && existingWithoutMarker === newWithoutMarker) {
37228
- if (output.mode !== void 0) {
37229
- await chmod2(outputPath, output.mode);
37230
- }
37231
- ConsoleOutput.unchanged(outputPath);
37232
- result.unchanged.push(outputPath);
37233
- continue;
37234
- }
37406
+ if (!fileExists3) {
37235
37407
  if (await safeWrite(outputPath, output.content, output.mode)) {
37236
37408
  ConsoleOutput.success(outputPath);
37237
37409
  result.written.push(outputPath);
37238
37410
  }
37239
37411
  continue;
37240
37412
  }
37241
- let contentMatches = false;
37242
- try {
37243
- const existingContent = await readFile14(outputPath, "utf-8");
37244
- contentMatches = existingContent === output.content;
37245
- } catch {
37246
- }
37247
- if (!options.force && contentMatches) {
37413
+ if (contentMatches) {
37248
37414
  if (output.mode !== void 0) {
37249
37415
  await chmod2(outputPath, output.mode);
37250
37416
  }
@@ -37252,6 +37418,13 @@ async function writeOutputs(outputs, options, _config, services) {
37252
37418
  result.unchanged.push(outputPath);
37253
37419
  continue;
37254
37420
  }
37421
+ if (existingContent !== void 0 && hasPromptScriptMarker(existingContent)) {
37422
+ if (await safeWrite(outputPath, output.content, output.mode)) {
37423
+ ConsoleOutput.success(outputPath);
37424
+ result.written.push(outputPath);
37425
+ }
37426
+ continue;
37427
+ }
37255
37428
  if (options.force || overwriteAll) {
37256
37429
  if (await safeWrite(outputPath, output.content, output.mode)) {
37257
37430
  ConsoleOutput.success(outputPath);
@@ -38227,8 +38400,8 @@ async function compareOutput(_name, output, _config, showFull, pager) {
38227
38400
  return true;
38228
38401
  }
38229
38402
  const existingContent = await readFile15(outputPath, "utf-8");
38230
- const existingStripped = stripMarkers(existingContent);
38231
- const newStripped = stripMarkers(newContent);
38403
+ const existingStripped = stripPromptScriptMarkers(existingContent);
38404
+ const newStripped = stripPromptScriptMarkers(newContent);
38232
38405
  if (existingStripped === newStripped) {
38233
38406
  pager.write(chalk2.gray(` ${outputPath} (no changes)`));
38234
38407
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promptscript/cli",
3
- "version": "1.14.1",
3
+ "version": "1.15.0",
4
4
  "description": "CLI for PromptScript - standardize AI instructions across GitHub Copilot, Claude, Cursor and other AI tools",
5
5
  "keywords": [
6
6
  "cli",
@@ -1 +1 @@
1
- {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../../../../packages/cli/src/commands/compile.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAkBlD,OAAO,EAAE,KAAK,WAAW,EAAyB,MAAM,gBAAgB,CAAC;AAijBzE;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,cAAc,EACvB,QAAQ,GAAE,WAAqC,GAC9C,OAAO,CAAC,IAAI,CAAC,CAEf"}
1
+ {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../../../../packages/cli/src/commands/compile.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAkBlD,OAAO,EAAE,KAAK,WAAW,EAAyB,MAAM,gBAAgB,CAAC;AAmhBzE;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,cAAc,EACvB,QAAQ,GAAE,WAAqC,GAC9C,OAAO,CAAC,IAAI,CAAC,CAEf"}
@@ -2,15 +2,8 @@
2
2
  * PromptScript generation marker utilities.
3
3
  *
4
4
  * Markers are added to compiled output files so the compiler can identify
5
- * files it previously generated. These utilities strip markers before
6
- * content comparison to avoid false diffs from timestamp-only changes.
5
+ * files it previously generated. Stripping them before content comparison
6
+ * avoids false diffs from timestamp-only changes.
7
7
  */
8
- /**
9
- * Strip PromptScript generation markers from content for comparison purposes.
10
- * This allows comparing actual content changes while ignoring timestamp updates.
11
- *
12
- * Regex literals are inlined to avoid stateful module-level RegExp objects
13
- * with the `g` flag (which maintain a `lastIndex` cursor between calls).
14
- */
15
- export declare function stripMarkers(content: string): string;
8
+ export { stripPromptScriptMarkers as stripMarkers } from '@promptscript/core';
16
9
  //# sourceMappingURL=markers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"markers.d.ts","sourceRoot":"","sources":["../../../../../packages/cli/src/utils/markers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAIpD"}
1
+ {"version":3,"file":"markers.d.ts","sourceRoot":"","sources":["../../../../../packages/cli/src/utils/markers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,wBAAwB,IAAI,YAAY,EAAE,MAAM,oBAAoB,CAAC"}