@mistralys/persona-builder 2.3.0 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/cli.cjs +71 -7
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +71 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +73 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +223 -26
- package/dist/index.d.ts +223 -26
- package/dist/index.js +72 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -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
|
|
@@ -71,6 +73,7 @@ See the [CLI docs](docs/cli.md) for config file format and all flags.
|
|
|
71
73
|
| [Getting Started](docs/getting-started.md) | Step-by-step tutorial — build your first persona from scratch |
|
|
72
74
|
| [Directory Convention](docs/directory-convention.md) | Expected source layout (`meta/`, `content/`, `partials/`) |
|
|
73
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 |
|
|
74
77
|
| [Plugins](docs/plugins.md) | `PersonaBuildPlugin` interface and examples |
|
|
75
78
|
|
|
76
79
|
**Reference** — look-up material:
|
package/dist/cli.cjs
CHANGED
|
@@ -131,7 +131,7 @@ function runBuildContext(plugins, ctx, persona, suite, target) {
|
|
|
131
131
|
let accumulated = ctx;
|
|
132
132
|
for (const plugin of plugins) {
|
|
133
133
|
if (typeof plugin.onBuildContext === "function") {
|
|
134
|
-
accumulated = plugin.onBuildContext(accumulated, persona, suite, target);
|
|
134
|
+
accumulated = plugin.onBuildContext(accumulated, persona, suite, target) ?? accumulated;
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
return accumulated;
|
|
@@ -140,11 +140,29 @@ function runPostRender(plugins, rendered, persona, target) {
|
|
|
140
140
|
let output = rendered;
|
|
141
141
|
for (const plugin of plugins) {
|
|
142
142
|
if (typeof plugin.onPostRender === "function") {
|
|
143
|
-
output = plugin.onPostRender(output, persona, target);
|
|
143
|
+
output = plugin.onPostRender(output, persona, target) ?? output;
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
return output;
|
|
147
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
|
+
}
|
|
148
166
|
function runValidate(plugins, persona, suite, target) {
|
|
149
167
|
const results = [];
|
|
150
168
|
for (const plugin of plugins) {
|
|
@@ -367,9 +385,20 @@ async function buildAgentNameMap(config) {
|
|
|
367
385
|
}
|
|
368
386
|
return agentMap;
|
|
369
387
|
}
|
|
370
|
-
function buildContext(
|
|
388
|
+
function buildContext(options) {
|
|
389
|
+
const {
|
|
390
|
+
personaMeta,
|
|
391
|
+
sharedMeta,
|
|
392
|
+
agentMap = {},
|
|
393
|
+
target,
|
|
394
|
+
registry,
|
|
395
|
+
configVariables,
|
|
396
|
+
suiteVariables
|
|
397
|
+
} = options;
|
|
371
398
|
const version = typeof personaMeta["version"] === "string" ? personaMeta["version"] : typeof sharedMeta["default_version"] === "string" ? sharedMeta["default_version"] : "0.0.0";
|
|
372
399
|
const merged = {
|
|
400
|
+
...configVariables ?? {},
|
|
401
|
+
...suiteVariables ?? {},
|
|
373
402
|
...sharedMeta,
|
|
374
403
|
...personaMeta,
|
|
375
404
|
version
|
|
@@ -422,18 +451,49 @@ function buildContext(personaMeta, sharedMeta, agentMap = {}, target, registry)
|
|
|
422
451
|
}
|
|
423
452
|
return merged;
|
|
424
453
|
}
|
|
454
|
+
function validateSubagentRefs(persona, agentMap) {
|
|
455
|
+
const subagents = persona.subagents;
|
|
456
|
+
if (!Array.isArray(subagents) || subagents.length === 0) return [];
|
|
457
|
+
const results = [];
|
|
458
|
+
for (const slug of subagents) {
|
|
459
|
+
const key = `agent_slug_${slug.replace(/-/g, "_")}`;
|
|
460
|
+
if (!(key in agentMap)) {
|
|
461
|
+
results.push({
|
|
462
|
+
severity: "error",
|
|
463
|
+
message: `Persona '${persona.name}' declares subagent '${slug}' but no persona with that slug exists in any configured suite.`
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return results;
|
|
468
|
+
}
|
|
425
469
|
async function buildPersona(personaYamlPath, suiteName, suiteConfig, sharedMeta, partialsMap, config, plugins, target, agentMap = {}, registry = defaultRegistry) {
|
|
426
470
|
const personaMeta = await loadPersonaYaml(personaYamlPath);
|
|
427
|
-
let context = buildContext(
|
|
471
|
+
let context = buildContext({
|
|
472
|
+
personaMeta,
|
|
473
|
+
sharedMeta,
|
|
474
|
+
agentMap,
|
|
475
|
+
target,
|
|
476
|
+
registry,
|
|
477
|
+
configVariables: config.variables,
|
|
478
|
+
suiteVariables: suiteConfig.variables
|
|
479
|
+
});
|
|
428
480
|
const personaMetaTyped = personaMeta;
|
|
429
481
|
context = runBuildContext(plugins, context, personaMetaTyped, suiteConfig, target);
|
|
482
|
+
const personaPartialsMap = runPersonaPartials(
|
|
483
|
+
plugins,
|
|
484
|
+
{ ...partialsMap },
|
|
485
|
+
personaMetaTyped,
|
|
486
|
+
context,
|
|
487
|
+
suiteConfig,
|
|
488
|
+
target
|
|
489
|
+
);
|
|
430
490
|
const fmTemplate = resolveFrontmatterTemplate(target, plugins, config.frontmatter, registry);
|
|
431
491
|
const contentBasename = path2__default.default.basename(personaYamlPath, ".yaml") + ".md";
|
|
432
492
|
const frontmatter = renderFrontmatter(fmTemplate, context, contentBasename);
|
|
433
493
|
const contentSubdir = suiteConfig.contentSubdir ?? "content";
|
|
434
494
|
const contentPath = path2__default.default.join(suiteConfig.srcDir, contentSubdir, contentBasename);
|
|
435
495
|
const bodyTemplate = normalizeNewlines(await promises.readFile(contentPath, "utf8"));
|
|
436
|
-
let body = resolvePartials(bodyTemplate,
|
|
496
|
+
let body = resolvePartials(bodyTemplate, personaPartialsMap);
|
|
437
497
|
body = resolveConditionals(body, context);
|
|
438
498
|
body = resolveVariables(body, context, contentBasename);
|
|
439
499
|
body = collapseBlankLines(body);
|
|
@@ -444,7 +504,10 @@ async function buildPersona(personaYamlPath, suiteName, suiteConfig, sharedMeta,
|
|
|
444
504
|
${body}
|
|
445
505
|
`);
|
|
446
506
|
output = runPostRender(plugins, output, personaMetaTyped, target);
|
|
447
|
-
const validationResults =
|
|
507
|
+
const validationResults = [
|
|
508
|
+
...runValidate(plugins, personaMetaTyped, suiteConfig, target),
|
|
509
|
+
...validateSubagentRefs(personaMetaTyped, agentMap)
|
|
510
|
+
];
|
|
448
511
|
const def = registry.has(target) ? registry.get(target) : void 0;
|
|
449
512
|
const outputDir = resolveOutputDir(target, suiteConfig, def);
|
|
450
513
|
const fnKey = def?.filenameContextKey;
|
|
@@ -471,7 +534,7 @@ async function buildSuite(suiteName, suiteConfig, config, plugins, target, agent
|
|
|
471
534
|
const metaSubdir = suiteConfig.metaSubdir ?? "meta";
|
|
472
535
|
const sharedYamlPath = path2__default.default.join(suiteConfig.srcDir, metaSubdir, "_shared.yaml");
|
|
473
536
|
const sharedMeta = await loadRawYaml(sharedYamlPath);
|
|
474
|
-
let partialsMap = {};
|
|
537
|
+
let partialsMap = { ...config.partials ?? {} };
|
|
475
538
|
if (config.sharedPartialsDir && fs.existsSync(config.sharedPartialsDir)) {
|
|
476
539
|
partialsMap = { ...partialsMap, ...await loadPartials(config.sharedPartialsDir) };
|
|
477
540
|
}
|
|
@@ -481,6 +544,7 @@ async function buildSuite(suiteName, suiteConfig, config, plugins, target, agent
|
|
|
481
544
|
partialsMap = { ...partialsMap, ...await loadPartials(suitePartialsDir) };
|
|
482
545
|
}
|
|
483
546
|
runSuiteInit(plugins, suiteConfig, sharedMeta);
|
|
547
|
+
partialsMap = runPartials(plugins, partialsMap, suiteName, suiteConfig);
|
|
484
548
|
const personaYamlPaths = await discoverSuitePersonaYamls(suiteConfig);
|
|
485
549
|
const results = [];
|
|
486
550
|
for (const yamlPath of personaYamlPaths) {
|