@mistralys/persona-builder 2.2.0 → 2.4.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/README.md +8 -2
- package/dist/cli.cjs +75 -9
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +75 -9
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +77 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +222 -27
- package/dist/index.d.ts +222 -27
- package/dist/index.js +76 -10
- package/dist/index.js.map +1 -1
- package/package.json +53 -53
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AI Persona Builder
|
|
2
2
|
|
|
3
|
-
Build AI persona instruction files for **VS Code Chat** and **
|
|
3
|
+
Build AI persona instruction files for **VS Code Chat**, **Claude Code** and **LangGraph Deep Agents** from YAML metadata and Markdown templates — with zero configuration friction.
|
|
4
4
|
|
|
5
5
|
Define your personas once as simple YAML + Markdown sources, and the library generates correctly formatted instruction files for both IDEs. A plugin system lets you inject custom frontmatter, run validators, or post-process output without touching the core engine.
|
|
6
6
|
|
|
@@ -10,6 +10,8 @@ Define your personas once as simple YAML + Markdown sources, and the library gen
|
|
|
10
10
|
- **Extensible target registry** — register custom targets via `TargetRegistry` without touching core code; each target declares its own output key, frontmatter template, and context flags
|
|
11
11
|
- **YAML + Markdown templating** — separate metadata from content; merge them at build time with `{{variables}}`, `{{> partials}}`, and `{{#if}}` conditionals
|
|
12
12
|
- **Shared + per-suite partials** — reuse content fragments across personas with local overrides
|
|
13
|
+
- **Custom variables** — inject global or per-suite template variables via `BuildConfig.variables` and `SuiteConfig.variables` without touching persona YAML files
|
|
14
|
+
- **Dynamic partials** — supply inline partials via `BuildConfig.partials`, or override them at suite or per-persona level through the `onPartials` and `onPersonaPartials` plugin hooks
|
|
13
15
|
- **Plugin architecture** — hook into context building, post-rendering, validation, and frontmatter generation
|
|
14
16
|
- **CI-friendly** — `--check` mode renders without writing; `--strict` exits non-zero on warnings
|
|
15
17
|
- **Programmatic & CLI** — use the `build()` API in scripts or run `persona-build` from the command line
|
|
@@ -25,6 +27,9 @@ Define your personas once as simple YAML + Markdown sources, and the library gen
|
|
|
25
27
|
npm install @mistralys/persona-builder
|
|
26
28
|
```
|
|
27
29
|
|
|
30
|
+
- View on NPM: https://www.npmjs.com/package/@mistralys/persona-builder
|
|
31
|
+
- View on Github: https://github.com/Mistralys/ai-persona-builder
|
|
32
|
+
|
|
28
33
|
### Programmatic API
|
|
29
34
|
|
|
30
35
|
```ts
|
|
@@ -68,6 +73,7 @@ See the [CLI docs](docs/cli.md) for config file format and all flags.
|
|
|
68
73
|
| [Getting Started](docs/getting-started.md) | Step-by-step tutorial — build your first persona from scratch |
|
|
69
74
|
| [Directory Convention](docs/directory-convention.md) | Expected source layout (`meta/`, `content/`, `partials/`) |
|
|
70
75
|
| [Template Syntax](docs/template-syntax.md) | Variables, partials, conditionals, and built-in context variables |
|
|
76
|
+
| [Custom Variables & Dynamic Partials](docs/dynamic-partials.md) | Inject build-time variables and partial content at global, suite, or per-persona level |
|
|
71
77
|
| [Plugins](docs/plugins.md) | `PersonaBuildPlugin` interface and examples |
|
|
72
78
|
|
|
73
79
|
**Reference** — look-up material:
|
|
@@ -100,7 +106,7 @@ MIT
|
|
|
100
106
|
|
|
101
107
|
## Release Workflow
|
|
102
108
|
|
|
103
|
-
1. Add changelog entries
|
|
109
|
+
1. Add changelog entries (do not change package.json version)
|
|
104
110
|
2. `npm version 0.0.0` - Updates package and lock versions + commit
|
|
105
111
|
3. `npm publish` - Publish version on NPM
|
|
106
112
|
4. `git push origin 0.0.0` - Add the tag in GIT
|
package/dist/cli.cjs
CHANGED
|
@@ -27,10 +27,30 @@ function resolvePartials(text, partialsMap, depth = 0) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
// src/engine/conditionals.ts
|
|
30
|
+
var NO_NESTED_IF = String.raw`(?:(?!\{\{#if\b)[\s\S])*?`;
|
|
31
|
+
var ELSE_IF_PATTERN = new RegExp(
|
|
32
|
+
String.raw`\{\{else if (\w+)\}\}(${NO_NESTED_IF})\{\{\/if\}\}`,
|
|
33
|
+
"g"
|
|
34
|
+
);
|
|
35
|
+
function resolveElseIf(text) {
|
|
36
|
+
if (!text.includes("{{else if ")) {
|
|
37
|
+
return text;
|
|
38
|
+
}
|
|
39
|
+
let result = text;
|
|
40
|
+
let prev;
|
|
41
|
+
do {
|
|
42
|
+
prev = result;
|
|
43
|
+
result = result.replace(
|
|
44
|
+
ELSE_IF_PATTERN,
|
|
45
|
+
(_match, flag, content) => `{{else}}{{#if ${flag}}}${content}{{/if}}{{/if}}`
|
|
46
|
+
);
|
|
47
|
+
} while (result !== prev);
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
30
50
|
function resolveConditionals(text, context) {
|
|
31
|
-
const
|
|
51
|
+
const normalized = resolveElseIf(text);
|
|
32
52
|
const pattern = new RegExp(
|
|
33
|
-
String.raw`\n*\{\{#if (\w+)\}\}(${
|
|
53
|
+
String.raw`\n*\{\{#if (\w+)\}\}(${NO_NESTED_IF})` + String.raw`(?:\{\{else\}\}(${NO_NESTED_IF}))?\{\{\/if\}\}\n*`,
|
|
34
54
|
"g"
|
|
35
55
|
);
|
|
36
56
|
const resolve = (_match, flag, inner, elseInner) => {
|
|
@@ -42,7 +62,7 @@ function resolveConditionals(text, context) {
|
|
|
42
62
|
}
|
|
43
63
|
return "\n";
|
|
44
64
|
};
|
|
45
|
-
let result =
|
|
65
|
+
let result = normalized;
|
|
46
66
|
let prev;
|
|
47
67
|
do {
|
|
48
68
|
prev = result;
|
|
@@ -111,7 +131,7 @@ function runBuildContext(plugins, ctx, persona, suite, target) {
|
|
|
111
131
|
let accumulated = ctx;
|
|
112
132
|
for (const plugin of plugins) {
|
|
113
133
|
if (typeof plugin.onBuildContext === "function") {
|
|
114
|
-
accumulated = plugin.onBuildContext(accumulated, persona, suite, target);
|
|
134
|
+
accumulated = plugin.onBuildContext(accumulated, persona, suite, target) ?? accumulated;
|
|
115
135
|
}
|
|
116
136
|
}
|
|
117
137
|
return accumulated;
|
|
@@ -120,11 +140,29 @@ function runPostRender(plugins, rendered, persona, target) {
|
|
|
120
140
|
let output = rendered;
|
|
121
141
|
for (const plugin of plugins) {
|
|
122
142
|
if (typeof plugin.onPostRender === "function") {
|
|
123
|
-
output = plugin.onPostRender(output, persona, target);
|
|
143
|
+
output = plugin.onPostRender(output, persona, target) ?? output;
|
|
124
144
|
}
|
|
125
145
|
}
|
|
126
146
|
return output;
|
|
127
147
|
}
|
|
148
|
+
function runPartials(plugins, partialsMap, suiteName, suite) {
|
|
149
|
+
let accumulated = partialsMap;
|
|
150
|
+
for (const plugin of plugins) {
|
|
151
|
+
if (typeof plugin.onPartials === "function") {
|
|
152
|
+
accumulated = plugin.onPartials(accumulated, suiteName, suite) ?? accumulated;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return accumulated;
|
|
156
|
+
}
|
|
157
|
+
function runPersonaPartials(plugins, partialsMap, persona, context, suite, target) {
|
|
158
|
+
let accumulated = partialsMap;
|
|
159
|
+
for (const plugin of plugins) {
|
|
160
|
+
if (typeof plugin.onPersonaPartials === "function") {
|
|
161
|
+
accumulated = plugin.onPersonaPartials(accumulated, persona, context, suite, target) ?? accumulated;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return accumulated;
|
|
165
|
+
}
|
|
128
166
|
function runValidate(plugins, persona, suite, target) {
|
|
129
167
|
const results = [];
|
|
130
168
|
for (const plugin of plugins) {
|
|
@@ -347,9 +385,20 @@ async function buildAgentNameMap(config) {
|
|
|
347
385
|
}
|
|
348
386
|
return agentMap;
|
|
349
387
|
}
|
|
350
|
-
function buildContext(
|
|
388
|
+
function buildContext(options) {
|
|
389
|
+
const {
|
|
390
|
+
personaMeta,
|
|
391
|
+
sharedMeta,
|
|
392
|
+
agentMap = {},
|
|
393
|
+
target,
|
|
394
|
+
registry,
|
|
395
|
+
configVariables,
|
|
396
|
+
suiteVariables
|
|
397
|
+
} = options;
|
|
351
398
|
const version = typeof personaMeta["version"] === "string" ? personaMeta["version"] : typeof sharedMeta["default_version"] === "string" ? sharedMeta["default_version"] : "0.0.0";
|
|
352
399
|
const merged = {
|
|
400
|
+
...configVariables ?? {},
|
|
401
|
+
...suiteVariables ?? {},
|
|
353
402
|
...sharedMeta,
|
|
354
403
|
...personaMeta,
|
|
355
404
|
version
|
|
@@ -404,16 +453,32 @@ function buildContext(personaMeta, sharedMeta, agentMap = {}, target, registry)
|
|
|
404
453
|
}
|
|
405
454
|
async function buildPersona(personaYamlPath, suiteName, suiteConfig, sharedMeta, partialsMap, config, plugins, target, agentMap = {}, registry = defaultRegistry) {
|
|
406
455
|
const personaMeta = await loadPersonaYaml(personaYamlPath);
|
|
407
|
-
let context = buildContext(
|
|
456
|
+
let context = buildContext({
|
|
457
|
+
personaMeta,
|
|
458
|
+
sharedMeta,
|
|
459
|
+
agentMap,
|
|
460
|
+
target,
|
|
461
|
+
registry,
|
|
462
|
+
configVariables: config.variables,
|
|
463
|
+
suiteVariables: suiteConfig.variables
|
|
464
|
+
});
|
|
408
465
|
const personaMetaTyped = personaMeta;
|
|
409
466
|
context = runBuildContext(plugins, context, personaMetaTyped, suiteConfig, target);
|
|
467
|
+
const personaPartialsMap = runPersonaPartials(
|
|
468
|
+
plugins,
|
|
469
|
+
{ ...partialsMap },
|
|
470
|
+
personaMetaTyped,
|
|
471
|
+
context,
|
|
472
|
+
suiteConfig,
|
|
473
|
+
target
|
|
474
|
+
);
|
|
410
475
|
const fmTemplate = resolveFrontmatterTemplate(target, plugins, config.frontmatter, registry);
|
|
411
476
|
const contentBasename = path2__default.default.basename(personaYamlPath, ".yaml") + ".md";
|
|
412
477
|
const frontmatter = renderFrontmatter(fmTemplate, context, contentBasename);
|
|
413
478
|
const contentSubdir = suiteConfig.contentSubdir ?? "content";
|
|
414
479
|
const contentPath = path2__default.default.join(suiteConfig.srcDir, contentSubdir, contentBasename);
|
|
415
480
|
const bodyTemplate = normalizeNewlines(await promises.readFile(contentPath, "utf8"));
|
|
416
|
-
let body = resolvePartials(bodyTemplate,
|
|
481
|
+
let body = resolvePartials(bodyTemplate, personaPartialsMap);
|
|
417
482
|
body = resolveConditionals(body, context);
|
|
418
483
|
body = resolveVariables(body, context, contentBasename);
|
|
419
484
|
body = collapseBlankLines(body);
|
|
@@ -451,7 +516,7 @@ async function buildSuite(suiteName, suiteConfig, config, plugins, target, agent
|
|
|
451
516
|
const metaSubdir = suiteConfig.metaSubdir ?? "meta";
|
|
452
517
|
const sharedYamlPath = path2__default.default.join(suiteConfig.srcDir, metaSubdir, "_shared.yaml");
|
|
453
518
|
const sharedMeta = await loadRawYaml(sharedYamlPath);
|
|
454
|
-
let partialsMap = {};
|
|
519
|
+
let partialsMap = { ...config.partials ?? {} };
|
|
455
520
|
if (config.sharedPartialsDir && fs.existsSync(config.sharedPartialsDir)) {
|
|
456
521
|
partialsMap = { ...partialsMap, ...await loadPartials(config.sharedPartialsDir) };
|
|
457
522
|
}
|
|
@@ -461,6 +526,7 @@ async function buildSuite(suiteName, suiteConfig, config, plugins, target, agent
|
|
|
461
526
|
partialsMap = { ...partialsMap, ...await loadPartials(suitePartialsDir) };
|
|
462
527
|
}
|
|
463
528
|
runSuiteInit(plugins, suiteConfig, sharedMeta);
|
|
529
|
+
partialsMap = runPartials(plugins, partialsMap, suiteName, suiteConfig);
|
|
464
530
|
const personaYamlPaths = await discoverSuitePersonaYamls(suiteConfig);
|
|
465
531
|
const results = [];
|
|
466
532
|
for (const yamlPath of personaYamlPaths) {
|