@vertesia/build-tools 0.80.0 → 0.80.2

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 (40) hide show
  1. package/lib/build-tools.js +1122 -8
  2. package/lib/build-tools.js.map +1 -1
  3. package/lib/cjs/index.js +6 -1
  4. package/lib/cjs/index.js.map +1 -1
  5. package/lib/cjs/plugin.js +14 -6
  6. package/lib/cjs/plugin.js.map +1 -1
  7. package/lib/cjs/presets/index.js +8 -1
  8. package/lib/cjs/presets/index.js.map +1 -1
  9. package/lib/cjs/presets/prompt.js +185 -0
  10. package/lib/cjs/presets/prompt.js.map +1 -0
  11. package/lib/cjs/presets/skill-collection.js +83 -0
  12. package/lib/cjs/presets/skill-collection.js.map +1 -0
  13. package/lib/esm/index.js +1 -1
  14. package/lib/esm/index.js.map +1 -1
  15. package/lib/esm/plugin.js +14 -6
  16. package/lib/esm/plugin.js.map +1 -1
  17. package/lib/esm/presets/index.js +2 -0
  18. package/lib/esm/presets/index.js.map +1 -1
  19. package/lib/esm/presets/prompt.js +181 -0
  20. package/lib/esm/presets/prompt.js.map +1 -0
  21. package/lib/esm/presets/skill-collection.js +77 -0
  22. package/lib/esm/presets/skill-collection.js.map +1 -0
  23. package/lib/types/index.d.ts +1 -1
  24. package/lib/types/index.d.ts.map +1 -1
  25. package/lib/types/plugin.d.ts.map +1 -1
  26. package/lib/types/presets/index.d.ts +2 -0
  27. package/lib/types/presets/index.d.ts.map +1 -1
  28. package/lib/types/presets/prompt.d.ts +63 -0
  29. package/lib/types/presets/prompt.d.ts.map +1 -0
  30. package/lib/types/presets/skill-collection.d.ts +26 -0
  31. package/lib/types/presets/skill-collection.d.ts.map +1 -0
  32. package/lib/types/types.d.ts +2 -0
  33. package/lib/types/types.d.ts.map +1 -1
  34. package/package.json +4 -2
  35. package/src/index.ts +8 -1
  36. package/src/plugin.ts +14 -6
  37. package/src/presets/index.ts +2 -0
  38. package/src/presets/prompt.ts +227 -0
  39. package/src/presets/skill-collection.ts +86 -0
  40. package/src/types.ts +3 -0
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Skill collection transformer for directory-based skill imports
3
+ * Scans a directory for subdirectories containing SKILL.md files
4
+ */
5
+ import { readdirSync, statSync, existsSync } from 'node:fs';
6
+ import path from 'node:path';
7
+ /**
8
+ * Skill collection transformer preset
9
+ * Transforms directory imports with ?skills suffix into an array of skill imports
10
+ *
11
+ * Matches:
12
+ * - ./all?skills (recommended - generates all.js in the directory)
13
+ * - ./_skills?skills (generates _skills.js in the directory)
14
+ * - Any path ending with a filename and ?skills
15
+ *
16
+ * NOTE: A filename before ?skills is REQUIRED to avoid naming conflicts.
17
+ * The filename becomes the output module name.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import skills from './all?skills';
22
+ * // Scans current directory for subdirectories with SKILL.md
23
+ * // Generates all.js containing array of all skills
24
+ * ```
25
+ */
26
+ export const skillCollectionTransformer = {
27
+ pattern: /\/[^/?]+\?skills$/,
28
+ virtual: true, // Indicates this doesn't transform a real file
29
+ transform: (_content, filePath) => {
30
+ // Remove ?skills suffix and the filename to get directory path
31
+ // Example: /path/code/all?skills -> /path/code/all -> /path/code/
32
+ const pathWithoutQuery = filePath.replace(/\?skills$/, '');
33
+ const dirPath = path.dirname(pathWithoutQuery);
34
+ if (!existsSync(dirPath)) {
35
+ throw new Error(`Directory not found: ${dirPath}`);
36
+ }
37
+ if (!statSync(dirPath).isDirectory()) {
38
+ throw new Error(`Not a directory: ${dirPath}`);
39
+ }
40
+ // Scan for subdirectories containing SKILL.md
41
+ const entries = readdirSync(dirPath);
42
+ const imports = [];
43
+ const names = [];
44
+ for (const entry of entries) {
45
+ const entryPath = path.join(dirPath, entry);
46
+ try {
47
+ if (statSync(entryPath).isDirectory()) {
48
+ const skillFile = path.join(entryPath, 'SKILL.md');
49
+ if (existsSync(skillFile)) {
50
+ // Generate unique identifier from directory name
51
+ const identifier = `Skill_${entry.replace(/[^a-zA-Z0-9_]/g, '_')}`;
52
+ imports.push(`import ${identifier} from './${entry}/SKILL.md';`);
53
+ names.push(identifier);
54
+ }
55
+ }
56
+ }
57
+ catch (err) {
58
+ // Skip entries that can't be read
59
+ continue;
60
+ }
61
+ }
62
+ if (names.length === 0) {
63
+ console.warn(`No SKILL.md files found in subdirectories of ${dirPath}`);
64
+ }
65
+ // Generate code that imports all skills and exports as array
66
+ const code = [
67
+ ...imports,
68
+ '',
69
+ `export default [${names.join(', ')}];`
70
+ ].join('\n');
71
+ return {
72
+ data: null, // Not used when custom code is provided
73
+ code
74
+ };
75
+ }
76
+ };
77
+ //# sourceMappingURL=skill-collection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-collection.js","sourceRoot":"","sources":["../../../src/presets/skill-collection.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAsB;IACzD,OAAO,EAAE,mBAAmB;IAC5B,OAAO,EAAE,IAAI,EAAE,+CAA+C;IAC9D,SAAS,EAAE,CAAC,QAAgB,EAAE,QAAgB,EAAE,EAAE;QAC9C,+DAA+D;QAC/D,kEAAkE;QAClE,MAAM,gBAAgB,GAAG,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAE/C,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,wBAAwB,OAAO,EAAE,CAAC,CAAC;QACvD,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,oBAAoB,OAAO,EAAE,CAAC,CAAC;QACnD,CAAC;QAED,8CAA8C;QAC9C,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC1B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YAE5C,IAAI,CAAC;gBACD,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;oBACpC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;oBACnD,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;wBACxB,iDAAiD;wBACjD,MAAM,UAAU,GAAG,SAAS,KAAK,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,EAAE,CAAC;wBACnE,OAAO,CAAC,IAAI,CAAC,UAAU,UAAU,YAAY,KAAK,aAAa,CAAC,CAAC;wBACjE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;oBAC3B,CAAC;gBACL,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACX,kCAAkC;gBAClC,SAAS;YACb,CAAC;QACL,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,IAAI,CAAC,gDAAgD,OAAO,EAAE,CAAC,CAAC;QAC5E,CAAC;QAED,6DAA6D;QAC7D,MAAM,IAAI,GAAG;YACT,GAAG,OAAO;YACV,EAAE;YACF,mBAAmB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;SAC1C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEb,OAAO;YACH,IAAI,EAAE,IAAI,EAAE,wCAAwC;YACpD,IAAI;SACP,CAAC;IACN,CAAC;CACJ,CAAC"}
@@ -19,6 +19,6 @@
19
19
  */
20
20
  export { vertesiaImportPlugin } from './plugin.js';
21
21
  export type { PluginConfig, TransformerRule, TransformerPreset, TransformFunction, TransformResult, AssetFile, WidgetConfig } from './types.js';
22
- export { skillTransformer, rawTransformer, SkillDefinitionSchema, type SkillDefinition, type SkillContentType } from './presets/index.js';
22
+ export { skillTransformer, rawTransformer, skillCollectionTransformer, promptTransformer, SkillDefinitionSchema, PromptDefinitionSchema, type SkillDefinition, type SkillContentType, type PromptDefinition, type PromptContentType, PromptRole, TemplateType } from './presets/index.js';
23
23
  export { parseFrontmatter, type FrontmatterResult } from './parsers/frontmatter.js';
24
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGnD,YAAY,EACR,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,SAAS,EACT,YAAY,EACf,MAAM,YAAY,CAAC;AAGpB,OAAO,EACH,gBAAgB,EAChB,cAAc,EACd,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACxB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGnD,YAAY,EACR,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,SAAS,EACT,YAAY,EACf,MAAM,YAAY,CAAC;AAGpB,OAAO,EACH,gBAAgB,EAChB,cAAc,EACd,0BAA0B,EAC1B,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,UAAU,EACV,YAAY,EACf,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGrC,OAAO,KAAK,EAAE,YAAY,EAA8B,MAAM,YAAY,CAAC;AAK3E;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CA8IjE"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGrC,OAAO,KAAK,EAAE,YAAY,EAA8B,MAAM,YAAY,CAAC;AAK3E;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAsJjE"}
@@ -2,5 +2,7 @@
2
2
  * Preset transformers for common use cases
3
3
  */
4
4
  export { skillTransformer, SkillDefinitionSchema, type SkillDefinition, type SkillContentType } from './skill.js';
5
+ export { skillCollectionTransformer } from './skill-collection.js';
5
6
  export { rawTransformer } from './raw.js';
7
+ export { promptTransformer, PromptDefinitionSchema, type PromptDefinition, type PromptContentType, PromptRole, TemplateType } from './prompt.js';
6
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/presets/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAClH,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/presets/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAClH,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,KAAK,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Prompt transformer preset for template files with frontmatter
3
+ * Supports .jst, .hbs, and plain text files
4
+ */
5
+ import { z } from 'zod';
6
+ import type { TransformerPreset } from '../types.js';
7
+ import { TemplateType } from '@vertesia/common';
8
+ import { PromptRole } from '@llumiverse/common';
9
+ /**
10
+ * Re-export types for backwards compatibility
11
+ */
12
+ export { TemplateType, PromptRole };
13
+ /**
14
+ * Template type alias
15
+ */
16
+ export type PromptContentType = TemplateType;
17
+ /**
18
+ * MUST be kept in sync with @vertesia/common InCodePrompt
19
+ * Zod schema for prompt definition
20
+ */
21
+ export declare const PromptDefinitionSchema: z.ZodObject<{
22
+ role: z.ZodNativeEnum<typeof PromptRole>;
23
+ content: z.ZodString;
24
+ content_type: z.ZodNativeEnum<typeof TemplateType>;
25
+ schema: z.ZodOptional<z.ZodAny>;
26
+ name: z.ZodOptional<z.ZodString>;
27
+ externalId: z.ZodOptional<z.ZodString>;
28
+ }, "strip", z.ZodTypeAny, {
29
+ content_type: TemplateType;
30
+ content: string;
31
+ role: PromptRole;
32
+ schema?: any;
33
+ name?: string | undefined;
34
+ externalId?: string | undefined;
35
+ }, {
36
+ content_type: TemplateType;
37
+ content: string;
38
+ role: PromptRole;
39
+ schema?: any;
40
+ name?: string | undefined;
41
+ externalId?: string | undefined;
42
+ }>;
43
+ /**
44
+ * TypeScript type inferred from the Zod schema
45
+ */
46
+ export type PromptDefinition = z.infer<typeof PromptDefinitionSchema>;
47
+ /**
48
+ * Prompt transformer preset
49
+ * Transforms template files with ?prompt suffix into prompt definition objects
50
+ *
51
+ * Supported file types:
52
+ * - .jst (JavaScript template literals) → content_type: 'jst'
53
+ * - .hbs (Handlebars templates) → content_type: 'handlebars'
54
+ * - .txt or other → content_type: 'text'
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * import PROMPT from './prompt.hbs?prompt';
59
+ * // PROMPT is an InCodePrompt object
60
+ * ```
61
+ */
62
+ export declare const promptTransformer: TransformerPreset;
63
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../src/presets/prompt.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD;;GAEG;AACH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC;AAEpC;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,YAAY,CAAC;AAkB7C;;;GAGG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;EAOjC,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAiGtE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iBAAiB,EAAE,iBA4D/B,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Skill collection transformer for directory-based skill imports
3
+ * Scans a directory for subdirectories containing SKILL.md files
4
+ */
5
+ import type { TransformerPreset } from '../types.js';
6
+ /**
7
+ * Skill collection transformer preset
8
+ * Transforms directory imports with ?skills suffix into an array of skill imports
9
+ *
10
+ * Matches:
11
+ * - ./all?skills (recommended - generates all.js in the directory)
12
+ * - ./_skills?skills (generates _skills.js in the directory)
13
+ * - Any path ending with a filename and ?skills
14
+ *
15
+ * NOTE: A filename before ?skills is REQUIRED to avoid naming conflicts.
16
+ * The filename becomes the output module name.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * import skills from './all?skills';
21
+ * // Scans current directory for subdirectories with SKILL.md
22
+ * // Generates all.js containing array of all skills
23
+ * ```
24
+ */
25
+ export declare const skillCollectionTransformer: TransformerPreset;
26
+ //# sourceMappingURL=skill-collection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-collection.d.ts","sourceRoot":"","sources":["../../../src/presets/skill-collection.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,0BAA0B,EAAE,iBAyDxC,CAAC"}
@@ -46,6 +46,8 @@ export interface TransformerRule {
46
46
  transform: TransformFunction;
47
47
  /** Optional: Zod schema for validation */
48
48
  schema?: z.ZodType<any>;
49
+ /** Optional: If true, the transformer generates virtual modules (no file to read) */
50
+ virtual?: boolean;
49
51
  /** Optional: additional options for this transformer */
50
52
  options?: Record<string, unknown>;
51
53
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,SAAS;IACtB,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAC;IAEnB,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;IAEjB,oCAAoC;IACpC,IAAI,EAAE,QAAQ,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,sDAAsD;IACtD,IAAI,EAAE,OAAO,CAAC;IAEd,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,+CAA+C;IAC/C,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;IAErB,gDAAgD;IAChD,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnD;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC5B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,KACf,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;AAEhD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAEhB,iDAAiD;IACjD,SAAS,EAAE,iBAAiB,CAAC;IAE7B,0CAA0C;IAC1C,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAExB,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAErC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB,0CAA0C;IAC1C,YAAY,EAAE,eAAe,EAAE,CAAC;IAEhC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAE3B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,eAAe,CAAC;AAEhD;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,YAAY,KAAK,MAAM,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,SAAS;IACtB,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAC;IAEnB,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;IAEjB,oCAAoC;IACpC,IAAI,EAAE,QAAQ,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,sDAAsD;IACtD,IAAI,EAAE,OAAO,CAAC;IAEd,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,+CAA+C;IAC/C,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;IAErB,gDAAgD;IAChD,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnD;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC5B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,KACf,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;AAEhD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAEhB,iDAAiD;IACjD,SAAS,EAAE,iBAAiB,CAAC;IAE7B,0CAA0C;IAC1C,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAExB,qFAAqF;IACrF,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAErC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB,0CAA0C;IAC1C,YAAY,EAAE,eAAe,EAAE,CAAC;IAEhC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAE3B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,eAAe,CAAC;AAEhD;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,YAAY,KAAK,MAAM,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertesia/build-tools",
3
- "version": "0.80.0",
3
+ "version": "0.80.2",
4
4
  "description": "Build tools for Vertesia projects - Rollup and Vite plugins for transforming imports, bundling skills, and compiling widgets",
5
5
  "type": "module",
6
6
  "main": "./lib/esm/index.js",
@@ -28,7 +28,9 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "gray-matter": "^4.0.3",
31
- "zod": "^3.24.1"
31
+ "zod": "^3.24.1",
32
+ "@vertesia/common": "0.81.0",
33
+ "@llumiverse/common": "0.24.0"
32
34
  },
33
35
  "peerDependencies": {
34
36
  "rollup": "^4.0.0"
package/src/index.ts CHANGED
@@ -36,9 +36,16 @@ export type {
36
36
  export {
37
37
  skillTransformer,
38
38
  rawTransformer,
39
+ skillCollectionTransformer,
40
+ promptTransformer,
39
41
  SkillDefinitionSchema,
42
+ PromptDefinitionSchema,
40
43
  type SkillDefinition,
41
- type SkillContentType
44
+ type SkillContentType,
45
+ type PromptDefinition,
46
+ type PromptContentType,
47
+ PromptRole,
48
+ TemplateType
42
49
  } from './presets/index.js';
43
50
 
44
51
  // Utilities
package/src/plugin.ts CHANGED
@@ -39,7 +39,13 @@ export function vertesiaImportPlugin(config: PluginConfig): Plugin {
39
39
  // Handle relative imports
40
40
  if (source.startsWith('.') && importer) {
41
41
  const cleanSource = source.replace(transformer.pattern, '');
42
- const resolved = path.resolve(path.dirname(importer), cleanSource);
42
+ // Strip query parameters from importer to get the file path
43
+ const cleanImporter = importer.indexOf('?') >= 0
44
+ ? importer.substring(0, importer.indexOf('?'))
45
+ : importer;
46
+ // Always use dirname to get the directory containing the importer
47
+ const baseDir = path.dirname(cleanImporter);
48
+ const resolved = path.resolve(baseDir, cleanSource);
43
49
  // Return with the pattern suffix to identify it in load
44
50
  const suffix = source.match(transformer.pattern)?.[0] || '';
45
51
  return resolved + suffix;
@@ -75,8 +81,10 @@ export function vertesiaImportPlugin(config: PluginConfig): Plugin {
75
81
  }
76
82
 
77
83
  try {
78
- // Read file content
79
- const content = readFileSync(cleanId, 'utf-8');
84
+ // Read file content (skip for virtual transforms)
85
+ const content = matchedTransformer.virtual
86
+ ? ''
87
+ : readFileSync(cleanId, 'utf-8');
80
88
 
81
89
  // Transform the content
82
90
  const result = await matchedTransformer.transform(content, cleanId);
@@ -105,12 +113,12 @@ export function vertesiaImportPlugin(config: PluginConfig): Plugin {
105
113
  }
106
114
 
107
115
  // Generate code
116
+ const imports = result.imports ? result.imports.join('\n') + '\n\n' : '';
108
117
  if (result.code) {
109
- // Custom code provided
110
- return result.code;
118
+ // Custom code provided - prepend imports
119
+ return imports + result.code;
111
120
  } else {
112
121
  // Default: export data (escape if string, otherwise stringify as JSON)
113
- const imports = result.imports ? result.imports.join('\n') + '\n\n' : '';
114
122
  const dataJson = JSON.stringify(result.data, null, 2);
115
123
  return `${imports}export default ${dataJson};`;
116
124
  }
@@ -3,4 +3,6 @@
3
3
  */
4
4
 
5
5
  export { skillTransformer, SkillDefinitionSchema, type SkillDefinition, type SkillContentType } from './skill.js';
6
+ export { skillCollectionTransformer } from './skill-collection.js';
6
7
  export { rawTransformer } from './raw.js';
8
+ export { promptTransformer, PromptDefinitionSchema, type PromptDefinition, type PromptContentType, PromptRole, TemplateType } from './prompt.js';
@@ -0,0 +1,227 @@
1
+ /**
2
+ * Prompt transformer preset for template files with frontmatter
3
+ * Supports .jst, .hbs, and plain text files
4
+ */
5
+
6
+ import { z } from 'zod';
7
+ import type { TransformerPreset } from '../types.js';
8
+ import { parseFrontmatter } from '../parsers/frontmatter.js';
9
+ import path from 'path';
10
+ import { TemplateType } from '@vertesia/common';
11
+ import { PromptRole } from '@llumiverse/common';
12
+
13
+ /**
14
+ * Re-export types for backwards compatibility
15
+ */
16
+ export { TemplateType, PromptRole };
17
+
18
+ /**
19
+ * Template type alias
20
+ */
21
+ export type PromptContentType = TemplateType;
22
+
23
+ /**
24
+ * Zod schema for prompt frontmatter validation
25
+ */
26
+ const PromptFrontmatterSchema = z.object({
27
+ // Required fields
28
+ role: z.nativeEnum(PromptRole, {
29
+ errorMap: () => ({ message: 'Role must be one of: safety, system, user, assistant, negative' })
30
+ }),
31
+
32
+ // Optional fields
33
+ content_type: z.nativeEnum(TemplateType).optional(),
34
+ schema: z.string().optional(),
35
+ name: z.string().optional(),
36
+ externalId: z.string().optional(),
37
+ }).strict();
38
+
39
+ /**
40
+ * MUST be kept in sync with @vertesia/common InCodePrompt
41
+ * Zod schema for prompt definition
42
+ */
43
+ export const PromptDefinitionSchema = z.object({
44
+ role: z.nativeEnum(PromptRole),
45
+ content: z.string(),
46
+ content_type: z.nativeEnum(TemplateType),
47
+ schema: z.any().optional(),
48
+ name: z.string().optional(),
49
+ externalId: z.string().optional(),
50
+ });
51
+
52
+ /**
53
+ * TypeScript type inferred from the Zod schema
54
+ */
55
+ export type PromptDefinition = z.infer<typeof PromptDefinitionSchema>;
56
+
57
+ /**
58
+ * Normalize schema path for import
59
+ * - Adds './' prefix if not a relative path
60
+ * - Replaces .ts with .js
61
+ * - Adds .js if no extension
62
+ *
63
+ * @param schemaPath - Original schema path from frontmatter
64
+ * @returns Normalized path for ES module import
65
+ */
66
+ function normalizeSchemaPath(schemaPath: string): string {
67
+ let normalized = schemaPath.trim();
68
+
69
+ // Add './' prefix if not already a relative path
70
+ if (!normalized.startsWith('.')) {
71
+ normalized = './' + normalized;
72
+ }
73
+
74
+ // Get the extension
75
+ const ext = path.extname(normalized);
76
+
77
+ if (ext === '.ts') {
78
+ // Replace .ts with .js
79
+ normalized = normalized.slice(0, -3) + '.js';
80
+ } else if (!ext) {
81
+ // No extension, add .js
82
+ normalized = normalized + '.js';
83
+ }
84
+ // If extension is already .js or something else, leave as is
85
+
86
+ return normalized;
87
+ }
88
+
89
+ /**
90
+ * Infer content type from file extension
91
+ *
92
+ * @param filePath - Path to the prompt file
93
+ * @returns Inferred content type
94
+ */
95
+ function inferContentType(filePath: string): TemplateType {
96
+ const ext = path.extname(filePath).toLowerCase();
97
+
98
+ switch (ext) {
99
+ case '.jst':
100
+ return TemplateType.jst;
101
+ case '.hbs':
102
+ return TemplateType.handlebars;
103
+ default:
104
+ return TemplateType.text;
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Build a PromptDefinition from frontmatter and content
110
+ *
111
+ * @param frontmatter - Parsed frontmatter object
112
+ * @param content - Prompt content (body of the file)
113
+ * @param filePath - Path to the prompt file (for content type inference)
114
+ * @returns Prompt definition object and optional imports
115
+ */
116
+ function buildPromptDefinition(
117
+ frontmatter: Record<string, any>,
118
+ content: string,
119
+ filePath: string
120
+ ): { prompt: PromptDefinition; imports?: string[]; schemaImportName?: string } {
121
+ // Determine content type from frontmatter or file extension
122
+ const content_type: TemplateType =
123
+ frontmatter.content_type || inferContentType(filePath);
124
+
125
+ const prompt: PromptDefinition = {
126
+ role: frontmatter.role,
127
+ content,
128
+ content_type,
129
+ };
130
+
131
+ // Add optional fields
132
+ if (frontmatter.name) {
133
+ prompt.name = frontmatter.name;
134
+ }
135
+ if (frontmatter.externalId) {
136
+ prompt.externalId = frontmatter.externalId;
137
+ }
138
+
139
+ // Handle schema import if specified
140
+ let imports: string[] | undefined;
141
+ let schemaImportName: string | undefined;
142
+
143
+ if (frontmatter.schema) {
144
+ const normalizedPath = normalizeSchemaPath(frontmatter.schema);
145
+ schemaImportName = '__promptSchema';
146
+ imports = [`import ${schemaImportName} from '${normalizedPath}';`];
147
+ }
148
+
149
+ return { prompt, imports, schemaImportName };
150
+ }
151
+
152
+ /**
153
+ * Prompt transformer preset
154
+ * Transforms template files with ?prompt suffix into prompt definition objects
155
+ *
156
+ * Supported file types:
157
+ * - .jst (JavaScript template literals) → content_type: 'jst'
158
+ * - .hbs (Handlebars templates) → content_type: 'handlebars'
159
+ * - .txt or other → content_type: 'text'
160
+ *
161
+ * @example
162
+ * ```typescript
163
+ * import PROMPT from './prompt.hbs?prompt';
164
+ * // PROMPT is an InCodePrompt object
165
+ * ```
166
+ */
167
+ export const promptTransformer: TransformerPreset = {
168
+ pattern: /\?prompt$/,
169
+ schema: PromptDefinitionSchema,
170
+ transform: (content: string, filePath: string) => {
171
+ const { frontmatter, content: promptContent } = parseFrontmatter(content);
172
+
173
+ // Validate frontmatter
174
+ const frontmatterValidation = PromptFrontmatterSchema.safeParse(frontmatter);
175
+ if (!frontmatterValidation.success) {
176
+ const errors = frontmatterValidation.error.errors
177
+ .map((err) => {
178
+ const path = err.path.length > 0 ? err.path.join('.') : 'frontmatter';
179
+ return ` - ${path}: ${err.message}`;
180
+ })
181
+ .join('\n');
182
+ throw new Error(
183
+ `Invalid frontmatter in ${filePath}:\n${errors}`
184
+ );
185
+ }
186
+
187
+ // Build prompt definition
188
+ const { prompt, imports, schemaImportName } = buildPromptDefinition(
189
+ frontmatter,
190
+ promptContent,
191
+ filePath
192
+ );
193
+
194
+ // If schema is specified, generate custom code with schema reference
195
+ if (schemaImportName) {
196
+ // Build the code manually to avoid JSON.stringify issues with schema reference
197
+ const lines = [
198
+ 'export default {',
199
+ ` role: "${prompt.role}",`,
200
+ ` content: ${JSON.stringify(prompt.content)},`,
201
+ ` content_type: "${prompt.content_type}",`,
202
+ ` schema: ${schemaImportName}`,
203
+ ];
204
+
205
+ if (prompt.name) {
206
+ lines.splice(4, 0, ` name: ${JSON.stringify(prompt.name)},`);
207
+ }
208
+ if (prompt.externalId) {
209
+ lines.splice(4, 0, ` externalId: ${JSON.stringify(prompt.externalId)},`);
210
+ }
211
+
212
+ lines.push('};');
213
+ const code = lines.join('\n');
214
+
215
+ return {
216
+ data: prompt,
217
+ imports,
218
+ code,
219
+ };
220
+ }
221
+
222
+ // Standard case without schema
223
+ return {
224
+ data: prompt,
225
+ };
226
+ }
227
+ };
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Skill collection transformer for directory-based skill imports
3
+ * Scans a directory for subdirectories containing SKILL.md files
4
+ */
5
+
6
+ import { readdirSync, statSync, existsSync } from 'node:fs';
7
+ import path from 'node:path';
8
+ import type { TransformerPreset } from '../types.js';
9
+
10
+ /**
11
+ * Skill collection transformer preset
12
+ * Transforms directory imports with ?skills suffix into an array of skill imports
13
+ *
14
+ * Matches:
15
+ * - ./all?skills (recommended - generates all.js in the directory)
16
+ * - ./_skills?skills (generates _skills.js in the directory)
17
+ * - Any path ending with a filename and ?skills
18
+ *
19
+ * NOTE: A filename before ?skills is REQUIRED to avoid naming conflicts.
20
+ * The filename becomes the output module name.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * import skills from './all?skills';
25
+ * // Scans current directory for subdirectories with SKILL.md
26
+ * // Generates all.js containing array of all skills
27
+ * ```
28
+ */
29
+ export const skillCollectionTransformer: TransformerPreset = {
30
+ pattern: /\/[^/?]+\?skills$/,
31
+ virtual: true, // Indicates this doesn't transform a real file
32
+ transform: (_content: string, filePath: string) => {
33
+ // Remove ?skills suffix and the filename to get directory path
34
+ // Example: /path/code/all?skills -> /path/code/all -> /path/code/
35
+ const pathWithoutQuery = filePath.replace(/\?skills$/, '');
36
+ const dirPath = path.dirname(pathWithoutQuery);
37
+
38
+ if (!existsSync(dirPath)) {
39
+ throw new Error(`Directory not found: ${dirPath}`);
40
+ }
41
+
42
+ if (!statSync(dirPath).isDirectory()) {
43
+ throw new Error(`Not a directory: ${dirPath}`);
44
+ }
45
+
46
+ // Scan for subdirectories containing SKILL.md
47
+ const entries = readdirSync(dirPath);
48
+ const imports: string[] = [];
49
+ const names: string[] = [];
50
+
51
+ for (const entry of entries) {
52
+ const entryPath = path.join(dirPath, entry);
53
+
54
+ try {
55
+ if (statSync(entryPath).isDirectory()) {
56
+ const skillFile = path.join(entryPath, 'SKILL.md');
57
+ if (existsSync(skillFile)) {
58
+ // Generate unique identifier from directory name
59
+ const identifier = `Skill_${entry.replace(/[^a-zA-Z0-9_]/g, '_')}`;
60
+ imports.push(`import ${identifier} from './${entry}/SKILL.md';`);
61
+ names.push(identifier);
62
+ }
63
+ }
64
+ } catch (err) {
65
+ // Skip entries that can't be read
66
+ continue;
67
+ }
68
+ }
69
+
70
+ if (names.length === 0) {
71
+ console.warn(`No SKILL.md files found in subdirectories of ${dirPath}`);
72
+ }
73
+
74
+ // Generate code that imports all skills and exports as array
75
+ const code = [
76
+ ...imports,
77
+ '',
78
+ `export default [${names.join(', ')}];`
79
+ ].join('\n');
80
+
81
+ return {
82
+ data: null, // Not used when custom code is provided
83
+ code
84
+ };
85
+ }
86
+ };
package/src/types.ts CHANGED
@@ -60,6 +60,9 @@ export interface TransformerRule {
60
60
  /** Optional: Zod schema for validation */
61
61
  schema?: z.ZodType<any>;
62
62
 
63
+ /** Optional: If true, the transformer generates virtual modules (no file to read) */
64
+ virtual?: boolean;
65
+
63
66
  /** Optional: additional options for this transformer */
64
67
  options?: Record<string, unknown>;
65
68
  }