@mistralys/persona-builder 2.1.2 → 2.2.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
@@ -28,18 +28,27 @@ function resolvePartials(text, partialsMap, depth = 0) {
28
28
 
29
29
  // src/engine/conditionals.ts
30
30
  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
- }
31
+ const noNestedIf = String.raw`(?:(?!\{\{#if\b)[\s\S])*?`;
32
+ const pattern = new RegExp(
33
+ String.raw`\n*\{\{#if (\w+)\}\}(${noNestedIf})` + String.raw`(?:\{\{else\}\}(${noNestedIf}))?\{\{\/if\}\}\n*`,
34
+ "g"
42
35
  );
36
+ const resolve = (_match, flag, inner, elseInner) => {
37
+ if (context[flag]) {
38
+ return "\n" + inner.replace(/^\n+/, "").replace(/\n+$/, "") + "\n";
39
+ }
40
+ if (elseInner !== void 0) {
41
+ return "\n" + elseInner.replace(/^\n+/, "").replace(/\n+$/, "") + "\n";
42
+ }
43
+ return "\n";
44
+ };
45
+ let result = text;
46
+ let prev;
47
+ do {
48
+ prev = result;
49
+ result = result.replace(pattern, resolve);
50
+ } while (result !== prev);
51
+ return result;
43
52
  }
44
53
 
45
54
  // src/engine/variables.ts
@@ -124,11 +133,11 @@ function runSuiteInit(plugins, suite, sharedMeta) {
124
133
  }
125
134
  }
126
135
  }
127
- function runBuildContext(plugins, ctx, persona, suite) {
136
+ function runBuildContext(plugins, ctx, persona, suite, target) {
128
137
  let accumulated = ctx;
129
138
  for (const plugin of plugins) {
130
139
  if (typeof plugin.onBuildContext === "function") {
131
- accumulated = plugin.onBuildContext(accumulated, persona, suite);
140
+ accumulated = plugin.onBuildContext(accumulated, persona, suite, target);
132
141
  }
133
142
  }
134
143
  return accumulated;
@@ -153,7 +162,10 @@ function runValidate(plugins, persona, suite, target) {
153
162
  return results;
154
163
  }
155
164
 
156
- // src/builders/frontmatter.ts
165
+ // src/targets/types.ts
166
+ var TARGET_VSCODE = "vscode";
167
+ var TARGET_CLAUDE_CODE = "claude-code";
168
+ var TARGET_DEEP_AGENTS = "deep-agents";
157
169
  var DEFAULT_FRONTMATTER_VSCODE = `---
158
170
  name: '{{name}} v{{version}}'
159
171
  description: '{{description}}'
@@ -166,7 +178,13 @@ model: {{cc_model}}
166
178
  memory: {{cc_memory}}
167
179
  allowedTools: [{{cc_tools_list}}]
168
180
  ---`;
169
- function resolveFrontmatterTemplate(target, plugins, configTemplates) {
181
+ var DEFAULT_FRONTMATTER_DEEP_AGENTS = `---
182
+ name: {{name}}
183
+ description: {{description}}
184
+ ---`;
185
+
186
+ // src/builders/frontmatter.ts
187
+ function resolveFrontmatterTemplate(target, plugins, configTemplates, registry) {
170
188
  for (const plugin of plugins) {
171
189
  if (plugin.frontmatterTemplates && target in plugin.frontmatterTemplates) {
172
190
  const tpl = plugin.frontmatterTemplates[target];
@@ -177,13 +195,124 @@ function resolveFrontmatterTemplate(target, plugins, configTemplates) {
177
195
  const tpl = configTemplates[target];
178
196
  if (tpl !== void 0) return tpl;
179
197
  }
180
- return target === "vscode" ? DEFAULT_FRONTMATTER_VSCODE : DEFAULT_FRONTMATTER_CLAUDE_CODE;
198
+ if (registry && registry.has(target)) {
199
+ return registry.get(target).defaultFrontmatter;
200
+ }
201
+ return DEFAULT_FRONTMATTER_VSCODE;
181
202
  }
182
203
  function renderFrontmatter(template, context, filename) {
183
204
  let rendered = resolveConditionals(template, context);
184
205
  rendered = resolveVariables(rendered, context, filename);
185
206
  return rendered;
186
207
  }
208
+
209
+ // src/targets/registry.ts
210
+ var TargetRegistry = class _TargetRegistry {
211
+ // Map preserves insertion order — names() and allDefinitions() are
212
+ // therefore deterministic and match registration sequence. This is
213
+ // intentional: the built-in registry guarantees ['vscode', 'claude-code']
214
+ // ordering for the default targets (AC-2).
215
+ _definitions = /* @__PURE__ */ new Map();
216
+ /**
217
+ * Register a new target definition.
218
+ *
219
+ * @param definition The target descriptor to register.
220
+ * @throws {Error} If a target with the same `name` is already registered.
221
+ */
222
+ register(definition) {
223
+ if (this._definitions.has(definition.name)) {
224
+ throw new Error(
225
+ `TargetRegistry: target "${definition.name}" is already registered. Use a unique name or remove the existing registration first.`
226
+ );
227
+ }
228
+ this._definitions.set(definition.name, definition);
229
+ }
230
+ /**
231
+ * Retrieve a registered target definition by name.
232
+ *
233
+ * Returns a shallow copy — mutating the returned object does not affect
234
+ * the registry's internal state.
235
+ *
236
+ * @param name The target name to look up.
237
+ * @returns A shallow copy of the matching TargetDefinition.
238
+ * @throws {Error} If no target with the given name is registered.
239
+ */
240
+ get(name) {
241
+ const def = this._definitions.get(name);
242
+ if (!def) {
243
+ const known = this.names().join(", ") || "(none)";
244
+ throw new Error(
245
+ `TargetRegistry: target "${name}" is not registered. Registered targets: ${known}.`
246
+ );
247
+ }
248
+ return { ...def };
249
+ }
250
+ /**
251
+ * Returns `true` if a target with the given name is registered.
252
+ *
253
+ * @param name The target name to check.
254
+ */
255
+ has(name) {
256
+ return this._definitions.has(name);
257
+ }
258
+ /**
259
+ * Returns the names of all registered targets, in registration order.
260
+ */
261
+ names() {
262
+ return Array.from(this._definitions.keys());
263
+ }
264
+ /**
265
+ * Returns all registered TargetDefinition objects, in registration order.
266
+ *
267
+ * Returns shallow copies — mutating a returned definition does not affect
268
+ * the registry's internal state.
269
+ */
270
+ allDefinitions() {
271
+ return Array.from(this._definitions.values()).map((def) => ({ ...def }));
272
+ }
273
+ /**
274
+ * Returns a new TargetRegistry pre-populated with the same definitions.
275
+ *
276
+ * Useful for test isolation: clone the `defaultRegistry` to get an
277
+ * independent copy that can be mutated without affecting the singleton.
278
+ */
279
+ clone() {
280
+ const copy = new _TargetRegistry();
281
+ for (const def of this._definitions.values()) {
282
+ copy.register({ ...def });
283
+ }
284
+ return copy;
285
+ }
286
+ };
287
+
288
+ // src/targets/built-in.ts
289
+ var defaultRegistry = new TargetRegistry();
290
+ defaultRegistry.register({
291
+ name: TARGET_VSCODE,
292
+ outputDirKey: "vscode",
293
+ filenameContextKey: "vs_file_name",
294
+ defaultFrontmatter: DEFAULT_FRONTMATTER_VSCODE,
295
+ contextFlags: { target_vscode: true },
296
+ defaultEnabled: true
297
+ });
298
+ defaultRegistry.register({
299
+ name: TARGET_CLAUDE_CODE,
300
+ outputDirKey: "claude-code",
301
+ filenameContextKey: "cc_file_name",
302
+ defaultFrontmatter: DEFAULT_FRONTMATTER_CLAUDE_CODE,
303
+ contextFlags: { target_claude_code: true },
304
+ defaultEnabled: true
305
+ });
306
+ defaultRegistry.register({
307
+ name: TARGET_DEEP_AGENTS,
308
+ outputDirKey: "deep-agents",
309
+ filenameContextKey: "da_file_name",
310
+ defaultFrontmatter: DEFAULT_FRONTMATTER_DEEP_AGENTS,
311
+ contextFlags: { target_deep_agents: true },
312
+ defaultEnabled: false
313
+ });
314
+
315
+ // src/builders/persona-builder.ts
187
316
  async function discoverSuitePersonaYamls(suiteConfig) {
188
317
  const metaSubdir = suiteConfig.metaSubdir ?? "meta";
189
318
  const metaDir = path4__default.default.join(suiteConfig.srcDir, metaSubdir);
@@ -210,6 +339,18 @@ async function loadPersonaYaml(yamlPath) {
210
339
  }
211
340
  return record;
212
341
  }
342
+ function resolveOutputDir(target, suiteConfig, definition) {
343
+ const merged = {};
344
+ if (suiteConfig.outVscode) merged["vscode"] = suiteConfig.outVscode;
345
+ if (suiteConfig.outClaudeCode) merged["claude-code"] = suiteConfig.outClaudeCode;
346
+ if (suiteConfig.outputDirs) Object.assign(merged, suiteConfig.outputDirs);
347
+ const lookupKey = definition?.outputDirKey ?? target;
348
+ const dir = merged[lookupKey];
349
+ if (dir) return dir;
350
+ throw new Error(
351
+ `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.`
352
+ );
353
+ }
213
354
  async function buildAgentNameMap(config) {
214
355
  const agentMap = {};
215
356
  for (const [, suiteConfig] of Object.entries(config.suites)) {
@@ -223,13 +364,16 @@ async function buildAgentNameMap(config) {
223
364
  const slug = typeof persona["slug"] === "string" ? persona["slug"] : path4__default.default.basename(yamlPath, ".yaml");
224
365
  const name = typeof persona["name"] === "string" ? persona["name"] : slug;
225
366
  const version = typeof persona["version"] === "string" ? persona["version"] : defaultVersion;
226
- const key = `agent_${slug.replace(/-/g, "_")}`;
367
+ const underscoredSlug = slug.replace(/-/g, "_");
368
+ const key = `agent_${underscoredSlug}`;
227
369
  agentMap[key] = `${name} v${version}`;
370
+ const slugKey = `agent_slug_${underscoredSlug}`;
371
+ agentMap[slugKey] = slug;
228
372
  }
229
373
  }
230
374
  return agentMap;
231
375
  }
232
- function buildContext(personaMeta, sharedMeta, agentMap = {}) {
376
+ function buildContext(personaMeta, sharedMeta, agentMap = {}, target, registry) {
233
377
  const version = typeof personaMeta["version"] === "string" ? personaMeta["version"] : typeof sharedMeta["default_version"] === "string" ? sharedMeta["default_version"] : "0.0.0";
234
378
  const merged = {
235
379
  ...sharedMeta,
@@ -254,19 +398,42 @@ function buildContext(personaMeta, sharedMeta, agentMap = {}) {
254
398
  const ccFileName = merged["cc_file_name"];
255
399
  merged["cc_file_name_stem"] = ccFileName.replace(/\.md$/, "");
256
400
  }
401
+ if (!("da_file_name_stem" in merged) && typeof merged["da_file_name"] === "string") {
402
+ const daFileName = merged["da_file_name"];
403
+ merged["da_file_name_stem"] = daFileName.replace(/\.md$/, "");
404
+ }
405
+ if (typeof merged["da_file_name"] === "string") {
406
+ const daTools = Array.isArray(merged["da_tools"]) ? merged["da_tools"] : tools;
407
+ if (!("da_tools_list" in merged)) {
408
+ merged["da_tools_list"] = serializeToolsList(daTools);
409
+ }
410
+ if (!("da_tools_json" in merged)) {
411
+ merged["da_tools_json"] = serializeTools(daTools);
412
+ }
413
+ }
257
414
  for (const [key, value] of Object.entries(agentMap)) {
258
415
  if (!(key in merged)) {
259
416
  merged[key] = value;
260
417
  }
261
418
  }
419
+ if (target !== void 0) {
420
+ if (registry && registry.has(target)) {
421
+ const flags = registry.get(target).contextFlags ?? {};
422
+ for (const [key, value] of Object.entries(flags)) {
423
+ merged[key] = value;
424
+ }
425
+ } else {
426
+ merged[`target_${target.replace(/-/g, "_")}`] = true;
427
+ }
428
+ }
262
429
  return merged;
263
430
  }
264
- async function buildPersona(personaYamlPath, suiteName, suiteConfig, sharedMeta, partialsMap, config, plugins, target, agentMap = {}) {
431
+ async function buildPersona(personaYamlPath, suiteName, suiteConfig, sharedMeta, partialsMap, config, plugins, target, agentMap = {}, registry = defaultRegistry) {
265
432
  const personaMeta = await loadPersonaYaml(personaYamlPath);
266
- let context = buildContext(personaMeta, sharedMeta, agentMap);
433
+ let context = buildContext(personaMeta, sharedMeta, agentMap, target, registry);
267
434
  const personaMetaTyped = personaMeta;
268
- context = runBuildContext(plugins, context, personaMetaTyped, suiteConfig);
269
- const fmTemplate = resolveFrontmatterTemplate(target, plugins, config.frontmatter);
435
+ context = runBuildContext(plugins, context, personaMetaTyped, suiteConfig, target);
436
+ const fmTemplate = resolveFrontmatterTemplate(target, plugins, config.frontmatter, registry);
270
437
  const contentBasename = path4__default.default.basename(personaYamlPath, ".yaml") + ".md";
271
438
  const frontmatter = renderFrontmatter(fmTemplate, context, contentBasename);
272
439
  const contentSubdir = suiteConfig.contentSubdir ?? "content";
@@ -284,15 +451,10 @@ ${body}
284
451
  `);
285
452
  output = runPostRender(plugins, output, personaMetaTyped, target);
286
453
  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
- }
454
+ const def = registry.has(target) ? registry.get(target) : void 0;
455
+ const outputDir = resolveOutputDir(target, suiteConfig, def);
456
+ const fnKey = def?.filenameContextKey;
457
+ const outputBasename = fnKey && typeof context[fnKey] === "string" ? context[fnKey] : contentBasename;
296
458
  const outputPath = path4__default.default.join(outputDir, outputBasename);
297
459
  const check = config.check ?? false;
298
460
  let written = false;
@@ -311,7 +473,7 @@ ${body}
311
473
  written
312
474
  };
313
475
  }
314
- async function buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap = {}) {
476
+ async function buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap = {}, registry = defaultRegistry) {
315
477
  const metaSubdir = suiteConfig.metaSubdir ?? "meta";
316
478
  const sharedYamlPath = path4__default.default.join(suiteConfig.srcDir, metaSubdir, "_shared.yaml");
317
479
  const sharedMeta = await loadRawYaml(sharedYamlPath);
@@ -337,7 +499,8 @@ async function buildSuite(suiteName, suiteConfig, config, plugins, target, agent
337
499
  config,
338
500
  plugins,
339
501
  target,
340
- agentMap
502
+ agentMap,
503
+ registry
341
504
  );
342
505
  results.push(result);
343
506
  }
@@ -345,12 +508,13 @@ async function buildSuite(suiteName, suiteConfig, config, plugins, target, agent
345
508
  }
346
509
  async function build(config) {
347
510
  const plugins = config.plugins ?? [];
348
- const targets = config.targets ?? ["vscode", "claude-code"];
511
+ const registry = config.targetRegistry ?? defaultRegistry;
512
+ const targets = config.targets ?? registry.names().filter((n) => registry.get(n).defaultEnabled !== false);
349
513
  const allResults = [];
350
514
  const agentMap = await buildAgentNameMap(config);
351
515
  for (const [suiteName, suiteConfig] of Object.entries(config.suites)) {
352
516
  for (const target of targets) {
353
- const suiteResults = await buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap);
517
+ const suiteResults = await buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap, registry);
354
518
  allResults.push(...suiteResults);
355
519
  }
356
520
  }
@@ -439,12 +603,18 @@ var _pkgRequire = module$1.createRequire((typeof document === 'undefined' ? requ
439
603
  var VERSION = _pkgRequire("../package.json").version;
440
604
 
441
605
  exports.DEFAULT_FRONTMATTER_CLAUDE_CODE = DEFAULT_FRONTMATTER_CLAUDE_CODE;
606
+ exports.DEFAULT_FRONTMATTER_DEEP_AGENTS = DEFAULT_FRONTMATTER_DEEP_AGENTS;
442
607
  exports.DEFAULT_FRONTMATTER_VSCODE = DEFAULT_FRONTMATTER_VSCODE;
608
+ exports.TARGET_CLAUDE_CODE = TARGET_CLAUDE_CODE;
609
+ exports.TARGET_DEEP_AGENTS = TARGET_DEEP_AGENTS;
610
+ exports.TARGET_VSCODE = TARGET_VSCODE;
611
+ exports.TargetRegistry = TargetRegistry;
443
612
  exports.VERSION = VERSION;
444
613
  exports.build = build;
445
614
  exports.buildPersona = buildPersona;
446
615
  exports.buildSuite = buildSuite;
447
616
  exports.collapseBlankLines = collapseBlankLines;
617
+ exports.defaultRegistry = defaultRegistry;
448
618
  exports.discoverPersonaYamls = discoverPersonaYamls;
449
619
  exports.ensureBlankLineBeforeHeadings = ensureBlankLineBeforeHeadings;
450
620
  exports.escapeRegExp = escapeRegExp;