@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.
- package/.turbo/turbo-build.log +18 -0
- package/.turbo/turbo-test$colon$coverage.log +26 -0
- package/.turbo/turbo-test.log +26 -0
- package/.turbo/turbo-typecheck.log +4 -0
- package/CHANGELOG.md +14 -0
- package/LICENSE +21 -0
- package/README.md +101 -0
- package/banner.svg +100 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/clean.ts.html +160 -0
- package/coverage/lcov-report/engine.ts.html +196 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +161 -0
- package/coverage/lcov-report/partials-dir.ts.html +100 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/registry.ts.html +280 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +75 -0
- package/dist/lib/index.d.mts +99 -0
- package/dist/lib/index.d.mts.map +1 -0
- package/dist/lib/index.mjs +117 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/prompts/constraints.prompt +20 -0
- package/dist/prompts/identity.prompt +6 -0
- package/dist/prompts/prompts/constraints.prompt +20 -0
- package/dist/prompts/prompts/identity.prompt +6 -0
- package/dist/prompts/prompts/tools.prompt +14 -0
- package/dist/prompts/tools.prompt +14 -0
- package/docs/cli/commands.md +73 -0
- package/docs/cli/overview.md +73 -0
- package/docs/codegen/overview.md +83 -0
- package/docs/file-format/frontmatter.md +55 -0
- package/docs/file-format/overview.md +67 -0
- package/docs/file-format/partials.md +87 -0
- package/docs/guides/add-partial.md +75 -0
- package/docs/guides/author-prompt.md +70 -0
- package/docs/guides/setup-project.md +75 -0
- package/docs/library/overview.md +64 -0
- package/docs/overview.md +102 -0
- package/docs/troubleshooting.md +37 -0
- package/logo.svg +20 -0
- package/package.json +53 -0
- package/src/clean.test.ts +44 -0
- package/src/clean.ts +25 -0
- package/src/engine.test.ts +44 -0
- package/src/engine.ts +37 -0
- package/src/index.ts +11 -0
- package/src/partials-dir.test.ts +15 -0
- package/src/partials-dir.ts +5 -0
- package/src/prompts/constraints.prompt +20 -0
- package/src/prompts/identity.prompt +6 -0
- package/src/prompts/tools.prompt +14 -0
- package/src/registry.test.ts +69 -0
- package/src/registry.ts +65 -0
- package/src/types.ts +62 -0
- package/tsconfig.json +25 -0
- package/tsdown.config.ts +12 -0
- 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)
|
package/docs/overview.md
ADDED
|
@@ -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
|
+
}
|