@funkai/prompts 0.1.0

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 (61) hide show
  1. package/.turbo/turbo-build.log +18 -0
  2. package/.turbo/turbo-test$colon$coverage.log +26 -0
  3. package/.turbo/turbo-test.log +26 -0
  4. package/.turbo/turbo-typecheck.log +4 -0
  5. package/CHANGELOG.md +14 -0
  6. package/LICENSE +21 -0
  7. package/README.md +101 -0
  8. package/banner.svg +100 -0
  9. package/coverage/lcov-report/base.css +224 -0
  10. package/coverage/lcov-report/block-navigation.js +87 -0
  11. package/coverage/lcov-report/clean.ts.html +160 -0
  12. package/coverage/lcov-report/engine.ts.html +196 -0
  13. package/coverage/lcov-report/favicon.png +0 -0
  14. package/coverage/lcov-report/index.html +161 -0
  15. package/coverage/lcov-report/partials-dir.ts.html +100 -0
  16. package/coverage/lcov-report/prettify.css +1 -0
  17. package/coverage/lcov-report/prettify.js +2 -0
  18. package/coverage/lcov-report/registry.ts.html +280 -0
  19. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  20. package/coverage/lcov-report/sorter.js +210 -0
  21. package/coverage/lcov.info +75 -0
  22. package/dist/lib/index.d.mts +99 -0
  23. package/dist/lib/index.d.mts.map +1 -0
  24. package/dist/lib/index.mjs +117 -0
  25. package/dist/lib/index.mjs.map +1 -0
  26. package/dist/prompts/constraints.prompt +20 -0
  27. package/dist/prompts/identity.prompt +6 -0
  28. package/dist/prompts/prompts/constraints.prompt +20 -0
  29. package/dist/prompts/prompts/identity.prompt +6 -0
  30. package/dist/prompts/prompts/tools.prompt +14 -0
  31. package/dist/prompts/tools.prompt +14 -0
  32. package/docs/cli/commands.md +73 -0
  33. package/docs/cli/overview.md +73 -0
  34. package/docs/codegen/overview.md +83 -0
  35. package/docs/file-format/frontmatter.md +55 -0
  36. package/docs/file-format/overview.md +67 -0
  37. package/docs/file-format/partials.md +87 -0
  38. package/docs/guides/add-partial.md +75 -0
  39. package/docs/guides/author-prompt.md +70 -0
  40. package/docs/guides/setup-project.md +75 -0
  41. package/docs/library/overview.md +64 -0
  42. package/docs/overview.md +102 -0
  43. package/docs/troubleshooting.md +37 -0
  44. package/logo.svg +20 -0
  45. package/package.json +53 -0
  46. package/src/clean.test.ts +44 -0
  47. package/src/clean.ts +25 -0
  48. package/src/engine.test.ts +44 -0
  49. package/src/engine.ts +37 -0
  50. package/src/index.ts +11 -0
  51. package/src/partials-dir.test.ts +15 -0
  52. package/src/partials-dir.ts +5 -0
  53. package/src/prompts/constraints.prompt +20 -0
  54. package/src/prompts/identity.prompt +6 -0
  55. package/src/prompts/tools.prompt +14 -0
  56. package/src/registry.test.ts +69 -0
  57. package/src/registry.ts +65 -0
  58. package/src/types.ts +62 -0
  59. package/tsconfig.json +25 -0
  60. package/tsdown.config.ts +12 -0
  61. package/vitest.config.ts +21 -0
@@ -0,0 +1,99 @@
1
+ import { Liquid, Liquid as Liquid$1, LiquidOptions } from "liquidjs";
2
+ import { ZodType } from "zod";
3
+
4
+ //#region src/types.d.ts
5
+ /**
6
+ * Options for creating a custom LiquidJS engine.
7
+ */
8
+ type CreateEngineOptions = Pick<LiquidOptions, "root" | "partials" | "extname" | "cache" | "strictFilters" | "strictVariables" | "ownPropertyOnly">;
9
+ /**
10
+ * A single prompt module produced by codegen.
11
+ *
12
+ * Each `.prompt` file generates a default export conforming to this shape.
13
+ */
14
+ interface PromptModule<T = unknown> {
15
+ readonly name: string;
16
+ readonly group: string | undefined;
17
+ readonly schema: ZodType<T>;
18
+ render(variables: T): string;
19
+ validate(variables: unknown): T;
20
+ }
21
+ /**
22
+ * A nested namespace node in the prompt tree.
23
+ * Values are either PromptModule leaves or further nested namespaces.
24
+ */
25
+ type PromptNamespace = {
26
+ readonly [key: string]: PromptModule | PromptNamespace;
27
+ };
28
+ /**
29
+ * Deep-readonly version of a prompt tree.
30
+ * Prevents reassignment at any nesting level.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * type MyRegistry = PromptRegistry<{
35
+ * agents: { coverageAssessor: PromptModule }
36
+ * greeting: PromptModule
37
+ * }>
38
+ * ```
39
+ */
40
+ 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
+ //#endregion
42
+ //#region src/engine.d.ts
43
+ /**
44
+ * Create a LiquidJS engine with custom options.
45
+ *
46
+ * The `partialsDir` is used as the root for `{% render %}` resolution.
47
+ * The `.prompt` extension is appended automatically.
48
+ *
49
+ * @param partialsDir - Root directory for `{% render %}` partial resolution.
50
+ * @param options - Optional overrides for the LiquidJS engine configuration.
51
+ * @returns A configured {@link Liquid} engine instance.
52
+ */
53
+ declare function createEngine(partialsDir: string, options?: Partial<CreateEngineOptions>): Liquid$1;
54
+ /**
55
+ * Shared LiquidJS engine for rendering prompt templates at runtime.
56
+ *
57
+ * Partials are flattened at codegen time by the CLI, so this engine
58
+ * only needs to handle `{{ var }}` expressions and basic Liquid
59
+ * control flow (`{% if %}`, `{% for %}`). No filesystem access required.
60
+ */
61
+ declare const engine: Liquid$1;
62
+ //#endregion
63
+ //#region src/clean.d.ts
64
+ /**
65
+ * Clean a raw `.prompt` file into a render-ready template.
66
+ *
67
+ * Runs the source through a pipeline of transforms — currently
68
+ * strips frontmatter, with more steps added over time.
69
+ */
70
+ declare function clean(text: string): string;
71
+ //#endregion
72
+ //#region src/partials-dir.d.ts
73
+ /** Absolute path to the SDK's built-in partials directory. */
74
+ declare const PARTIALS_DIR: string;
75
+ //#endregion
76
+ //#region src/registry.d.ts
77
+ /**
78
+ * Create a typed, frozen prompt registry from a (possibly nested) map of prompt modules.
79
+ *
80
+ * The registry is typically created by generated code — the CLI produces
81
+ * an `index.ts` that calls `createPromptRegistry()` with all discovered
82
+ * prompt modules keyed by camelCase name, nested by group.
83
+ *
84
+ * @param modules - Record mapping camelCase prompt names (or group namespaces) to their modules.
85
+ * @returns A deep-frozen, typed record with direct property access.
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * const prompts = createPromptRegistry({
90
+ * agents: { coverageAssessor },
91
+ * greeting,
92
+ * })
93
+ * prompts.agents.coverageAssessor.render({ scope: 'full' })
94
+ * ```
95
+ */
96
+ declare function createPromptRegistry<T extends PromptNamespace>(modules: T): PromptRegistry<T>;
97
+ //#endregion
98
+ export { type CreateEngineOptions, type Liquid, PARTIALS_DIR, type PromptModule, type PromptNamespace, type PromptRegistry, clean, createEngine, createPromptRegistry, engine };
99
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/types.ts","../../src/engine.ts","../../src/clean.ts","../../src/partials-dir.ts","../../src/registry.ts"],"mappings":";;;;;;AAMA;KAAY,mBAAA,GAAsB,IAAA,CAChC,aAAA;;;;AAeF;;UAAiB,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;;;;;KAOpB,eAAA;EAAA,UACA,GAAA,WAAc,YAAA,GAAe,eAAA;AAAA;;;;;;;;AADzC;;;;;KAgBY,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;;;;;AAjDV;;;;;AAgBA;;;iBCRgB,YAAA,CAAa,WAAA,UAAqB,OAAA,GAAU,OAAA,CAAQ,mBAAA,IAAuB,QAAA;;;;;;;;cAmB9E,MAAA,EAAM,QAAA;;;;;;;AD3BnB;;iBEgBgB,KAAA,CAAM,IAAA;;;;cClBT,YAAA;;;;;;AHEb;;;;;AAgBA;;;;;;;;;;;iBIwCgB,oBAAA,WAA+B,eAAA,CAAA,CAAiB,OAAA,EAAS,CAAA,GAAI,cAAA,CAAe,CAAA"}
@@ -0,0 +1,117 @@
1
+ import { Liquid } from "liquidjs";
2
+ import { flow } from "es-toolkit";
3
+ import { dirname, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ //#region src/engine.ts
6
+ /**
7
+ * Create a LiquidJS engine with custom options.
8
+ *
9
+ * The `partialsDir` is used as the root for `{% render %}` resolution.
10
+ * The `.prompt` extension is appended automatically.
11
+ *
12
+ * @param partialsDir - Root directory for `{% render %}` partial resolution.
13
+ * @param options - Optional overrides for the LiquidJS engine configuration.
14
+ * @returns A configured {@link Liquid} engine instance.
15
+ */
16
+ function createEngine(partialsDir, options) {
17
+ return new Liquid({
18
+ root: [partialsDir],
19
+ partials: [partialsDir],
20
+ extname: ".prompt",
21
+ cache: true,
22
+ strictFilters: true,
23
+ ownPropertyOnly: true,
24
+ ...options
25
+ });
26
+ }
27
+ /**
28
+ * Shared LiquidJS engine for rendering prompt templates at runtime.
29
+ *
30
+ * Partials are flattened at codegen time by the CLI, so this engine
31
+ * only needs to handle `{{ var }}` expressions and basic Liquid
32
+ * control flow (`{% if %}`, `{% for %}`). No filesystem access required.
33
+ */
34
+ const engine = new Liquid({
35
+ strictFilters: true,
36
+ ownPropertyOnly: true
37
+ });
38
+ //#endregion
39
+ //#region src/clean.ts
40
+ const FRONTMATTER_RE = /^---\r?\n[\s\S]*?\r?\n---\r?\n?/;
41
+ /**
42
+ * Remove YAML frontmatter from the beginning of a string.
43
+ *
44
+ * Frontmatter is a block delimited by `---` at the start of the file.
45
+ * If no frontmatter is present, the string is returned unchanged.
46
+ */
47
+ function stripFrontmatter(text) {
48
+ return text.replace(FRONTMATTER_RE, "");
49
+ }
50
+ const pipeline = flow(stripFrontmatter);
51
+ /**
52
+ * Clean a raw `.prompt` file into a render-ready template.
53
+ *
54
+ * Runs the source through a pipeline of transforms — currently
55
+ * strips frontmatter, with more steps added over time.
56
+ */
57
+ function clean(text) {
58
+ return pipeline(text);
59
+ }
60
+ //#endregion
61
+ //#region src/partials-dir.ts
62
+ /** Absolute path to the SDK's built-in partials directory. */
63
+ const PARTIALS_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "../prompts");
64
+ //#endregion
65
+ //#region src/registry.ts
66
+ /**
67
+ * Check whether a value looks like a PromptModule leaf.
68
+ * Leaves have `name`, `schema`, and `render` — namespaces do not.
69
+ *
70
+ * @private
71
+ */
72
+ function isPromptModule(value) {
73
+ return typeof value === "object" && value !== null && "render" in value && "schema" in value && "name" in value;
74
+ }
75
+ /**
76
+ * Recursively freeze a prompt namespace tree.
77
+ * Only recurses into plain namespace nodes — PromptModule leaves
78
+ * (which contain Zod schemas) are frozen shallowly.
79
+ *
80
+ * @param obj - The namespace object to freeze.
81
+ * @returns The frozen object cast to its deep-readonly type.
82
+ *
83
+ * @private
84
+ */
85
+ function deepFreeze(obj) {
86
+ Object.freeze(obj);
87
+ Object.values(obj).forEach((value) => {
88
+ if (typeof value === "object" && value !== null && !Object.isFrozen(value) && !isPromptModule(value)) deepFreeze(value);
89
+ });
90
+ return obj;
91
+ }
92
+ /**
93
+ * Create a typed, frozen prompt registry from a (possibly nested) map of prompt modules.
94
+ *
95
+ * The registry is typically created by generated code — the CLI produces
96
+ * an `index.ts` that calls `createPromptRegistry()` with all discovered
97
+ * prompt modules keyed by camelCase name, nested by group.
98
+ *
99
+ * @param modules - Record mapping camelCase prompt names (or group namespaces) to their modules.
100
+ * @returns A deep-frozen, typed record with direct property access.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * const prompts = createPromptRegistry({
105
+ * agents: { coverageAssessor },
106
+ * greeting,
107
+ * })
108
+ * prompts.agents.coverageAssessor.render({ scope: 'full' })
109
+ * ```
110
+ */
111
+ function createPromptRegistry(modules) {
112
+ return deepFreeze({ ...modules });
113
+ }
114
+ //#endregion
115
+ export { PARTIALS_DIR, clean, createEngine, createPromptRegistry, engine };
116
+
117
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/engine.ts","../../src/clean.ts","../../src/partials-dir.ts","../../src/registry.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 strictFilters: true,\n ownPropertyOnly: true,\n ...options,\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 */\nexport const engine = new Liquid({\n strictFilters: true,\n ownPropertyOnly: true,\n});\n","import { flow } from \"es-toolkit\";\n\nconst FRONTMATTER_RE = /^---\\r?\\n[\\s\\S]*?\\r?\\n---\\r?\\n?/;\n\n/**\n * Remove YAML frontmatter from the beginning of a string.\n *\n * Frontmatter is a block delimited by `---` at the start of the file.\n * If no frontmatter is present, the string is returned unchanged.\n */\nfunction stripFrontmatter(text: string): string {\n return text.replace(FRONTMATTER_RE, \"\");\n}\n\nconst pipeline = flow(stripFrontmatter);\n\n/**\n * Clean a raw `.prompt` file into a render-ready template.\n *\n * Runs the source through a pipeline of transforms — currently\n * strips frontmatter, with more steps added over time.\n */\nexport function clean(text: string): string {\n return pipeline(text);\n}\n","import { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/** Absolute path to the SDK's built-in partials directory. */\nexport const PARTIALS_DIR = resolve(dirname(fileURLToPath(import.meta.url)), \"../prompts\");\n","import type { PromptModule, PromptNamespace, PromptRegistry } from \"./types.js\";\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 frozen shallowly.\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 Object.values(obj).forEach((value) => {\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\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"],"mappings":";;;;;;;;;;;;;;;AAcA,SAAgB,aAAa,aAAqB,SAAgD;AAChG,QAAO,IAAI,OAAO;EAChB,MAAM,CAAC,YAAY;EACnB,UAAU,CAAC,YAAY;EACvB,SAAS;EACT,OAAO;EACP,eAAe;EACf,iBAAiB;EACjB,GAAG;EACJ,CAAC;;;;;;;;;AAUJ,MAAa,SAAS,IAAI,OAAO;CAC/B,eAAe;CACf,iBAAiB;CAClB,CAAC;;;AClCF,MAAM,iBAAiB;;;;;;;AAQvB,SAAS,iBAAiB,MAAsB;AAC9C,QAAO,KAAK,QAAQ,gBAAgB,GAAG;;AAGzC,MAAM,WAAW,KAAK,iBAAiB;;;;;;;AAQvC,SAAgB,MAAM,MAAsB;AAC1C,QAAO,SAAS,KAAK;;;;;ACnBvB,MAAa,eAAe,QAAQ,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC,EAAE,aAAa;;;;;;;;;ACI1F,SAAS,eAAe,OAAuC;AAC7D,QACE,OAAO,UAAU,YACjB,UAAU,QACV,YAAY,SACZ,YAAY,SACZ,UAAU;;;;;;;;;;;;AAcd,SAAS,WAAsC,KAA2B;AACxE,QAAO,OAAO,IAAI;AAClB,QAAO,OAAO,IAAI,CAAC,SAAS,UAAU;AACpC,MACE,OAAO,UAAU,YACjB,UAAU,QACV,CAAC,OAAO,SAAS,MAAM,IACvB,CAAC,eAAe,MAAM,CAEtB,YAAW,MAAyB;GAEtC;AACF,QAAO;;;;;;;;;;;;;;;;;;;;;AAsBT,SAAgB,qBAAgD,SAA+B;AAC7F,QAAO,WAAW,EAAE,GAAG,SAAS,CAAC"}
@@ -0,0 +1,20 @@
1
+ <constraints>
2
+ {% if in_scope %}
3
+ ## In Scope
4
+ {% for item in in_scope %}
5
+ - {{ item }}
6
+ {% endfor %}
7
+ {% endif %}
8
+ {% if out_of_scope %}
9
+ ## Out of Scope
10
+ {% for item in out_of_scope %}
11
+ - {{ item }}
12
+ {% endfor %}
13
+ {% endif %}
14
+ {% if rules %}
15
+ ## Rules
16
+ {% for rule in rules %}
17
+ - {{ rule }}
18
+ {% endfor %}
19
+ {% endif %}
20
+ </constraints>
@@ -0,0 +1,6 @@
1
+ <identity>
2
+ You are {{ role }}, {{ desc }}.
3
+ {% if context %}
4
+ {{ context }}
5
+ {% endif %}
6
+ </identity>
@@ -0,0 +1,20 @@
1
+ <constraints>
2
+ {% if in_scope %}
3
+ ## In Scope
4
+ {% for item in in_scope %}
5
+ - {{ item }}
6
+ {% endfor %}
7
+ {% endif %}
8
+ {% if out_of_scope %}
9
+ ## Out of Scope
10
+ {% for item in out_of_scope %}
11
+ - {{ item }}
12
+ {% endfor %}
13
+ {% endif %}
14
+ {% if rules %}
15
+ ## Rules
16
+ {% for rule in rules %}
17
+ - {{ rule }}
18
+ {% endfor %}
19
+ {% endif %}
20
+ </constraints>
@@ -0,0 +1,6 @@
1
+ <identity>
2
+ You are {{ role }}, {{ desc }}.
3
+ {% if context %}
4
+ {{ context }}
5
+ {% endif %}
6
+ </identity>
@@ -0,0 +1,14 @@
1
+ <tools>
2
+ You have access to the following tools:
3
+ {% if tools %}
4
+ {% for tool in tools %}
5
+ **{{ tool.name }}** -- {{ tool.description }}
6
+ {% for param in tool.params %}
7
+ - `{{ param.name }}` ({{ param.type }}{% if param.required %}, required{% endif %}) -- {{ param.description }}
8
+ {% endfor %}
9
+
10
+ {% endfor %}
11
+ {% else %}
12
+ No tools are configured for this agent.
13
+ {% endif %}
14
+ </tools>
@@ -0,0 +1,14 @@
1
+ <tools>
2
+ You have access to the following tools:
3
+ {% if tools %}
4
+ {% for tool in tools %}
5
+ **{{ tool.name }}** -- {{ tool.description }}
6
+ {% for param in tool.params %}
7
+ - `{{ param.name }}` ({{ param.type }}{% if param.required %}, required{% endif %}) -- {{ param.description }}
8
+ {% endfor %}
9
+
10
+ {% endfor %}
11
+ {% else %}
12
+ No tools are configured for this agent.
13
+ {% endif %}
14
+ </tools>
@@ -0,0 +1,73 @@
1
+ # CLI Commands Reference
2
+
3
+ ## `prompts generate`
4
+
5
+ Generate typed TypeScript modules from `.prompt` files.
6
+
7
+ **Alias:** `gen`
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 |
14
+
15
+ ```bash
16
+ prompts generate --out .prompts/client --roots prompts src/agents src/workflows
17
+ ```
18
+
19
+ Custom partials are auto-discovered from the sibling `partials/` directory (relative to `--out`).
20
+
21
+ Runs lint validation automatically before generating. Exits with code 1 on lint errors.
22
+
23
+ ## `prompts lint`
24
+
25
+ Validate `.prompt` files without generating output.
26
+
27
+ | Flag | Alias | Required | Description |
28
+ | ------------ | ----- | -------- | -------------------------------------------------------- |
29
+ | `--roots` | `-r` | Yes | Directories to scan |
30
+ | `--partials` | `-p` | No | Custom partials directory (default: `.prompts/partials`) |
31
+ | `--silent` | --- | No | Suppress output except errors |
32
+
33
+ **Diagnostics:**
34
+
35
+ | Level | Meaning |
36
+ | ----- | ---------------------------------------- |
37
+ | Error | Template variable not declared in schema |
38
+ | Warn | Schema variable not used in template |
39
+
40
+ ```bash
41
+ prompts lint --roots prompts src/agents
42
+ ```
43
+
44
+ ## `prompts create`
45
+
46
+ Scaffold a new `.prompt` file.
47
+
48
+ | Arg/Flag | Required | Description |
49
+ | ----------- | -------- | ------------------------------------------------------------- |
50
+ | `<name>` | Yes | Prompt name (kebab-case) |
51
+ | `--out` | No | Output directory (defaults to cwd) |
52
+ | `--partial` | No | Create as a partial in `.prompts/partials/` (ignores `--out`) |
53
+
54
+ ```bash
55
+ prompts create coverage-assessor --out src/agents/coverage-assessor
56
+ prompts create summary --partial
57
+ ```
58
+
59
+ ## `prompts setup`
60
+
61
+ Interactive project configuration for `.prompt` development. No flags -- fully interactive.
62
+
63
+ Configures:
64
+
65
+ 1. VSCode file association (`*.prompt` -> Markdown)
66
+ 2. VSCode Liquid extension recommendation
67
+ 3. `.gitignore` entry for generated `.prompts/client/` directory
68
+ 4. `tsconfig.json` path alias (`~prompts` -> `./.prompts/client/index.ts`)
69
+
70
+ ## References
71
+
72
+ - [CLI Overview](overview.md)
73
+ - [Troubleshooting](../troubleshooting.md)
@@ -0,0 +1,73 @@
1
+ # CLI Overview
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 roots
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
47
+
48
+ | Command | Alias | Description |
49
+ | ------------------ | ----- | ---------------------------------------------- |
50
+ | `prompts generate` | `gen` | Generate typed TS modules from `.prompt` files |
51
+ | `prompts lint` | --- | Validate `.prompt` files without generating |
52
+ | `prompts create` | --- | Scaffold a new `.prompt` file |
53
+ | `prompts setup` | --- | Interactive project configuration |
54
+
55
+ See the [Commands Reference](commands.md) for flags, examples, and diagnostics.
56
+
57
+ ## Integration
58
+
59
+ Add a generate script to your `package.json`:
60
+
61
+ ```json
62
+ {
63
+ "scripts": {
64
+ "prompts:generate": "prompts generate --out .prompts/client --roots prompts src/agents"
65
+ }
66
+ }
67
+ ```
68
+
69
+ ## References
70
+
71
+ - [Commands Reference](commands.md)
72
+ - [Code Generation](../codegen/overview.md)
73
+ - [Setup Guide](../guides/setup-project.md)
@@ -0,0 +1,83 @@
1
+ # Code Generation
2
+
3
+ The CLI transforms `.prompt` source files into typed TypeScript modules. This doc explains the pipeline stages and the shape of generated output.
4
+
5
+ ## Pipeline
6
+
7
+ ```mermaid
8
+ %%{init: {
9
+ 'theme': 'base',
10
+ 'themeVariables': {
11
+ 'primaryColor': '#313244',
12
+ 'primaryTextColor': '#cdd6f4',
13
+ 'primaryBorderColor': '#6c7086',
14
+ 'lineColor': '#89b4fa',
15
+ 'secondaryColor': '#45475a',
16
+ 'tertiaryColor': '#1e1e2e',
17
+ 'background': '#1e1e2e',
18
+ 'mainBkg': '#313244',
19
+ 'clusterBkg': '#1e1e2e',
20
+ 'clusterBorder': '#45475a'
21
+ },
22
+ 'flowchart': { 'curve': 'basis', 'padding': 15 }
23
+ }}%%
24
+ flowchart TD
25
+ classDef core fill:#313244,stroke:#89b4fa,stroke-width:2px,color:#cdd6f4
26
+ classDef agent fill:#313244,stroke:#a6e3a1,stroke-width:2px,color:#cdd6f4
27
+
28
+ subgraph Per Prompt
29
+ A[discoverPrompts]:::core --> B[parseFrontmatter]:::core
30
+ B --> C[clean]:::core
31
+ C --> D[flattenPartials]:::core
32
+ D --> E[extractVariables]:::core
33
+ E --> F[lintPrompt]:::core
34
+ end
35
+
36
+ subgraph Output
37
+ F --> G[generatePromptModule]:::agent
38
+ F --> H[generateRegistry]:::agent
39
+ end
40
+ ```
41
+
42
+ ## Pipeline Stages
43
+
44
+ | Stage | Input | Output | Description |
45
+ | ----------------- | ---------------------------- | ---------------------------------- | ------------------------------------------------------- |
46
+ | Discover | Root directories | `DiscoveredPrompt[]` | Scans for `.prompt` files (max depth 5) |
47
+ | Parse Frontmatter | Raw file content | `{ name, group, version, schema }` | Extracts and validates YAML metadata |
48
+ | Clean | Raw content | Template string | Strips frontmatter delimiters |
49
+ | Flatten Partials | Template with `{% render %}` | Resolved template | Inlines partial content with bound params |
50
+ | Extract Variables | Template string | `string[]` | Finds `{{ var }}`, `{% if var %}`, `{% for x in var %}` |
51
+ | Lint | Schema + variables | Diagnostics | Checks schema/template variable alignment |
52
+
53
+ ## Generated Output
54
+
55
+ ### Per-Prompt Module (`<name>.ts`)
56
+
57
+ Each module exports a default object conforming to `PromptModule`:
58
+
59
+ | Member | Type | Description |
60
+ | --------------------- | ------------------------ | ------------------------------------------------ |
61
+ | `name` | `string` (const) | Prompt name from frontmatter |
62
+ | `group` | `string \| undefined` | Optional grouping key |
63
+ | `schema` | `ZodObject` | Zod schema built from frontmatter `schema` block |
64
+ | `render(variables)` | `(Variables) => string` | Validates input then renders via LiquidJS |
65
+ | `validate(variables)` | `(unknown) => Variables` | Zod parse only |
66
+
67
+ ### Registry (`index.ts`)
68
+
69
+ Aggregates all per-prompt modules into a single entry point:
70
+
71
+ | Export | Type | Description |
72
+ | --------- | --------------------- | ------------------------------------------------------------------------------------------------------ |
73
+ | `prompts` | `PromptRegistry<...>` | Deep-frozen const object with dot-access, nested by group. Use `typeof prompts` for type-level access. |
74
+
75
+ ## Output Directory
76
+
77
+ Generated files go to the `--out` directory (conventionally `.prompts/client/`). This subdirectory should be gitignored. The parent `.prompts/` directory also holds `partials/` for custom partials (committed to git). Import generated code via the `~prompts` tsconfig alias.
78
+
79
+ ## References
80
+
81
+ - [File Format](../file-format/overview.md)
82
+ - [Library API](../library/overview.md)
83
+ - [CLI Commands](../cli/commands.md)
@@ -0,0 +1,55 @@
1
+ # Frontmatter Reference
2
+
3
+ The YAML frontmatter block defines metadata and the variable schema for a `.prompt` file. It is delimited by `---` fences at the top of the file.
4
+
5
+ ## Fields
6
+
7
+ | Field | Required | Type | Description |
8
+ | --------- | -------- | -------- | ------------------------------------------------ |
9
+ | `name` | Yes | `string` | Unique kebab-case identifier (`^[a-z0-9-]+$`) |
10
+ | `group` | No | `string` | Namespace path (e.g. `agents/coverage-assessor`) |
11
+ | `version` | No | `string` | Version identifier |
12
+ | `schema` | No | `object` | Variable declarations map |
13
+
14
+ ## Schema Variables
15
+
16
+ Each key under `schema` declares a template variable. Two syntaxes are supported.
17
+
18
+ **Shorthand** -- type string only, defaults to required:
19
+
20
+ ```yaml
21
+ schema:
22
+ scope: string
23
+ ```
24
+
25
+ **Full object** -- explicit control over all fields:
26
+
27
+ ```yaml
28
+ schema:
29
+ scope:
30
+ type: string
31
+ required: true
32
+ description: Assessment scope
33
+ ```
34
+
35
+ ### Variable Fields
36
+
37
+ | Field | Default | Description |
38
+ | ------------- | -------- | ---------------------------------------------------- |
39
+ | `type` | `string` | Variable type (only `string` supported) |
40
+ | `required` | `true` | Whether the variable must be provided at render time |
41
+ | `description` | -- | Human-readable description (used in generated JSDoc) |
42
+
43
+ ## Validation Rules
44
+
45
+ - `name` is required and must match `^[a-z0-9-]+$`
46
+ - Frontmatter must be valid YAML between `---` delimiters
47
+ - `schema` must be an object (not an array)
48
+ - Shorthand `scope: string` expands to `{ type: 'string', required: true }`
49
+ - Missing or empty `name` throws a parse error with the file path
50
+ - Non-object frontmatter (e.g. a bare string) is rejected
51
+
52
+ ## References
53
+
54
+ - [File Format Overview](overview.md)
55
+ - [Code Generation](../codegen/overview.md)
@@ -0,0 +1,67 @@
1
+ # .prompt File Format
2
+
3
+ A `.prompt` file is a LiquidJS template with YAML frontmatter. It is a declarative prompt authoring format compiled to typed TypeScript at build time.
4
+
5
+ ## Anatomy
6
+
7
+ ```text
8
+ ---
9
+ name: coverage-assessor
10
+ group: agents/coverage-assessor
11
+ schema:
12
+ scope:
13
+ type: string
14
+ description: Assessment scope
15
+ target:
16
+ type: string
17
+ required: false
18
+ ---
19
+
20
+ You are a coverage assessor for {{ scope }}.
21
+ {% if target %}Targeting {{ target }} docs.{% endif %}
22
+ ```
23
+
24
+ | Section | Description |
25
+ | ------------------- | ------------------------------------------------------------- |
26
+ | Frontmatter (`---`) | YAML metadata block defining name, group, and variable schema |
27
+ | Body | LiquidJS template rendered at runtime with typed variables |
28
+
29
+ ## Template Language
30
+
31
+ | Syntax | Purpose |
32
+ | --------------------------------------- | ----------------------------------- |
33
+ | `{{ var }}` | Variable output |
34
+ | `{{ var \| filter }}` | Filtered output |
35
+ | `{% if var %}...{% endif %}` | Conditional |
36
+ | `{% for item in list %}...{% endfor %}` | Iteration |
37
+ | `{% render 'name', key: 'value' %}` | Partial inclusion (build-time only) |
38
+
39
+ Strict filters are enabled -- unknown filters throw an error. Variable access is restricted to own properties only.
40
+
41
+ ## Naming Convention
42
+
43
+ Names must match `^[a-z0-9-]+$` (lowercase, digits, hyphens). The `name` field in frontmatter is required and takes precedence. A file named `prompt.prompt` derives its name from the parent directory (e.g. `agents/gap-detector/prompt.prompt` becomes `gap-detector`).
44
+
45
+ ## Discovery
46
+
47
+ The CLI scans `--roots` directories recursively (max depth 5). Files must have the `.prompt` extension. Symbolic links are skipped. Duplicate names across roots cause an error with paths listed.
48
+
49
+ Results are sorted alphabetically by name.
50
+
51
+ ## File Structure
52
+
53
+ ```text
54
+ 📁 src/
55
+ ├── 📁 agents/
56
+ │ └── 📁 coverage-assessor/
57
+ │ └── 📄 prompt.prompt
58
+ ├── 📁 prompts/
59
+ │ ├── 📄 identity.prompt
60
+ │ └── 📄 constraints.prompt
61
+ ```
62
+
63
+ ## References
64
+
65
+ - [Frontmatter Reference](frontmatter.md)
66
+ - [Partials](partials.md)
67
+ - [Author a Prompt](../guides/author-prompt.md)