@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/README.md +12 -5
- package/dist/cli.cjs +217 -35
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +217 -35
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +225 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +313 -45
- package/dist/index.d.ts +313 -45
- package/dist/index.js +220 -36
- package/dist/index.js.map +1 -1
- package/package.json +53 -53
package/dist/cli.js
CHANGED
|
@@ -19,19 +19,48 @@ function resolvePartials(text, partialsMap, depth = 0) {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
// src/engine/conditionals.ts
|
|
22
|
+
var NO_NESTED_IF = String.raw`(?:(?!\{\{#if\b)[\s\S])*?`;
|
|
23
|
+
var ELSE_IF_PATTERN = new RegExp(
|
|
24
|
+
String.raw`\{\{else if (\w+)\}\}(${NO_NESTED_IF})\{\{\/if\}\}`,
|
|
25
|
+
"g"
|
|
26
|
+
);
|
|
27
|
+
function resolveElseIf(text) {
|
|
28
|
+
if (!text.includes("{{else if ")) {
|
|
29
|
+
return text;
|
|
30
|
+
}
|
|
31
|
+
let result = text;
|
|
32
|
+
let prev;
|
|
33
|
+
do {
|
|
34
|
+
prev = result;
|
|
35
|
+
result = result.replace(
|
|
36
|
+
ELSE_IF_PATTERN,
|
|
37
|
+
(_match, flag, content) => `{{else}}{{#if ${flag}}}${content}{{/if}}{{/if}}`
|
|
38
|
+
);
|
|
39
|
+
} while (result !== prev);
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
22
42
|
function resolveConditionals(text, context) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
(
|
|
26
|
-
|
|
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
|
-
}
|
|
43
|
+
const normalized = resolveElseIf(text);
|
|
44
|
+
const pattern = new RegExp(
|
|
45
|
+
String.raw`\n*\{\{#if (\w+)\}\}(${NO_NESTED_IF})` + String.raw`(?:\{\{else\}\}(${NO_NESTED_IF}))?\{\{\/if\}\}\n*`,
|
|
46
|
+
"g"
|
|
34
47
|
);
|
|
48
|
+
const resolve = (_match, flag, inner, elseInner) => {
|
|
49
|
+
if (context[flag]) {
|
|
50
|
+
return "\n" + inner.replace(/^\n+/, "").replace(/\n+$/, "") + "\n";
|
|
51
|
+
}
|
|
52
|
+
if (elseInner !== void 0) {
|
|
53
|
+
return "\n" + elseInner.replace(/^\n+/, "").replace(/\n+$/, "") + "\n";
|
|
54
|
+
}
|
|
55
|
+
return "\n";
|
|
56
|
+
};
|
|
57
|
+
let result = normalized;
|
|
58
|
+
let prev;
|
|
59
|
+
do {
|
|
60
|
+
prev = result;
|
|
61
|
+
result = result.replace(pattern, resolve);
|
|
62
|
+
} while (result !== prev);
|
|
63
|
+
return result;
|
|
35
64
|
}
|
|
36
65
|
|
|
37
66
|
// src/engine/variables.ts
|
|
@@ -90,11 +119,11 @@ function runSuiteInit(plugins, suite, sharedMeta) {
|
|
|
90
119
|
}
|
|
91
120
|
}
|
|
92
121
|
}
|
|
93
|
-
function runBuildContext(plugins, ctx, persona, suite) {
|
|
122
|
+
function runBuildContext(plugins, ctx, persona, suite, target) {
|
|
94
123
|
let accumulated = ctx;
|
|
95
124
|
for (const plugin of plugins) {
|
|
96
125
|
if (typeof plugin.onBuildContext === "function") {
|
|
97
|
-
accumulated = plugin.onBuildContext(accumulated, persona, suite);
|
|
126
|
+
accumulated = plugin.onBuildContext(accumulated, persona, suite, target);
|
|
98
127
|
}
|
|
99
128
|
}
|
|
100
129
|
return accumulated;
|
|
@@ -119,7 +148,10 @@ function runValidate(plugins, persona, suite, target) {
|
|
|
119
148
|
return results;
|
|
120
149
|
}
|
|
121
150
|
|
|
122
|
-
// src/
|
|
151
|
+
// src/targets/types.ts
|
|
152
|
+
var TARGET_VSCODE = "vscode";
|
|
153
|
+
var TARGET_CLAUDE_CODE = "claude-code";
|
|
154
|
+
var TARGET_DEEP_AGENTS = "deep-agents";
|
|
123
155
|
var DEFAULT_FRONTMATTER_VSCODE = `---
|
|
124
156
|
name: '{{name}} v{{version}}'
|
|
125
157
|
description: '{{description}}'
|
|
@@ -132,7 +164,13 @@ model: {{cc_model}}
|
|
|
132
164
|
memory: {{cc_memory}}
|
|
133
165
|
allowedTools: [{{cc_tools_list}}]
|
|
134
166
|
---`;
|
|
135
|
-
|
|
167
|
+
var DEFAULT_FRONTMATTER_DEEP_AGENTS = `---
|
|
168
|
+
name: {{name}}
|
|
169
|
+
description: {{description}}
|
|
170
|
+
---`;
|
|
171
|
+
|
|
172
|
+
// src/builders/frontmatter.ts
|
|
173
|
+
function resolveFrontmatterTemplate(target, plugins, configTemplates, registry) {
|
|
136
174
|
for (const plugin of plugins) {
|
|
137
175
|
if (plugin.frontmatterTemplates && target in plugin.frontmatterTemplates) {
|
|
138
176
|
const tpl = plugin.frontmatterTemplates[target];
|
|
@@ -143,7 +181,10 @@ function resolveFrontmatterTemplate(target, plugins, configTemplates) {
|
|
|
143
181
|
const tpl = configTemplates[target];
|
|
144
182
|
if (tpl !== void 0) return tpl;
|
|
145
183
|
}
|
|
146
|
-
|
|
184
|
+
if (registry && registry.has(target)) {
|
|
185
|
+
return registry.get(target).defaultFrontmatter;
|
|
186
|
+
}
|
|
187
|
+
return DEFAULT_FRONTMATTER_VSCODE;
|
|
147
188
|
}
|
|
148
189
|
function renderFrontmatter(template, context, filename) {
|
|
149
190
|
let rendered = resolveConditionals(template, context);
|
|
@@ -151,6 +192,112 @@ function renderFrontmatter(template, context, filename) {
|
|
|
151
192
|
return rendered;
|
|
152
193
|
}
|
|
153
194
|
|
|
195
|
+
// src/targets/registry.ts
|
|
196
|
+
var TargetRegistry = class _TargetRegistry {
|
|
197
|
+
// Map preserves insertion order — names() and allDefinitions() are
|
|
198
|
+
// therefore deterministic and match registration sequence. This is
|
|
199
|
+
// intentional: the built-in registry guarantees ['vscode', 'claude-code']
|
|
200
|
+
// ordering for the default targets (AC-2).
|
|
201
|
+
_definitions = /* @__PURE__ */ new Map();
|
|
202
|
+
/**
|
|
203
|
+
* Register a new target definition.
|
|
204
|
+
*
|
|
205
|
+
* @param definition The target descriptor to register.
|
|
206
|
+
* @throws {Error} If a target with the same `name` is already registered.
|
|
207
|
+
*/
|
|
208
|
+
register(definition) {
|
|
209
|
+
if (this._definitions.has(definition.name)) {
|
|
210
|
+
throw new Error(
|
|
211
|
+
`TargetRegistry: target "${definition.name}" is already registered. Use a unique name or remove the existing registration first.`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
this._definitions.set(definition.name, definition);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Retrieve a registered target definition by name.
|
|
218
|
+
*
|
|
219
|
+
* Returns a shallow copy — mutating the returned object does not affect
|
|
220
|
+
* the registry's internal state.
|
|
221
|
+
*
|
|
222
|
+
* @param name The target name to look up.
|
|
223
|
+
* @returns A shallow copy of the matching TargetDefinition.
|
|
224
|
+
* @throws {Error} If no target with the given name is registered.
|
|
225
|
+
*/
|
|
226
|
+
get(name) {
|
|
227
|
+
const def = this._definitions.get(name);
|
|
228
|
+
if (!def) {
|
|
229
|
+
const known = this.names().join(", ") || "(none)";
|
|
230
|
+
throw new Error(
|
|
231
|
+
`TargetRegistry: target "${name}" is not registered. Registered targets: ${known}.`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
return { ...def };
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Returns `true` if a target with the given name is registered.
|
|
238
|
+
*
|
|
239
|
+
* @param name The target name to check.
|
|
240
|
+
*/
|
|
241
|
+
has(name) {
|
|
242
|
+
return this._definitions.has(name);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Returns the names of all registered targets, in registration order.
|
|
246
|
+
*/
|
|
247
|
+
names() {
|
|
248
|
+
return Array.from(this._definitions.keys());
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Returns all registered TargetDefinition objects, in registration order.
|
|
252
|
+
*
|
|
253
|
+
* Returns shallow copies — mutating a returned definition does not affect
|
|
254
|
+
* the registry's internal state.
|
|
255
|
+
*/
|
|
256
|
+
allDefinitions() {
|
|
257
|
+
return Array.from(this._definitions.values()).map((def) => ({ ...def }));
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Returns a new TargetRegistry pre-populated with the same definitions.
|
|
261
|
+
*
|
|
262
|
+
* Useful for test isolation: clone the `defaultRegistry` to get an
|
|
263
|
+
* independent copy that can be mutated without affecting the singleton.
|
|
264
|
+
*/
|
|
265
|
+
clone() {
|
|
266
|
+
const copy = new _TargetRegistry();
|
|
267
|
+
for (const def of this._definitions.values()) {
|
|
268
|
+
copy.register({ ...def });
|
|
269
|
+
}
|
|
270
|
+
return copy;
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// src/targets/built-in.ts
|
|
275
|
+
var defaultRegistry = new TargetRegistry();
|
|
276
|
+
defaultRegistry.register({
|
|
277
|
+
name: TARGET_VSCODE,
|
|
278
|
+
outputDirKey: "vscode",
|
|
279
|
+
filenameContextKey: "vs_file_name",
|
|
280
|
+
defaultFrontmatter: DEFAULT_FRONTMATTER_VSCODE,
|
|
281
|
+
contextFlags: { target_vscode: true },
|
|
282
|
+
defaultEnabled: true
|
|
283
|
+
});
|
|
284
|
+
defaultRegistry.register({
|
|
285
|
+
name: TARGET_CLAUDE_CODE,
|
|
286
|
+
outputDirKey: "claude-code",
|
|
287
|
+
filenameContextKey: "cc_file_name",
|
|
288
|
+
defaultFrontmatter: DEFAULT_FRONTMATTER_CLAUDE_CODE,
|
|
289
|
+
contextFlags: { target_claude_code: true },
|
|
290
|
+
defaultEnabled: true
|
|
291
|
+
});
|
|
292
|
+
defaultRegistry.register({
|
|
293
|
+
name: TARGET_DEEP_AGENTS,
|
|
294
|
+
outputDirKey: "deep-agents",
|
|
295
|
+
filenameContextKey: "da_file_name",
|
|
296
|
+
defaultFrontmatter: DEFAULT_FRONTMATTER_DEEP_AGENTS,
|
|
297
|
+
contextFlags: { target_deep_agents: true },
|
|
298
|
+
defaultEnabled: false
|
|
299
|
+
});
|
|
300
|
+
|
|
154
301
|
// src/builders/persona-builder.ts
|
|
155
302
|
async function discoverSuitePersonaYamls(suiteConfig) {
|
|
156
303
|
const metaSubdir = suiteConfig.metaSubdir ?? "meta";
|
|
@@ -178,6 +325,18 @@ async function loadPersonaYaml(yamlPath) {
|
|
|
178
325
|
}
|
|
179
326
|
return record;
|
|
180
327
|
}
|
|
328
|
+
function resolveOutputDir(target, suiteConfig, definition) {
|
|
329
|
+
const merged = {};
|
|
330
|
+
if (suiteConfig.outVscode) merged["vscode"] = suiteConfig.outVscode;
|
|
331
|
+
if (suiteConfig.outClaudeCode) merged["claude-code"] = suiteConfig.outClaudeCode;
|
|
332
|
+
if (suiteConfig.outputDirs) Object.assign(merged, suiteConfig.outputDirs);
|
|
333
|
+
const lookupKey = definition?.outputDirKey ?? target;
|
|
334
|
+
const dir = merged[lookupKey];
|
|
335
|
+
if (dir) return dir;
|
|
336
|
+
throw new Error(
|
|
337
|
+
`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.`
|
|
338
|
+
);
|
|
339
|
+
}
|
|
181
340
|
async function buildAgentNameMap(config) {
|
|
182
341
|
const agentMap = {};
|
|
183
342
|
for (const [, suiteConfig] of Object.entries(config.suites)) {
|
|
@@ -191,13 +350,16 @@ async function buildAgentNameMap(config) {
|
|
|
191
350
|
const slug = typeof persona["slug"] === "string" ? persona["slug"] : path2.basename(yamlPath, ".yaml");
|
|
192
351
|
const name = typeof persona["name"] === "string" ? persona["name"] : slug;
|
|
193
352
|
const version = typeof persona["version"] === "string" ? persona["version"] : defaultVersion;
|
|
194
|
-
const
|
|
353
|
+
const underscoredSlug = slug.replace(/-/g, "_");
|
|
354
|
+
const key = `agent_${underscoredSlug}`;
|
|
195
355
|
agentMap[key] = `${name} v${version}`;
|
|
356
|
+
const slugKey = `agent_slug_${underscoredSlug}`;
|
|
357
|
+
agentMap[slugKey] = slug;
|
|
196
358
|
}
|
|
197
359
|
}
|
|
198
360
|
return agentMap;
|
|
199
361
|
}
|
|
200
|
-
function buildContext(personaMeta, sharedMeta, agentMap = {}) {
|
|
362
|
+
function buildContext(personaMeta, sharedMeta, agentMap = {}, target, registry) {
|
|
201
363
|
const version = typeof personaMeta["version"] === "string" ? personaMeta["version"] : typeof sharedMeta["default_version"] === "string" ? sharedMeta["default_version"] : "0.0.0";
|
|
202
364
|
const merged = {
|
|
203
365
|
...sharedMeta,
|
|
@@ -222,19 +384,42 @@ function buildContext(personaMeta, sharedMeta, agentMap = {}) {
|
|
|
222
384
|
const ccFileName = merged["cc_file_name"];
|
|
223
385
|
merged["cc_file_name_stem"] = ccFileName.replace(/\.md$/, "");
|
|
224
386
|
}
|
|
387
|
+
if (!("da_file_name_stem" in merged) && typeof merged["da_file_name"] === "string") {
|
|
388
|
+
const daFileName = merged["da_file_name"];
|
|
389
|
+
merged["da_file_name_stem"] = daFileName.replace(/\.md$/, "");
|
|
390
|
+
}
|
|
391
|
+
if (typeof merged["da_file_name"] === "string") {
|
|
392
|
+
const daTools = Array.isArray(merged["da_tools"]) ? merged["da_tools"] : tools;
|
|
393
|
+
if (!("da_tools_list" in merged)) {
|
|
394
|
+
merged["da_tools_list"] = serializeToolsList(daTools);
|
|
395
|
+
}
|
|
396
|
+
if (!("da_tools_json" in merged)) {
|
|
397
|
+
merged["da_tools_json"] = serializeTools(daTools);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
225
400
|
for (const [key, value] of Object.entries(agentMap)) {
|
|
226
401
|
if (!(key in merged)) {
|
|
227
402
|
merged[key] = value;
|
|
228
403
|
}
|
|
229
404
|
}
|
|
405
|
+
if (target !== void 0) {
|
|
406
|
+
if (registry && registry.has(target)) {
|
|
407
|
+
const flags = registry.get(target).contextFlags ?? {};
|
|
408
|
+
for (const [key, value] of Object.entries(flags)) {
|
|
409
|
+
merged[key] = value;
|
|
410
|
+
}
|
|
411
|
+
} else {
|
|
412
|
+
merged[`target_${target.replace(/-/g, "_")}`] = true;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
230
415
|
return merged;
|
|
231
416
|
}
|
|
232
|
-
async function buildPersona(personaYamlPath, suiteName, suiteConfig, sharedMeta, partialsMap, config, plugins, target, agentMap = {}) {
|
|
417
|
+
async function buildPersona(personaYamlPath, suiteName, suiteConfig, sharedMeta, partialsMap, config, plugins, target, agentMap = {}, registry = defaultRegistry) {
|
|
233
418
|
const personaMeta = await loadPersonaYaml(personaYamlPath);
|
|
234
|
-
let context = buildContext(personaMeta, sharedMeta, agentMap);
|
|
419
|
+
let context = buildContext(personaMeta, sharedMeta, agentMap, target, registry);
|
|
235
420
|
const personaMetaTyped = personaMeta;
|
|
236
|
-
context = runBuildContext(plugins, context, personaMetaTyped, suiteConfig);
|
|
237
|
-
const fmTemplate = resolveFrontmatterTemplate(target, plugins, config.frontmatter);
|
|
421
|
+
context = runBuildContext(plugins, context, personaMetaTyped, suiteConfig, target);
|
|
422
|
+
const fmTemplate = resolveFrontmatterTemplate(target, plugins, config.frontmatter, registry);
|
|
238
423
|
const contentBasename = path2.basename(personaYamlPath, ".yaml") + ".md";
|
|
239
424
|
const frontmatter = renderFrontmatter(fmTemplate, context, contentBasename);
|
|
240
425
|
const contentSubdir = suiteConfig.contentSubdir ?? "content";
|
|
@@ -252,15 +437,10 @@ ${body}
|
|
|
252
437
|
`);
|
|
253
438
|
output = runPostRender(plugins, output, personaMetaTyped, target);
|
|
254
439
|
const validationResults = runValidate(plugins, personaMetaTyped, suiteConfig, target);
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
} else if (target === "claude-code" && typeof context["cc_file_name"] === "string") {
|
|
260
|
-
outputBasename = context["cc_file_name"];
|
|
261
|
-
} else {
|
|
262
|
-
outputBasename = contentBasename;
|
|
263
|
-
}
|
|
440
|
+
const def = registry.has(target) ? registry.get(target) : void 0;
|
|
441
|
+
const outputDir = resolveOutputDir(target, suiteConfig, def);
|
|
442
|
+
const fnKey = def?.filenameContextKey;
|
|
443
|
+
const outputBasename = fnKey && typeof context[fnKey] === "string" ? context[fnKey] : contentBasename;
|
|
264
444
|
const outputPath = path2.join(outputDir, outputBasename);
|
|
265
445
|
const check = config.check ?? false;
|
|
266
446
|
let written = false;
|
|
@@ -279,7 +459,7 @@ ${body}
|
|
|
279
459
|
written
|
|
280
460
|
};
|
|
281
461
|
}
|
|
282
|
-
async function buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap = {}) {
|
|
462
|
+
async function buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap = {}, registry = defaultRegistry) {
|
|
283
463
|
const metaSubdir = suiteConfig.metaSubdir ?? "meta";
|
|
284
464
|
const sharedYamlPath = path2.join(suiteConfig.srcDir, metaSubdir, "_shared.yaml");
|
|
285
465
|
const sharedMeta = await loadRawYaml(sharedYamlPath);
|
|
@@ -305,7 +485,8 @@ async function buildSuite(suiteName, suiteConfig, config, plugins, target, agent
|
|
|
305
485
|
config,
|
|
306
486
|
plugins,
|
|
307
487
|
target,
|
|
308
|
-
agentMap
|
|
488
|
+
agentMap,
|
|
489
|
+
registry
|
|
309
490
|
);
|
|
310
491
|
results.push(result);
|
|
311
492
|
}
|
|
@@ -313,12 +494,13 @@ async function buildSuite(suiteName, suiteConfig, config, plugins, target, agent
|
|
|
313
494
|
}
|
|
314
495
|
async function build(config) {
|
|
315
496
|
const plugins = config.plugins ?? [];
|
|
316
|
-
const
|
|
497
|
+
const registry = config.targetRegistry ?? defaultRegistry;
|
|
498
|
+
const targets = config.targets ?? registry.names().filter((n) => registry.get(n).defaultEnabled !== false);
|
|
317
499
|
const allResults = [];
|
|
318
500
|
const agentMap = await buildAgentNameMap(config);
|
|
319
501
|
for (const [suiteName, suiteConfig] of Object.entries(config.suites)) {
|
|
320
502
|
for (const target of targets) {
|
|
321
|
-
const suiteResults = await buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap);
|
|
503
|
+
const suiteResults = await buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap, registry);
|
|
322
504
|
allResults.push(...suiteResults);
|
|
323
505
|
}
|
|
324
506
|
}
|