@promptscript/cli 1.0.0-alpha.2 → 1.0.0-alpha.3

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
@@ -1,7 +1,7 @@
1
1
  // packages/cli/src/cli.ts
2
2
  import { Command } from "commander";
3
3
  import { fileURLToPath as fileURLToPath2 } from "url";
4
- import { dirname as dirname5 } from "path";
4
+ import { dirname as dirname7 } from "path";
5
5
 
6
6
  // packages/core/src/types/constants.ts
7
7
  var BLOCK_TYPES = [
@@ -86,6 +86,13 @@ function isBuiltInConvention(name) {
86
86
  return name in BUILT_IN_CONVENTIONS;
87
87
  }
88
88
 
89
+ // packages/core/src/types/prettier.ts
90
+ var DEFAULT_PRETTIER_OPTIONS = {
91
+ proseWrap: "preserve",
92
+ tabWidth: 2,
93
+ printWidth: 80
94
+ };
95
+
89
96
  // packages/core/src/errors/base.ts
90
97
  var PSError = class extends Error {
91
98
  /** Error code */
@@ -291,7 +298,7 @@ function getPackageVersion(baseDir, relativePath = "../package.json") {
291
298
 
292
299
  // packages/cli/src/commands/init.ts
293
300
  import { fileURLToPath } from "url";
294
- import { dirname } from "path";
301
+ import { dirname as dirname2 } from "path";
295
302
 
296
303
  // packages/cli/src/services.ts
297
304
  import { writeFile, mkdir, readFile, readdir } from "fs/promises";
@@ -308,7 +315,8 @@ var defaultFileSystem = {
308
315
  var defaultPrompts = {
309
316
  input: prompts.input,
310
317
  confirm: prompts.confirm,
311
- checkbox: prompts.checkbox
318
+ checkbox: prompts.checkbox,
319
+ select: prompts.select
312
320
  };
313
321
  var createDefaultServices = () => ({
314
322
  fs: defaultFileSystem,
@@ -378,6 +386,13 @@ var ConsoleOutput = {
378
386
  if (isQuiet()) return;
379
387
  console.log(chalk.yellow(` \u26A0 ${message}`));
380
388
  },
389
+ /**
390
+ * Print a skipped file message.
391
+ */
392
+ skipped(message) {
393
+ if (isQuiet()) return;
394
+ console.log(chalk.yellow(` \u2298 ${message}`));
395
+ },
381
396
  /**
382
397
  * Print an info message.
383
398
  */
@@ -680,9 +695,114 @@ function formatDetectionResults(detection) {
680
695
  return lines;
681
696
  }
682
697
 
698
+ // packages/cli/src/prettier/loader.ts
699
+ import { existsSync as existsSync2 } from "fs";
700
+ import { readFile as readFile2 } from "fs/promises";
701
+ import { join as join2, dirname, resolve } from "path";
702
+ import { parse as parseYaml } from "yaml";
703
+ var PRETTIER_CONFIG_FILES = [
704
+ ".prettierrc",
705
+ ".prettierrc.json",
706
+ ".prettierrc.yaml",
707
+ ".prettierrc.yml"
708
+ ];
709
+ function findPrettierConfig(startDir = process.cwd()) {
710
+ let currentDir = resolve(startDir);
711
+ const root2 = dirname(currentDir);
712
+ while (currentDir !== root2) {
713
+ for (const configFile of PRETTIER_CONFIG_FILES) {
714
+ const configPath = join2(currentDir, configFile);
715
+ if (existsSync2(configPath)) {
716
+ return configPath;
717
+ }
718
+ }
719
+ currentDir = dirname(currentDir);
720
+ }
721
+ for (const configFile of PRETTIER_CONFIG_FILES) {
722
+ const configPath = join2(currentDir, configFile);
723
+ if (existsSync2(configPath)) {
724
+ return configPath;
725
+ }
726
+ }
727
+ return null;
728
+ }
729
+ async function loadPrettierConfig(configPath) {
730
+ try {
731
+ const content = await readFile2(configPath, "utf-8");
732
+ const isYaml = configPath.endsWith(".yaml") || configPath.endsWith(".yml");
733
+ let parsed;
734
+ if (isYaml || configPath.endsWith(".prettierrc")) {
735
+ try {
736
+ parsed = parseYaml(content);
737
+ } catch {
738
+ parsed = JSON.parse(content);
739
+ }
740
+ } else {
741
+ parsed = JSON.parse(content);
742
+ }
743
+ if (!parsed || typeof parsed !== "object") {
744
+ return null;
745
+ }
746
+ const config = parsed;
747
+ const result = {};
748
+ if (typeof config["proseWrap"] === "string") {
749
+ const proseWrap = config["proseWrap"];
750
+ if (proseWrap === "always" || proseWrap === "never" || proseWrap === "preserve") {
751
+ result.proseWrap = proseWrap;
752
+ }
753
+ }
754
+ if (typeof config["tabWidth"] === "number") {
755
+ result.tabWidth = config["tabWidth"];
756
+ }
757
+ if (typeof config["printWidth"] === "number") {
758
+ result.printWidth = config["printWidth"];
759
+ }
760
+ return result;
761
+ } catch {
762
+ return null;
763
+ }
764
+ }
765
+ async function resolvePrettierOptions(config, basePath = process.cwd()) {
766
+ const formatting = config?.formatting;
767
+ if (!formatting) {
768
+ return { ...DEFAULT_PRETTIER_OPTIONS };
769
+ }
770
+ const result = { ...DEFAULT_PRETTIER_OPTIONS };
771
+ const prettier = formatting.prettier;
772
+ if (prettier === true) {
773
+ const configPath = findPrettierConfig(basePath);
774
+ if (configPath) {
775
+ const loadedOptions = await loadPrettierConfig(configPath);
776
+ if (loadedOptions) {
777
+ Object.assign(result, loadedOptions);
778
+ }
779
+ }
780
+ } else if (typeof prettier === "string") {
781
+ const configPath = resolve(basePath, prettier);
782
+ if (existsSync2(configPath)) {
783
+ const loadedOptions = await loadPrettierConfig(configPath);
784
+ if (loadedOptions) {
785
+ Object.assign(result, loadedOptions);
786
+ }
787
+ }
788
+ } else if (prettier && typeof prettier === "object") {
789
+ Object.assign(result, prettier);
790
+ }
791
+ if (formatting.proseWrap !== void 0) {
792
+ result.proseWrap = formatting.proseWrap;
793
+ }
794
+ if (formatting.tabWidth !== void 0) {
795
+ result.tabWidth = formatting.tabWidth;
796
+ }
797
+ if (formatting.printWidth !== void 0) {
798
+ result.printWidth = formatting.printWidth;
799
+ }
800
+ return result;
801
+ }
802
+
683
803
  // packages/cli/src/commands/init.ts
684
804
  var __filename = fileURLToPath(import.meta.url);
685
- var __dirname = dirname(__filename);
805
+ var __dirname = dirname2(__filename);
686
806
  async function initCommand(options, services = createDefaultServices()) {
687
807
  const { fs: fs4 } = services;
688
808
  if (fs4.existsSync("promptscript.yaml") && !options.force) {
@@ -693,7 +813,14 @@ async function initCommand(options, services = createDefaultServices()) {
693
813
  try {
694
814
  const projectInfo = await detectProject(services);
695
815
  const aiToolsDetection = await detectAITools(services);
696
- const config = await resolveConfig(options, projectInfo, aiToolsDetection, services);
816
+ const prettierConfigPath = findPrettierConfig(process.cwd());
817
+ const config = await resolveConfig(
818
+ options,
819
+ projectInfo,
820
+ aiToolsDetection,
821
+ prettierConfigPath,
822
+ services
823
+ );
697
824
  const spinner = createSpinner("Creating PromptScript configuration...").start();
698
825
  await fs4.mkdir(".promptscript", { recursive: true });
699
826
  const configContent = generateConfig(config);
@@ -716,6 +843,14 @@ async function initCommand(options, services = createDefaultServices()) {
716
843
  ConsoleOutput.muted(` Registry: ${config.registry}`);
717
844
  }
718
845
  ConsoleOutput.newline();
846
+ if (config.prettierConfigPath) {
847
+ ConsoleOutput.info(`Prettier config detected: ${config.prettierConfigPath}`);
848
+ ConsoleOutput.muted(" Output formatting will respect your Prettier settings");
849
+ } else {
850
+ ConsoleOutput.muted("No Prettier config found");
851
+ ConsoleOutput.muted(" Default formatting options added to config (tabWidth: 2)");
852
+ }
853
+ ConsoleOutput.newline();
719
854
  console.log("Next steps:");
720
855
  ConsoleOutput.muted("1. Edit .promptscript/project.prs to customize your AI instructions");
721
856
  ConsoleOutput.muted("2. Run: prs compile");
@@ -729,14 +864,15 @@ async function initCommand(options, services = createDefaultServices()) {
729
864
  process.exit(1);
730
865
  }
731
866
  }
732
- async function resolveConfig(options, projectInfo, aiToolsDetection, services) {
867
+ async function resolveConfig(options, projectInfo, aiToolsDetection, prettierConfigPath, services) {
733
868
  if (options.yes) {
734
869
  return {
735
870
  projectId: options.name ?? projectInfo.name,
736
871
  team: options.team,
737
872
  inherit: options.inherit,
738
873
  registry: options.registry ?? "./registry",
739
- targets: options.targets ?? getSuggestedTargets(aiToolsDetection)
874
+ targets: options.targets ?? getSuggestedTargets(aiToolsDetection),
875
+ prettierConfigPath
740
876
  };
741
877
  }
742
878
  if (!options.interactive && options.name && options.targets) {
@@ -745,12 +881,19 @@ async function resolveConfig(options, projectInfo, aiToolsDetection, services) {
745
881
  team: options.team,
746
882
  inherit: options.inherit,
747
883
  registry: options.registry,
748
- targets: options.targets
884
+ targets: options.targets,
885
+ prettierConfigPath
749
886
  };
750
887
  }
751
- return await runInteractivePrompts(options, projectInfo, aiToolsDetection, services);
888
+ return await runInteractivePrompts(
889
+ options,
890
+ projectInfo,
891
+ aiToolsDetection,
892
+ prettierConfigPath,
893
+ services
894
+ );
752
895
  }
753
- async function runInteractivePrompts(options, projectInfo, aiToolsDetection, services) {
896
+ async function runInteractivePrompts(options, projectInfo, aiToolsDetection, prettierConfigPath, services) {
754
897
  const { prompts: prompts2 } = services;
755
898
  ConsoleOutput.newline();
756
899
  console.log("\u{1F680} PromptScript Setup");
@@ -768,6 +911,9 @@ async function runInteractivePrompts(options, projectInfo, aiToolsDetection, ser
768
911
  for (const line of detectionLines) {
769
912
  ConsoleOutput.muted(line);
770
913
  }
914
+ if (prettierConfigPath) {
915
+ ConsoleOutput.muted(`Prettier: ${prettierConfigPath}`);
916
+ }
771
917
  ConsoleOutput.newline();
772
918
  const projectId = await prompts2.input({
773
919
  message: "Project name:",
@@ -829,7 +975,8 @@ async function runInteractivePrompts(options, projectInfo, aiToolsDetection, ser
829
975
  team,
830
976
  inherit,
831
977
  registry,
832
- targets
978
+ targets,
979
+ prettierConfigPath
833
980
  };
834
981
  }
835
982
  function formatTargetName(target) {
@@ -862,7 +1009,18 @@ function generateConfig(config) {
862
1009
  for (const target of config.targets) {
863
1010
  lines.push(` - ${target}`);
864
1011
  }
865
- lines.push("", "validation:", " rules:", " empty-block: warn", "");
1012
+ lines.push("", "validation:", " rules:", " empty-block: warn");
1013
+ lines.push("");
1014
+ if (config.prettierConfigPath) {
1015
+ lines.push("formatting:", " prettier: true # Auto-detected from project");
1016
+ } else {
1017
+ lines.push(
1018
+ "formatting:",
1019
+ " tabWidth: 2",
1020
+ " proseWrap: preserve # 'always' | 'never' | 'preserve'"
1021
+ );
1022
+ }
1023
+ lines.push("");
866
1024
  return lines.join("\n");
867
1025
  }
868
1026
  function generateProjectPs(config, projectInfo) {
@@ -912,15 +1070,15 @@ ${frameworksLine}
912
1070
  }
913
1071
 
914
1072
  // packages/cli/src/commands/compile.ts
915
- import { resolve as resolve2, dirname as dirname3 } from "path";
916
- import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
917
- import { existsSync as existsSync6 } from "fs";
1073
+ import { resolve as resolve4, dirname as dirname5 } from "path";
1074
+ import { writeFile as writeFile2, mkdir as mkdir2, readFile as readFile6 } from "fs/promises";
1075
+ import { existsSync as existsSync7 } from "fs";
918
1076
  import chokidar from "chokidar";
919
1077
 
920
1078
  // packages/cli/src/config/loader.ts
921
- import { readFile as readFile2 } from "fs/promises";
922
- import { existsSync as existsSync2 } from "fs";
923
- import { parse as parseYaml } from "yaml";
1079
+ import { readFile as readFile3 } from "fs/promises";
1080
+ import { existsSync as existsSync3 } from "fs";
1081
+ import { parse as parseYaml2 } from "yaml";
924
1082
  var CONFIG_FILES = [
925
1083
  "promptscript.yaml",
926
1084
  "promptscript.yml",
@@ -943,14 +1101,14 @@ function interpolateEnvVars(text) {
943
1101
  }
944
1102
  function findConfigFile(customPath) {
945
1103
  if (customPath) {
946
- return existsSync2(customPath) ? customPath : null;
1104
+ return existsSync3(customPath) ? customPath : null;
947
1105
  }
948
1106
  const envConfig = process.env["PROMPTSCRIPT_CONFIG"];
949
- if (envConfig && existsSync2(envConfig)) {
1107
+ if (envConfig && existsSync3(envConfig)) {
950
1108
  return envConfig;
951
1109
  }
952
1110
  for (const file of CONFIG_FILES) {
953
- if (existsSync2(file)) {
1111
+ if (existsSync3(file)) {
954
1112
  return file;
955
1113
  }
956
1114
  }
@@ -964,10 +1122,10 @@ async function loadConfig(customPath) {
964
1122
  }
965
1123
  throw new Error("No PromptScript configuration found. Run: prs init");
966
1124
  }
967
- let content = await readFile2(configFile, "utf-8");
1125
+ let content = await readFile3(configFile, "utf-8");
968
1126
  content = interpolateEnvVars(content);
969
1127
  try {
970
- return parseYaml(content);
1128
+ return parseYaml2(content);
971
1129
  } catch (error) {
972
1130
  const message = error instanceof Error ? error.message : "Unknown parse error";
973
1131
  throw new Error(`Failed to parse ${configFile}: ${message}`);
@@ -977,7 +1135,17 @@ async function loadConfig(customPath) {
977
1135
  // packages/formatters/src/convention-renderer.ts
978
1136
  var ConventionRenderer = class {
979
1137
  convention;
980
- constructor(convention = "xml") {
1138
+ prettierOptions;
1139
+ constructor(conventionOrOptions = "xml") {
1140
+ let convention;
1141
+ let prettier;
1142
+ if (typeof conventionOrOptions === "object" && ("convention" in conventionOrOptions || "prettier" in conventionOrOptions)) {
1143
+ const opts = conventionOrOptions;
1144
+ convention = opts.convention ?? "xml";
1145
+ prettier = opts.prettier;
1146
+ } else {
1147
+ convention = conventionOrOptions;
1148
+ }
981
1149
  if (typeof convention === "string") {
982
1150
  if (isBuiltInConvention(convention)) {
983
1151
  this.convention = BUILT_IN_CONVENTIONS[convention];
@@ -989,6 +1157,16 @@ var ConventionRenderer = class {
989
1157
  } else {
990
1158
  this.convention = convention;
991
1159
  }
1160
+ this.prettierOptions = {
1161
+ ...DEFAULT_PRETTIER_OPTIONS,
1162
+ ...prettier
1163
+ };
1164
+ }
1165
+ /**
1166
+ * Get the current Prettier options.
1167
+ */
1168
+ getPrettierOptions() {
1169
+ return this.prettierOptions;
992
1170
  }
993
1171
  /**
994
1172
  * Get the current convention.
@@ -1008,16 +1186,24 @@ var ConventionRenderer = class {
1008
1186
  const transformedName = this.transformName(name, renderer.nameTransform);
1009
1187
  const start = this.applyTemplate(renderer.start, { name: transformedName, level });
1010
1188
  const end = renderer.end ? this.applyTemplate(renderer.end, { name: transformedName, level }) : "";
1011
- const trimmedContent = content.trim();
1189
+ let trimmedContent = content.trim();
1012
1190
  if (!trimmedContent) {
1013
1191
  return end ? `${start}
1014
1192
  ${end}` : start;
1015
1193
  }
1194
+ if (this.convention.name === "markdown") {
1195
+ trimmedContent = this.escapeMarkdownSpecialChars(trimmedContent);
1196
+ }
1016
1197
  const indentedContent = this.indentContent(trimmedContent, renderer, level);
1017
1198
  if (end) {
1018
1199
  return `${start}
1019
1200
  ${indentedContent}
1020
1201
  ${end}`;
1202
+ }
1203
+ if (this.convention.name === "markdown") {
1204
+ return `${start}
1205
+
1206
+ ${indentedContent}`;
1021
1207
  }
1022
1208
  return `${start}
1023
1209
  ${indentedContent}`;
@@ -1105,13 +1291,22 @@ ${this.convention.rootWrapper.end}`;
1105
1291
  }
1106
1292
  return result;
1107
1293
  }
1294
+ /**
1295
+ * Escape markdown special characters for Prettier compatibility.
1296
+ * - Escapes __ to \_\_ (to avoid emphasis)
1297
+ * - Escapes /* to /\* (to avoid glob patterns being interpreted)
1298
+ */
1299
+ escapeMarkdownSpecialChars(content) {
1300
+ return content.replace(/__/g, "\\_\\_").replace(/\/\*/g, "/\\*");
1301
+ }
1108
1302
  indentContent(content, renderer, level) {
1109
1303
  const indent = renderer.indent ?? "";
1110
1304
  if (!indent) {
1111
1305
  return content;
1112
1306
  }
1113
1307
  if (this.convention.name === "xml") {
1114
- const baseIndent = indent.repeat(level);
1308
+ const indentStr = " ".repeat(this.prettierOptions.tabWidth);
1309
+ const baseIndent = indentStr.repeat(level);
1115
1310
  return content.split("\n").map((line) => line.trim() ? `${baseIndent}${line}` : line).join("\n");
1116
1311
  }
1117
1312
  return content;
@@ -1130,7 +1325,19 @@ var BaseFormatter = class {
1130
1325
  */
1131
1326
  createRenderer(options) {
1132
1327
  const convention = options?.convention ?? this.defaultConvention;
1133
- return new ConventionRenderer(convention);
1328
+ return new ConventionRenderer({
1329
+ convention,
1330
+ prettier: options?.prettier
1331
+ });
1332
+ }
1333
+ /**
1334
+ * Get resolved Prettier options, merging provided options with defaults.
1335
+ */
1336
+ getPrettierOptions(options) {
1337
+ return {
1338
+ ...DEFAULT_PRETTIER_OPTIONS,
1339
+ ...options?.prettier
1340
+ };
1134
1341
  }
1135
1342
  /**
1136
1343
  * Get the output path, respecting options override.
@@ -1183,6 +1390,14 @@ var BaseFormatter = class {
1183
1390
  return {};
1184
1391
  }
1185
1392
  }
1393
+ /**
1394
+ * Format standards list from array of values (pass-through).
1395
+ * Returns array of strings for rendering as bullet list.
1396
+ */
1397
+ formatStandardsList(items) {
1398
+ if (!Array.isArray(items)) return [];
1399
+ return items.map((item) => this.valueToString(item)).filter((s) => s.length > 0);
1400
+ }
1186
1401
  /**
1187
1402
  * Format an array as comma-separated string.
1188
1403
  */
@@ -1248,6 +1463,136 @@ var BaseFormatter = class {
1248
1463
  const endPos = endCodeBlock + 3;
1249
1464
  return text.substring(headerIndex, endPos);
1250
1465
  }
1466
+ /**
1467
+ * Normalize markdown content to match Prettier formatting.
1468
+ * - Strips common leading indentation from lines
1469
+ * - Trims trailing whitespace from lines
1470
+ * - Normalizes markdown table formatting
1471
+ * - Adds blank lines before lists when preceded by text
1472
+ * - Escapes markdown special characters in paths
1473
+ */
1474
+ normalizeMarkdownForPrettier(content) {
1475
+ const lines = content.split("\n");
1476
+ let minIndent = Infinity;
1477
+ let inCodeBlock = false;
1478
+ for (const line of lines) {
1479
+ const trimmed = line.trimEnd();
1480
+ if (trimmed.startsWith("```")) {
1481
+ inCodeBlock = !inCodeBlock;
1482
+ continue;
1483
+ }
1484
+ if (inCodeBlock) continue;
1485
+ if (trimmed.length === 0) continue;
1486
+ const match = line.match(/^(\s*)/);
1487
+ const leadingSpaces = match?.[1]?.length ?? 0;
1488
+ minIndent = Math.min(minIndent, leadingSpaces);
1489
+ }
1490
+ if (minIndent === Infinity) minIndent = 0;
1491
+ const result = [];
1492
+ let inTable = false;
1493
+ let tableLines = [];
1494
+ inCodeBlock = false;
1495
+ for (const line of lines) {
1496
+ const trimmedLine = line.trimEnd();
1497
+ if (trimmedLine.trimStart().startsWith("```")) {
1498
+ inCodeBlock = !inCodeBlock;
1499
+ }
1500
+ const unindentedLine = minIndent > 0 ? trimmedLine.slice(minIndent) : trimmedLine;
1501
+ if (inCodeBlock || unindentedLine.startsWith("```")) {
1502
+ result.push(unindentedLine);
1503
+ continue;
1504
+ }
1505
+ let processedLine = unindentedLine;
1506
+ processedLine = processedLine.replace(/__([^_]+)__/g, "\\_\\_$1\\_\\_");
1507
+ processedLine = processedLine.replace(/\/\*/g, "/\\*");
1508
+ if (processedLine.trimStart().startsWith("|") && processedLine.trimEnd().endsWith("|")) {
1509
+ inTable = true;
1510
+ tableLines.push(processedLine.trim());
1511
+ } else {
1512
+ if (inTable && tableLines.length > 0) {
1513
+ result.push(...this.formatMarkdownTable(tableLines));
1514
+ tableLines = [];
1515
+ inTable = false;
1516
+ }
1517
+ const prevLine = result.length > 0 ? result[result.length - 1] : "";
1518
+ const isListItem = processedLine.trimStart().startsWith("- ");
1519
+ if (isListItem && prevLine && !prevLine.trimStart().startsWith("- ")) {
1520
+ result.push("");
1521
+ }
1522
+ result.push(processedLine);
1523
+ }
1524
+ }
1525
+ if (tableLines.length > 0) {
1526
+ result.push(...this.formatMarkdownTable(tableLines));
1527
+ }
1528
+ return result.join("\n");
1529
+ }
1530
+ /**
1531
+ * Strip all leading indentation from markdown content.
1532
+ * Used for AGENTS.md where content from multiple sources has inconsistent indentation.
1533
+ * Preserves indentation inside code blocks.
1534
+ */
1535
+ stripAllIndent(content) {
1536
+ const lines = content.split("\n");
1537
+ const result = [];
1538
+ let inCodeBlock = false;
1539
+ for (const line of lines) {
1540
+ const trimmedEnd = line.trimEnd();
1541
+ if (trimmedEnd.trimStart().startsWith("```")) {
1542
+ if (!inCodeBlock) {
1543
+ const prevLine2 = result.length > 0 ? result[result.length - 1] : "";
1544
+ if (prevLine2 && prevLine2.trim()) {
1545
+ result.push("");
1546
+ }
1547
+ }
1548
+ inCodeBlock = !inCodeBlock;
1549
+ result.push(trimmedEnd.trimStart());
1550
+ continue;
1551
+ }
1552
+ if (inCodeBlock) {
1553
+ result.push(trimmedEnd);
1554
+ continue;
1555
+ }
1556
+ let stripped = trimmedEnd.trimStart();
1557
+ stripped = stripped.replace(/__/g, "\\_\\_");
1558
+ stripped = stripped.replace(/\/\*/g, "/\\*");
1559
+ const prevLine = result.length > 0 ? result[result.length - 1] : "";
1560
+ if (stripped.startsWith("- ") && prevLine && !prevLine.startsWith("- ")) {
1561
+ result.push("");
1562
+ }
1563
+ result.push(stripped);
1564
+ }
1565
+ return result.join("\n");
1566
+ }
1567
+ /**
1568
+ * Format a markdown table to match Prettier output.
1569
+ * Prettier removes trailing whitespace from cells.
1570
+ */
1571
+ formatMarkdownTable(tableLines) {
1572
+ if (tableLines.length === 0) return [];
1573
+ const rows = tableLines.map(
1574
+ (line) => line.split("|").slice(1, -1).map((cell) => cell.trim())
1575
+ );
1576
+ const colCount = rows[0]?.length ?? 0;
1577
+ const colWidths = new Array(colCount).fill(0);
1578
+ for (const row of rows) {
1579
+ for (let i = 0; i < row.length; i++) {
1580
+ const cell = row[i] ?? "";
1581
+ const width = cell.match(/^-+$/) ? 3 : cell.length;
1582
+ colWidths[i] = Math.max(colWidths[i] ?? 0, width);
1583
+ }
1584
+ }
1585
+ return rows.map((row) => {
1586
+ const cells = row.map((cell, colIndex) => {
1587
+ const width = colWidths[colIndex] ?? 0;
1588
+ if (cell.match(/^-+$/)) {
1589
+ return "-".repeat(width);
1590
+ }
1591
+ return cell.padEnd(width);
1592
+ });
1593
+ return "| " + cells.join(" | ") + " |";
1594
+ });
1595
+ }
1251
1596
  };
1252
1597
 
1253
1598
  // packages/formatters/src/registry.ts
@@ -1327,6 +1672,51 @@ var GITHUB_VERSIONS = {
1327
1672
  outputPath: ".github/copilot-instructions.md"
1328
1673
  }
1329
1674
  };
1675
+ var TOOL_NAME_MAPPING = {
1676
+ // Read tools
1677
+ Read: "read",
1678
+ NotebookRead: "read",
1679
+ // Edit tools
1680
+ Edit: "edit",
1681
+ MultiEdit: "edit",
1682
+ Write: "edit",
1683
+ NotebookEdit: "edit",
1684
+ // Search tools
1685
+ Grep: "search",
1686
+ Glob: "search",
1687
+ // Execute tools
1688
+ Bash: "execute",
1689
+ // Web tools
1690
+ WebFetch: "web",
1691
+ WebSearch: "web",
1692
+ // Agent tools
1693
+ Task: "agent",
1694
+ // Todo tools
1695
+ TodoWrite: "todo",
1696
+ TodoRead: "todo"
1697
+ };
1698
+ var MODEL_NAME_MAPPING = {
1699
+ // Claude models - default aliases map to latest versions (4.5)
1700
+ sonnet: "Claude Sonnet 4.5",
1701
+ opus: "Claude Opus 4.5",
1702
+ haiku: "Claude Haiku 4.5",
1703
+ // Explicit version mappings
1704
+ "sonnet-4": "Claude Sonnet 4",
1705
+ "sonnet-4.5": "Claude Sonnet 4.5",
1706
+ "opus-4": "Claude Opus 4",
1707
+ "opus-4.5": "Claude Opus 4.5",
1708
+ "haiku-4": "Claude Haiku 4",
1709
+ "haiku-4.5": "Claude Haiku 4.5",
1710
+ // OpenAI models (pass through if already in correct format)
1711
+ "gpt-4o": "GPT-4o",
1712
+ "gpt-4.1": "GPT-4.1",
1713
+ "gpt-5": "GPT-5",
1714
+ "gpt-5-mini": "GPT-5 mini",
1715
+ // Special values
1716
+ inherit: "",
1717
+ // Empty string means omit the model property
1718
+ auto: "Auto"
1719
+ };
1330
1720
  var GitHubFormatter = class extends BaseFormatter {
1331
1721
  name = "github";
1332
1722
  outputPath = ".github/copilot-instructions.md";
@@ -1368,7 +1758,7 @@ var GitHubFormatter = class extends BaseFormatter {
1368
1758
  this.addCommonSections(ast, renderer, sections);
1369
1759
  return {
1370
1760
  path: this.getOutputPath(options),
1371
- content: sections.join("\n\n")
1761
+ content: sections.join("\n\n") + "\n"
1372
1762
  };
1373
1763
  }
1374
1764
  // ============================================================
@@ -1392,7 +1782,7 @@ var GitHubFormatter = class extends BaseFormatter {
1392
1782
  this.addCommonSections(ast, renderer, sections);
1393
1783
  return {
1394
1784
  path: this.getOutputPath(options),
1395
- content: sections.join("\n\n"),
1785
+ content: sections.join("\n\n") + "\n",
1396
1786
  additionalFiles: additionalFiles.length > 0 ? additionalFiles : void 0
1397
1787
  };
1398
1788
  }
@@ -1429,7 +1819,7 @@ var GitHubFormatter = class extends BaseFormatter {
1429
1819
  this.addCommonSections(ast, renderer, sections);
1430
1820
  return {
1431
1821
  path: this.getOutputPath(options),
1432
- content: sections.join("\n\n"),
1822
+ content: sections.join("\n\n") + "\n",
1433
1823
  additionalFiles: additionalFiles.length > 0 ? additionalFiles : void 0
1434
1824
  };
1435
1825
  }
@@ -1600,10 +1990,9 @@ var GitHubFormatter = class extends BaseFormatter {
1600
1990
  if (config.mode === "agent") {
1601
1991
  lines.push("mode: agent");
1602
1992
  if (config.tools && config.tools.length > 0) {
1603
- lines.push("tools:");
1604
- for (const tool of config.tools) {
1605
- lines.push(` - ${tool}`);
1606
- }
1993
+ const mappedTools = config.tools.map((tool) => TOOL_NAME_MAPPING[tool] ?? tool.toLowerCase()).filter((t, i, arr) => arr.indexOf(t) === i);
1994
+ const toolsArray = mappedTools.map((t) => `'${t}'`).join(", ");
1995
+ lines.push(`tools: [${toolsArray}]`);
1607
1996
  }
1608
1997
  }
1609
1998
  lines.push("---");
@@ -1646,19 +2035,21 @@ var GitHubFormatter = class extends BaseFormatter {
1646
2035
  generateSkillFile(config) {
1647
2036
  const lines = [];
1648
2037
  lines.push("---");
1649
- lines.push(`name: "${config.name}"`);
1650
- lines.push(`description: "${config.description}"`);
2038
+ lines.push(`name: '${config.name}'`);
2039
+ const descQuote = config.description.includes("'") ? '"' : "'";
2040
+ lines.push(`description: ${descQuote}${config.description}${descQuote}`);
1651
2041
  if (config.disableModelInvocation) {
1652
2042
  lines.push("disable-model-invocation: true");
1653
2043
  }
1654
2044
  lines.push("---");
1655
2045
  lines.push("");
1656
2046
  if (config.content) {
1657
- lines.push(config.content);
2047
+ const normalizedContent = this.normalizeMarkdownForPrettier(config.content);
2048
+ lines.push(normalizedContent);
1658
2049
  }
1659
2050
  return {
1660
2051
  path: `.github/skills/${config.name}/SKILL.md`,
1661
- content: lines.join("\n")
2052
+ content: lines.join("\n") + "\n"
1662
2053
  };
1663
2054
  }
1664
2055
  // ============================================================
@@ -1679,7 +2070,7 @@ var GitHubFormatter = class extends BaseFormatter {
1679
2070
  if (identityText) {
1680
2071
  lines.push("## Identity");
1681
2072
  lines.push("");
1682
- lines.push(identityText);
2073
+ lines.push(this.stripAllIndent(identityText));
1683
2074
  lines.push("");
1684
2075
  }
1685
2076
  const context = this.findBlock(ast, "context");
@@ -1688,7 +2079,7 @@ var GitHubFormatter = class extends BaseFormatter {
1688
2079
  if (contextText) {
1689
2080
  lines.push("## Context");
1690
2081
  lines.push("");
1691
- lines.push(contextText);
2082
+ lines.push(this.stripAllIndent(contextText));
1692
2083
  lines.push("");
1693
2084
  }
1694
2085
  }
@@ -1701,12 +2092,11 @@ var GitHubFormatter = class extends BaseFormatter {
1701
2092
  for (const item of items) {
1702
2093
  lines.push(`- ${item}`);
1703
2094
  }
1704
- lines.push("");
1705
2095
  }
1706
2096
  }
1707
2097
  return {
1708
2098
  path: "AGENTS.md",
1709
- content: lines.join("\n")
2099
+ content: lines.join("\n") + "\n"
1710
2100
  };
1711
2101
  }
1712
2102
  /**
@@ -1757,12 +2147,26 @@ var GitHubFormatter = class extends BaseFormatter {
1757
2147
  return agents;
1758
2148
  }
1759
2149
  /**
1760
- * Parse tools array from a Value.
2150
+ * Parse tools array from a Value and map to GitHub Copilot canonical names.
2151
+ *
2152
+ * @see https://docs.github.com/en/copilot/reference/custom-agents-configuration
1761
2153
  */
1762
2154
  parseToolsArray(value) {
1763
2155
  if (!value || !Array.isArray(value)) return void 0;
1764
- const arr = value.map((v) => this.valueToString(v)).filter((s) => s.length > 0);
1765
- return arr.length > 0 ? arr : void 0;
2156
+ const mapped = value.map((v) => this.valueToString(v)).filter((s) => s.length > 0).map((tool) => TOOL_NAME_MAPPING[tool] ?? tool.toLowerCase());
2157
+ const unique = [...new Set(mapped)];
2158
+ return unique.length > 0 ? unique : void 0;
2159
+ }
2160
+ /**
2161
+ * Map a model name from PromptScript/Claude Code format to GitHub Copilot format.
2162
+ *
2163
+ * @returns The mapped model name, or undefined if the model should be omitted (e.g., "inherit")
2164
+ */
2165
+ mapModelName(model) {
2166
+ if (!model) return void 0;
2167
+ const mapped = MODEL_NAME_MAPPING[model.toLowerCase()];
2168
+ if (mapped === "") return void 0;
2169
+ return mapped ?? model;
1766
2170
  }
1767
2171
  /**
1768
2172
  * Generate a .github/agents/<name>.md file.
@@ -1775,13 +2179,12 @@ var GitHubFormatter = class extends BaseFormatter {
1775
2179
  lines.push(`name: ${config.name}`);
1776
2180
  lines.push(`description: ${config.description}`);
1777
2181
  if (config.tools && config.tools.length > 0) {
1778
- lines.push("tools:");
1779
- for (const tool of config.tools) {
1780
- lines.push(` - ${tool}`);
1781
- }
2182
+ const toolsArray = config.tools.map((t) => `'${t}'`).join(", ");
2183
+ lines.push(`tools: [${toolsArray}]`);
1782
2184
  }
1783
- if (config.model) {
1784
- lines.push(`model: ${config.model}`);
2185
+ const mappedModel = this.mapModelName(config.model);
2186
+ if (mappedModel) {
2187
+ lines.push(`model: ${mappedModel}`);
1785
2188
  }
1786
2189
  lines.push("---");
1787
2190
  lines.push("");
@@ -1790,7 +2193,7 @@ var GitHubFormatter = class extends BaseFormatter {
1790
2193
  }
1791
2194
  return {
1792
2195
  path: `.github/agents/${config.name}.md`,
1793
- content: lines.join("\n")
2196
+ content: lines.join("\n") + "\n"
1794
2197
  };
1795
2198
  }
1796
2199
  // ============================================================
@@ -1837,7 +2240,7 @@ var GitHubFormatter = class extends BaseFormatter {
1837
2240
  const identity2 = this.findBlock(ast, "identity");
1838
2241
  if (!identity2) return null;
1839
2242
  const content = this.extractText(identity2.content);
1840
- return renderer.renderSection("project", content);
2243
+ return renderer.renderSection("project", this.stripAllIndent(content));
1841
2244
  }
1842
2245
  techStack(ast, renderer) {
1843
2246
  const context = this.findBlock(ast, "context");
@@ -1875,50 +2278,30 @@ var GitHubFormatter = class extends BaseFormatter {
1875
2278
  const archMatch = this.extractSectionWithCodeBlock(text, "## Architecture");
1876
2279
  if (!archMatch) return null;
1877
2280
  const content = archMatch.replace("## Architecture", "").trim();
1878
- return renderer.renderSection("architecture", content);
2281
+ return renderer.renderSection("architecture", this.stripAllIndent(content));
1879
2282
  }
1880
2283
  codeStandards(ast, renderer) {
1881
2284
  const standards = this.findBlock(ast, "standards");
1882
2285
  if (!standards) return null;
1883
2286
  const props = this.getProps(standards.content);
1884
2287
  const subsections = [];
1885
- this.addStandardsSubsection(
1886
- subsections,
1887
- renderer,
1888
- props["typescript"],
1889
- "typescript",
1890
- this.formatStandardsObject.bind(this)
1891
- );
1892
- this.addStandardsSubsection(
1893
- subsections,
1894
- renderer,
1895
- props["naming"],
1896
- "naming",
1897
- this.formatNamingStandards.bind(this)
1898
- );
1899
- this.addStandardsSubsection(
1900
- subsections,
1901
- renderer,
1902
- props["errors"],
1903
- "error-handling",
1904
- this.formatErrorStandards.bind(this)
1905
- );
1906
- this.addStandardsSubsection(
1907
- subsections,
1908
- renderer,
1909
- props["testing"],
1910
- "testing",
1911
- this.formatTestingStandards.bind(this)
1912
- );
1913
- if (subsections.length === 0) return null;
1914
- return renderer.renderSection("code-standards", subsections.join("\n"));
1915
- }
1916
- addStandardsSubsection(subsections, renderer, value, name, formatter) {
1917
- if (!value || typeof value !== "object" || Array.isArray(value)) return;
1918
- const items = formatter(value);
1919
- if (items.length > 0) {
1920
- subsections.push(renderer.renderSection(name, renderer.renderList(items), 2));
2288
+ const sectionMap = {
2289
+ typescript: "typescript",
2290
+ naming: "naming",
2291
+ errors: "error-handling",
2292
+ testing: "testing"
2293
+ };
2294
+ for (const [key, sectionName] of Object.entries(sectionMap)) {
2295
+ const value = props[key];
2296
+ if (Array.isArray(value)) {
2297
+ const items = this.formatStandardsList(value);
2298
+ if (items.length > 0) {
2299
+ subsections.push(renderer.renderSection(sectionName, renderer.renderList(items), 2));
2300
+ }
2301
+ }
1921
2302
  }
2303
+ if (subsections.length === 0) return null;
2304
+ return renderer.renderSection("code-standards", subsections.join("\n\n"));
1922
2305
  }
1923
2306
  commands(ast, renderer) {
1924
2307
  const knowledge = this.findBlock(ast, "knowledge");
@@ -1927,7 +2310,7 @@ var GitHubFormatter = class extends BaseFormatter {
1927
2310
  const commandsMatch = this.extractSectionWithCodeBlock(text, "## Development Commands");
1928
2311
  if (!commandsMatch) return null;
1929
2312
  const content = commandsMatch.replace("## Development Commands", "").trim();
1930
- return renderer.renderSection("commands", content);
2313
+ return renderer.renderSection("commands", this.stripAllIndent(content));
1931
2314
  }
1932
2315
  gitCommits(ast, renderer) {
1933
2316
  const standards = this.findBlock(ast, "standards");
@@ -1978,7 +2361,7 @@ var GitHubFormatter = class extends BaseFormatter {
1978
2361
  subsections.push(renderer.renderSection("vite-vitest", `Vite root: ${value}`, 2));
1979
2362
  }
1980
2363
  if (subsections.length === 0) return null;
1981
- return renderer.renderSection("configuration-files", subsections.join("\n"));
2364
+ return renderer.renderSection("configuration-files", subsections.join("\n\n"));
1982
2365
  }
1983
2366
  documentation(ast, renderer) {
1984
2367
  const standards = this.findBlock(ast, "standards");
@@ -2014,8 +2397,11 @@ var GitHubFormatter = class extends BaseFormatter {
2014
2397
  if (!postMatch) return null;
2015
2398
  const intro = "After completing any code changes, run the following commands to ensure code quality:";
2016
2399
  const content = postMatch.replace("## Post-Work Verification", "").trim();
2017
- return renderer.renderSection("post-work-verification", `${intro}
2018
- ${content}`);
2400
+ return renderer.renderSection(
2401
+ "post-work-verification",
2402
+ `${intro}
2403
+ ${this.stripAllIndent(content)}`
2404
+ );
2019
2405
  }
2020
2406
  restrictions(ast, renderer) {
2021
2407
  const block = this.findBlock(ast, "restrictions");
@@ -2070,47 +2456,6 @@ ${content}`);
2070
2456
  formatTechItem(arr) {
2071
2457
  return arr.map(String).join(", ");
2072
2458
  }
2073
- formatStandardsObject(obj) {
2074
- const items = [];
2075
- if (obj["strictMode"]) items.push("Strict mode enabled, no `any` types");
2076
- if (obj["useUnknown"]) items.push(`Use \`unknown\` ${this.valueToString(obj["useUnknown"])}`);
2077
- if (obj["interfaces"])
2078
- items.push(`Prefer \`interface\` ${this.valueToString(obj["interfaces"])}`);
2079
- if (obj["types"]) items.push(`Use \`type\` ${this.valueToString(obj["types"])}`);
2080
- if (obj["exports"]) items.push(`${this.capitalize(this.valueToString(obj["exports"]))}`);
2081
- if (obj["returnTypes"])
2082
- items.push(`Explicit return types ${this.valueToString(obj["returnTypes"])}`);
2083
- return items;
2084
- }
2085
- formatNamingStandards(obj) {
2086
- const items = [];
2087
- if (obj["files"]) items.push(`Files: \`${this.valueToString(obj["files"])}\``);
2088
- if (obj["classes"]) items.push(`Classes/Interfaces: \`${this.valueToString(obj["classes"])}\``);
2089
- if (obj["functions"])
2090
- items.push(`Functions/Variables: \`${this.valueToString(obj["functions"])}\``);
2091
- if (obj["constants"]) items.push(`Constants: \`${this.valueToString(obj["constants"])}\``);
2092
- return items;
2093
- }
2094
- formatErrorStandards(obj) {
2095
- const items = [];
2096
- if (obj["customClasses"])
2097
- items.push(
2098
- `Use custom error classes extending \`${this.valueToString(obj["customClasses"])}\``
2099
- );
2100
- if (obj["locationInfo"]) items.push("Always include location information");
2101
- if (obj["messages"])
2102
- items.push(`Provide ${this.valueToString(obj["messages"])} error messages`);
2103
- return items;
2104
- }
2105
- formatTestingStandards(obj) {
2106
- const items = [];
2107
- if (obj["filePattern"]) items.push(`Test files: \`${this.valueToString(obj["filePattern"])}\``);
2108
- if (obj["pattern"]) items.push(`Follow ${this.valueToString(obj["pattern"])} pattern`);
2109
- if (obj["coverage"])
2110
- items.push(`Target >${this.valueToString(obj["coverage"])}% coverage for libraries`);
2111
- if (obj["fixtures"]) items.push(`Use fixtures ${this.valueToString(obj["fixtures"])}`);
2112
- return items;
2113
- }
2114
2459
  formatRestriction(text) {
2115
2460
  const formatted = text.replace(/^-\s*/, "").replace(/^"/, "").replace(/"$/, "").replace(/^Never\s+/i, "Don't ");
2116
2461
  return formatted;
@@ -2385,8 +2730,9 @@ var ClaudeFormatter = class extends BaseFormatter {
2385
2730
  generateSkillFile(config) {
2386
2731
  const lines = [];
2387
2732
  lines.push("---");
2388
- lines.push(`name: "${config.name}"`);
2389
- lines.push(`description: "${config.description}"`);
2733
+ lines.push(`name: '${config.name}'`);
2734
+ const descQuote = config.description.includes("'") ? '"' : "'";
2735
+ lines.push(`description: ${descQuote}${config.description}${descQuote}`);
2390
2736
  if (config.context) {
2391
2737
  lines.push(`context: ${config.context}`);
2392
2738
  }
@@ -2405,11 +2751,12 @@ var ClaudeFormatter = class extends BaseFormatter {
2405
2751
  lines.push("---");
2406
2752
  lines.push("");
2407
2753
  if (config.content) {
2408
- lines.push(config.content);
2754
+ const normalizedContent = this.normalizeMarkdownForPrettier(config.content);
2755
+ lines.push(normalizedContent);
2409
2756
  }
2410
2757
  return {
2411
2758
  path: `.claude/skills/${config.name}/SKILL.md`,
2412
- content: lines.join("\n")
2759
+ content: lines.join("\n") + "\n"
2413
2760
  };
2414
2761
  }
2415
2762
  // ============================================================
@@ -2518,7 +2865,7 @@ var ClaudeFormatter = class extends BaseFormatter {
2518
2865
  }
2519
2866
  return {
2520
2867
  path: `.claude/agents/${config.name}.md`,
2521
- content: lines.join("\n")
2868
+ content: lines.join("\n") + "\n"
2522
2869
  };
2523
2870
  }
2524
2871
  // ============================================================
@@ -3567,7 +3914,7 @@ var AntigravityFormatter = class extends BaseFormatter {
3567
3914
  if (diagrams) sections.push(diagrams);
3568
3915
  const restrictions = this.restrictions(ast, renderer);
3569
3916
  if (restrictions) sections.push(restrictions);
3570
- const content = sections.join("\n\n");
3917
+ const content = sections.join("\n\n") + "\n";
3571
3918
  this.validateContentLength(content);
3572
3919
  const mainOutput = {
3573
3920
  path: this.getOutputPath(options),
@@ -3740,7 +4087,7 @@ var AntigravityFormatter = class extends BaseFormatter {
3740
4087
  if (!content) return null;
3741
4088
  return `## Project Identity
3742
4089
 
3743
- ${content}`;
4090
+ ${this.stripAllIndent(content)}`;
3744
4091
  }
3745
4092
  /**
3746
4093
  * Extract tech stack from @context block (same as GitHub/Cursor).
@@ -3794,7 +4141,7 @@ ${items.join("\n")}`;
3794
4141
  const content = archMatch.replace("## Architecture", "").trim();
3795
4142
  return `## Architecture
3796
4143
 
3797
- ${content}`;
4144
+ ${this.stripAllIndent(content)}`;
3798
4145
  }
3799
4146
  const props = this.getProps(context.content);
3800
4147
  const arch = props["architecture"];
@@ -3803,7 +4150,7 @@ ${content}`;
3803
4150
  if (archText.trim()) {
3804
4151
  return `## Architecture
3805
4152
 
3806
- ${archText.trim()}`;
4153
+ ${this.stripAllIndent(archText.trim())}`;
3807
4154
  }
3808
4155
  }
3809
4156
  }
@@ -3813,53 +4160,35 @@ ${archText.trim()}`;
3813
4160
  if (content) {
3814
4161
  return `## Architecture
3815
4162
 
3816
- ${content}`;
4163
+ ${this.stripAllIndent(content)}`;
3817
4164
  }
3818
4165
  }
3819
4166
  return null;
3820
4167
  }
3821
4168
  /**
3822
- * Extract code standards from @standards block (same as GitHub/Cursor).
4169
+ * Extract code standards from @standards block.
4170
+ * Expects arrays of strings (pass-through format).
3823
4171
  */
3824
4172
  codeStandards(ast, _renderer) {
3825
4173
  const standards = this.findBlock(ast, "standards");
3826
4174
  if (!standards) return null;
3827
4175
  const props = this.getProps(standards.content);
3828
4176
  const subsections = [];
3829
- const typescript = props["typescript"];
3830
- if (typescript && typeof typescript === "object" && !Array.isArray(typescript)) {
3831
- const items = this.formatTypeScriptStandards(typescript);
3832
- if (items.length > 0) {
3833
- subsections.push(`### TypeScript
3834
-
3835
- ${items.map((i) => "- " + i).join("\n")}`);
3836
- }
3837
- }
3838
- const naming = props["naming"];
3839
- if (naming && typeof naming === "object" && !Array.isArray(naming)) {
3840
- const items = this.formatNamingStandards(naming);
3841
- if (items.length > 0) {
3842
- subsections.push(`### Naming Conventions
3843
-
3844
- ${items.map((i) => "- " + i).join("\n")}`);
3845
- }
3846
- }
3847
- const errors = props["errors"];
3848
- if (errors && typeof errors === "object" && !Array.isArray(errors)) {
3849
- const items = this.formatErrorStandards(errors);
3850
- if (items.length > 0) {
3851
- subsections.push(`### Error Handling
3852
-
3853
- ${items.map((i) => "- " + i).join("\n")}`);
3854
- }
3855
- }
3856
- const testing = props["testing"];
3857
- if (testing && typeof testing === "object" && !Array.isArray(testing)) {
3858
- const items = this.formatTestingStandards(testing);
3859
- if (items.length > 0) {
3860
- subsections.push(`### Testing
4177
+ const sectionMap = {
4178
+ typescript: "TypeScript",
4179
+ naming: "Naming Conventions",
4180
+ errors: "Error Handling",
4181
+ testing: "Testing"
4182
+ };
4183
+ for (const [key, title] of Object.entries(sectionMap)) {
4184
+ const value = props[key];
4185
+ if (Array.isArray(value)) {
4186
+ const items = this.formatStandardsList(value);
4187
+ if (items.length > 0) {
4188
+ subsections.push(`### ${title}
3861
4189
 
3862
4190
  ${items.map((i) => "- " + i).join("\n")}`);
4191
+ }
3863
4192
  }
3864
4193
  }
3865
4194
  if (subsections.length === 0) return null;
@@ -3922,7 +4251,7 @@ ${items.map((i) => "- " + i).join("\n")}`;
3922
4251
  }
3923
4252
  const vite = configObj["viteRoot"];
3924
4253
  if (vite) {
3925
- const value = this.valueToString(vite);
4254
+ const value = this.valueToString(vite).replace(/__/g, "\\_\\_").replace(/\/\*/g, "/\\*");
3926
4255
  subsections.push(`### Vite/Vitest
3927
4256
 
3928
4257
  - Vite root: ${value}`);
@@ -3944,7 +4273,11 @@ ${subsections.join("\n\n")}`;
3944
4273
  for (const [key, value] of Object.entries(props)) {
3945
4274
  if (this.isWorkflow(value)) continue;
3946
4275
  const valueStr = this.valueToString(value);
3947
- lines.push(`- **${key}**: ${valueStr}`);
4276
+ if (valueStr) {
4277
+ lines.push(`- **${key}**: ${valueStr}`);
4278
+ } else {
4279
+ lines.push(`- **${key}**:`);
4280
+ }
3948
4281
  }
3949
4282
  if (lines.length === 0) return null;
3950
4283
  return `## Commands
@@ -3963,7 +4296,7 @@ ${lines.join("\n")}`;
3963
4296
  const content = commandsMatch.replace("## Development Commands", "").trim();
3964
4297
  return `## Development Commands
3965
4298
 
3966
- ${content}`;
4299
+ ${this.stripAllIndent(content)}`;
3967
4300
  }
3968
4301
  /**
3969
4302
  * Extract post-work verification from @knowledge block (same as GitHub/Cursor).
@@ -3979,7 +4312,7 @@ ${content}`;
3979
4312
  return `## Post-Work Verification
3980
4313
 
3981
4314
  ${intro}
3982
- ${content}`;
4315
+ ${this.stripAllIndent(content)}`;
3983
4316
  }
3984
4317
  /**
3985
4318
  * Extract documentation standards from @standards.documentation (same as GitHub/Cursor).
@@ -4025,9 +4358,8 @@ ${items.map((i) => "- " + i).join("\n")}`;
4025
4358
  const items = [];
4026
4359
  const format2 = diagObj["format"];
4027
4360
  if (format2) {
4028
- items.push(
4029
- `Always use **${this.valueToString(format2)}** syntax for diagrams in documentation`
4030
- );
4361
+ const formatValue = this.valueToString(format2).replace(/__/g, "\\_\\_").replace(/\/\*/g, "/\\*");
4362
+ items.push(`Always use **${formatValue}** syntax for diagrams in documentation`);
4031
4363
  }
4032
4364
  const types = diagObj["types"];
4033
4365
  if (types && Array.isArray(types)) {
@@ -4072,55 +4404,6 @@ ${items.map((i) => "- " + i).join("\n")}`;
4072
4404
  formatRestriction(text) {
4073
4405
  return text.replace(/^-\s*/, "").replace(/^"/, "").replace(/"$/, "").replace(/^Never\s+/i, "Don't ");
4074
4406
  }
4075
- // Helper methods for formatting standards
4076
- formatTypeScriptStandards(obj) {
4077
- const items = [];
4078
- if (obj["strictMode"]) items.push("Strict mode enabled, no `any` types");
4079
- if (obj["noAny"]) items.push("Never use `any` type - use `unknown` with type guards");
4080
- if (obj["useUnknown"]) items.push(`Use \`unknown\` ${this.valueToString(obj["useUnknown"])}`);
4081
- if (obj["interfaces"])
4082
- items.push(`Prefer \`interface\` ${this.valueToString(obj["interfaces"])}`);
4083
- if (obj["types"]) items.push(`Use \`type\` ${this.valueToString(obj["types"])}`);
4084
- if (obj["exports"]) items.push(`${this.capitalize(this.valueToString(obj["exports"]))}`);
4085
- if (obj["returnTypes"])
4086
- items.push(`Explicit return types ${this.valueToString(obj["returnTypes"])}`);
4087
- return items;
4088
- }
4089
- formatNamingStandards(obj) {
4090
- const items = [];
4091
- if (obj["files"]) items.push(`Files: \`${this.valueToString(obj["files"])}\``);
4092
- if (obj["classes"]) items.push(`Classes/Interfaces: \`${this.valueToString(obj["classes"])}\``);
4093
- if (obj["interfaces"]) items.push(`Interfaces: \`${this.valueToString(obj["interfaces"])}\``);
4094
- if (obj["functions"])
4095
- items.push(`Functions/Variables: \`${this.valueToString(obj["functions"])}\``);
4096
- if (obj["variables"]) items.push(`Variables: \`${this.valueToString(obj["variables"])}\``);
4097
- if (obj["constants"]) items.push(`Constants: \`${this.valueToString(obj["constants"])}\``);
4098
- return items;
4099
- }
4100
- formatErrorStandards(obj) {
4101
- const items = [];
4102
- if (obj["customClasses"])
4103
- items.push(
4104
- `Use custom error classes extending \`${this.valueToString(obj["customClasses"])}\``
4105
- );
4106
- if (obj["locationInfo"]) items.push("Always include location information");
4107
- if (obj["messages"])
4108
- items.push(`Provide ${this.valueToString(obj["messages"])} error messages`);
4109
- return items;
4110
- }
4111
- formatTestingStandards(obj) {
4112
- const items = [];
4113
- if (obj["filePattern"]) items.push(`Test files: \`${this.valueToString(obj["filePattern"])}\``);
4114
- if (obj["pattern"]) items.push(`Follow ${this.valueToString(obj["pattern"])} pattern`);
4115
- if (obj["framework"]) items.push(`Framework: ${this.valueToString(obj["framework"])}`);
4116
- if (obj["coverage"])
4117
- items.push(`Target >${this.valueToString(obj["coverage"])}% coverage for libraries`);
4118
- if (obj["fixtures"]) items.push(`Use fixtures ${this.valueToString(obj["fixtures"])}`);
4119
- return items;
4120
- }
4121
- capitalize(str) {
4122
- return str.charAt(0).toUpperCase() + str.slice(1);
4123
- }
4124
4407
  };
4125
4408
 
4126
4409
  // packages/formatters/src/index.ts
@@ -14277,8 +14560,8 @@ function parse(source, options = {}) {
14277
14560
  }
14278
14561
 
14279
14562
  // packages/resolver/src/loader.ts
14280
- import { readFile as readFile3 } from "fs/promises";
14281
- import { resolve, dirname as dirname2, isAbsolute, join as join2 } from "path";
14563
+ import { readFile as readFile4 } from "fs/promises";
14564
+ import { resolve as resolve2, dirname as dirname3, isAbsolute } from "path";
14282
14565
  var FileLoader = class {
14283
14566
  registryPath;
14284
14567
  localPath;
@@ -14295,7 +14578,7 @@ var FileLoader = class {
14295
14578
  */
14296
14579
  async load(path) {
14297
14580
  try {
14298
- return await readFile3(path, "utf-8");
14581
+ return await readFile4(path, "utf-8");
14299
14582
  } catch (err) {
14300
14583
  if (err.code === "ENOENT") {
14301
14584
  throw new FileNotFoundError(path);
@@ -14324,11 +14607,11 @@ var FileLoader = class {
14324
14607
  const namespace = match[1];
14325
14608
  const segments = match[2];
14326
14609
  const fileName2 = segments.endsWith(".prs") ? segments : `${segments}.prs`;
14327
- return resolve(this.registryPath, `@${namespace}`, fileName2);
14610
+ return resolve2(this.registryPath, `@${namespace}`, fileName2);
14328
14611
  }
14329
14612
  }
14330
14613
  const fileName = path.endsWith(".prs") ? path : `${path}.prs`;
14331
- return resolve(this.localPath, fileName);
14614
+ return resolve2(this.localPath, fileName);
14332
14615
  }
14333
14616
  /**
14334
14617
  * Resolve a PathReference to an absolute path.
@@ -14339,9 +14622,9 @@ var FileLoader = class {
14339
14622
  */
14340
14623
  resolveRef(ref, fromFile) {
14341
14624
  if (ref.isRelative) {
14342
- const dir = dirname2(fromFile);
14343
- const fileName = join2(...ref.segments) + ".prs";
14344
- return resolve(dir, fileName);
14625
+ const dir = dirname3(fromFile);
14626
+ const rawPath = ref.raw.endsWith(".prs") ? ref.raw : `${ref.raw}.prs`;
14627
+ return resolve2(dir, rawPath);
14345
14628
  }
14346
14629
  return this.toAbsolutePath(ref.raw);
14347
14630
  }
@@ -14665,9 +14948,7 @@ function mergeProperties2(source, target) {
14665
14948
  sourceVal,
14666
14949
  targetVal
14667
14950
  );
14668
- } else if (typeof sourceVal === typeof targetVal) {
14669
14951
  } else {
14670
- result[key] = deepCloneValue2(targetVal);
14671
14952
  }
14672
14953
  }
14673
14954
  return result;
@@ -15001,6 +15282,115 @@ function uniqueConcat3(parent, child) {
15001
15282
  return result;
15002
15283
  }
15003
15284
 
15285
+ // packages/resolver/src/skills.ts
15286
+ import { readFile as readFile5, access } from "fs/promises";
15287
+ import { resolve as resolve3, dirname as dirname4 } from "path";
15288
+ function parseSkillMd(content) {
15289
+ const lines = content.split("\n");
15290
+ let inFrontmatter = false;
15291
+ let frontmatterStart = -1;
15292
+ let frontmatterEnd = -1;
15293
+ for (let i = 0; i < lines.length; i++) {
15294
+ const line = lines[i]?.trim() ?? "";
15295
+ if (line === "---") {
15296
+ if (!inFrontmatter) {
15297
+ inFrontmatter = true;
15298
+ frontmatterStart = i;
15299
+ } else {
15300
+ frontmatterEnd = i;
15301
+ break;
15302
+ }
15303
+ }
15304
+ }
15305
+ let name;
15306
+ let description;
15307
+ let bodyContent;
15308
+ if (frontmatterStart >= 0 && frontmatterEnd > frontmatterStart) {
15309
+ const frontmatterLines = lines.slice(frontmatterStart + 1, frontmatterEnd);
15310
+ for (const line of frontmatterLines) {
15311
+ const nameMatch = line.match(/^name:\s*(?:"([^"]+)"|'([^']+)'|(.+))\s*$/);
15312
+ if (nameMatch) {
15313
+ name = (nameMatch[1] ?? nameMatch[2] ?? nameMatch[3])?.trim();
15314
+ }
15315
+ const descMatch = line.match(/^description:\s*(?:"([^"]+)"|'([^']+)'|(.+))\s*$/);
15316
+ if (descMatch) {
15317
+ description = (descMatch[1] ?? descMatch[2] ?? descMatch[3])?.trim();
15318
+ }
15319
+ }
15320
+ bodyContent = lines.slice(frontmatterEnd + 1).join("\n").trim();
15321
+ } else {
15322
+ bodyContent = content.trim();
15323
+ }
15324
+ return { name, description, content: bodyContent };
15325
+ }
15326
+ async function fileExists(path) {
15327
+ try {
15328
+ await access(path);
15329
+ return true;
15330
+ } catch {
15331
+ return false;
15332
+ }
15333
+ }
15334
+ async function resolveNativeSkills(ast, registryPath, sourceFile) {
15335
+ const skillsBlock = ast.blocks.find((b) => b.name === "skills");
15336
+ if (!skillsBlock || skillsBlock.content.type !== "ObjectContent") {
15337
+ return ast;
15338
+ }
15339
+ const skillsContent = skillsBlock.content;
15340
+ const updatedProperties = { ...skillsContent.properties };
15341
+ let hasUpdates = false;
15342
+ const sourceDir = dirname4(sourceFile);
15343
+ const isSkillsDir = sourceDir.includes("@skills");
15344
+ for (const [skillName, skillValue] of Object.entries(skillsContent.properties)) {
15345
+ if (typeof skillValue !== "object" || skillValue === null || Array.isArray(skillValue)) {
15346
+ continue;
15347
+ }
15348
+ const skillObj = skillValue;
15349
+ let skillMdPath = null;
15350
+ if (isSkillsDir) {
15351
+ const basePath = sourceDir;
15352
+ skillMdPath = resolve3(basePath, skillName, "SKILL.md");
15353
+ } else {
15354
+ skillMdPath = resolve3(registryPath, "@skills", skillName, "SKILL.md");
15355
+ }
15356
+ if (skillMdPath && await fileExists(skillMdPath)) {
15357
+ try {
15358
+ const rawContent = await readFile5(skillMdPath, "utf-8");
15359
+ const parsed = parseSkillMd(rawContent);
15360
+ const updatedSkill = { ...skillObj };
15361
+ if (parsed.content) {
15362
+ updatedSkill["content"] = {
15363
+ type: "TextContent",
15364
+ value: parsed.content,
15365
+ loc: { file: skillMdPath, line: 1, column: 1, offset: 0 }
15366
+ };
15367
+ }
15368
+ if (parsed.description && (!skillObj["description"] || typeof skillObj["description"] === "string" && parsed.description.length > skillObj["description"].length)) {
15369
+ updatedSkill["description"] = parsed.description;
15370
+ }
15371
+ updatedProperties[skillName] = updatedSkill;
15372
+ hasUpdates = true;
15373
+ } catch {
15374
+ }
15375
+ }
15376
+ }
15377
+ if (!hasUpdates) {
15378
+ return ast;
15379
+ }
15380
+ const updatedSkillsBlock = {
15381
+ ...skillsBlock,
15382
+ content: {
15383
+ ...skillsContent,
15384
+ properties: updatedProperties
15385
+ }
15386
+ };
15387
+ const updatedBlocks = ast.blocks.map((b) => b.name === "skills" ? updatedSkillsBlock : b);
15388
+ return {
15389
+ ...ast,
15390
+ blocks: updatedBlocks
15391
+ };
15392
+ }
15393
+
15004
15394
  // packages/resolver/src/resolver.ts
15005
15395
  var Resolver = class {
15006
15396
  loader;
@@ -15053,6 +15443,7 @@ var Resolver = class {
15053
15443
  ast = await this.resolveInherit(ast, absPath, sources, errors);
15054
15444
  ast = await this.resolveImports(ast, absPath, sources, errors);
15055
15445
  ast = applyExtends(ast);
15446
+ ast = await resolveNativeSkills(ast, this.loader.getRegistryPath(), absPath);
15056
15447
  return {
15057
15448
  ast,
15058
15449
  sources: [...new Set(sources)],
@@ -15146,7 +15537,7 @@ var Resolver = class {
15146
15537
  };
15147
15538
 
15148
15539
  // packages/resolver/src/registry.ts
15149
- import { existsSync as existsSync3, promises as fs } from "fs";
15540
+ import { existsSync as existsSync4, promises as fs } from "fs";
15150
15541
  import { join as join3 } from "path";
15151
15542
  var FileSystemRegistry = class {
15152
15543
  rootPath;
@@ -15173,7 +15564,7 @@ var FileSystemRegistry = class {
15173
15564
  }
15174
15565
  async exists(path) {
15175
15566
  const fullPath = this.resolvePath(path);
15176
- return existsSync3(fullPath);
15567
+ return existsSync4(fullPath);
15177
15568
  }
15178
15569
  async list(path) {
15179
15570
  const fullPath = this.resolvePath(path);
@@ -15229,7 +15620,7 @@ var HttpRegistry = class {
15229
15620
  * Sleep for specified milliseconds.
15230
15621
  */
15231
15622
  sleep(ms) {
15232
- return new Promise((resolve7) => setTimeout(resolve7, ms));
15623
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
15233
15624
  }
15234
15625
  /**
15235
15626
  * Fetch with timeout.
@@ -15371,12 +15762,12 @@ function createHttpRegistry(options) {
15371
15762
  }
15372
15763
 
15373
15764
  // packages/resolver/src/git-registry.ts
15374
- import { existsSync as existsSync5, promises as fs3 } from "fs";
15765
+ import { existsSync as existsSync6, promises as fs3 } from "fs";
15375
15766
  import { join as join5 } from "path";
15376
15767
  import { simpleGit } from "simple-git";
15377
15768
 
15378
15769
  // packages/resolver/src/git-cache-manager.ts
15379
- import { existsSync as existsSync4, promises as fs2 } from "fs";
15770
+ import { existsSync as existsSync5, promises as fs2 } from "fs";
15380
15771
  import { join as join4 } from "path";
15381
15772
  import { homedir } from "os";
15382
15773
 
@@ -15526,7 +15917,7 @@ var GitCacheManager = class {
15526
15917
  */
15527
15918
  async get(url, ref) {
15528
15919
  const cachePath = this.getCachePath(url, ref);
15529
- if (!existsSync4(cachePath)) {
15920
+ if (!existsSync5(cachePath)) {
15530
15921
  return null;
15531
15922
  }
15532
15923
  const metadata = await this.readMetadata(cachePath);
@@ -15591,7 +15982,7 @@ var GitCacheManager = class {
15591
15982
  */
15592
15983
  async remove(url, ref) {
15593
15984
  const cachePath = this.getCachePath(url, ref);
15594
- if (existsSync4(cachePath)) {
15985
+ if (existsSync5(cachePath)) {
15595
15986
  await fs2.rm(cachePath, { recursive: true, force: true });
15596
15987
  }
15597
15988
  }
@@ -15601,7 +15992,7 @@ var GitCacheManager = class {
15601
15992
  * @returns Array of cache entries
15602
15993
  */
15603
15994
  async list() {
15604
- if (!existsSync4(this.cacheDir)) {
15995
+ if (!existsSync5(this.cacheDir)) {
15605
15996
  return [];
15606
15997
  }
15607
15998
  const entries = [];
@@ -15636,7 +16027,7 @@ var GitCacheManager = class {
15636
16027
  * Remove all cache entries.
15637
16028
  */
15638
16029
  async clear() {
15639
- if (existsSync4(this.cacheDir)) {
16030
+ if (existsSync5(this.cacheDir)) {
15640
16031
  await fs2.rm(this.cacheDir, { recursive: true, force: true });
15641
16032
  }
15642
16033
  }
@@ -15646,7 +16037,7 @@ var GitCacheManager = class {
15646
16037
  * @returns Total cache size in bytes
15647
16038
  */
15648
16039
  async getSize() {
15649
- if (!existsSync4(this.cacheDir)) {
16040
+ if (!existsSync5(this.cacheDir)) {
15650
16041
  return 0;
15651
16042
  }
15652
16043
  return this.calculateDirSize(this.cacheDir);
@@ -15673,7 +16064,7 @@ var GitCacheManager = class {
15673
16064
  */
15674
16065
  async readMetadata(cachePath) {
15675
16066
  const metadataPath = join4(cachePath, METADATA_FILE);
15676
- if (!existsSync4(metadataPath)) {
16067
+ if (!existsSync5(metadataPath)) {
15677
16068
  return null;
15678
16069
  }
15679
16070
  try {
@@ -15756,7 +16147,7 @@ var GitRegistry = class {
15756
16147
  const ref = version ?? this.defaultRef;
15757
16148
  const repoPath = await this.ensureCloned(ref);
15758
16149
  const filePath = this.resolveFilePath(repoPath, basePath);
15759
- if (!existsSync5(filePath)) {
16150
+ if (!existsSync6(filePath)) {
15760
16151
  throw new FileNotFoundError(path);
15761
16152
  }
15762
16153
  return fs3.readFile(filePath, "utf-8");
@@ -15773,7 +16164,7 @@ var GitRegistry = class {
15773
16164
  const ref = version ?? this.defaultRef;
15774
16165
  const repoPath = await this.ensureCloned(ref);
15775
16166
  const filePath = this.resolveFilePath(repoPath, basePath);
15776
- return existsSync5(filePath);
16167
+ return existsSync6(filePath);
15777
16168
  } catch {
15778
16169
  return false;
15779
16170
  }
@@ -15790,7 +16181,7 @@ var GitRegistry = class {
15790
16181
  const ref = version ?? this.defaultRef;
15791
16182
  const repoPath = await this.ensureCloned(ref);
15792
16183
  const dirPath = this.resolveDirectoryPath(repoPath, basePath);
15793
- if (!existsSync5(dirPath)) {
16184
+ if (!existsSync6(dirPath)) {
15794
16185
  return [];
15795
16186
  }
15796
16187
  const entries = await fs3.readdir(dirPath, { withFileTypes: true });
@@ -15852,7 +16243,7 @@ var GitRegistry = class {
15852
16243
  * Clone the repository to the specified path.
15853
16244
  */
15854
16245
  async clone(targetPath, ref) {
15855
- if (existsSync5(targetPath)) {
16246
+ if (existsSync6(targetPath)) {
15856
16247
  await fs3.rm(targetPath, { recursive: true, force: true });
15857
16248
  }
15858
16249
  await fs3.mkdir(targetPath, { recursive: true });
@@ -16644,12 +17035,12 @@ var Compiler = class _Compiler {
16644
17035
  */
16645
17036
  async watch(entryPath, options = {}) {
16646
17037
  const { default: chokidar2 } = await import("chokidar");
16647
- const { dirname: dirname6, resolve: resolve7 } = await import("path");
16648
- const baseDir = dirname6(resolve7(entryPath));
17038
+ const { dirname: dirname8, resolve: resolve9 } = await import("path");
17039
+ const baseDir = dirname8(resolve9(entryPath));
16649
17040
  const includePatterns = options.include ?? ["**/*.prs"];
16650
17041
  const excludePatterns = options.exclude ?? ["**/node_modules/**"];
16651
17042
  const debounceMs = options.debounce ?? 300;
16652
- const watchPatterns = includePatterns.map((p) => resolve7(baseDir, p));
17043
+ const watchPatterns = includePatterns.map((p) => resolve9(baseDir, p));
16653
17044
  let debounceTimer = null;
16654
17045
  let pendingChanges = [];
16655
17046
  const handleChange = async (changedFiles) => {
@@ -16704,14 +17095,13 @@ var Compiler = class _Compiler {
16704
17095
  */
16705
17096
  getFormatOptionsForTarget(_targetName, config) {
16706
17097
  const customConventions = this.options.customConventions;
16707
- if (!config?.convention && !config?.version && !config?.output) {
16708
- return {};
16709
- }
16710
- const conventionName = config?.convention;
17098
+ const prettierOptions = this.options.prettier;
16711
17099
  const options = {
16712
17100
  outputPath: config?.output,
16713
- version: config?.version
17101
+ version: config?.version,
17102
+ prettier: prettierOptions
16714
17103
  };
17104
+ const conventionName = config?.convention;
16715
17105
  if (conventionName && customConventions?.[conventionName]) {
16716
17106
  options.convention = customConventions[conventionName];
16717
17107
  } else if (conventionName) {
@@ -16788,7 +17178,86 @@ var Compiler = class _Compiler {
16788
17178
  }
16789
17179
  };
16790
17180
 
17181
+ // packages/cli/src/output/pager.ts
17182
+ import { spawn } from "child_process";
17183
+ function isTTY() {
17184
+ return process.stdout.isTTY === true;
17185
+ }
17186
+ function getPagerCommand() {
17187
+ return process.env["PAGER"] ?? "less";
17188
+ }
17189
+ var Pager = class {
17190
+ lines = [];
17191
+ enabled;
17192
+ constructor(enabled = true) {
17193
+ this.enabled = enabled && isTTY();
17194
+ }
17195
+ /**
17196
+ * Write a line to the pager buffer.
17197
+ */
17198
+ write(line) {
17199
+ this.lines.push(line);
17200
+ }
17201
+ /**
17202
+ * Write multiple lines to the pager buffer.
17203
+ */
17204
+ writeLines(lines) {
17205
+ this.lines.push(...lines);
17206
+ }
17207
+ /**
17208
+ * Flush all buffered content through the pager.
17209
+ * If pager is disabled or not a TTY, just prints to stdout.
17210
+ */
17211
+ async flush() {
17212
+ const content = this.lines.join("\n");
17213
+ if (!this.enabled || this.lines.length === 0) {
17214
+ if (content) {
17215
+ console.log(content);
17216
+ }
17217
+ return;
17218
+ }
17219
+ return new Promise((resolve9) => {
17220
+ const pagerCmd = getPagerCommand();
17221
+ const [cmd, ...args] = pagerCmd.split(" ");
17222
+ if (cmd === "less" && !args.includes("-R")) {
17223
+ args.push("-R");
17224
+ }
17225
+ if (!cmd) {
17226
+ console.log(content);
17227
+ resolve9();
17228
+ return;
17229
+ }
17230
+ const pager = spawn(cmd, args, {
17231
+ stdio: ["pipe", "inherit", "inherit"],
17232
+ env: {
17233
+ ...process.env,
17234
+ // Ensure less handles colors properly
17235
+ LESS: process.env["LESS"] ?? "-R"
17236
+ }
17237
+ });
17238
+ pager.on("error", (_err) => {
17239
+ console.log(content);
17240
+ resolve9();
17241
+ });
17242
+ pager.on("close", () => {
17243
+ resolve9();
17244
+ });
17245
+ if (pager.stdin) {
17246
+ pager.stdin.write(content);
17247
+ pager.stdin.end();
17248
+ } else {
17249
+ console.log(content);
17250
+ resolve9();
17251
+ }
17252
+ });
17253
+ }
17254
+ };
17255
+ function createPager(enabled = true) {
17256
+ return new Pager(enabled);
17257
+ }
17258
+
16791
17259
  // packages/cli/src/commands/compile.ts
17260
+ var PROMPTSCRIPT_MARKER = "> Auto-generated by PromptScript";
16792
17261
  function parseTargets(targets) {
16793
17262
  return targets.map((entry) => {
16794
17263
  if (typeof entry === "string") {
@@ -16802,6 +17271,28 @@ function parseTargets(targets) {
16802
17271
  return { name, config };
16803
17272
  }).filter((target) => target.config?.enabled !== false);
16804
17273
  }
17274
+ async function isPromptScriptGenerated(filePath) {
17275
+ try {
17276
+ const content = await readFile6(filePath, "utf-8");
17277
+ const lines = content.split("\n").slice(0, 10);
17278
+ return lines.some((line) => line.includes(PROMPTSCRIPT_MARKER));
17279
+ } catch {
17280
+ return false;
17281
+ }
17282
+ }
17283
+ async function promptForOverwrite(filePath, services) {
17284
+ const response = await services.prompts.select({
17285
+ message: `File exists and was not generated by PromptScript: ${filePath}
17286
+ Overwrite?`,
17287
+ choices: [
17288
+ { name: "No (skip this file)", value: "no" },
17289
+ { name: "Yes (overwrite this file)", value: "yes" },
17290
+ { name: "All (overwrite all remaining files)", value: "all" }
17291
+ ],
17292
+ default: "no"
17293
+ });
17294
+ return response;
17295
+ }
16805
17296
  function printErrors(errors) {
16806
17297
  for (const err of errors) {
16807
17298
  ConsoleOutput.error(err.message);
@@ -16810,17 +17301,84 @@ function printErrors(errors) {
16810
17301
  }
16811
17302
  }
16812
17303
  }
16813
- async function writeOutputs(outputs, options, _config) {
17304
+ async function writeOutputs(outputs, options, _config, services) {
17305
+ const result = { written: [], skipped: [] };
17306
+ let overwriteAll = false;
17307
+ const conflicts = [];
16814
17308
  for (const output of outputs.values()) {
16815
- const outputPath = options.output ? resolve2(options.output, output.path) : resolve2(output.path);
17309
+ const outputPath = options.output ? resolve4(options.output, output.path) : resolve4(output.path);
17310
+ const fileExists2 = existsSync7(outputPath);
16816
17311
  if (options.dryRun) {
16817
- ConsoleOutput.dryRun(`Would write: ${outputPath}`);
16818
- } else {
16819
- await mkdir2(dirname3(outputPath), { recursive: true });
17312
+ if (fileExists2) {
17313
+ const isGenerated2 = await isPromptScriptGenerated(outputPath);
17314
+ if (isGenerated2) {
17315
+ ConsoleOutput.dryRun(`Would overwrite: ${outputPath}`);
17316
+ } else {
17317
+ ConsoleOutput.warning(`Would conflict: ${outputPath} (not generated by PromptScript)`);
17318
+ }
17319
+ } else {
17320
+ ConsoleOutput.dryRun(`Would write: ${outputPath}`);
17321
+ }
17322
+ result.written.push(outputPath);
17323
+ continue;
17324
+ }
17325
+ if (!fileExists2) {
17326
+ await mkdir2(dirname5(outputPath), { recursive: true });
17327
+ await writeFile2(outputPath, output.content, "utf-8");
17328
+ ConsoleOutput.success(outputPath);
17329
+ result.written.push(outputPath);
17330
+ continue;
17331
+ }
17332
+ const isGenerated = await isPromptScriptGenerated(outputPath);
17333
+ if (isGenerated) {
17334
+ await mkdir2(dirname5(outputPath), { recursive: true });
16820
17335
  await writeFile2(outputPath, output.content, "utf-8");
16821
17336
  ConsoleOutput.success(outputPath);
17337
+ result.written.push(outputPath);
17338
+ continue;
17339
+ }
17340
+ if (options.force || overwriteAll) {
17341
+ await mkdir2(dirname5(outputPath), { recursive: true });
17342
+ await writeFile2(outputPath, output.content, "utf-8");
17343
+ ConsoleOutput.success(outputPath);
17344
+ result.written.push(outputPath);
17345
+ continue;
17346
+ }
17347
+ if (!isTTY()) {
17348
+ conflicts.push(outputPath);
17349
+ continue;
17350
+ }
17351
+ const response = await promptForOverwrite(outputPath, services);
17352
+ switch (response) {
17353
+ case "yes":
17354
+ await mkdir2(dirname5(outputPath), { recursive: true });
17355
+ await writeFile2(outputPath, output.content, "utf-8");
17356
+ ConsoleOutput.success(outputPath);
17357
+ result.written.push(outputPath);
17358
+ break;
17359
+ case "no":
17360
+ ConsoleOutput.skipped(outputPath);
17361
+ result.skipped.push(outputPath);
17362
+ break;
17363
+ case "all":
17364
+ overwriteAll = true;
17365
+ await mkdir2(dirname5(outputPath), { recursive: true });
17366
+ await writeFile2(outputPath, output.content, "utf-8");
17367
+ ConsoleOutput.success(outputPath);
17368
+ result.written.push(outputPath);
17369
+ break;
16822
17370
  }
16823
17371
  }
17372
+ if (conflicts.length > 0) {
17373
+ const fileList = conflicts.map((f) => ` - ${f}`).join("\n");
17374
+ throw new Error(
17375
+ `Cannot overwrite files not generated by PromptScript in non-interactive mode:
17376
+ ${fileList}
17377
+
17378
+ Use --force to overwrite these files.`
17379
+ );
17380
+ }
17381
+ return result;
16824
17382
  }
16825
17383
  function printStats(stats) {
16826
17384
  ConsoleOutput.newline();
@@ -16837,7 +17395,7 @@ function printWarnings(warnings) {
16837
17395
  }
16838
17396
  }
16839
17397
  }
16840
- async function compileCommand(options) {
17398
+ async function compileCommand(options, services = createDefaultServices()) {
16841
17399
  const spinner = createSpinner("Loading configuration...").start();
16842
17400
  try {
16843
17401
  const config = await loadConfig(options.config);
@@ -16845,6 +17403,7 @@ async function compileCommand(options) {
16845
17403
  const selectedTarget = options.target ?? options.format;
16846
17404
  const targets = selectedTarget ? [{ name: selectedTarget }] : parseTargets(config.targets);
16847
17405
  const registryPath = options.registry ?? config.registry?.path ?? "./registry";
17406
+ const prettierOptions = await resolvePrettierOptions(config, process.cwd());
16848
17407
  const compiler = new Compiler({
16849
17408
  resolver: {
16850
17409
  registryPath,
@@ -16852,10 +17411,11 @@ async function compileCommand(options) {
16852
17411
  },
16853
17412
  validator: config.validation,
16854
17413
  formatters: targets,
16855
- customConventions: config.customConventions
17414
+ customConventions: config.customConventions,
17415
+ prettier: prettierOptions
16856
17416
  });
16857
- const entryPath = resolve2("./.promptscript/project.prs");
16858
- if (!existsSync6(entryPath)) {
17417
+ const entryPath = resolve4("./.promptscript/project.prs");
17418
+ if (!existsSync7(entryPath)) {
16859
17419
  spinner.fail("Entry file not found");
16860
17420
  ConsoleOutput.error(`File not found: ${entryPath}`);
16861
17421
  ConsoleOutput.muted("Run: prs init");
@@ -16870,15 +17430,27 @@ async function compileCommand(options) {
16870
17430
  }
16871
17431
  spinner.succeed("Compilation successful");
16872
17432
  ConsoleOutput.newline();
16873
- await writeOutputs(result.outputs, options, config);
17433
+ const writeResult = await writeOutputs(result.outputs, options, config, services);
17434
+ if (writeResult.skipped.length > 0) {
17435
+ ConsoleOutput.muted(`Skipped ${writeResult.skipped.length} file(s)`);
17436
+ }
16874
17437
  printStats(result.stats);
16875
17438
  printWarnings(result.warnings);
16876
17439
  if (options.watch) {
16877
17440
  ConsoleOutput.newline();
16878
17441
  ConsoleOutput.info("Watching for changes... (Ctrl+C to stop)");
16879
- watchForChanges("./.promptscript", () => compileCommand({ ...options, watch: false }));
17442
+ watchForChanges(
17443
+ "./.promptscript",
17444
+ () => compileCommand({ ...options, watch: false }, services)
17445
+ );
16880
17446
  }
16881
17447
  } catch (error) {
17448
+ if (error.name === "ExitPromptError") {
17449
+ spinner.stop();
17450
+ ConsoleOutput.newline();
17451
+ ConsoleOutput.muted("Compilation cancelled");
17452
+ return;
17453
+ }
16882
17454
  spinner.fail("Error");
16883
17455
  ConsoleOutput.error(error.message);
16884
17456
  process.exit(1);
@@ -16919,8 +17491,8 @@ function watchForChanges(dir, callback) {
16919
17491
  }
16920
17492
 
16921
17493
  // packages/cli/src/commands/validate.ts
16922
- import { resolve as resolve3 } from "path";
16923
- import { existsSync as existsSync7 } from "fs";
17494
+ import { resolve as resolve5 } from "path";
17495
+ import { existsSync as existsSync8 } from "fs";
16924
17496
  function printValidationErrors(errors) {
16925
17497
  if (errors.length === 0) return;
16926
17498
  console.log(`Errors (${errors.length}):`);
@@ -17010,8 +17582,8 @@ async function validateCommand(options) {
17010
17582
  formatters: []
17011
17583
  // No formatters needed for validation only
17012
17584
  });
17013
- const entryPath = resolve3("./.promptscript/project.prs");
17014
- if (!existsSync7(entryPath)) {
17585
+ const entryPath = resolve5("./.promptscript/project.prs");
17586
+ if (!existsSync8(entryPath)) {
17015
17587
  handleEntryNotFound(entryPath, isJsonFormat, spinner);
17016
17588
  }
17017
17589
  const result = await compiler.compile(entryPath);
@@ -17051,8 +17623,8 @@ function outputJsonResult(result) {
17051
17623
 
17052
17624
  // packages/cli/src/commands/pull.ts
17053
17625
  import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
17054
- import { existsSync as existsSync8 } from "fs";
17055
- import { resolve as resolve4, dirname as dirname4 } from "path";
17626
+ import { existsSync as existsSync9 } from "fs";
17627
+ import { resolve as resolve6, dirname as dirname6 } from "path";
17056
17628
  async function pullCommand(options) {
17057
17629
  const spinner = createSpinner("Loading configuration...").start();
17058
17630
  try {
@@ -17075,8 +17647,8 @@ async function pullCommand(options) {
17075
17647
  }
17076
17648
  spinner.text = `Fetching ${inheritPath}...`;
17077
17649
  const content = await registry.fetch(inheritPath);
17078
- const destPath = resolve4("./.promptscript/.inherited", inheritPath);
17079
- if (existsSync8(destPath) && !options.force && !options.dryRun) {
17650
+ const destPath = resolve6("./.promptscript/.inherited", inheritPath);
17651
+ if (existsSync9(destPath) && !options.force && !options.dryRun) {
17080
17652
  spinner.warn("File already exists (use --force to overwrite)");
17081
17653
  ConsoleOutput.muted(destPath);
17082
17654
  return;
@@ -17086,12 +17658,12 @@ async function pullCommand(options) {
17086
17658
  ConsoleOutput.newline();
17087
17659
  ConsoleOutput.dryRun(`Would fetch: ${config.inherit}`);
17088
17660
  ConsoleOutput.dryRun(` to: ${destPath}`);
17089
- if (existsSync8(destPath)) {
17661
+ if (existsSync9(destPath)) {
17090
17662
  ConsoleOutput.dryRun("(would overwrite existing file)");
17091
17663
  }
17092
17664
  return;
17093
17665
  }
17094
- await mkdir3(dirname4(destPath), { recursive: true });
17666
+ await mkdir3(dirname6(destPath), { recursive: true });
17095
17667
  await writeFile3(destPath, content, "utf-8");
17096
17668
  spinner.succeed("Pulled from registry");
17097
17669
  ConsoleOutput.success(destPath);
@@ -17134,10 +17706,10 @@ async function createRegistry(config, options, spinner) {
17134
17706
  }
17135
17707
  const registryPath = config.registry?.path ?? "./registry";
17136
17708
  spinner.text = `Using local registry: ${registryPath}...`;
17137
- if (!existsSync8(registryPath)) {
17709
+ if (!existsSync9(registryPath)) {
17138
17710
  throw new Error(`Local registry path not found: ${registryPath}`);
17139
17711
  }
17140
- return createFileSystemRegistry(resolve4(registryPath));
17712
+ return createFileSystemRegistry(resolve6(registryPath));
17141
17713
  }
17142
17714
  function parseInheritPath(inheritPath) {
17143
17715
  const versionIndex = inheritPath.lastIndexOf("@");
@@ -17184,89 +17756,9 @@ function handlePullError(error, spinner) {
17184
17756
  }
17185
17757
 
17186
17758
  // packages/cli/src/commands/diff.ts
17187
- import { resolve as resolve5 } from "path";
17188
- import { readFile as readFile4 } from "fs/promises";
17189
- import { existsSync as existsSync9 } from "fs";
17190
-
17191
- // packages/cli/src/output/pager.ts
17192
- import { spawn } from "child_process";
17193
- function isTTY() {
17194
- return process.stdout.isTTY === true;
17195
- }
17196
- function getPagerCommand() {
17197
- return process.env["PAGER"] ?? "less";
17198
- }
17199
- var Pager = class {
17200
- lines = [];
17201
- enabled;
17202
- constructor(enabled = true) {
17203
- this.enabled = enabled && isTTY();
17204
- }
17205
- /**
17206
- * Write a line to the pager buffer.
17207
- */
17208
- write(line) {
17209
- this.lines.push(line);
17210
- }
17211
- /**
17212
- * Write multiple lines to the pager buffer.
17213
- */
17214
- writeLines(lines) {
17215
- this.lines.push(...lines);
17216
- }
17217
- /**
17218
- * Flush all buffered content through the pager.
17219
- * If pager is disabled or not a TTY, just prints to stdout.
17220
- */
17221
- async flush() {
17222
- const content = this.lines.join("\n");
17223
- if (!this.enabled || this.lines.length === 0) {
17224
- if (content) {
17225
- console.log(content);
17226
- }
17227
- return;
17228
- }
17229
- return new Promise((resolve7) => {
17230
- const pagerCmd = getPagerCommand();
17231
- const [cmd, ...args] = pagerCmd.split(" ");
17232
- if (cmd === "less" && !args.includes("-R")) {
17233
- args.push("-R");
17234
- }
17235
- if (!cmd) {
17236
- console.log(content);
17237
- resolve7();
17238
- return;
17239
- }
17240
- const pager = spawn(cmd, args, {
17241
- stdio: ["pipe", "inherit", "inherit"],
17242
- env: {
17243
- ...process.env,
17244
- // Ensure less handles colors properly
17245
- LESS: process.env["LESS"] ?? "-R"
17246
- }
17247
- });
17248
- pager.on("error", (_err) => {
17249
- console.log(content);
17250
- resolve7();
17251
- });
17252
- pager.on("close", () => {
17253
- resolve7();
17254
- });
17255
- if (pager.stdin) {
17256
- pager.stdin.write(content);
17257
- pager.stdin.end();
17258
- } else {
17259
- console.log(content);
17260
- resolve7();
17261
- }
17262
- });
17263
- }
17264
- };
17265
- function createPager(enabled = true) {
17266
- return new Pager(enabled);
17267
- }
17268
-
17269
- // packages/cli/src/commands/diff.ts
17759
+ import { resolve as resolve7 } from "path";
17760
+ import { readFile as readFile7 } from "fs/promises";
17761
+ import { existsSync as existsSync10 } from "fs";
17270
17762
  import chalk2 from "chalk";
17271
17763
  function configureColors(options) {
17272
17764
  if (options.color === false) {
@@ -17300,16 +17792,16 @@ function printNewFilePreview(content, showFull, pager) {
17300
17792
  }
17301
17793
  }
17302
17794
  async function compareOutput(_name, output, _config, showFull, pager) {
17303
- const outputPath = resolve5(output.path);
17795
+ const outputPath = resolve7(output.path);
17304
17796
  const newContent = output.content;
17305
- if (!existsSync9(outputPath)) {
17797
+ if (!existsSync10(outputPath)) {
17306
17798
  pager.write(chalk2.green(`+ ${outputPath} (new file)`));
17307
17799
  pager.write("");
17308
17800
  printNewFilePreview(newContent, showFull, pager);
17309
17801
  pager.write("");
17310
17802
  return true;
17311
17803
  }
17312
- const existingContent = await readFile4(outputPath, "utf-8");
17804
+ const existingContent = await readFile7(outputPath, "utf-8");
17313
17805
  if (existingContent === newContent) {
17314
17806
  pager.write(chalk2.gray(` ${outputPath} (no changes)`));
17315
17807
  return false;
@@ -17338,8 +17830,8 @@ async function diffCommand(options) {
17338
17830
  formatters: targets,
17339
17831
  customConventions: config.customConventions
17340
17832
  });
17341
- const entryPath = resolve5("./.promptscript/project.prs");
17342
- if (!existsSync9(entryPath)) {
17833
+ const entryPath = resolve7("./.promptscript/project.prs");
17834
+ if (!existsSync10(entryPath)) {
17343
17835
  spinner.fail("Entry file not found");
17344
17836
  ConsoleOutput.error(`File not found: ${entryPath}`);
17345
17837
  ConsoleOutput.muted("Run: prs init");
@@ -17405,8 +17897,8 @@ function printSimpleDiff(existingLines, newLines, showFull, pager) {
17405
17897
  }
17406
17898
 
17407
17899
  // packages/cli/src/commands/check.ts
17408
- import { existsSync as existsSync10 } from "fs";
17409
- import { resolve as resolve6 } from "path";
17900
+ import { existsSync as existsSync11 } from "fs";
17901
+ import { resolve as resolve8 } from "path";
17410
17902
  async function checkCommand(_options) {
17411
17903
  const spinner = createSpinner("Checking project health...").start();
17412
17904
  const results = [];
@@ -17468,7 +17960,7 @@ async function checkCommand(_options) {
17468
17960
  });
17469
17961
  }
17470
17962
  const entryPath = config.input?.entry ?? ".promptscript/project.prs";
17471
- if (existsSync10(entryPath)) {
17963
+ if (existsSync11(entryPath)) {
17472
17964
  results.push({
17473
17965
  name: "Entry file",
17474
17966
  status: "ok",
@@ -17483,8 +17975,8 @@ async function checkCommand(_options) {
17483
17975
  hasErrors = true;
17484
17976
  }
17485
17977
  if (config.registry?.path) {
17486
- const registryPath = resolve6(config.registry.path);
17487
- if (existsSync10(registryPath)) {
17978
+ const registryPath = resolve8(config.registry.path);
17979
+ if (existsSync11(registryPath)) {
17488
17980
  results.push({
17489
17981
  name: "Registry path",
17490
17982
  status: "ok",
@@ -17567,7 +18059,7 @@ function printResults(results) {
17567
18059
 
17568
18060
  // packages/cli/src/cli.ts
17569
18061
  var __filename2 = fileURLToPath2(import.meta.url);
17570
- var __dirname2 = dirname5(__filename2);
18062
+ var __dirname2 = dirname7(__filename2);
17571
18063
  var program = new Command();
17572
18064
  program.name("prs").description("PromptScript CLI - Standardize AI instructions").version(getPackageVersion(__dirname2, "./package.json")).option("--verbose", "Enable verbose output").option("--quiet", "Suppress non-error output").hook("preAction", (thisCommand) => {
17573
18065
  const opts = thisCommand.opts();
@@ -17581,7 +18073,7 @@ program.name("prs").description("PromptScript CLI - Standardize AI instructions"
17581
18073
  }
17582
18074
  });
17583
18075
  program.command("init").description("Initialize PromptScript in current directory").option("-n, --name <name>", "Project name (auto-detected from package.json, etc.)").option("-t, --team <team>", "Team namespace").option("--inherit <path>", "Inheritance path (e.g., @company/team)").option("--registry <path>", "Registry path").option("--targets <targets...>", "Target AI tools (github, claude, cursor)").option("-i, --interactive", "Force interactive mode").option("-y, --yes", "Skip prompts, use defaults").option("-f, --force", "Force reinitialize even if already initialized").action((opts) => initCommand(opts));
17584
- program.command("compile").description("Compile PromptScript to target formats").option("-t, --target <target>", "Specific target (github, claude, cursor)").option("-f, --format <format>", "Output format (alias for --target)").option("-a, --all", "All configured targets", true).option("-w, --watch", "Watch mode").option("-o, --output <dir>", "Output directory").option("--dry-run", "Preview changes").option("--registry <path>", "Registry path (overrides config)").option("-c, --config <path>", "Path to custom config file").action(compileCommand);
18076
+ program.command("compile").description("Compile PromptScript to target formats").option("-t, --target <target>", "Specific target (github, claude, cursor)").option("-f, --format <format>", "Output format (alias for --target)").option("-a, --all", "All configured targets", true).option("-w, --watch", "Watch mode").option("-o, --output <dir>", "Output directory").option("--dry-run", "Preview changes").option("--registry <path>", "Registry path (overrides config)").option("-c, --config <path>", "Path to custom config file").option("--force", "Force overwrite existing files without prompts").action(compileCommand);
17585
18077
  program.command("validate").description("Validate PromptScript files").option("--strict", "Treat warnings as errors").option("--format <format>", "Output format (text, json)", "text").action(validateCommand);
17586
18078
  program.command("pull").description("Pull updates from registry").option("-f, --force", "Force overwrite").option("--dry-run", "Preview changes without pulling").option("-b, --branch <name>", "Git branch to pull from").option("--tag <name>", "Git tag to pull from").option("--commit <hash>", "Git commit to pull from").option("--refresh", "Force re-fetch from remote (ignore cache)").action(pullCommand);
17587
18079
  program.command("diff").description("Show diff for compiled output").option("-t, --target <target>", "Specific target").option("-a, --all", "Show diff for all targets at once").option("--full", "Show full diff without truncation").option("--no-pager", "Disable pager output").option("--color", "Force colored output").option("--no-color", "Disable colored output").action(diffCommand);