@funkai/prompts 0.3.0 → 0.4.1

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/.turbo/turbo-build.log +13 -13
  2. package/CHANGELOG.md +28 -0
  3. package/README.md +3 -3
  4. package/dist/lib/cli.d.mts +2 -2
  5. package/dist/lib/cli.mjs +1 -1
  6. package/dist/lib/{engine-CQfJbVz6.d.mts → engine-BAYeiay3.d.mts} +2 -2
  7. package/dist/lib/{engine-CQfJbVz6.d.mts.map → engine-BAYeiay3.d.mts.map} +1 -1
  8. package/dist/lib/{engine-CPKs9QbX.mjs → engine-Bhv5Zxdu.mjs} +3 -1
  9. package/dist/lib/{engine-CPKs9QbX.mjs.map → engine-Bhv5Zxdu.mjs.map} +1 -1
  10. package/dist/lib/index.d.mts +59 -2
  11. package/dist/lib/index.d.mts.map +1 -1
  12. package/dist/lib/index.mjs +84 -4
  13. package/dist/lib/index.mjs.map +1 -1
  14. package/dist/lib/runtime.d.mts +1 -1
  15. package/dist/lib/runtime.mjs +1 -1
  16. package/dist/lib/{types-2PI_9h-M.d.mts → types-DmnHC99v.d.mts} +25 -3
  17. package/dist/lib/types-DmnHC99v.d.mts.map +1 -0
  18. package/docs/cli/commands.md +8 -8
  19. package/docs/cli/overview.md +1 -1
  20. package/docs/cli.md +132 -0
  21. package/docs/codegen.md +142 -0
  22. package/docs/file-format/overview.md +1 -1
  23. package/docs/file-format.md +306 -0
  24. package/docs/guides/author-prompt.md +2 -2
  25. package/docs/guides/setup-project.md +1 -1
  26. package/docs/overview.md +9 -36
  27. package/docs/setup.md +76 -0
  28. package/docs/troubleshooting.md +1 -1
  29. package/package.json +2 -2
  30. package/src/engine.ts +2 -0
  31. package/src/group.test.ts +89 -0
  32. package/src/group.ts +36 -0
  33. package/src/index.ts +3 -1
  34. package/src/prompt.test.ts +90 -0
  35. package/src/prompt.ts +60 -0
  36. package/src/registry.test.ts +10 -20
  37. package/src/registry.ts +11 -11
  38. package/src/types.ts +24 -1
  39. package/tsconfig.json +12 -5
  40. package/dist/lib/types-2PI_9h-M.d.mts.map +0 -1
@@ -1,5 +1,5 @@
1
1
 
2
- > @funkai/prompts@0.3.0 build /home/runner/work/funkai/funkai/packages/prompts
2
+ > @funkai/prompts@0.4.1 build /home/runner/work/funkai/funkai/packages/prompts
3
3
  > tsdown && cp -r src/prompts dist/prompts
4
4
 
5
5
  ℹ tsdown v0.21.4 powered by rolldown v1.0.0-rc.9
@@ -8,21 +8,21 @@
8
8
  ℹ target: node22
9
9
  ℹ tsconfig: tsconfig.json
10
10
  ℹ Build start
11
- ℹ dist/lib/index.mjs 1.70 kB │ gzip: 0.82 kB
11
+ ℹ dist/lib/index.mjs 3.99 kB │ gzip: 1.56 kB
12
12
  ℹ dist/lib/cli.mjs 1.39 kB │ gzip: 0.75 kB
13
13
  ℹ dist/lib/runtime.mjs 0.08 kB │ gzip: 0.08 kB
14
- ℹ dist/lib/index.mjs.map 2.71 kB │ gzip: 1.16 kB
14
+ ℹ dist/lib/index.mjs.map 6.42 kB │ gzip: 2.30 kB
15
15
  ℹ dist/lib/cli.mjs.map 1.86 kB │ gzip: 0.90 kB
16
- ℹ dist/lib/engine-CPKs9QbX.mjs.map 1.67 kB │ gzip: 0.85 kB
17
- ℹ dist/lib/engine-CPKs9QbX.mjs 1.17 kB │ gzip: 0.62 kB
18
- ℹ dist/lib/types-2PI_9h-M.d.mts.map 0.53 kB │ gzip: 0.29 kB
19
- ℹ dist/lib/engine-CQfJbVz6.d.mts.map 0.22 kB │ gzip: 0.18 kB
20
- ℹ dist/lib/index.d.mts.map 0.19 kB │ gzip: 0.15 kB
16
+ ℹ dist/lib/engine-Bhv5Zxdu.mjs.map 1.75 kB │ gzip: 0.87 kB
17
+ ℹ dist/lib/engine-Bhv5Zxdu.mjs 1.21 kB │ gzip: 0.64 kB
18
+ ℹ dist/lib/types-DmnHC99v.d.mts.map 0.66 kB │ gzip: 0.34 kB
19
+ ℹ dist/lib/index.d.mts.map 0.45 kB │ gzip: 0.25 kB
20
+ ℹ dist/lib/engine-BAYeiay3.d.mts.map 0.22 kB │ gzip: 0.17 kB
21
21
  ℹ dist/lib/cli.d.mts.map 0.18 kB │ gzip: 0.15 kB
22
- ℹ dist/lib/index.d.mts 1.04 kB │ gzip: 0.54 kB
22
+ ℹ dist/lib/index.d.mts 3.02 kB │ gzip: 1.14 kB
23
23
  ℹ dist/lib/cli.d.mts 0.94 kB │ gzip: 0.53 kB
24
24
  ℹ dist/lib/runtime.d.mts 0.08 kB │ gzip: 0.08 kB
25
- ℹ dist/lib/types-2PI_9h-M.d.mts 1.43 kB │ gzip: 0.69 kB
26
- ℹ dist/lib/engine-CQfJbVz6.d.mts 1.08 kB │ gzip: 0.58 kB
27
- ℹ 16 files, total: 16.28 kB
28
- ✔ Build complete in 1612ms
25
+ ℹ dist/lib/types-DmnHC99v.d.mts 2.17 kB │ gzip: 0.94 kB
26
+ ℹ dist/lib/engine-BAYeiay3.d.mts 1.08 kB │ gzip: 0.58 kB
27
+ ℹ 16 files, total: 25.52 kB
28
+ ✔ Build complete in 2824ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @funkai/prompts
2
2
 
3
+ ## 0.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - ef51bd7: fix(packages/prompts): enable strictVariables, fix deepFreeze mutation, add @throws docs
8
+
9
+ - Add `strictVariables: true` to shared liquid engine — template/schema mismatches now throw instead of silently rendering empty strings
10
+ - Fix `deepFreeze` to shallow-copy nested namespace objects before freezing (previously mutated caller references)
11
+ - Replace `for...of` loop in `deepFreeze` with `Object.entries().reduce()`
12
+ - Add `@throws {ZodError}` documentation to `render()` and `validate()` methods
13
+
14
+ ## 0.4.0
15
+
16
+ ### Minor Changes
17
+
18
+ - c8569db: feat(prompts): add createPrompt, createPromptGroup, and config-based group assignment
19
+
20
+ - Add `createPrompt<T>(config)` factory for building prompt modules at runtime and codegen
21
+ - Add `createPromptGroup(name, prompts)` for grouping prompt modules into namespaces
22
+ - Add `PromptConfig<T>` type for prompt module configuration
23
+ - Codegen now uses `createPrompt()` instead of raw object literals
24
+ - Scope name uniqueness to group+name instead of global name
25
+ - Derive file slugs and import names from group+name to avoid collisions
26
+ - Replace `roots` config field with `includes`/`excludes` glob patterns
27
+ - Add `groups` config field for pattern-based group assignment via picomatch
28
+ - Frontmatter `group` takes precedence over config-defined groups
29
+ - Updated banner format for generated files
30
+
3
31
  ## 0.3.0
4
32
 
5
33
  ### Minor Changes
package/README.md CHANGED
@@ -39,7 +39,7 @@ You are a {{ tone }} writer.
39
39
  ### Generate typed modules
40
40
 
41
41
  ```bash
42
- npx funkai prompts generate --out .prompts/client --roots src/agents
42
+ npx funkai prompts generate --out .prompts/client --includes "src/agents/**"
43
43
  ```
44
44
 
45
45
  ### Consume prompts
@@ -94,8 +94,8 @@ Use `{% render 'name', key: 'value' %}` to include shared partials. Partials res
94
94
 
95
95
  ## Documentation
96
96
 
97
- For comprehensive documentation, see [docs/overview.md](docs/overview.md).
97
+ For comprehensive documentation, see the [Prompts concept](/concepts/prompts) and [Prompts CLI reference](/reference/prompts/cli).
98
98
 
99
99
  ## License
100
100
 
101
- [MIT](../../LICENSE)
101
+ [MIT](https://github.com/joggrdocs/funkai/blob/main/LICENSE)
@@ -1,5 +1,5 @@
1
- import { n as Liquid, t as CreateEngineOptions } from "./types-2PI_9h-M.mjs";
2
- import { t as createEngine } from "./engine-CQfJbVz6.mjs";
1
+ import { n as Liquid, t as CreateEngineOptions } from "./types-DmnHC99v.mjs";
2
+ import { t as createEngine } from "./engine-BAYeiay3.mjs";
3
3
 
4
4
  //#region src/clean.d.ts
5
5
  /**
package/dist/lib/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as createEngine } from "./engine-CPKs9QbX.mjs";
1
+ import { t as createEngine } from "./engine-Bhv5Zxdu.mjs";
2
2
  import { flow } from "es-toolkit";
3
3
  import { dirname, resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
@@ -1,4 +1,4 @@
1
- import { t as CreateEngineOptions } from "./types-2PI_9h-M.mjs";
1
+ import { t as CreateEngineOptions } from "./types-DmnHC99v.mjs";
2
2
  import { Liquid } from "liquidjs";
3
3
 
4
4
  //#region src/engine.d.ts
@@ -25,4 +25,4 @@ declare function createEngine(partialsDir: string, options?: Partial<CreateEngin
25
25
  declare const liquidEngine: Liquid;
26
26
  //#endregion
27
27
  export { liquidEngine as n, createEngine as t };
28
- //# sourceMappingURL=engine-CQfJbVz6.d.mts.map
28
+ //# sourceMappingURL=engine-BAYeiay3.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"engine-CQfJbVz6.d.mts","names":[],"sources":["../../src/engine.ts"],"mappings":";;;;;;AAcA;;;;;;;;iBAAgB,YAAA,CAAa,WAAA,UAAqB,OAAA,GAAU,OAAA,CAAQ,mBAAA,IAAuB,MAAA;;;;;;;AAsB3F;;;cAAa,YAAA,EAAY,MAAA"}
1
+ {"version":3,"file":"engine-BAYeiay3.d.mts","names":[],"sources":["../../src/engine.ts"],"mappings":";;;;;;AAcA;;;;;;;;iBAAgB,YAAA,CAAa,WAAA,UAAqB,OAAA,GAAU,OAAA,CAAQ,mBAAA,IAAuB,MAAA;;;;;;;AAuB3F;;;cAAa,YAAA,EAAY,MAAA"}
@@ -18,6 +18,7 @@ function createEngine(partialsDir, options) {
18
18
  cache: true,
19
19
  ...options,
20
20
  strictFilters: true,
21
+ strictVariables: true,
21
22
  ownPropertyOnly: true
22
23
  });
23
24
  }
@@ -32,9 +33,10 @@ function createEngine(partialsDir, options) {
32
33
  */
33
34
  const liquidEngine = new Liquid({
34
35
  strictFilters: true,
36
+ strictVariables: true,
35
37
  ownPropertyOnly: true
36
38
  });
37
39
  //#endregion
38
40
  export { liquidEngine as n, createEngine as t };
39
41
 
40
- //# sourceMappingURL=engine-CPKs9QbX.mjs.map
42
+ //# sourceMappingURL=engine-Bhv5Zxdu.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"engine-CPKs9QbX.mjs","names":[],"sources":["../../src/engine.ts"],"sourcesContent":["import { Liquid } from \"liquidjs\";\n\nimport type { CreateEngineOptions } from \"./types.js\";\n\n/**\n * Create a LiquidJS engine with custom options.\n *\n * The `partialsDir` is used as the root for `{% render %}` resolution.\n * The `.prompt` extension is appended automatically.\n *\n * @param partialsDir - Root directory for `{% render %}` partial resolution.\n * @param options - Optional overrides for the LiquidJS engine configuration.\n * @returns A configured {@link Liquid} engine instance.\n */\nexport function createEngine(partialsDir: string, options?: Partial<CreateEngineOptions>): Liquid {\n return new Liquid({\n root: [partialsDir],\n partials: [partialsDir],\n extname: \".prompt\",\n cache: true,\n ...options,\n // Safety defaults — applied after spread so callers cannot disable them\n strictFilters: true,\n ownPropertyOnly: true,\n });\n}\n\n/**\n * Shared LiquidJS engine for rendering prompt templates at runtime.\n *\n * Partials are flattened at codegen time by the CLI, so this engine\n * only needs to handle `{{ var }}` expressions and basic Liquid\n * control flow (`{% if %}`, `{% for %}`). No filesystem access required.\n *\n * @type {Liquid}\n */\nexport const liquidEngine = new Liquid({\n strictFilters: true,\n ownPropertyOnly: true,\n});\n"],"mappings":";;;;;;;;;;;;AAcA,SAAgB,aAAa,aAAqB,SAAgD;AAChG,QAAO,IAAI,OAAO;EAChB,MAAM,CAAC,YAAY;EACnB,UAAU,CAAC,YAAY;EACvB,SAAS;EACT,OAAO;EACP,GAAG;EAEH,eAAe;EACf,iBAAiB;EAClB,CAAC;;;;;;;;;;;AAYJ,MAAa,eAAe,IAAI,OAAO;CACrC,eAAe;CACf,iBAAiB;CAClB,CAAC"}
1
+ {"version":3,"file":"engine-Bhv5Zxdu.mjs","names":[],"sources":["../../src/engine.ts"],"sourcesContent":["import { Liquid } from \"liquidjs\";\n\nimport type { CreateEngineOptions } from \"./types.js\";\n\n/**\n * Create a LiquidJS engine with custom options.\n *\n * The `partialsDir` is used as the root for `{% render %}` resolution.\n * The `.prompt` extension is appended automatically.\n *\n * @param partialsDir - Root directory for `{% render %}` partial resolution.\n * @param options - Optional overrides for the LiquidJS engine configuration.\n * @returns A configured {@link Liquid} engine instance.\n */\nexport function createEngine(partialsDir: string, options?: Partial<CreateEngineOptions>): Liquid {\n return new Liquid({\n root: [partialsDir],\n partials: [partialsDir],\n extname: \".prompt\",\n cache: true,\n ...options,\n // Safety defaults — applied after spread so callers cannot disable them\n strictFilters: true,\n strictVariables: true,\n ownPropertyOnly: true,\n });\n}\n\n/**\n * Shared LiquidJS engine for rendering prompt templates at runtime.\n *\n * Partials are flattened at codegen time by the CLI, so this engine\n * only needs to handle `{{ var }}` expressions and basic Liquid\n * control flow (`{% if %}`, `{% for %}`). No filesystem access required.\n *\n * @type {Liquid}\n */\nexport const liquidEngine = new Liquid({\n strictFilters: true,\n strictVariables: true,\n ownPropertyOnly: true,\n});\n"],"mappings":";;;;;;;;;;;;AAcA,SAAgB,aAAa,aAAqB,SAAgD;AAChG,QAAO,IAAI,OAAO;EAChB,MAAM,CAAC,YAAY;EACnB,UAAU,CAAC,YAAY;EACvB,SAAS;EACT,OAAO;EACP,GAAG;EAEH,eAAe;EACf,iBAAiB;EACjB,iBAAiB;EAClB,CAAC;;;;;;;;;;;AAYJ,MAAa,eAAe,IAAI,OAAO;CACrC,eAAe;CACf,iBAAiB;CACjB,iBAAiB;CAClB,CAAC"}
@@ -1,5 +1,62 @@
1
- import { a as PromptRegistry, i as PromptNamespace, r as PromptModule } from "./types-2PI_9h-M.mjs";
1
+ import { a as PromptNamespace, i as PromptModule, o as PromptRegistry, r as PromptConfig } from "./types-DmnHC99v.mjs";
2
2
 
3
+ //#region src/group.d.ts
4
+ /**
5
+ * Create a prompt group namespace from a record of prompt modules.
6
+ *
7
+ * Sets the `group` field on each module to the given group name,
8
+ * producing a new namespace object without mutating the originals.
9
+ *
10
+ * @param name - Group name applied to each prompt (e.g. `'agents'`, `'agents/core'`).
11
+ * @param prompts - Record of prompt modules to group.
12
+ * @returns A new {@link PromptNamespace} with group-tagged prompt modules.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { createPrompt, createPromptGroup } from '@funkai/prompts'
17
+ * import { z } from 'zod'
18
+ *
19
+ * const agents = createPromptGroup('agents', {
20
+ * greeting: createPrompt({
21
+ * name: 'greeting',
22
+ * template: 'Hello {{ name }}!',
23
+ * schema: z.object({ name: z.string() }),
24
+ * }),
25
+ * })
26
+ *
27
+ * agents.greeting.render({ name: 'Alice' })
28
+ * ```
29
+ */
30
+ declare function createPromptGroup<T extends Record<string, PromptModule>>(name: string, prompts: T): T;
31
+ //#endregion
32
+ //#region src/prompt.d.ts
33
+ /**
34
+ * Create a prompt module from a config object.
35
+ *
36
+ * Encapsulates template rendering (via LiquidJS) and variable validation
37
+ * (via Zod) into a single {@link PromptModule}. Works for both codegen
38
+ * output and runtime on-the-fly prompt construction.
39
+ *
40
+ * @param config - Prompt configuration with name, template, schema, and optional group.
41
+ * @returns A {@link PromptModule} with `render` and `validate` methods.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * import { createPrompt } from '@funkai/prompts'
46
+ * import { z } from 'zod'
47
+ *
48
+ * const greeting = createPrompt({
49
+ * name: 'greeting',
50
+ * template: 'Hello {{ name }}, welcome to {{ place }}!',
51
+ * schema: z.object({ name: z.string(), place: z.string() }),
52
+ * })
53
+ *
54
+ * greeting.render({ name: 'Alice', place: 'Wonderland' })
55
+ * // => "Hello Alice, welcome to Wonderland!"
56
+ * ```
57
+ */
58
+ declare function createPrompt<T>(config: PromptConfig<T>): PromptModule<T>;
59
+ //#endregion
3
60
  //#region src/registry.d.ts
4
61
  /**
5
62
  * Create a typed, frozen prompt registry from a (possibly nested) map of prompt modules.
@@ -22,5 +79,5 @@ import { a as PromptRegistry, i as PromptNamespace, r as PromptModule } from "./
22
79
  */
23
80
  declare function createPromptRegistry<T extends PromptNamespace>(modules: T): PromptRegistry<T>;
24
81
  //#endregion
25
- export { type PromptModule, type PromptNamespace, type PromptRegistry, createPromptRegistry };
82
+ export { type PromptConfig, type PromptModule, type PromptNamespace, type PromptRegistry, createPrompt, createPromptGroup, createPromptRegistry };
26
83
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/registry.ts"],"mappings":";;;;;AAqBA;;;;;;;;;;;;;;;;;iBAAgB,oBAAA,WAA+B,eAAA,CAAA,CAAiB,OAAA,EAAS,CAAA,GAAI,cAAA,CAAe,CAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/group.ts","../../src/prompt.ts","../../src/registry.ts"],"mappings":";;;;;AA4BA;;;;;;;;;;;;;;;;;;;;;ACAA;;;iBDAgB,iBAAA,WAA4B,MAAA,SAAe,YAAA,EAAA,CACzD,IAAA,UACA,OAAA,EAAS,CAAA,GACR,CAAA;;;;;AAHH;;;;;;;;;;;;;;;;;;;;;ACAA;;iBAAgB,YAAA,GAAA,CAAgB,MAAA,EAAQ,YAAA,CAAa,CAAA,IAAK,YAAA,CAAa,CAAA;;;;;ADAvE;;;;;;;;;;;;;;;;;iBEPgB,oBAAA,WAA+B,eAAA,CAAA,CAAiB,OAAA,EAAS,CAAA,GAAI,cAAA,CAAe,CAAA"}
@@ -1,3 +1,79 @@
1
+ import { n as liquidEngine } from "./engine-Bhv5Zxdu.mjs";
2
+ //#region src/group.ts
3
+ /**
4
+ * Create a prompt group namespace from a record of prompt modules.
5
+ *
6
+ * Sets the `group` field on each module to the given group name,
7
+ * producing a new namespace object without mutating the originals.
8
+ *
9
+ * @param name - Group name applied to each prompt (e.g. `'agents'`, `'agents/core'`).
10
+ * @param prompts - Record of prompt modules to group.
11
+ * @returns A new {@link PromptNamespace} with group-tagged prompt modules.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import { createPrompt, createPromptGroup } from '@funkai/prompts'
16
+ * import { z } from 'zod'
17
+ *
18
+ * const agents = createPromptGroup('agents', {
19
+ * greeting: createPrompt({
20
+ * name: 'greeting',
21
+ * template: 'Hello {{ name }}!',
22
+ * schema: z.object({ name: z.string() }),
23
+ * }),
24
+ * })
25
+ *
26
+ * agents.greeting.render({ name: 'Alice' })
27
+ * ```
28
+ */
29
+ function createPromptGroup(name, prompts) {
30
+ return Object.fromEntries(Object.entries(prompts).map(([key, module]) => [key, {
31
+ ...module,
32
+ group: name
33
+ }]));
34
+ }
35
+ //#endregion
36
+ //#region src/prompt.ts
37
+ /**
38
+ * Create a prompt module from a config object.
39
+ *
40
+ * Encapsulates template rendering (via LiquidJS) and variable validation
41
+ * (via Zod) into a single {@link PromptModule}. Works for both codegen
42
+ * output and runtime on-the-fly prompt construction.
43
+ *
44
+ * @param config - Prompt configuration with name, template, schema, and optional group.
45
+ * @returns A {@link PromptModule} with `render` and `validate` methods.
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * import { createPrompt } from '@funkai/prompts'
50
+ * import { z } from 'zod'
51
+ *
52
+ * const greeting = createPrompt({
53
+ * name: 'greeting',
54
+ * template: 'Hello {{ name }}, welcome to {{ place }}!',
55
+ * schema: z.object({ name: z.string(), place: z.string() }),
56
+ * })
57
+ *
58
+ * greeting.render({ name: 'Alice', place: 'Wonderland' })
59
+ * // => "Hello Alice, welcome to Wonderland!"
60
+ * ```
61
+ */
62
+ function createPrompt(config) {
63
+ const { name, group, template, schema } = config;
64
+ return {
65
+ name,
66
+ group,
67
+ schema,
68
+ render(variables) {
69
+ return liquidEngine.parseAndRenderSync(template, schema.parse(variables));
70
+ },
71
+ validate(variables) {
72
+ return schema.parse(variables);
73
+ }
74
+ };
75
+ }
76
+ //#endregion
1
77
  //#region src/registry.ts
2
78
  /**
3
79
  * Create a typed, frozen prompt registry from a (possibly nested) map of prompt modules.
@@ -42,11 +118,15 @@ function isPromptModule(value) {
42
118
  * @private
43
119
  */
44
120
  function deepFreeze(obj) {
45
- Object.freeze(obj);
46
- for (const value of Object.values(obj)) if (typeof value === "object" && value !== null && !Object.isFrozen(value) && !isPromptModule(value)) deepFreeze(value);
47
- return obj;
121
+ const copied = Object.entries(obj).reduce((acc, [key, value]) => {
122
+ if (typeof value === "object" && value !== null && !isPromptModule(value)) acc[key] = deepFreeze({ ...value });
123
+ else acc[key] = value;
124
+ return acc;
125
+ }, {});
126
+ Object.freeze(copied);
127
+ return copied;
48
128
  }
49
129
  //#endregion
50
- export { createPromptRegistry };
130
+ export { createPrompt, createPromptGroup, createPromptRegistry };
51
131
 
52
132
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/registry.ts"],"sourcesContent":["import type { PromptModule, PromptNamespace, PromptRegistry } from \"./types.js\";\n\n/**\n * Create a typed, frozen prompt registry from a (possibly nested) map of prompt modules.\n *\n * The registry is typically created by generated code — the CLI produces\n * an `index.ts` that calls `createPromptRegistry()` with all discovered\n * prompt modules keyed by camelCase name, nested by group.\n *\n * @param modules - Record mapping camelCase prompt names (or group namespaces) to their modules.\n * @returns A deep-frozen, typed record with direct property access.\n *\n * @example\n * ```ts\n * const prompts = createPromptRegistry({\n * agents: { coverageAssessor },\n * greeting,\n * })\n * prompts.agents.coverageAssessor.render({ scope: 'full' })\n * ```\n */\nexport function createPromptRegistry<T extends PromptNamespace>(modules: T): PromptRegistry<T> {\n return deepFreeze({ ...modules });\n}\n\n// ---------------------------------------------------------------------------\n// Private\n// ---------------------------------------------------------------------------\n\n/**\n * Check whether a value looks like a PromptModule leaf.\n * Leaves have `name`, `schema`, and `render` — namespaces do not.\n *\n * @private\n */\nfunction isPromptModule(value: unknown): value is PromptModule {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"render\" in value &&\n \"schema\" in value &&\n \"name\" in value\n );\n}\n\n/**\n * Recursively freeze a prompt namespace tree.\n * Only recurses into plain namespace nodes — PromptModule leaves\n * (which contain Zod schemas) are intentionally left unfrozen\n * to avoid breaking Zod internal state.\n *\n * @param obj - The namespace object to freeze.\n * @returns The frozen object cast to its deep-readonly type.\n *\n * @private\n */\nfunction deepFreeze<T extends PromptNamespace>(obj: T): PromptRegistry<T> {\n Object.freeze(obj);\n for (const value of Object.values(obj)) {\n if (\n typeof value === \"object\" &&\n value !== null &&\n !Object.isFrozen(value) &&\n !isPromptModule(value)\n ) {\n deepFreeze(value as PromptNamespace);\n }\n }\n return obj as PromptRegistry<T>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,qBAAgD,SAA+B;AAC7F,QAAO,WAAW,EAAE,GAAG,SAAS,CAAC;;;;;;;;AAanC,SAAS,eAAe,OAAuC;AAC7D,QACE,OAAO,UAAU,YACjB,UAAU,QACV,YAAY,SACZ,YAAY,SACZ,UAAU;;;;;;;;;;;;;AAed,SAAS,WAAsC,KAA2B;AACxE,QAAO,OAAO,IAAI;AAClB,MAAK,MAAM,SAAS,OAAO,OAAO,IAAI,CACpC,KACE,OAAO,UAAU,YACjB,UAAU,QACV,CAAC,OAAO,SAAS,MAAM,IACvB,CAAC,eAAe,MAAM,CAEtB,YAAW,MAAyB;AAGxC,QAAO"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/group.ts","../../src/prompt.ts","../../src/registry.ts"],"sourcesContent":["import type { PromptModule } from \"./types.js\";\n\n/**\n * Create a prompt group namespace from a record of prompt modules.\n *\n * Sets the `group` field on each module to the given group name,\n * producing a new namespace object without mutating the originals.\n *\n * @param name - Group name applied to each prompt (e.g. `'agents'`, `'agents/core'`).\n * @param prompts - Record of prompt modules to group.\n * @returns A new {@link PromptNamespace} with group-tagged prompt modules.\n *\n * @example\n * ```ts\n * import { createPrompt, createPromptGroup } from '@funkai/prompts'\n * import { z } from 'zod'\n *\n * const agents = createPromptGroup('agents', {\n * greeting: createPrompt({\n * name: 'greeting',\n * template: 'Hello {{ name }}!',\n * schema: z.object({ name: z.string() }),\n * }),\n * })\n *\n * agents.greeting.render({ name: 'Alice' })\n * ```\n */\nexport function createPromptGroup<T extends Record<string, PromptModule>>(\n name: string,\n prompts: T,\n): T {\n return Object.fromEntries(\n Object.entries(prompts).map(([key, module]) => [key, { ...module, group: name }]),\n ) as T;\n}\n","import { liquidEngine } from \"./engine.js\";\nimport type { PromptConfig, PromptModule } from \"./types.js\";\n\n/**\n * Create a prompt module from a config object.\n *\n * Encapsulates template rendering (via LiquidJS) and variable validation\n * (via Zod) into a single {@link PromptModule}. Works for both codegen\n * output and runtime on-the-fly prompt construction.\n *\n * @param config - Prompt configuration with name, template, schema, and optional group.\n * @returns A {@link PromptModule} with `render` and `validate` methods.\n *\n * @example\n * ```ts\n * import { createPrompt } from '@funkai/prompts'\n * import { z } from 'zod'\n *\n * const greeting = createPrompt({\n * name: 'greeting',\n * template: 'Hello {{ name }}, welcome to {{ place }}!',\n * schema: z.object({ name: z.string(), place: z.string() }),\n * })\n *\n * greeting.render({ name: 'Alice', place: 'Wonderland' })\n * // => \"Hello Alice, welcome to Wonderland!\"\n * ```\n */\nexport function createPrompt<T>(config: PromptConfig<T>): PromptModule<T> {\n const { name, group, template, schema } = config;\n\n return {\n name,\n group,\n schema,\n /**\n * Render the prompt template with the given variables.\n *\n * @param variables - Template variables matching the prompt schema.\n * @returns The rendered prompt string.\n * @throws {ZodError} If variables fail schema validation.\n */\n render(variables: T): string {\n return liquidEngine.parseAndRenderSync(\n template,\n schema.parse(variables) as Record<string, unknown>,\n );\n },\n /**\n * Validate variables against the prompt schema.\n *\n * @param variables - Variables to validate.\n * @returns The parsed and validated variables.\n * @throws {ZodError} If variables fail schema validation.\n */\n validate(variables: unknown): T {\n return schema.parse(variables);\n },\n };\n}\n","import type { PromptModule, PromptNamespace, PromptRegistry } from \"./types.js\";\n\n/**\n * Create a typed, frozen prompt registry from a (possibly nested) map of prompt modules.\n *\n * The registry is typically created by generated code — the CLI produces\n * an `index.ts` that calls `createPromptRegistry()` with all discovered\n * prompt modules keyed by camelCase name, nested by group.\n *\n * @param modules - Record mapping camelCase prompt names (or group namespaces) to their modules.\n * @returns A deep-frozen, typed record with direct property access.\n *\n * @example\n * ```ts\n * const prompts = createPromptRegistry({\n * agents: { coverageAssessor },\n * greeting,\n * })\n * prompts.agents.coverageAssessor.render({ scope: 'full' })\n * ```\n */\nexport function createPromptRegistry<T extends PromptNamespace>(modules: T): PromptRegistry<T> {\n return deepFreeze({ ...modules });\n}\n\n// ---------------------------------------------------------------------------\n// Private\n// ---------------------------------------------------------------------------\n\n/**\n * Check whether a value looks like a PromptModule leaf.\n * Leaves have `name`, `schema`, and `render` — namespaces do not.\n *\n * @private\n */\nfunction isPromptModule(value: unknown): value is PromptModule {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"render\" in value &&\n \"schema\" in value &&\n \"name\" in value\n );\n}\n\n/**\n * Recursively freeze a prompt namespace tree.\n * Only recurses into plain namespace nodes — PromptModule leaves\n * (which contain Zod schemas) are intentionally left unfrozen\n * to avoid breaking Zod internal state.\n *\n * @param obj - The namespace object to freeze.\n * @returns The frozen object cast to its deep-readonly type.\n *\n * @private\n */\nfunction deepFreeze<T extends PromptNamespace>(obj: T): PromptRegistry<T> {\n const copied = Object.entries(obj).reduce<Record<string, unknown>>((acc, [key, value]) => {\n const isNamespace = typeof value === \"object\" && value !== null && !isPromptModule(value);\n if (isNamespace) {\n acc[key] = deepFreeze({ ...value } as PromptNamespace);\n } else {\n acc[key] = value;\n }\n return acc;\n }, {});\n\n Object.freeze(copied);\n return copied as PromptRegistry<T>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,kBACd,MACA,SACG;AACH,QAAO,OAAO,YACZ,OAAO,QAAQ,QAAQ,CAAC,KAAK,CAAC,KAAK,YAAY,CAAC,KAAK;EAAE,GAAG;EAAQ,OAAO;EAAM,CAAC,CAAC,CAClF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACNH,SAAgB,aAAgB,QAA0C;CACxE,MAAM,EAAE,MAAM,OAAO,UAAU,WAAW;AAE1C,QAAO;EACL;EACA;EACA;EAQA,OAAO,WAAsB;AAC3B,UAAO,aAAa,mBAClB,UACA,OAAO,MAAM,UAAU,CACxB;;EASH,SAAS,WAAuB;AAC9B,UAAO,OAAO,MAAM,UAAU;;EAEjC;;;;;;;;;;;;;;;;;;;;;;;ACrCH,SAAgB,qBAAgD,SAA+B;AAC7F,QAAO,WAAW,EAAE,GAAG,SAAS,CAAC;;;;;;;;AAanC,SAAS,eAAe,OAAuC;AAC7D,QACE,OAAO,UAAU,YACjB,UAAU,QACV,YAAY,SACZ,YAAY,SACZ,UAAU;;;;;;;;;;;;;AAed,SAAS,WAAsC,KAA2B;CACxE,MAAM,SAAS,OAAO,QAAQ,IAAI,CAAC,QAAiC,KAAK,CAAC,KAAK,WAAW;AAExF,MADoB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,eAAe,MAAM,CAEvF,KAAI,OAAO,WAAW,EAAE,GAAG,OAAO,CAAoB;MAEtD,KAAI,OAAO;AAEb,SAAO;IACN,EAAE,CAAC;AAEN,QAAO,OAAO,OAAO;AACrB,QAAO"}
@@ -1,2 +1,2 @@
1
- import { n as liquidEngine } from "./engine-CQfJbVz6.mjs";
1
+ import { n as liquidEngine } from "./engine-BAYeiay3.mjs";
2
2
  export { liquidEngine };
@@ -1,2 +1,2 @@
1
- import { n as liquidEngine } from "./engine-CPKs9QbX.mjs";
1
+ import { n as liquidEngine } from "./engine-Bhv5Zxdu.mjs";
2
2
  export { liquidEngine };
@@ -7,7 +7,29 @@ import { ZodType } from "zod";
7
7
  */
8
8
  type CreateEngineOptions = Pick<LiquidOptions, "root" | "partials" | "extname" | "cache" | "strictVariables">;
9
9
  /**
10
- * A single prompt module produced by codegen.
10
+ * Configuration for creating a prompt module via {@link createPrompt}.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const config: PromptConfig<{ name: string }> = {
15
+ * name: 'greeting',
16
+ * template: 'Hello {{ name }}!',
17
+ * schema: z.object({ name: z.string() }),
18
+ * }
19
+ * ```
20
+ */
21
+ interface PromptConfig<T = unknown> {
22
+ /** Kebab-case prompt identifier (e.g. `'greeting'`, `'worker-system'`). */
23
+ readonly name: string;
24
+ /** LiquidJS template string with `{{ variable }}` expressions. */
25
+ readonly template: string;
26
+ /** Zod schema for validating template variables. */
27
+ readonly schema: ZodType<T>;
28
+ /** Optional group path (e.g. `'agents'`, `'agents/core'`). */
29
+ readonly group?: string;
30
+ }
31
+ /**
32
+ * A single prompt module produced by {@link createPrompt} or codegen.
11
33
  *
12
34
  * Each `.prompt` file generates a default export conforming to this shape.
13
35
  */
@@ -39,5 +61,5 @@ interface PromptNamespace {
39
61
  */
40
62
  type PromptRegistry<T extends PromptNamespace> = { readonly [K in keyof T]: T[K] extends PromptModule ? T[K] : T[K] extends PromptNamespace ? PromptRegistry<T[K]> : T[K] };
41
63
  //#endregion
42
- export { PromptRegistry as a, PromptNamespace as i, Liquid$1 as n, PromptModule as r, CreateEngineOptions as t };
43
- //# sourceMappingURL=types-2PI_9h-M.d.mts.map
64
+ export { PromptNamespace as a, PromptModule as i, Liquid$1 as n, PromptRegistry as o, PromptConfig as r, CreateEngineOptions as t };
65
+ //# sourceMappingURL=types-DmnHC99v.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-DmnHC99v.d.mts","names":[],"sources":["../../src/types.ts"],"mappings":";;;;;;AAMA;KAAY,mBAAA,GAAsB,IAAA,CAChC,aAAA;;;;AAgBF;;;;;;;;;UAAiB,YAAA;EAQN;EAAA,SANA,IAAA;EAMK;EAAA,SAJL,QAAA;EAYkB;EAAA,SAVlB,MAAA,EAAQ,OAAA,CAAQ,CAAA;EAaA;EAAA,SAXhB,KAAA;AAAA;;;;;;UAQM,YAAA;EAAA,SACN,IAAA;EAAA,SACA,KAAA;EAAA,SACA,MAAA,EAAQ,OAAA,CAAQ,CAAA;EACzB,MAAA,CAAO,SAAA,EAAW,CAAA;EAClB,QAAA,CAAS,SAAA,YAAqB,CAAA;AAAA;;;;;UAOf,eAAA;EAAA,UACL,GAAA,WAAc,YAAA,GAAe,eAAA;AAAA;;;;;;;;AAezC;;;;;KAAY,cAAA,WAAyB,eAAA,2BACd,CAAA,GAAI,CAAA,CAAE,CAAA,UAAW,YAAA,GAClC,CAAA,CAAE,CAAA,IACF,CAAA,CAAE,CAAA,UAAW,eAAA,GACX,cAAA,CAAe,CAAA,CAAE,CAAA,KACjB,CAAA,CAAE,CAAA"}
@@ -6,14 +6,14 @@ Generate typed TypeScript modules from `.prompt` files.
6
6
 
7
7
  **Alias:** `gen`
8
8
 
9
- | Flag | Alias | Required | Description |
10
- | ---------- | ----- | -------- | ------------------------------------------------------- |
11
- | `--out` | `-o` | Yes | Output directory for generated files |
12
- | `--roots` | `-r` | Yes | Space-separated directories to scan for `.prompt` files |
13
- | `--silent` | --- | No | Suppress output except errors |
9
+ | Flag | Alias | Required | Description |
10
+ | ------------ | ----- | -------- | ----------------------------------------- |
11
+ | `--out` | `-o` | Yes | Output directory for generated files |
12
+ | `--includes` | `-r` | Yes | Glob patterns to scan for `.prompt` files |
13
+ | `--silent` | --- | No | Suppress output except errors |
14
14
 
15
15
  ```bash
16
- prompts generate --out .prompts/client --roots prompts src/agents src/workflows
16
+ prompts generate --out .prompts/client --includes "prompts/**" "src/agents/**" "src/workflows/**"
17
17
  ```
18
18
 
19
19
  Custom partials are auto-discovered from the sibling `partials/` directory (relative to `--out`).
@@ -26,7 +26,7 @@ Validate `.prompt` files without generating output.
26
26
 
27
27
  | Flag | Alias | Required | Description |
28
28
  | ------------ | ----- | -------- | -------------------------------------------------------- |
29
- | `--roots` | `-r` | Yes | Directories to scan |
29
+ | `--includes` | `-r` | Yes | Glob patterns to scan for `.prompt` files |
30
30
  | `--partials` | `-p` | No | Custom partials directory (default: `.prompts/partials`) |
31
31
  | `--silent` | --- | No | Suppress output except errors |
32
32
 
@@ -38,7 +38,7 @@ Validate `.prompt` files without generating output.
38
38
  | Warn | Schema variable not used in template |
39
39
 
40
40
  ```bash
41
- prompts lint --roots prompts src/agents
41
+ prompts lint --includes "prompts/**" "src/agents/**"
42
42
  ```
43
43
 
44
44
  ## `prompts create`
@@ -61,7 +61,7 @@ Add a generate script to your `package.json`:
61
61
  ```json
62
62
  {
63
63
  "scripts": {
64
- "prompts:generate": "prompts generate --out .prompts/client --roots prompts src/agents"
64
+ "prompts:generate": "prompts generate --out .prompts/client --includes \"prompts/**\" \"src/agents/**\""
65
65
  }
66
66
  }
67
67
  ```
package/docs/cli.md ADDED
@@ -0,0 +1,132 @@
1
+ # CLI
2
+
3
+ The `prompts` CLI discovers, validates, and generates typed TypeScript from `.prompt` files.
4
+
5
+ ## Installation
6
+
7
+ Available as the `prompts` binary from `@funkai/cli`. Install it as a workspace dependency:
8
+
9
+ ```bash
10
+ pnpm add @funkai/cli --workspace
11
+ ```
12
+
13
+ ## Workflow
14
+
15
+ ```mermaid
16
+ %%{init: {
17
+ 'theme': 'base',
18
+ 'themeVariables': {
19
+ 'primaryColor': '#313244',
20
+ 'primaryTextColor': '#cdd6f4',
21
+ 'primaryBorderColor': '#6c7086',
22
+ 'lineColor': '#89b4fa',
23
+ 'secondaryColor': '#45475a',
24
+ 'tertiaryColor': '#1e1e2e',
25
+ 'actorBkg': '#313244',
26
+ 'actorBorder': '#89b4fa',
27
+ 'actorTextColor': '#cdd6f4',
28
+ 'signalColor': '#cdd6f4',
29
+ 'signalTextColor': '#cdd6f4'
30
+ }
31
+ }}%%
32
+ sequenceDiagram
33
+ participant Dev as Developer
34
+ participant CLI as prompts CLI
35
+ participant FS as File System
36
+
37
+ Dev->>CLI: prompts generate
38
+ CLI->>FS: Discover .prompt files from --includes globs
39
+ CLI->>CLI: Parse frontmatter + extract variables
40
+ CLI->>CLI: Lint (schema vs template match)
41
+ CLI->>CLI: Flatten partials
42
+ CLI->>FS: Write generated .ts modules
43
+ FS-->>Dev: Import typed prompts from ~prompts
44
+ ```
45
+
46
+ ## Commands Reference
47
+
48
+ ### `prompts generate`
49
+
50
+ Generate typed TypeScript modules from `.prompt` files.
51
+
52
+ **Alias:** `gen`
53
+
54
+ | Flag | Alias | Required | Description |
55
+ | ------------ | ----- | -------- | ----------------------------------------- |
56
+ | `--out` | `-o` | Yes | Output directory for generated files |
57
+ | `--includes` | `-r` | Yes | Glob patterns to scan for `.prompt` files |
58
+ | `--silent` | --- | No | Suppress output except errors |
59
+
60
+ ```bash
61
+ prompts generate --out .prompts/client --includes "prompts/**" "src/agents/**" "src/workflows/**"
62
+ ```
63
+
64
+ Custom partials are auto-discovered from the sibling `partials/` directory (relative to `--out`).
65
+
66
+ Runs lint validation automatically before generating. Exits with code 1 on lint errors.
67
+
68
+ ### `prompts lint`
69
+
70
+ Validate `.prompt` files without generating output.
71
+
72
+ | Flag | Alias | Required | Description |
73
+ | ------------ | ----- | -------- | -------------------------------------------------------- |
74
+ | `--includes` | `-r` | Yes | Glob patterns to scan for `.prompt` files |
75
+ | `--partials` | `-p` | No | Custom partials directory (default: `.prompts/partials`) |
76
+ | `--silent` | --- | No | Suppress output except errors |
77
+
78
+ **Diagnostics:**
79
+
80
+ | Level | Meaning |
81
+ | ----- | ---------------------------------------- |
82
+ | Error | Template variable not declared in schema |
83
+ | Warn | Schema variable not used in template |
84
+
85
+ ```bash
86
+ prompts lint --includes "prompts/**" "src/agents/**"
87
+ ```
88
+
89
+ ### `prompts create`
90
+
91
+ Scaffold a new `.prompt` file.
92
+
93
+ | Arg/Flag | Required | Description |
94
+ | ----------- | -------- | ------------------------------------------------------------- |
95
+ | `<name>` | Yes | Prompt name (kebab-case) |
96
+ | `--out` | No | Output directory (defaults to cwd) |
97
+ | `--partial` | No | Create as a partial in `.prompts/partials/` (ignores `--out`) |
98
+
99
+ ```bash
100
+ prompts create coverage-assessor --out src/agents/coverage-assessor
101
+ prompts create summary --partial
102
+ ```
103
+
104
+ ### `prompts setup`
105
+
106
+ Interactive project configuration for `.prompt` development. No flags -- fully interactive.
107
+
108
+ Configures:
109
+
110
+ 1. VSCode file association (`*.prompt` -> Markdown)
111
+ 2. VSCode Liquid extension recommendation
112
+ 3. `.gitignore` entry for generated `.prompts/client/` directory
113
+ 4. `tsconfig.json` path alias (`~prompts` -> `./.prompts/client/index.ts`)
114
+
115
+ ## Integration
116
+
117
+ Add a generate script to your `package.json`:
118
+
119
+ ```json
120
+ {
121
+ "scripts": {
122
+ "prompts:generate": "prompts generate --out .prompts/client --includes \"prompts/**\" \"src/agents/**\""
123
+ }
124
+ }
125
+ ```
126
+
127
+ ## References
128
+
129
+ - [File Format](file-format.md)
130
+ - [Code Generation & Library](codegen.md)
131
+ - [Setup](setup.md)
132
+ - [Troubleshooting](troubleshooting.md)