@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,87 @@
1
+ # Partials
2
+
3
+ Partials are reusable template fragments included with `{% render %}` tags. They are resolved and flattened at build time -- the generated output contains no render tags.
4
+
5
+ ## Syntax
6
+
7
+ ```liquid
8
+ {% render 'identity', role: 'Coverage Assessor', desc: 'an expert at assessing documentation coverage' %}
9
+ ```
10
+
11
+ Only literal string parameters are supported. Variable references (e.g. `key: myVar`) are not allowed and throw an error at codegen time. Whitespace trim variants `{%-` and `-%}` are supported.
12
+
13
+ ## Resolution Order
14
+
15
+ Partials are resolved from two locations, searched in order (first match wins):
16
+
17
+ | Priority | Location | Description |
18
+ | -------- | -------------------- | ------------------------------------------------ |
19
+ | 1 | `.prompts/partials/` | Custom project partials (committed to git) |
20
+ | 2 | SDK `src/prompts/` | Built-in partials shipped with `@funkai/prompts` |
21
+
22
+ Custom partials take precedence — a custom partial with the same name as a built-in overrides it.
23
+
24
+ ## Built-in Partials
25
+
26
+ | Partial | Parameters | Purpose |
27
+ | ------------- | -------------------------------------------------- | --------------------------------------------------- |
28
+ | `identity` | `role`, `desc`, `context` (optional) | Agent identity block (`<identity>` wrapper) |
29
+ | `constraints` | `in_scope`, `out_of_scope`, `rules` (all optional) | Scoping constraints block (`<constraints>` wrapper) |
30
+ | `tools` | `tools` (optional) | Tool listing block (`<tools>` wrapper) |
31
+
32
+ ## Identity Partial
33
+
34
+ ```liquid
35
+ <identity>
36
+ You are {{ role }}, {{ desc }}.
37
+ {% if context %}
38
+ {{ context }}
39
+ {% endif %}
40
+ </identity>
41
+ ```
42
+
43
+ ## Constraints Partial
44
+
45
+ ```liquid
46
+ <constraints>
47
+ {% if in_scope %}
48
+ ## In Scope
49
+ {% for item in in_scope %}
50
+ - {{ item }}
51
+ {% endfor %}
52
+ {% endif %}
53
+ {% if out_of_scope %}
54
+ ## Out of Scope
55
+ {% for item in out_of_scope %}
56
+ - {{ item }}
57
+ {% endfor %}
58
+ {% endif %}
59
+ {% if rules %}
60
+ ## Rules
61
+ {% for rule in rules %}
62
+ - {{ rule }}
63
+ {% endfor %}
64
+ {% endif %}
65
+ </constraints>
66
+ ```
67
+
68
+ ## Custom Partials
69
+
70
+ Place custom `.prompt` files in `.prompts/partials/`:
71
+
72
+ ```
73
+ 📁 .prompts/
74
+ ├── 📁 client/ # Generated (gitignored)
75
+ └── 📁 partials/ # Custom partials (committed)
76
+ └── 📄 summary.prompt
77
+ ```
78
+
79
+ The CLI auto-discovers this directory:
80
+
81
+ - `prompts generate` derives it from `--out` (sibling `partials/` dir)
82
+ - `prompts lint` defaults to `.prompts/partials` (configurable via `--partials`)
83
+
84
+ ## References
85
+
86
+ - [File Format Overview](overview.md)
87
+ - [Add a Partial](../guides/add-partial.md)
@@ -0,0 +1,75 @@
1
+ # Add a Partial
2
+
3
+ ## Custom Partials
4
+
5
+ Custom partials live in `.prompts/partials/` and are auto-discovered at build time. They take precedence over built-in SDK partials.
6
+
7
+ ### Steps
8
+
9
+ 1. Scaffold with the CLI or create manually:
10
+
11
+ ```bash
12
+ prompts create summary --partial
13
+ ```
14
+
15
+ Or create `.prompts/partials/<name>.prompt` by hand:
16
+
17
+ ```liquid
18
+ <summary>
19
+ {{ content }}
20
+ {% if notes %}
21
+ Notes: {{ notes }}
22
+ {% endif %}
23
+ </summary>
24
+ ```
25
+
26
+ 2. Use in a `.prompt` file:
27
+
28
+ ```liquid
29
+ {% render 'summary', content: 'Analysis complete' %}
30
+ ```
31
+
32
+ 3. Run `prompts generate` — the partial is flattened into the generated output.
33
+
34
+ ### Overriding Built-ins
35
+
36
+ To override a built-in partial, create a file with the same name in `.prompts/partials/` (e.g. `.prompts/partials/identity.prompt`). Custom partials take precedence over SDK built-ins.
37
+
38
+ ## Built-in Partials
39
+
40
+ Built-in partials require access to the `@funkai/prompts` source.
41
+
42
+ ### Steps
43
+
44
+ 1. Create `packages/prompts/src/prompts/<name>.prompt`.
45
+
46
+ 2. Write the partial template — use XML-style wrapper tags and Liquid variables:
47
+
48
+ ```liquid
49
+ <output>
50
+ {{ content }}
51
+ </output>
52
+ ```
53
+
54
+ 3. Test with a consumer `.prompt` file and run `prompts generate`.
55
+
56
+ 4. Document the new partial in `docs/file-format/partials.md`.
57
+
58
+ ## Verification
59
+
60
+ The generated `.ts` module contains the flattened partial content. No `{% render %}` tags remain.
61
+
62
+ ## Troubleshooting
63
+
64
+ ### Variable reference not supported
65
+
66
+ **Fix:** Only literal string params are allowed in `{% render %}` tags. Replace variable references with string literals.
67
+
68
+ ### Partial not found
69
+
70
+ **Fix:** Verify the file is in `.prompts/partials/` (custom) or `src/prompts/` (built-in) with `.prompt` extension.
71
+
72
+ ## References
73
+
74
+ - [Partials Reference](../file-format/partials.md)
75
+ - [File Format](../file-format/overview.md)
@@ -0,0 +1,70 @@
1
+ # Author a Prompt
2
+
3
+ ## Prerequisites
4
+
5
+ - `@funkai/prompts` installed
6
+ - Project configured ([Setup guide](setup-project.md))
7
+
8
+ ## Steps
9
+
10
+ 1. Scaffold with the CLI:
11
+
12
+ ```bash
13
+ prompts create my-agent --out src/agents/my-agent
14
+ ```
15
+
16
+ 2. Edit the frontmatter — set `name`, `group`, and `schema` variables.
17
+
18
+ 3. Write the template body using `{{ var }}` syntax and conditionals.
19
+
20
+ 4. Add partials if needed:
21
+
22
+ ```liquid
23
+ {% render 'identity', role: 'Analyzer', desc: 'a code analyzer' %}
24
+ ```
25
+
26
+ 5. Lint:
27
+
28
+ ```bash
29
+ prompts lint --roots src/agents
30
+ ```
31
+
32
+ 6. Generate:
33
+
34
+ ```bash
35
+ prompts generate --out .prompts/client --roots src/agents
36
+ ```
37
+
38
+ 7. Import and use:
39
+
40
+ ```ts
41
+ import { prompts } from "~prompts";
42
+
43
+ const text = prompts.myAgent.render({ scope: "full" });
44
+ ```
45
+
46
+ ## Verification
47
+
48
+ - `prompts lint` reports no errors
49
+ - Generated file exists at `.prompts/client/my-agent.ts`
50
+ - TypeScript compiles without errors
51
+
52
+ ## Troubleshooting
53
+
54
+ ### Undefined variable error
55
+
56
+ **Fix:** Add the variable to the frontmatter `schema` block.
57
+
58
+ ### Duplicate prompt name
59
+
60
+ **Fix:** Two `.prompt` files share the same `name` — rename one to a unique kebab-case identifier.
61
+
62
+ ### TypeScript can't find `~prompts`
63
+
64
+ **Fix:** Run `prompts setup` or add the path alias to `tsconfig.json`.
65
+
66
+ ## References
67
+
68
+ - [File Format](../file-format/overview.md)
69
+ - [CLI Commands](../cli/commands.md)
70
+ - [Partials](../file-format/partials.md)
@@ -0,0 +1,75 @@
1
+ # Setup Prompt Development
2
+
3
+ ## Prerequisites
4
+
5
+ - Node 24
6
+ - pnpm workspace
7
+
8
+ ## Steps
9
+
10
+ 1. Install:
11
+
12
+ ```bash
13
+ pnpm add @funkai/prompts --workspace
14
+ ```
15
+
16
+ 2. Run interactive setup:
17
+
18
+ ```bash
19
+ prompts setup
20
+ ```
21
+
22
+ Or configure manually (steps 3-6).
23
+
24
+ 3. Add VSCode file association in `.vscode/settings.json`:
25
+
26
+ ```json
27
+ {
28
+ "files.associations": {
29
+ "*.prompt": "markdown"
30
+ }
31
+ }
32
+ ```
33
+
34
+ 4. Add `.prompts/client/` to `.gitignore`.
35
+
36
+ 5. Add `~prompts` path alias to `tsconfig.json`:
37
+
38
+ ```json
39
+ {
40
+ "compilerOptions": {
41
+ "paths": {
42
+ "~prompts": ["./.prompts/client/index.ts"]
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ 6. Add generate script to `package.json`:
49
+
50
+ ```json
51
+ {
52
+ "scripts": {
53
+ "prompts:generate": "prompts generate --out .prompts/client --roots prompts src/agents"
54
+ }
55
+ }
56
+ ```
57
+
58
+ ## Verification
59
+
60
+ Run `prompts generate` and verify `.prompts/client/` directory is created with an `index.ts`.
61
+
62
+ ## Troubleshooting
63
+
64
+ ### VSCode not highlighting `.prompt` files
65
+
66
+ **Fix:** Check `.vscode/settings.json` file association is set correctly.
67
+
68
+ ### TypeScript can't resolve `~prompts`
69
+
70
+ **Fix:** Verify `tsconfig.json` paths alias points to `./.prompts/client/index.ts`.
71
+
72
+ ## References
73
+
74
+ - [CLI Commands](../cli/commands.md)
75
+ - [Overview](../overview.md)
@@ -0,0 +1,64 @@
1
+ # Library API
2
+
3
+ The library surface provides the runtime engine and registry used by generated code and consuming packages.
4
+
5
+ ## Exports
6
+
7
+ | Export | Type | Description |
8
+ | ---------------------- | ----------------------------------- | -------------------------------------------------------------------------- |
9
+ | `engine` | `Liquid` | Shared LiquidJS instance (no filesystem, strict filters) |
10
+ | `createEngine` | `(partialsDir, options?) => Liquid` | Factory for filesystem-backed engines (used by CLI for partial resolution) |
11
+ | `clean` | `(content: string) => string` | Strips frontmatter, returns render-ready template |
12
+ | `createPromptRegistry` | `(modules) => PromptRegistry` | Creates typed registry from prompt module map |
13
+
14
+ ## Types
15
+
16
+ | Type | Description |
17
+ | --------------------- | ------------------------------------------------------------------------------------------------------------------------- |
18
+ | `PromptModule` | Interface: `name`, `group`, `schema` (ZodType), `render(vars)`, `validate(vars)` |
19
+ | `PromptNamespace` | A nested namespace node — values are `PromptModule` leaves or further nested namespaces |
20
+ | `PromptRegistry` | Deep-readonly mapped type over a `PromptNamespace` tree |
21
+ | `CreateEngineOptions` | Options for `createEngine`: `root`, `partials`, `extname`, `cache`, `strictFilters`, `strictVariables`, `ownPropertyOnly` |
22
+ | `Liquid` | Re-exported LiquidJS engine type |
23
+
24
+ ## Engine
25
+
26
+ The shared `engine` instance is configured with `ownPropertyOnly: true` and `strictFilters: true` for security. No filesystem access -- templates are rendered from strings via `parseAndRenderSync`.
27
+
28
+ `createEngine` accepts a `partialsDir` and optional overrides. It enables filesystem-backed partial resolution (`.prompt` extension, caching enabled) for use during codegen flattening.
29
+
30
+ ## Registry
31
+
32
+ `createPromptRegistry` accepts a (possibly nested) record of `PromptModule` objects and namespace nodes. It returns a deep-frozen `PromptRegistry` with direct property access:
33
+
34
+ ```ts
35
+ const prompts = createPromptRegistry({
36
+ agents: { coverageAssessor },
37
+ greeting,
38
+ });
39
+ prompts.agents.coverageAssessor.render({ scope: "full" });
40
+ prompts.greeting.render();
41
+ ```
42
+
43
+ Nesting is driven by the `group` field in frontmatter. Each `/`-separated segment becomes a nesting level, with all names converted to camelCase. The registry is frozen at creation time to prevent mutation.
44
+
45
+ ## Consumer Pattern
46
+
47
+ The generated `index.ts` calls `createPromptRegistry` with all prompt modules organized by group and exports a `prompts` const object. Consumers import via the `~prompts` tsconfig alias:
48
+
49
+ ```ts
50
+ import { prompts } from "~prompts";
51
+
52
+ // Flat (no group)
53
+ const text = prompts.greeting.render();
54
+
55
+ // Nested (group: agents)
56
+ const text = prompts.agents.coverageAssessor.render({ scope: "full" });
57
+ ```
58
+
59
+ Types are inferred from the object structure, giving full type safety on `render` and `validate` arguments at every nesting level.
60
+
61
+ ## References
62
+
63
+ - [Code Generation](../codegen/overview.md)
64
+ - [Overview](../overview.md)
@@ -0,0 +1,102 @@
1
+ # Prompts SDK Overview
2
+
3
+ Prompt authoring SDK with two surfaces: a **CLI** for build-time code generation from `.prompt` files, and a **library** for runtime template rendering with full type safety.
4
+
5
+ ## Architecture
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 LR
25
+ P[".prompt files"]:::external
26
+
27
+ subgraph CLI ["CLI (Build Time)"]
28
+ direction LR
29
+ D[Discover] --> PA[Parse] --> L[Lint] --> F[Flatten] --> C[Codegen]
30
+ end
31
+
32
+ subgraph RT ["Runtime"]
33
+ direction LR
34
+ E[Engine] --> R[Registry]
35
+ end
36
+
37
+ P --> D
38
+ C --> G["Generated .prompts/client/*.ts"]:::agent
39
+ G --> E
40
+ R --> O["Rendered string"]:::agent
41
+
42
+ class D,PA,L,F,C core
43
+ class E,R gateway
44
+
45
+ style CLI fill:none,stroke:#89b4fa,stroke-width:2px,stroke-dasharray:5 5
46
+ style RT fill:none,stroke:#fab387,stroke-width:2px,stroke-dasharray:5 5
47
+
48
+ classDef external fill:#313244,stroke:#f5c2e7,stroke-width:2px,color:#cdd6f4
49
+ classDef core fill:#313244,stroke:#89b4fa,stroke-width:2px,color:#cdd6f4
50
+ classDef agent fill:#313244,stroke:#a6e3a1,stroke-width:2px,color:#cdd6f4
51
+ classDef gateway fill:#313244,stroke:#fab387,stroke-width:2px,color:#cdd6f4
52
+ ```
53
+
54
+ ## Package Structure
55
+
56
+ ```
57
+ 📁 packages/prompts/
58
+ ├── 📁 src/
59
+ │ ├── 📁 prompts/ # Built-in partials (identity, constraints, tools)
60
+ │ ├── 📄 engine.ts # LiquidJS engine factory
61
+ │ ├── 📄 registry.ts # Typed prompt registry
62
+ │ ├── 📄 clean.ts # Frontmatter stripping pipeline
63
+ │ ├── 📄 partials-dir.ts # PARTIALS_DIR export for CLI/consumers
64
+ │ ├── 📄 types.ts # PromptModule, PromptNamespace, PromptRegistry types
65
+ │ └── 📄 index.ts # Public exports
66
+ └── 📁 docs/
67
+
68
+ 📁 packages/cli/ # @funkai/cli — CLI binary (see @funkai/cli README)
69
+ ├── 📁 commands/ # generate, lint, create, setup
70
+ ├── 📁 src/lib/ # codegen, frontmatter, flatten, lint, paths
71
+ └── 📄 index.ts # CLI entry point (kidd-cli)
72
+ ```
73
+
74
+ > **Note:** The CLI was extracted to `@funkai/cli`. Install it separately for the `prompts` binary.
75
+
76
+ ## Dual Surface
77
+
78
+ | Surface | When | What |
79
+ | ------- | ---------- | ---------------------------------------------------------------------------- |
80
+ | CLI | Build time | Discovers `.prompt` files, validates frontmatter, generates typed TS modules |
81
+ | Library | Runtime | LiquidJS engine renders templates, registry provides typed access |
82
+
83
+ ## Quick Start
84
+
85
+ 1. Create a `.prompt` file with YAML frontmatter and a LiquidJS template body.
86
+ 2. Run `prompts generate --out .prompts/client --roots src/agents` to produce typed modules.
87
+ 3. Import from the `~prompts` alias in your application code.
88
+ 4. Call `.render({ vars })` with full type safety derived from the Zod schema in frontmatter.
89
+
90
+ ## References
91
+
92
+ - [File Format](file-format/overview.md)
93
+ - [Frontmatter](file-format/frontmatter.md)
94
+ - [Partials](file-format/partials.md)
95
+ - [CLI](cli/overview.md)
96
+ - [CLI Commands](cli/commands.md)
97
+ - [Code Generation](codegen/overview.md)
98
+ - [Library API](library/overview.md)
99
+ - [Guide: Author a Prompt](guides/author-prompt.md)
100
+ - [Guide: Setup Project](guides/setup-project.md)
101
+ - [Guide: Add a Partial](guides/add-partial.md)
102
+ - [Troubleshooting](troubleshooting.md)
@@ -0,0 +1,37 @@
1
+ # Troubleshooting
2
+
3
+ ## Undefined variable lint error
4
+
5
+ **Fix:** Add the variable to the frontmatter `schema` block, or remove it from the template.
6
+
7
+ ## Unused variable lint warning
8
+
9
+ **Fix:** Remove the variable from `schema`, or use it in the template body.
10
+
11
+ ## Duplicate prompt name
12
+
13
+ **Fix:** Two `.prompt` files share the same `name` field. Rename one to a unique kebab-case identifier.
14
+
15
+ ## Invalid prompt name
16
+
17
+ **Fix:** Names must match `^[a-z0-9-]+$` — lowercase letters, digits, and hyphens only.
18
+
19
+ ## Partial variable reference error
20
+
21
+ **Fix:** Only literal string parameters are supported in `{% render %}` tags. Replace variable references with string literals.
22
+
23
+ ## Dangerous variable name
24
+
25
+ **Fix:** `__proto__`, `constructor`, and `prototype` are forbidden variable names. Choose a different name.
26
+
27
+ ## TypeScript can't find `~prompts`
28
+
29
+ **Fix:** Add the path alias to `tsconfig.json`: `"~prompts": ["./.prompts/client/index.ts"]`. Or run `prompts setup`.
30
+
31
+ ## Generated files not updating
32
+
33
+ **Fix:** Re-run `prompts generate` after editing `.prompt` files. Generated output is not watched.
34
+
35
+ ## VSCode shows `.prompt` as plain text
36
+
37
+ **Fix:** Run `prompts setup` or add `"files.associations": { "*.prompt": "markdown" }` to `.vscode/settings.json`.
package/logo.svg ADDED
@@ -0,0 +1,20 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 550 160">
2
+ <defs>
3
+ <style>
4
+ .text { font-family: 'SF Mono', 'Fira Code', 'JetBrains Mono', Consolas, monospace; }
5
+ .brand { fill: #a78bfa; }
6
+ .dim { fill: #6c7086; }
7
+ </style>
8
+ </defs>
9
+
10
+ <g transform="translate(24, 20)">
11
+ <text class="text brand" font-size="13" y="0" xml:space="preserve">██████╗ ██████╗ ██████╗ ███╗ ███╗██████╗ ████████╗███████╗</text>
12
+ <text class="text brand" font-size="13" y="16" xml:space="preserve">██╔══██╗██╔══██╗██╔═══██╗████╗ ████║██╔══██╗╚══██╔══╝██╔════╝</text>
13
+ <text class="text brand" font-size="13" y="32" xml:space="preserve">██████╔╝██████╔╝██║ ██║██╔████╔██║██████╔╝ ██║ ███████╗</text>
14
+ <text class="text brand" font-size="13" y="48" xml:space="preserve">██╔═══╝ ██╔══██╗██║ ██║██║╚██╔╝██║██╔═══╝ ██║ ╚════██║</text>
15
+ <text class="text brand" font-size="13" y="64" xml:space="preserve">██║ ██║ ██║╚██████╔╝██║ ╚═╝ ██║██║ ██║ ███████║</text>
16
+ <text class="text brand" font-size="13" y="80" xml:space="preserve">╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚══════╝</text>
17
+
18
+ <text class="text dim" font-size="12" y="112" x="0">prompt sdk with liquidjs templating and zod validation</text>
19
+ </g>
20
+ </svg>
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@funkai/prompts",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "description": "Prompt SDK with LiquidJS templating and Zod validation",
6
+ "keywords": [
7
+ "ai",
8
+ "codegen",
9
+ "liquidjs",
10
+ "prompts",
11
+ "templates",
12
+ "typescript",
13
+ "zod"
14
+ ],
15
+ "homepage": "https://github.com/joggrdocs/funkai/tree/main/packages/prompts#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/joggrdocs/funkai/issues"
18
+ },
19
+ "license": "MIT",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/joggrdocs/funkai.git",
23
+ "directory": "packages/prompts"
24
+ },
25
+ "type": "module",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/lib/index.d.mts",
29
+ "import": "./dist/lib/index.mjs"
30
+ }
31
+ },
32
+ "dependencies": {
33
+ "es-toolkit": "^1.45.1",
34
+ "liquidjs": "^10.25.0",
35
+ "zod": "^4.3.6"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^25.5.0",
39
+ "@vitest/coverage-v8": "^4.1.0",
40
+ "tsdown": "^0.21.2",
41
+ "typescript": "^5.9.3",
42
+ "vitest": "^4.1.0"
43
+ },
44
+ "engines": {
45
+ "node": ">=24.0.0"
46
+ },
47
+ "scripts": {
48
+ "build": "tsdown && cp -r src/prompts dist/prompts",
49
+ "typecheck": "tsc --noEmit",
50
+ "test": "vitest run",
51
+ "test:coverage": "vitest run --coverage"
52
+ }
53
+ }
@@ -0,0 +1,44 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { clean } from "@/clean.js";
4
+
5
+ describe("clean", () => {
6
+ it("should strip YAML frontmatter from a prompt file", () => {
7
+ const input = "---\nname: test-prompt\ngroup: agents\n---\nHello {{ name }}";
8
+ expect(clean(input)).toBe("Hello {{ name }}");
9
+ });
10
+
11
+ it("should return the string unchanged when no frontmatter is present", () => {
12
+ const input = "Hello {{ name }}";
13
+ expect(clean(input)).toBe("Hello {{ name }}");
14
+ });
15
+
16
+ it("should handle Windows-style line endings", () => {
17
+ const input = "---\r\nname: test\r\n---\r\nHello {{ name }}";
18
+ expect(clean(input)).toBe("Hello {{ name }}");
19
+ });
20
+
21
+ it("should handle frontmatter with no trailing newline after closing delimiter", () => {
22
+ const input = "---\nname: test\n---";
23
+ expect(clean(input)).toBe("");
24
+ });
25
+
26
+ it("should return an empty string when input is empty", () => {
27
+ expect(clean("")).toBe("");
28
+ });
29
+
30
+ it("should preserve multiline template body after frontmatter", () => {
31
+ const input = [
32
+ "---",
33
+ "name: multi-line",
34
+ "schema:",
35
+ " name: string",
36
+ " age: number",
37
+ "---",
38
+ "Line 1",
39
+ "Line 2",
40
+ "{{ variable }}",
41
+ ].join("\n");
42
+ expect(clean(input)).toBe("Line 1\nLine 2\n{{ variable }}");
43
+ });
44
+ });
package/src/clean.ts ADDED
@@ -0,0 +1,25 @@
1
+ import { flow } from "es-toolkit";
2
+
3
+ const FRONTMATTER_RE = /^---\r?\n[\s\S]*?\r?\n---\r?\n?/;
4
+
5
+ /**
6
+ * Remove YAML frontmatter from the beginning of a string.
7
+ *
8
+ * Frontmatter is a block delimited by `---` at the start of the file.
9
+ * If no frontmatter is present, the string is returned unchanged.
10
+ */
11
+ function stripFrontmatter(text: string): string {
12
+ return text.replace(FRONTMATTER_RE, "");
13
+ }
14
+
15
+ const pipeline = flow(stripFrontmatter);
16
+
17
+ /**
18
+ * Clean a raw `.prompt` file into a render-ready template.
19
+ *
20
+ * Runs the source through a pipeline of transforms — currently
21
+ * strips frontmatter, with more steps added over time.
22
+ */
23
+ export function clean(text: string): string {
24
+ return pipeline(text);
25
+ }