@mistralys/persona-builder 2.1.3 → 2.3.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/dist/index.cjs CHANGED
@@ -27,19 +27,48 @@ 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
- return text.replace(
32
- /\n*\{\{#if (\w+)\}\}([\s\S]*?)(?:\{\{else\}\}([\s\S]*?))?\{\{\/if\}\}\n*/g,
33
- (_match, flag, inner, elseInner) => {
34
- if (context[flag]) {
35
- return "\n" + inner.replace(/^\n+/, "").replace(/\n+$/, "") + "\n";
36
- }
37
- if (elseInner !== void 0) {
38
- return "\n" + elseInner.replace(/^\n+/, "").replace(/\n+$/, "") + "\n";
39
- }
40
- return "\n";
41
- }
51
+ const normalized = resolveElseIf(text);
52
+ const pattern = new RegExp(
53
+ String.raw`\n*\{\{#if (\w+)\}\}(${NO_NESTED_IF})` + String.raw`(?:\{\{else\}\}(${NO_NESTED_IF}))?\{\{\/if\}\}\n*`,
54
+ "g"
42
55
  );
56
+ const resolve = (_match, flag, inner, elseInner) => {
57
+ if (context[flag]) {
58
+ return "\n" + inner.replace(/^\n+/, "").replace(/\n+$/, "") + "\n";
59
+ }
60
+ if (elseInner !== void 0) {
61
+ return "\n" + elseInner.replace(/^\n+/, "").replace(/\n+$/, "") + "\n";
62
+ }
63
+ return "\n";
64
+ };
65
+ let result = normalized;
66
+ let prev;
67
+ do {
68
+ prev = result;
69
+ result = result.replace(pattern, resolve);
70
+ } while (result !== prev);
71
+ return result;
43
72
  }
44
73
 
45
74
  // src/engine/variables.ts
@@ -124,11 +153,11 @@ function runSuiteInit(plugins, suite, sharedMeta) {
124
153
  }
125
154
  }
126
155
  }
127
- function runBuildContext(plugins, ctx, persona, suite) {
156
+ function runBuildContext(plugins, ctx, persona, suite, target) {
128
157
  let accumulated = ctx;
129
158
  for (const plugin of plugins) {
130
159
  if (typeof plugin.onBuildContext === "function") {
131
- accumulated = plugin.onBuildContext(accumulated, persona, suite);
160
+ accumulated = plugin.onBuildContext(accumulated, persona, suite, target);
132
161
  }
133
162
  }
134
163
  return accumulated;
@@ -153,7 +182,10 @@ function runValidate(plugins, persona, suite, target) {
153
182
  return results;
154
183
  }
155
184
 
156
- // src/builders/frontmatter.ts
185
+ // src/targets/types.ts
186
+ var TARGET_VSCODE = "vscode";
187
+ var TARGET_CLAUDE_CODE = "claude-code";
188
+ var TARGET_DEEP_AGENTS = "deep-agents";
157
189
  var DEFAULT_FRONTMATTER_VSCODE = `---
158
190
  name: '{{name}} v{{version}}'
159
191
  description: '{{description}}'
@@ -166,7 +198,13 @@ model: {{cc_model}}
166
198
  memory: {{cc_memory}}
167
199
  allowedTools: [{{cc_tools_list}}]
168
200
  ---`;
169
- function resolveFrontmatterTemplate(target, plugins, configTemplates) {
201
+ var DEFAULT_FRONTMATTER_DEEP_AGENTS = `---
202
+ name: {{name}}
203
+ description: {{description}}
204
+ ---`;
205
+
206
+ // src/builders/frontmatter.ts
207
+ function resolveFrontmatterTemplate(target, plugins, configTemplates, registry) {
170
208
  for (const plugin of plugins) {
171
209
  if (plugin.frontmatterTemplates && target in plugin.frontmatterTemplates) {
172
210
  const tpl = plugin.frontmatterTemplates[target];
@@ -177,13 +215,124 @@ function resolveFrontmatterTemplate(target, plugins, configTemplates) {
177
215
  const tpl = configTemplates[target];
178
216
  if (tpl !== void 0) return tpl;
179
217
  }
180
- return target === "vscode" ? DEFAULT_FRONTMATTER_VSCODE : DEFAULT_FRONTMATTER_CLAUDE_CODE;
218
+ if (registry && registry.has(target)) {
219
+ return registry.get(target).defaultFrontmatter;
220
+ }
221
+ return DEFAULT_FRONTMATTER_VSCODE;
181
222
  }
182
223
  function renderFrontmatter(template, context, filename) {
183
224
  let rendered = resolveConditionals(template, context);
184
225
  rendered = resolveVariables(rendered, context, filename);
185
226
  return rendered;
186
227
  }
228
+
229
+ // src/targets/registry.ts
230
+ var TargetRegistry = class _TargetRegistry {
231
+ // Map preserves insertion order — names() and allDefinitions() are
232
+ // therefore deterministic and match registration sequence. This is
233
+ // intentional: the built-in registry guarantees ['vscode', 'claude-code']
234
+ // ordering for the default targets (AC-2).
235
+ _definitions = /* @__PURE__ */ new Map();
236
+ /**
237
+ * Register a new target definition.
238
+ *
239
+ * @param definition The target descriptor to register.
240
+ * @throws {Error} If a target with the same `name` is already registered.
241
+ */
242
+ register(definition) {
243
+ if (this._definitions.has(definition.name)) {
244
+ throw new Error(
245
+ `TargetRegistry: target "${definition.name}" is already registered. Use a unique name or remove the existing registration first.`
246
+ );
247
+ }
248
+ this._definitions.set(definition.name, definition);
249
+ }
250
+ /**
251
+ * Retrieve a registered target definition by name.
252
+ *
253
+ * Returns a shallow copy — mutating the returned object does not affect
254
+ * the registry's internal state.
255
+ *
256
+ * @param name The target name to look up.
257
+ * @returns A shallow copy of the matching TargetDefinition.
258
+ * @throws {Error} If no target with the given name is registered.
259
+ */
260
+ get(name) {
261
+ const def = this._definitions.get(name);
262
+ if (!def) {
263
+ const known = this.names().join(", ") || "(none)";
264
+ throw new Error(
265
+ `TargetRegistry: target "${name}" is not registered. Registered targets: ${known}.`
266
+ );
267
+ }
268
+ return { ...def };
269
+ }
270
+ /**
271
+ * Returns `true` if a target with the given name is registered.
272
+ *
273
+ * @param name The target name to check.
274
+ */
275
+ has(name) {
276
+ return this._definitions.has(name);
277
+ }
278
+ /**
279
+ * Returns the names of all registered targets, in registration order.
280
+ */
281
+ names() {
282
+ return Array.from(this._definitions.keys());
283
+ }
284
+ /**
285
+ * Returns all registered TargetDefinition objects, in registration order.
286
+ *
287
+ * Returns shallow copies — mutating a returned definition does not affect
288
+ * the registry's internal state.
289
+ */
290
+ allDefinitions() {
291
+ return Array.from(this._definitions.values()).map((def) => ({ ...def }));
292
+ }
293
+ /**
294
+ * Returns a new TargetRegistry pre-populated with the same definitions.
295
+ *
296
+ * Useful for test isolation: clone the `defaultRegistry` to get an
297
+ * independent copy that can be mutated without affecting the singleton.
298
+ */
299
+ clone() {
300
+ const copy = new _TargetRegistry();
301
+ for (const def of this._definitions.values()) {
302
+ copy.register({ ...def });
303
+ }
304
+ return copy;
305
+ }
306
+ };
307
+
308
+ // src/targets/built-in.ts
309
+ var defaultRegistry = new TargetRegistry();
310
+ defaultRegistry.register({
311
+ name: TARGET_VSCODE,
312
+ outputDirKey: "vscode",
313
+ filenameContextKey: "vs_file_name",
314
+ defaultFrontmatter: DEFAULT_FRONTMATTER_VSCODE,
315
+ contextFlags: { target_vscode: true },
316
+ defaultEnabled: true
317
+ });
318
+ defaultRegistry.register({
319
+ name: TARGET_CLAUDE_CODE,
320
+ outputDirKey: "claude-code",
321
+ filenameContextKey: "cc_file_name",
322
+ defaultFrontmatter: DEFAULT_FRONTMATTER_CLAUDE_CODE,
323
+ contextFlags: { target_claude_code: true },
324
+ defaultEnabled: true
325
+ });
326
+ defaultRegistry.register({
327
+ name: TARGET_DEEP_AGENTS,
328
+ outputDirKey: "deep-agents",
329
+ filenameContextKey: "da_file_name",
330
+ defaultFrontmatter: DEFAULT_FRONTMATTER_DEEP_AGENTS,
331
+ contextFlags: { target_deep_agents: true },
332
+ defaultEnabled: false
333
+ });
334
+
335
+ // src/builders/persona-builder.ts
187
336
  async function discoverSuitePersonaYamls(suiteConfig) {
188
337
  const metaSubdir = suiteConfig.metaSubdir ?? "meta";
189
338
  const metaDir = path4__default.default.join(suiteConfig.srcDir, metaSubdir);
@@ -210,6 +359,18 @@ async function loadPersonaYaml(yamlPath) {
210
359
  }
211
360
  return record;
212
361
  }
362
+ function resolveOutputDir(target, suiteConfig, definition) {
363
+ const merged = {};
364
+ if (suiteConfig.outVscode) merged["vscode"] = suiteConfig.outVscode;
365
+ if (suiteConfig.outClaudeCode) merged["claude-code"] = suiteConfig.outClaudeCode;
366
+ if (suiteConfig.outputDirs) Object.assign(merged, suiteConfig.outputDirs);
367
+ const lookupKey = definition?.outputDirKey ?? target;
368
+ const dir = merged[lookupKey];
369
+ if (dir) return dir;
370
+ throw new Error(
371
+ `buildPersona: no output directory configured for target "${target}". Add outputDirs['${lookupKey}'] to the suite config, or, for the built-in targets, provide the outVscode / outClaudeCode fields.`
372
+ );
373
+ }
213
374
  async function buildAgentNameMap(config) {
214
375
  const agentMap = {};
215
376
  for (const [, suiteConfig] of Object.entries(config.suites)) {
@@ -223,13 +384,16 @@ async function buildAgentNameMap(config) {
223
384
  const slug = typeof persona["slug"] === "string" ? persona["slug"] : path4__default.default.basename(yamlPath, ".yaml");
224
385
  const name = typeof persona["name"] === "string" ? persona["name"] : slug;
225
386
  const version = typeof persona["version"] === "string" ? persona["version"] : defaultVersion;
226
- const key = `agent_${slug.replace(/-/g, "_")}`;
387
+ const underscoredSlug = slug.replace(/-/g, "_");
388
+ const key = `agent_${underscoredSlug}`;
227
389
  agentMap[key] = `${name} v${version}`;
390
+ const slugKey = `agent_slug_${underscoredSlug}`;
391
+ agentMap[slugKey] = slug;
228
392
  }
229
393
  }
230
394
  return agentMap;
231
395
  }
232
- function buildContext(personaMeta, sharedMeta, agentMap = {}) {
396
+ function buildContext(personaMeta, sharedMeta, agentMap = {}, target, registry) {
233
397
  const version = typeof personaMeta["version"] === "string" ? personaMeta["version"] : typeof sharedMeta["default_version"] === "string" ? sharedMeta["default_version"] : "0.0.0";
234
398
  const merged = {
235
399
  ...sharedMeta,
@@ -254,19 +418,42 @@ function buildContext(personaMeta, sharedMeta, agentMap = {}) {
254
418
  const ccFileName = merged["cc_file_name"];
255
419
  merged["cc_file_name_stem"] = ccFileName.replace(/\.md$/, "");
256
420
  }
421
+ if (!("da_file_name_stem" in merged) && typeof merged["da_file_name"] === "string") {
422
+ const daFileName = merged["da_file_name"];
423
+ merged["da_file_name_stem"] = daFileName.replace(/\.md$/, "");
424
+ }
425
+ if (typeof merged["da_file_name"] === "string") {
426
+ const daTools = Array.isArray(merged["da_tools"]) ? merged["da_tools"] : tools;
427
+ if (!("da_tools_list" in merged)) {
428
+ merged["da_tools_list"] = serializeToolsList(daTools);
429
+ }
430
+ if (!("da_tools_json" in merged)) {
431
+ merged["da_tools_json"] = serializeTools(daTools);
432
+ }
433
+ }
257
434
  for (const [key, value] of Object.entries(agentMap)) {
258
435
  if (!(key in merged)) {
259
436
  merged[key] = value;
260
437
  }
261
438
  }
439
+ if (target !== void 0) {
440
+ if (registry && registry.has(target)) {
441
+ const flags = registry.get(target).contextFlags ?? {};
442
+ for (const [key, value] of Object.entries(flags)) {
443
+ merged[key] = value;
444
+ }
445
+ } else {
446
+ merged[`target_${target.replace(/-/g, "_")}`] = true;
447
+ }
448
+ }
262
449
  return merged;
263
450
  }
264
- async function buildPersona(personaYamlPath, suiteName, suiteConfig, sharedMeta, partialsMap, config, plugins, target, agentMap = {}) {
451
+ async function buildPersona(personaYamlPath, suiteName, suiteConfig, sharedMeta, partialsMap, config, plugins, target, agentMap = {}, registry = defaultRegistry) {
265
452
  const personaMeta = await loadPersonaYaml(personaYamlPath);
266
- let context = buildContext(personaMeta, sharedMeta, agentMap);
453
+ let context = buildContext(personaMeta, sharedMeta, agentMap, target, registry);
267
454
  const personaMetaTyped = personaMeta;
268
- context = runBuildContext(plugins, context, personaMetaTyped, suiteConfig);
269
- const fmTemplate = resolveFrontmatterTemplate(target, plugins, config.frontmatter);
455
+ context = runBuildContext(plugins, context, personaMetaTyped, suiteConfig, target);
456
+ const fmTemplate = resolveFrontmatterTemplate(target, plugins, config.frontmatter, registry);
270
457
  const contentBasename = path4__default.default.basename(personaYamlPath, ".yaml") + ".md";
271
458
  const frontmatter = renderFrontmatter(fmTemplate, context, contentBasename);
272
459
  const contentSubdir = suiteConfig.contentSubdir ?? "content";
@@ -284,15 +471,10 @@ ${body}
284
471
  `);
285
472
  output = runPostRender(plugins, output, personaMetaTyped, target);
286
473
  const validationResults = runValidate(plugins, personaMetaTyped, suiteConfig, target);
287
- const outputDir = target === "vscode" ? suiteConfig.outVscode : suiteConfig.outClaudeCode;
288
- let outputBasename;
289
- if (target === "vscode" && typeof context["vs_file_name"] === "string") {
290
- outputBasename = context["vs_file_name"];
291
- } else if (target === "claude-code" && typeof context["cc_file_name"] === "string") {
292
- outputBasename = context["cc_file_name"];
293
- } else {
294
- outputBasename = contentBasename;
295
- }
474
+ const def = registry.has(target) ? registry.get(target) : void 0;
475
+ const outputDir = resolveOutputDir(target, suiteConfig, def);
476
+ const fnKey = def?.filenameContextKey;
477
+ const outputBasename = fnKey && typeof context[fnKey] === "string" ? context[fnKey] : contentBasename;
296
478
  const outputPath = path4__default.default.join(outputDir, outputBasename);
297
479
  const check = config.check ?? false;
298
480
  let written = false;
@@ -311,7 +493,7 @@ ${body}
311
493
  written
312
494
  };
313
495
  }
314
- async function buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap = {}) {
496
+ async function buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap = {}, registry = defaultRegistry) {
315
497
  const metaSubdir = suiteConfig.metaSubdir ?? "meta";
316
498
  const sharedYamlPath = path4__default.default.join(suiteConfig.srcDir, metaSubdir, "_shared.yaml");
317
499
  const sharedMeta = await loadRawYaml(sharedYamlPath);
@@ -337,7 +519,8 @@ async function buildSuite(suiteName, suiteConfig, config, plugins, target, agent
337
519
  config,
338
520
  plugins,
339
521
  target,
340
- agentMap
522
+ agentMap,
523
+ registry
341
524
  );
342
525
  results.push(result);
343
526
  }
@@ -345,12 +528,13 @@ async function buildSuite(suiteName, suiteConfig, config, plugins, target, agent
345
528
  }
346
529
  async function build(config) {
347
530
  const plugins = config.plugins ?? [];
348
- const targets = config.targets ?? ["vscode", "claude-code"];
531
+ const registry = config.targetRegistry ?? defaultRegistry;
532
+ const targets = config.targets ?? registry.names().filter((n) => registry.get(n).defaultEnabled !== false);
349
533
  const allResults = [];
350
534
  const agentMap = await buildAgentNameMap(config);
351
535
  for (const [suiteName, suiteConfig] of Object.entries(config.suites)) {
352
536
  for (const target of targets) {
353
- const suiteResults = await buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap);
537
+ const suiteResults = await buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap, registry);
354
538
  allResults.push(...suiteResults);
355
539
  }
356
540
  }
@@ -439,12 +623,18 @@ var _pkgRequire = module$1.createRequire((typeof document === 'undefined' ? requ
439
623
  var VERSION = _pkgRequire("../package.json").version;
440
624
 
441
625
  exports.DEFAULT_FRONTMATTER_CLAUDE_CODE = DEFAULT_FRONTMATTER_CLAUDE_CODE;
626
+ exports.DEFAULT_FRONTMATTER_DEEP_AGENTS = DEFAULT_FRONTMATTER_DEEP_AGENTS;
442
627
  exports.DEFAULT_FRONTMATTER_VSCODE = DEFAULT_FRONTMATTER_VSCODE;
628
+ exports.TARGET_CLAUDE_CODE = TARGET_CLAUDE_CODE;
629
+ exports.TARGET_DEEP_AGENTS = TARGET_DEEP_AGENTS;
630
+ exports.TARGET_VSCODE = TARGET_VSCODE;
631
+ exports.TargetRegistry = TargetRegistry;
443
632
  exports.VERSION = VERSION;
444
633
  exports.build = build;
445
634
  exports.buildPersona = buildPersona;
446
635
  exports.buildSuite = buildSuite;
447
636
  exports.collapseBlankLines = collapseBlankLines;
637
+ exports.defaultRegistry = defaultRegistry;
448
638
  exports.discoverPersonaYamls = discoverPersonaYamls;
449
639
  exports.ensureBlankLineBeforeHeadings = ensureBlankLineBeforeHeadings;
450
640
  exports.escapeRegExp = escapeRegExp;