agentsmesh 0.33.0 → 0.34.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/CHANGELOG.md +20 -0
- package/README.md +72 -121
- package/dist/canonical.js +491 -274
- package/dist/canonical.js.map +1 -1
- package/dist/cli.js +254 -253
- package/dist/engine.d.ts +1 -1
- package/dist/engine.js +569 -242
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +772 -401
- package/dist/index.js.map +1 -1
- package/dist/{init-rvKjGLTB.d.ts → init-ChDbKSJ0.d.ts} +8 -8
- package/dist/lessons.d.ts +2 -2
- package/dist/lessons.js +1121 -973
- package/dist/lessons.js.map +1 -1
- package/dist/{target-descriptor-DeS4XOtV.d.ts → target-descriptor-DtgeHKPG.d.ts} +7 -0
- package/dist/targets.d.ts +2 -2
- package/dist/targets.js +331 -110
- package/dist/targets.js.map +1 -1
- package/package.json +27 -22
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { stringify, parse, parseDocument, YAMLSeq, YAMLMap, isMap, Document, isSeq, isScalar, Scalar, Pair } from 'yaml';
|
|
3
|
+
import { readFileSync, existsSync, mkdirSync, writeFileSync, constants, rmSync, renameSync, readdirSync, realpathSync, statSync } from 'fs';
|
|
3
4
|
import { join, resolve, relative, sep, dirname, basename, win32, posix, extname } from 'path';
|
|
4
5
|
import { mkdir, access, readdir, rm, readFile, writeFile, stat, lstat, unlink, rename, chmod, realpath, mkdtemp, cp } from 'fs/promises';
|
|
5
6
|
import { setTimeout as setTimeout$1 } from 'timers/promises';
|
|
6
|
-
import { readFileSync, existsSync, mkdirSync, writeFileSync, constants, rmSync, renameSync, readdirSync, realpathSync, statSync } from 'fs';
|
|
7
7
|
import { parse as parse$1, stringify as stringify$1 } from 'smol-toml';
|
|
8
8
|
import { Buffer } from 'buffer';
|
|
9
9
|
import { homedir, hostname, tmpdir } from 'os';
|
|
@@ -94,8 +94,17 @@ var init_target_descriptor_schema = __esm({
|
|
|
94
94
|
managedOutputsSchema = z.object({
|
|
95
95
|
dirs: z.array(z.string()),
|
|
96
96
|
files: z.array(z.string()),
|
|
97
|
-
coOwnedFiles: z.array(z.string()).optional()
|
|
97
|
+
coOwnedFiles: z.array(z.string()).optional(),
|
|
98
|
+
supersededFiles: z.array(z.string()).optional()
|
|
98
99
|
}).passthrough().superRefine((value, ctx) => {
|
|
100
|
+
for (const file of value.supersededFiles ?? []) {
|
|
101
|
+
if (!value.files.includes(file) && !(value.coOwnedFiles ?? []).includes(file)) continue;
|
|
102
|
+
ctx.addIssue({
|
|
103
|
+
code: "custom",
|
|
104
|
+
path: ["supersededFiles"],
|
|
105
|
+
message: `"${file}" is in managedOutputs.supersededFiles and another managedOutputs list.`
|
|
106
|
+
});
|
|
107
|
+
}
|
|
99
108
|
for (const file of value.coOwnedFiles ?? []) {
|
|
100
109
|
if (!value.files.includes(file)) continue;
|
|
101
110
|
ctx.addIssue({
|
|
@@ -398,24 +407,28 @@ var init_target_ids = __esm({
|
|
|
398
407
|
}
|
|
399
408
|
});
|
|
400
409
|
function parseFrontmatter(content) {
|
|
401
|
-
const
|
|
402
|
-
if (
|
|
403
|
-
|
|
404
|
-
}
|
|
405
|
-
const close = content.indexOf("---", 3);
|
|
406
|
-
if (close === -1) {
|
|
407
|
-
return { frontmatter: {}, body: content.trim() };
|
|
408
|
-
}
|
|
409
|
-
const yamlStr = content.slice(3, close).trim();
|
|
410
|
-
const body = content.slice(close + 3).trim();
|
|
410
|
+
const split = splitFrontmatter(content);
|
|
411
|
+
if (split === null) return { frontmatter: {}, body: content.trim() };
|
|
412
|
+
const yamlStr = split.yaml.trim();
|
|
411
413
|
const frontmatter = yamlStr === "" ? {} : parse(yamlStr) ?? {};
|
|
412
|
-
return { frontmatter, body };
|
|
414
|
+
return { frontmatter, body: split.body };
|
|
415
|
+
}
|
|
416
|
+
function splitFrontmatter(content) {
|
|
417
|
+
const opener = OPENER.exec(content);
|
|
418
|
+
if (opener === null) return null;
|
|
419
|
+
const yamlStart = opener[0].length;
|
|
420
|
+
const closer = CLOSER.exec(content.slice(yamlStart));
|
|
421
|
+
if (closer === null) return null;
|
|
422
|
+
const closeStart = yamlStart + closer.index;
|
|
423
|
+
const closeEnd = closeStart + closer[0].length;
|
|
424
|
+
return {
|
|
425
|
+
yaml: content.slice(yamlStart, closeStart),
|
|
426
|
+
body: content.slice(closeEnd).trim(),
|
|
427
|
+
prefix: content.slice(0, closeEnd)
|
|
428
|
+
};
|
|
413
429
|
}
|
|
414
430
|
function extractBody(content) {
|
|
415
|
-
|
|
416
|
-
const close = content.indexOf("---", 3);
|
|
417
|
-
if (close === -1) return content.trim();
|
|
418
|
-
return content.slice(close + 3).trim();
|
|
431
|
+
return splitFrontmatter(content)?.body ?? content.trim();
|
|
419
432
|
}
|
|
420
433
|
function tryParseFrontmatter(content, filePath2) {
|
|
421
434
|
try {
|
|
@@ -447,8 +460,11 @@ ${yamlStr}
|
|
|
447
460
|
|
|
448
461
|
${body}`;
|
|
449
462
|
}
|
|
463
|
+
var OPENER, CLOSER;
|
|
450
464
|
var init_markdown = __esm({
|
|
451
465
|
"src/utils/text/markdown.ts"() {
|
|
466
|
+
OPENER = /^---[ \t]*\r?\n/;
|
|
467
|
+
CLOSER = /^---[ \t]*\r?$/m;
|
|
452
468
|
}
|
|
453
469
|
});
|
|
454
470
|
|
|
@@ -508,11 +524,60 @@ var init_command_skill = __esm({
|
|
|
508
524
|
LEGACY_CODEX_COMMAND_SKILL_PREFIX = "ab-command-";
|
|
509
525
|
}
|
|
510
526
|
});
|
|
527
|
+
function injectEvent(doc, event, matcher) {
|
|
528
|
+
const existing = doc.get(event);
|
|
529
|
+
const seq = existing instanceof YAMLSeq ? existing : new YAMLSeq();
|
|
530
|
+
const present = seq.items.some(
|
|
531
|
+
(item) => item instanceof YAMLMap && item.get("command") === RECALL_HOOK_COMMAND
|
|
532
|
+
);
|
|
533
|
+
if (present) return false;
|
|
534
|
+
seq.add(doc.createNode({ matcher, type: "command", command: RECALL_HOOK_COMMAND }));
|
|
535
|
+
doc.set(event, seq);
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
function injectRecallHook(projectRoot) {
|
|
539
|
+
const path = join(projectRoot, ".agentsmesh", "hooks.yaml");
|
|
540
|
+
if (!existsSync(path)) return false;
|
|
541
|
+
const doc = parseDocument(readFileSync(path, "utf8"));
|
|
542
|
+
let changed = false;
|
|
543
|
+
for (const { event, matcher } of RECALL_EVENTS) {
|
|
544
|
+
if (injectEvent(doc, event, matcher)) changed = true;
|
|
545
|
+
}
|
|
546
|
+
if (changed) writeFileSync(path, String(doc), "utf8");
|
|
547
|
+
return changed;
|
|
548
|
+
}
|
|
549
|
+
var RECALL_HOOK_COMMAND, RECALL_HOOK_TOOL_MATCHER, RECALL_EVENTS;
|
|
550
|
+
var init_recall_hook_scaffold = __esm({
|
|
551
|
+
"src/lessons/recall-hook-scaffold.ts"() {
|
|
552
|
+
RECALL_HOOK_COMMAND = "agentsmesh lessons hook";
|
|
553
|
+
RECALL_HOOK_TOOL_MATCHER = "Edit|Write|Bash";
|
|
554
|
+
RECALL_EVENTS = [
|
|
555
|
+
{ event: "PreToolUse", matcher: RECALL_HOOK_TOOL_MATCHER },
|
|
556
|
+
{ event: "PostToolUse", matcher: RECALL_HOOK_TOOL_MATCHER },
|
|
557
|
+
{ event: "UserPromptSubmit", matcher: "*" },
|
|
558
|
+
// Capture-on-failure nudge (see capture-nudge.ts). BEST-EFFORT: only Claude
|
|
559
|
+
// Code's passthrough hooks emit it; whitelist targets drop it without warning
|
|
560
|
+
// (BEST_EFFORT_HOOK_EVENTS). PostToolUse is success-only, so failures need this.
|
|
561
|
+
{ event: "PostToolUseFailure", matcher: "*" },
|
|
562
|
+
// Reset recall dedup after a context compaction/clear (see hook.ts SessionStart).
|
|
563
|
+
// BEST-EFFORT: targets that can't represent SessionStart just keep dedup as-is.
|
|
564
|
+
{ event: "SessionStart", matcher: "*" }
|
|
565
|
+
];
|
|
566
|
+
}
|
|
567
|
+
});
|
|
511
568
|
|
|
512
569
|
// src/core/hook-types.ts
|
|
570
|
+
function isBestEffortHookEvent(event, entries) {
|
|
571
|
+
if (!BEST_EFFORT_HOOK_EVENTS.has(event)) return false;
|
|
572
|
+
if (!Array.isArray(entries)) return true;
|
|
573
|
+
return entries.every(
|
|
574
|
+
(entry) => typeof entry === "object" && entry !== null && typeof entry.command === "string" && entry.command.includes(RECALL_HOOK_COMMAND)
|
|
575
|
+
);
|
|
576
|
+
}
|
|
513
577
|
var BEST_EFFORT_HOOK_EVENTS;
|
|
514
578
|
var init_hook_types = __esm({
|
|
515
579
|
"src/core/hook-types.ts"() {
|
|
580
|
+
init_recall_hook_scaffold();
|
|
516
581
|
BEST_EFFORT_HOOK_EVENTS = /* @__PURE__ */ new Set([
|
|
517
582
|
"UserPromptSubmit",
|
|
518
583
|
"PostToolUseFailure",
|
|
@@ -698,7 +763,7 @@ var init_conf_merge = __esm({
|
|
|
698
763
|
});
|
|
699
764
|
|
|
700
765
|
// src/core/errors.ts
|
|
701
|
-
var AgentsMeshError, ConfigNotFoundError, ConfigValidationError, TargetNotFoundError, ImportError, GenerationError, RemoteFetchError, LockAcquisitionError, FileSystemError;
|
|
766
|
+
var AgentsMeshError, ConfigNotFoundError, ConfigValidationError, CanonicalParseError, TargetNotFoundError, ImportError, GenerationError, RemoteFetchError, LockAcquisitionError, FileSystemError;
|
|
702
767
|
var init_errors = __esm({
|
|
703
768
|
"src/core/errors.ts"() {
|
|
704
769
|
AgentsMeshError = class extends Error {
|
|
@@ -733,6 +798,21 @@ var init_errors = __esm({
|
|
|
733
798
|
this.issues = issues;
|
|
734
799
|
}
|
|
735
800
|
};
|
|
801
|
+
CanonicalParseError = class extends AgentsMeshError {
|
|
802
|
+
path;
|
|
803
|
+
constructor(path, cause) {
|
|
804
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
805
|
+
super(
|
|
806
|
+
"AM_CONFIG_INVALID",
|
|
807
|
+
`Invalid canonical file ${path}: ${detail}. Fix the syntax and try again.`,
|
|
808
|
+
{
|
|
809
|
+
cause
|
|
810
|
+
}
|
|
811
|
+
);
|
|
812
|
+
this.name = "CanonicalParseError";
|
|
813
|
+
this.path = path;
|
|
814
|
+
}
|
|
815
|
+
};
|
|
736
816
|
TargetNotFoundError = class extends AgentsMeshError {
|
|
737
817
|
target;
|
|
738
818
|
constructor(target34, options) {
|
|
@@ -806,6 +886,11 @@ function normalizeLineEndings(content) {
|
|
|
806
886
|
function executableModeFor(path) {
|
|
807
887
|
return EXECUTABLE_SCRIPT_EXTENSIONS.has(extname(path).toLowerCase()) ? 493 : void 0;
|
|
808
888
|
}
|
|
889
|
+
function normalizeTextPayload(path, content) {
|
|
890
|
+
if (!shouldNormalizeLineEndings(path)) return content;
|
|
891
|
+
const withoutBom = content.startsWith(UTF8_BOM) ? content.slice(UTF8_BOM.length) : content;
|
|
892
|
+
return normalizeLineEndings(withoutBom);
|
|
893
|
+
}
|
|
809
894
|
var UTF8_BOM, TEXT_EXTENSIONS, TEXT_DOTFILES, EXECUTABLE_SCRIPT_EXTENSIONS;
|
|
810
895
|
var init_fs_text_encoding = __esm({
|
|
811
896
|
"src/utils/filesystem/fs-text-encoding.ts"() {
|
|
@@ -1306,10 +1391,8 @@ function stripManagedBlock(content, start, end) {
|
|
|
1306
1391
|
return content.replace(managedBlockPattern(start, end), "").trim();
|
|
1307
1392
|
}
|
|
1308
1393
|
function splitFrontmatterPrefix(content) {
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
if (close === -1) return { prefix: "", body: content.trim() };
|
|
1312
|
-
return { prefix: content.slice(0, close + 3), body: content.slice(close + 3).trim() };
|
|
1394
|
+
const split = splitFrontmatter(content);
|
|
1395
|
+
return split === null ? { prefix: "", body: content.trim() } : { prefix: split.prefix, body: split.body };
|
|
1313
1396
|
}
|
|
1314
1397
|
function insertAtBodyTop(content, block) {
|
|
1315
1398
|
const { prefix, body } = splitFrontmatterPrefix(content);
|
|
@@ -1410,6 +1493,7 @@ function extractEmbeddedRules(content) {
|
|
|
1410
1493
|
var ROOT_CONTRACT_START, ROOT_CONTRACT_END, LESSONS_CONTRACT_START, LESSONS_CONTRACT_END, EMBEDDED_RULES_START, EMBEDDED_RULES_END, EMBEDDED_RULE_END, EMBEDDED_RULE_START_PREFIX, EMBEDDED_RULE_START_SUFFIX;
|
|
1411
1494
|
var init_managed_blocks = __esm({
|
|
1412
1495
|
"src/targets/projection/managed-blocks.ts"() {
|
|
1496
|
+
init_markdown();
|
|
1413
1497
|
ROOT_CONTRACT_START = "<!-- agentsmesh:root-generation-contract:start -->";
|
|
1414
1498
|
ROOT_CONTRACT_END = "<!-- agentsmesh:root-generation-contract:end -->";
|
|
1415
1499
|
LESSONS_CONTRACT_START = "<!-- agentsmesh:lessons-contract:start -->";
|
|
@@ -1577,10 +1661,17 @@ async function serializeImportedCommandWithFallback(destinationPath, imported, b
|
|
|
1577
1661
|
})();
|
|
1578
1662
|
const description = imported.hasDescription ? imported.description ?? "" : typeof existingFrontmatter.description === "string" ? existingFrontmatter.description : "";
|
|
1579
1663
|
const allowedTools = imported.hasAllowedTools ? imported.allowedTools ?? [] : existingAllowedTools;
|
|
1664
|
+
const {
|
|
1665
|
+
description: _d,
|
|
1666
|
+
"allowed-tools": _a,
|
|
1667
|
+
allowedTools: _c,
|
|
1668
|
+
...preserved
|
|
1669
|
+
} = existingFrontmatter;
|
|
1580
1670
|
return serializeFrontmatter(
|
|
1581
1671
|
{
|
|
1582
1672
|
description,
|
|
1583
|
-
"allowed-tools": allowedTools
|
|
1673
|
+
"allowed-tools": allowedTools,
|
|
1674
|
+
...preserved
|
|
1584
1675
|
},
|
|
1585
1676
|
body.trim() || ""
|
|
1586
1677
|
);
|
|
@@ -2478,6 +2569,26 @@ var init_link_rebaser_resolution = __esm({
|
|
|
2478
2569
|
}
|
|
2479
2570
|
});
|
|
2480
2571
|
|
|
2572
|
+
// src/core/reference/link-uri-encoding.ts
|
|
2573
|
+
function decodeLinkPath(token, role) {
|
|
2574
|
+
if (role !== "markdown-link-dest" || !token.includes("%")) return token;
|
|
2575
|
+
try {
|
|
2576
|
+
return decodeURIComponent(token);
|
|
2577
|
+
} catch {
|
|
2578
|
+
return token;
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
function encodeLinkPath(path, enabled) {
|
|
2582
|
+
if (!enabled) return path;
|
|
2583
|
+
return path.split("/").map(
|
|
2584
|
+
(segment) => segment === "" || segment === "." || segment === ".." ? segment : encodeURIComponent(segment)
|
|
2585
|
+
).join("/");
|
|
2586
|
+
}
|
|
2587
|
+
var init_link_uri_encoding = __esm({
|
|
2588
|
+
"src/core/reference/link-uri-encoding.ts"() {
|
|
2589
|
+
}
|
|
2590
|
+
});
|
|
2591
|
+
|
|
2481
2592
|
// src/core/reference/link-token-guards.ts
|
|
2482
2593
|
function isTildeHomeRelativePathToken(fullContent, matchOffset, matchText) {
|
|
2483
2594
|
if (matchOffset >= 2 && fullContent[matchOffset - 2] === "~" && fullContent[matchOffset - 1] === "/") {
|
|
@@ -2607,10 +2718,11 @@ function rewriteFileLinks(input) {
|
|
|
2607
2718
|
const { candidate: punctStripped, suffix } = stripTrailingPunctuation(match);
|
|
2608
2719
|
if (!punctStripped) return match;
|
|
2609
2720
|
const lineNumMatch = LINE_NUMBER_SUFFIX.exec(punctStripped);
|
|
2610
|
-
const
|
|
2721
|
+
const rawCandidate = lineNumMatch ? punctStripped.slice(0, lineNumMatch.index) : punctStripped;
|
|
2611
2722
|
const lineNumSuffix = lineNumMatch ? lineNumMatch[0] : "";
|
|
2612
|
-
if (!
|
|
2613
|
-
const tokenContext = getTokenContext(fullContent, offset, offset +
|
|
2723
|
+
if (!rawCandidate) return match;
|
|
2724
|
+
const tokenContext = getTokenContext(fullContent, offset, offset + rawCandidate.length);
|
|
2725
|
+
const candidate = decodeLinkPath(rawCandidate, tokenContext.role);
|
|
2614
2726
|
if (tokenContext.role !== "markdown-link-dest" && WINDOWS_ABSOLUTE_PATH.test(candidate)) {
|
|
2615
2727
|
return match;
|
|
2616
2728
|
}
|
|
@@ -2680,7 +2792,7 @@ function rewriteFileLinks(input) {
|
|
|
2680
2792
|
const targetTop = targetFromRoot.split("/").filter(Boolean)[0] ?? "";
|
|
2681
2793
|
const tokenIsCanonicalMesh = normalizeSeparators(candidate).startsWith(".agentsmesh/");
|
|
2682
2794
|
const preferRelativeProseInSameSurface = !tokenIsCanonicalMesh && !targetIsDirectory && destTop.length > 0 && destTop === targetTop && destTop.startsWith(".") && destTop !== ".agentsmesh";
|
|
2683
|
-
const forceRelative = preferRelativeProseInSameSurface || tokenContext.role === "markdown-link-dest" || isMarkdownLinkDestinationToken(fullContent, offset,
|
|
2795
|
+
const forceRelative = preferRelativeProseInSameSurface || tokenContext.role === "markdown-link-dest" || isMarkdownLinkDestinationToken(fullContent, offset, rawCandidate);
|
|
2684
2796
|
const rewritten = formatLinkPathForDestination(
|
|
2685
2797
|
input.projectRoot,
|
|
2686
2798
|
input.destinationFile,
|
|
@@ -2697,7 +2809,7 @@ function rewriteFileLinks(input) {
|
|
|
2697
2809
|
}
|
|
2698
2810
|
);
|
|
2699
2811
|
if (!rewritten) return match;
|
|
2700
|
-
return `${rewritten}${lineNumSuffix}${suffix}`;
|
|
2812
|
+
return `${encodeLinkPath(rewritten, candidate !== rawCandidate)}${lineNumSuffix}${suffix}`;
|
|
2701
2813
|
});
|
|
2702
2814
|
return { content, missing: [...missing] };
|
|
2703
2815
|
}
|
|
@@ -2707,6 +2819,7 @@ var init_link_rebaser = __esm({
|
|
|
2707
2819
|
init_link_rebaser_helpers();
|
|
2708
2820
|
init_link_rebaser_output();
|
|
2709
2821
|
init_link_rebaser_resolution();
|
|
2822
|
+
init_link_uri_encoding();
|
|
2710
2823
|
init_link_token_guards();
|
|
2711
2824
|
init_link_token_context();
|
|
2712
2825
|
}
|
|
@@ -3590,7 +3703,7 @@ function unsupportedHookEventNames(hooks, supportedEvents) {
|
|
|
3590
3703
|
if (!hooks) return [];
|
|
3591
3704
|
const supported = new Set(supportedEvents);
|
|
3592
3705
|
return Object.keys(hooks).filter(
|
|
3593
|
-
(event) => !supported.has(event) && !
|
|
3706
|
+
(event) => !supported.has(event) && !isBestEffortHookEvent(event, hooks[event])
|
|
3594
3707
|
);
|
|
3595
3708
|
}
|
|
3596
3709
|
function createUnsupportedHookWarning(event, target34, supportedEvents, options) {
|
|
@@ -7127,6 +7240,21 @@ var init_embedded_rules = __esm({
|
|
|
7127
7240
|
}
|
|
7128
7241
|
});
|
|
7129
7242
|
|
|
7243
|
+
// src/canonical/features/syntax-error.ts
|
|
7244
|
+
function failSyntax(filePath2, cause, onParseError) {
|
|
7245
|
+
const error = new CanonicalParseError(filePath2, cause);
|
|
7246
|
+
if (onParseError !== void 0) {
|
|
7247
|
+
onParseError(error, filePath2);
|
|
7248
|
+
return null;
|
|
7249
|
+
}
|
|
7250
|
+
throw error;
|
|
7251
|
+
}
|
|
7252
|
+
var init_syntax_error = __esm({
|
|
7253
|
+
"src/canonical/features/syntax-error.ts"() {
|
|
7254
|
+
init_errors();
|
|
7255
|
+
}
|
|
7256
|
+
});
|
|
7257
|
+
|
|
7130
7258
|
// src/canonical/features/mcp.ts
|
|
7131
7259
|
function parseStringMap(raw) {
|
|
7132
7260
|
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return {};
|
|
@@ -7209,14 +7337,14 @@ function stripJsonComments(text) {
|
|
|
7209
7337
|
}
|
|
7210
7338
|
return result2;
|
|
7211
7339
|
}
|
|
7212
|
-
async function parseMcp(mcpPath) {
|
|
7340
|
+
async function parseMcp(mcpPath, onParseError) {
|
|
7213
7341
|
const content = await readFileSafe(mcpPath);
|
|
7214
7342
|
if (!content) return null;
|
|
7215
7343
|
let parsed;
|
|
7216
7344
|
try {
|
|
7217
7345
|
parsed = JSON.parse(stripJsonComments(content));
|
|
7218
|
-
} catch {
|
|
7219
|
-
return
|
|
7346
|
+
} catch (err) {
|
|
7347
|
+
return failSyntax(mcpPath, err, onParseError);
|
|
7220
7348
|
}
|
|
7221
7349
|
if (!parsed || typeof parsed !== "object") return null;
|
|
7222
7350
|
const mcpServersRaw = parsed.mcpServers;
|
|
@@ -7231,6 +7359,7 @@ async function parseMcp(mcpPath) {
|
|
|
7231
7359
|
}
|
|
7232
7360
|
var init_mcp = __esm({
|
|
7233
7361
|
"src/canonical/features/mcp.ts"() {
|
|
7362
|
+
init_syntax_error();
|
|
7234
7363
|
init_fs();
|
|
7235
7364
|
}
|
|
7236
7365
|
});
|
|
@@ -9010,10 +9139,10 @@ var init_claude_code2 = __esm({
|
|
|
9010
9139
|
skillDir: ".claude/skills",
|
|
9011
9140
|
managedOutputs: {
|
|
9012
9141
|
dirs: [".claude/agents", ".claude/commands", ".claude/rules", ".claude/skills"],
|
|
9013
|
-
|
|
9014
|
-
//
|
|
9142
|
+
files: [CLAUDE_ROOT, ".claudeignore"],
|
|
9143
|
+
// CLAUDE_NESTED_ROOT is the pre-migration project location: evicted once a run
|
|
9015
9144
|
// writes the root `CLAUDE.md`, so Claude Code never concatenates both into context.
|
|
9016
|
-
|
|
9145
|
+
supersededFiles: [CLAUDE_NESTED_ROOT],
|
|
9017
9146
|
// `.mcp.json` is the shared project MCP file teams hand-commit and
|
|
9018
9147
|
// deepagents-cli writes too; agentsmesh owns only `mcpServers` in it.
|
|
9019
9148
|
// `.claude/settings.json` is co-owned through the `SETTINGS_JSON_PATHS`
|
|
@@ -10244,9 +10373,8 @@ var init_cline2 = __esm({
|
|
|
10244
10373
|
};
|
|
10245
10374
|
}
|
|
10246
10375
|
});
|
|
10247
|
-
|
|
10248
|
-
|
|
10249
|
-
}
|
|
10376
|
+
|
|
10377
|
+
// src/targets/codex-cli/codex-rule-paths.ts
|
|
10250
10378
|
function directoryFromGlob(glob) {
|
|
10251
10379
|
let normalized = glob.trim();
|
|
10252
10380
|
if (normalized.startsWith("./")) normalized = normalized.slice(2);
|
|
@@ -10267,12 +10395,12 @@ function codexRuleDirectory(rule) {
|
|
|
10267
10395
|
const dir = directoryFromGlob(glob);
|
|
10268
10396
|
if (dir) return dir;
|
|
10269
10397
|
}
|
|
10270
|
-
return
|
|
10398
|
+
return null;
|
|
10271
10399
|
}
|
|
10272
10400
|
function codexNestedAgentsPath(rule) {
|
|
10273
10401
|
const dir = codexRuleDirectory(rule);
|
|
10274
10402
|
const filename = rule.codexInstructionVariant === "override" ? "AGENTS.override.md" : "AGENTS.md";
|
|
10275
|
-
return `${dir}/${filename}`;
|
|
10403
|
+
return dir === null ? filename : `${dir}/${filename}`;
|
|
10276
10404
|
}
|
|
10277
10405
|
var GLOB_METACHAR;
|
|
10278
10406
|
var init_codex_rule_paths = __esm({
|
|
@@ -10280,10 +10408,9 @@ var init_codex_rule_paths = __esm({
|
|
|
10280
10408
|
GLOB_METACHAR = /[*?[\]]/;
|
|
10281
10409
|
}
|
|
10282
10410
|
});
|
|
10283
|
-
|
|
10284
|
-
// src/targets/codebuff/rule-paths.ts
|
|
10285
10411
|
function codebuffNestedKnowledgePath(rule) {
|
|
10286
|
-
|
|
10412
|
+
const dir = codexRuleDirectory(rule) ?? basename(rule.source, ".md");
|
|
10413
|
+
return `${dir}/AGENTS.md`;
|
|
10287
10414
|
}
|
|
10288
10415
|
var init_rule_paths = __esm({
|
|
10289
10416
|
"src/targets/codebuff/rule-paths.ts"() {
|
|
@@ -10894,6 +11021,9 @@ function eligibleAdvisoryRules(canonical) {
|
|
|
10894
11021
|
return rule.targets.length === 0 || rule.targets.includes("codex-cli");
|
|
10895
11022
|
});
|
|
10896
11023
|
}
|
|
11024
|
+
function isRootEmbedded(rule) {
|
|
11025
|
+
return codexNestedAgentsPath(rule) === AGENTS_MD;
|
|
11026
|
+
}
|
|
10897
11027
|
function groupByNestedPath2(rules) {
|
|
10898
11028
|
const groups = /* @__PURE__ */ new Map();
|
|
10899
11029
|
for (const rule of rules) {
|
|
@@ -10906,9 +11036,11 @@ function groupByNestedPath2(rules) {
|
|
|
10906
11036
|
}
|
|
10907
11037
|
function generateRules9(canonical) {
|
|
10908
11038
|
const root = canonical.rules.find((r) => r.root);
|
|
11039
|
+
const advisory = eligibleAdvisoryRules(canonical);
|
|
10909
11040
|
const outputs = [];
|
|
10910
11041
|
if (root) {
|
|
10911
|
-
|
|
11042
|
+
const content = appendEmbeddedRulesBlock(root.body.trim(), advisory.filter(isRootEmbedded));
|
|
11043
|
+
outputs.push({ path: AGENTS_MD, content });
|
|
10912
11044
|
}
|
|
10913
11045
|
for (const rule of canonical.rules) {
|
|
10914
11046
|
if (rule.root) continue;
|
|
@@ -10920,7 +11052,8 @@ function generateRules9(canonical) {
|
|
|
10920
11052
|
content: toSafeCodexRulesContent(rule.body)
|
|
10921
11053
|
});
|
|
10922
11054
|
}
|
|
10923
|
-
|
|
11055
|
+
const nested = advisory.filter((rule) => !isRootEmbedded(rule));
|
|
11056
|
+
for (const [path, rules] of groupByNestedPath2(nested)) {
|
|
10924
11057
|
const content = rules.map((rule) => rule.body.trim()).filter((body) => body.length > 0).join("\n\n");
|
|
10925
11058
|
outputs.push({ path, content });
|
|
10926
11059
|
}
|
|
@@ -11745,6 +11878,23 @@ var init_linter9 = __esm({
|
|
|
11745
11878
|
});
|
|
11746
11879
|
|
|
11747
11880
|
// src/targets/codex-cli/lint.ts
|
|
11881
|
+
function lintAgents3(canonical) {
|
|
11882
|
+
const diagnostics = [];
|
|
11883
|
+
for (const agent of canonical.agents) {
|
|
11884
|
+
const dropped = CODEX_DROPPED_AGENT_FIELDS.filter(
|
|
11885
|
+
(field) => hasAgentValue(agent, field)
|
|
11886
|
+
).sort();
|
|
11887
|
+
if (dropped.length === 0) continue;
|
|
11888
|
+
diagnostics.push(
|
|
11889
|
+
createWarning(
|
|
11890
|
+
agent.source,
|
|
11891
|
+
"codex-cli",
|
|
11892
|
+
`Codex agent TOML supports name, description, developer_instructions, model, sandbox_mode and mcp_servers; canonical ${dropped.join(", ")} are not projected to ${CODEX_AGENTS_DIR}/${agent.name}.toml.`
|
|
11893
|
+
)
|
|
11894
|
+
);
|
|
11895
|
+
}
|
|
11896
|
+
return diagnostics;
|
|
11897
|
+
}
|
|
11748
11898
|
function lintMcp4(canonical) {
|
|
11749
11899
|
if (!canonical.mcp || Object.keys(canonical.mcp.mcpServers).length === 0) return [];
|
|
11750
11900
|
const diagnostics = [];
|
|
@@ -11776,11 +11926,21 @@ function lintHooks7(canonical) {
|
|
|
11776
11926
|
(event) => createUnsupportedHookWarning(event, "codex-cli", CODEX_SUPPORTED_HOOK_EVENTS)
|
|
11777
11927
|
);
|
|
11778
11928
|
}
|
|
11929
|
+
var CODEX_DROPPED_AGENT_FIELDS;
|
|
11779
11930
|
var init_lint8 = __esm({
|
|
11780
11931
|
"src/targets/codex-cli/lint.ts"() {
|
|
11781
11932
|
init_helpers();
|
|
11782
11933
|
init_mcp_servers();
|
|
11934
|
+
init_agents_format();
|
|
11783
11935
|
init_constants10();
|
|
11936
|
+
CODEX_DROPPED_AGENT_FIELDS = [
|
|
11937
|
+
"tools",
|
|
11938
|
+
"disallowedTools",
|
|
11939
|
+
"maxTurns",
|
|
11940
|
+
"hooks",
|
|
11941
|
+
"skills",
|
|
11942
|
+
"memory"
|
|
11943
|
+
];
|
|
11784
11944
|
}
|
|
11785
11945
|
});
|
|
11786
11946
|
|
|
@@ -11855,6 +12015,7 @@ var init_codex_cli2 = __esm({
|
|
|
11855
12015
|
generateMcp: generateMcp6,
|
|
11856
12016
|
generateHooks: generateHooks6,
|
|
11857
12017
|
generatePermissions: generatePermissions7,
|
|
12018
|
+
lint: lintAgents3,
|
|
11858
12019
|
importFrom: importFromCodex
|
|
11859
12020
|
};
|
|
11860
12021
|
project6 = {
|
|
@@ -11902,7 +12063,7 @@ var init_codex_cli2 = __esm({
|
|
|
11902
12063
|
},
|
|
11903
12064
|
rewriteGeneratedPath(path) {
|
|
11904
12065
|
if (path === AGENTS_MD) return CODEX_GLOBAL_AGENTS_MD;
|
|
11905
|
-
if (
|
|
12066
|
+
if (/(^|\/)AGENTS(\.override)?\.md$/.test(path)) return null;
|
|
11906
12067
|
if (path.startsWith(`${CODEX_INSTRUCTIONS_DIR}/`)) return null;
|
|
11907
12068
|
return path;
|
|
11908
12069
|
},
|
|
@@ -12558,7 +12719,7 @@ function hasValue(agent, field) {
|
|
|
12558
12719
|
if (value && typeof value === "object") return Object.keys(value).length > 0;
|
|
12559
12720
|
return false;
|
|
12560
12721
|
}
|
|
12561
|
-
function
|
|
12722
|
+
function lintAgents4(canonical) {
|
|
12562
12723
|
const diagnostics = [];
|
|
12563
12724
|
for (const agent of canonical.agents) {
|
|
12564
12725
|
const dropped = DROPPED_FIELDS.filter((field) => hasValue(agent, field)).sort();
|
|
@@ -12726,7 +12887,7 @@ var init_continue2 = __esm({
|
|
|
12726
12887
|
generateHooks: generateHooks7,
|
|
12727
12888
|
generateIgnore: generateIgnore8,
|
|
12728
12889
|
// Feature-independent lint hook: agent warnings must not hang off `rules`.
|
|
12729
|
-
lint:
|
|
12890
|
+
lint: lintAgents4,
|
|
12730
12891
|
importFrom: importFromContinue
|
|
12731
12892
|
};
|
|
12732
12893
|
descriptor10 = {
|
|
@@ -12983,7 +13144,7 @@ var init_hook_format = __esm({
|
|
|
12983
13144
|
init_hook_entry();
|
|
12984
13145
|
}
|
|
12985
13146
|
});
|
|
12986
|
-
function
|
|
13147
|
+
function ruleSlug2(source) {
|
|
12987
13148
|
const name = basename(source, ".md");
|
|
12988
13149
|
return name === "_root" ? "root" : name;
|
|
12989
13150
|
}
|
|
@@ -13017,7 +13178,7 @@ function generateRules11(canonical) {
|
|
|
13017
13178
|
if (rule.root) continue;
|
|
13018
13179
|
if (rule.targets.length > 0 && !rule.targets.includes("copilot")) continue;
|
|
13019
13180
|
if (rule.globs.length === 0) continue;
|
|
13020
|
-
const slug =
|
|
13181
|
+
const slug = ruleSlug2(rule.source);
|
|
13021
13182
|
const frontmatter = {
|
|
13022
13183
|
description: rule.description || void 0,
|
|
13023
13184
|
applyTo: rule.globs.length === 1 ? rule.globs[0] : rule.globs
|
|
@@ -14506,13 +14667,18 @@ var init_rules2 = __esm({
|
|
|
14506
14667
|
|
|
14507
14668
|
// src/targets/cursor/generator/commands.ts
|
|
14508
14669
|
function generateCommands13(canonical) {
|
|
14509
|
-
return canonical.commands.map((cmd) =>
|
|
14510
|
-
|
|
14511
|
-
|
|
14512
|
-
|
|
14670
|
+
return canonical.commands.map((cmd) => {
|
|
14671
|
+
const frontmatter = {};
|
|
14672
|
+
if (cmd.description) frontmatter.description = cmd.description;
|
|
14673
|
+
return {
|
|
14674
|
+
path: `${CURSOR_COMMANDS_DIR}/${cmd.name}.md`,
|
|
14675
|
+
content: serializeFrontmatter(frontmatter, cmd.body.trim() || "")
|
|
14676
|
+
};
|
|
14677
|
+
});
|
|
14513
14678
|
}
|
|
14514
14679
|
var init_commands = __esm({
|
|
14515
14680
|
"src/targets/cursor/generator/commands.ts"() {
|
|
14681
|
+
init_markdown();
|
|
14516
14682
|
init_constants13();
|
|
14517
14683
|
}
|
|
14518
14684
|
});
|
|
@@ -14607,7 +14773,7 @@ var init_permissions3 = __esm({
|
|
|
14607
14773
|
// src/targets/cursor/hook-format.ts
|
|
14608
14774
|
function unmappedCursorHookEvents(hooks) {
|
|
14609
14775
|
return Object.keys(hooks).filter(
|
|
14610
|
-
(event) => Array.isArray(hooks[event]) && hooks[event].length > 0 && !(event in CANONICAL_TO_CURSOR) && !
|
|
14776
|
+
(event) => Array.isArray(hooks[event]) && hooks[event].length > 0 && !(event in CANONICAL_TO_CURSOR) && !isBestEffortHookEvent(event, hooks[event])
|
|
14611
14777
|
);
|
|
14612
14778
|
}
|
|
14613
14779
|
function toCursorHooks(hooks) {
|
|
@@ -15045,14 +15211,11 @@ async function hasGlobalCursorArtifacts(projectRoot) {
|
|
|
15045
15211
|
join(projectRoot, CURSOR_GLOBAL_USER_RULES),
|
|
15046
15212
|
join(projectRoot, CURSOR_MCP),
|
|
15047
15213
|
join(projectRoot, CURSOR_HOOKS),
|
|
15048
|
-
join(projectRoot, CURSOR_IGNORE)
|
|
15049
|
-
join(projectRoot, CURSOR_SKILLS_DIR),
|
|
15050
|
-
join(projectRoot, CURSOR_AGENTS_DIR),
|
|
15051
|
-
join(projectRoot, CURSOR_COMMANDS_DIR)
|
|
15214
|
+
join(projectRoot, CURSOR_IGNORE)
|
|
15052
15215
|
];
|
|
15053
15216
|
for (const p of candidates) {
|
|
15054
|
-
const
|
|
15055
|
-
if (
|
|
15217
|
+
const content = await readFileSafe(p);
|
|
15218
|
+
if (content !== null && content.trim() !== "") return true;
|
|
15056
15219
|
}
|
|
15057
15220
|
const skillFiles = await readDirRecursiveNoSymlinks(join(projectRoot, CURSOR_SKILLS_DIR));
|
|
15058
15221
|
if (skillFiles.some((f) => f.endsWith(".md"))) return true;
|
|
@@ -15339,11 +15502,11 @@ function lintHooks10(canonical) {
|
|
|
15339
15502
|
];
|
|
15340
15503
|
}
|
|
15341
15504
|
function lintCommands6(canonical) {
|
|
15342
|
-
return canonical.commands.filter((command) => command.
|
|
15505
|
+
return canonical.commands.filter((command) => command.allowedTools.length > 0).map(
|
|
15343
15506
|
(command) => createWarning(
|
|
15344
15507
|
command.source,
|
|
15345
15508
|
"cursor",
|
|
15346
|
-
"Cursor command files
|
|
15509
|
+
"Cursor command files project only description frontmatter; allowed-tools metadata is not projected."
|
|
15347
15510
|
)
|
|
15348
15511
|
);
|
|
15349
15512
|
}
|
|
@@ -15676,7 +15839,7 @@ var init_mcp_merge4 = __esm({
|
|
|
15676
15839
|
// src/targets/deepagents-cli/hooks-format.ts
|
|
15677
15840
|
function unmappedDeepagentsHookEvents(hooks) {
|
|
15678
15841
|
return Object.keys(hooks).filter(
|
|
15679
|
-
(event) => Array.isArray(hooks[event]) && hooks[event].length > 0 && !(event in CANONICAL_TO_DEEPAGENTS) && !
|
|
15842
|
+
(event) => Array.isArray(hooks[event]) && hooks[event].length > 0 && !(event in CANONICAL_TO_DEEPAGENTS) && !isBestEffortHookEvent(event, hooks[event])
|
|
15680
15843
|
);
|
|
15681
15844
|
}
|
|
15682
15845
|
function toDeepagentsHooks(hooks) {
|
|
@@ -16244,7 +16407,8 @@ var init_deepagents_cli2 = __esm({
|
|
|
16244
16407
|
}
|
|
16245
16408
|
},
|
|
16246
16409
|
buildImportPaths: buildDeepagentsCliImportPaths,
|
|
16247
|
-
|
|
16410
|
+
// `.mcp.json` is co-owned with claude-code (agentsmesh writes it), so it must not enroll this target.
|
|
16411
|
+
detectionPaths: [DEEPAGENTS_CLI_ROOT_FILE]
|
|
16248
16412
|
};
|
|
16249
16413
|
}
|
|
16250
16414
|
});
|
|
@@ -20222,7 +20386,8 @@ var init_layout8 = __esm({
|
|
|
20222
20386
|
skillDir: KIMI_CODE_SKILLS_DIR,
|
|
20223
20387
|
managedOutputs: {
|
|
20224
20388
|
dirs: [KIMI_CODE_AGENTS_DIR, KIMI_CODE_SKILLS_DIR],
|
|
20225
|
-
files: [KIMI_CODE_ROOT_FILE
|
|
20389
|
+
files: [KIMI_CODE_ROOT_FILE],
|
|
20390
|
+
supersededFiles: [KIMI_CODE_NESTED_ROOT_FILE],
|
|
20226
20391
|
// Kimi Code's own MCP config, in the same directory as the credential-
|
|
20227
20392
|
// bearing config.toml this layout already refuses to delete.
|
|
20228
20393
|
coOwnedFiles: [KIMI_CODE_MCP_FILE]
|
|
@@ -20870,7 +21035,7 @@ function lintMcp9(canonical) {
|
|
|
20870
21035
|
}
|
|
20871
21036
|
return diagnostics;
|
|
20872
21037
|
}
|
|
20873
|
-
function
|
|
21038
|
+
function lintAgents5(canonical) {
|
|
20874
21039
|
return canonical.agents.flatMap((agent) => {
|
|
20875
21040
|
const dropped = DROPPED_AGENT_FIELDS.filter(([, has]) => has(agent)).map(([field]) => field);
|
|
20876
21041
|
if (dropped.length === 0) return [];
|
|
@@ -20961,7 +21126,7 @@ var init_kimi_code2 = __esm({
|
|
|
20961
21126
|
generateHooks: generateHooks14,
|
|
20962
21127
|
generatePermissions: generatePermissions16,
|
|
20963
21128
|
importFrom: importFromKimiCode,
|
|
20964
|
-
lint:
|
|
21129
|
+
lint: lintAgents5
|
|
20965
21130
|
};
|
|
20966
21131
|
capabilities9 = {
|
|
20967
21132
|
rules: "native",
|
|
@@ -23259,7 +23424,7 @@ function lintAgentFields(agent) {
|
|
|
23259
23424
|
)
|
|
23260
23425
|
];
|
|
23261
23426
|
}
|
|
23262
|
-
function
|
|
23427
|
+
function lintAgents6(canonical) {
|
|
23263
23428
|
const diagnostics = [];
|
|
23264
23429
|
for (const agent of canonical.agents) {
|
|
23265
23430
|
diagnostics.push(...lintAgentFields(agent));
|
|
@@ -23321,7 +23486,7 @@ var init_openhands2 = __esm({
|
|
|
23321
23486
|
generatePermissions: generatePermissions19,
|
|
23322
23487
|
importFrom: importFromOpenhands,
|
|
23323
23488
|
// Ungated by feature, so agent-only feature sets still get the warning.
|
|
23324
|
-
lint:
|
|
23489
|
+
lint: lintAgents6
|
|
23325
23490
|
};
|
|
23326
23491
|
descriptor24 = {
|
|
23327
23492
|
id: OPENHANDS_TARGET,
|
|
@@ -27136,7 +27301,7 @@ var init_constants34 = __esm({
|
|
|
27136
27301
|
WINDSURF_GLOBAL_AGENTS_SKILLS_DIR = ".agents/skills";
|
|
27137
27302
|
}
|
|
27138
27303
|
});
|
|
27139
|
-
function
|
|
27304
|
+
function ruleSlug3(source) {
|
|
27140
27305
|
const name = basename(source, ".md");
|
|
27141
27306
|
return name === "_root" ? "root" : name;
|
|
27142
27307
|
}
|
|
@@ -27157,7 +27322,7 @@ function generateRules32(canonical) {
|
|
|
27157
27322
|
for (const rule of canonical.rules) {
|
|
27158
27323
|
if (rule.root) continue;
|
|
27159
27324
|
if (rule.targets.length > 0 && !rule.targets.includes("windsurf")) continue;
|
|
27160
|
-
const slug =
|
|
27325
|
+
const slug = ruleSlug3(rule.source);
|
|
27161
27326
|
const normalizedTrigger = rule.trigger || (rule.globs.length > 0 ? "glob" : void 0);
|
|
27162
27327
|
const frontmatter = {
|
|
27163
27328
|
description: rule.description || void 0,
|
|
@@ -27261,19 +27426,34 @@ var init_mcp4 = __esm({
|
|
|
27261
27426
|
}
|
|
27262
27427
|
});
|
|
27263
27428
|
|
|
27264
|
-
// src/targets/windsurf/
|
|
27429
|
+
// src/targets/windsurf/hook-events.ts
|
|
27265
27430
|
function windsurfEventName(event) {
|
|
27266
|
-
const explicit = {
|
|
27267
|
-
PreToolUse: "pre_tool_use",
|
|
27268
|
-
PostToolUse: "post_tool_use",
|
|
27269
|
-
Notification: "notification",
|
|
27270
|
-
UserPromptSubmit: "user_prompt_submit",
|
|
27271
|
-
SubagentStart: "subagent_start",
|
|
27272
|
-
SubagentStop: "subagent_stop"
|
|
27273
|
-
};
|
|
27274
|
-
if (explicit[event]) return explicit[event];
|
|
27275
27431
|
return event.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[-\s]+/g, "_").toLowerCase();
|
|
27276
27432
|
}
|
|
27433
|
+
function canonicalHookEventName(event) {
|
|
27434
|
+
if (KNOWN_CANONICAL_HOOK_EVENTS.includes(event)) return event;
|
|
27435
|
+
return WINDSURF_TO_CANONICAL.get(event) ?? null;
|
|
27436
|
+
}
|
|
27437
|
+
var KNOWN_CANONICAL_HOOK_EVENTS, WINDSURF_TO_CANONICAL;
|
|
27438
|
+
var init_hook_events = __esm({
|
|
27439
|
+
"src/targets/windsurf/hook-events.ts"() {
|
|
27440
|
+
init_hook_types();
|
|
27441
|
+
KNOWN_CANONICAL_HOOK_EVENTS = [
|
|
27442
|
+
"PreToolUse",
|
|
27443
|
+
"PostToolUse",
|
|
27444
|
+
"Notification",
|
|
27445
|
+
"UserPromptSubmit",
|
|
27446
|
+
"SubagentStart",
|
|
27447
|
+
"SubagentStop",
|
|
27448
|
+
...BEST_EFFORT_HOOK_EVENTS
|
|
27449
|
+
];
|
|
27450
|
+
WINDSURF_TO_CANONICAL = new Map(
|
|
27451
|
+
KNOWN_CANONICAL_HOOK_EVENTS.map((event) => [windsurfEventName(event), event])
|
|
27452
|
+
);
|
|
27453
|
+
}
|
|
27454
|
+
});
|
|
27455
|
+
|
|
27456
|
+
// src/targets/windsurf/generator/hooks.ts
|
|
27277
27457
|
function toWindsurfHooks(hooks) {
|
|
27278
27458
|
const result2 = {};
|
|
27279
27459
|
for (const [event, entries] of Object.entries(hooks)) {
|
|
@@ -27301,6 +27481,7 @@ var init_hooks4 = __esm({
|
|
|
27301
27481
|
"src/targets/windsurf/generator/hooks.ts"() {
|
|
27302
27482
|
init_hook_command();
|
|
27303
27483
|
init_constants34();
|
|
27484
|
+
init_hook_events();
|
|
27304
27485
|
}
|
|
27305
27486
|
});
|
|
27306
27487
|
|
|
@@ -27432,6 +27613,57 @@ var init_skills_adapter5 = __esm({
|
|
|
27432
27613
|
init_constants34();
|
|
27433
27614
|
}
|
|
27434
27615
|
});
|
|
27616
|
+
function toHookEntry(raw) {
|
|
27617
|
+
if (!raw || typeof raw !== "object") return null;
|
|
27618
|
+
const obj = raw;
|
|
27619
|
+
const matcher = obj.matcher;
|
|
27620
|
+
if (typeof matcher !== "string") return null;
|
|
27621
|
+
const command = getHookText(obj);
|
|
27622
|
+
if (!command) return null;
|
|
27623
|
+
const type = typeof obj.type === "string" && VALID_TYPES.includes(obj.type) ? obj.type : void 0;
|
|
27624
|
+
const timeout = typeof obj.timeout === "number" && Number.isFinite(obj.timeout) ? obj.timeout : void 0;
|
|
27625
|
+
const prompt = getHookPrompt(obj) || void 0;
|
|
27626
|
+
return {
|
|
27627
|
+
matcher,
|
|
27628
|
+
command,
|
|
27629
|
+
...timeout !== void 0 && { timeout },
|
|
27630
|
+
...type && { type },
|
|
27631
|
+
...prompt && { prompt }
|
|
27632
|
+
};
|
|
27633
|
+
}
|
|
27634
|
+
async function parseHooks(hooksPath, onParseError) {
|
|
27635
|
+
const content = await readFileSafe(hooksPath);
|
|
27636
|
+
if (content === null) return null;
|
|
27637
|
+
if (!content.trim()) return {};
|
|
27638
|
+
let parsed;
|
|
27639
|
+
try {
|
|
27640
|
+
parsed = parse(content);
|
|
27641
|
+
} catch (err) {
|
|
27642
|
+
return failSyntax(hooksPath, err, onParseError);
|
|
27643
|
+
}
|
|
27644
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
27645
|
+
const result2 = {};
|
|
27646
|
+
const obj = parsed;
|
|
27647
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
27648
|
+
if (!Array.isArray(val)) continue;
|
|
27649
|
+
const entries = [];
|
|
27650
|
+
for (const item of val) {
|
|
27651
|
+
const entry = toHookEntry(item);
|
|
27652
|
+
if (entry) entries.push(entry);
|
|
27653
|
+
}
|
|
27654
|
+
if (entries.length > 0) result2[key] = entries;
|
|
27655
|
+
}
|
|
27656
|
+
return result2;
|
|
27657
|
+
}
|
|
27658
|
+
var VALID_TYPES;
|
|
27659
|
+
var init_hooks5 = __esm({
|
|
27660
|
+
"src/canonical/features/hooks.ts"() {
|
|
27661
|
+
init_syntax_error();
|
|
27662
|
+
init_fs();
|
|
27663
|
+
init_hook_command();
|
|
27664
|
+
VALID_TYPES = ["command", "prompt"];
|
|
27665
|
+
}
|
|
27666
|
+
});
|
|
27435
27667
|
async function importWindsurfHooks(projectRoot, results) {
|
|
27436
27668
|
const hooksPath = join(projectRoot, WINDSURF_HOOKS_FILE);
|
|
27437
27669
|
const hooksContent = await readFileSafe(hooksPath);
|
|
@@ -27439,9 +27671,10 @@ async function importWindsurfHooks(projectRoot, results) {
|
|
|
27439
27671
|
try {
|
|
27440
27672
|
const parsed = JSON.parse(hooksContent);
|
|
27441
27673
|
if (!parsed.hooks || typeof parsed.hooks !== "object" || Array.isArray(parsed.hooks)) return;
|
|
27442
|
-
const canonical = windsurfHooksToCanonical(parsed.hooks);
|
|
27443
|
-
if (Object.keys(canonical).length === 0) return;
|
|
27444
27674
|
const destPath = join(projectRoot, WINDSURF_CANONICAL_HOOKS);
|
|
27675
|
+
const existing = await parseHooks(destPath) ?? {};
|
|
27676
|
+
const canonical = windsurfHooksToCanonical(parsed.hooks, existing);
|
|
27677
|
+
if (Object.keys(canonical).length === 0) return;
|
|
27445
27678
|
await mkdirp(dirname(destPath));
|
|
27446
27679
|
await writeFileAtomic(destPath, stringify(canonical));
|
|
27447
27680
|
results.push({
|
|
@@ -27453,54 +27686,63 @@ async function importWindsurfHooks(projectRoot, results) {
|
|
|
27453
27686
|
} catch {
|
|
27454
27687
|
}
|
|
27455
27688
|
}
|
|
27456
|
-
function
|
|
27457
|
-
const
|
|
27458
|
-
|
|
27459
|
-
|
|
27460
|
-
|
|
27461
|
-
|
|
27462
|
-
|
|
27463
|
-
|
|
27464
|
-
|
|
27465
|
-
|
|
27689
|
+
function preservedMatcher(existing, event, command) {
|
|
27690
|
+
const match = existing[event]?.find((entry) => entry.command === command);
|
|
27691
|
+
return match?.matcher ?? WILDCARD_MATCHER;
|
|
27692
|
+
}
|
|
27693
|
+
function legacyEntries(entry) {
|
|
27694
|
+
const matcher = typeof entry.matcher === "string" && entry.matcher.trim() ? entry.matcher : WILDCARD_MATCHER;
|
|
27695
|
+
const hooksList = Array.isArray(entry.hooks) ? entry.hooks : [];
|
|
27696
|
+
const out2 = [];
|
|
27697
|
+
for (const item of hooksList) {
|
|
27698
|
+
if (!item || typeof item !== "object") continue;
|
|
27699
|
+
const hook = item;
|
|
27700
|
+
const command = typeof hook.command === "string" ? hook.command : typeof hook.prompt === "string" ? hook.prompt : "";
|
|
27701
|
+
if (!command.trim()) continue;
|
|
27702
|
+
const canonical = {
|
|
27703
|
+
matcher,
|
|
27704
|
+
type: hook.type === "prompt" ? "prompt" : "command",
|
|
27705
|
+
command
|
|
27706
|
+
};
|
|
27707
|
+
if (typeof hook.timeout === "number") canonical.timeout = hook.timeout;
|
|
27708
|
+
out2.push(canonical);
|
|
27709
|
+
}
|
|
27710
|
+
return out2;
|
|
27466
27711
|
}
|
|
27467
|
-
function windsurfHooksToCanonical(hooks) {
|
|
27712
|
+
function windsurfHooksToCanonical(hooks, existing) {
|
|
27468
27713
|
const result2 = {};
|
|
27469
27714
|
for (const [event, entries] of Object.entries(hooks)) {
|
|
27470
27715
|
if (!Array.isArray(entries)) continue;
|
|
27471
27716
|
const mappedEvent = canonicalHookEventName(event);
|
|
27717
|
+
if (mappedEvent === null) continue;
|
|
27472
27718
|
const canonicalEntries = [];
|
|
27473
27719
|
for (const entry of entries) {
|
|
27474
27720
|
if (!entry || typeof entry !== "object") continue;
|
|
27475
27721
|
const e = entry;
|
|
27476
27722
|
if (typeof e.command === "string" && e.command.trim()) {
|
|
27477
27723
|
canonicalEntries.push({
|
|
27478
|
-
matcher:
|
|
27724
|
+
matcher: preservedMatcher(existing, mappedEvent, e.command),
|
|
27479
27725
|
type: "command",
|
|
27480
27726
|
command: e.command
|
|
27481
27727
|
});
|
|
27482
27728
|
continue;
|
|
27483
27729
|
}
|
|
27484
|
-
|
|
27485
|
-
const hooksList = Array.isArray(e.hooks) ? e.hooks : [];
|
|
27486
|
-
for (const item of hooksList) {
|
|
27487
|
-
if (!item || typeof item !== "object") continue;
|
|
27488
|
-
const hook = item;
|
|
27489
|
-
const command = typeof hook.command === "string" ? hook.command : typeof hook.prompt === "string" ? hook.prompt : "";
|
|
27490
|
-
if (!command.trim()) continue;
|
|
27491
|
-
const canonical = {
|
|
27492
|
-
matcher,
|
|
27493
|
-
type: hook.type === "prompt" ? "prompt" : "command",
|
|
27494
|
-
command
|
|
27495
|
-
};
|
|
27496
|
-
if (typeof hook.timeout === "number") canonical.timeout = hook.timeout;
|
|
27497
|
-
canonicalEntries.push(canonical);
|
|
27498
|
-
}
|
|
27730
|
+
canonicalEntries.push(...legacyEntries(e));
|
|
27499
27731
|
}
|
|
27500
27732
|
if (canonicalEntries.length > 0) result2[mappedEvent] = canonicalEntries;
|
|
27501
27733
|
}
|
|
27502
27734
|
return result2;
|
|
27503
27735
|
}
|
|
27736
|
+
var WILDCARD_MATCHER;
|
|
27737
|
+
var init_importer_hooks2 = __esm({
|
|
27738
|
+
"src/targets/windsurf/importer-hooks.ts"() {
|
|
27739
|
+
init_hooks5();
|
|
27740
|
+
init_fs();
|
|
27741
|
+
init_constants34();
|
|
27742
|
+
init_hook_events();
|
|
27743
|
+
WILDCARD_MATCHER = "*";
|
|
27744
|
+
}
|
|
27745
|
+
});
|
|
27504
27746
|
async function importWindsurfMcp(projectRoot, results) {
|
|
27505
27747
|
const sourceCandidates = [WINDSURF_MCP_EXAMPLE_FILE, WINDSURF_MCP_CONFIG_FILE];
|
|
27506
27748
|
for (const relPath of sourceCandidates) {
|
|
@@ -27524,8 +27766,8 @@ async function importWindsurfMcp(projectRoot, results) {
|
|
|
27524
27766
|
}
|
|
27525
27767
|
}
|
|
27526
27768
|
}
|
|
27527
|
-
var
|
|
27528
|
-
"src/targets/windsurf/importer-
|
|
27769
|
+
var init_importer_mcp = __esm({
|
|
27770
|
+
"src/targets/windsurf/importer-mcp.ts"() {
|
|
27529
27771
|
init_fs();
|
|
27530
27772
|
init_constants34();
|
|
27531
27773
|
}
|
|
@@ -27675,7 +27917,8 @@ var init_importer32 = __esm({
|
|
|
27675
27917
|
init_constants34();
|
|
27676
27918
|
init_importer_workflows();
|
|
27677
27919
|
init_skills_adapter5();
|
|
27678
|
-
|
|
27920
|
+
init_importer_hooks2();
|
|
27921
|
+
init_importer_mcp();
|
|
27679
27922
|
}
|
|
27680
27923
|
});
|
|
27681
27924
|
|
|
@@ -27760,9 +28003,28 @@ function lintPermissions22(canonical) {
|
|
|
27760
28003
|
)
|
|
27761
28004
|
];
|
|
27762
28005
|
}
|
|
28006
|
+
function lintHooks23(canonical) {
|
|
28007
|
+
if (!canonical.hooks) return [];
|
|
28008
|
+
const diagnostics = [];
|
|
28009
|
+
for (const [event, entries] of Object.entries(canonical.hooks)) {
|
|
28010
|
+
for (const entry of entries ?? []) {
|
|
28011
|
+
if (WILDCARD_MATCHERS.has(entry.matcher.trim())) continue;
|
|
28012
|
+
diagnostics.push(
|
|
28013
|
+
createWarning(
|
|
28014
|
+
".agentsmesh/hooks.yaml",
|
|
28015
|
+
"windsurf",
|
|
28016
|
+
`Windsurf hooks have no matcher field; ${event} hook "${entry.command}" runs on every ${event} event (matcher "${entry.matcher}" is not projected).`
|
|
28017
|
+
)
|
|
28018
|
+
);
|
|
28019
|
+
}
|
|
28020
|
+
}
|
|
28021
|
+
return diagnostics;
|
|
28022
|
+
}
|
|
28023
|
+
var WILDCARD_MATCHERS;
|
|
27763
28024
|
var init_lint31 = __esm({
|
|
27764
28025
|
"src/targets/windsurf/lint.ts"() {
|
|
27765
28026
|
init_helpers();
|
|
28027
|
+
WILDCARD_MATCHERS = /* @__PURE__ */ new Set(["", "*", ".*"]);
|
|
27766
28028
|
}
|
|
27767
28029
|
});
|
|
27768
28030
|
|
|
@@ -27918,6 +28180,7 @@ var init_windsurf2 = __esm({
|
|
|
27918
28180
|
lintRules: lintRules32,
|
|
27919
28181
|
lint: {
|
|
27920
28182
|
commands: lintCommands10,
|
|
28183
|
+
hooks: lintHooks23,
|
|
27921
28184
|
mcp: lintMcp13,
|
|
27922
28185
|
permissions: lintPermissions22
|
|
27923
28186
|
},
|
|
@@ -29787,6 +30050,7 @@ function ruleNameFromSource(source) {
|
|
|
29787
30050
|
}
|
|
29788
30051
|
|
|
29789
30052
|
// src/core/generate/collision.ts
|
|
30053
|
+
init_fs_text_encoding();
|
|
29790
30054
|
init_target_ids();
|
|
29791
30055
|
var AGENTS_SUFFIX = "AGENTS.md";
|
|
29792
30056
|
function statusRank(status) {
|
|
@@ -29895,7 +30159,7 @@ function assertNoCaseOnlyPathCollisions(results) {
|
|
|
29895
30159
|
}
|
|
29896
30160
|
}
|
|
29897
30161
|
function refreshResultStatus(result2) {
|
|
29898
|
-
const status = result2.currentContent === void 0 ? "created" : result2.currentContent !== result2.content ? "updated" : "unchanged";
|
|
30162
|
+
const status = result2.currentContent === void 0 ? "created" : normalizeTextPayload(result2.path, result2.currentContent) !== normalizeTextPayload(result2.path, result2.content) ? "updated" : "unchanged";
|
|
29899
30163
|
return result2.status === status ? result2 : { ...result2, status };
|
|
29900
30164
|
}
|
|
29901
30165
|
|
|
@@ -30300,8 +30564,46 @@ function mergeLocalConfig(project26, local) {
|
|
|
30300
30564
|
if (Array.isArray(local.extends) && local.extends.length > 0) {
|
|
30301
30565
|
merged.extends = [...project26.extends ?? [], ...local.extends];
|
|
30302
30566
|
}
|
|
30567
|
+
if (Array.isArray(local.plugins)) {
|
|
30568
|
+
merged.plugins = mergeById(project26.plugins, local.plugins);
|
|
30569
|
+
}
|
|
30570
|
+
if (Array.isArray(local.pluginTargets)) {
|
|
30571
|
+
merged.pluginTargets = [.../* @__PURE__ */ new Set([...project26.pluginTargets, ...local.pluginTargets])];
|
|
30572
|
+
}
|
|
30573
|
+
if (typeof local.collaboration === "object" && local.collaboration !== null && !Array.isArray(local.collaboration)) {
|
|
30574
|
+
merged.collaboration = local.collaboration;
|
|
30575
|
+
}
|
|
30576
|
+
warnUnhandledLocalKeys(local);
|
|
30303
30577
|
return merged;
|
|
30304
30578
|
}
|
|
30579
|
+
var LOCAL_KEYS = /* @__PURE__ */ new Set([
|
|
30580
|
+
"version",
|
|
30581
|
+
"targets",
|
|
30582
|
+
"features",
|
|
30583
|
+
"overrides",
|
|
30584
|
+
"conversions",
|
|
30585
|
+
"extends",
|
|
30586
|
+
"plugins",
|
|
30587
|
+
"pluginTargets",
|
|
30588
|
+
"collaboration"
|
|
30589
|
+
]);
|
|
30590
|
+
function warnUnhandledLocalKeys(local) {
|
|
30591
|
+
const unknown = Object.keys(local).filter((key) => !LOCAL_KEYS.has(key));
|
|
30592
|
+
if (unknown.length === 0) return;
|
|
30593
|
+
logger.warn(
|
|
30594
|
+
`agentsmesh.local.yaml: ignoring unknown key(s) ${unknown.join(", ")}; supported keys are ${[...LOCAL_KEYS].join(", ")}.`
|
|
30595
|
+
);
|
|
30596
|
+
}
|
|
30597
|
+
function mergeById(project26, local) {
|
|
30598
|
+
const byId = /* @__PURE__ */ new Map();
|
|
30599
|
+
const anonymous = [];
|
|
30600
|
+
for (const entry of [...project26, ...local]) {
|
|
30601
|
+
const id = typeof entry === "object" && entry !== null && typeof entry.id === "string" ? entry.id : void 0;
|
|
30602
|
+
if (id === void 0) anonymous.push(entry);
|
|
30603
|
+
else byId.set(id, entry);
|
|
30604
|
+
}
|
|
30605
|
+
return [...byId.values(), ...anonymous];
|
|
30606
|
+
}
|
|
30305
30607
|
async function loadConfigFromExactDir(configDir) {
|
|
30306
30608
|
const configPath = join(configDir, CONFIG_FILENAME);
|
|
30307
30609
|
let config = await loadConfig(configPath);
|
|
@@ -30790,24 +31092,14 @@ async function sweepStaleCache(cacheDir, maxAgeMs) {
|
|
|
30790
31092
|
|
|
30791
31093
|
// src/config/remote/remote-fetcher.ts
|
|
30792
31094
|
var MAX_CACHE_KEY_LENGTH = 80;
|
|
31095
|
+
var CACHE_KEY_HASH_LENGTH = 12;
|
|
30793
31096
|
function buildCacheKey(provider, identifier, ref) {
|
|
30794
31097
|
const safe = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_").replace(/^\.+/, "_");
|
|
30795
|
-
|
|
30796
|
-
|
|
30797
|
-
|
|
30798
|
-
|
|
30799
|
-
|
|
30800
|
-
} else {
|
|
30801
|
-
key = `${safe(provider)}__${safe(identifier)}__${safe(ref)}`;
|
|
30802
|
-
}
|
|
30803
|
-
} else {
|
|
30804
|
-
key = `${safe(provider)}__${safe(identifier)}__${safe(ref)}`;
|
|
30805
|
-
}
|
|
30806
|
-
if (key.length > MAX_CACHE_KEY_LENGTH) {
|
|
30807
|
-
const hash = createHash("sha256").update(key).digest("hex").slice(0, 16);
|
|
30808
|
-
key = `${key.slice(0, MAX_CACHE_KEY_LENGTH - 18)}--${hash}`;
|
|
30809
|
-
}
|
|
30810
|
-
return key;
|
|
31098
|
+
const [org, repo] = provider === "github" ? identifier.split("/", 2) : [];
|
|
31099
|
+
const readable = org && repo ? `${safe(org)}--${safe(repo)}--${safe(ref)}` : `${safe(provider)}__${safe(identifier)}__${safe(ref)}`;
|
|
31100
|
+
const hash = createHash("sha256").update(`${provider}|${identifier}|${ref}`).digest("hex").slice(0, CACHE_KEY_HASH_LENGTH);
|
|
31101
|
+
const maxReadable = MAX_CACHE_KEY_LENGTH - CACHE_KEY_HASH_LENGTH - 2;
|
|
31102
|
+
return `${readable.slice(0, maxReadable)}--${hash}`;
|
|
30811
31103
|
}
|
|
30812
31104
|
function getCacheDir() {
|
|
30813
31105
|
const env = process.env.AGENTSMESH_CACHE;
|
|
@@ -30919,6 +31211,13 @@ async function resolveExtendPaths(config, configDir, options = {}) {
|
|
|
30919
31211
|
return result2;
|
|
30920
31212
|
}
|
|
30921
31213
|
|
|
31214
|
+
// src/canonical/features/empty-file.ts
|
|
31215
|
+
function isEmptyCanonicalFile(content, path) {
|
|
31216
|
+
if (content.trim() !== "") return false;
|
|
31217
|
+
logger.warn(`Skipping empty canonical file ${path.replaceAll("\\", "/")}`);
|
|
31218
|
+
return true;
|
|
31219
|
+
}
|
|
31220
|
+
|
|
30922
31221
|
// src/canonical/features/rules.ts
|
|
30923
31222
|
init_fs();
|
|
30924
31223
|
init_markdown();
|
|
@@ -31058,7 +31357,8 @@ async function parseRules(rulesDir, opts = {}) {
|
|
|
31058
31357
|
const rules = [];
|
|
31059
31358
|
for (const path of mdFiles) {
|
|
31060
31359
|
const content = await readFileSafe(path);
|
|
31061
|
-
if (
|
|
31360
|
+
if (content === null) continue;
|
|
31361
|
+
if (isEmptyCanonicalFile(content, path)) continue;
|
|
31062
31362
|
const parsed = parseOrSkipFrontmatter(content, path, opts.onParseError);
|
|
31063
31363
|
if (!parsed) continue;
|
|
31064
31364
|
const { frontmatter, body } = parsed;
|
|
@@ -31112,7 +31412,8 @@ async function parseCommands(commandsDir, opts = {}) {
|
|
|
31112
31412
|
const commands = [];
|
|
31113
31413
|
for (const path of mdFiles) {
|
|
31114
31414
|
const content = await readFileSafe(path);
|
|
31115
|
-
if (
|
|
31415
|
+
if (content === null) continue;
|
|
31416
|
+
if (isEmptyCanonicalFile(content, path)) continue;
|
|
31116
31417
|
const parsed = parseOrSkipFrontmatter(content, path, opts.onParseError);
|
|
31117
31418
|
if (!parsed) continue;
|
|
31118
31419
|
const { frontmatter, body } = parsed;
|
|
@@ -31172,15 +31473,14 @@ async function parseAgents(agentsDir, opts = {}) {
|
|
|
31172
31473
|
const agents = [];
|
|
31173
31474
|
for (const path of mdFiles) {
|
|
31174
31475
|
const content = await readFileSafe(path);
|
|
31175
|
-
if (
|
|
31476
|
+
if (content === null) continue;
|
|
31477
|
+
if (isEmptyCanonicalFile(content, path)) continue;
|
|
31176
31478
|
const parsed = parseOrSkipFrontmatter(content, path, opts.onParseError);
|
|
31177
31479
|
if (!parsed) continue;
|
|
31178
31480
|
const { frontmatter, body } = parsed;
|
|
31179
31481
|
const name = basename(path, ".md");
|
|
31180
31482
|
assertCanonicalName("agent", name);
|
|
31181
|
-
const
|
|
31182
|
-
const toolsKebab = toStrArray2(frontmatter["tools"]);
|
|
31183
|
-
const tools = toolsCamel.length > 0 ? toolsCamel : toolsKebab;
|
|
31483
|
+
const tools = toStrArray2(frontmatter.tools);
|
|
31184
31484
|
const disallowedCamel = toStrArray2(frontmatter.disallowedTools);
|
|
31185
31485
|
const disallowedKebab = toStrArray2(frontmatter["disallowed-tools"]);
|
|
31186
31486
|
const disallowedTools = disallowedCamel.length > 0 ? disallowedCamel : disallowedKebab;
|
|
@@ -31302,20 +31602,21 @@ async function parseSkills(skillsDir, opts = {}) {
|
|
|
31302
31602
|
init_mcp();
|
|
31303
31603
|
|
|
31304
31604
|
// src/canonical/features/permissions.ts
|
|
31605
|
+
init_syntax_error();
|
|
31305
31606
|
init_fs();
|
|
31306
31607
|
function ensureStringArray(val) {
|
|
31307
31608
|
if (!Array.isArray(val)) return [];
|
|
31308
31609
|
return val.filter((x) => typeof x === "string");
|
|
31309
31610
|
}
|
|
31310
|
-
async function parsePermissions(permissionsPath) {
|
|
31611
|
+
async function parsePermissions(permissionsPath, onParseError) {
|
|
31311
31612
|
const content = await readFileSafe(permissionsPath);
|
|
31312
31613
|
if (content === null) return null;
|
|
31313
31614
|
if (!content.trim()) return { allow: [], deny: [], ask: [] };
|
|
31314
31615
|
let parsed;
|
|
31315
31616
|
try {
|
|
31316
31617
|
parsed = parse(content);
|
|
31317
|
-
} catch {
|
|
31318
|
-
return
|
|
31618
|
+
} catch (err) {
|
|
31619
|
+
return failSyntax(permissionsPath, err, onParseError);
|
|
31319
31620
|
}
|
|
31320
31621
|
if (!parsed || typeof parsed !== "object") return null;
|
|
31321
31622
|
const obj = parsed;
|
|
@@ -31325,52 +31626,8 @@ async function parsePermissions(permissionsPath) {
|
|
|
31325
31626
|
return { allow, deny, ask };
|
|
31326
31627
|
}
|
|
31327
31628
|
|
|
31328
|
-
// src/canonical/
|
|
31329
|
-
|
|
31330
|
-
init_hook_command();
|
|
31331
|
-
var VALID_TYPES = ["command", "prompt"];
|
|
31332
|
-
function toHookEntry(raw) {
|
|
31333
|
-
if (!raw || typeof raw !== "object") return null;
|
|
31334
|
-
const obj = raw;
|
|
31335
|
-
const matcher = obj.matcher;
|
|
31336
|
-
if (typeof matcher !== "string") return null;
|
|
31337
|
-
const command = getHookText(obj);
|
|
31338
|
-
if (!command) return null;
|
|
31339
|
-
const type = typeof obj.type === "string" && VALID_TYPES.includes(obj.type) ? obj.type : void 0;
|
|
31340
|
-
const timeout = typeof obj.timeout === "number" && Number.isFinite(obj.timeout) ? obj.timeout : void 0;
|
|
31341
|
-
const prompt = getHookPrompt(obj) || void 0;
|
|
31342
|
-
return {
|
|
31343
|
-
matcher,
|
|
31344
|
-
command,
|
|
31345
|
-
...timeout !== void 0 && { timeout },
|
|
31346
|
-
...type && { type },
|
|
31347
|
-
...prompt && { prompt }
|
|
31348
|
-
};
|
|
31349
|
-
}
|
|
31350
|
-
async function parseHooks(hooksPath) {
|
|
31351
|
-
const content = await readFileSafe(hooksPath);
|
|
31352
|
-
if (content === null) return null;
|
|
31353
|
-
if (!content.trim()) return {};
|
|
31354
|
-
let parsed;
|
|
31355
|
-
try {
|
|
31356
|
-
parsed = parse(content);
|
|
31357
|
-
} catch {
|
|
31358
|
-
return null;
|
|
31359
|
-
}
|
|
31360
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
31361
|
-
const result2 = {};
|
|
31362
|
-
const obj = parsed;
|
|
31363
|
-
for (const [key, val] of Object.entries(obj)) {
|
|
31364
|
-
if (!Array.isArray(val)) continue;
|
|
31365
|
-
const entries = [];
|
|
31366
|
-
for (const item of val) {
|
|
31367
|
-
const entry = toHookEntry(item);
|
|
31368
|
-
if (entry) entries.push(entry);
|
|
31369
|
-
}
|
|
31370
|
-
if (entries.length > 0) result2[key] = entries;
|
|
31371
|
-
}
|
|
31372
|
-
return result2;
|
|
31373
|
-
}
|
|
31629
|
+
// src/canonical/load/loader.ts
|
|
31630
|
+
init_hooks5();
|
|
31374
31631
|
|
|
31375
31632
|
// src/canonical/features/ignore.ts
|
|
31376
31633
|
init_fs();
|
|
@@ -31397,9 +31654,9 @@ async function loadCanonicalFiles(canonicalDirOrProjectRoot, opts = {}) {
|
|
|
31397
31654
|
parseCommands(join(canonicalDir, "commands"), opts),
|
|
31398
31655
|
parseAgents(join(canonicalDir, "agents"), opts),
|
|
31399
31656
|
parseSkills(join(canonicalDir, "skills"), opts),
|
|
31400
|
-
parseMcp(join(canonicalDir, "mcp.json")),
|
|
31401
|
-
parsePermissions(join(canonicalDir, "permissions.yaml")),
|
|
31402
|
-
parseHooks(join(canonicalDir, "hooks.yaml")),
|
|
31657
|
+
parseMcp(join(canonicalDir, "mcp.json"), opts.onParseError),
|
|
31658
|
+
parsePermissions(join(canonicalDir, "permissions.yaml"), opts.onParseError),
|
|
31659
|
+
parseHooks(join(canonicalDir, "hooks.yaml"), opts.onParseError),
|
|
31403
31660
|
parseIgnore(join(canonicalDir, "ignore"))
|
|
31404
31661
|
]);
|
|
31405
31662
|
return {
|
|
@@ -31413,13 +31670,13 @@ async function loadCanonicalFiles(canonicalDirOrProjectRoot, opts = {}) {
|
|
|
31413
31670
|
ignore
|
|
31414
31671
|
};
|
|
31415
31672
|
}
|
|
31416
|
-
function
|
|
31673
|
+
function ruleSlug4(r) {
|
|
31417
31674
|
return basename(r.source, ".md");
|
|
31418
31675
|
}
|
|
31419
31676
|
function mergeCanonicalFiles(base, overlay) {
|
|
31420
|
-
const baseRuleMap = new Map(base.rules.map((r) => [
|
|
31677
|
+
const baseRuleMap = new Map(base.rules.map((r) => [ruleSlug4(r), r]));
|
|
31421
31678
|
for (const r of overlay.rules) {
|
|
31422
|
-
baseRuleMap.set(
|
|
31679
|
+
baseRuleMap.set(ruleSlug4(r), r);
|
|
31423
31680
|
}
|
|
31424
31681
|
const baseCmdMap = new Map(base.commands.map((c2) => [c2.name, c2]));
|
|
31425
31682
|
for (const c2 of overlay.commands) {
|
|
@@ -32302,6 +32559,7 @@ function gateExtendElevatedArtifacts(canonical, ext) {
|
|
|
32302
32559
|
});
|
|
32303
32560
|
}
|
|
32304
32561
|
init_mcp();
|
|
32562
|
+
init_hooks5();
|
|
32305
32563
|
|
|
32306
32564
|
// src/install/pack/pack-reader.ts
|
|
32307
32565
|
init_fs();
|
|
@@ -33067,88 +33325,6 @@ function collectOrphans(graph, findings) {
|
|
|
33067
33325
|
}
|
|
33068
33326
|
}
|
|
33069
33327
|
|
|
33070
|
-
// src/lessons/ranking-text.ts
|
|
33071
|
-
var K1 = 1.5;
|
|
33072
|
-
var B = 0.75;
|
|
33073
|
-
var STOP = /* @__PURE__ */ new Set([
|
|
33074
|
-
"the",
|
|
33075
|
-
"a",
|
|
33076
|
-
"an",
|
|
33077
|
-
"to",
|
|
33078
|
-
"of",
|
|
33079
|
-
"in",
|
|
33080
|
-
"and",
|
|
33081
|
-
"or",
|
|
33082
|
-
"for",
|
|
33083
|
-
"is",
|
|
33084
|
-
"on",
|
|
33085
|
-
"at",
|
|
33086
|
-
"with",
|
|
33087
|
-
"be",
|
|
33088
|
-
"as",
|
|
33089
|
-
"it",
|
|
33090
|
-
"that",
|
|
33091
|
-
"this",
|
|
33092
|
-
"its",
|
|
33093
|
-
"must"
|
|
33094
|
-
]);
|
|
33095
|
-
function tokenize(text) {
|
|
33096
|
-
return text.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 2 && !STOP.has(t));
|
|
33097
|
-
}
|
|
33098
|
-
function queryTerms(query) {
|
|
33099
|
-
const parts = [];
|
|
33100
|
-
if (query.keyword !== void 0) parts.push(query.keyword);
|
|
33101
|
-
if (query.file !== void 0) parts.push(query.file);
|
|
33102
|
-
if (query.command !== void 0) parts.push(query.command);
|
|
33103
|
-
return tokenize(parts.join(" "));
|
|
33104
|
-
}
|
|
33105
|
-
function buildCorpus(graph) {
|
|
33106
|
-
const docs = [];
|
|
33107
|
-
const df = /* @__PURE__ */ new Map();
|
|
33108
|
-
let total = 0;
|
|
33109
|
-
let n = 0;
|
|
33110
|
-
for (const lesson of Object.values(graph.lessons)) {
|
|
33111
|
-
if (lesson.status !== "active") continue;
|
|
33112
|
-
const toks = tokenize(lesson.rule);
|
|
33113
|
-
n += 1;
|
|
33114
|
-
total += toks.length;
|
|
33115
|
-
docs.push(toks.length);
|
|
33116
|
-
for (const t of new Set(toks)) df.set(t, (df.get(t) ?? 0) + 1);
|
|
33117
|
-
}
|
|
33118
|
-
const N = Math.max(n, 1);
|
|
33119
|
-
const idf = /* @__PURE__ */ new Map();
|
|
33120
|
-
for (const [t, f] of df) idf.set(t, Math.log(1 + (N - f + 0.5) / (f + 0.5)));
|
|
33121
|
-
return { idf, avgdl: total / N || 1 };
|
|
33122
|
-
}
|
|
33123
|
-
function bm25(terms, ruleText, corpus) {
|
|
33124
|
-
const toks = tokenize(ruleText);
|
|
33125
|
-
const dl = toks.length || 1;
|
|
33126
|
-
const tf = /* @__PURE__ */ new Map();
|
|
33127
|
-
for (const t of toks) tf.set(t, (tf.get(t) ?? 0) + 1);
|
|
33128
|
-
let score = 0;
|
|
33129
|
-
for (const t of new Set(terms)) {
|
|
33130
|
-
const f = tf.get(t) ?? 0;
|
|
33131
|
-
if (f === 0) continue;
|
|
33132
|
-
const idf = corpus.idf.get(t);
|
|
33133
|
-
score += idf * (f * (K1 + 1)) / (f + K1 * (1 - B + B * dl / corpus.avgdl));
|
|
33134
|
-
}
|
|
33135
|
-
return score;
|
|
33136
|
-
}
|
|
33137
|
-
|
|
33138
|
-
// src/lessons/keyword-signal.ts
|
|
33139
|
-
var MAX_RECOMMENDED_KEYWORD_TOKENS = 5;
|
|
33140
|
-
function isLowSignalKeyword(pattern) {
|
|
33141
|
-
return tokenize(pattern).length > MAX_RECOMMENDED_KEYWORD_TOKENS;
|
|
33142
|
-
}
|
|
33143
|
-
function splitRawTokens(pattern) {
|
|
33144
|
-
return pattern.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 0);
|
|
33145
|
-
}
|
|
33146
|
-
function keywordNeedleLosesTokens(pattern) {
|
|
33147
|
-
const raw = splitRawTokens(pattern);
|
|
33148
|
-
if (raw.length < 2) return false;
|
|
33149
|
-
return tokenize(pattern).length !== raw.length;
|
|
33150
|
-
}
|
|
33151
|
-
|
|
33152
33328
|
// src/lessons/regex-linear/nfa-compile.ts
|
|
33153
33329
|
var MAX_NFA_STATES = 2e3;
|
|
33154
33330
|
var Builder = class {
|
|
@@ -33669,6 +33845,93 @@ function collectFanout(graph, findings) {
|
|
|
33669
33845
|
});
|
|
33670
33846
|
}
|
|
33671
33847
|
}
|
|
33848
|
+
function normalizeRule(rule) {
|
|
33849
|
+
return rule.trim().replace(/\s+/g, " ").toLowerCase();
|
|
33850
|
+
}
|
|
33851
|
+
|
|
33852
|
+
// src/lessons/ranking-text.ts
|
|
33853
|
+
var K1 = 1.5;
|
|
33854
|
+
var B = 0.75;
|
|
33855
|
+
var STOP = /* @__PURE__ */ new Set([
|
|
33856
|
+
"the",
|
|
33857
|
+
"a",
|
|
33858
|
+
"an",
|
|
33859
|
+
"to",
|
|
33860
|
+
"of",
|
|
33861
|
+
"in",
|
|
33862
|
+
"and",
|
|
33863
|
+
"or",
|
|
33864
|
+
"for",
|
|
33865
|
+
"is",
|
|
33866
|
+
"on",
|
|
33867
|
+
"at",
|
|
33868
|
+
"with",
|
|
33869
|
+
"be",
|
|
33870
|
+
"as",
|
|
33871
|
+
"it",
|
|
33872
|
+
"that",
|
|
33873
|
+
"this",
|
|
33874
|
+
"its",
|
|
33875
|
+
"must"
|
|
33876
|
+
]);
|
|
33877
|
+
function tokenize(text) {
|
|
33878
|
+
return text.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 2 && !STOP.has(t));
|
|
33879
|
+
}
|
|
33880
|
+
function queryTerms(query) {
|
|
33881
|
+
const parts = [];
|
|
33882
|
+
if (query.keyword !== void 0) parts.push(query.keyword);
|
|
33883
|
+
if (query.file !== void 0) parts.push(query.file);
|
|
33884
|
+
if (query.command !== void 0) parts.push(query.command);
|
|
33885
|
+
return tokenize(parts.join(" "));
|
|
33886
|
+
}
|
|
33887
|
+
function buildCorpus(graph) {
|
|
33888
|
+
const docs = [];
|
|
33889
|
+
const df = /* @__PURE__ */ new Map();
|
|
33890
|
+
let total = 0;
|
|
33891
|
+
let n = 0;
|
|
33892
|
+
for (const lesson of Object.values(graph.lessons)) {
|
|
33893
|
+
if (lesson.status !== "active") continue;
|
|
33894
|
+
const toks = tokenize(lesson.rule);
|
|
33895
|
+
n += 1;
|
|
33896
|
+
total += toks.length;
|
|
33897
|
+
docs.push(toks.length);
|
|
33898
|
+
for (const t of new Set(toks)) df.set(t, (df.get(t) ?? 0) + 1);
|
|
33899
|
+
}
|
|
33900
|
+
const N = Math.max(n, 1);
|
|
33901
|
+
const idf = /* @__PURE__ */ new Map();
|
|
33902
|
+
for (const [t, f] of df) idf.set(t, Math.log(1 + (N - f + 0.5) / (f + 0.5)));
|
|
33903
|
+
return { idf, avgdl: total / N || 1 };
|
|
33904
|
+
}
|
|
33905
|
+
function bm25(terms, ruleText, corpus) {
|
|
33906
|
+
const toks = tokenize(ruleText);
|
|
33907
|
+
const dl = toks.length || 1;
|
|
33908
|
+
const tf = /* @__PURE__ */ new Map();
|
|
33909
|
+
for (const t of toks) tf.set(t, (tf.get(t) ?? 0) + 1);
|
|
33910
|
+
let score = 0;
|
|
33911
|
+
for (const t of new Set(terms)) {
|
|
33912
|
+
const f = tf.get(t) ?? 0;
|
|
33913
|
+
if (f === 0) continue;
|
|
33914
|
+
const idf = corpus.idf.get(t);
|
|
33915
|
+
score += idf * (f * (K1 + 1)) / (f + K1 * (1 - B + B * dl / corpus.avgdl));
|
|
33916
|
+
}
|
|
33917
|
+
return score;
|
|
33918
|
+
}
|
|
33919
|
+
|
|
33920
|
+
// src/lessons/keyword-signal.ts
|
|
33921
|
+
var MAX_RECOMMENDED_KEYWORD_TOKENS = 5;
|
|
33922
|
+
function isLowSignalKeyword(pattern) {
|
|
33923
|
+
return tokenize(pattern).length > MAX_RECOMMENDED_KEYWORD_TOKENS;
|
|
33924
|
+
}
|
|
33925
|
+
function splitRawTokens(pattern) {
|
|
33926
|
+
return pattern.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 0);
|
|
33927
|
+
}
|
|
33928
|
+
function keywordNeedleLosesTokens(pattern) {
|
|
33929
|
+
const raw = splitRawTokens(pattern);
|
|
33930
|
+
if (raw.length < 2) return false;
|
|
33931
|
+
return tokenize(pattern).length !== raw.length;
|
|
33932
|
+
}
|
|
33933
|
+
|
|
33934
|
+
// src/lessons/validate-keywords.ts
|
|
33672
33935
|
function collectLowSignalKeywords(graph, findings) {
|
|
33673
33936
|
const activeTriggerIds2 = /* @__PURE__ */ new Set();
|
|
33674
33937
|
for (const lesson of Object.values(graph.lessons)) {
|
|
@@ -33682,7 +33945,7 @@ function collectLowSignalKeywords(graph, findings) {
|
|
|
33682
33945
|
findings.push({
|
|
33683
33946
|
level: "warning",
|
|
33684
33947
|
code: "LOW_SIGNAL_KEYWORD",
|
|
33685
|
-
message: `Keyword trigger "${triggerId}" carries more than ${MAX_RECOMMENDED_KEYWORD_TOKENS} tokens (${trigger.pattern}); recall matches a keyword only as a
|
|
33948
|
+
message: `Keyword trigger "${triggerId}" carries more than ${MAX_RECOMMENDED_KEYWORD_TOKENS} tokens (${trigger.pattern}); recall matches a keyword only as a contiguous token-run in --keyword or the file/command, so it rarely fires \u2014 use a short distinctive phrase.`,
|
|
33686
33949
|
triggerId
|
|
33687
33950
|
});
|
|
33688
33951
|
}
|
|
@@ -33707,9 +33970,62 @@ function collectStopwordKeywords(graph, findings) {
|
|
|
33707
33970
|
});
|
|
33708
33971
|
}
|
|
33709
33972
|
}
|
|
33710
|
-
|
|
33711
|
-
|
|
33973
|
+
|
|
33974
|
+
// src/lessons/glob-breadth.ts
|
|
33975
|
+
var WILDCARD = /[*?[\]]/;
|
|
33976
|
+
function globNarrowness(pattern) {
|
|
33977
|
+
const segments = pattern.replaceAll("\\", "/").split("/").filter((segment) => segment !== "" && segment !== ".");
|
|
33978
|
+
if (segments.length === 0) return 0;
|
|
33979
|
+
let literal = 0;
|
|
33980
|
+
let globstars = 0;
|
|
33981
|
+
for (const segment of segments) {
|
|
33982
|
+
if (segment === "**") globstars += 1;
|
|
33983
|
+
else if (!WILDCARD.test(segment)) literal += 1;
|
|
33984
|
+
}
|
|
33985
|
+
return literal / (segments.length + globstars);
|
|
33986
|
+
}
|
|
33987
|
+
var BROAD_GLOB_NARROWNESS = 0.34;
|
|
33988
|
+
function isBroadFileGlob(pattern) {
|
|
33989
|
+
return globNarrowness(pattern) < BROAD_GLOB_NARROWNESS;
|
|
33990
|
+
}
|
|
33991
|
+
|
|
33992
|
+
// src/lessons/command-pattern-breadth.ts
|
|
33993
|
+
var COMMAND_PROBE_CORPUS = [
|
|
33994
|
+
"git status",
|
|
33995
|
+
'git commit -m "wip"',
|
|
33996
|
+
"pnpm test",
|
|
33997
|
+
"npx vitest run src/x.test.ts",
|
|
33998
|
+
"ls -la",
|
|
33999
|
+
"cat README.md",
|
|
34000
|
+
"rm -rf dist",
|
|
34001
|
+
"mkdir -p build/out",
|
|
34002
|
+
"node scripts/build.js",
|
|
34003
|
+
"docker compose up -d",
|
|
34004
|
+
"curl -s https://example.com",
|
|
34005
|
+
"echo hello > out.txt",
|
|
34006
|
+
"sed -i 's/a/b/' file.txt",
|
|
34007
|
+
"pnpm lint --fix",
|
|
34008
|
+
"python3 -m pytest",
|
|
34009
|
+
"cargo build --release",
|
|
34010
|
+
"make",
|
|
34011
|
+
"npm install --global typescript",
|
|
34012
|
+
"cp a.txt b.txt",
|
|
34013
|
+
"grep -rn TODO src"
|
|
34014
|
+
];
|
|
34015
|
+
var BROAD_HIT_RATIO = 0.5;
|
|
34016
|
+
var PROBE_BUDGET = 1e5;
|
|
34017
|
+
function isBroadCommandPattern(pattern) {
|
|
34018
|
+
const matcher = getCommandMatcher(pattern);
|
|
34019
|
+
if (matcher === null) return false;
|
|
34020
|
+
if (matcher.test("", { remaining: PROBE_BUDGET })) return true;
|
|
34021
|
+
let hits = 0;
|
|
34022
|
+
for (const command of COMMAND_PROBE_CORPUS) {
|
|
34023
|
+
if (matcher.test(command, { remaining: PROBE_BUDGET })) hits += 1;
|
|
34024
|
+
}
|
|
34025
|
+
return hits > COMMAND_PROBE_CORPUS.length * BROAD_HIT_RATIO;
|
|
33712
34026
|
}
|
|
34027
|
+
|
|
34028
|
+
// src/lessons/validate-liveness.ts
|
|
33713
34029
|
function activeTriggerIds(graph) {
|
|
33714
34030
|
const ids = /* @__PURE__ */ new Set();
|
|
33715
34031
|
for (const lesson of Object.values(graph.lessons)) {
|
|
@@ -33761,6 +34077,34 @@ function collectRunnerAnchoredPatterns(graph, findings) {
|
|
|
33761
34077
|
});
|
|
33762
34078
|
}
|
|
33763
34079
|
}
|
|
34080
|
+
function collectBroadFileGlobs(graph, findings) {
|
|
34081
|
+
const active = activeTriggerIds(graph);
|
|
34082
|
+
for (const [triggerId, trigger] of Object.entries(graph.triggers)) {
|
|
34083
|
+
if (trigger.kind !== "file_glob") continue;
|
|
34084
|
+
if (!active.has(triggerId)) continue;
|
|
34085
|
+
if (!isBroadFileGlob(trigger.pattern)) continue;
|
|
34086
|
+
findings.push({
|
|
34087
|
+
level: "warning",
|
|
34088
|
+
code: "BROAD_FILE_GLOB",
|
|
34089
|
+
message: `file_glob trigger "${triggerId}" (${trigger.pattern}) matches most of the repository, so it outranks nothing and crowds the recall budget. Narrow it to the directory or file class the rule is really about, or detach it with \`lessons untrigger\`.`,
|
|
34090
|
+
triggerId
|
|
34091
|
+
});
|
|
34092
|
+
}
|
|
34093
|
+
}
|
|
34094
|
+
function collectBroadCommandPatterns(graph, findings) {
|
|
34095
|
+
const active = activeTriggerIds(graph);
|
|
34096
|
+
for (const [triggerId, trigger] of Object.entries(graph.triggers)) {
|
|
34097
|
+
if (trigger.kind !== "command_pattern") continue;
|
|
34098
|
+
if (!active.has(triggerId)) continue;
|
|
34099
|
+
if (!isBroadCommandPattern(trigger.pattern)) continue;
|
|
34100
|
+
findings.push({
|
|
34101
|
+
level: "warning",
|
|
34102
|
+
code: "BROAD_COMMAND_PATTERN",
|
|
34103
|
+
message: `command_pattern trigger "${triggerId}" (${trigger.pattern}) matches nearly every command, so the lesson fires on every recall. Key it on the action (e.g. \`\\bgit commit\\b\`), or detach it with \`lessons untrigger\`.`,
|
|
34104
|
+
triggerId
|
|
34105
|
+
});
|
|
34106
|
+
}
|
|
34107
|
+
}
|
|
33764
34108
|
|
|
33765
34109
|
// src/lessons/validate.ts
|
|
33766
34110
|
function validateLessonsGraph(graph, options = {}) {
|
|
@@ -33788,6 +34132,8 @@ function validateLessonsGraph(graph, options = {}) {
|
|
|
33788
34132
|
collectLowSignalKeywords(graph, findings);
|
|
33789
34133
|
collectStopwordKeywords(graph, findings);
|
|
33790
34134
|
collectRunnerAnchoredPatterns(graph, findings);
|
|
34135
|
+
collectBroadCommandPatterns(graph, findings);
|
|
34136
|
+
collectBroadFileGlobs(graph, findings);
|
|
33791
34137
|
if (options.knownPaths !== void 0) collectDeadFileGlobs(graph, findings, options.knownPaths);
|
|
33792
34138
|
const ok = findings.every((f) => f.level !== "error");
|
|
33793
34139
|
return { ok, findings };
|
|
@@ -33839,13 +34185,13 @@ function diag(level, file, message) {
|
|
|
33839
34185
|
|
|
33840
34186
|
// src/core/lint/linter.ts
|
|
33841
34187
|
var EXCLUDE_DIRS = ["node_modules", ".git", "dist", "coverage", ".agentsmesh"];
|
|
34188
|
+
function isExcludedProjectPath(rel2) {
|
|
34189
|
+
const posix9 = rel2.replaceAll("\\", "/");
|
|
34190
|
+
return EXCLUDE_DIRS.some((d) => posix9.includes(`/${d}/`) || posix9.startsWith(`${d}/`));
|
|
34191
|
+
}
|
|
33842
34192
|
async function getProjectFiles(projectRoot) {
|
|
33843
34193
|
const all = await readDirRecursive(projectRoot);
|
|
33844
|
-
|
|
33845
|
-
const rel2 = relative(projectRoot, p);
|
|
33846
|
-
return !EXCLUDE_DIRS.some((d) => rel2.includes(`/${d}/`) || rel2.startsWith(`${d}/`));
|
|
33847
|
-
});
|
|
33848
|
-
return filtered.map((p) => relative(projectRoot, p));
|
|
34194
|
+
return all.filter((p) => !isExcludedProjectPath(relative(projectRoot, p))).map((p) => relative(projectRoot, p));
|
|
33849
34195
|
}
|
|
33850
34196
|
async function runLint(config, canonical, projectRoot, targetFilter, options = {}) {
|
|
33851
34197
|
const scope = options.scope ?? "project";
|
|
@@ -34107,6 +34453,7 @@ async function diffOutputChecksums(rootBase, lockOutputs) {
|
|
|
34107
34453
|
// src/core/generate/stale-cleanup.ts
|
|
34108
34454
|
init_fs();
|
|
34109
34455
|
init_builtin_targets();
|
|
34456
|
+
init_registry();
|
|
34110
34457
|
async function listFiles2(root, base = root) {
|
|
34111
34458
|
const entries = await readdir(root, { withFileTypes: true });
|
|
34112
34459
|
const files = [];
|
|
@@ -34127,6 +34474,11 @@ function retainedDirs(inactiveTargets, scope) {
|
|
|
34127
34474
|
}
|
|
34128
34475
|
return dirs;
|
|
34129
34476
|
}
|
|
34477
|
+
function primaryEmitted(target34, scope, expected) {
|
|
34478
|
+
const descriptor34 = getBuiltinTargetDefinition(target34) ?? getDescriptor(target34);
|
|
34479
|
+
const primary = getTargetLayout(target34, scope)?.rootInstructionPath ?? descriptor34?.generators.primaryRootInstructionPath;
|
|
34480
|
+
return primary !== void 0 && expected.has(primary);
|
|
34481
|
+
}
|
|
34130
34482
|
async function findStaleGeneratedOutputs(args) {
|
|
34131
34483
|
const expected = new Set(args.expectedPaths);
|
|
34132
34484
|
const stale = /* @__PURE__ */ new Set();
|
|
@@ -34138,7 +34490,13 @@ async function findStaleGeneratedOutputs(args) {
|
|
|
34138
34490
|
const managed = getTargetManagedOutputs(target34, scope);
|
|
34139
34491
|
if (!managed) continue;
|
|
34140
34492
|
for (const file of managed.coOwnedFiles ?? []) coOwned.add(file);
|
|
34141
|
-
for (const file of managed.files)
|
|
34493
|
+
for (const file of managed.files) {
|
|
34494
|
+
if (generated !== null && !generated.has(file)) continue;
|
|
34495
|
+
stale.add(file);
|
|
34496
|
+
}
|
|
34497
|
+
if (primaryEmitted(target34, scope, expected)) {
|
|
34498
|
+
for (const file of managed.supersededFiles ?? []) stale.add(file);
|
|
34499
|
+
}
|
|
34142
34500
|
for (const dir of managed.dirs) {
|
|
34143
34501
|
if (retained.has(dir)) continue;
|
|
34144
34502
|
const absDir = join(args.projectRoot, dir);
|
|
@@ -34578,6 +34936,23 @@ function todayIso() {
|
|
|
34578
34936
|
}
|
|
34579
34937
|
|
|
34580
34938
|
// src/lessons/add-errors.ts
|
|
34939
|
+
var EmptyRuleError = class extends Error {
|
|
34940
|
+
code = "EMPTY_RULE";
|
|
34941
|
+
constructor() {
|
|
34942
|
+
super("Lesson rule must not be empty \u2014 pass one imperative sentence.");
|
|
34943
|
+
this.name = "EmptyRuleError";
|
|
34944
|
+
}
|
|
34945
|
+
};
|
|
34946
|
+
var BroadCommandPatternError = class extends Error {
|
|
34947
|
+
constructor(pattern) {
|
|
34948
|
+
super(
|
|
34949
|
+
`Command pattern ${JSON.stringify(pattern)} matches nearly every command, so it would fire on every recall. Key it on the action instead \u2014 a word-bounded program + subcommand (e.g. "\\bgit commit\\b", "\\brm\\b").`
|
|
34950
|
+
);
|
|
34951
|
+
this.pattern = pattern;
|
|
34952
|
+
this.name = "BroadCommandPatternError";
|
|
34953
|
+
}
|
|
34954
|
+
code = "BROAD_COMMAND_PATTERN";
|
|
34955
|
+
};
|
|
34581
34956
|
var UnknownTopicError = class extends Error {
|
|
34582
34957
|
constructor(topic) {
|
|
34583
34958
|
super(`Unknown topic: ${topic}. Pass allowNewTopic + topicSummary to create it.`);
|
|
@@ -34617,6 +34992,77 @@ var UnrecallableLessonError = class extends Error {
|
|
|
34617
34992
|
code = "UNRECALLABLE_LESSON";
|
|
34618
34993
|
};
|
|
34619
34994
|
|
|
34995
|
+
// src/lessons/trigger-effectiveness.ts
|
|
34996
|
+
function ineffectiveTriggers(graph, triggerIds) {
|
|
34997
|
+
const out2 = [];
|
|
34998
|
+
for (const id of triggerIds) {
|
|
34999
|
+
const trigger = graph.triggers[id];
|
|
35000
|
+
if (trigger === void 0) continue;
|
|
35001
|
+
const reason = ineffectiveReason(trigger.kind, trigger.pattern);
|
|
35002
|
+
if (reason !== null) out2.push({ id, kind: trigger.kind, pattern: trigger.pattern, reason });
|
|
35003
|
+
}
|
|
35004
|
+
return out2;
|
|
35005
|
+
}
|
|
35006
|
+
function ineffectiveReason(kind, pattern) {
|
|
35007
|
+
if (kind === "keyword") {
|
|
35008
|
+
if (tokenize(pattern).length === 0) {
|
|
35009
|
+
return "keyword has no matchable token after stopword filtering \u2014 it cannot fire on the mandatory --file/--cmd recall path";
|
|
35010
|
+
}
|
|
35011
|
+
if (keywordNeedleLosesTokens(pattern)) {
|
|
35012
|
+
return "keyword contains stopwords/short words, so its needle can never appear as a contiguous run on the mandatory --file/--cmd recall path";
|
|
35013
|
+
}
|
|
35014
|
+
return null;
|
|
35015
|
+
}
|
|
35016
|
+
if (kind === "command_pattern") {
|
|
35017
|
+
let valid = true;
|
|
35018
|
+
try {
|
|
35019
|
+
new RegExp(pattern);
|
|
35020
|
+
} catch {
|
|
35021
|
+
valid = false;
|
|
35022
|
+
}
|
|
35023
|
+
if (!valid) {
|
|
35024
|
+
return "invalid regex \u2014 recall compiles it with new RegExp and swallows the throw as a non-match, so it never fires";
|
|
35025
|
+
}
|
|
35026
|
+
if (!isSafeRegexPattern(pattern)) {
|
|
35027
|
+
return "regex is outside the provably-linear engine \u2014 recall skips it (ReDoS guard), so it never fires";
|
|
35028
|
+
}
|
|
35029
|
+
return null;
|
|
35030
|
+
}
|
|
35031
|
+
return null;
|
|
35032
|
+
}
|
|
35033
|
+
function blockingDeadTriggers(graph, triggerIds) {
|
|
35034
|
+
return ineffectiveTriggers(graph, triggerIds).filter((t) => t.kind !== "command_pattern");
|
|
35035
|
+
}
|
|
35036
|
+
|
|
35037
|
+
// src/lessons/add-gates.ts
|
|
35038
|
+
function assertRuleShape(rule) {
|
|
35039
|
+
const trimmed = rule.trim();
|
|
35040
|
+
if (trimmed.length === 0) throw new EmptyRuleError();
|
|
35041
|
+
if (trimmed.length > MAX_RULE_LENGTH) throw new RuleTooLongError(trimmed.length, MAX_RULE_LENGTH);
|
|
35042
|
+
return trimmed;
|
|
35043
|
+
}
|
|
35044
|
+
function skipsTriggerGates(input, options) {
|
|
35045
|
+
return options.allowNoTrigger === true || input.scope === "always";
|
|
35046
|
+
}
|
|
35047
|
+
function countInputTriggers(triggers) {
|
|
35048
|
+
return (triggers.files?.length ?? 0) + (triggers.commands?.length ?? 0) + (triggers.keywords?.length ?? 0);
|
|
35049
|
+
}
|
|
35050
|
+
function assertTriggerInputs(input, options, existingTriggerCount) {
|
|
35051
|
+
if (!skipsTriggerGates(input, options) && countInputTriggers(input.triggers) === 0 && existingTriggerCount === 0) {
|
|
35052
|
+
throw new NoTriggerError();
|
|
35053
|
+
}
|
|
35054
|
+
if (options.allowNoTrigger !== true) {
|
|
35055
|
+
const broad = (input.triggers.commands ?? []).find(isBroadCommandPattern);
|
|
35056
|
+
if (broad !== void 0) throw new BroadCommandPatternError(broad);
|
|
35057
|
+
}
|
|
35058
|
+
}
|
|
35059
|
+
function assertRecallable(graph, resultingTriggers) {
|
|
35060
|
+
const blockingDead = blockingDeadTriggers(graph, resultingTriggers);
|
|
35061
|
+
if (resultingTriggers.length > 0 && blockingDead.length === resultingTriggers.length) {
|
|
35062
|
+
throw new UnrecallableLessonError(blockingDead);
|
|
35063
|
+
}
|
|
35064
|
+
}
|
|
35065
|
+
|
|
34620
35066
|
// src/lessons/capture-guardrails.ts
|
|
34621
35067
|
var WIDE_GLOB_MATCH_COUNT = 40;
|
|
34622
35068
|
var MAX_RECOMMENDED_TRIGGERS = 8;
|
|
@@ -34655,7 +35101,7 @@ function inspectCapturedLesson(graph, lessonId, knownPaths) {
|
|
|
34655
35101
|
if (lowSignal.length > 0) {
|
|
34656
35102
|
warnings.push({
|
|
34657
35103
|
code: "LOW_SIGNAL_KEYWORD",
|
|
34658
|
-
message: `Lesson "${lessonId}" has long keyword trigger(s) (${lowSignal.join(", ")}); recall matches a keyword only as a
|
|
35104
|
+
message: `Lesson "${lessonId}" has long keyword trigger(s) (${lowSignal.join(", ")}); recall matches a keyword only as a contiguous token-run in --keyword or the file/command, so a pattern past ${MAX_RECOMMENDED_KEYWORD_TOKENS} tokens rarely fires \u2014 use a short distinctive phrase.`
|
|
34659
35105
|
});
|
|
34660
35106
|
}
|
|
34661
35107
|
const stopworded = triggers.filter((t) => t.kind === "keyword" && keywordNeedleLosesTokens(t.pattern)).map((t) => t.pattern);
|
|
@@ -34716,7 +35162,7 @@ function jaccard(a, b) {
|
|
|
34716
35162
|
|
|
34717
35163
|
// src/utils/filesystem/process-lock.ts
|
|
34718
35164
|
init_errors();
|
|
34719
|
-
var DEFAULT_STALE_MS =
|
|
35165
|
+
var DEFAULT_STALE_MS = 6 * 60 * 60 * 1e3;
|
|
34720
35166
|
var DEFAULT_RETRIES = 30;
|
|
34721
35167
|
var DEFAULT_RETRY_DELAY_MS = 200;
|
|
34722
35168
|
var YOUNG_LOCK_GRACE_MS = 2e3;
|
|
@@ -34801,10 +35247,9 @@ async function inspectLock(lockPath) {
|
|
|
34801
35247
|
}
|
|
34802
35248
|
function isStale(meta, staleMs) {
|
|
34803
35249
|
if (!meta) return true;
|
|
34804
|
-
const
|
|
34805
|
-
if (
|
|
34806
|
-
|
|
34807
|
-
return !isProcessAlive(meta.pid);
|
|
35250
|
+
const sameHost = !meta.hostname || meta.hostname === getHostname();
|
|
35251
|
+
if (sameHost && !isProcessAlive(meta.pid)) return true;
|
|
35252
|
+
return Date.now() - meta.started > staleMs;
|
|
34808
35253
|
}
|
|
34809
35254
|
function isProcessAlive(pid) {
|
|
34810
35255
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
@@ -34881,52 +35326,7 @@ async function mutateLessonsGraph(projectRoot, mutator, options = {}) {
|
|
|
34881
35326
|
return mutateLessonsGraphLocked(projectRoot, mutator, options);
|
|
34882
35327
|
}
|
|
34883
35328
|
|
|
34884
|
-
// src/lessons/trigger-effectiveness.ts
|
|
34885
|
-
function ineffectiveTriggers(graph, triggerIds) {
|
|
34886
|
-
const out2 = [];
|
|
34887
|
-
for (const id of triggerIds) {
|
|
34888
|
-
const trigger = graph.triggers[id];
|
|
34889
|
-
if (trigger === void 0) continue;
|
|
34890
|
-
const reason = ineffectiveReason(trigger.kind, trigger.pattern);
|
|
34891
|
-
if (reason !== null) out2.push({ id, kind: trigger.kind, pattern: trigger.pattern, reason });
|
|
34892
|
-
}
|
|
34893
|
-
return out2;
|
|
34894
|
-
}
|
|
34895
|
-
function ineffectiveReason(kind, pattern) {
|
|
34896
|
-
if (kind === "keyword") {
|
|
34897
|
-
if (tokenize(pattern).length === 0) {
|
|
34898
|
-
return "keyword has no matchable token after stopword filtering \u2014 it cannot fire on the mandatory --file/--cmd recall path";
|
|
34899
|
-
}
|
|
34900
|
-
if (keywordNeedleLosesTokens(pattern)) {
|
|
34901
|
-
return "keyword contains stopwords/short words, so its needle can never appear as a contiguous run on the mandatory --file/--cmd recall path";
|
|
34902
|
-
}
|
|
34903
|
-
return null;
|
|
34904
|
-
}
|
|
34905
|
-
if (kind === "command_pattern") {
|
|
34906
|
-
let valid = true;
|
|
34907
|
-
try {
|
|
34908
|
-
new RegExp(pattern);
|
|
34909
|
-
} catch {
|
|
34910
|
-
valid = false;
|
|
34911
|
-
}
|
|
34912
|
-
if (!valid) {
|
|
34913
|
-
return "invalid regex \u2014 recall compiles it with new RegExp and swallows the throw as a non-match, so it never fires";
|
|
34914
|
-
}
|
|
34915
|
-
if (!isSafeRegexPattern(pattern)) {
|
|
34916
|
-
return "regex is outside the provably-linear engine \u2014 recall skips it (ReDoS guard), so it never fires";
|
|
34917
|
-
}
|
|
34918
|
-
return null;
|
|
34919
|
-
}
|
|
34920
|
-
return null;
|
|
34921
|
-
}
|
|
34922
|
-
function blockingDeadTriggers(graph, triggerIds) {
|
|
34923
|
-
return ineffectiveTriggers(graph, triggerIds).filter((t) => t.kind !== "command_pattern");
|
|
34924
|
-
}
|
|
34925
|
-
|
|
34926
35329
|
// src/lessons/add.ts
|
|
34927
|
-
function countInputTriggers(triggers) {
|
|
34928
|
-
return (triggers.files?.length ?? 0) + (triggers.commands?.length ?? 0) + (triggers.keywords?.length ?? 0);
|
|
34929
|
-
}
|
|
34930
35330
|
async function addLesson(projectRoot, input, options = {}) {
|
|
34931
35331
|
return mutateLessonsGraph(projectRoot, (graph) => addLessonInto(graph, input, options), {
|
|
34932
35332
|
retries: options.retries
|
|
@@ -34934,10 +35334,7 @@ async function addLesson(projectRoot, input, options = {}) {
|
|
|
34934
35334
|
}
|
|
34935
35335
|
function addLessonInto(graph, input, options) {
|
|
34936
35336
|
const ruleKey2 = normalizeRule2(input.rule);
|
|
34937
|
-
const trimmedRule = input.rule
|
|
34938
|
-
if (trimmedRule.length > MAX_RULE_LENGTH) {
|
|
34939
|
-
throw new RuleTooLongError(trimmedRule.length, MAX_RULE_LENGTH);
|
|
34940
|
-
}
|
|
35337
|
+
const trimmedRule = assertRuleShape(input.rule);
|
|
34941
35338
|
const existingId = findExistingLessonByRule(graph, ruleKey2);
|
|
34942
35339
|
const isNewTopic = graph.topics[input.topic] === void 0;
|
|
34943
35340
|
if (isNewTopic) {
|
|
@@ -34947,29 +35344,23 @@ function addLessonInto(graph, input, options) {
|
|
|
34947
35344
|
}
|
|
34948
35345
|
graph.topics[input.topic] = { summary: options.topicSummary };
|
|
34949
35346
|
}
|
|
34950
|
-
const
|
|
34951
|
-
|
|
34952
|
-
const existingTriggers = existingId !== null ? graph.lessons[existingId]?.triggers.length ?? 0 : 0;
|
|
34953
|
-
if (countInputTriggers(input.triggers) === 0 && existingTriggers === 0) {
|
|
34954
|
-
throw new NoTriggerError();
|
|
34955
|
-
}
|
|
34956
|
-
}
|
|
35347
|
+
const existing = existingId !== null ? graph.lessons[existingId] : void 0;
|
|
35348
|
+
assertTriggerInputs(input, options, existing?.triggers.length ?? 0);
|
|
34957
35349
|
const { triggerIds, newTriggerIds } = mergeTriggers(graph, input.triggers);
|
|
34958
|
-
if (!
|
|
34959
|
-
|
|
34960
|
-
|
|
34961
|
-
|
|
34962
|
-
|
|
34963
|
-
}
|
|
35350
|
+
if (!skipsTriggerGates(input, options)) {
|
|
35351
|
+
assertRecallable(
|
|
35352
|
+
graph,
|
|
35353
|
+
existing === void 0 ? triggerIds : union(existing.triggers, triggerIds)
|
|
35354
|
+
);
|
|
34964
35355
|
}
|
|
34965
35356
|
if (existingId !== null) {
|
|
34966
|
-
const
|
|
35357
|
+
const existing2 = graph.lessons[existingId];
|
|
34967
35358
|
graph.lessons[existingId] = {
|
|
34968
|
-
...
|
|
34969
|
-
topics: union(
|
|
34970
|
-
triggers: union(
|
|
34971
|
-
evidence: union(
|
|
34972
|
-
...
|
|
35359
|
+
...existing2,
|
|
35360
|
+
topics: union(existing2.topics, [input.topic]),
|
|
35361
|
+
triggers: union(existing2.triggers, triggerIds),
|
|
35362
|
+
evidence: union(existing2.evidence, input.evidence ?? []),
|
|
35363
|
+
...existing2.rationale === void 0 && input.rationale !== void 0 ? { rationale: input.rationale } : {},
|
|
34973
35364
|
// Re-capturing a rule with --scope always promotes it to always-on.
|
|
34974
35365
|
...input.scope === "always" ? { scope: "always" } : {}
|
|
34975
35366
|
};
|
|
@@ -35150,14 +35541,16 @@ function deriveHaystackTokens(query) {
|
|
|
35150
35541
|
if (query.command !== void 0) parts.push(query.command);
|
|
35151
35542
|
if (parts.length === 0) return [];
|
|
35152
35543
|
const out2 = [];
|
|
35153
|
-
for (const raw of parts.join(" ")
|
|
35154
|
-
if (raw.length === 0) continue;
|
|
35544
|
+
for (const raw of splitTokens(parts.join(" "))) {
|
|
35155
35545
|
out2.push(raw.toLowerCase());
|
|
35156
35546
|
const sub = raw.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").toLowerCase().split(" ").filter((t) => t.length > 0);
|
|
35157
35547
|
if (sub.length > 1) out2.push(...sub);
|
|
35158
35548
|
}
|
|
35159
35549
|
return out2;
|
|
35160
35550
|
}
|
|
35551
|
+
function splitTokens(text) {
|
|
35552
|
+
return text.split(/[^A-Za-z0-9]+/).filter((t) => t.length > 0);
|
|
35553
|
+
}
|
|
35161
35554
|
function containsRun(needle, hay) {
|
|
35162
35555
|
if (needle.length === 0) return false;
|
|
35163
35556
|
for (let i = 0; i + needle.length <= hay.length; i += 1) {
|
|
@@ -35173,10 +35566,11 @@ function containsRun(needle, hay) {
|
|
|
35173
35566
|
return false;
|
|
35174
35567
|
}
|
|
35175
35568
|
function keywordMatches(pattern, query) {
|
|
35176
|
-
|
|
35569
|
+
const needle = tokenize(pattern);
|
|
35570
|
+
if (query.keyword !== void 0 && containsRun(needle, splitTokens(query.keyword.toLowerCase()))) {
|
|
35177
35571
|
return true;
|
|
35178
35572
|
}
|
|
35179
|
-
return containsRun(
|
|
35573
|
+
return containsRun(needle, deriveHaystackTokens(query));
|
|
35180
35574
|
}
|
|
35181
35575
|
|
|
35182
35576
|
// src/lessons/query.ts
|
|
@@ -35242,6 +35636,15 @@ function buildFanout(graph) {
|
|
|
35242
35636
|
}
|
|
35243
35637
|
return fanout;
|
|
35244
35638
|
}
|
|
35639
|
+
var KEYWORD_NARROWNESS = 0.4;
|
|
35640
|
+
function buildNarrowness(graph) {
|
|
35641
|
+
const narrowness = /* @__PURE__ */ new Map();
|
|
35642
|
+
for (const [id, trigger] of Object.entries(graph.triggers)) {
|
|
35643
|
+
if (trigger.kind === "file_glob") narrowness.set(id, globNarrowness(trigger.pattern));
|
|
35644
|
+
else narrowness.set(id, trigger.kind === "keyword" ? KEYWORD_NARROWNESS : 1);
|
|
35645
|
+
}
|
|
35646
|
+
return narrowness;
|
|
35647
|
+
}
|
|
35245
35648
|
function buildTopicCoherence(matches) {
|
|
35246
35649
|
const topicCount = /* @__PURE__ */ new Map();
|
|
35247
35650
|
for (const { lesson } of matches) {
|
|
@@ -35260,7 +35663,7 @@ function buildTopicCoherence(matches) {
|
|
|
35260
35663
|
var DEFAULT_RECALL_LIMIT = 10;
|
|
35261
35664
|
var DEFAULT_RECALL_MAX_TOKENS = 400;
|
|
35262
35665
|
var RRF_K = 60;
|
|
35263
|
-
var SPECIFICITY_WEIGHT =
|
|
35666
|
+
var SPECIFICITY_WEIGHT = 5;
|
|
35264
35667
|
var TOPIC_COHERENCE_WEIGHT = 2;
|
|
35265
35668
|
var BM25_WEIGHT = 1;
|
|
35266
35669
|
var EFFECTIVENESS_WEIGHT = 1;
|
|
@@ -35287,12 +35690,15 @@ function rankLessons(graph, query, matches, options = {}) {
|
|
|
35287
35690
|
const terms = queryTerms(query);
|
|
35288
35691
|
const corpus = buildCorpus(graph);
|
|
35289
35692
|
const fanout = buildFanout(graph);
|
|
35693
|
+
const narrowness = buildNarrowness(graph);
|
|
35290
35694
|
const coherence = buildTopicCoherence(matches);
|
|
35291
35695
|
const matchedTriggerIds = collectMatchedTriggerIds(graph, query);
|
|
35292
35696
|
const scored = matches.map(({ id, lesson }) => {
|
|
35293
35697
|
const hitTriggers = lesson.triggers.filter((t) => matchedTriggerIds.has(t));
|
|
35294
35698
|
let specificity = 0;
|
|
35295
|
-
for (const t of hitTriggers)
|
|
35699
|
+
for (const t of hitTriggers) {
|
|
35700
|
+
specificity = Math.max(specificity, (narrowness.get(t) ?? 1) / fanout.get(t));
|
|
35701
|
+
}
|
|
35296
35702
|
return {
|
|
35297
35703
|
id,
|
|
35298
35704
|
lesson,
|
|
@@ -35404,9 +35810,9 @@ var LINE_REFS = String.raw`L\d+(?:\s*,\s*L\d+)*`;
|
|
|
35404
35810
|
var LINE_REF_PATTERNS = [
|
|
35405
35811
|
new RegExp(String.raw`\s*\bSee\s+${LINE_REFS}\.?`, "g"),
|
|
35406
35812
|
// " See L128." / " See L140, L149"
|
|
35407
|
-
new RegExp(String.raw`\s*\((?:${LINE_REFS})\)
|
|
35813
|
+
new RegExp(String.raw`\s*\((?:${LINE_REFS})\)`, "g"),
|
|
35408
35814
|
// " (L174)" / " (L92, L163)"
|
|
35409
|
-
new RegExp(String.raw`\s*\[(?:${LINE_REFS})\]
|
|
35815
|
+
new RegExp(String.raw`\s*\[(?:${LINE_REFS})\]`, "g")
|
|
35410
35816
|
// " [L161, L208]"
|
|
35411
35817
|
];
|
|
35412
35818
|
var ALSO_RELEVANT_PATTERN = /\s*\(also relevant[^)]*\)\s*/g;
|
|
@@ -35440,42 +35846,7 @@ async function stripMarkersInGraph(projectRoot, options = {}) {
|
|
|
35440
35846
|
});
|
|
35441
35847
|
return { changedIds, changedCount: changedIds.length };
|
|
35442
35848
|
}
|
|
35443
|
-
|
|
35444
|
-
var RECALL_HOOK_TOOL_MATCHER = "Edit|Write|Bash";
|
|
35445
|
-
var RECALL_EVENTS = [
|
|
35446
|
-
{ event: "PreToolUse", matcher: RECALL_HOOK_TOOL_MATCHER },
|
|
35447
|
-
{ event: "PostToolUse", matcher: RECALL_HOOK_TOOL_MATCHER },
|
|
35448
|
-
{ event: "UserPromptSubmit", matcher: "*" },
|
|
35449
|
-
// Capture-on-failure nudge (see capture-nudge.ts). BEST-EFFORT: only Claude
|
|
35450
|
-
// Code's passthrough hooks emit it; whitelist targets drop it without warning
|
|
35451
|
-
// (BEST_EFFORT_HOOK_EVENTS). PostToolUse is success-only, so failures need this.
|
|
35452
|
-
{ event: "PostToolUseFailure", matcher: "*" },
|
|
35453
|
-
// Reset recall dedup after a context compaction/clear (see hook.ts SessionStart).
|
|
35454
|
-
// BEST-EFFORT: targets that can't represent SessionStart just keep dedup as-is.
|
|
35455
|
-
{ event: "SessionStart", matcher: "*" }
|
|
35456
|
-
];
|
|
35457
|
-
function injectEvent(doc, event, matcher) {
|
|
35458
|
-
const existing = doc.get(event);
|
|
35459
|
-
const seq = existing instanceof YAMLSeq ? existing : new YAMLSeq();
|
|
35460
|
-
const present = seq.items.some(
|
|
35461
|
-
(item) => item instanceof YAMLMap && item.get("command") === RECALL_HOOK_COMMAND
|
|
35462
|
-
);
|
|
35463
|
-
if (present) return false;
|
|
35464
|
-
seq.add(doc.createNode({ matcher, type: "command", command: RECALL_HOOK_COMMAND }));
|
|
35465
|
-
doc.set(event, seq);
|
|
35466
|
-
return true;
|
|
35467
|
-
}
|
|
35468
|
-
function injectRecallHook(projectRoot) {
|
|
35469
|
-
const path = join(projectRoot, ".agentsmesh", "hooks.yaml");
|
|
35470
|
-
if (!existsSync(path)) return false;
|
|
35471
|
-
const doc = parseDocument(readFileSync(path, "utf8"));
|
|
35472
|
-
let changed = false;
|
|
35473
|
-
for (const { event, matcher } of RECALL_EVENTS) {
|
|
35474
|
-
if (injectEvent(doc, event, matcher)) changed = true;
|
|
35475
|
-
}
|
|
35476
|
-
if (changed) writeFileSync(path, String(doc), "utf8");
|
|
35477
|
-
return changed;
|
|
35478
|
-
}
|
|
35849
|
+
init_recall_hook_scaffold();
|
|
35479
35850
|
|
|
35480
35851
|
// src/lessons/merge-driver-setup.ts
|
|
35481
35852
|
var LESSONS_MERGE_DRIVER = "agentsmesh-lessons";
|