@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/README.md +17 -3
- package/dist/cli.cjs +197 -35
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +197 -35
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +205 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +307 -45
- package/dist/index.d.ts +307 -45
- package/dist/index.js +200 -36
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.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
|
-
|
|
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
|
-
}
|
|
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
|
|
@@ -90,11 +99,11 @@ function runSuiteInit(plugins, suite, sharedMeta) {
|
|
|
90
99
|
}
|
|
91
100
|
}
|
|
92
101
|
}
|
|
93
|
-
function runBuildContext(plugins, ctx, persona, suite) {
|
|
102
|
+
function runBuildContext(plugins, ctx, persona, suite, target) {
|
|
94
103
|
let accumulated = ctx;
|
|
95
104
|
for (const plugin of plugins) {
|
|
96
105
|
if (typeof plugin.onBuildContext === "function") {
|
|
97
|
-
accumulated = plugin.onBuildContext(accumulated, persona, suite);
|
|
106
|
+
accumulated = plugin.onBuildContext(accumulated, persona, suite, target);
|
|
98
107
|
}
|
|
99
108
|
}
|
|
100
109
|
return accumulated;
|
|
@@ -119,7 +128,10 @@ function runValidate(plugins, persona, suite, target) {
|
|
|
119
128
|
return results;
|
|
120
129
|
}
|
|
121
130
|
|
|
122
|
-
// src/
|
|
131
|
+
// src/targets/types.ts
|
|
132
|
+
var TARGET_VSCODE = "vscode";
|
|
133
|
+
var TARGET_CLAUDE_CODE = "claude-code";
|
|
134
|
+
var TARGET_DEEP_AGENTS = "deep-agents";
|
|
123
135
|
var DEFAULT_FRONTMATTER_VSCODE = `---
|
|
124
136
|
name: '{{name}} v{{version}}'
|
|
125
137
|
description: '{{description}}'
|
|
@@ -132,7 +144,13 @@ model: {{cc_model}}
|
|
|
132
144
|
memory: {{cc_memory}}
|
|
133
145
|
allowedTools: [{{cc_tools_list}}]
|
|
134
146
|
---`;
|
|
135
|
-
|
|
147
|
+
var DEFAULT_FRONTMATTER_DEEP_AGENTS = `---
|
|
148
|
+
name: {{name}}
|
|
149
|
+
description: {{description}}
|
|
150
|
+
---`;
|
|
151
|
+
|
|
152
|
+
// src/builders/frontmatter.ts
|
|
153
|
+
function resolveFrontmatterTemplate(target, plugins, configTemplates, registry) {
|
|
136
154
|
for (const plugin of plugins) {
|
|
137
155
|
if (plugin.frontmatterTemplates && target in plugin.frontmatterTemplates) {
|
|
138
156
|
const tpl = plugin.frontmatterTemplates[target];
|
|
@@ -143,7 +161,10 @@ function resolveFrontmatterTemplate(target, plugins, configTemplates) {
|
|
|
143
161
|
const tpl = configTemplates[target];
|
|
144
162
|
if (tpl !== void 0) return tpl;
|
|
145
163
|
}
|
|
146
|
-
|
|
164
|
+
if (registry && registry.has(target)) {
|
|
165
|
+
return registry.get(target).defaultFrontmatter;
|
|
166
|
+
}
|
|
167
|
+
return DEFAULT_FRONTMATTER_VSCODE;
|
|
147
168
|
}
|
|
148
169
|
function renderFrontmatter(template, context, filename) {
|
|
149
170
|
let rendered = resolveConditionals(template, context);
|
|
@@ -151,6 +172,112 @@ function renderFrontmatter(template, context, filename) {
|
|
|
151
172
|
return rendered;
|
|
152
173
|
}
|
|
153
174
|
|
|
175
|
+
// src/targets/registry.ts
|
|
176
|
+
var TargetRegistry = class _TargetRegistry {
|
|
177
|
+
// Map preserves insertion order — names() and allDefinitions() are
|
|
178
|
+
// therefore deterministic and match registration sequence. This is
|
|
179
|
+
// intentional: the built-in registry guarantees ['vscode', 'claude-code']
|
|
180
|
+
// ordering for the default targets (AC-2).
|
|
181
|
+
_definitions = /* @__PURE__ */ new Map();
|
|
182
|
+
/**
|
|
183
|
+
* Register a new target definition.
|
|
184
|
+
*
|
|
185
|
+
* @param definition The target descriptor to register.
|
|
186
|
+
* @throws {Error} If a target with the same `name` is already registered.
|
|
187
|
+
*/
|
|
188
|
+
register(definition) {
|
|
189
|
+
if (this._definitions.has(definition.name)) {
|
|
190
|
+
throw new Error(
|
|
191
|
+
`TargetRegistry: target "${definition.name}" is already registered. Use a unique name or remove the existing registration first.`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
this._definitions.set(definition.name, definition);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Retrieve a registered target definition by name.
|
|
198
|
+
*
|
|
199
|
+
* Returns a shallow copy — mutating the returned object does not affect
|
|
200
|
+
* the registry's internal state.
|
|
201
|
+
*
|
|
202
|
+
* @param name The target name to look up.
|
|
203
|
+
* @returns A shallow copy of the matching TargetDefinition.
|
|
204
|
+
* @throws {Error} If no target with the given name is registered.
|
|
205
|
+
*/
|
|
206
|
+
get(name) {
|
|
207
|
+
const def = this._definitions.get(name);
|
|
208
|
+
if (!def) {
|
|
209
|
+
const known = this.names().join(", ") || "(none)";
|
|
210
|
+
throw new Error(
|
|
211
|
+
`TargetRegistry: target "${name}" is not registered. Registered targets: ${known}.`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
return { ...def };
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Returns `true` if a target with the given name is registered.
|
|
218
|
+
*
|
|
219
|
+
* @param name The target name to check.
|
|
220
|
+
*/
|
|
221
|
+
has(name) {
|
|
222
|
+
return this._definitions.has(name);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Returns the names of all registered targets, in registration order.
|
|
226
|
+
*/
|
|
227
|
+
names() {
|
|
228
|
+
return Array.from(this._definitions.keys());
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Returns all registered TargetDefinition objects, in registration order.
|
|
232
|
+
*
|
|
233
|
+
* Returns shallow copies — mutating a returned definition does not affect
|
|
234
|
+
* the registry's internal state.
|
|
235
|
+
*/
|
|
236
|
+
allDefinitions() {
|
|
237
|
+
return Array.from(this._definitions.values()).map((def) => ({ ...def }));
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Returns a new TargetRegistry pre-populated with the same definitions.
|
|
241
|
+
*
|
|
242
|
+
* Useful for test isolation: clone the `defaultRegistry` to get an
|
|
243
|
+
* independent copy that can be mutated without affecting the singleton.
|
|
244
|
+
*/
|
|
245
|
+
clone() {
|
|
246
|
+
const copy = new _TargetRegistry();
|
|
247
|
+
for (const def of this._definitions.values()) {
|
|
248
|
+
copy.register({ ...def });
|
|
249
|
+
}
|
|
250
|
+
return copy;
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
// src/targets/built-in.ts
|
|
255
|
+
var defaultRegistry = new TargetRegistry();
|
|
256
|
+
defaultRegistry.register({
|
|
257
|
+
name: TARGET_VSCODE,
|
|
258
|
+
outputDirKey: "vscode",
|
|
259
|
+
filenameContextKey: "vs_file_name",
|
|
260
|
+
defaultFrontmatter: DEFAULT_FRONTMATTER_VSCODE,
|
|
261
|
+
contextFlags: { target_vscode: true },
|
|
262
|
+
defaultEnabled: true
|
|
263
|
+
});
|
|
264
|
+
defaultRegistry.register({
|
|
265
|
+
name: TARGET_CLAUDE_CODE,
|
|
266
|
+
outputDirKey: "claude-code",
|
|
267
|
+
filenameContextKey: "cc_file_name",
|
|
268
|
+
defaultFrontmatter: DEFAULT_FRONTMATTER_CLAUDE_CODE,
|
|
269
|
+
contextFlags: { target_claude_code: true },
|
|
270
|
+
defaultEnabled: true
|
|
271
|
+
});
|
|
272
|
+
defaultRegistry.register({
|
|
273
|
+
name: TARGET_DEEP_AGENTS,
|
|
274
|
+
outputDirKey: "deep-agents",
|
|
275
|
+
filenameContextKey: "da_file_name",
|
|
276
|
+
defaultFrontmatter: DEFAULT_FRONTMATTER_DEEP_AGENTS,
|
|
277
|
+
contextFlags: { target_deep_agents: true },
|
|
278
|
+
defaultEnabled: false
|
|
279
|
+
});
|
|
280
|
+
|
|
154
281
|
// src/builders/persona-builder.ts
|
|
155
282
|
async function discoverSuitePersonaYamls(suiteConfig) {
|
|
156
283
|
const metaSubdir = suiteConfig.metaSubdir ?? "meta";
|
|
@@ -178,6 +305,18 @@ async function loadPersonaYaml(yamlPath) {
|
|
|
178
305
|
}
|
|
179
306
|
return record;
|
|
180
307
|
}
|
|
308
|
+
function resolveOutputDir(target, suiteConfig, definition) {
|
|
309
|
+
const merged = {};
|
|
310
|
+
if (suiteConfig.outVscode) merged["vscode"] = suiteConfig.outVscode;
|
|
311
|
+
if (suiteConfig.outClaudeCode) merged["claude-code"] = suiteConfig.outClaudeCode;
|
|
312
|
+
if (suiteConfig.outputDirs) Object.assign(merged, suiteConfig.outputDirs);
|
|
313
|
+
const lookupKey = definition?.outputDirKey ?? target;
|
|
314
|
+
const dir = merged[lookupKey];
|
|
315
|
+
if (dir) return dir;
|
|
316
|
+
throw new Error(
|
|
317
|
+
`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.`
|
|
318
|
+
);
|
|
319
|
+
}
|
|
181
320
|
async function buildAgentNameMap(config) {
|
|
182
321
|
const agentMap = {};
|
|
183
322
|
for (const [, suiteConfig] of Object.entries(config.suites)) {
|
|
@@ -191,13 +330,16 @@ async function buildAgentNameMap(config) {
|
|
|
191
330
|
const slug = typeof persona["slug"] === "string" ? persona["slug"] : path2.basename(yamlPath, ".yaml");
|
|
192
331
|
const name = typeof persona["name"] === "string" ? persona["name"] : slug;
|
|
193
332
|
const version = typeof persona["version"] === "string" ? persona["version"] : defaultVersion;
|
|
194
|
-
const
|
|
333
|
+
const underscoredSlug = slug.replace(/-/g, "_");
|
|
334
|
+
const key = `agent_${underscoredSlug}`;
|
|
195
335
|
agentMap[key] = `${name} v${version}`;
|
|
336
|
+
const slugKey = `agent_slug_${underscoredSlug}`;
|
|
337
|
+
agentMap[slugKey] = slug;
|
|
196
338
|
}
|
|
197
339
|
}
|
|
198
340
|
return agentMap;
|
|
199
341
|
}
|
|
200
|
-
function buildContext(personaMeta, sharedMeta, agentMap = {}) {
|
|
342
|
+
function buildContext(personaMeta, sharedMeta, agentMap = {}, target, registry) {
|
|
201
343
|
const version = typeof personaMeta["version"] === "string" ? personaMeta["version"] : typeof sharedMeta["default_version"] === "string" ? sharedMeta["default_version"] : "0.0.0";
|
|
202
344
|
const merged = {
|
|
203
345
|
...sharedMeta,
|
|
@@ -222,19 +364,42 @@ function buildContext(personaMeta, sharedMeta, agentMap = {}) {
|
|
|
222
364
|
const ccFileName = merged["cc_file_name"];
|
|
223
365
|
merged["cc_file_name_stem"] = ccFileName.replace(/\.md$/, "");
|
|
224
366
|
}
|
|
367
|
+
if (!("da_file_name_stem" in merged) && typeof merged["da_file_name"] === "string") {
|
|
368
|
+
const daFileName = merged["da_file_name"];
|
|
369
|
+
merged["da_file_name_stem"] = daFileName.replace(/\.md$/, "");
|
|
370
|
+
}
|
|
371
|
+
if (typeof merged["da_file_name"] === "string") {
|
|
372
|
+
const daTools = Array.isArray(merged["da_tools"]) ? merged["da_tools"] : tools;
|
|
373
|
+
if (!("da_tools_list" in merged)) {
|
|
374
|
+
merged["da_tools_list"] = serializeToolsList(daTools);
|
|
375
|
+
}
|
|
376
|
+
if (!("da_tools_json" in merged)) {
|
|
377
|
+
merged["da_tools_json"] = serializeTools(daTools);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
225
380
|
for (const [key, value] of Object.entries(agentMap)) {
|
|
226
381
|
if (!(key in merged)) {
|
|
227
382
|
merged[key] = value;
|
|
228
383
|
}
|
|
229
384
|
}
|
|
385
|
+
if (target !== void 0) {
|
|
386
|
+
if (registry && registry.has(target)) {
|
|
387
|
+
const flags = registry.get(target).contextFlags ?? {};
|
|
388
|
+
for (const [key, value] of Object.entries(flags)) {
|
|
389
|
+
merged[key] = value;
|
|
390
|
+
}
|
|
391
|
+
} else {
|
|
392
|
+
merged[`target_${target.replace(/-/g, "_")}`] = true;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
230
395
|
return merged;
|
|
231
396
|
}
|
|
232
|
-
async function buildPersona(personaYamlPath, suiteName, suiteConfig, sharedMeta, partialsMap, config, plugins, target, agentMap = {}) {
|
|
397
|
+
async function buildPersona(personaYamlPath, suiteName, suiteConfig, sharedMeta, partialsMap, config, plugins, target, agentMap = {}, registry = defaultRegistry) {
|
|
233
398
|
const personaMeta = await loadPersonaYaml(personaYamlPath);
|
|
234
|
-
let context = buildContext(personaMeta, sharedMeta, agentMap);
|
|
399
|
+
let context = buildContext(personaMeta, sharedMeta, agentMap, target, registry);
|
|
235
400
|
const personaMetaTyped = personaMeta;
|
|
236
|
-
context = runBuildContext(plugins, context, personaMetaTyped, suiteConfig);
|
|
237
|
-
const fmTemplate = resolveFrontmatterTemplate(target, plugins, config.frontmatter);
|
|
401
|
+
context = runBuildContext(plugins, context, personaMetaTyped, suiteConfig, target);
|
|
402
|
+
const fmTemplate = resolveFrontmatterTemplate(target, plugins, config.frontmatter, registry);
|
|
238
403
|
const contentBasename = path2.basename(personaYamlPath, ".yaml") + ".md";
|
|
239
404
|
const frontmatter = renderFrontmatter(fmTemplate, context, contentBasename);
|
|
240
405
|
const contentSubdir = suiteConfig.contentSubdir ?? "content";
|
|
@@ -252,15 +417,10 @@ ${body}
|
|
|
252
417
|
`);
|
|
253
418
|
output = runPostRender(plugins, output, personaMetaTyped, target);
|
|
254
419
|
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
|
-
}
|
|
420
|
+
const def = registry.has(target) ? registry.get(target) : void 0;
|
|
421
|
+
const outputDir = resolveOutputDir(target, suiteConfig, def);
|
|
422
|
+
const fnKey = def?.filenameContextKey;
|
|
423
|
+
const outputBasename = fnKey && typeof context[fnKey] === "string" ? context[fnKey] : contentBasename;
|
|
264
424
|
const outputPath = path2.join(outputDir, outputBasename);
|
|
265
425
|
const check = config.check ?? false;
|
|
266
426
|
let written = false;
|
|
@@ -279,7 +439,7 @@ ${body}
|
|
|
279
439
|
written
|
|
280
440
|
};
|
|
281
441
|
}
|
|
282
|
-
async function buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap = {}) {
|
|
442
|
+
async function buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap = {}, registry = defaultRegistry) {
|
|
283
443
|
const metaSubdir = suiteConfig.metaSubdir ?? "meta";
|
|
284
444
|
const sharedYamlPath = path2.join(suiteConfig.srcDir, metaSubdir, "_shared.yaml");
|
|
285
445
|
const sharedMeta = await loadRawYaml(sharedYamlPath);
|
|
@@ -305,7 +465,8 @@ async function buildSuite(suiteName, suiteConfig, config, plugins, target, agent
|
|
|
305
465
|
config,
|
|
306
466
|
plugins,
|
|
307
467
|
target,
|
|
308
|
-
agentMap
|
|
468
|
+
agentMap,
|
|
469
|
+
registry
|
|
309
470
|
);
|
|
310
471
|
results.push(result);
|
|
311
472
|
}
|
|
@@ -313,12 +474,13 @@ async function buildSuite(suiteName, suiteConfig, config, plugins, target, agent
|
|
|
313
474
|
}
|
|
314
475
|
async function build(config) {
|
|
315
476
|
const plugins = config.plugins ?? [];
|
|
316
|
-
const
|
|
477
|
+
const registry = config.targetRegistry ?? defaultRegistry;
|
|
478
|
+
const targets = config.targets ?? registry.names().filter((n) => registry.get(n).defaultEnabled !== false);
|
|
317
479
|
const allResults = [];
|
|
318
480
|
const agentMap = await buildAgentNameMap(config);
|
|
319
481
|
for (const [suiteName, suiteConfig] of Object.entries(config.suites)) {
|
|
320
482
|
for (const target of targets) {
|
|
321
|
-
const suiteResults = await buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap);
|
|
483
|
+
const suiteResults = await buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap, registry);
|
|
322
484
|
allResults.push(...suiteResults);
|
|
323
485
|
}
|
|
324
486
|
}
|