@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.js CHANGED
@@ -20,18 +20,27 @@ function resolvePartials(text, partialsMap, depth = 0) {
20
20
 
21
21
  // src/engine/conditionals.ts
22
22
  function resolveConditionals(text, context) {
23
- return text.replace(
24
- /\n*\{\{#if (\w+)\}\}([\s\S]*?)(?:\{\{else\}\}([\s\S]*?))?\{\{\/if\}\}\n*/g,
25
- (_match, flag, inner, elseInner) => {
26
- if (context[flag]) {
27
- return "\n" + inner.replace(/^\n+/, "").replace(/\n+$/, "") + "\n";
28
- }
29
- if (elseInner !== void 0) {
30
- return "\n" + elseInner.replace(/^\n+/, "").replace(/\n+$/, "") + "\n";
31
- }
32
- return "\n";
33
- }
23
+ const noNestedIf = String.raw`(?:(?!\{\{#if\b)[\s\S])*?`;
24
+ const pattern = new RegExp(
25
+ String.raw`\n*\{\{#if (\w+)\}\}(${noNestedIf})` + String.raw`(?:\{\{else\}\}(${noNestedIf}))?\{\{\/if\}\}\n*`,
26
+ "g"
34
27
  );
28
+ const resolve = (_match, flag, inner, elseInner) => {
29
+ if (context[flag]) {
30
+ return "\n" + inner.replace(/^\n+/, "").replace(/\n+$/, "") + "\n";
31
+ }
32
+ if (elseInner !== void 0) {
33
+ return "\n" + elseInner.replace(/^\n+/, "").replace(/\n+$/, "") + "\n";
34
+ }
35
+ return "\n";
36
+ };
37
+ let result = text;
38
+ let prev;
39
+ do {
40
+ prev = result;
41
+ result = result.replace(pattern, resolve);
42
+ } while (result !== prev);
43
+ return result;
35
44
  }
36
45
 
37
46
  // src/engine/variables.ts
@@ -116,11 +125,11 @@ function runSuiteInit(plugins, suite, sharedMeta) {
116
125
  }
117
126
  }
118
127
  }
119
- function runBuildContext(plugins, ctx, persona, suite) {
128
+ function runBuildContext(plugins, ctx, persona, suite, target) {
120
129
  let accumulated = ctx;
121
130
  for (const plugin of plugins) {
122
131
  if (typeof plugin.onBuildContext === "function") {
123
- accumulated = plugin.onBuildContext(accumulated, persona, suite);
132
+ accumulated = plugin.onBuildContext(accumulated, persona, suite, target);
124
133
  }
125
134
  }
126
135
  return accumulated;
@@ -145,7 +154,10 @@ function runValidate(plugins, persona, suite, target) {
145
154
  return results;
146
155
  }
147
156
 
148
- // src/builders/frontmatter.ts
157
+ // src/targets/types.ts
158
+ var TARGET_VSCODE = "vscode";
159
+ var TARGET_CLAUDE_CODE = "claude-code";
160
+ var TARGET_DEEP_AGENTS = "deep-agents";
149
161
  var DEFAULT_FRONTMATTER_VSCODE = `---
150
162
  name: '{{name}} v{{version}}'
151
163
  description: '{{description}}'
@@ -158,7 +170,13 @@ model: {{cc_model}}
158
170
  memory: {{cc_memory}}
159
171
  allowedTools: [{{cc_tools_list}}]
160
172
  ---`;
161
- function resolveFrontmatterTemplate(target, plugins, configTemplates) {
173
+ var DEFAULT_FRONTMATTER_DEEP_AGENTS = `---
174
+ name: {{name}}
175
+ description: {{description}}
176
+ ---`;
177
+
178
+ // src/builders/frontmatter.ts
179
+ function resolveFrontmatterTemplate(target, plugins, configTemplates, registry) {
162
180
  for (const plugin of plugins) {
163
181
  if (plugin.frontmatterTemplates && target in plugin.frontmatterTemplates) {
164
182
  const tpl = plugin.frontmatterTemplates[target];
@@ -169,13 +187,124 @@ function resolveFrontmatterTemplate(target, plugins, configTemplates) {
169
187
  const tpl = configTemplates[target];
170
188
  if (tpl !== void 0) return tpl;
171
189
  }
172
- return target === "vscode" ? DEFAULT_FRONTMATTER_VSCODE : DEFAULT_FRONTMATTER_CLAUDE_CODE;
190
+ if (registry && registry.has(target)) {
191
+ return registry.get(target).defaultFrontmatter;
192
+ }
193
+ return DEFAULT_FRONTMATTER_VSCODE;
173
194
  }
174
195
  function renderFrontmatter(template, context, filename) {
175
196
  let rendered = resolveConditionals(template, context);
176
197
  rendered = resolveVariables(rendered, context, filename);
177
198
  return rendered;
178
199
  }
200
+
201
+ // src/targets/registry.ts
202
+ var TargetRegistry = class _TargetRegistry {
203
+ // Map preserves insertion order — names() and allDefinitions() are
204
+ // therefore deterministic and match registration sequence. This is
205
+ // intentional: the built-in registry guarantees ['vscode', 'claude-code']
206
+ // ordering for the default targets (AC-2).
207
+ _definitions = /* @__PURE__ */ new Map();
208
+ /**
209
+ * Register a new target definition.
210
+ *
211
+ * @param definition The target descriptor to register.
212
+ * @throws {Error} If a target with the same `name` is already registered.
213
+ */
214
+ register(definition) {
215
+ if (this._definitions.has(definition.name)) {
216
+ throw new Error(
217
+ `TargetRegistry: target "${definition.name}" is already registered. Use a unique name or remove the existing registration first.`
218
+ );
219
+ }
220
+ this._definitions.set(definition.name, definition);
221
+ }
222
+ /**
223
+ * Retrieve a registered target definition by name.
224
+ *
225
+ * Returns a shallow copy — mutating the returned object does not affect
226
+ * the registry's internal state.
227
+ *
228
+ * @param name The target name to look up.
229
+ * @returns A shallow copy of the matching TargetDefinition.
230
+ * @throws {Error} If no target with the given name is registered.
231
+ */
232
+ get(name) {
233
+ const def = this._definitions.get(name);
234
+ if (!def) {
235
+ const known = this.names().join(", ") || "(none)";
236
+ throw new Error(
237
+ `TargetRegistry: target "${name}" is not registered. Registered targets: ${known}.`
238
+ );
239
+ }
240
+ return { ...def };
241
+ }
242
+ /**
243
+ * Returns `true` if a target with the given name is registered.
244
+ *
245
+ * @param name The target name to check.
246
+ */
247
+ has(name) {
248
+ return this._definitions.has(name);
249
+ }
250
+ /**
251
+ * Returns the names of all registered targets, in registration order.
252
+ */
253
+ names() {
254
+ return Array.from(this._definitions.keys());
255
+ }
256
+ /**
257
+ * Returns all registered TargetDefinition objects, in registration order.
258
+ *
259
+ * Returns shallow copies — mutating a returned definition does not affect
260
+ * the registry's internal state.
261
+ */
262
+ allDefinitions() {
263
+ return Array.from(this._definitions.values()).map((def) => ({ ...def }));
264
+ }
265
+ /**
266
+ * Returns a new TargetRegistry pre-populated with the same definitions.
267
+ *
268
+ * Useful for test isolation: clone the `defaultRegistry` to get an
269
+ * independent copy that can be mutated without affecting the singleton.
270
+ */
271
+ clone() {
272
+ const copy = new _TargetRegistry();
273
+ for (const def of this._definitions.values()) {
274
+ copy.register({ ...def });
275
+ }
276
+ return copy;
277
+ }
278
+ };
279
+
280
+ // src/targets/built-in.ts
281
+ var defaultRegistry = new TargetRegistry();
282
+ defaultRegistry.register({
283
+ name: TARGET_VSCODE,
284
+ outputDirKey: "vscode",
285
+ filenameContextKey: "vs_file_name",
286
+ defaultFrontmatter: DEFAULT_FRONTMATTER_VSCODE,
287
+ contextFlags: { target_vscode: true },
288
+ defaultEnabled: true
289
+ });
290
+ defaultRegistry.register({
291
+ name: TARGET_CLAUDE_CODE,
292
+ outputDirKey: "claude-code",
293
+ filenameContextKey: "cc_file_name",
294
+ defaultFrontmatter: DEFAULT_FRONTMATTER_CLAUDE_CODE,
295
+ contextFlags: { target_claude_code: true },
296
+ defaultEnabled: true
297
+ });
298
+ defaultRegistry.register({
299
+ name: TARGET_DEEP_AGENTS,
300
+ outputDirKey: "deep-agents",
301
+ filenameContextKey: "da_file_name",
302
+ defaultFrontmatter: DEFAULT_FRONTMATTER_DEEP_AGENTS,
303
+ contextFlags: { target_deep_agents: true },
304
+ defaultEnabled: false
305
+ });
306
+
307
+ // src/builders/persona-builder.ts
179
308
  async function discoverSuitePersonaYamls(suiteConfig) {
180
309
  const metaSubdir = suiteConfig.metaSubdir ?? "meta";
181
310
  const metaDir = path4.join(suiteConfig.srcDir, metaSubdir);
@@ -202,6 +331,18 @@ async function loadPersonaYaml(yamlPath) {
202
331
  }
203
332
  return record;
204
333
  }
334
+ function resolveOutputDir(target, suiteConfig, definition) {
335
+ const merged = {};
336
+ if (suiteConfig.outVscode) merged["vscode"] = suiteConfig.outVscode;
337
+ if (suiteConfig.outClaudeCode) merged["claude-code"] = suiteConfig.outClaudeCode;
338
+ if (suiteConfig.outputDirs) Object.assign(merged, suiteConfig.outputDirs);
339
+ const lookupKey = definition?.outputDirKey ?? target;
340
+ const dir = merged[lookupKey];
341
+ if (dir) return dir;
342
+ throw new Error(
343
+ `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.`
344
+ );
345
+ }
205
346
  async function buildAgentNameMap(config) {
206
347
  const agentMap = {};
207
348
  for (const [, suiteConfig] of Object.entries(config.suites)) {
@@ -215,13 +356,16 @@ async function buildAgentNameMap(config) {
215
356
  const slug = typeof persona["slug"] === "string" ? persona["slug"] : path4.basename(yamlPath, ".yaml");
216
357
  const name = typeof persona["name"] === "string" ? persona["name"] : slug;
217
358
  const version = typeof persona["version"] === "string" ? persona["version"] : defaultVersion;
218
- const key = `agent_${slug.replace(/-/g, "_")}`;
359
+ const underscoredSlug = slug.replace(/-/g, "_");
360
+ const key = `agent_${underscoredSlug}`;
219
361
  agentMap[key] = `${name} v${version}`;
362
+ const slugKey = `agent_slug_${underscoredSlug}`;
363
+ agentMap[slugKey] = slug;
220
364
  }
221
365
  }
222
366
  return agentMap;
223
367
  }
224
- function buildContext(personaMeta, sharedMeta, agentMap = {}) {
368
+ function buildContext(personaMeta, sharedMeta, agentMap = {}, target, registry) {
225
369
  const version = typeof personaMeta["version"] === "string" ? personaMeta["version"] : typeof sharedMeta["default_version"] === "string" ? sharedMeta["default_version"] : "0.0.0";
226
370
  const merged = {
227
371
  ...sharedMeta,
@@ -246,19 +390,42 @@ function buildContext(personaMeta, sharedMeta, agentMap = {}) {
246
390
  const ccFileName = merged["cc_file_name"];
247
391
  merged["cc_file_name_stem"] = ccFileName.replace(/\.md$/, "");
248
392
  }
393
+ if (!("da_file_name_stem" in merged) && typeof merged["da_file_name"] === "string") {
394
+ const daFileName = merged["da_file_name"];
395
+ merged["da_file_name_stem"] = daFileName.replace(/\.md$/, "");
396
+ }
397
+ if (typeof merged["da_file_name"] === "string") {
398
+ const daTools = Array.isArray(merged["da_tools"]) ? merged["da_tools"] : tools;
399
+ if (!("da_tools_list" in merged)) {
400
+ merged["da_tools_list"] = serializeToolsList(daTools);
401
+ }
402
+ if (!("da_tools_json" in merged)) {
403
+ merged["da_tools_json"] = serializeTools(daTools);
404
+ }
405
+ }
249
406
  for (const [key, value] of Object.entries(agentMap)) {
250
407
  if (!(key in merged)) {
251
408
  merged[key] = value;
252
409
  }
253
410
  }
411
+ if (target !== void 0) {
412
+ if (registry && registry.has(target)) {
413
+ const flags = registry.get(target).contextFlags ?? {};
414
+ for (const [key, value] of Object.entries(flags)) {
415
+ merged[key] = value;
416
+ }
417
+ } else {
418
+ merged[`target_${target.replace(/-/g, "_")}`] = true;
419
+ }
420
+ }
254
421
  return merged;
255
422
  }
256
- async function buildPersona(personaYamlPath, suiteName, suiteConfig, sharedMeta, partialsMap, config, plugins, target, agentMap = {}) {
423
+ async function buildPersona(personaYamlPath, suiteName, suiteConfig, sharedMeta, partialsMap, config, plugins, target, agentMap = {}, registry = defaultRegistry) {
257
424
  const personaMeta = await loadPersonaYaml(personaYamlPath);
258
- let context = buildContext(personaMeta, sharedMeta, agentMap);
425
+ let context = buildContext(personaMeta, sharedMeta, agentMap, target, registry);
259
426
  const personaMetaTyped = personaMeta;
260
- context = runBuildContext(plugins, context, personaMetaTyped, suiteConfig);
261
- const fmTemplate = resolveFrontmatterTemplate(target, plugins, config.frontmatter);
427
+ context = runBuildContext(plugins, context, personaMetaTyped, suiteConfig, target);
428
+ const fmTemplate = resolveFrontmatterTemplate(target, plugins, config.frontmatter, registry);
262
429
  const contentBasename = path4.basename(personaYamlPath, ".yaml") + ".md";
263
430
  const frontmatter = renderFrontmatter(fmTemplate, context, contentBasename);
264
431
  const contentSubdir = suiteConfig.contentSubdir ?? "content";
@@ -276,15 +443,10 @@ ${body}
276
443
  `);
277
444
  output = runPostRender(plugins, output, personaMetaTyped, target);
278
445
  const validationResults = runValidate(plugins, personaMetaTyped, suiteConfig, target);
279
- const outputDir = target === "vscode" ? suiteConfig.outVscode : suiteConfig.outClaudeCode;
280
- let outputBasename;
281
- if (target === "vscode" && typeof context["vs_file_name"] === "string") {
282
- outputBasename = context["vs_file_name"];
283
- } else if (target === "claude-code" && typeof context["cc_file_name"] === "string") {
284
- outputBasename = context["cc_file_name"];
285
- } else {
286
- outputBasename = contentBasename;
287
- }
446
+ const def = registry.has(target) ? registry.get(target) : void 0;
447
+ const outputDir = resolveOutputDir(target, suiteConfig, def);
448
+ const fnKey = def?.filenameContextKey;
449
+ const outputBasename = fnKey && typeof context[fnKey] === "string" ? context[fnKey] : contentBasename;
288
450
  const outputPath = path4.join(outputDir, outputBasename);
289
451
  const check = config.check ?? false;
290
452
  let written = false;
@@ -303,7 +465,7 @@ ${body}
303
465
  written
304
466
  };
305
467
  }
306
- async function buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap = {}) {
468
+ async function buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap = {}, registry = defaultRegistry) {
307
469
  const metaSubdir = suiteConfig.metaSubdir ?? "meta";
308
470
  const sharedYamlPath = path4.join(suiteConfig.srcDir, metaSubdir, "_shared.yaml");
309
471
  const sharedMeta = await loadRawYaml(sharedYamlPath);
@@ -329,7 +491,8 @@ async function buildSuite(suiteName, suiteConfig, config, plugins, target, agent
329
491
  config,
330
492
  plugins,
331
493
  target,
332
- agentMap
494
+ agentMap,
495
+ registry
333
496
  );
334
497
  results.push(result);
335
498
  }
@@ -337,12 +500,13 @@ async function buildSuite(suiteName, suiteConfig, config, plugins, target, agent
337
500
  }
338
501
  async function build(config) {
339
502
  const plugins = config.plugins ?? [];
340
- const targets = config.targets ?? ["vscode", "claude-code"];
503
+ const registry = config.targetRegistry ?? defaultRegistry;
504
+ const targets = config.targets ?? registry.names().filter((n) => registry.get(n).defaultEnabled !== false);
341
505
  const allResults = [];
342
506
  const agentMap = await buildAgentNameMap(config);
343
507
  for (const [suiteName, suiteConfig] of Object.entries(config.suites)) {
344
508
  for (const target of targets) {
345
- const suiteResults = await buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap);
509
+ const suiteResults = await buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap, registry);
346
510
  allResults.push(...suiteResults);
347
511
  }
348
512
  }
@@ -430,6 +594,6 @@ function escapeRegExp(str) {
430
594
  var _pkgRequire = createRequire(import.meta.url);
431
595
  var VERSION = _pkgRequire("../package.json").version;
432
596
 
433
- export { DEFAULT_FRONTMATTER_CLAUDE_CODE, DEFAULT_FRONTMATTER_VSCODE, VERSION, build, buildPersona, buildSuite, collapseBlankLines, discoverPersonaYamls, ensureBlankLineBeforeHeadings, escapeRegExp, loadContent, loadMetadata, loadPartials, normalizeNewlines, renderFrontmatter, resolveConditionals, resolveFrontmatterTemplate, resolvePartials, resolveVariables, runBuildContext, runPostRender, runSuiteInit, runValidate, serializeTools, serializeToolsList, validateFileName, validateStrictMarkers };
597
+ export { DEFAULT_FRONTMATTER_CLAUDE_CODE, DEFAULT_FRONTMATTER_DEEP_AGENTS, DEFAULT_FRONTMATTER_VSCODE, TARGET_CLAUDE_CODE, TARGET_DEEP_AGENTS, TARGET_VSCODE, TargetRegistry, VERSION, build, buildPersona, buildSuite, collapseBlankLines, defaultRegistry, discoverPersonaYamls, ensureBlankLineBeforeHeadings, escapeRegExp, loadContent, loadMetadata, loadPartials, normalizeNewlines, renderFrontmatter, resolveConditionals, resolveFrontmatterTemplate, resolvePartials, resolveVariables, runBuildContext, runPostRender, runSuiteInit, runValidate, serializeTools, serializeToolsList, validateFileName, validateStrictMarkers };
434
598
  //# sourceMappingURL=index.js.map
435
599
  //# sourceMappingURL=index.js.map