@promptscript/cli 1.0.0-alpha.6 → 1.0.0-alpha.7

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
- import { fileURLToPath as fileURLToPath2 } from "url";
4
- import { dirname as dirname7 } from "path";
3
+ import { fileURLToPath as fileURLToPath3 } from "url";
4
+ import { dirname as dirname9 } from "path";
5
5
 
6
6
  // packages/core/src/types/constants.ts
7
7
  var BLOCK_TYPES = [
@@ -306,7 +306,7 @@ var noopLogger = {
306
306
 
307
307
  // packages/cli/src/commands/init.ts
308
308
  import { fileURLToPath } from "url";
309
- import { dirname as dirname2 } from "path";
309
+ import { dirname as dirname3 } from "path";
310
310
 
311
311
  // packages/cli/src/services.ts
312
312
  import { writeFile, mkdir, readFile, readdir } from "fs/promises";
@@ -1366,9 +1366,410 @@ Before completing migration:
1366
1366
  - No duplicate content across blocks
1367
1367
  `;
1368
1368
 
1369
+ // packages/cli/src/utils/manifest-loader.ts
1370
+ import { join as join3, dirname as dirname2 } from "path";
1371
+ import { parse as parseYaml2 } from "yaml";
1372
+ var OFFICIAL_REGISTRY = {
1373
+ name: "PromptScript Official Registry",
1374
+ url: "https://github.com/mrwogu/promptscript-registry.git",
1375
+ branch: "main",
1376
+ manifestUrl: "https://raw.githubusercontent.com/mrwogu/promptscript-registry/main/registry-manifest.yaml"
1377
+ };
1378
+ var ManifestLoadError = class extends Error {
1379
+ originalCause;
1380
+ constructor(message, cause) {
1381
+ super(message);
1382
+ this.name = "ManifestLoadError";
1383
+ this.originalCause = cause;
1384
+ }
1385
+ };
1386
+ var manifestCache = /* @__PURE__ */ new Map();
1387
+ var MANIFEST_FILENAME = "registry-manifest.yaml";
1388
+ async function loadManifest(options = {}, services = createDefaultServices()) {
1389
+ const { registryPath = findDefaultRegistryPath(services), useCache = true } = options;
1390
+ const manifestPath = resolveManifestPath(registryPath, services);
1391
+ if (useCache && manifestCache.has(manifestPath)) {
1392
+ return {
1393
+ manifest: manifestCache.get(manifestPath),
1394
+ path: manifestPath,
1395
+ cached: true
1396
+ };
1397
+ }
1398
+ const manifest = await loadManifestFromPath(manifestPath, services);
1399
+ validateManifest(manifest);
1400
+ if (useCache) {
1401
+ manifestCache.set(manifestPath, manifest);
1402
+ }
1403
+ return {
1404
+ manifest,
1405
+ path: manifestPath,
1406
+ cached: false
1407
+ };
1408
+ }
1409
+ function findDefaultRegistryPath(services) {
1410
+ const candidates = ["./registry", "../promptscript-registry", "./.promptscript-registry"];
1411
+ for (const candidate of candidates) {
1412
+ const manifestPath = join3(services.cwd, candidate, MANIFEST_FILENAME);
1413
+ if (services.fs.existsSync(manifestPath)) {
1414
+ return candidate;
1415
+ }
1416
+ }
1417
+ return "./registry";
1418
+ }
1419
+ function resolveManifestPath(registryPath, services) {
1420
+ if (registryPath.endsWith(".yaml") || registryPath.endsWith(".yml")) {
1421
+ return join3(services.cwd, registryPath);
1422
+ }
1423
+ return join3(services.cwd, registryPath, MANIFEST_FILENAME);
1424
+ }
1425
+ async function loadManifestFromPath(manifestPath, services) {
1426
+ if (!services.fs.existsSync(manifestPath)) {
1427
+ throw new ManifestLoadError(
1428
+ `Registry manifest not found at: ${manifestPath}
1429
+ Run \`prs pull\` to fetch the registry or check your registry configuration.`
1430
+ );
1431
+ }
1432
+ try {
1433
+ const content = await services.fs.readFile(manifestPath, "utf-8");
1434
+ const manifest = parseYaml2(content);
1435
+ return manifest;
1436
+ } catch (error) {
1437
+ throw new ManifestLoadError(
1438
+ `Failed to parse manifest: ${manifestPath}`,
1439
+ error instanceof Error ? error : void 0
1440
+ );
1441
+ }
1442
+ }
1443
+ function validateManifest(manifest) {
1444
+ if (!manifest.version) {
1445
+ throw new ManifestLoadError("Manifest missing required field: version");
1446
+ }
1447
+ if (manifest.version !== "1") {
1448
+ throw new ManifestLoadError(`Unsupported manifest version: ${manifest.version}`);
1449
+ }
1450
+ if (!manifest.meta) {
1451
+ throw new ManifestLoadError("Manifest missing required field: meta");
1452
+ }
1453
+ if (!manifest.catalog) {
1454
+ throw new ManifestLoadError("Manifest missing required field: catalog");
1455
+ }
1456
+ if (!Array.isArray(manifest.catalog)) {
1457
+ throw new ManifestLoadError("Manifest catalog must be an array");
1458
+ }
1459
+ }
1460
+ async function loadManifestFromUrl(url = OFFICIAL_REGISTRY.manifestUrl, useCache = true) {
1461
+ if (useCache && manifestCache.has(url)) {
1462
+ return {
1463
+ manifest: manifestCache.get(url),
1464
+ url,
1465
+ cached: true
1466
+ };
1467
+ }
1468
+ try {
1469
+ const response = await fetch(url, {
1470
+ headers: {
1471
+ Accept: "application/x-yaml, text/yaml, text/plain",
1472
+ "User-Agent": "PromptScript-CLI"
1473
+ }
1474
+ });
1475
+ if (!response.ok) {
1476
+ throw new ManifestLoadError(
1477
+ `Failed to fetch manifest from ${url}: ${response.status} ${response.statusText}`
1478
+ );
1479
+ }
1480
+ const content = await response.text();
1481
+ const manifest = parseYaml2(content);
1482
+ validateManifest(manifest);
1483
+ if (useCache) {
1484
+ manifestCache.set(url, manifest);
1485
+ }
1486
+ return {
1487
+ manifest,
1488
+ url,
1489
+ cached: false
1490
+ };
1491
+ } catch (error) {
1492
+ if (error instanceof ManifestLoadError) {
1493
+ throw error;
1494
+ }
1495
+ throw new ManifestLoadError(
1496
+ `Failed to load manifest from ${url}`,
1497
+ error instanceof Error ? error : void 0
1498
+ );
1499
+ }
1500
+ }
1501
+ function isValidGitUrl(url, allowedHosts) {
1502
+ try {
1503
+ if (url.startsWith("git@")) {
1504
+ const hostMatch = url.match(/^git@([^:]+):/);
1505
+ if (hostMatch && hostMatch[1]) {
1506
+ const host = hostMatch[1];
1507
+ return allowedHosts ? allowedHosts.includes(host) : true;
1508
+ }
1509
+ return false;
1510
+ }
1511
+ if (!url.startsWith("https://") && !url.startsWith("http://")) {
1512
+ return false;
1513
+ }
1514
+ const parsed = new URL(url);
1515
+ if (!parsed.hostname || parsed.hostname.length === 0) {
1516
+ return false;
1517
+ }
1518
+ if (allowedHosts) {
1519
+ return allowedHosts.includes(parsed.hostname);
1520
+ }
1521
+ return true;
1522
+ } catch {
1523
+ return false;
1524
+ }
1525
+ }
1526
+
1527
+ // packages/cli/src/utils/suggestion-engine.ts
1528
+ async function buildProjectContext(projectInfo, services = createDefaultServices()) {
1529
+ const files = await detectProjectFiles(services);
1530
+ const dependencies = await detectDependencies(services);
1531
+ return {
1532
+ files,
1533
+ dependencies,
1534
+ languages: projectInfo.languages,
1535
+ frameworks: projectInfo.frameworks
1536
+ };
1537
+ }
1538
+ async function detectProjectFiles(services) {
1539
+ const relevantFiles = [
1540
+ "package.json",
1541
+ "tsconfig.json",
1542
+ "pyproject.toml",
1543
+ "requirements.txt",
1544
+ "Cargo.toml",
1545
+ "go.mod",
1546
+ "pom.xml",
1547
+ "build.gradle",
1548
+ ".git",
1549
+ ".env",
1550
+ ".env.example",
1551
+ "jest.config.js",
1552
+ "vitest.config.ts",
1553
+ "vitest.config.js",
1554
+ "pytest.ini",
1555
+ "next.config.js",
1556
+ "next.config.mjs",
1557
+ "next.config.ts",
1558
+ "nuxt.config.js",
1559
+ "nuxt.config.ts",
1560
+ "vite.config.js",
1561
+ "vite.config.ts",
1562
+ "Dockerfile",
1563
+ "docker-compose.yml"
1564
+ ];
1565
+ const existingFiles = [];
1566
+ for (const file of relevantFiles) {
1567
+ if (services.fs.existsSync(file)) {
1568
+ existingFiles.push(file);
1569
+ }
1570
+ }
1571
+ return existingFiles;
1572
+ }
1573
+ async function detectDependencies(services) {
1574
+ if (!services.fs.existsSync("package.json")) {
1575
+ return [];
1576
+ }
1577
+ try {
1578
+ const content = await services.fs.readFile("package.json", "utf-8");
1579
+ const pkg = JSON.parse(content);
1580
+ return [...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {})];
1581
+ } catch {
1582
+ return [];
1583
+ }
1584
+ }
1585
+ function calculateSuggestions(manifest, context) {
1586
+ const result = {
1587
+ inherit: void 0,
1588
+ use: [],
1589
+ skills: [],
1590
+ reasoning: []
1591
+ };
1592
+ const suggestedUse = /* @__PURE__ */ new Set();
1593
+ const suggestedSkills = /* @__PURE__ */ new Set();
1594
+ for (const rule of manifest.suggestionRules) {
1595
+ const match = matchCondition(rule.condition, context);
1596
+ if (match.matches) {
1597
+ if (rule.suggest.inherit) {
1598
+ if (!result.inherit) {
1599
+ result.inherit = rule.suggest.inherit;
1600
+ result.reasoning.push({
1601
+ suggestion: `inherit: ${rule.suggest.inherit}`,
1602
+ reason: match.reason,
1603
+ trigger: match.trigger,
1604
+ matchedValue: match.matchedValue
1605
+ });
1606
+ }
1607
+ }
1608
+ if (rule.suggest.use) {
1609
+ for (const useItem of rule.suggest.use) {
1610
+ if (!suggestedUse.has(useItem)) {
1611
+ suggestedUse.add(useItem);
1612
+ result.use.push(useItem);
1613
+ result.reasoning.push({
1614
+ suggestion: `use: ${useItem}`,
1615
+ reason: match.reason,
1616
+ trigger: match.trigger,
1617
+ matchedValue: match.matchedValue
1618
+ });
1619
+ }
1620
+ }
1621
+ }
1622
+ if (rule.suggest.skills) {
1623
+ for (const skill of rule.suggest.skills) {
1624
+ if (!suggestedSkills.has(skill)) {
1625
+ suggestedSkills.add(skill);
1626
+ result.skills.push(skill);
1627
+ result.reasoning.push({
1628
+ suggestion: `skill: ${skill}`,
1629
+ reason: match.reason,
1630
+ trigger: match.trigger,
1631
+ matchedValue: match.matchedValue
1632
+ });
1633
+ }
1634
+ }
1635
+ }
1636
+ }
1637
+ }
1638
+ return result;
1639
+ }
1640
+ function matchCondition(condition, context) {
1641
+ if (condition.always) {
1642
+ return {
1643
+ matches: true,
1644
+ reason: "Default recommendation",
1645
+ trigger: "always"
1646
+ };
1647
+ }
1648
+ if (condition.files) {
1649
+ for (const file of condition.files) {
1650
+ if (context.files.includes(file)) {
1651
+ return {
1652
+ matches: true,
1653
+ reason: `Detected file: ${file}`,
1654
+ trigger: "file",
1655
+ matchedValue: file
1656
+ };
1657
+ }
1658
+ }
1659
+ }
1660
+ if (condition.dependencies) {
1661
+ for (const dep of condition.dependencies) {
1662
+ if (context.dependencies.includes(dep)) {
1663
+ return {
1664
+ matches: true,
1665
+ reason: `Detected dependency: ${dep}`,
1666
+ trigger: "dependency",
1667
+ matchedValue: dep
1668
+ };
1669
+ }
1670
+ }
1671
+ }
1672
+ if (condition.languages) {
1673
+ for (const lang of condition.languages) {
1674
+ if (context.languages.includes(lang)) {
1675
+ return {
1676
+ matches: true,
1677
+ reason: `Detected language: ${lang}`,
1678
+ trigger: "language",
1679
+ matchedValue: lang
1680
+ };
1681
+ }
1682
+ }
1683
+ }
1684
+ if (condition.frameworks) {
1685
+ for (const framework of condition.frameworks) {
1686
+ if (context.frameworks.includes(framework)) {
1687
+ return {
1688
+ matches: true,
1689
+ reason: `Detected framework: ${framework}`,
1690
+ trigger: "framework",
1691
+ matchedValue: framework
1692
+ };
1693
+ }
1694
+ }
1695
+ }
1696
+ return {
1697
+ matches: false,
1698
+ reason: "",
1699
+ trigger: "always"
1700
+ };
1701
+ }
1702
+ function formatSuggestionResult(result) {
1703
+ const lines = [];
1704
+ if (result.inherit) {
1705
+ lines.push(`\u{1F4E6} Inherit: ${result.inherit}`);
1706
+ }
1707
+ if (result.use.length > 0) {
1708
+ lines.push(`\u{1F527} Use: ${result.use.join(", ")}`);
1709
+ }
1710
+ if (result.skills.length > 0) {
1711
+ lines.push(`\u26A1 Skills: ${result.skills.join(", ")}`);
1712
+ }
1713
+ if (result.reasoning.length > 0) {
1714
+ lines.push("");
1715
+ lines.push("Reasoning:");
1716
+ for (const r of result.reasoning) {
1717
+ lines.push(` \u2022 ${r.suggestion} (${r.reason})`);
1718
+ }
1719
+ }
1720
+ return lines;
1721
+ }
1722
+ function createSuggestionChoices(manifest, result) {
1723
+ const choices = [];
1724
+ if (result.inherit) {
1725
+ const entry = manifest.catalog.find((e) => e.id === result.inherit);
1726
+ choices.push({
1727
+ name: `${result.inherit} (inherit)`,
1728
+ value: `inherit:${result.inherit}`,
1729
+ checked: true,
1730
+ description: entry?.description
1731
+ });
1732
+ }
1733
+ for (const use of result.use) {
1734
+ const entry = manifest.catalog.find((e) => e.id === use);
1735
+ choices.push({
1736
+ name: `${use} (use)`,
1737
+ value: `use:${use}`,
1738
+ checked: true,
1739
+ description: entry?.description
1740
+ });
1741
+ }
1742
+ for (const skill of result.skills) {
1743
+ const entry = manifest.catalog.find((e) => e.id === skill);
1744
+ choices.push({
1745
+ name: `${skill} (skill)`,
1746
+ value: `skill:${skill}`,
1747
+ checked: true,
1748
+ description: entry?.description
1749
+ });
1750
+ }
1751
+ return choices;
1752
+ }
1753
+ function parseSelectedChoices(selected) {
1754
+ const result = {
1755
+ use: [],
1756
+ skills: []
1757
+ };
1758
+ for (const choice of selected) {
1759
+ if (choice.startsWith("inherit:")) {
1760
+ result.inherit = choice.slice("inherit:".length);
1761
+ } else if (choice.startsWith("use:")) {
1762
+ result.use.push(choice.slice("use:".length));
1763
+ } else if (choice.startsWith("skill:")) {
1764
+ result.skills.push(choice.slice("skill:".length));
1765
+ }
1766
+ }
1767
+ return result;
1768
+ }
1769
+
1369
1770
  // packages/cli/src/commands/init.ts
1370
1771
  var __filename = fileURLToPath(import.meta.url);
1371
- var __dirname = dirname2(__filename);
1772
+ var __dirname = dirname3(__filename);
1372
1773
  async function initCommand(options, services = createDefaultServices()) {
1373
1774
  const { fs: fs4 } = services;
1374
1775
  if (fs4.existsSync("promptscript.yaml") && !options.force) {
@@ -1380,11 +1781,22 @@ async function initCommand(options, services = createDefaultServices()) {
1380
1781
  const projectInfo = await detectProject(services);
1381
1782
  const aiToolsDetection = await detectAITools(services);
1382
1783
  const prettierConfigPath = findPrettierConfig(process.cwd());
1784
+ let manifest;
1785
+ try {
1786
+ const registryPath = options.registry ?? "./registry";
1787
+ const { manifest: loadedManifest } = await loadManifest({ registryPath }, services);
1788
+ manifest = loadedManifest;
1789
+ } catch (error) {
1790
+ if (!(error instanceof ManifestLoadError)) {
1791
+ throw error;
1792
+ }
1793
+ }
1383
1794
  const config = await resolveConfig(
1384
1795
  options,
1385
1796
  projectInfo,
1386
1797
  aiToolsDetection,
1387
1798
  prettierConfigPath,
1799
+ manifest,
1388
1800
  services
1389
1801
  );
1390
1802
  const spinner = createSpinner("Creating PromptScript configuration...").start();
@@ -1440,7 +1852,14 @@ async function initCommand(options, services = createDefaultServices()) {
1440
1852
  ConsoleOutput.muted(` Inherit: ${config.inherit}`);
1441
1853
  }
1442
1854
  if (config.registry) {
1443
- ConsoleOutput.muted(` Registry: ${config.registry}`);
1855
+ if (config.registry.type === "git") {
1856
+ ConsoleOutput.muted(` Registry: ${config.registry.url} (${config.registry.ref})`);
1857
+ } else {
1858
+ ConsoleOutput.muted(` Registry: ${config.registry.path} (local)`);
1859
+ }
1860
+ }
1861
+ if (config.use && config.use.length > 0) {
1862
+ ConsoleOutput.muted(` Use: ${config.use.join(", ")}`);
1444
1863
  }
1445
1864
  ConsoleOutput.newline();
1446
1865
  if (config.prettierConfigPath) {
@@ -1494,23 +1913,46 @@ async function initCommand(options, services = createDefaultServices()) {
1494
1913
  process.exit(1);
1495
1914
  }
1496
1915
  }
1497
- async function resolveConfig(options, projectInfo, aiToolsDetection, prettierConfigPath, services) {
1916
+ async function resolveConfig(options, projectInfo, aiToolsDetection, prettierConfigPath, manifest, services) {
1498
1917
  if (options.yes) {
1918
+ let inherit = options.inherit;
1919
+ let use;
1920
+ let activeManifest = manifest;
1921
+ if (!activeManifest) {
1922
+ try {
1923
+ const { manifest: remoteManifest } = await loadManifestFromUrl();
1924
+ activeManifest = remoteManifest;
1925
+ } catch {
1926
+ }
1927
+ }
1928
+ if (activeManifest) {
1929
+ const context = await buildProjectContext(projectInfo, services);
1930
+ const suggestions = calculateSuggestions(activeManifest, context);
1931
+ if (!inherit && suggestions.inherit) {
1932
+ inherit = suggestions.inherit;
1933
+ }
1934
+ if (suggestions.use.length > 0) {
1935
+ use = suggestions.use;
1936
+ }
1937
+ }
1938
+ const registry = options.registry ? { type: "local", path: options.registry } : { type: "git", url: OFFICIAL_REGISTRY.url, ref: OFFICIAL_REGISTRY.branch };
1499
1939
  return {
1500
1940
  projectId: options.name ?? projectInfo.name,
1501
1941
  team: options.team,
1502
- inherit: options.inherit,
1503
- registry: options.registry ?? "./registry",
1942
+ inherit,
1943
+ use,
1944
+ registry,
1504
1945
  targets: options.targets ?? getSuggestedTargets(aiToolsDetection),
1505
1946
  prettierConfigPath
1506
1947
  };
1507
1948
  }
1508
1949
  if (!options.interactive && options.name && options.targets) {
1950
+ const registry = options.registry ? { type: "local", path: options.registry } : void 0;
1509
1951
  return {
1510
1952
  projectId: options.name,
1511
1953
  team: options.team,
1512
1954
  inherit: options.inherit,
1513
- registry: options.registry,
1955
+ registry,
1514
1956
  targets: options.targets,
1515
1957
  prettierConfigPath
1516
1958
  };
@@ -1520,10 +1962,11 @@ async function resolveConfig(options, projectInfo, aiToolsDetection, prettierCon
1520
1962
  projectInfo,
1521
1963
  aiToolsDetection,
1522
1964
  prettierConfigPath,
1965
+ manifest,
1523
1966
  services
1524
1967
  );
1525
1968
  }
1526
- async function runInteractivePrompts(options, projectInfo, aiToolsDetection, prettierConfigPath, services) {
1969
+ async function runInteractivePrompts(options, projectInfo, aiToolsDetection, prettierConfigPath, manifest, services) {
1527
1970
  const { prompts: prompts2 } = services;
1528
1971
  ConsoleOutput.newline();
1529
1972
  console.log("\u{1F680} PromptScript Setup");
@@ -1549,33 +1992,124 @@ async function runInteractivePrompts(options, projectInfo, aiToolsDetection, pre
1549
1992
  message: "Project name:",
1550
1993
  default: options.name ?? projectInfo.name
1551
1994
  });
1552
- const wantsInherit = await prompts2.confirm({
1553
- message: "Do you want to inherit from a parent configuration?",
1554
- default: false
1995
+ let registry;
1996
+ let activeManifest = manifest;
1997
+ const registryChoice = await prompts2.select({
1998
+ message: "Registry configuration:",
1999
+ choices: [
2000
+ {
2001
+ name: `\u{1F4E6} Use official PromptScript Registry (${OFFICIAL_REGISTRY.url})`,
2002
+ value: "official"
2003
+ },
2004
+ {
2005
+ name: "\u{1F517} Connect to a custom Git registry",
2006
+ value: "custom-git"
2007
+ },
2008
+ {
2009
+ name: "\u{1F4C1} Use a local registry directory",
2010
+ value: "local"
2011
+ },
2012
+ {
2013
+ name: "\u23ED\uFE0F Skip registry (configure later)",
2014
+ value: "skip"
2015
+ }
2016
+ ],
2017
+ default: manifest ? "official" : "skip"
1555
2018
  });
1556
- let inherit;
1557
- if (wantsInherit) {
1558
- inherit = await prompts2.input({
1559
- message: "Inheritance path (e.g., @company/team):",
1560
- default: options.inherit ?? "@company/team",
2019
+ if (registryChoice === "official") {
2020
+ registry = {
2021
+ type: "git",
2022
+ url: OFFICIAL_REGISTRY.url,
2023
+ ref: OFFICIAL_REGISTRY.branch
2024
+ };
2025
+ if (!activeManifest) {
2026
+ try {
2027
+ ConsoleOutput.muted("Fetching registry manifest...");
2028
+ const { manifest: remoteManifest } = await loadManifestFromUrl();
2029
+ activeManifest = remoteManifest;
2030
+ ConsoleOutput.success(
2031
+ `Loaded ${remoteManifest.catalog.length} configurations from official registry`
2032
+ );
2033
+ } catch {
2034
+ ConsoleOutput.warn("Could not fetch registry manifest - suggestions will be limited");
2035
+ }
2036
+ }
2037
+ } else if (registryChoice === "custom-git") {
2038
+ const gitUrl = await prompts2.input({
2039
+ message: "Git repository URL:",
2040
+ default: "https://github.com/your-org/your-registry.git",
1561
2041
  validate: (value) => {
1562
- if (!value.startsWith("@")) {
1563
- return "Inheritance path should start with @";
2042
+ if (!isValidGitUrl(value)) {
2043
+ return "Please enter a valid Git repository URL (https:// or git@)";
1564
2044
  }
1565
2045
  return true;
1566
2046
  }
1567
2047
  });
1568
- }
1569
- const wantsRegistry = await prompts2.confirm({
1570
- message: "Do you want to configure a registry?",
1571
- default: false
1572
- });
1573
- let registry;
1574
- if (wantsRegistry) {
1575
- registry = await prompts2.input({
1576
- message: "Registry path:",
2048
+ const gitRef = await prompts2.input({
2049
+ message: "Branch or tag:",
2050
+ default: "main"
2051
+ });
2052
+ registry = {
2053
+ type: "git",
2054
+ url: gitUrl,
2055
+ ref: gitRef
2056
+ };
2057
+ } else if (registryChoice === "local") {
2058
+ const localPath = await prompts2.input({
2059
+ message: "Local registry path:",
1577
2060
  default: options.registry ?? "./registry"
1578
2061
  });
2062
+ registry = {
2063
+ type: "local",
2064
+ path: localPath
2065
+ };
2066
+ }
2067
+ let inherit = options.inherit;
2068
+ let use;
2069
+ if (activeManifest) {
2070
+ const context = await buildProjectContext(projectInfo, services);
2071
+ const suggestions = calculateSuggestions(activeManifest, context);
2072
+ if (suggestions.inherit || suggestions.use.length > 0) {
2073
+ ConsoleOutput.newline();
2074
+ console.log("\u{1F4E6} Suggested configurations based on your project:");
2075
+ const suggestionLines = formatSuggestionResult(suggestions);
2076
+ for (const line of suggestionLines) {
2077
+ ConsoleOutput.muted(` ${line}`);
2078
+ }
2079
+ ConsoleOutput.newline();
2080
+ const choices = createSuggestionChoices(activeManifest, suggestions);
2081
+ if (choices.length > 0) {
2082
+ const selected = await prompts2.checkbox({
2083
+ message: "Select configurations to use:",
2084
+ choices: choices.map((c) => ({
2085
+ name: c.description ? `${c.name} - ${c.description}` : c.name,
2086
+ value: c.value,
2087
+ checked: c.checked
2088
+ }))
2089
+ });
2090
+ const parsed = parseSelectedChoices(selected);
2091
+ inherit = parsed.inherit;
2092
+ use = parsed.use.length > 0 ? parsed.use : void 0;
2093
+ }
2094
+ }
2095
+ }
2096
+ if (!inherit) {
2097
+ const wantsInherit = await prompts2.confirm({
2098
+ message: "Do you want to inherit from a parent configuration?",
2099
+ default: false
2100
+ });
2101
+ if (wantsInherit) {
2102
+ inherit = await prompts2.input({
2103
+ message: "Inheritance path (e.g., @stacks/react):",
2104
+ default: options.inherit ?? "@stacks/react",
2105
+ validate: (value) => {
2106
+ if (!value.startsWith("@")) {
2107
+ return "Inheritance path should start with @";
2108
+ }
2109
+ return true;
2110
+ }
2111
+ });
2112
+ }
1579
2113
  }
1580
2114
  const suggestedTargets = getSuggestedTargets(aiToolsDetection);
1581
2115
  const allTargets = getAllTargets();
@@ -1604,6 +2138,7 @@ async function runInteractivePrompts(options, projectInfo, aiToolsDetection, pre
1604
2138
  projectId,
1605
2139
  team,
1606
2140
  inherit,
2141
+ use,
1607
2142
  registry,
1608
2143
  targets,
1609
2144
  prettierConfigPath
@@ -1627,13 +2162,34 @@ function generateConfig(config) {
1627
2162
  if (config.inherit) {
1628
2163
  lines.push(`inherit: '${config.inherit}'`);
1629
2164
  } else {
1630
- lines.push("# inherit: '@company/team'");
2165
+ lines.push("# inherit: '@stacks/react'");
2166
+ }
2167
+ lines.push("");
2168
+ if (config.use && config.use.length > 0) {
2169
+ lines.push("use:");
2170
+ for (const useItem of config.use) {
2171
+ lines.push(` - '${useItem}'`);
2172
+ }
2173
+ } else {
2174
+ lines.push("# use:", "# - '@fragments/testing'", "# - '@fragments/typescript'");
1631
2175
  }
1632
2176
  lines.push("");
1633
2177
  if (config.registry) {
1634
- lines.push("registry:", ` path: '${config.registry}'`);
2178
+ if (config.registry.type === "git") {
2179
+ lines.push("registry:", " git:", ` url: '${config.registry.url}'`);
2180
+ if (config.registry.ref) {
2181
+ lines.push(` ref: '${config.registry.ref}'`);
2182
+ }
2183
+ } else {
2184
+ lines.push("registry:", ` path: '${config.registry.path}'`);
2185
+ }
1635
2186
  } else {
1636
- lines.push("# registry:", "# path: './registry'");
2187
+ lines.push(
2188
+ "# registry:",
2189
+ "# git:",
2190
+ "# url: 'https://github.com/mrwogu/promptscript-registry.git'",
2191
+ "# ref: 'main'"
2192
+ );
1637
2193
  }
1638
2194
  lines.push("", "targets:");
1639
2195
  for (const target of config.targets) {
@@ -1654,7 +2210,13 @@ function generateConfig(config) {
1654
2210
  return lines.join("\n");
1655
2211
  }
1656
2212
  function generateProjectPs(config, projectInfo) {
1657
- const inheritLine = config.inherit ? `@inherit ${config.inherit}` : "# @inherit @company/team";
2213
+ const inheritLine = config.inherit ? `@inherit ${config.inherit}` : "# @inherit @stacks/react";
2214
+ let useLines = "";
2215
+ if (config.use && config.use.length > 0) {
2216
+ useLines = config.use.map((u) => `@use ${u}`).join("\n");
2217
+ } else {
2218
+ useLines = "# @use @fragments/testing\n# @use @fragments/typescript";
2219
+ }
1658
2220
  const languagesLine = projectInfo.languages.length > 0 ? ` languages: [${projectInfo.languages.join(", ")}]` : " # languages: [typescript]";
1659
2221
  const frameworksLine = projectInfo.frameworks.length > 0 ? ` frameworks: [${projectInfo.frameworks.join(", ")}]` : " # frameworks: []";
1660
2222
  const syntaxVersion = getPackageVersion(__dirname, "./package.json");
@@ -1667,11 +2229,12 @@ function generateProjectPs(config, projectInfo) {
1667
2229
  }
1668
2230
 
1669
2231
  ${inheritLine}
2232
+ ${useLines}
1670
2233
 
1671
2234
  @identity {
1672
2235
  """
1673
2236
  You are working on the ${config.projectId} project.
1674
-
2237
+
1675
2238
  [Describe your project here]
1676
2239
  """
1677
2240
  }
@@ -1700,7 +2263,7 @@ ${frameworksLine}
1700
2263
  }
1701
2264
 
1702
2265
  // packages/cli/src/commands/compile.ts
1703
- import { resolve as resolve4, dirname as dirname5 } from "path";
2266
+ import { resolve as resolve4, dirname as dirname6 } from "path";
1704
2267
  import { writeFile as writeFile2, mkdir as mkdir2, readFile as readFile6 } from "fs/promises";
1705
2268
  import { existsSync as existsSync7 } from "fs";
1706
2269
  import chokidar from "chokidar";
@@ -1708,7 +2271,7 @@ import chokidar from "chokidar";
1708
2271
  // packages/cli/src/config/loader.ts
1709
2272
  import { readFile as readFile3 } from "fs/promises";
1710
2273
  import { existsSync as existsSync3 } from "fs";
1711
- import { parse as parseYaml2 } from "yaml";
2274
+ import { parse as parseYaml3 } from "yaml";
1712
2275
  var CONFIG_FILES = [
1713
2276
  "promptscript.yaml",
1714
2277
  "promptscript.yml",
@@ -1755,7 +2318,7 @@ async function loadConfig(customPath) {
1755
2318
  let content = await readFile3(configFile, "utf-8");
1756
2319
  content = interpolateEnvVars(content);
1757
2320
  try {
1758
- return parseYaml2(content);
2321
+ return parseYaml3(content);
1759
2322
  } catch (error) {
1760
2323
  const message = error instanceof Error ? error.message : "Unknown parse error";
1761
2324
  throw new Error(`Failed to parse ${configFile}: ${message}`);
@@ -15317,7 +15880,7 @@ function parse(source, options = {}) {
15317
15880
 
15318
15881
  // packages/resolver/src/loader.ts
15319
15882
  import { readFile as readFile4 } from "fs/promises";
15320
- import { resolve as resolve2, dirname as dirname3, isAbsolute } from "path";
15883
+ import { resolve as resolve2, dirname as dirname4, isAbsolute } from "path";
15321
15884
  var FileLoader = class {
15322
15885
  registryPath;
15323
15886
  localPath;
@@ -15378,7 +15941,7 @@ var FileLoader = class {
15378
15941
  */
15379
15942
  resolveRef(ref, fromFile) {
15380
15943
  if (ref.isRelative) {
15381
- const dir = dirname3(fromFile);
15944
+ const dir = dirname4(fromFile);
15382
15945
  const rawPath = ref.raw.endsWith(".prs") ? ref.raw : `${ref.raw}.prs`;
15383
15946
  return resolve2(dir, rawPath);
15384
15947
  }
@@ -16040,7 +16603,7 @@ function uniqueConcat3(parent, child) {
16040
16603
 
16041
16604
  // packages/resolver/src/skills.ts
16042
16605
  import { readFile as readFile5, access } from "fs/promises";
16043
- import { resolve as resolve3, dirname as dirname4 } from "path";
16606
+ import { resolve as resolve3, dirname as dirname5 } from "path";
16044
16607
  function parseSkillMd(content) {
16045
16608
  const lines = content.split("\n");
16046
16609
  let inFrontmatter = false;
@@ -16095,7 +16658,7 @@ async function resolveNativeSkills(ast, registryPath, sourceFile) {
16095
16658
  const skillsContent = skillsBlock.content;
16096
16659
  const updatedProperties = { ...skillsContent.properties };
16097
16660
  let hasUpdates = false;
16098
- const sourceDir = dirname4(sourceFile);
16661
+ const sourceDir = dirname5(sourceFile);
16099
16662
  const isSkillsDir = sourceDir.includes("@skills");
16100
16663
  for (const [skillName, skillValue] of Object.entries(skillsContent.properties)) {
16101
16664
  if (typeof skillValue !== "object" || skillValue === null || Array.isArray(skillValue)) {
@@ -16323,7 +16886,7 @@ var Resolver = class {
16323
16886
 
16324
16887
  // packages/resolver/src/registry.ts
16325
16888
  import { existsSync as existsSync4, promises as fs } from "fs";
16326
- import { join as join3 } from "path";
16889
+ import { join as join4 } from "path";
16327
16890
  var FileSystemRegistry = class {
16328
16891
  rootPath;
16329
16892
  constructor(options) {
@@ -16333,7 +16896,7 @@ var FileSystemRegistry = class {
16333
16896
  * Resolve a path relative to the registry root.
16334
16897
  */
16335
16898
  resolvePath(path) {
16336
- return join3(this.rootPath, path);
16899
+ return join4(this.rootPath, path);
16337
16900
  }
16338
16901
  async fetch(path) {
16339
16902
  const fullPath = this.resolvePath(path);
@@ -16548,12 +17111,12 @@ function createHttpRegistry(options) {
16548
17111
 
16549
17112
  // packages/resolver/src/git-registry.ts
16550
17113
  import { existsSync as existsSync6, promises as fs3 } from "fs";
16551
- import { join as join5 } from "path";
17114
+ import { join as join6 } from "path";
16552
17115
  import { simpleGit } from "simple-git";
16553
17116
 
16554
17117
  // packages/resolver/src/git-cache-manager.ts
16555
17118
  import { existsSync as existsSync5, promises as fs2 } from "fs";
16556
- import { join as join4 } from "path";
17119
+ import { join as join5 } from "path";
16557
17120
  import { homedir } from "os";
16558
17121
 
16559
17122
  // packages/resolver/src/git-url-utils.ts
@@ -16661,7 +17224,7 @@ function parseVersionedPath(path) {
16661
17224
  }
16662
17225
 
16663
17226
  // packages/resolver/src/git-cache-manager.ts
16664
- var DEFAULT_CACHE_DIR = join4(homedir(), ".promptscript", ".cache", "git");
17227
+ var DEFAULT_CACHE_DIR = join5(homedir(), ".promptscript", ".cache", "git");
16665
17228
  var DEFAULT_TTL = 36e5;
16666
17229
  var METADATA_FILE = ".prs-cache-meta.json";
16667
17230
  var GitCacheManager = class {
@@ -16680,7 +17243,7 @@ var GitCacheManager = class {
16680
17243
  */
16681
17244
  getCachePath(url, ref) {
16682
17245
  const cacheKey = getCacheKey(url, ref);
16683
- return join4(this.cacheDir, cacheKey);
17246
+ return join5(this.cacheDir, cacheKey);
16684
17247
  }
16685
17248
  /**
16686
17249
  * Check if a cache entry exists and is not stale.
@@ -16786,7 +17349,7 @@ var GitCacheManager = class {
16786
17349
  if (!dir.isDirectory()) {
16787
17350
  continue;
16788
17351
  }
16789
- const cachePath = join4(this.cacheDir, dir.name);
17352
+ const cachePath = join5(this.cacheDir, dir.name);
16790
17353
  const metadata = await this.readMetadata(cachePath);
16791
17354
  if (metadata) {
16792
17355
  const isStale = Date.now() - metadata.lastUpdated > this.ttl;
@@ -16834,7 +17397,7 @@ var GitCacheManager = class {
16834
17397
  let size = 0;
16835
17398
  const entries = await fs2.readdir(dirPath, { withFileTypes: true });
16836
17399
  for (const entry of entries) {
16837
- const fullPath = join4(dirPath, entry.name);
17400
+ const fullPath = join5(dirPath, entry.name);
16838
17401
  if (entry.isDirectory()) {
16839
17402
  size += await this.calculateDirSize(fullPath);
16840
17403
  } else {
@@ -16848,7 +17411,7 @@ var GitCacheManager = class {
16848
17411
  * Read metadata from a cache directory.
16849
17412
  */
16850
17413
  async readMetadata(cachePath) {
16851
- const metadataPath = join4(cachePath, METADATA_FILE);
17414
+ const metadataPath = join5(cachePath, METADATA_FILE);
16852
17415
  if (!existsSync5(metadataPath)) {
16853
17416
  return null;
16854
17417
  }
@@ -16863,7 +17426,7 @@ var GitCacheManager = class {
16863
17426
  * Write metadata to a cache directory.
16864
17427
  */
16865
17428
  async writeMetadata(cachePath, metadata) {
16866
- const metadataPath = join4(cachePath, METADATA_FILE);
17429
+ const metadataPath = join5(cachePath, METADATA_FILE);
16867
17430
  await fs2.writeFile(metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
16868
17431
  }
16869
17432
  };
@@ -17154,9 +17717,9 @@ var GitRegistry = class {
17154
17717
  cleanPath += ".prs";
17155
17718
  }
17156
17719
  if (this.subPath) {
17157
- return join5(repoPath, this.subPath, cleanPath);
17720
+ return join6(repoPath, this.subPath, cleanPath);
17158
17721
  }
17159
- return join5(repoPath, cleanPath);
17722
+ return join6(repoPath, cleanPath);
17160
17723
  }
17161
17724
  /**
17162
17725
  * Resolve a directory path within the repository.
@@ -17166,9 +17729,9 @@ var GitRegistry = class {
17166
17729
  */
17167
17730
  resolveDirectoryPath(repoPath, relativePath) {
17168
17731
  if (this.subPath) {
17169
- return join5(repoPath, this.subPath, relativePath);
17732
+ return join6(repoPath, this.subPath, relativePath);
17170
17733
  }
17171
- return join5(repoPath, relativePath);
17734
+ return join6(repoPath, relativePath);
17172
17735
  }
17173
17736
  /**
17174
17737
  * Check if an error is an authentication error.
@@ -17886,8 +18449,8 @@ var Compiler = class _Compiler {
17886
18449
  */
17887
18450
  async watch(entryPath, options = {}) {
17888
18451
  const { default: chokidar2 } = await import("chokidar");
17889
- const { dirname: dirname8, resolve: resolve9 } = await import("path");
17890
- const baseDir = dirname8(resolve9(entryPath));
18452
+ const { dirname: dirname10, resolve: resolve9 } = await import("path");
18453
+ const baseDir = dirname10(resolve9(entryPath));
17891
18454
  const includePatterns = options.include ?? ["**/*.prs"];
17892
18455
  const excludePatterns = options.exclude ?? ["**/node_modules/**"];
17893
18456
  const debounceMs = options.debounce ?? 300;
@@ -18107,6 +18670,69 @@ function createPager(enabled = true) {
18107
18670
  return new Pager(enabled);
18108
18671
  }
18109
18672
 
18673
+ // packages/cli/src/utils/registry-resolver.ts
18674
+ import { join as join7 } from "path";
18675
+ async function resolveRegistryPath(config) {
18676
+ if (config.registry?.git) {
18677
+ const gitConfig = config.registry.git;
18678
+ const ref = gitConfig.ref ?? "main";
18679
+ const cacheManager = new GitCacheManager({
18680
+ ttl: config.registry.cache?.ttl
18681
+ });
18682
+ const normalizedUrl = normalizeGitUrl(gitConfig.url);
18683
+ const cachePath = cacheManager.getCachePath(normalizedUrl, ref);
18684
+ const isValid = await cacheManager.isValid(normalizedUrl, ref);
18685
+ if (isValid) {
18686
+ const subPath2 = gitConfig.path ?? "";
18687
+ return {
18688
+ path: subPath2 ? join7(cachePath, subPath2) : cachePath,
18689
+ isRemote: true,
18690
+ source: "git"
18691
+ };
18692
+ }
18693
+ const registry = createGitRegistry({
18694
+ url: gitConfig.url,
18695
+ ref,
18696
+ path: gitConfig.path,
18697
+ auth: gitConfig.auth,
18698
+ cache: {
18699
+ enabled: config.registry.cache?.enabled ?? true,
18700
+ ttl: config.registry.cache?.ttl
18701
+ }
18702
+ });
18703
+ try {
18704
+ await registry.fetch("registry-manifest.yaml");
18705
+ } catch (error) {
18706
+ const message = error instanceof Error ? error.message : String(error);
18707
+ if (!message.includes("not found") && !message.includes("FileNotFoundError")) {
18708
+ throw new Error(
18709
+ `Failed to clone registry from ${gitConfig.url}: ${message}. Please check your network connection and registry configuration.`
18710
+ );
18711
+ }
18712
+ }
18713
+ const subPath = gitConfig.path ?? "";
18714
+ return {
18715
+ path: subPath ? join7(cachePath, subPath) : cachePath,
18716
+ isRemote: true,
18717
+ source: "git"
18718
+ };
18719
+ }
18720
+ if (config.registry?.url) {
18721
+ const localPath = config.registry?.path ?? "./registry";
18722
+ return {
18723
+ path: localPath,
18724
+ isRemote: true,
18725
+ source: "http"
18726
+ };
18727
+ }
18728
+ const registryPath = config.registry?.path ?? "./registry";
18729
+ return {
18730
+ path: registryPath,
18731
+ isRemote: false,
18732
+ source: "local"
18733
+ };
18734
+ }
18735
+
18110
18736
  // packages/cli/src/commands/compile.ts
18111
18737
  var PROMPTSCRIPT_MARKERS = [
18112
18738
  "<!-- PromptScript",
@@ -18207,7 +18833,7 @@ async function writeOutputs(outputs, options, _config, services) {
18207
18833
  continue;
18208
18834
  }
18209
18835
  if (!fileExists2) {
18210
- await mkdir2(dirname5(outputPath), { recursive: true });
18836
+ await mkdir2(dirname6(outputPath), { recursive: true });
18211
18837
  await writeFile2(outputPath, output.content, "utf-8");
18212
18838
  ConsoleOutput.success(outputPath);
18213
18839
  result.written.push(outputPath);
@@ -18223,14 +18849,14 @@ async function writeOutputs(outputs, options, _config, services) {
18223
18849
  result.unchanged.push(outputPath);
18224
18850
  continue;
18225
18851
  }
18226
- await mkdir2(dirname5(outputPath), { recursive: true });
18852
+ await mkdir2(dirname6(outputPath), { recursive: true });
18227
18853
  await writeFile2(outputPath, output.content, "utf-8");
18228
18854
  ConsoleOutput.success(outputPath);
18229
18855
  result.written.push(outputPath);
18230
18856
  continue;
18231
18857
  }
18232
18858
  if (options.force || overwriteAll) {
18233
- await mkdir2(dirname5(outputPath), { recursive: true });
18859
+ await mkdir2(dirname6(outputPath), { recursive: true });
18234
18860
  await writeFile2(outputPath, output.content, "utf-8");
18235
18861
  ConsoleOutput.success(outputPath);
18236
18862
  result.written.push(outputPath);
@@ -18243,7 +18869,7 @@ async function writeOutputs(outputs, options, _config, services) {
18243
18869
  const response = await promptForOverwrite(outputPath, services);
18244
18870
  switch (response) {
18245
18871
  case "yes":
18246
- await mkdir2(dirname5(outputPath), { recursive: true });
18872
+ await mkdir2(dirname6(outputPath), { recursive: true });
18247
18873
  await writeFile2(outputPath, output.content, "utf-8");
18248
18874
  ConsoleOutput.success(outputPath);
18249
18875
  result.written.push(outputPath);
@@ -18254,7 +18880,7 @@ async function writeOutputs(outputs, options, _config, services) {
18254
18880
  break;
18255
18881
  case "all":
18256
18882
  overwriteAll = true;
18257
- await mkdir2(dirname5(outputPath), { recursive: true });
18883
+ await mkdir2(dirname6(outputPath), { recursive: true });
18258
18884
  await writeFile2(outputPath, output.content, "utf-8");
18259
18885
  ConsoleOutput.success(outputPath);
18260
18886
  result.written.push(outputPath);
@@ -18293,10 +18919,20 @@ async function compileCommand(options, services = createDefaultServices()) {
18293
18919
  try {
18294
18920
  logger.verbose("Loading configuration...");
18295
18921
  const config = await loadConfig(options.config);
18296
- spinner.text = "Compiling...";
18297
18922
  const selectedTarget = options.target ?? options.format;
18298
18923
  const targets = selectedTarget ? [{ name: selectedTarget }] : parseTargets(config.targets);
18299
- const registryPath = options.registry ?? config.registry?.path ?? "./registry";
18924
+ let registryPath;
18925
+ if (options.registry) {
18926
+ registryPath = options.registry;
18927
+ } else {
18928
+ spinner.text = "Resolving registry...";
18929
+ const registry = await resolveRegistryPath(config);
18930
+ registryPath = registry.path;
18931
+ if (registry.isRemote) {
18932
+ logger.verbose(`Using cached git registry: ${registryPath}`);
18933
+ }
18934
+ }
18935
+ spinner.text = "Compiling...";
18300
18936
  logger.verbose(`Registry: ${registryPath}`);
18301
18937
  logger.debug(`Config: ${JSON.stringify(config, null, 2)}`);
18302
18938
  const prettierOptions = await resolvePrettierOptions(config, process.cwd());
@@ -18472,10 +19108,12 @@ async function validateCommand(options) {
18472
19108
  const spinner = isJsonFormat ? createSpinner("").stop() : createSpinner("Loading configuration...").start();
18473
19109
  try {
18474
19110
  const config = await loadConfig();
19111
+ if (!isJsonFormat) spinner.text = "Resolving registry...";
19112
+ const registry = await resolveRegistryPath(config);
18475
19113
  if (!isJsonFormat) spinner.text = "Validating...";
18476
19114
  const compiler = new Compiler({
18477
19115
  resolver: {
18478
- registryPath: config.registry?.path ?? "./registry",
19116
+ registryPath: registry.path,
18479
19117
  localPath: "./.promptscript"
18480
19118
  },
18481
19119
  validator: config.validation,
@@ -18524,7 +19162,7 @@ function outputJsonResult(result) {
18524
19162
  // packages/cli/src/commands/pull.ts
18525
19163
  import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
18526
19164
  import { existsSync as existsSync9 } from "fs";
18527
- import { resolve as resolve6, dirname as dirname6 } from "path";
19165
+ import { resolve as resolve6, dirname as dirname7 } from "path";
18528
19166
  async function pullCommand(options) {
18529
19167
  const spinner = createSpinner("Loading configuration...").start();
18530
19168
  try {
@@ -18563,7 +19201,7 @@ async function pullCommand(options) {
18563
19201
  }
18564
19202
  return;
18565
19203
  }
18566
- await mkdir3(dirname6(destPath), { recursive: true });
19204
+ await mkdir3(dirname7(destPath), { recursive: true });
18567
19205
  await writeFile3(destPath, content, "utf-8");
18568
19206
  spinner.succeed("Pulled from registry");
18569
19207
  ConsoleOutput.success(destPath);
@@ -18719,11 +19357,13 @@ async function diffCommand(options) {
18719
19357
  const spinner = createSpinner("Loading configuration...").start();
18720
19358
  try {
18721
19359
  const config = await loadConfig();
19360
+ spinner.text = "Resolving registry...";
19361
+ const registry = await resolveRegistryPath(config);
18722
19362
  spinner.text = "Compiling...";
18723
19363
  const targets = options.target ? [{ name: options.target }] : parseTargets2(config.targets);
18724
19364
  const compiler = new Compiler({
18725
19365
  resolver: {
18726
- registryPath: config.registry?.path ?? "./registry",
19366
+ registryPath: registry.path,
18727
19367
  localPath: "./.promptscript"
18728
19368
  },
18729
19369
  validator: config.validation,
@@ -18957,11 +19597,189 @@ function printResults(results) {
18957
19597
  }
18958
19598
  }
18959
19599
 
18960
- // packages/cli/src/cli.ts
19600
+ // packages/cli/src/commands/update-check.ts
19601
+ import { fileURLToPath as fileURLToPath2 } from "url";
19602
+ import { dirname as dirname8 } from "path";
19603
+
19604
+ // packages/cli/src/utils/version-check.ts
19605
+ import { homedir as homedir2 } from "os";
19606
+ import { join as join8 } from "path";
19607
+ import { existsSync as existsSync12, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
19608
+ var NPM_REGISTRY_URL = "https://registry.npmjs.org/@promptscript/cli/latest";
19609
+ var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
19610
+ var FETCH_TIMEOUT_MS = 3e3;
19611
+ function getCacheDir() {
19612
+ return join8(homedir2(), ".promptscript", ".cache");
19613
+ }
19614
+ function getCachePath() {
19615
+ return join8(getCacheDir(), "version.json");
19616
+ }
19617
+ function readCache() {
19618
+ try {
19619
+ const cachePath = getCachePath();
19620
+ if (!existsSync12(cachePath)) {
19621
+ return null;
19622
+ }
19623
+ const content = readFileSync3(cachePath, "utf-8");
19624
+ return JSON.parse(content);
19625
+ } catch {
19626
+ return null;
19627
+ }
19628
+ }
19629
+ function writeCache(cache) {
19630
+ try {
19631
+ const cacheDir = getCacheDir();
19632
+ if (!existsSync12(cacheDir)) {
19633
+ mkdirSync(cacheDir, { recursive: true });
19634
+ }
19635
+ const cachePath = getCachePath();
19636
+ writeFileSync(cachePath, JSON.stringify(cache, null, 2), "utf-8");
19637
+ } catch {
19638
+ }
19639
+ }
19640
+ function isCacheValid(cache) {
19641
+ try {
19642
+ const lastCheck = new Date(cache.lastCheck).getTime();
19643
+ const now = Date.now();
19644
+ return now - lastCheck < CACHE_TTL_MS;
19645
+ } catch {
19646
+ return false;
19647
+ }
19648
+ }
19649
+ async function fetchLatestVersion() {
19650
+ try {
19651
+ const controller = new AbortController();
19652
+ const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
19653
+ const response = await fetch(NPM_REGISTRY_URL, {
19654
+ signal: controller.signal,
19655
+ headers: {
19656
+ Accept: "application/json"
19657
+ }
19658
+ });
19659
+ clearTimeout(timeout);
19660
+ if (!response.ok) {
19661
+ if (isVerbose()) {
19662
+ ConsoleOutput.verbose(`Could not check for updates: HTTP ${response.status}`);
19663
+ }
19664
+ return null;
19665
+ }
19666
+ const data = await response.json();
19667
+ return data.version ?? null;
19668
+ } catch (error) {
19669
+ if (isVerbose()) {
19670
+ const code = error.code;
19671
+ const message = error instanceof Error ? error.message : "Unknown error";
19672
+ ConsoleOutput.verbose(`Could not check for updates: ${code ?? message}`);
19673
+ }
19674
+ return null;
19675
+ }
19676
+ }
19677
+ function isNewerVersion(currentVersion, latestVersion) {
19678
+ const cleanCurrent = currentVersion.replace(/^v/, "");
19679
+ const cleanLatest = latestVersion.replace(/^v/, "");
19680
+ const [currentBase, currentPrerelease] = cleanCurrent.split("-");
19681
+ const [latestBase, latestPrerelease] = cleanLatest.split("-");
19682
+ const currentParts = (currentBase ?? "").split(".").map((p) => parseInt(p, 10) || 0);
19683
+ const latestParts = (latestBase ?? "").split(".").map((p) => parseInt(p, 10) || 0);
19684
+ for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
19685
+ const c = currentParts[i] ?? 0;
19686
+ const l = latestParts[i] ?? 0;
19687
+ if (l > c) return true;
19688
+ if (l < c) return false;
19689
+ }
19690
+ if (currentPrerelease && !latestPrerelease) {
19691
+ return true;
19692
+ }
19693
+ return false;
19694
+ }
19695
+ async function checkForUpdates(currentVersion) {
19696
+ if (process.env["PROMPTSCRIPT_NO_UPDATE_CHECK"]) {
19697
+ return null;
19698
+ }
19699
+ if (isQuiet()) {
19700
+ return null;
19701
+ }
19702
+ const cache = readCache();
19703
+ if (cache && isCacheValid(cache) && cache.currentVersion === currentVersion) {
19704
+ if (isNewerVersion(currentVersion, cache.latestVersion)) {
19705
+ return {
19706
+ currentVersion,
19707
+ latestVersion: cache.latestVersion,
19708
+ updateAvailable: true
19709
+ };
19710
+ }
19711
+ return null;
19712
+ }
19713
+ const latestVersion = await fetchLatestVersion();
19714
+ if (!latestVersion) {
19715
+ return null;
19716
+ }
19717
+ writeCache({
19718
+ lastCheck: (/* @__PURE__ */ new Date()).toISOString(),
19719
+ latestVersion,
19720
+ currentVersion
19721
+ });
19722
+ if (isNewerVersion(currentVersion, latestVersion)) {
19723
+ return {
19724
+ currentVersion,
19725
+ latestVersion,
19726
+ updateAvailable: true
19727
+ };
19728
+ }
19729
+ return null;
19730
+ }
19731
+ async function forceCheckForUpdates(currentVersion) {
19732
+ const latestVersion = await fetchLatestVersion();
19733
+ if (!latestVersion) {
19734
+ return { info: null, error: true };
19735
+ }
19736
+ writeCache({
19737
+ lastCheck: (/* @__PURE__ */ new Date()).toISOString(),
19738
+ latestVersion,
19739
+ currentVersion
19740
+ });
19741
+ const updateAvailable = isNewerVersion(currentVersion, latestVersion);
19742
+ return {
19743
+ info: {
19744
+ currentVersion,
19745
+ latestVersion,
19746
+ updateAvailable
19747
+ },
19748
+ error: false
19749
+ };
19750
+ }
19751
+ function printUpdateNotification(info) {
19752
+ console.log(
19753
+ `Update available: ${info.currentVersion} \u2192 ${info.latestVersion} (npm i -g @promptscript/cli)`
19754
+ );
19755
+ }
19756
+
19757
+ // packages/cli/src/commands/update-check.ts
18961
19758
  var __filename2 = fileURLToPath2(import.meta.url);
18962
- var __dirname2 = dirname7(__filename2);
19759
+ var __dirname2 = dirname8(__filename2);
19760
+ async function updateCheckCommand() {
19761
+ const currentVersion = getPackageVersion(__dirname2, "../../package.json");
19762
+ console.log(`@promptscript/cli v${currentVersion}`);
19763
+ const { info, error } = await forceCheckForUpdates(currentVersion);
19764
+ if (error) {
19765
+ ConsoleOutput.error("Could not check for updates");
19766
+ process.exitCode = 1;
19767
+ return;
19768
+ }
19769
+ if (info?.updateAvailable) {
19770
+ console.log(
19771
+ `Update available: ${info.currentVersion} \u2192 ${info.latestVersion} (npm i -g @promptscript/cli)`
19772
+ );
19773
+ } else {
19774
+ ConsoleOutput.success("Up to date");
19775
+ }
19776
+ }
19777
+
19778
+ // packages/cli/src/cli.ts
19779
+ var __filename3 = fileURLToPath3(import.meta.url);
19780
+ var __dirname3 = dirname9(__filename3);
18963
19781
  var program = new Command();
18964
- program.name("prs").description("PromptScript CLI - Standardize AI instructions").version(getPackageVersion(__dirname2, "./package.json")).option("--verbose", "Enable verbose output").option("--debug", "Enable debug output (includes verbose)").option("--quiet", "Suppress non-error output").hook("preAction", (thisCommand) => {
19782
+ program.name("prs").description("PromptScript CLI - Standardize AI instructions").version(getPackageVersion(__dirname3, "../package.json")).option("--verbose", "Enable verbose output").option("--debug", "Enable debug output (includes verbose)").option("--quiet", "Suppress non-error output").hook("preAction", async (thisCommand, actionCommand) => {
18965
19783
  const opts = thisCommand.opts();
18966
19784
  if (opts["quiet"]) {
18967
19785
  setContext({ logLevel: 0 /* Quiet */ });
@@ -18975,13 +19793,22 @@ program.name("prs").description("PromptScript CLI - Standardize AI instructions"
18975
19793
  } else if (process.env["PROMPTSCRIPT_VERBOSE"] === "1" || process.env["PROMPTSCRIPT_VERBOSE"] === "true") {
18976
19794
  setContext({ logLevel: 2 /* Verbose */ });
18977
19795
  }
19796
+ if (actionCommand.name() === "update-check") {
19797
+ return;
19798
+ }
19799
+ const currentVersion = getPackageVersion(__dirname3, "../package.json");
19800
+ const updateInfo = await checkForUpdates(currentVersion);
19801
+ if (updateInfo) {
19802
+ printUpdateNotification(updateInfo);
19803
+ }
18978
19804
  });
18979
19805
  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").option("-m, --migrate", "Install migration skill for AI-assisted migration").action((opts) => initCommand(opts));
18980
19806
  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((opts) => compileCommand(opts));
18981
19807
  program.command("validate").description("Validate PromptScript files").option("--strict", "Treat warnings as errors").option("--format <format>", "Output format (text, json)", "text").action(validateCommand);
18982
19808
  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);
18983
19809
  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);
18984
- program.command("check").description("Check configuration and dependencies health").option("--fix", "Attempt to fix issues").action(checkCommand);
19810
+ program.command("check").description("Check configuration and dependencies health").action(checkCommand);
19811
+ program.command("update-check").description("Check for CLI updates").action(updateCheckCommand);
18985
19812
  function run(args = process.argv) {
18986
19813
  program.parse(args);
18987
19814
  }
@@ -18991,18 +19818,25 @@ export {
18991
19818
  ConsoleOutput,
18992
19819
  LogLevel,
18993
19820
  checkCommand,
19821
+ checkForUpdates,
18994
19822
  compileCommand,
18995
19823
  createSpinner,
18996
19824
  diffCommand,
19825
+ fetchLatestVersion,
18997
19826
  findConfigFile,
19827
+ forceCheckForUpdates,
19828
+ getCacheDir,
19829
+ getCachePath,
18998
19830
  getContext,
18999
19831
  initCommand,
19000
19832
  isQuiet,
19001
19833
  isVerbose,
19002
19834
  loadConfig,
19835
+ printUpdateNotification,
19003
19836
  pullCommand,
19004
19837
  run,
19005
19838
  setContext,
19839
+ updateCheckCommand,
19006
19840
  validateCommand
19007
19841
  };
19008
19842
  /*! Bundled license information: