pupt-lib 1.3.5 → 1.3.6

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.
Files changed (32) hide show
  1. package/README.md +24 -345
  2. package/dist/components/structural/Prompt.d.ts.map +1 -1
  3. package/dist/components/structural/Role.d.ts.map +1 -1
  4. package/dist/index.js +788 -129
  5. package/dist/src/api.d.ts +9 -0
  6. package/dist/src/api.d.ts.map +1 -1
  7. package/dist/src/index.d.ts +6 -3
  8. package/dist/src/index.d.ts.map +1 -1
  9. package/dist/src/render.d.ts.map +1 -1
  10. package/dist/src/services/module-loader.d.ts +58 -7
  11. package/dist/src/services/module-loader.d.ts.map +1 -1
  12. package/dist/src/services/prompt-sources/github-prompt-source.d.ts +35 -0
  13. package/dist/src/services/prompt-sources/github-prompt-source.d.ts.map +1 -0
  14. package/dist/src/services/prompt-sources/index.d.ts +8 -0
  15. package/dist/src/services/prompt-sources/index.d.ts.map +1 -0
  16. package/dist/src/services/prompt-sources/local-prompt-source.d.ts +13 -0
  17. package/dist/src/services/prompt-sources/local-prompt-source.d.ts.map +1 -0
  18. package/dist/src/services/prompt-sources/npm-local-prompt-source.d.ts +18 -0
  19. package/dist/src/services/prompt-sources/npm-local-prompt-source.d.ts.map +1 -0
  20. package/dist/src/services/prompt-sources/npm-registry-prompt-source.d.ts +36 -0
  21. package/dist/src/services/prompt-sources/npm-registry-prompt-source.d.ts.map +1 -0
  22. package/dist/src/services/prompt-sources/tar-utils.d.ts +30 -0
  23. package/dist/src/services/prompt-sources/tar-utils.d.ts.map +1 -0
  24. package/dist/src/types/index.d.ts +4 -1
  25. package/dist/src/types/index.d.ts.map +1 -1
  26. package/dist/src/types/module.d.ts +12 -1
  27. package/dist/src/types/module.d.ts.map +1 -1
  28. package/dist/src/types/prompt-source.d.ts +12 -0
  29. package/dist/src/types/prompt-source.d.ts.map +1 -0
  30. package/dist/src/types/render.d.ts +11 -1
  31. package/dist/src/types/render.d.ts.map +1 -1
  32. package/package.json +8 -2
package/dist/index.js CHANGED
@@ -3854,6 +3854,12 @@ function createRuntimeConfig() {
3854
3854
  uuid: crypto.randomUUID()
3855
3855
  };
3856
3856
  }
3857
+ function isWarningCode(code) {
3858
+ return code.startsWith("warn_") || code === "validation_warning";
3859
+ }
3860
+ function isPromptSource(value) {
3861
+ return value !== null && typeof value === "object" && "getPrompts" in value && typeof value.getPrompts === "function";
3862
+ }
3857
3863
  const COMPONENT_MARKER = Symbol.for("pupt-lib:component:v1");
3858
3864
  _a = COMPONENT_MARKER;
3859
3865
  class Component {
@@ -4002,7 +4008,9 @@ async function render(element, options = {}) {
4002
4008
  const {
4003
4009
  inputs = /* @__PURE__ */ new Map(),
4004
4010
  env = DEFAULT_ENVIRONMENT,
4005
- trim = true
4011
+ trim = true,
4012
+ throwOnWarnings = false,
4013
+ ignoreWarnings = []
4006
4014
  } = options;
4007
4015
  const postExecution = [];
4008
4016
  const errors = [];
@@ -4021,13 +4029,26 @@ async function render(element, options = {}) {
4021
4029
  seedAskDefaults(element, context);
4022
4030
  const text = await renderNode(element, state);
4023
4031
  const trimmedText = trim ? text.trim() : text;
4024
- const warnings = errors.filter((e) => e.code === "validation_warning");
4025
- const hardErrors = errors.filter((e) => e.code !== "validation_warning");
4032
+ const ignoreSet = new Set(ignoreWarnings);
4033
+ const warnings = [];
4034
+ const hardErrors = [];
4035
+ for (const err of errors) {
4036
+ if (isWarningCode(err.code)) {
4037
+ if (ignoreSet.has(err.code)) continue;
4038
+ if (throwOnWarnings) {
4039
+ hardErrors.push(err);
4040
+ } else {
4041
+ warnings.push(err);
4042
+ }
4043
+ } else {
4044
+ hardErrors.push(err);
4045
+ }
4046
+ }
4026
4047
  if (hardErrors.length > 0) {
4027
4048
  return {
4028
4049
  ok: false,
4029
4050
  text: trimmedText,
4030
- errors,
4051
+ errors: [...hardErrors, ...warnings],
4031
4052
  postExecution
4032
4053
  };
4033
4054
  }
@@ -43178,6 +43199,49 @@ ${imports}
43178
43199
 
43179
43200
  ${usesSection}${result}`;
43180
43201
  }
43202
+ const CUSTOM_COMPONENTS_GLOBAL = "__PUPT_CUSTOM_COMPONENTS__";
43203
+ async function createPromptFromSource(source, filename, options = {}) {
43204
+ const { components } = options;
43205
+ let processedSource = preprocessSource(source, { filename });
43206
+ if (components && Object.keys(components).length > 0) {
43207
+ globalThis[CUSTOM_COMPONENTS_GLOBAL] = components;
43208
+ const componentNames = Object.keys(components);
43209
+ const extractCode = `const { ${componentNames.join(", ")} } = globalThis.${CUSTOM_COMPONENTS_GLOBAL};
43210
+ `;
43211
+ let lastImportEnd = 0;
43212
+ const fullImportRegex = /import\s+(?:(?:\{[^}]*\}|[^;{]*)\s+from\s+)?['"][^'"]+['"];?/g;
43213
+ let match;
43214
+ while ((match = fullImportRegex.exec(processedSource)) !== null) {
43215
+ const matchEnd = match.index + match[0].length;
43216
+ if (matchEnd > lastImportEnd) {
43217
+ lastImportEnd = matchEnd;
43218
+ }
43219
+ }
43220
+ if (lastImportEnd > 0) {
43221
+ processedSource = processedSource.slice(0, lastImportEnd) + "\n" + extractCode + processedSource.slice(lastImportEnd);
43222
+ } else {
43223
+ processedSource = extractCode + processedSource;
43224
+ }
43225
+ }
43226
+ try {
43227
+ const transformer = new Transformer2();
43228
+ const code = await transformer.transformSourceAsync(processedSource, filename);
43229
+ const module = await evaluateModule(code, { filename });
43230
+ if (module.default === void 0) {
43231
+ throw new Error(`${filename} must have a default export`);
43232
+ }
43233
+ return module.default;
43234
+ } finally {
43235
+ if (components) {
43236
+ delete globalThis[CUSTOM_COMPONENTS_GLOBAL];
43237
+ }
43238
+ }
43239
+ }
43240
+ async function createPrompt(filePath, options = {}) {
43241
+ const { readFile } = await import("fs/promises");
43242
+ const source = await readFile(filePath, "utf-8");
43243
+ return createPromptFromSource(source, filePath, options);
43244
+ }
43181
43245
  const isNode = typeof process !== "undefined" && ((_b = process.versions) == null ? void 0 : _b.node);
43182
43246
  async function resolveLocalPath(source) {
43183
43247
  if (!isNode) {
@@ -43253,7 +43317,7 @@ class ModuleLoader {
43253
43317
  if (this.loading.has(normalizedSource)) {
43254
43318
  return this.loading.get(normalizedSource);
43255
43319
  }
43256
- const promise = this.doLoad(normalizedSource);
43320
+ const promise = this.doLoad(source);
43257
43321
  this.loading.set(normalizedSource, promise);
43258
43322
  try {
43259
43323
  const library = await promise;
@@ -43266,6 +43330,67 @@ class ModuleLoader {
43266
43330
  this.loading.delete(normalizedSource);
43267
43331
  }
43268
43332
  }
43333
+ /**
43334
+ * Load a module entry of any type (string, PromptSource, or package reference).
43335
+ * Dispatches to the appropriate loading strategy based on entry type.
43336
+ */
43337
+ async loadEntry(entry) {
43338
+ if (typeof entry === "string") {
43339
+ return this.load(entry);
43340
+ }
43341
+ if (isPromptSource(entry)) {
43342
+ return this.loadPromptSource(entry);
43343
+ }
43344
+ if (typeof entry === "object" && entry !== null && "source" in entry && "config" in entry) {
43345
+ return this.loadPackageReference(entry);
43346
+ }
43347
+ throw new Error("Invalid module entry: must be a string, PromptSource, or { source, config } object");
43348
+ }
43349
+ /**
43350
+ * Load prompts from a PromptSource instance.
43351
+ */
43352
+ async loadPromptSource(source, name) {
43353
+ var _a2;
43354
+ const prompts = await this.discoverAndCompilePrompts(source);
43355
+ return {
43356
+ name: name ?? ((_a2 = source.constructor) == null ? void 0 : _a2.name) ?? "PromptSource",
43357
+ components: {},
43358
+ prompts,
43359
+ dependencies: []
43360
+ };
43361
+ }
43362
+ /**
43363
+ * Load prompts from a dynamic package reference.
43364
+ * Imports the source module, instantiates its default export with the config,
43365
+ * and calls getPrompts() on it.
43366
+ */
43367
+ async loadPackageReference(ref) {
43368
+ let resolvedSource = ref.source;
43369
+ if (isNode && (ref.source.startsWith("./") || ref.source.startsWith("/") || ref.source.startsWith("../"))) {
43370
+ const path = await import("path");
43371
+ const url = await import("url");
43372
+ const absolutePath = path.resolve(process.cwd(), ref.source);
43373
+ resolvedSource = url.pathToFileURL(absolutePath).href;
43374
+ }
43375
+ const module = await import(
43376
+ /* @vite-ignore */
43377
+ resolvedSource
43378
+ );
43379
+ const SourceClass = module.default;
43380
+ if (typeof SourceClass !== "function") {
43381
+ throw new Error(
43382
+ `Package reference source "${ref.source}" must have a default export that is a class or constructor function`
43383
+ );
43384
+ }
43385
+ const sourceInstance = new SourceClass(ref.config);
43386
+ const prompts = await this.discoverAndCompilePrompts(sourceInstance);
43387
+ return {
43388
+ name: ref.source,
43389
+ components: {},
43390
+ prompts,
43391
+ dependencies: []
43392
+ };
43393
+ }
43269
43394
  /**
43270
43395
  * Detect Component classes in module exports
43271
43396
  */
@@ -43301,9 +43426,25 @@ class ModuleLoader {
43301
43426
  return props !== null && typeof props === "object" && "name" in props;
43302
43427
  }
43303
43428
  /**
43304
- * Normalize a source string for consistent caching
43429
+ * Normalize a source string for consistent deduplication.
43430
+ * Resolves relative paths to absolute, normalizes package names to lowercase.
43305
43431
  */
43306
43432
  normalizeSource(source) {
43433
+ const sourceType = this.resolveSourceType(source);
43434
+ if (sourceType === "local" && isNode) {
43435
+ try {
43436
+ if (source.startsWith("./") || source.startsWith("../")) {
43437
+ const cwd = process.cwd();
43438
+ return `${cwd}/${source}`.replace(/\/\.\//g, "/");
43439
+ }
43440
+ } catch {
43441
+ }
43442
+ }
43443
+ if (sourceType === "npm") {
43444
+ const parsed = this.parsePackageSource(source);
43445
+ const normalizedName = parsed.name.toLowerCase();
43446
+ return parsed.version ? `${normalizedName}@${parsed.version}` : normalizedName;
43447
+ }
43307
43448
  return source;
43308
43449
  }
43309
43450
  /**
@@ -43325,49 +43466,130 @@ class ModuleLoader {
43325
43466
  throw new Error(`Unsupported source type: ${sourceType}`);
43326
43467
  }
43327
43468
  }
43469
+ /**
43470
+ * Discover and compile .prompt files from a PromptSource.
43471
+ * Each file is compiled through createPromptFromSource() and metadata extracted.
43472
+ */
43473
+ async discoverAndCompilePrompts(source) {
43474
+ const files = await source.getPrompts();
43475
+ return this.compilePromptFiles(files);
43476
+ }
43477
+ /**
43478
+ * Compile an array of discovered prompt files into CompiledPrompt records.
43479
+ */
43480
+ async compilePromptFiles(files) {
43481
+ const prompts = {};
43482
+ for (const file of files) {
43483
+ const element = await createPromptFromSource(file.content, file.filename);
43484
+ const props = element[PROPS];
43485
+ const name = (props == null ? void 0 : props.name) ?? file.filename.replace(/\.prompt$/, "");
43486
+ prompts[name] = {
43487
+ element,
43488
+ id: crypto.randomUUID(),
43489
+ name,
43490
+ description: (props == null ? void 0 : props.description) ?? "",
43491
+ tags: (props == null ? void 0 : props.tags) ?? [],
43492
+ version: props == null ? void 0 : props.version
43493
+ };
43494
+ }
43495
+ return prompts;
43496
+ }
43328
43497
  /**
43329
43498
  * Load a local module
43330
43499
  */
43331
43500
  async loadLocal(source) {
43501
+ let components = {};
43502
+ let dependencies = [];
43503
+ let prompts = {};
43332
43504
  try {
43333
43505
  const resolvedPath = await resolveLocalPath(source);
43334
43506
  const module = await import(
43335
43507
  /* @vite-ignore */
43336
43508
  resolvedPath
43337
43509
  );
43338
- return {
43339
- name: this.extractNameFromPath(source),
43340
- components: this.detectComponents(module),
43341
- dependencies: module.dependencies ?? []
43342
- };
43343
- } catch (error) {
43510
+ components = this.detectComponents(module);
43511
+ dependencies = module.dependencies ?? [];
43512
+ } catch {
43513
+ }
43514
+ if (isNode) {
43515
+ try {
43516
+ const { LocalPromptSource: LocalPromptSource2 } = await Promise.resolve().then(() => localPromptSource);
43517
+ const promptSource = new LocalPromptSource2(source);
43518
+ prompts = await this.discoverAndCompilePrompts(promptSource);
43519
+ } catch {
43520
+ }
43521
+ }
43522
+ if (Object.keys(components).length === 0 && Object.keys(prompts).length === 0) {
43344
43523
  throw new Error(
43345
- `Failed to load local module "${source}": ${error instanceof Error ? error.message : String(error)}`
43524
+ `Failed to load local module "${source}": no JS module or .prompt files found`
43346
43525
  );
43347
43526
  }
43527
+ return {
43528
+ name: this.extractNameFromPath(source),
43529
+ components,
43530
+ prompts,
43531
+ dependencies
43532
+ };
43348
43533
  }
43349
43534
  /**
43350
- * Load an npm package
43535
+ * Load an npm package.
43536
+ * Tries to import as a JS module and additionally discovers .prompt files.
43351
43537
  */
43352
43538
  async loadNpm(source) {
43353
43539
  const parsed = this.parsePackageSource(source);
43540
+ let components = {};
43541
+ let dependencies = [];
43542
+ let prompts = {};
43543
+ let jsLoaded = false;
43354
43544
  try {
43355
43545
  const module = await import(parsed.name);
43356
- return {
43357
- name: parsed.name,
43358
- components: this.detectComponents(module),
43359
- dependencies: module.dependencies ?? []
43360
- };
43361
- } catch (error) {
43546
+ components = this.detectComponents(module);
43547
+ dependencies = module.dependencies ?? [];
43548
+ jsLoaded = true;
43549
+ } catch {
43550
+ }
43551
+ if (isNode) {
43552
+ try {
43553
+ const { NpmLocalPromptSource: NpmLocalPromptSource2 } = await Promise.resolve().then(() => npmLocalPromptSource);
43554
+ const promptSource = new NpmLocalPromptSource2(parsed.name);
43555
+ prompts = await this.discoverAndCompilePrompts(promptSource);
43556
+ } catch {
43557
+ }
43558
+ }
43559
+ if (!jsLoaded && Object.keys(prompts).length === 0) {
43362
43560
  throw new Error(
43363
- `Failed to load npm package "${source}": ${error instanceof Error ? error.message : String(error)}`
43561
+ `Failed to load npm package "${source}": no JS module or .prompt files found`
43364
43562
  );
43365
43563
  }
43564
+ return {
43565
+ name: parsed.name,
43566
+ components,
43567
+ prompts,
43568
+ dependencies
43569
+ };
43366
43570
  }
43367
43571
  /**
43368
- * Load a module from a URL
43572
+ * Check if a URL looks like an npm tarball or CDN package URL.
43573
+ */
43574
+ isTarballOrCdnUrl(url) {
43575
+ if (url.endsWith(".tgz")) return true;
43576
+ try {
43577
+ const parsed = new URL(url);
43578
+ const host = parsed.hostname;
43579
+ return host === "cdn.jsdelivr.net" || host === "unpkg.com" || host === "esm.sh" || host === "registry.npmjs.org";
43580
+ } catch {
43581
+ return false;
43582
+ }
43583
+ }
43584
+ /**
43585
+ * Load a module from a URL.
43586
+ * Routes tarball/CDN URLs to NpmRegistryPromptSource for prompt discovery.
43587
+ * Other URLs are loaded as JS modules via dynamic import.
43369
43588
  */
43370
43589
  async loadUrl(source) {
43590
+ if (this.isTarballOrCdnUrl(source)) {
43591
+ return this.loadUrlAsPromptSource(source);
43592
+ }
43371
43593
  try {
43372
43594
  const module = await import(
43373
43595
  /* webpackIgnore: true */
@@ -43376,6 +43598,7 @@ class ModuleLoader {
43376
43598
  return {
43377
43599
  name: this.extractNameFromUrl(source),
43378
43600
  components: this.detectComponents(module),
43601
+ prompts: {},
43379
43602
  dependencies: module.dependencies ?? []
43380
43603
  };
43381
43604
  } catch (error) {
@@ -43385,7 +43608,22 @@ class ModuleLoader {
43385
43608
  }
43386
43609
  }
43387
43610
  /**
43388
- * Load a module from GitHub
43611
+ * Load prompts from a tarball or CDN URL via NpmRegistryPromptSource.
43612
+ */
43613
+ async loadUrlAsPromptSource(source) {
43614
+ const { NpmRegistryPromptSource: NpmRegistryPromptSource2 } = await Promise.resolve().then(() => npmRegistryPromptSource);
43615
+ const promptSource = NpmRegistryPromptSource2.fromUrl(source);
43616
+ const prompts = await this.discoverAndCompilePrompts(promptSource);
43617
+ return {
43618
+ name: this.extractNameFromUrl(source),
43619
+ components: {},
43620
+ prompts,
43621
+ dependencies: []
43622
+ };
43623
+ }
43624
+ /**
43625
+ * Load a module from GitHub.
43626
+ * Tries to import index.js and additionally discovers .prompt files via GitHubPromptSource.
43389
43627
  */
43390
43628
  async loadGithub(source) {
43391
43629
  const match = source.match(/^github:([^/]+)\/([^#]+)(?:#(.+))?$/);
@@ -43393,8 +43631,35 @@ class ModuleLoader {
43393
43631
  throw new Error(`Invalid GitHub source format: ${source}`);
43394
43632
  }
43395
43633
  const [, user, repo, ref] = match;
43396
- const url = `https://raw.githubusercontent.com/${user}/${repo}/${ref ?? "main"}/index.js`;
43397
- return this.loadUrl(url);
43634
+ let components = {};
43635
+ let dependencies = [];
43636
+ let prompts = {};
43637
+ try {
43638
+ const url = `https://raw.githubusercontent.com/${user}/${repo}/${ref ?? "main"}/index.js`;
43639
+ const library = await this.loadUrl(url);
43640
+ components = library.components;
43641
+ dependencies = library.dependencies;
43642
+ } catch {
43643
+ }
43644
+ try {
43645
+ const { GitHubPromptSource: GitHubPromptSource2 } = await Promise.resolve().then(() => githubPromptSource);
43646
+ const ownerRepo = `${user}/${repo}`;
43647
+ const options = ref ? { ref } : void 0;
43648
+ const promptSource = new GitHubPromptSource2(ownerRepo, options);
43649
+ prompts = await this.discoverAndCompilePrompts(promptSource);
43650
+ } catch {
43651
+ }
43652
+ if (Object.keys(components).length === 0 && Object.keys(prompts).length === 0) {
43653
+ throw new Error(
43654
+ `Failed to load GitHub module "${source}": no JS module or .prompt files found`
43655
+ );
43656
+ }
43657
+ return {
43658
+ name: `${user}/${repo}`,
43659
+ components,
43660
+ prompts,
43661
+ dependencies
43662
+ };
43398
43663
  }
43399
43664
  /**
43400
43665
  * Extract a module name from a file path
@@ -43426,6 +43691,376 @@ class ModuleLoader {
43426
43691
  this.versions.clear();
43427
43692
  }
43428
43693
  }
43694
+ class LocalPromptSource {
43695
+ constructor(dirPath) {
43696
+ __publicField(this, "dirPath");
43697
+ this.dirPath = dirPath;
43698
+ }
43699
+ async getPrompts() {
43700
+ const fs = await import("fs/promises");
43701
+ const path = await import("path");
43702
+ const resolvedPath = path.resolve(this.dirPath);
43703
+ try {
43704
+ await fs.access(resolvedPath);
43705
+ } catch {
43706
+ throw new Error(`Directory not found: ${resolvedPath}`);
43707
+ }
43708
+ let scanDir = resolvedPath;
43709
+ const entries = await fs.readdir(resolvedPath);
43710
+ const hasPromptFiles = entries.some((e) => e.endsWith(".prompt"));
43711
+ if (!hasPromptFiles) {
43712
+ const promptsSubdir = path.join(resolvedPath, "prompts");
43713
+ try {
43714
+ await fs.access(promptsSubdir);
43715
+ scanDir = promptsSubdir;
43716
+ } catch {
43717
+ return [];
43718
+ }
43719
+ }
43720
+ const dirEntries = await fs.readdir(scanDir);
43721
+ const promptFiles = dirEntries.filter((e) => e.endsWith(".prompt"));
43722
+ const results = [];
43723
+ for (const filename of promptFiles) {
43724
+ const filePath = path.join(scanDir, filename);
43725
+ const content = await fs.readFile(filePath, "utf-8");
43726
+ results.push({ filename, content });
43727
+ }
43728
+ return results;
43729
+ }
43730
+ }
43731
+ const localPromptSource = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
43732
+ __proto__: null,
43733
+ LocalPromptSource
43734
+ }, Symbol.toStringTag, { value: "Module" }));
43735
+ class NpmLocalPromptSource {
43736
+ constructor(packageName) {
43737
+ __publicField(this, "packageName");
43738
+ this.packageName = packageName;
43739
+ }
43740
+ async getPrompts() {
43741
+ const packageDir = await this.resolvePackagePath(this.packageName);
43742
+ const fs = await import("fs/promises");
43743
+ const path = await import("path");
43744
+ const promptsDir = path.join(packageDir, "prompts");
43745
+ try {
43746
+ await fs.access(promptsDir);
43747
+ } catch {
43748
+ return [];
43749
+ }
43750
+ const entries = await fs.readdir(promptsDir);
43751
+ const promptFiles = entries.filter((e) => e.endsWith(".prompt"));
43752
+ const results = [];
43753
+ for (const filename of promptFiles) {
43754
+ const filePath = path.join(promptsDir, filename);
43755
+ const content = await fs.readFile(filePath, "utf-8");
43756
+ results.push({ filename, content });
43757
+ }
43758
+ return results;
43759
+ }
43760
+ /**
43761
+ * Resolve a bare package name to its directory path in node_modules.
43762
+ * Uses import.meta.resolve() when available, with createRequire fallback.
43763
+ */
43764
+ async resolvePackagePath(packageName) {
43765
+ const path = await import("path");
43766
+ const pkgJsonSpecifier = `${packageName}/package.json`;
43767
+ if (typeof import.meta.resolve === "function") {
43768
+ try {
43769
+ const resolved = import.meta.resolve(pkgJsonSpecifier);
43770
+ const { fileURLToPath } = await import("url");
43771
+ const pkgJsonPath = fileURLToPath(resolved);
43772
+ return path.dirname(pkgJsonPath);
43773
+ } catch {
43774
+ }
43775
+ }
43776
+ try {
43777
+ const { createRequire } = await import("module");
43778
+ const esmRequire = createRequire(import.meta.url);
43779
+ const pkgJsonPath = esmRequire.resolve(pkgJsonSpecifier);
43780
+ return path.dirname(pkgJsonPath);
43781
+ } catch (error) {
43782
+ const message = error instanceof Error ? error.message : String(error);
43783
+ throw new Error(
43784
+ `Cannot resolve npm package "${packageName}": ${message}`
43785
+ );
43786
+ }
43787
+ }
43788
+ }
43789
+ const npmLocalPromptSource = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
43790
+ __proto__: null,
43791
+ NpmLocalPromptSource
43792
+ }, Symbol.toStringTag, { value: "Module" }));
43793
+ function parseGitHubSource(source) {
43794
+ const hashIndex = source.indexOf("#");
43795
+ let ownerRepo = source;
43796
+ let ref;
43797
+ if (hashIndex !== -1) {
43798
+ ownerRepo = source.slice(0, hashIndex);
43799
+ ref = source.slice(hashIndex + 1);
43800
+ }
43801
+ const slashIndex = ownerRepo.indexOf("/");
43802
+ if (slashIndex === -1) {
43803
+ throw new Error(`Invalid GitHub source format: expected "owner/repo", got "${source}"`);
43804
+ }
43805
+ return {
43806
+ owner: ownerRepo.slice(0, slashIndex),
43807
+ repo: ownerRepo.slice(slashIndex + 1),
43808
+ ref
43809
+ };
43810
+ }
43811
+ class GitHubPromptSource {
43812
+ constructor(ownerRepo, options) {
43813
+ __publicField(this, "owner");
43814
+ __publicField(this, "repo");
43815
+ __publicField(this, "ref");
43816
+ __publicField(this, "token");
43817
+ const parsed = parseGitHubSource(ownerRepo);
43818
+ this.owner = parsed.owner;
43819
+ this.repo = parsed.repo;
43820
+ this.ref = (options == null ? void 0 : options.ref) ?? parsed.ref ?? "main";
43821
+ this.token = options == null ? void 0 : options.token;
43822
+ }
43823
+ async getPrompts() {
43824
+ const tree = await this.fetchTree();
43825
+ const promptEntries = this.filterPromptFiles(tree);
43826
+ if (promptEntries.length === 0) {
43827
+ return [];
43828
+ }
43829
+ const results = [];
43830
+ for (const entry of promptEntries) {
43831
+ const content = await this.fetchFileContent(entry.path);
43832
+ const filename = entry.path.split("/").pop();
43833
+ results.push({ filename, content });
43834
+ }
43835
+ return results;
43836
+ }
43837
+ async fetchTree() {
43838
+ var _a2;
43839
+ const url = `https://api.github.com/repos/${this.owner}/${this.repo}/git/trees/${this.ref}?recursive=1`;
43840
+ let response;
43841
+ try {
43842
+ response = await fetch(url, { headers: this.getHeaders() });
43843
+ } catch (error) {
43844
+ throw new Error(
43845
+ `Failed to fetch GitHub tree for ${this.owner}/${this.repo}: ${error instanceof Error ? error.message : String(error)}`
43846
+ );
43847
+ }
43848
+ if (response.status === 403) {
43849
+ const body = await response.json();
43850
+ if ((_a2 = body.message) == null ? void 0 : _a2.toLowerCase().includes("rate limit")) {
43851
+ throw new Error(`GitHub API rate limit exceeded for ${this.owner}/${this.repo}`);
43852
+ }
43853
+ throw new Error(`GitHub API access forbidden for ${this.owner}/${this.repo}: ${body.message ?? "Unknown error"}`);
43854
+ }
43855
+ if (!response.ok) {
43856
+ throw new Error(
43857
+ `Failed to fetch GitHub tree for ${this.owner}/${this.repo}: HTTP ${response.status}`
43858
+ );
43859
+ }
43860
+ const data = await response.json();
43861
+ return data.tree;
43862
+ }
43863
+ filterPromptFiles(tree) {
43864
+ return tree.filter(
43865
+ (entry) => entry.type === "blob" && entry.path.startsWith("prompts/") && entry.path.endsWith(".prompt")
43866
+ );
43867
+ }
43868
+ async fetchFileContent(path) {
43869
+ const url = `https://raw.githubusercontent.com/${this.owner}/${this.repo}/${this.ref}/${path}`;
43870
+ let response;
43871
+ try {
43872
+ response = await fetch(url, { headers: this.getHeaders() });
43873
+ } catch (error) {
43874
+ throw new Error(
43875
+ `Failed to fetch file ${path} from ${this.owner}/${this.repo}: ${error instanceof Error ? error.message : String(error)}`
43876
+ );
43877
+ }
43878
+ if (!response.ok) {
43879
+ throw new Error(
43880
+ `Failed to fetch file ${path} from ${this.owner}/${this.repo}: HTTP ${response.status}`
43881
+ );
43882
+ }
43883
+ return response.text();
43884
+ }
43885
+ getHeaders() {
43886
+ const headers = {
43887
+ "Accept": "application/vnd.github.v3+json"
43888
+ };
43889
+ if (this.token) {
43890
+ headers["Authorization"] = `token ${this.token}`;
43891
+ }
43892
+ return headers;
43893
+ }
43894
+ }
43895
+ const githubPromptSource = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
43896
+ __proto__: null,
43897
+ GitHubPromptSource,
43898
+ parseGitHubSource
43899
+ }, Symbol.toStringTag, { value: "Module" }));
43900
+ async function decompressGzip(compressed) {
43901
+ const stream = new DecompressionStream("gzip");
43902
+ const writer = stream.writable.getWriter();
43903
+ writer.write(new Uint8Array(compressed));
43904
+ writer.close();
43905
+ const reader = stream.readable.getReader();
43906
+ const chunks = [];
43907
+ let totalLength = 0;
43908
+ while (true) {
43909
+ const { done, value } = await reader.read();
43910
+ if (done) break;
43911
+ chunks.push(value);
43912
+ totalLength += value.length;
43913
+ }
43914
+ const result = new Uint8Array(totalLength);
43915
+ let offset = 0;
43916
+ for (const chunk of chunks) {
43917
+ result.set(chunk, offset);
43918
+ offset += chunk.length;
43919
+ }
43920
+ return result.buffer;
43921
+ }
43922
+ function parseTar(buffer) {
43923
+ const data = new Uint8Array(buffer);
43924
+ const entries = [];
43925
+ let offset = 0;
43926
+ while (offset + 512 <= data.length) {
43927
+ const header = data.slice(offset, offset + 512);
43928
+ if (header.every((b) => b === 0)) {
43929
+ break;
43930
+ }
43931
+ const name = readString(header, 0, 100);
43932
+ const sizeStr = readString(header, 124, 12);
43933
+ const size = parseInt(sizeStr, 8) || 0;
43934
+ const typeFlag = header[156];
43935
+ offset += 512;
43936
+ if ((typeFlag === 48 || typeFlag === 0) && size > 0) {
43937
+ const content = data.slice(offset, offset + size);
43938
+ entries.push({ name, size, content });
43939
+ }
43940
+ offset += Math.ceil(size / 512) * 512;
43941
+ }
43942
+ return entries;
43943
+ }
43944
+ function readString(data, offset, maxLength) {
43945
+ let end = offset;
43946
+ while (end < offset + maxLength && data[end] !== 0) {
43947
+ end++;
43948
+ }
43949
+ return new TextDecoder("ascii").decode(data.slice(offset, end)).trim();
43950
+ }
43951
+ async function extractPromptFiles(tarballBuffer) {
43952
+ const decompressed = await decompressGzip(tarballBuffer);
43953
+ const entries = parseTar(decompressed);
43954
+ const promptFiles = [];
43955
+ for (const entry of entries) {
43956
+ const normalizedName = entry.name.replace(/^package\//, "");
43957
+ if (normalizedName.startsWith("prompts/") && normalizedName.endsWith(".prompt")) {
43958
+ const filename = normalizedName.split("/").pop();
43959
+ const content = new TextDecoder("utf-8").decode(entry.content);
43960
+ promptFiles.push({ filename, content });
43961
+ }
43962
+ }
43963
+ return promptFiles;
43964
+ }
43965
+ class NpmRegistryPromptSource {
43966
+ constructor(specifier, options) {
43967
+ __publicField(this, "packageName");
43968
+ __publicField(this, "version");
43969
+ __publicField(this, "tarballUrl");
43970
+ __publicField(this, "registryUrl");
43971
+ this.registryUrl = (options == null ? void 0 : options.registryUrl) ?? "https://registry.npmjs.org";
43972
+ if (specifier.startsWith("https://") || specifier.startsWith("http://")) {
43973
+ this.tarballUrl = specifier;
43974
+ } else {
43975
+ const { name, version } = this.parseSpecifier(specifier);
43976
+ this.packageName = name;
43977
+ this.version = version;
43978
+ }
43979
+ }
43980
+ /**
43981
+ * Create an NpmRegistryPromptSource from a direct tarball URL.
43982
+ */
43983
+ static fromUrl(url) {
43984
+ return new NpmRegistryPromptSource(url);
43985
+ }
43986
+ async getPrompts() {
43987
+ const tarballUrl = this.tarballUrl ?? await this.resolveTarballUrl();
43988
+ const tarballBuffer = await this.downloadTarball(tarballUrl);
43989
+ return extractPromptFiles(tarballBuffer);
43990
+ }
43991
+ /**
43992
+ * Parse a package specifier into name and optional version.
43993
+ */
43994
+ parseSpecifier(specifier) {
43995
+ if (specifier.startsWith("@")) {
43996
+ const atIndex2 = specifier.indexOf("@", 1);
43997
+ if (atIndex2 !== -1) {
43998
+ return {
43999
+ name: specifier.slice(0, atIndex2),
44000
+ version: specifier.slice(atIndex2 + 1)
44001
+ };
44002
+ }
44003
+ return { name: specifier };
44004
+ }
44005
+ const atIndex = specifier.indexOf("@");
44006
+ if (atIndex !== -1) {
44007
+ return {
44008
+ name: specifier.slice(0, atIndex),
44009
+ version: specifier.slice(atIndex + 1)
44010
+ };
44011
+ }
44012
+ return { name: specifier };
44013
+ }
44014
+ /**
44015
+ * Resolve the tarball URL by fetching package metadata from the registry.
44016
+ */
44017
+ async resolveTarballUrl() {
44018
+ var _a2;
44019
+ const version = this.version ?? "latest";
44020
+ const url = `${this.registryUrl}/${this.packageName}/${version}`;
44021
+ let response;
44022
+ try {
44023
+ response = await fetch(url);
44024
+ } catch (error) {
44025
+ const message = error instanceof Error ? error.message : String(error);
44026
+ throw new Error(`Fetch failed for "${this.packageName}@${version}": ${message}`);
44027
+ }
44028
+ if (!response.ok) {
44029
+ throw new Error(
44030
+ `Fetch failed for "${this.packageName}@${version}": HTTP ${response.status} ${response.statusText}`
44031
+ );
44032
+ }
44033
+ const metadata = await response.json();
44034
+ if (!((_a2 = metadata.dist) == null ? void 0 : _a2.tarball)) {
44035
+ throw new Error(
44036
+ `No tarball URL found in registry metadata for "${this.packageName}@${version}"`
44037
+ );
44038
+ }
44039
+ return metadata.dist.tarball;
44040
+ }
44041
+ /**
44042
+ * Download a tarball from the given URL.
44043
+ */
44044
+ async downloadTarball(url) {
44045
+ let response;
44046
+ try {
44047
+ response = await fetch(url);
44048
+ } catch (error) {
44049
+ const message = error instanceof Error ? error.message : String(error);
44050
+ throw new Error(`Fetch failed for tarball at "${url}": ${message}`);
44051
+ }
44052
+ if (!response.ok) {
44053
+ throw new Error(
44054
+ `Fetch failed for tarball at "${url}": HTTP ${response.status} ${response.statusText}`
44055
+ );
44056
+ }
44057
+ return response.arrayBuffer();
44058
+ }
44059
+ }
44060
+ const npmRegistryPromptSource = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
44061
+ __proto__: null,
44062
+ NpmRegistryPromptSource
44063
+ }, Symbol.toStringTag, { value: "Module" }));
43429
44064
  const CDN_TEMPLATES = {
43430
44065
  "esm.sh": "https://esm.sh/{name}@{version}",
43431
44066
  "unpkg": "https://unpkg.com/{name}@{version}",
@@ -43515,18 +44150,7 @@ class Role extends Component {
43515
44150
  children
43516
44151
  } = props;
43517
44152
  if (this.hasContent(children)) {
43518
- const parts2 = [children];
43519
- if (expertise) {
43520
- const expertiseStr = Array.isArray(expertise) ? expertise.join(", ") : expertise;
43521
- parts2.push(`
43522
- with expertise in ${expertiseStr}`);
43523
- }
43524
- if (domain) {
43525
- parts2.push(`
43526
- specializing in the ${domain} domain`);
43527
- }
43528
- const content = parts2.length === 1 ? parts2[0] : parts2;
43529
- return wrapWithDelimiter(content, "role", delimiter);
44153
+ return wrapWithDelimiter(children, "role", delimiter);
43530
44154
  }
43531
44155
  const config = preset ? ROLE_PRESETS[preset] : void 0;
43532
44156
  const provider = this.getProvider(context);
@@ -43962,6 +44586,37 @@ class References extends Component {
43962
44586
  }
43963
44587
  }
43964
44588
  __publicField(References, "schema", referencesSchema);
44589
+ const chainOfThoughtSchema = objectType({
44590
+ style: enumType(["step-by-step", "think-aloud", "structured", "minimal"]).optional(),
44591
+ showReasoning: booleanType().optional(),
44592
+ delimiter: enumType(["xml", "markdown", "none"]).optional()
44593
+ }).passthrough();
44594
+ const STYLE_INSTRUCTIONS = {
44595
+ "step-by-step": "Think through this step by step before providing your answer.",
44596
+ "think-aloud": "Reason through your thought process as you work on this.",
44597
+ "structured": "Break down your reasoning into: 1) Understanding, 2) Analysis, 3) Conclusion.",
44598
+ "minimal": "Consider the problem carefully before answering."
44599
+ };
44600
+ class ChainOfThought extends Component {
44601
+ render(props, _resolvedValue, _context) {
44602
+ const { style = "step-by-step", showReasoning = true, delimiter = "xml", children } = props;
44603
+ const sections = [];
44604
+ if (this.hasContent(children)) {
44605
+ const parts = [children];
44606
+ if (showReasoning) {
44607
+ parts.push("\nShow your reasoning process.");
44608
+ }
44609
+ return wrapWithDelimiter(parts, "reasoning", delimiter);
44610
+ }
44611
+ sections.push(STYLE_INSTRUCTIONS[style]);
44612
+ if (showReasoning) {
44613
+ sections.push("Show your reasoning process.");
44614
+ }
44615
+ const content = sections.join("\n");
44616
+ return wrapWithDelimiter(content, "reasoning", delimiter);
44617
+ }
44618
+ }
44619
+ __publicField(ChainOfThought, "schema", chainOfThoughtSchema);
43965
44620
  const promptDefaultsObjectSchema = objectType({
43966
44621
  role: booleanType().optional(),
43967
44622
  format: booleanType().optional(),
@@ -44028,7 +44683,7 @@ class Prompt extends Component {
44028
44683
  component: "Prompt",
44029
44684
  prop: null,
44030
44685
  message: "Prompt has no Task child. Consider adding a <Task> element to define the objective.",
44031
- code: "validation_warning",
44686
+ code: "warn_missing_task",
44032
44687
  path: []
44033
44688
  });
44034
44689
  }
@@ -44039,6 +44694,23 @@ class Prompt extends Component {
44039
44694
  findChildrenOfType(childArray, EdgeCases).length > 0;
44040
44695
  findChildrenOfType(childArray, Fallbacks).length > 0;
44041
44696
  findChildrenOfType(childArray, References).length > 0;
44697
+ if (hasFormat) {
44698
+ const formatElements = findChildrenOfType(childArray, Format);
44699
+ const cotElements = findChildrenOfType(childArray, ChainOfThought);
44700
+ if (cotElements.length > 0) {
44701
+ const formatIsStrict = formatElements.some((el) => el[PROPS].strict === true);
44702
+ const cotShowsReasoning = cotElements.some((el) => el[PROPS].showReasoning !== false);
44703
+ if (formatIsStrict && cotShowsReasoning) {
44704
+ context.errors.push({
44705
+ component: "Prompt",
44706
+ prop: null,
44707
+ message: "<Format strict> and <ChainOfThought showReasoning> produce contradictory instructions. Format strict tells the LLM to return ONLY formatted output, while ChainOfThought asks it to show reasoning. Consider setting showReasoning={false} or removing strict.",
44708
+ code: "warn_conflicting_instructions",
44709
+ path: []
44710
+ });
44711
+ }
44712
+ }
44713
+ }
44042
44714
  const sections = [];
44043
44715
  const includeRole = this.shouldIncludeSection(resolvedDefaults.role, promptConfig.includeRole);
44044
44716
  if (includeRole && !hasRole) {
@@ -45674,37 +46346,6 @@ class Steps extends Component {
45674
46346
  }
45675
46347
  }
45676
46348
  __publicField(Steps, "schema", stepsSchema);
45677
- const chainOfThoughtSchema = objectType({
45678
- style: enumType(["step-by-step", "think-aloud", "structured", "minimal"]).optional(),
45679
- showReasoning: booleanType().optional(),
45680
- delimiter: enumType(["xml", "markdown", "none"]).optional()
45681
- }).passthrough();
45682
- const STYLE_INSTRUCTIONS = {
45683
- "step-by-step": "Think through this step by step before providing your answer.",
45684
- "think-aloud": "Reason through your thought process as you work on this.",
45685
- "structured": "Break down your reasoning into: 1) Understanding, 2) Analysis, 3) Conclusion.",
45686
- "minimal": "Consider the problem carefully before answering."
45687
- };
45688
- class ChainOfThought extends Component {
45689
- render(props, _resolvedValue, _context) {
45690
- const { style = "step-by-step", showReasoning = true, delimiter = "xml", children } = props;
45691
- const sections = [];
45692
- if (this.hasContent(children)) {
45693
- const parts = [children];
45694
- if (showReasoning) {
45695
- parts.push("\nShow your reasoning process.");
45696
- }
45697
- return wrapWithDelimiter(parts, "reasoning", delimiter);
45698
- }
45699
- sections.push(STYLE_INSTRUCTIONS[style]);
45700
- if (showReasoning) {
45701
- sections.push("Show your reasoning process.");
45702
- }
45703
- const content = sections.join("\n");
45704
- return wrapWithDelimiter(content, "reasoning", delimiter);
45705
- }
45706
- }
45707
- __publicField(ChainOfThought, "schema", chainOfThoughtSchema);
45708
46349
  const codeSchema = objectType({
45709
46350
  language: stringType().optional(),
45710
46351
  filename: stringType().optional()
@@ -45981,49 +46622,6 @@ const _allComponentExports = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Objec
45981
46622
  WhenUncertain,
45982
46623
  Xml
45983
46624
  }, Symbol.toStringTag, { value: "Module" }));
45984
- const CUSTOM_COMPONENTS_GLOBAL = "__PUPT_CUSTOM_COMPONENTS__";
45985
- async function createPromptFromSource(source, filename, options = {}) {
45986
- const { components } = options;
45987
- let processedSource = preprocessSource(source, { filename });
45988
- if (components && Object.keys(components).length > 0) {
45989
- globalThis[CUSTOM_COMPONENTS_GLOBAL] = components;
45990
- const componentNames = Object.keys(components);
45991
- const extractCode = `const { ${componentNames.join(", ")} } = globalThis.${CUSTOM_COMPONENTS_GLOBAL};
45992
- `;
45993
- let lastImportEnd = 0;
45994
- const fullImportRegex = /import\s+(?:(?:\{[^}]*\}|[^;{]*)\s+from\s+)?['"][^'"]+['"];?/g;
45995
- let match;
45996
- while ((match = fullImportRegex.exec(processedSource)) !== null) {
45997
- const matchEnd = match.index + match[0].length;
45998
- if (matchEnd > lastImportEnd) {
45999
- lastImportEnd = matchEnd;
46000
- }
46001
- }
46002
- if (lastImportEnd > 0) {
46003
- processedSource = processedSource.slice(0, lastImportEnd) + "\n" + extractCode + processedSource.slice(lastImportEnd);
46004
- } else {
46005
- processedSource = extractCode + processedSource;
46006
- }
46007
- }
46008
- try {
46009
- const transformer = new Transformer2();
46010
- const code = await transformer.transformSourceAsync(processedSource, filename);
46011
- const module = await evaluateModule(code, { filename });
46012
- if (module.default === void 0) {
46013
- throw new Error(`${filename} must have a default export`);
46014
- }
46015
- return module.default;
46016
- } finally {
46017
- if (components) {
46018
- delete globalThis[CUSTOM_COMPONENTS_GLOBAL];
46019
- }
46020
- }
46021
- }
46022
- async function createPrompt(filePath, options = {}) {
46023
- const { readFile } = await import("fs/promises");
46024
- const source = await readFile(filePath, "utf-8");
46025
- return createPromptFromSource(source, filePath, options);
46026
- }
46027
46625
  const ENTRIES = "ENTRIES";
46028
46626
  const KEYS = "KEYS";
46029
46627
  const VALUES = "VALUES";
@@ -47903,6 +48501,7 @@ class Pupt {
47903
48501
  __publicField(this, "moduleLoader", new ModuleLoader());
47904
48502
  __publicField(this, "searchEngine");
47905
48503
  __publicField(this, "prompts", []);
48504
+ __publicField(this, "warnings", []);
47906
48505
  __publicField(this, "initialized", false);
47907
48506
  this.config = config;
47908
48507
  this.searchEngine = createSearchEngine(config.searchConfig);
@@ -47929,6 +48528,9 @@ class Pupt {
47929
48528
  getPrompt(name) {
47930
48529
  return this.prompts.find((p) => p.name === name);
47931
48530
  }
48531
+ getPromptById(id) {
48532
+ return this.prompts.find((p) => p.id === id);
48533
+ }
47932
48534
  searchPrompts(query, options) {
47933
48535
  return this.searchEngine.search(query, options);
47934
48536
  }
@@ -47938,31 +48540,80 @@ class Pupt {
47938
48540
  getPromptsByTag(tag) {
47939
48541
  return this.prompts.filter((p) => p.tags.includes(tag));
47940
48542
  }
48543
+ getWarnings() {
48544
+ return [...this.warnings];
48545
+ }
47941
48546
  async discoverPrompts() {
48547
+ var _a2;
47942
48548
  const discovered = [];
47943
- for (const source of this.config.modules ?? []) {
47944
- const library = await this.moduleLoader.load(source);
48549
+ const processedLibraries = /* @__PURE__ */ new Set();
48550
+ for (const entry of this.config.modules ?? []) {
47945
48551
  try {
47946
- const moduleExports = await this.loadModuleExports(source);
47947
- for (const [, value] of Object.entries(moduleExports)) {
47948
- if (this.isPromptElement(value)) {
47949
- const element = value;
47950
- const props = element[PROPS];
47951
- const prompt = this.createDiscoveredPrompt(
47952
- props.name,
47953
- props.description ?? "",
47954
- props.tags ?? [],
47955
- library.name,
47956
- element
47957
- );
47958
- discovered.push(prompt);
48552
+ const dedupKey = this.getEntryDedupKey(entry);
48553
+ if (dedupKey !== null) {
48554
+ if (processedLibraries.has(dedupKey)) {
48555
+ continue;
47959
48556
  }
48557
+ processedLibraries.add(dedupKey);
48558
+ }
48559
+ const library = await this.moduleLoader.loadEntry(entry);
48560
+ for (const compiled of Object.values(library.prompts)) {
48561
+ const prompt = this.createDiscoveredPrompt(
48562
+ compiled.id,
48563
+ compiled.name,
48564
+ compiled.description,
48565
+ compiled.tags,
48566
+ library.name,
48567
+ compiled.element
48568
+ );
48569
+ discovered.push(prompt);
47960
48570
  }
47961
- } catch {
48571
+ if (typeof entry === "string" && !isPromptSource(entry)) {
48572
+ try {
48573
+ const moduleExports = await this.loadModuleExports(entry);
48574
+ for (const [, value] of Object.entries(moduleExports)) {
48575
+ if (this.isPromptElement(value)) {
48576
+ const element = value;
48577
+ const props = element[PROPS];
48578
+ const prompt = this.createDiscoveredPrompt(
48579
+ crypto.randomUUID(),
48580
+ props.name,
48581
+ props.description ?? "",
48582
+ props.tags ?? [],
48583
+ library.name,
48584
+ element
48585
+ );
48586
+ discovered.push(prompt);
48587
+ }
48588
+ }
48589
+ } catch {
48590
+ }
48591
+ }
48592
+ } catch (error) {
48593
+ const sourceId = typeof entry === "string" ? entry : isPromptSource(entry) ? ((_a2 = entry.constructor) == null ? void 0 : _a2.name) ?? "PromptSource" : `{ source: ${entry.source} }`;
48594
+ const message = error instanceof Error ? error.message : String(error);
48595
+ this.warnings.push(`Failed to load module "${sourceId}": ${message}`);
47962
48596
  }
47963
48597
  }
47964
48598
  return discovered;
47965
48599
  }
48600
+ /**
48601
+ * Get a deduplication key for a module entry.
48602
+ * Returns null for PromptSource instances (cannot be deduplicated).
48603
+ */
48604
+ getEntryDedupKey(entry) {
48605
+ if (typeof entry === "string") {
48606
+ return this.moduleLoader.normalizeSource(entry);
48607
+ }
48608
+ if (isPromptSource(entry)) {
48609
+ return null;
48610
+ }
48611
+ if (typeof entry === "object" && entry !== null && "source" in entry && "config" in entry) {
48612
+ const ref = entry;
48613
+ return `pkg:${ref.source}:${JSON.stringify(ref.config)}`;
48614
+ }
48615
+ return null;
48616
+ }
47966
48617
  async loadModuleExports(source) {
47967
48618
  var _a2;
47968
48619
  const isNode2 = typeof process !== "undefined" && ((_a2 = process.versions) == null ? void 0 : _a2.node);
@@ -47985,8 +48636,9 @@ class Pupt {
47985
48636
  const props = value[PROPS];
47986
48637
  return props !== null && typeof props === "object" && "name" in props;
47987
48638
  }
47988
- createDiscoveredPrompt(name, description, tags, library, element) {
48639
+ createDiscoveredPrompt(id, name, description, tags, library, element) {
47989
48640
  return {
48641
+ id,
47990
48642
  name,
47991
48643
  description,
47992
48644
  tags,
@@ -48348,14 +49000,18 @@ export {
48348
49000
  FileSearchEngine,
48349
49001
  ForEach,
48350
49002
  Format,
49003
+ GitHubPromptSource,
48351
49004
  Guardrails,
48352
49005
  Hostname,
48353
49006
  If,
48354
49007
  Json,
48355
49008
  LANGUAGE_CONVENTIONS,
48356
49009
  LLM_PROVIDERS,
49010
+ LocalPromptSource,
48357
49011
  ModuleLoader,
48358
49012
  NegativeExample,
49013
+ NpmLocalPromptSource,
49014
+ NpmRegistryPromptSource,
48359
49015
  Objective,
48360
49016
  OpenUrl,
48361
49017
  PROPS,
@@ -48416,8 +49072,11 @@ export {
48416
49072
  isDeferredRef,
48417
49073
  isElementOfType,
48418
49074
  isPromptFile,
49075
+ isPromptSource,
48419
49076
  isPuptElement,
49077
+ isWarningCode,
48420
49078
  loadNodeModules,
49079
+ parseGitHubSource,
48421
49080
  partitionChildren,
48422
49081
  preprocessSource,
48423
49082
  render,