@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/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
|
-
|
|
32
|
-
|
|
33
|
-
(
|
|
34
|
-
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
|
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;
|