agentsmesh 0.33.0 → 0.35.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 +30 -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 +788 -402
- package/dist/index.js.map +1 -1
- package/dist/{init-rvKjGLTB.d.ts → init-C3pMnqoU.d.ts} +10 -8
- package/dist/lessons.d.ts +2 -2
- package/dist/lessons.js +1159 -983
- 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,74 @@ var init_command_skill = __esm({
|
|
|
508
524
|
LEGACY_CODEX_COMMAND_SKILL_PREFIX = "ab-command-";
|
|
509
525
|
}
|
|
510
526
|
});
|
|
527
|
+
function removeEvent(doc, event) {
|
|
528
|
+
const existing = doc.get(event);
|
|
529
|
+
if (!(existing instanceof YAMLSeq)) return false;
|
|
530
|
+
const kept = existing.items.filter(
|
|
531
|
+
(item) => !(item instanceof YAMLMap && item.get("command") === RECALL_HOOK_COMMAND)
|
|
532
|
+
);
|
|
533
|
+
if (kept.length === existing.items.length) return false;
|
|
534
|
+
if (kept.length === 0) doc.delete(event);
|
|
535
|
+
else existing.items = kept;
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
function injectEvent(doc, event, matcher) {
|
|
539
|
+
const existing = doc.get(event);
|
|
540
|
+
const seq = existing instanceof YAMLSeq ? existing : new YAMLSeq();
|
|
541
|
+
const present = seq.items.some(
|
|
542
|
+
(item) => item instanceof YAMLMap && item.get("command") === RECALL_HOOK_COMMAND
|
|
543
|
+
);
|
|
544
|
+
if (present) return false;
|
|
545
|
+
seq.add(doc.createNode({ matcher, type: "command", command: RECALL_HOOK_COMMAND }));
|
|
546
|
+
doc.set(event, seq);
|
|
547
|
+
return true;
|
|
548
|
+
}
|
|
549
|
+
function injectRecallHook(projectRoot) {
|
|
550
|
+
const path = join(projectRoot, ".agentsmesh", "hooks.yaml");
|
|
551
|
+
if (!existsSync(path)) return false;
|
|
552
|
+
const doc = parseDocument(readFileSync(path, "utf8"));
|
|
553
|
+
let changed = false;
|
|
554
|
+
for (const { event, matcher } of RECALL_EVENTS) {
|
|
555
|
+
if (injectEvent(doc, event, matcher)) changed = true;
|
|
556
|
+
}
|
|
557
|
+
for (const event of RETIRED_EVENTS) {
|
|
558
|
+
if (removeEvent(doc, event)) changed = true;
|
|
559
|
+
}
|
|
560
|
+
if (changed) writeFileSync(path, String(doc), "utf8");
|
|
561
|
+
return changed;
|
|
562
|
+
}
|
|
563
|
+
var RECALL_HOOK_COMMAND, RECALL_HOOK_TOOL_MATCHER, RECALL_EVENTS, RETIRED_EVENTS;
|
|
564
|
+
var init_recall_hook_scaffold = __esm({
|
|
565
|
+
"src/lessons/recall-hook-scaffold.ts"() {
|
|
566
|
+
RECALL_HOOK_COMMAND = "agentsmesh lessons hook";
|
|
567
|
+
RECALL_HOOK_TOOL_MATCHER = "Edit|Write|Bash";
|
|
568
|
+
RECALL_EVENTS = [
|
|
569
|
+
{ event: "PreToolUse", matcher: RECALL_HOOK_TOOL_MATCHER },
|
|
570
|
+
{ event: "UserPromptSubmit", matcher: "*" },
|
|
571
|
+
// Capture-on-failure nudge (see capture-nudge.ts). BEST-EFFORT: only Claude
|
|
572
|
+
// Code's passthrough hooks emit it; whitelist targets drop it without warning
|
|
573
|
+
// (BEST_EFFORT_HOOK_EVENTS). PostToolUse is success-only, so failures need this.
|
|
574
|
+
{ event: "PostToolUseFailure", matcher: "*" },
|
|
575
|
+
// Reset recall dedup after a context compaction/clear (see hook.ts SessionStart).
|
|
576
|
+
// BEST-EFFORT: targets that can't represent SessionStart just keep dedup as-is.
|
|
577
|
+
{ event: "SessionStart", matcher: "*" }
|
|
578
|
+
];
|
|
579
|
+
RETIRED_EVENTS = ["PostToolUse"];
|
|
580
|
+
}
|
|
581
|
+
});
|
|
511
582
|
|
|
512
583
|
// src/core/hook-types.ts
|
|
584
|
+
function isBestEffortHookEvent(event, entries) {
|
|
585
|
+
if (!BEST_EFFORT_HOOK_EVENTS.has(event)) return false;
|
|
586
|
+
if (!Array.isArray(entries)) return true;
|
|
587
|
+
return entries.every(
|
|
588
|
+
(entry) => typeof entry === "object" && entry !== null && typeof entry.command === "string" && entry.command.includes(RECALL_HOOK_COMMAND)
|
|
589
|
+
);
|
|
590
|
+
}
|
|
513
591
|
var BEST_EFFORT_HOOK_EVENTS;
|
|
514
592
|
var init_hook_types = __esm({
|
|
515
593
|
"src/core/hook-types.ts"() {
|
|
594
|
+
init_recall_hook_scaffold();
|
|
516
595
|
BEST_EFFORT_HOOK_EVENTS = /* @__PURE__ */ new Set([
|
|
517
596
|
"UserPromptSubmit",
|
|
518
597
|
"PostToolUseFailure",
|
|
@@ -698,7 +777,7 @@ var init_conf_merge = __esm({
|
|
|
698
777
|
});
|
|
699
778
|
|
|
700
779
|
// src/core/errors.ts
|
|
701
|
-
var AgentsMeshError, ConfigNotFoundError, ConfigValidationError, TargetNotFoundError, ImportError, GenerationError, RemoteFetchError, LockAcquisitionError, FileSystemError;
|
|
780
|
+
var AgentsMeshError, ConfigNotFoundError, ConfigValidationError, CanonicalParseError, TargetNotFoundError, ImportError, GenerationError, RemoteFetchError, LockAcquisitionError, FileSystemError;
|
|
702
781
|
var init_errors = __esm({
|
|
703
782
|
"src/core/errors.ts"() {
|
|
704
783
|
AgentsMeshError = class extends Error {
|
|
@@ -733,6 +812,21 @@ var init_errors = __esm({
|
|
|
733
812
|
this.issues = issues;
|
|
734
813
|
}
|
|
735
814
|
};
|
|
815
|
+
CanonicalParseError = class extends AgentsMeshError {
|
|
816
|
+
path;
|
|
817
|
+
constructor(path, cause) {
|
|
818
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
819
|
+
super(
|
|
820
|
+
"AM_CONFIG_INVALID",
|
|
821
|
+
`Invalid canonical file ${path}: ${detail}. Fix the syntax and try again.`,
|
|
822
|
+
{
|
|
823
|
+
cause
|
|
824
|
+
}
|
|
825
|
+
);
|
|
826
|
+
this.name = "CanonicalParseError";
|
|
827
|
+
this.path = path;
|
|
828
|
+
}
|
|
829
|
+
};
|
|
736
830
|
TargetNotFoundError = class extends AgentsMeshError {
|
|
737
831
|
target;
|
|
738
832
|
constructor(target34, options) {
|
|
@@ -806,6 +900,11 @@ function normalizeLineEndings(content) {
|
|
|
806
900
|
function executableModeFor(path) {
|
|
807
901
|
return EXECUTABLE_SCRIPT_EXTENSIONS.has(extname(path).toLowerCase()) ? 493 : void 0;
|
|
808
902
|
}
|
|
903
|
+
function normalizeTextPayload(path, content) {
|
|
904
|
+
if (!shouldNormalizeLineEndings(path)) return content;
|
|
905
|
+
const withoutBom = content.startsWith(UTF8_BOM) ? content.slice(UTF8_BOM.length) : content;
|
|
906
|
+
return normalizeLineEndings(withoutBom);
|
|
907
|
+
}
|
|
809
908
|
var UTF8_BOM, TEXT_EXTENSIONS, TEXT_DOTFILES, EXECUTABLE_SCRIPT_EXTENSIONS;
|
|
810
909
|
var init_fs_text_encoding = __esm({
|
|
811
910
|
"src/utils/filesystem/fs-text-encoding.ts"() {
|
|
@@ -1306,10 +1405,8 @@ function stripManagedBlock(content, start, end) {
|
|
|
1306
1405
|
return content.replace(managedBlockPattern(start, end), "").trim();
|
|
1307
1406
|
}
|
|
1308
1407
|
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() };
|
|
1408
|
+
const split = splitFrontmatter(content);
|
|
1409
|
+
return split === null ? { prefix: "", body: content.trim() } : { prefix: split.prefix, body: split.body };
|
|
1313
1410
|
}
|
|
1314
1411
|
function insertAtBodyTop(content, block) {
|
|
1315
1412
|
const { prefix, body } = splitFrontmatterPrefix(content);
|
|
@@ -1410,6 +1507,7 @@ function extractEmbeddedRules(content) {
|
|
|
1410
1507
|
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
1508
|
var init_managed_blocks = __esm({
|
|
1412
1509
|
"src/targets/projection/managed-blocks.ts"() {
|
|
1510
|
+
init_markdown();
|
|
1413
1511
|
ROOT_CONTRACT_START = "<!-- agentsmesh:root-generation-contract:start -->";
|
|
1414
1512
|
ROOT_CONTRACT_END = "<!-- agentsmesh:root-generation-contract:end -->";
|
|
1415
1513
|
LESSONS_CONTRACT_START = "<!-- agentsmesh:lessons-contract:start -->";
|
|
@@ -1577,10 +1675,17 @@ async function serializeImportedCommandWithFallback(destinationPath, imported, b
|
|
|
1577
1675
|
})();
|
|
1578
1676
|
const description = imported.hasDescription ? imported.description ?? "" : typeof existingFrontmatter.description === "string" ? existingFrontmatter.description : "";
|
|
1579
1677
|
const allowedTools = imported.hasAllowedTools ? imported.allowedTools ?? [] : existingAllowedTools;
|
|
1678
|
+
const {
|
|
1679
|
+
description: _d,
|
|
1680
|
+
"allowed-tools": _a,
|
|
1681
|
+
allowedTools: _c,
|
|
1682
|
+
...preserved
|
|
1683
|
+
} = existingFrontmatter;
|
|
1580
1684
|
return serializeFrontmatter(
|
|
1581
1685
|
{
|
|
1582
1686
|
description,
|
|
1583
|
-
"allowed-tools": allowedTools
|
|
1687
|
+
"allowed-tools": allowedTools,
|
|
1688
|
+
...preserved
|
|
1584
1689
|
},
|
|
1585
1690
|
body.trim() || ""
|
|
1586
1691
|
);
|
|
@@ -2478,6 +2583,26 @@ var init_link_rebaser_resolution = __esm({
|
|
|
2478
2583
|
}
|
|
2479
2584
|
});
|
|
2480
2585
|
|
|
2586
|
+
// src/core/reference/link-uri-encoding.ts
|
|
2587
|
+
function decodeLinkPath(token, role) {
|
|
2588
|
+
if (role !== "markdown-link-dest" || !token.includes("%")) return token;
|
|
2589
|
+
try {
|
|
2590
|
+
return decodeURIComponent(token);
|
|
2591
|
+
} catch {
|
|
2592
|
+
return token;
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
function encodeLinkPath(path, enabled) {
|
|
2596
|
+
if (!enabled) return path;
|
|
2597
|
+
return path.split("/").map(
|
|
2598
|
+
(segment) => segment === "" || segment === "." || segment === ".." ? segment : encodeURIComponent(segment)
|
|
2599
|
+
).join("/");
|
|
2600
|
+
}
|
|
2601
|
+
var init_link_uri_encoding = __esm({
|
|
2602
|
+
"src/core/reference/link-uri-encoding.ts"() {
|
|
2603
|
+
}
|
|
2604
|
+
});
|
|
2605
|
+
|
|
2481
2606
|
// src/core/reference/link-token-guards.ts
|
|
2482
2607
|
function isTildeHomeRelativePathToken(fullContent, matchOffset, matchText) {
|
|
2483
2608
|
if (matchOffset >= 2 && fullContent[matchOffset - 2] === "~" && fullContent[matchOffset - 1] === "/") {
|
|
@@ -2607,10 +2732,11 @@ function rewriteFileLinks(input) {
|
|
|
2607
2732
|
const { candidate: punctStripped, suffix } = stripTrailingPunctuation(match);
|
|
2608
2733
|
if (!punctStripped) return match;
|
|
2609
2734
|
const lineNumMatch = LINE_NUMBER_SUFFIX.exec(punctStripped);
|
|
2610
|
-
const
|
|
2735
|
+
const rawCandidate = lineNumMatch ? punctStripped.slice(0, lineNumMatch.index) : punctStripped;
|
|
2611
2736
|
const lineNumSuffix = lineNumMatch ? lineNumMatch[0] : "";
|
|
2612
|
-
if (!
|
|
2613
|
-
const tokenContext = getTokenContext(fullContent, offset, offset +
|
|
2737
|
+
if (!rawCandidate) return match;
|
|
2738
|
+
const tokenContext = getTokenContext(fullContent, offset, offset + rawCandidate.length);
|
|
2739
|
+
const candidate = decodeLinkPath(rawCandidate, tokenContext.role);
|
|
2614
2740
|
if (tokenContext.role !== "markdown-link-dest" && WINDOWS_ABSOLUTE_PATH.test(candidate)) {
|
|
2615
2741
|
return match;
|
|
2616
2742
|
}
|
|
@@ -2680,7 +2806,7 @@ function rewriteFileLinks(input) {
|
|
|
2680
2806
|
const targetTop = targetFromRoot.split("/").filter(Boolean)[0] ?? "";
|
|
2681
2807
|
const tokenIsCanonicalMesh = normalizeSeparators(candidate).startsWith(".agentsmesh/");
|
|
2682
2808
|
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,
|
|
2809
|
+
const forceRelative = preferRelativeProseInSameSurface || tokenContext.role === "markdown-link-dest" || isMarkdownLinkDestinationToken(fullContent, offset, rawCandidate);
|
|
2684
2810
|
const rewritten = formatLinkPathForDestination(
|
|
2685
2811
|
input.projectRoot,
|
|
2686
2812
|
input.destinationFile,
|
|
@@ -2697,7 +2823,7 @@ function rewriteFileLinks(input) {
|
|
|
2697
2823
|
}
|
|
2698
2824
|
);
|
|
2699
2825
|
if (!rewritten) return match;
|
|
2700
|
-
return `${rewritten}${lineNumSuffix}${suffix}`;
|
|
2826
|
+
return `${encodeLinkPath(rewritten, candidate !== rawCandidate)}${lineNumSuffix}${suffix}`;
|
|
2701
2827
|
});
|
|
2702
2828
|
return { content, missing: [...missing] };
|
|
2703
2829
|
}
|
|
@@ -2707,6 +2833,7 @@ var init_link_rebaser = __esm({
|
|
|
2707
2833
|
init_link_rebaser_helpers();
|
|
2708
2834
|
init_link_rebaser_output();
|
|
2709
2835
|
init_link_rebaser_resolution();
|
|
2836
|
+
init_link_uri_encoding();
|
|
2710
2837
|
init_link_token_guards();
|
|
2711
2838
|
init_link_token_context();
|
|
2712
2839
|
}
|
|
@@ -3590,7 +3717,7 @@ function unsupportedHookEventNames(hooks, supportedEvents) {
|
|
|
3590
3717
|
if (!hooks) return [];
|
|
3591
3718
|
const supported = new Set(supportedEvents);
|
|
3592
3719
|
return Object.keys(hooks).filter(
|
|
3593
|
-
(event) => !supported.has(event) && !
|
|
3720
|
+
(event) => !supported.has(event) && !isBestEffortHookEvent(event, hooks[event])
|
|
3594
3721
|
);
|
|
3595
3722
|
}
|
|
3596
3723
|
function createUnsupportedHookWarning(event, target34, supportedEvents, options) {
|
|
@@ -7127,6 +7254,21 @@ var init_embedded_rules = __esm({
|
|
|
7127
7254
|
}
|
|
7128
7255
|
});
|
|
7129
7256
|
|
|
7257
|
+
// src/canonical/features/syntax-error.ts
|
|
7258
|
+
function failSyntax(filePath2, cause, onParseError) {
|
|
7259
|
+
const error = new CanonicalParseError(filePath2, cause);
|
|
7260
|
+
if (onParseError !== void 0) {
|
|
7261
|
+
onParseError(error, filePath2);
|
|
7262
|
+
return null;
|
|
7263
|
+
}
|
|
7264
|
+
throw error;
|
|
7265
|
+
}
|
|
7266
|
+
var init_syntax_error = __esm({
|
|
7267
|
+
"src/canonical/features/syntax-error.ts"() {
|
|
7268
|
+
init_errors();
|
|
7269
|
+
}
|
|
7270
|
+
});
|
|
7271
|
+
|
|
7130
7272
|
// src/canonical/features/mcp.ts
|
|
7131
7273
|
function parseStringMap(raw) {
|
|
7132
7274
|
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return {};
|
|
@@ -7209,14 +7351,14 @@ function stripJsonComments(text) {
|
|
|
7209
7351
|
}
|
|
7210
7352
|
return result2;
|
|
7211
7353
|
}
|
|
7212
|
-
async function parseMcp(mcpPath) {
|
|
7354
|
+
async function parseMcp(mcpPath, onParseError) {
|
|
7213
7355
|
const content = await readFileSafe(mcpPath);
|
|
7214
7356
|
if (!content) return null;
|
|
7215
7357
|
let parsed;
|
|
7216
7358
|
try {
|
|
7217
7359
|
parsed = JSON.parse(stripJsonComments(content));
|
|
7218
|
-
} catch {
|
|
7219
|
-
return
|
|
7360
|
+
} catch (err) {
|
|
7361
|
+
return failSyntax(mcpPath, err, onParseError);
|
|
7220
7362
|
}
|
|
7221
7363
|
if (!parsed || typeof parsed !== "object") return null;
|
|
7222
7364
|
const mcpServersRaw = parsed.mcpServers;
|
|
@@ -7231,6 +7373,7 @@ async function parseMcp(mcpPath) {
|
|
|
7231
7373
|
}
|
|
7232
7374
|
var init_mcp = __esm({
|
|
7233
7375
|
"src/canonical/features/mcp.ts"() {
|
|
7376
|
+
init_syntax_error();
|
|
7234
7377
|
init_fs();
|
|
7235
7378
|
}
|
|
7236
7379
|
});
|
|
@@ -9010,10 +9153,10 @@ var init_claude_code2 = __esm({
|
|
|
9010
9153
|
skillDir: ".claude/skills",
|
|
9011
9154
|
managedOutputs: {
|
|
9012
9155
|
dirs: [".claude/agents", ".claude/commands", ".claude/rules", ".claude/skills"],
|
|
9013
|
-
|
|
9014
|
-
//
|
|
9156
|
+
files: [CLAUDE_ROOT, ".claudeignore"],
|
|
9157
|
+
// CLAUDE_NESTED_ROOT is the pre-migration project location: evicted once a run
|
|
9015
9158
|
// writes the root `CLAUDE.md`, so Claude Code never concatenates both into context.
|
|
9016
|
-
|
|
9159
|
+
supersededFiles: [CLAUDE_NESTED_ROOT],
|
|
9017
9160
|
// `.mcp.json` is the shared project MCP file teams hand-commit and
|
|
9018
9161
|
// deepagents-cli writes too; agentsmesh owns only `mcpServers` in it.
|
|
9019
9162
|
// `.claude/settings.json` is co-owned through the `SETTINGS_JSON_PATHS`
|
|
@@ -10244,9 +10387,8 @@ var init_cline2 = __esm({
|
|
|
10244
10387
|
};
|
|
10245
10388
|
}
|
|
10246
10389
|
});
|
|
10247
|
-
|
|
10248
|
-
|
|
10249
|
-
}
|
|
10390
|
+
|
|
10391
|
+
// src/targets/codex-cli/codex-rule-paths.ts
|
|
10250
10392
|
function directoryFromGlob(glob) {
|
|
10251
10393
|
let normalized = glob.trim();
|
|
10252
10394
|
if (normalized.startsWith("./")) normalized = normalized.slice(2);
|
|
@@ -10267,12 +10409,12 @@ function codexRuleDirectory(rule) {
|
|
|
10267
10409
|
const dir = directoryFromGlob(glob);
|
|
10268
10410
|
if (dir) return dir;
|
|
10269
10411
|
}
|
|
10270
|
-
return
|
|
10412
|
+
return null;
|
|
10271
10413
|
}
|
|
10272
10414
|
function codexNestedAgentsPath(rule) {
|
|
10273
10415
|
const dir = codexRuleDirectory(rule);
|
|
10274
10416
|
const filename = rule.codexInstructionVariant === "override" ? "AGENTS.override.md" : "AGENTS.md";
|
|
10275
|
-
return `${dir}/${filename}`;
|
|
10417
|
+
return dir === null ? filename : `${dir}/${filename}`;
|
|
10276
10418
|
}
|
|
10277
10419
|
var GLOB_METACHAR;
|
|
10278
10420
|
var init_codex_rule_paths = __esm({
|
|
@@ -10280,10 +10422,9 @@ var init_codex_rule_paths = __esm({
|
|
|
10280
10422
|
GLOB_METACHAR = /[*?[\]]/;
|
|
10281
10423
|
}
|
|
10282
10424
|
});
|
|
10283
|
-
|
|
10284
|
-
// src/targets/codebuff/rule-paths.ts
|
|
10285
10425
|
function codebuffNestedKnowledgePath(rule) {
|
|
10286
|
-
|
|
10426
|
+
const dir = codexRuleDirectory(rule) ?? basename(rule.source, ".md");
|
|
10427
|
+
return `${dir}/AGENTS.md`;
|
|
10287
10428
|
}
|
|
10288
10429
|
var init_rule_paths = __esm({
|
|
10289
10430
|
"src/targets/codebuff/rule-paths.ts"() {
|
|
@@ -10894,6 +11035,9 @@ function eligibleAdvisoryRules(canonical) {
|
|
|
10894
11035
|
return rule.targets.length === 0 || rule.targets.includes("codex-cli");
|
|
10895
11036
|
});
|
|
10896
11037
|
}
|
|
11038
|
+
function isRootEmbedded(rule) {
|
|
11039
|
+
return codexNestedAgentsPath(rule) === AGENTS_MD;
|
|
11040
|
+
}
|
|
10897
11041
|
function groupByNestedPath2(rules) {
|
|
10898
11042
|
const groups = /* @__PURE__ */ new Map();
|
|
10899
11043
|
for (const rule of rules) {
|
|
@@ -10906,9 +11050,11 @@ function groupByNestedPath2(rules) {
|
|
|
10906
11050
|
}
|
|
10907
11051
|
function generateRules9(canonical) {
|
|
10908
11052
|
const root = canonical.rules.find((r) => r.root);
|
|
11053
|
+
const advisory = eligibleAdvisoryRules(canonical);
|
|
10909
11054
|
const outputs = [];
|
|
10910
11055
|
if (root) {
|
|
10911
|
-
|
|
11056
|
+
const content = appendEmbeddedRulesBlock(root.body.trim(), advisory.filter(isRootEmbedded));
|
|
11057
|
+
outputs.push({ path: AGENTS_MD, content });
|
|
10912
11058
|
}
|
|
10913
11059
|
for (const rule of canonical.rules) {
|
|
10914
11060
|
if (rule.root) continue;
|
|
@@ -10920,7 +11066,8 @@ function generateRules9(canonical) {
|
|
|
10920
11066
|
content: toSafeCodexRulesContent(rule.body)
|
|
10921
11067
|
});
|
|
10922
11068
|
}
|
|
10923
|
-
|
|
11069
|
+
const nested = advisory.filter((rule) => !isRootEmbedded(rule));
|
|
11070
|
+
for (const [path, rules] of groupByNestedPath2(nested)) {
|
|
10924
11071
|
const content = rules.map((rule) => rule.body.trim()).filter((body) => body.length > 0).join("\n\n");
|
|
10925
11072
|
outputs.push({ path, content });
|
|
10926
11073
|
}
|
|
@@ -11745,6 +11892,23 @@ var init_linter9 = __esm({
|
|
|
11745
11892
|
});
|
|
11746
11893
|
|
|
11747
11894
|
// src/targets/codex-cli/lint.ts
|
|
11895
|
+
function lintAgents3(canonical) {
|
|
11896
|
+
const diagnostics = [];
|
|
11897
|
+
for (const agent of canonical.agents) {
|
|
11898
|
+
const dropped = CODEX_DROPPED_AGENT_FIELDS.filter(
|
|
11899
|
+
(field) => hasAgentValue(agent, field)
|
|
11900
|
+
).sort();
|
|
11901
|
+
if (dropped.length === 0) continue;
|
|
11902
|
+
diagnostics.push(
|
|
11903
|
+
createWarning(
|
|
11904
|
+
agent.source,
|
|
11905
|
+
"codex-cli",
|
|
11906
|
+
`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.`
|
|
11907
|
+
)
|
|
11908
|
+
);
|
|
11909
|
+
}
|
|
11910
|
+
return diagnostics;
|
|
11911
|
+
}
|
|
11748
11912
|
function lintMcp4(canonical) {
|
|
11749
11913
|
if (!canonical.mcp || Object.keys(canonical.mcp.mcpServers).length === 0) return [];
|
|
11750
11914
|
const diagnostics = [];
|
|
@@ -11776,11 +11940,21 @@ function lintHooks7(canonical) {
|
|
|
11776
11940
|
(event) => createUnsupportedHookWarning(event, "codex-cli", CODEX_SUPPORTED_HOOK_EVENTS)
|
|
11777
11941
|
);
|
|
11778
11942
|
}
|
|
11943
|
+
var CODEX_DROPPED_AGENT_FIELDS;
|
|
11779
11944
|
var init_lint8 = __esm({
|
|
11780
11945
|
"src/targets/codex-cli/lint.ts"() {
|
|
11781
11946
|
init_helpers();
|
|
11782
11947
|
init_mcp_servers();
|
|
11948
|
+
init_agents_format();
|
|
11783
11949
|
init_constants10();
|
|
11950
|
+
CODEX_DROPPED_AGENT_FIELDS = [
|
|
11951
|
+
"tools",
|
|
11952
|
+
"disallowedTools",
|
|
11953
|
+
"maxTurns",
|
|
11954
|
+
"hooks",
|
|
11955
|
+
"skills",
|
|
11956
|
+
"memory"
|
|
11957
|
+
];
|
|
11784
11958
|
}
|
|
11785
11959
|
});
|
|
11786
11960
|
|
|
@@ -11855,6 +12029,7 @@ var init_codex_cli2 = __esm({
|
|
|
11855
12029
|
generateMcp: generateMcp6,
|
|
11856
12030
|
generateHooks: generateHooks6,
|
|
11857
12031
|
generatePermissions: generatePermissions7,
|
|
12032
|
+
lint: lintAgents3,
|
|
11858
12033
|
importFrom: importFromCodex
|
|
11859
12034
|
};
|
|
11860
12035
|
project6 = {
|
|
@@ -11902,7 +12077,7 @@ var init_codex_cli2 = __esm({
|
|
|
11902
12077
|
},
|
|
11903
12078
|
rewriteGeneratedPath(path) {
|
|
11904
12079
|
if (path === AGENTS_MD) return CODEX_GLOBAL_AGENTS_MD;
|
|
11905
|
-
if (
|
|
12080
|
+
if (/(^|\/)AGENTS(\.override)?\.md$/.test(path)) return null;
|
|
11906
12081
|
if (path.startsWith(`${CODEX_INSTRUCTIONS_DIR}/`)) return null;
|
|
11907
12082
|
return path;
|
|
11908
12083
|
},
|
|
@@ -12558,7 +12733,7 @@ function hasValue(agent, field) {
|
|
|
12558
12733
|
if (value && typeof value === "object") return Object.keys(value).length > 0;
|
|
12559
12734
|
return false;
|
|
12560
12735
|
}
|
|
12561
|
-
function
|
|
12736
|
+
function lintAgents4(canonical) {
|
|
12562
12737
|
const diagnostics = [];
|
|
12563
12738
|
for (const agent of canonical.agents) {
|
|
12564
12739
|
const dropped = DROPPED_FIELDS.filter((field) => hasValue(agent, field)).sort();
|
|
@@ -12726,7 +12901,7 @@ var init_continue2 = __esm({
|
|
|
12726
12901
|
generateHooks: generateHooks7,
|
|
12727
12902
|
generateIgnore: generateIgnore8,
|
|
12728
12903
|
// Feature-independent lint hook: agent warnings must not hang off `rules`.
|
|
12729
|
-
lint:
|
|
12904
|
+
lint: lintAgents4,
|
|
12730
12905
|
importFrom: importFromContinue
|
|
12731
12906
|
};
|
|
12732
12907
|
descriptor10 = {
|
|
@@ -12983,7 +13158,7 @@ var init_hook_format = __esm({
|
|
|
12983
13158
|
init_hook_entry();
|
|
12984
13159
|
}
|
|
12985
13160
|
});
|
|
12986
|
-
function
|
|
13161
|
+
function ruleSlug2(source) {
|
|
12987
13162
|
const name = basename(source, ".md");
|
|
12988
13163
|
return name === "_root" ? "root" : name;
|
|
12989
13164
|
}
|
|
@@ -13017,7 +13192,7 @@ function generateRules11(canonical) {
|
|
|
13017
13192
|
if (rule.root) continue;
|
|
13018
13193
|
if (rule.targets.length > 0 && !rule.targets.includes("copilot")) continue;
|
|
13019
13194
|
if (rule.globs.length === 0) continue;
|
|
13020
|
-
const slug =
|
|
13195
|
+
const slug = ruleSlug2(rule.source);
|
|
13021
13196
|
const frontmatter = {
|
|
13022
13197
|
description: rule.description || void 0,
|
|
13023
13198
|
applyTo: rule.globs.length === 1 ? rule.globs[0] : rule.globs
|
|
@@ -14506,13 +14681,18 @@ var init_rules2 = __esm({
|
|
|
14506
14681
|
|
|
14507
14682
|
// src/targets/cursor/generator/commands.ts
|
|
14508
14683
|
function generateCommands13(canonical) {
|
|
14509
|
-
return canonical.commands.map((cmd) =>
|
|
14510
|
-
|
|
14511
|
-
|
|
14512
|
-
|
|
14684
|
+
return canonical.commands.map((cmd) => {
|
|
14685
|
+
const frontmatter = {};
|
|
14686
|
+
if (cmd.description) frontmatter.description = cmd.description;
|
|
14687
|
+
return {
|
|
14688
|
+
path: `${CURSOR_COMMANDS_DIR}/${cmd.name}.md`,
|
|
14689
|
+
content: serializeFrontmatter(frontmatter, cmd.body.trim() || "")
|
|
14690
|
+
};
|
|
14691
|
+
});
|
|
14513
14692
|
}
|
|
14514
14693
|
var init_commands = __esm({
|
|
14515
14694
|
"src/targets/cursor/generator/commands.ts"() {
|
|
14695
|
+
init_markdown();
|
|
14516
14696
|
init_constants13();
|
|
14517
14697
|
}
|
|
14518
14698
|
});
|
|
@@ -14607,7 +14787,7 @@ var init_permissions3 = __esm({
|
|
|
14607
14787
|
// src/targets/cursor/hook-format.ts
|
|
14608
14788
|
function unmappedCursorHookEvents(hooks) {
|
|
14609
14789
|
return Object.keys(hooks).filter(
|
|
14610
|
-
(event) => Array.isArray(hooks[event]) && hooks[event].length > 0 && !(event in CANONICAL_TO_CURSOR) && !
|
|
14790
|
+
(event) => Array.isArray(hooks[event]) && hooks[event].length > 0 && !(event in CANONICAL_TO_CURSOR) && !isBestEffortHookEvent(event, hooks[event])
|
|
14611
14791
|
);
|
|
14612
14792
|
}
|
|
14613
14793
|
function toCursorHooks(hooks) {
|
|
@@ -15045,14 +15225,11 @@ async function hasGlobalCursorArtifacts(projectRoot) {
|
|
|
15045
15225
|
join(projectRoot, CURSOR_GLOBAL_USER_RULES),
|
|
15046
15226
|
join(projectRoot, CURSOR_MCP),
|
|
15047
15227
|
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)
|
|
15228
|
+
join(projectRoot, CURSOR_IGNORE)
|
|
15052
15229
|
];
|
|
15053
15230
|
for (const p of candidates) {
|
|
15054
|
-
const
|
|
15055
|
-
if (
|
|
15231
|
+
const content = await readFileSafe(p);
|
|
15232
|
+
if (content !== null && content.trim() !== "") return true;
|
|
15056
15233
|
}
|
|
15057
15234
|
const skillFiles = await readDirRecursiveNoSymlinks(join(projectRoot, CURSOR_SKILLS_DIR));
|
|
15058
15235
|
if (skillFiles.some((f) => f.endsWith(".md"))) return true;
|
|
@@ -15339,11 +15516,11 @@ function lintHooks10(canonical) {
|
|
|
15339
15516
|
];
|
|
15340
15517
|
}
|
|
15341
15518
|
function lintCommands6(canonical) {
|
|
15342
|
-
return canonical.commands.filter((command) => command.
|
|
15519
|
+
return canonical.commands.filter((command) => command.allowedTools.length > 0).map(
|
|
15343
15520
|
(command) => createWarning(
|
|
15344
15521
|
command.source,
|
|
15345
15522
|
"cursor",
|
|
15346
|
-
"Cursor command files
|
|
15523
|
+
"Cursor command files project only description frontmatter; allowed-tools metadata is not projected."
|
|
15347
15524
|
)
|
|
15348
15525
|
);
|
|
15349
15526
|
}
|
|
@@ -15676,7 +15853,7 @@ var init_mcp_merge4 = __esm({
|
|
|
15676
15853
|
// src/targets/deepagents-cli/hooks-format.ts
|
|
15677
15854
|
function unmappedDeepagentsHookEvents(hooks) {
|
|
15678
15855
|
return Object.keys(hooks).filter(
|
|
15679
|
-
(event) => Array.isArray(hooks[event]) && hooks[event].length > 0 && !(event in CANONICAL_TO_DEEPAGENTS) && !
|
|
15856
|
+
(event) => Array.isArray(hooks[event]) && hooks[event].length > 0 && !(event in CANONICAL_TO_DEEPAGENTS) && !isBestEffortHookEvent(event, hooks[event])
|
|
15680
15857
|
);
|
|
15681
15858
|
}
|
|
15682
15859
|
function toDeepagentsHooks(hooks) {
|
|
@@ -16244,7 +16421,8 @@ var init_deepagents_cli2 = __esm({
|
|
|
16244
16421
|
}
|
|
16245
16422
|
},
|
|
16246
16423
|
buildImportPaths: buildDeepagentsCliImportPaths,
|
|
16247
|
-
|
|
16424
|
+
// `.mcp.json` is co-owned with claude-code (agentsmesh writes it), so it must not enroll this target.
|
|
16425
|
+
detectionPaths: [DEEPAGENTS_CLI_ROOT_FILE]
|
|
16248
16426
|
};
|
|
16249
16427
|
}
|
|
16250
16428
|
});
|
|
@@ -20222,7 +20400,8 @@ var init_layout8 = __esm({
|
|
|
20222
20400
|
skillDir: KIMI_CODE_SKILLS_DIR,
|
|
20223
20401
|
managedOutputs: {
|
|
20224
20402
|
dirs: [KIMI_CODE_AGENTS_DIR, KIMI_CODE_SKILLS_DIR],
|
|
20225
|
-
files: [KIMI_CODE_ROOT_FILE
|
|
20403
|
+
files: [KIMI_CODE_ROOT_FILE],
|
|
20404
|
+
supersededFiles: [KIMI_CODE_NESTED_ROOT_FILE],
|
|
20226
20405
|
// Kimi Code's own MCP config, in the same directory as the credential-
|
|
20227
20406
|
// bearing config.toml this layout already refuses to delete.
|
|
20228
20407
|
coOwnedFiles: [KIMI_CODE_MCP_FILE]
|
|
@@ -20870,7 +21049,7 @@ function lintMcp9(canonical) {
|
|
|
20870
21049
|
}
|
|
20871
21050
|
return diagnostics;
|
|
20872
21051
|
}
|
|
20873
|
-
function
|
|
21052
|
+
function lintAgents5(canonical) {
|
|
20874
21053
|
return canonical.agents.flatMap((agent) => {
|
|
20875
21054
|
const dropped = DROPPED_AGENT_FIELDS.filter(([, has]) => has(agent)).map(([field]) => field);
|
|
20876
21055
|
if (dropped.length === 0) return [];
|
|
@@ -20961,7 +21140,7 @@ var init_kimi_code2 = __esm({
|
|
|
20961
21140
|
generateHooks: generateHooks14,
|
|
20962
21141
|
generatePermissions: generatePermissions16,
|
|
20963
21142
|
importFrom: importFromKimiCode,
|
|
20964
|
-
lint:
|
|
21143
|
+
lint: lintAgents5
|
|
20965
21144
|
};
|
|
20966
21145
|
capabilities9 = {
|
|
20967
21146
|
rules: "native",
|
|
@@ -23259,7 +23438,7 @@ function lintAgentFields(agent) {
|
|
|
23259
23438
|
)
|
|
23260
23439
|
];
|
|
23261
23440
|
}
|
|
23262
|
-
function
|
|
23441
|
+
function lintAgents6(canonical) {
|
|
23263
23442
|
const diagnostics = [];
|
|
23264
23443
|
for (const agent of canonical.agents) {
|
|
23265
23444
|
diagnostics.push(...lintAgentFields(agent));
|
|
@@ -23321,7 +23500,7 @@ var init_openhands2 = __esm({
|
|
|
23321
23500
|
generatePermissions: generatePermissions19,
|
|
23322
23501
|
importFrom: importFromOpenhands,
|
|
23323
23502
|
// Ungated by feature, so agent-only feature sets still get the warning.
|
|
23324
|
-
lint:
|
|
23503
|
+
lint: lintAgents6
|
|
23325
23504
|
};
|
|
23326
23505
|
descriptor24 = {
|
|
23327
23506
|
id: OPENHANDS_TARGET,
|
|
@@ -27136,7 +27315,7 @@ var init_constants34 = __esm({
|
|
|
27136
27315
|
WINDSURF_GLOBAL_AGENTS_SKILLS_DIR = ".agents/skills";
|
|
27137
27316
|
}
|
|
27138
27317
|
});
|
|
27139
|
-
function
|
|
27318
|
+
function ruleSlug3(source) {
|
|
27140
27319
|
const name = basename(source, ".md");
|
|
27141
27320
|
return name === "_root" ? "root" : name;
|
|
27142
27321
|
}
|
|
@@ -27157,7 +27336,7 @@ function generateRules32(canonical) {
|
|
|
27157
27336
|
for (const rule of canonical.rules) {
|
|
27158
27337
|
if (rule.root) continue;
|
|
27159
27338
|
if (rule.targets.length > 0 && !rule.targets.includes("windsurf")) continue;
|
|
27160
|
-
const slug =
|
|
27339
|
+
const slug = ruleSlug3(rule.source);
|
|
27161
27340
|
const normalizedTrigger = rule.trigger || (rule.globs.length > 0 ? "glob" : void 0);
|
|
27162
27341
|
const frontmatter = {
|
|
27163
27342
|
description: rule.description || void 0,
|
|
@@ -27261,19 +27440,34 @@ var init_mcp4 = __esm({
|
|
|
27261
27440
|
}
|
|
27262
27441
|
});
|
|
27263
27442
|
|
|
27264
|
-
// src/targets/windsurf/
|
|
27443
|
+
// src/targets/windsurf/hook-events.ts
|
|
27265
27444
|
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
27445
|
return event.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[-\s]+/g, "_").toLowerCase();
|
|
27276
27446
|
}
|
|
27447
|
+
function canonicalHookEventName(event) {
|
|
27448
|
+
if (KNOWN_CANONICAL_HOOK_EVENTS.includes(event)) return event;
|
|
27449
|
+
return WINDSURF_TO_CANONICAL.get(event) ?? null;
|
|
27450
|
+
}
|
|
27451
|
+
var KNOWN_CANONICAL_HOOK_EVENTS, WINDSURF_TO_CANONICAL;
|
|
27452
|
+
var init_hook_events = __esm({
|
|
27453
|
+
"src/targets/windsurf/hook-events.ts"() {
|
|
27454
|
+
init_hook_types();
|
|
27455
|
+
KNOWN_CANONICAL_HOOK_EVENTS = [
|
|
27456
|
+
"PreToolUse",
|
|
27457
|
+
"PostToolUse",
|
|
27458
|
+
"Notification",
|
|
27459
|
+
"UserPromptSubmit",
|
|
27460
|
+
"SubagentStart",
|
|
27461
|
+
"SubagentStop",
|
|
27462
|
+
...BEST_EFFORT_HOOK_EVENTS
|
|
27463
|
+
];
|
|
27464
|
+
WINDSURF_TO_CANONICAL = new Map(
|
|
27465
|
+
KNOWN_CANONICAL_HOOK_EVENTS.map((event) => [windsurfEventName(event), event])
|
|
27466
|
+
);
|
|
27467
|
+
}
|
|
27468
|
+
});
|
|
27469
|
+
|
|
27470
|
+
// src/targets/windsurf/generator/hooks.ts
|
|
27277
27471
|
function toWindsurfHooks(hooks) {
|
|
27278
27472
|
const result2 = {};
|
|
27279
27473
|
for (const [event, entries] of Object.entries(hooks)) {
|
|
@@ -27301,6 +27495,7 @@ var init_hooks4 = __esm({
|
|
|
27301
27495
|
"src/targets/windsurf/generator/hooks.ts"() {
|
|
27302
27496
|
init_hook_command();
|
|
27303
27497
|
init_constants34();
|
|
27498
|
+
init_hook_events();
|
|
27304
27499
|
}
|
|
27305
27500
|
});
|
|
27306
27501
|
|
|
@@ -27432,6 +27627,57 @@ var init_skills_adapter5 = __esm({
|
|
|
27432
27627
|
init_constants34();
|
|
27433
27628
|
}
|
|
27434
27629
|
});
|
|
27630
|
+
function toHookEntry(raw) {
|
|
27631
|
+
if (!raw || typeof raw !== "object") return null;
|
|
27632
|
+
const obj = raw;
|
|
27633
|
+
const matcher = obj.matcher;
|
|
27634
|
+
if (typeof matcher !== "string") return null;
|
|
27635
|
+
const command = getHookText(obj);
|
|
27636
|
+
if (!command) return null;
|
|
27637
|
+
const type = typeof obj.type === "string" && VALID_TYPES.includes(obj.type) ? obj.type : void 0;
|
|
27638
|
+
const timeout = typeof obj.timeout === "number" && Number.isFinite(obj.timeout) ? obj.timeout : void 0;
|
|
27639
|
+
const prompt = getHookPrompt(obj) || void 0;
|
|
27640
|
+
return {
|
|
27641
|
+
matcher,
|
|
27642
|
+
command,
|
|
27643
|
+
...timeout !== void 0 && { timeout },
|
|
27644
|
+
...type && { type },
|
|
27645
|
+
...prompt && { prompt }
|
|
27646
|
+
};
|
|
27647
|
+
}
|
|
27648
|
+
async function parseHooks(hooksPath, onParseError) {
|
|
27649
|
+
const content = await readFileSafe(hooksPath);
|
|
27650
|
+
if (content === null) return null;
|
|
27651
|
+
if (!content.trim()) return {};
|
|
27652
|
+
let parsed;
|
|
27653
|
+
try {
|
|
27654
|
+
parsed = parse(content);
|
|
27655
|
+
} catch (err) {
|
|
27656
|
+
return failSyntax(hooksPath, err, onParseError);
|
|
27657
|
+
}
|
|
27658
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
27659
|
+
const result2 = {};
|
|
27660
|
+
const obj = parsed;
|
|
27661
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
27662
|
+
if (!Array.isArray(val)) continue;
|
|
27663
|
+
const entries = [];
|
|
27664
|
+
for (const item of val) {
|
|
27665
|
+
const entry = toHookEntry(item);
|
|
27666
|
+
if (entry) entries.push(entry);
|
|
27667
|
+
}
|
|
27668
|
+
if (entries.length > 0) result2[key] = entries;
|
|
27669
|
+
}
|
|
27670
|
+
return result2;
|
|
27671
|
+
}
|
|
27672
|
+
var VALID_TYPES;
|
|
27673
|
+
var init_hooks5 = __esm({
|
|
27674
|
+
"src/canonical/features/hooks.ts"() {
|
|
27675
|
+
init_syntax_error();
|
|
27676
|
+
init_fs();
|
|
27677
|
+
init_hook_command();
|
|
27678
|
+
VALID_TYPES = ["command", "prompt"];
|
|
27679
|
+
}
|
|
27680
|
+
});
|
|
27435
27681
|
async function importWindsurfHooks(projectRoot, results) {
|
|
27436
27682
|
const hooksPath = join(projectRoot, WINDSURF_HOOKS_FILE);
|
|
27437
27683
|
const hooksContent = await readFileSafe(hooksPath);
|
|
@@ -27439,9 +27685,10 @@ async function importWindsurfHooks(projectRoot, results) {
|
|
|
27439
27685
|
try {
|
|
27440
27686
|
const parsed = JSON.parse(hooksContent);
|
|
27441
27687
|
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
27688
|
const destPath = join(projectRoot, WINDSURF_CANONICAL_HOOKS);
|
|
27689
|
+
const existing = await parseHooks(destPath) ?? {};
|
|
27690
|
+
const canonical = windsurfHooksToCanonical(parsed.hooks, existing);
|
|
27691
|
+
if (Object.keys(canonical).length === 0) return;
|
|
27445
27692
|
await mkdirp(dirname(destPath));
|
|
27446
27693
|
await writeFileAtomic(destPath, stringify(canonical));
|
|
27447
27694
|
results.push({
|
|
@@ -27453,54 +27700,63 @@ async function importWindsurfHooks(projectRoot, results) {
|
|
|
27453
27700
|
} catch {
|
|
27454
27701
|
}
|
|
27455
27702
|
}
|
|
27456
|
-
function
|
|
27457
|
-
const
|
|
27458
|
-
|
|
27459
|
-
post_tool_use: "PostToolUse",
|
|
27460
|
-
notification: "Notification",
|
|
27461
|
-
user_prompt_submit: "UserPromptSubmit",
|
|
27462
|
-
subagent_start: "SubagentStart",
|
|
27463
|
-
subagent_stop: "SubagentStop"
|
|
27464
|
-
};
|
|
27465
|
-
return explicit[event] ?? event;
|
|
27703
|
+
function preservedMatcher(existing, event, command) {
|
|
27704
|
+
const match = existing[event]?.find((entry) => entry.command === command);
|
|
27705
|
+
return match?.matcher ?? WILDCARD_MATCHER;
|
|
27466
27706
|
}
|
|
27467
|
-
function
|
|
27707
|
+
function legacyEntries(entry) {
|
|
27708
|
+
const matcher = typeof entry.matcher === "string" && entry.matcher.trim() ? entry.matcher : WILDCARD_MATCHER;
|
|
27709
|
+
const hooksList = Array.isArray(entry.hooks) ? entry.hooks : [];
|
|
27710
|
+
const out2 = [];
|
|
27711
|
+
for (const item of hooksList) {
|
|
27712
|
+
if (!item || typeof item !== "object") continue;
|
|
27713
|
+
const hook = item;
|
|
27714
|
+
const command = typeof hook.command === "string" ? hook.command : typeof hook.prompt === "string" ? hook.prompt : "";
|
|
27715
|
+
if (!command.trim()) continue;
|
|
27716
|
+
const canonical = {
|
|
27717
|
+
matcher,
|
|
27718
|
+
type: hook.type === "prompt" ? "prompt" : "command",
|
|
27719
|
+
command
|
|
27720
|
+
};
|
|
27721
|
+
if (typeof hook.timeout === "number") canonical.timeout = hook.timeout;
|
|
27722
|
+
out2.push(canonical);
|
|
27723
|
+
}
|
|
27724
|
+
return out2;
|
|
27725
|
+
}
|
|
27726
|
+
function windsurfHooksToCanonical(hooks, existing) {
|
|
27468
27727
|
const result2 = {};
|
|
27469
27728
|
for (const [event, entries] of Object.entries(hooks)) {
|
|
27470
27729
|
if (!Array.isArray(entries)) continue;
|
|
27471
27730
|
const mappedEvent = canonicalHookEventName(event);
|
|
27731
|
+
if (mappedEvent === null) continue;
|
|
27472
27732
|
const canonicalEntries = [];
|
|
27473
27733
|
for (const entry of entries) {
|
|
27474
27734
|
if (!entry || typeof entry !== "object") continue;
|
|
27475
27735
|
const e = entry;
|
|
27476
27736
|
if (typeof e.command === "string" && e.command.trim()) {
|
|
27477
27737
|
canonicalEntries.push({
|
|
27478
|
-
matcher:
|
|
27738
|
+
matcher: preservedMatcher(existing, mappedEvent, e.command),
|
|
27479
27739
|
type: "command",
|
|
27480
27740
|
command: e.command
|
|
27481
27741
|
});
|
|
27482
27742
|
continue;
|
|
27483
27743
|
}
|
|
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
|
-
}
|
|
27744
|
+
canonicalEntries.push(...legacyEntries(e));
|
|
27499
27745
|
}
|
|
27500
27746
|
if (canonicalEntries.length > 0) result2[mappedEvent] = canonicalEntries;
|
|
27501
27747
|
}
|
|
27502
27748
|
return result2;
|
|
27503
27749
|
}
|
|
27750
|
+
var WILDCARD_MATCHER;
|
|
27751
|
+
var init_importer_hooks2 = __esm({
|
|
27752
|
+
"src/targets/windsurf/importer-hooks.ts"() {
|
|
27753
|
+
init_hooks5();
|
|
27754
|
+
init_fs();
|
|
27755
|
+
init_constants34();
|
|
27756
|
+
init_hook_events();
|
|
27757
|
+
WILDCARD_MATCHER = "*";
|
|
27758
|
+
}
|
|
27759
|
+
});
|
|
27504
27760
|
async function importWindsurfMcp(projectRoot, results) {
|
|
27505
27761
|
const sourceCandidates = [WINDSURF_MCP_EXAMPLE_FILE, WINDSURF_MCP_CONFIG_FILE];
|
|
27506
27762
|
for (const relPath of sourceCandidates) {
|
|
@@ -27524,8 +27780,8 @@ async function importWindsurfMcp(projectRoot, results) {
|
|
|
27524
27780
|
}
|
|
27525
27781
|
}
|
|
27526
27782
|
}
|
|
27527
|
-
var
|
|
27528
|
-
"src/targets/windsurf/importer-
|
|
27783
|
+
var init_importer_mcp = __esm({
|
|
27784
|
+
"src/targets/windsurf/importer-mcp.ts"() {
|
|
27529
27785
|
init_fs();
|
|
27530
27786
|
init_constants34();
|
|
27531
27787
|
}
|
|
@@ -27675,7 +27931,8 @@ var init_importer32 = __esm({
|
|
|
27675
27931
|
init_constants34();
|
|
27676
27932
|
init_importer_workflows();
|
|
27677
27933
|
init_skills_adapter5();
|
|
27678
|
-
|
|
27934
|
+
init_importer_hooks2();
|
|
27935
|
+
init_importer_mcp();
|
|
27679
27936
|
}
|
|
27680
27937
|
});
|
|
27681
27938
|
|
|
@@ -27760,9 +28017,28 @@ function lintPermissions22(canonical) {
|
|
|
27760
28017
|
)
|
|
27761
28018
|
];
|
|
27762
28019
|
}
|
|
28020
|
+
function lintHooks23(canonical) {
|
|
28021
|
+
if (!canonical.hooks) return [];
|
|
28022
|
+
const diagnostics = [];
|
|
28023
|
+
for (const [event, entries] of Object.entries(canonical.hooks)) {
|
|
28024
|
+
for (const entry of entries ?? []) {
|
|
28025
|
+
if (WILDCARD_MATCHERS.has(entry.matcher.trim())) continue;
|
|
28026
|
+
diagnostics.push(
|
|
28027
|
+
createWarning(
|
|
28028
|
+
".agentsmesh/hooks.yaml",
|
|
28029
|
+
"windsurf",
|
|
28030
|
+
`Windsurf hooks have no matcher field; ${event} hook "${entry.command}" runs on every ${event} event (matcher "${entry.matcher}" is not projected).`
|
|
28031
|
+
)
|
|
28032
|
+
);
|
|
28033
|
+
}
|
|
28034
|
+
}
|
|
28035
|
+
return diagnostics;
|
|
28036
|
+
}
|
|
28037
|
+
var WILDCARD_MATCHERS;
|
|
27763
28038
|
var init_lint31 = __esm({
|
|
27764
28039
|
"src/targets/windsurf/lint.ts"() {
|
|
27765
28040
|
init_helpers();
|
|
28041
|
+
WILDCARD_MATCHERS = /* @__PURE__ */ new Set(["", "*", ".*"]);
|
|
27766
28042
|
}
|
|
27767
28043
|
});
|
|
27768
28044
|
|
|
@@ -27918,6 +28194,7 @@ var init_windsurf2 = __esm({
|
|
|
27918
28194
|
lintRules: lintRules32,
|
|
27919
28195
|
lint: {
|
|
27920
28196
|
commands: lintCommands10,
|
|
28197
|
+
hooks: lintHooks23,
|
|
27921
28198
|
mcp: lintMcp13,
|
|
27922
28199
|
permissions: lintPermissions22
|
|
27923
28200
|
},
|
|
@@ -29787,6 +30064,7 @@ function ruleNameFromSource(source) {
|
|
|
29787
30064
|
}
|
|
29788
30065
|
|
|
29789
30066
|
// src/core/generate/collision.ts
|
|
30067
|
+
init_fs_text_encoding();
|
|
29790
30068
|
init_target_ids();
|
|
29791
30069
|
var AGENTS_SUFFIX = "AGENTS.md";
|
|
29792
30070
|
function statusRank(status) {
|
|
@@ -29895,7 +30173,7 @@ function assertNoCaseOnlyPathCollisions(results) {
|
|
|
29895
30173
|
}
|
|
29896
30174
|
}
|
|
29897
30175
|
function refreshResultStatus(result2) {
|
|
29898
|
-
const status = result2.currentContent === void 0 ? "created" : result2.currentContent !== result2.content ? "updated" : "unchanged";
|
|
30176
|
+
const status = result2.currentContent === void 0 ? "created" : normalizeTextPayload(result2.path, result2.currentContent) !== normalizeTextPayload(result2.path, result2.content) ? "updated" : "unchanged";
|
|
29899
30177
|
return result2.status === status ? result2 : { ...result2, status };
|
|
29900
30178
|
}
|
|
29901
30179
|
|
|
@@ -30300,8 +30578,46 @@ function mergeLocalConfig(project26, local) {
|
|
|
30300
30578
|
if (Array.isArray(local.extends) && local.extends.length > 0) {
|
|
30301
30579
|
merged.extends = [...project26.extends ?? [], ...local.extends];
|
|
30302
30580
|
}
|
|
30581
|
+
if (Array.isArray(local.plugins)) {
|
|
30582
|
+
merged.plugins = mergeById(project26.plugins, local.plugins);
|
|
30583
|
+
}
|
|
30584
|
+
if (Array.isArray(local.pluginTargets)) {
|
|
30585
|
+
merged.pluginTargets = [.../* @__PURE__ */ new Set([...project26.pluginTargets, ...local.pluginTargets])];
|
|
30586
|
+
}
|
|
30587
|
+
if (typeof local.collaboration === "object" && local.collaboration !== null && !Array.isArray(local.collaboration)) {
|
|
30588
|
+
merged.collaboration = local.collaboration;
|
|
30589
|
+
}
|
|
30590
|
+
warnUnhandledLocalKeys(local);
|
|
30303
30591
|
return merged;
|
|
30304
30592
|
}
|
|
30593
|
+
var LOCAL_KEYS = /* @__PURE__ */ new Set([
|
|
30594
|
+
"version",
|
|
30595
|
+
"targets",
|
|
30596
|
+
"features",
|
|
30597
|
+
"overrides",
|
|
30598
|
+
"conversions",
|
|
30599
|
+
"extends",
|
|
30600
|
+
"plugins",
|
|
30601
|
+
"pluginTargets",
|
|
30602
|
+
"collaboration"
|
|
30603
|
+
]);
|
|
30604
|
+
function warnUnhandledLocalKeys(local) {
|
|
30605
|
+
const unknown = Object.keys(local).filter((key) => !LOCAL_KEYS.has(key));
|
|
30606
|
+
if (unknown.length === 0) return;
|
|
30607
|
+
logger.warn(
|
|
30608
|
+
`agentsmesh.local.yaml: ignoring unknown key(s) ${unknown.join(", ")}; supported keys are ${[...LOCAL_KEYS].join(", ")}.`
|
|
30609
|
+
);
|
|
30610
|
+
}
|
|
30611
|
+
function mergeById(project26, local) {
|
|
30612
|
+
const byId = /* @__PURE__ */ new Map();
|
|
30613
|
+
const anonymous = [];
|
|
30614
|
+
for (const entry of [...project26, ...local]) {
|
|
30615
|
+
const id = typeof entry === "object" && entry !== null && typeof entry.id === "string" ? entry.id : void 0;
|
|
30616
|
+
if (id === void 0) anonymous.push(entry);
|
|
30617
|
+
else byId.set(id, entry);
|
|
30618
|
+
}
|
|
30619
|
+
return [...byId.values(), ...anonymous];
|
|
30620
|
+
}
|
|
30305
30621
|
async function loadConfigFromExactDir(configDir) {
|
|
30306
30622
|
const configPath = join(configDir, CONFIG_FILENAME);
|
|
30307
30623
|
let config = await loadConfig(configPath);
|
|
@@ -30790,24 +31106,14 @@ async function sweepStaleCache(cacheDir, maxAgeMs) {
|
|
|
30790
31106
|
|
|
30791
31107
|
// src/config/remote/remote-fetcher.ts
|
|
30792
31108
|
var MAX_CACHE_KEY_LENGTH = 80;
|
|
31109
|
+
var CACHE_KEY_HASH_LENGTH = 12;
|
|
30793
31110
|
function buildCacheKey(provider, identifier, ref) {
|
|
30794
31111
|
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;
|
|
31112
|
+
const [org, repo] = provider === "github" ? identifier.split("/", 2) : [];
|
|
31113
|
+
const readable = org && repo ? `${safe(org)}--${safe(repo)}--${safe(ref)}` : `${safe(provider)}__${safe(identifier)}__${safe(ref)}`;
|
|
31114
|
+
const hash = createHash("sha256").update(`${provider}|${identifier}|${ref}`).digest("hex").slice(0, CACHE_KEY_HASH_LENGTH);
|
|
31115
|
+
const maxReadable = MAX_CACHE_KEY_LENGTH - CACHE_KEY_HASH_LENGTH - 2;
|
|
31116
|
+
return `${readable.slice(0, maxReadable)}--${hash}`;
|
|
30811
31117
|
}
|
|
30812
31118
|
function getCacheDir() {
|
|
30813
31119
|
const env = process.env.AGENTSMESH_CACHE;
|
|
@@ -30919,6 +31225,13 @@ async function resolveExtendPaths(config, configDir, options = {}) {
|
|
|
30919
31225
|
return result2;
|
|
30920
31226
|
}
|
|
30921
31227
|
|
|
31228
|
+
// src/canonical/features/empty-file.ts
|
|
31229
|
+
function isEmptyCanonicalFile(content, path) {
|
|
31230
|
+
if (content.trim() !== "") return false;
|
|
31231
|
+
logger.warn(`Skipping empty canonical file ${path.replaceAll("\\", "/")}`);
|
|
31232
|
+
return true;
|
|
31233
|
+
}
|
|
31234
|
+
|
|
30922
31235
|
// src/canonical/features/rules.ts
|
|
30923
31236
|
init_fs();
|
|
30924
31237
|
init_markdown();
|
|
@@ -31058,7 +31371,8 @@ async function parseRules(rulesDir, opts = {}) {
|
|
|
31058
31371
|
const rules = [];
|
|
31059
31372
|
for (const path of mdFiles) {
|
|
31060
31373
|
const content = await readFileSafe(path);
|
|
31061
|
-
if (
|
|
31374
|
+
if (content === null) continue;
|
|
31375
|
+
if (isEmptyCanonicalFile(content, path)) continue;
|
|
31062
31376
|
const parsed = parseOrSkipFrontmatter(content, path, opts.onParseError);
|
|
31063
31377
|
if (!parsed) continue;
|
|
31064
31378
|
const { frontmatter, body } = parsed;
|
|
@@ -31112,7 +31426,8 @@ async function parseCommands(commandsDir, opts = {}) {
|
|
|
31112
31426
|
const commands = [];
|
|
31113
31427
|
for (const path of mdFiles) {
|
|
31114
31428
|
const content = await readFileSafe(path);
|
|
31115
|
-
if (
|
|
31429
|
+
if (content === null) continue;
|
|
31430
|
+
if (isEmptyCanonicalFile(content, path)) continue;
|
|
31116
31431
|
const parsed = parseOrSkipFrontmatter(content, path, opts.onParseError);
|
|
31117
31432
|
if (!parsed) continue;
|
|
31118
31433
|
const { frontmatter, body } = parsed;
|
|
@@ -31172,15 +31487,14 @@ async function parseAgents(agentsDir, opts = {}) {
|
|
|
31172
31487
|
const agents = [];
|
|
31173
31488
|
for (const path of mdFiles) {
|
|
31174
31489
|
const content = await readFileSafe(path);
|
|
31175
|
-
if (
|
|
31490
|
+
if (content === null) continue;
|
|
31491
|
+
if (isEmptyCanonicalFile(content, path)) continue;
|
|
31176
31492
|
const parsed = parseOrSkipFrontmatter(content, path, opts.onParseError);
|
|
31177
31493
|
if (!parsed) continue;
|
|
31178
31494
|
const { frontmatter, body } = parsed;
|
|
31179
31495
|
const name = basename(path, ".md");
|
|
31180
31496
|
assertCanonicalName("agent", name);
|
|
31181
|
-
const
|
|
31182
|
-
const toolsKebab = toStrArray2(frontmatter["tools"]);
|
|
31183
|
-
const tools = toolsCamel.length > 0 ? toolsCamel : toolsKebab;
|
|
31497
|
+
const tools = toStrArray2(frontmatter.tools);
|
|
31184
31498
|
const disallowedCamel = toStrArray2(frontmatter.disallowedTools);
|
|
31185
31499
|
const disallowedKebab = toStrArray2(frontmatter["disallowed-tools"]);
|
|
31186
31500
|
const disallowedTools = disallowedCamel.length > 0 ? disallowedCamel : disallowedKebab;
|
|
@@ -31302,20 +31616,21 @@ async function parseSkills(skillsDir, opts = {}) {
|
|
|
31302
31616
|
init_mcp();
|
|
31303
31617
|
|
|
31304
31618
|
// src/canonical/features/permissions.ts
|
|
31619
|
+
init_syntax_error();
|
|
31305
31620
|
init_fs();
|
|
31306
31621
|
function ensureStringArray(val) {
|
|
31307
31622
|
if (!Array.isArray(val)) return [];
|
|
31308
31623
|
return val.filter((x) => typeof x === "string");
|
|
31309
31624
|
}
|
|
31310
|
-
async function parsePermissions(permissionsPath) {
|
|
31625
|
+
async function parsePermissions(permissionsPath, onParseError) {
|
|
31311
31626
|
const content = await readFileSafe(permissionsPath);
|
|
31312
31627
|
if (content === null) return null;
|
|
31313
31628
|
if (!content.trim()) return { allow: [], deny: [], ask: [] };
|
|
31314
31629
|
let parsed;
|
|
31315
31630
|
try {
|
|
31316
31631
|
parsed = parse(content);
|
|
31317
|
-
} catch {
|
|
31318
|
-
return
|
|
31632
|
+
} catch (err) {
|
|
31633
|
+
return failSyntax(permissionsPath, err, onParseError);
|
|
31319
31634
|
}
|
|
31320
31635
|
if (!parsed || typeof parsed !== "object") return null;
|
|
31321
31636
|
const obj = parsed;
|
|
@@ -31325,52 +31640,8 @@ async function parsePermissions(permissionsPath) {
|
|
|
31325
31640
|
return { allow, deny, ask };
|
|
31326
31641
|
}
|
|
31327
31642
|
|
|
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
|
-
}
|
|
31643
|
+
// src/canonical/load/loader.ts
|
|
31644
|
+
init_hooks5();
|
|
31374
31645
|
|
|
31375
31646
|
// src/canonical/features/ignore.ts
|
|
31376
31647
|
init_fs();
|
|
@@ -31397,9 +31668,9 @@ async function loadCanonicalFiles(canonicalDirOrProjectRoot, opts = {}) {
|
|
|
31397
31668
|
parseCommands(join(canonicalDir, "commands"), opts),
|
|
31398
31669
|
parseAgents(join(canonicalDir, "agents"), opts),
|
|
31399
31670
|
parseSkills(join(canonicalDir, "skills"), opts),
|
|
31400
|
-
parseMcp(join(canonicalDir, "mcp.json")),
|
|
31401
|
-
parsePermissions(join(canonicalDir, "permissions.yaml")),
|
|
31402
|
-
parseHooks(join(canonicalDir, "hooks.yaml")),
|
|
31671
|
+
parseMcp(join(canonicalDir, "mcp.json"), opts.onParseError),
|
|
31672
|
+
parsePermissions(join(canonicalDir, "permissions.yaml"), opts.onParseError),
|
|
31673
|
+
parseHooks(join(canonicalDir, "hooks.yaml"), opts.onParseError),
|
|
31403
31674
|
parseIgnore(join(canonicalDir, "ignore"))
|
|
31404
31675
|
]);
|
|
31405
31676
|
return {
|
|
@@ -31413,13 +31684,13 @@ async function loadCanonicalFiles(canonicalDirOrProjectRoot, opts = {}) {
|
|
|
31413
31684
|
ignore
|
|
31414
31685
|
};
|
|
31415
31686
|
}
|
|
31416
|
-
function
|
|
31687
|
+
function ruleSlug4(r) {
|
|
31417
31688
|
return basename(r.source, ".md");
|
|
31418
31689
|
}
|
|
31419
31690
|
function mergeCanonicalFiles(base, overlay) {
|
|
31420
|
-
const baseRuleMap = new Map(base.rules.map((r) => [
|
|
31691
|
+
const baseRuleMap = new Map(base.rules.map((r) => [ruleSlug4(r), r]));
|
|
31421
31692
|
for (const r of overlay.rules) {
|
|
31422
|
-
baseRuleMap.set(
|
|
31693
|
+
baseRuleMap.set(ruleSlug4(r), r);
|
|
31423
31694
|
}
|
|
31424
31695
|
const baseCmdMap = new Map(base.commands.map((c2) => [c2.name, c2]));
|
|
31425
31696
|
for (const c2 of overlay.commands) {
|
|
@@ -32302,6 +32573,7 @@ function gateExtendElevatedArtifacts(canonical, ext) {
|
|
|
32302
32573
|
});
|
|
32303
32574
|
}
|
|
32304
32575
|
init_mcp();
|
|
32576
|
+
init_hooks5();
|
|
32305
32577
|
|
|
32306
32578
|
// src/install/pack/pack-reader.ts
|
|
32307
32579
|
init_fs();
|
|
@@ -33067,88 +33339,6 @@ function collectOrphans(graph, findings) {
|
|
|
33067
33339
|
}
|
|
33068
33340
|
}
|
|
33069
33341
|
|
|
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
33342
|
// src/lessons/regex-linear/nfa-compile.ts
|
|
33153
33343
|
var MAX_NFA_STATES = 2e3;
|
|
33154
33344
|
var Builder = class {
|
|
@@ -33669,6 +33859,93 @@ function collectFanout(graph, findings) {
|
|
|
33669
33859
|
});
|
|
33670
33860
|
}
|
|
33671
33861
|
}
|
|
33862
|
+
function normalizeRule(rule) {
|
|
33863
|
+
return rule.trim().replace(/\s+/g, " ").toLowerCase();
|
|
33864
|
+
}
|
|
33865
|
+
|
|
33866
|
+
// src/lessons/ranking-text.ts
|
|
33867
|
+
var K1 = 1.5;
|
|
33868
|
+
var B = 0.75;
|
|
33869
|
+
var STOP = /* @__PURE__ */ new Set([
|
|
33870
|
+
"the",
|
|
33871
|
+
"a",
|
|
33872
|
+
"an",
|
|
33873
|
+
"to",
|
|
33874
|
+
"of",
|
|
33875
|
+
"in",
|
|
33876
|
+
"and",
|
|
33877
|
+
"or",
|
|
33878
|
+
"for",
|
|
33879
|
+
"is",
|
|
33880
|
+
"on",
|
|
33881
|
+
"at",
|
|
33882
|
+
"with",
|
|
33883
|
+
"be",
|
|
33884
|
+
"as",
|
|
33885
|
+
"it",
|
|
33886
|
+
"that",
|
|
33887
|
+
"this",
|
|
33888
|
+
"its",
|
|
33889
|
+
"must"
|
|
33890
|
+
]);
|
|
33891
|
+
function tokenize(text) {
|
|
33892
|
+
return text.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 2 && !STOP.has(t));
|
|
33893
|
+
}
|
|
33894
|
+
function queryTerms(query) {
|
|
33895
|
+
const parts = [];
|
|
33896
|
+
if (query.keyword !== void 0) parts.push(query.keyword);
|
|
33897
|
+
if (query.file !== void 0) parts.push(query.file);
|
|
33898
|
+
if (query.command !== void 0) parts.push(query.command);
|
|
33899
|
+
return tokenize(parts.join(" "));
|
|
33900
|
+
}
|
|
33901
|
+
function buildCorpus(graph) {
|
|
33902
|
+
const docs = [];
|
|
33903
|
+
const df = /* @__PURE__ */ new Map();
|
|
33904
|
+
let total = 0;
|
|
33905
|
+
let n = 0;
|
|
33906
|
+
for (const lesson of Object.values(graph.lessons)) {
|
|
33907
|
+
if (lesson.status !== "active") continue;
|
|
33908
|
+
const toks = tokenize(lesson.rule);
|
|
33909
|
+
n += 1;
|
|
33910
|
+
total += toks.length;
|
|
33911
|
+
docs.push(toks.length);
|
|
33912
|
+
for (const t of new Set(toks)) df.set(t, (df.get(t) ?? 0) + 1);
|
|
33913
|
+
}
|
|
33914
|
+
const N = Math.max(n, 1);
|
|
33915
|
+
const idf = /* @__PURE__ */ new Map();
|
|
33916
|
+
for (const [t, f] of df) idf.set(t, Math.log(1 + (N - f + 0.5) / (f + 0.5)));
|
|
33917
|
+
return { idf, avgdl: total / N || 1 };
|
|
33918
|
+
}
|
|
33919
|
+
function bm25(terms, ruleText, corpus) {
|
|
33920
|
+
const toks = tokenize(ruleText);
|
|
33921
|
+
const dl = toks.length || 1;
|
|
33922
|
+
const tf = /* @__PURE__ */ new Map();
|
|
33923
|
+
for (const t of toks) tf.set(t, (tf.get(t) ?? 0) + 1);
|
|
33924
|
+
let score = 0;
|
|
33925
|
+
for (const t of new Set(terms)) {
|
|
33926
|
+
const f = tf.get(t) ?? 0;
|
|
33927
|
+
if (f === 0) continue;
|
|
33928
|
+
const idf = corpus.idf.get(t);
|
|
33929
|
+
score += idf * (f * (K1 + 1)) / (f + K1 * (1 - B + B * dl / corpus.avgdl));
|
|
33930
|
+
}
|
|
33931
|
+
return score;
|
|
33932
|
+
}
|
|
33933
|
+
|
|
33934
|
+
// src/lessons/keyword-signal.ts
|
|
33935
|
+
var MAX_RECOMMENDED_KEYWORD_TOKENS = 5;
|
|
33936
|
+
function isLowSignalKeyword(pattern) {
|
|
33937
|
+
return tokenize(pattern).length > MAX_RECOMMENDED_KEYWORD_TOKENS;
|
|
33938
|
+
}
|
|
33939
|
+
function splitRawTokens(pattern) {
|
|
33940
|
+
return pattern.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 0);
|
|
33941
|
+
}
|
|
33942
|
+
function keywordNeedleLosesTokens(pattern) {
|
|
33943
|
+
const raw = splitRawTokens(pattern);
|
|
33944
|
+
if (raw.length < 2) return false;
|
|
33945
|
+
return tokenize(pattern).length !== raw.length;
|
|
33946
|
+
}
|
|
33947
|
+
|
|
33948
|
+
// src/lessons/validate-keywords.ts
|
|
33672
33949
|
function collectLowSignalKeywords(graph, findings) {
|
|
33673
33950
|
const activeTriggerIds2 = /* @__PURE__ */ new Set();
|
|
33674
33951
|
for (const lesson of Object.values(graph.lessons)) {
|
|
@@ -33682,7 +33959,7 @@ function collectLowSignalKeywords(graph, findings) {
|
|
|
33682
33959
|
findings.push({
|
|
33683
33960
|
level: "warning",
|
|
33684
33961
|
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
|
|
33962
|
+
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
33963
|
triggerId
|
|
33687
33964
|
});
|
|
33688
33965
|
}
|
|
@@ -33707,9 +33984,62 @@ function collectStopwordKeywords(graph, findings) {
|
|
|
33707
33984
|
});
|
|
33708
33985
|
}
|
|
33709
33986
|
}
|
|
33710
|
-
|
|
33711
|
-
|
|
33987
|
+
|
|
33988
|
+
// src/lessons/glob-breadth.ts
|
|
33989
|
+
var WILDCARD = /[*?[\]]/;
|
|
33990
|
+
function globNarrowness(pattern) {
|
|
33991
|
+
const segments = pattern.replaceAll("\\", "/").split("/").filter((segment) => segment !== "" && segment !== ".");
|
|
33992
|
+
if (segments.length === 0) return 0;
|
|
33993
|
+
let literal = 0;
|
|
33994
|
+
let globstars = 0;
|
|
33995
|
+
for (const segment of segments) {
|
|
33996
|
+
if (segment === "**") globstars += 1;
|
|
33997
|
+
else if (!WILDCARD.test(segment)) literal += 1;
|
|
33998
|
+
}
|
|
33999
|
+
return literal / (segments.length + globstars);
|
|
34000
|
+
}
|
|
34001
|
+
var BROAD_GLOB_NARROWNESS = 0.34;
|
|
34002
|
+
function isBroadFileGlob(pattern) {
|
|
34003
|
+
return globNarrowness(pattern) < BROAD_GLOB_NARROWNESS;
|
|
34004
|
+
}
|
|
34005
|
+
|
|
34006
|
+
// src/lessons/command-pattern-breadth.ts
|
|
34007
|
+
var COMMAND_PROBE_CORPUS = [
|
|
34008
|
+
"git status",
|
|
34009
|
+
'git commit -m "wip"',
|
|
34010
|
+
"pnpm test",
|
|
34011
|
+
"npx vitest run src/x.test.ts",
|
|
34012
|
+
"ls -la",
|
|
34013
|
+
"cat README.md",
|
|
34014
|
+
"rm -rf dist",
|
|
34015
|
+
"mkdir -p build/out",
|
|
34016
|
+
"node scripts/build.js",
|
|
34017
|
+
"docker compose up -d",
|
|
34018
|
+
"curl -s https://example.com",
|
|
34019
|
+
"echo hello > out.txt",
|
|
34020
|
+
"sed -i 's/a/b/' file.txt",
|
|
34021
|
+
"pnpm lint --fix",
|
|
34022
|
+
"python3 -m pytest",
|
|
34023
|
+
"cargo build --release",
|
|
34024
|
+
"make",
|
|
34025
|
+
"npm install --global typescript",
|
|
34026
|
+
"cp a.txt b.txt",
|
|
34027
|
+
"grep -rn TODO src"
|
|
34028
|
+
];
|
|
34029
|
+
var BROAD_HIT_RATIO = 0.5;
|
|
34030
|
+
var PROBE_BUDGET = 1e5;
|
|
34031
|
+
function isBroadCommandPattern(pattern) {
|
|
34032
|
+
const matcher = getCommandMatcher(pattern);
|
|
34033
|
+
if (matcher === null) return false;
|
|
34034
|
+
if (matcher.test("", { remaining: PROBE_BUDGET })) return true;
|
|
34035
|
+
let hits = 0;
|
|
34036
|
+
for (const command of COMMAND_PROBE_CORPUS) {
|
|
34037
|
+
if (matcher.test(command, { remaining: PROBE_BUDGET })) hits += 1;
|
|
34038
|
+
}
|
|
34039
|
+
return hits > COMMAND_PROBE_CORPUS.length * BROAD_HIT_RATIO;
|
|
33712
34040
|
}
|
|
34041
|
+
|
|
34042
|
+
// src/lessons/validate-liveness.ts
|
|
33713
34043
|
function activeTriggerIds(graph) {
|
|
33714
34044
|
const ids = /* @__PURE__ */ new Set();
|
|
33715
34045
|
for (const lesson of Object.values(graph.lessons)) {
|
|
@@ -33761,6 +34091,34 @@ function collectRunnerAnchoredPatterns(graph, findings) {
|
|
|
33761
34091
|
});
|
|
33762
34092
|
}
|
|
33763
34093
|
}
|
|
34094
|
+
function collectBroadFileGlobs(graph, findings) {
|
|
34095
|
+
const active = activeTriggerIds(graph);
|
|
34096
|
+
for (const [triggerId, trigger] of Object.entries(graph.triggers)) {
|
|
34097
|
+
if (trigger.kind !== "file_glob") continue;
|
|
34098
|
+
if (!active.has(triggerId)) continue;
|
|
34099
|
+
if (!isBroadFileGlob(trigger.pattern)) continue;
|
|
34100
|
+
findings.push({
|
|
34101
|
+
level: "warning",
|
|
34102
|
+
code: "BROAD_FILE_GLOB",
|
|
34103
|
+
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\`.`,
|
|
34104
|
+
triggerId
|
|
34105
|
+
});
|
|
34106
|
+
}
|
|
34107
|
+
}
|
|
34108
|
+
function collectBroadCommandPatterns(graph, findings) {
|
|
34109
|
+
const active = activeTriggerIds(graph);
|
|
34110
|
+
for (const [triggerId, trigger] of Object.entries(graph.triggers)) {
|
|
34111
|
+
if (trigger.kind !== "command_pattern") continue;
|
|
34112
|
+
if (!active.has(triggerId)) continue;
|
|
34113
|
+
if (!isBroadCommandPattern(trigger.pattern)) continue;
|
|
34114
|
+
findings.push({
|
|
34115
|
+
level: "warning",
|
|
34116
|
+
code: "BROAD_COMMAND_PATTERN",
|
|
34117
|
+
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\`.`,
|
|
34118
|
+
triggerId
|
|
34119
|
+
});
|
|
34120
|
+
}
|
|
34121
|
+
}
|
|
33764
34122
|
|
|
33765
34123
|
// src/lessons/validate.ts
|
|
33766
34124
|
function validateLessonsGraph(graph, options = {}) {
|
|
@@ -33788,6 +34146,8 @@ function validateLessonsGraph(graph, options = {}) {
|
|
|
33788
34146
|
collectLowSignalKeywords(graph, findings);
|
|
33789
34147
|
collectStopwordKeywords(graph, findings);
|
|
33790
34148
|
collectRunnerAnchoredPatterns(graph, findings);
|
|
34149
|
+
collectBroadCommandPatterns(graph, findings);
|
|
34150
|
+
collectBroadFileGlobs(graph, findings);
|
|
33791
34151
|
if (options.knownPaths !== void 0) collectDeadFileGlobs(graph, findings, options.knownPaths);
|
|
33792
34152
|
const ok = findings.every((f) => f.level !== "error");
|
|
33793
34153
|
return { ok, findings };
|
|
@@ -33839,13 +34199,13 @@ function diag(level, file, message) {
|
|
|
33839
34199
|
|
|
33840
34200
|
// src/core/lint/linter.ts
|
|
33841
34201
|
var EXCLUDE_DIRS = ["node_modules", ".git", "dist", "coverage", ".agentsmesh"];
|
|
34202
|
+
function isExcludedProjectPath(rel2) {
|
|
34203
|
+
const posix9 = rel2.replaceAll("\\", "/");
|
|
34204
|
+
return EXCLUDE_DIRS.some((d) => posix9.includes(`/${d}/`) || posix9.startsWith(`${d}/`));
|
|
34205
|
+
}
|
|
33842
34206
|
async function getProjectFiles(projectRoot) {
|
|
33843
34207
|
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));
|
|
34208
|
+
return all.filter((p) => !isExcludedProjectPath(relative(projectRoot, p))).map((p) => relative(projectRoot, p));
|
|
33849
34209
|
}
|
|
33850
34210
|
async function runLint(config, canonical, projectRoot, targetFilter, options = {}) {
|
|
33851
34211
|
const scope = options.scope ?? "project";
|
|
@@ -34107,6 +34467,7 @@ async function diffOutputChecksums(rootBase, lockOutputs) {
|
|
|
34107
34467
|
// src/core/generate/stale-cleanup.ts
|
|
34108
34468
|
init_fs();
|
|
34109
34469
|
init_builtin_targets();
|
|
34470
|
+
init_registry();
|
|
34110
34471
|
async function listFiles2(root, base = root) {
|
|
34111
34472
|
const entries = await readdir(root, { withFileTypes: true });
|
|
34112
34473
|
const files = [];
|
|
@@ -34127,6 +34488,11 @@ function retainedDirs(inactiveTargets, scope) {
|
|
|
34127
34488
|
}
|
|
34128
34489
|
return dirs;
|
|
34129
34490
|
}
|
|
34491
|
+
function primaryEmitted(target34, scope, expected) {
|
|
34492
|
+
const descriptor34 = getBuiltinTargetDefinition(target34) ?? getDescriptor(target34);
|
|
34493
|
+
const primary = getTargetLayout(target34, scope)?.rootInstructionPath ?? descriptor34?.generators.primaryRootInstructionPath;
|
|
34494
|
+
return primary !== void 0 && expected.has(primary);
|
|
34495
|
+
}
|
|
34130
34496
|
async function findStaleGeneratedOutputs(args) {
|
|
34131
34497
|
const expected = new Set(args.expectedPaths);
|
|
34132
34498
|
const stale = /* @__PURE__ */ new Set();
|
|
@@ -34138,7 +34504,13 @@ async function findStaleGeneratedOutputs(args) {
|
|
|
34138
34504
|
const managed = getTargetManagedOutputs(target34, scope);
|
|
34139
34505
|
if (!managed) continue;
|
|
34140
34506
|
for (const file of managed.coOwnedFiles ?? []) coOwned.add(file);
|
|
34141
|
-
for (const file of managed.files)
|
|
34507
|
+
for (const file of managed.files) {
|
|
34508
|
+
if (generated !== null && !generated.has(file)) continue;
|
|
34509
|
+
stale.add(file);
|
|
34510
|
+
}
|
|
34511
|
+
if (primaryEmitted(target34, scope, expected)) {
|
|
34512
|
+
for (const file of managed.supersededFiles ?? []) stale.add(file);
|
|
34513
|
+
}
|
|
34142
34514
|
for (const dir of managed.dirs) {
|
|
34143
34515
|
if (retained.has(dir)) continue;
|
|
34144
34516
|
const absDir = join(args.projectRoot, dir);
|
|
@@ -34578,6 +34950,23 @@ function todayIso() {
|
|
|
34578
34950
|
}
|
|
34579
34951
|
|
|
34580
34952
|
// src/lessons/add-errors.ts
|
|
34953
|
+
var EmptyRuleError = class extends Error {
|
|
34954
|
+
code = "EMPTY_RULE";
|
|
34955
|
+
constructor() {
|
|
34956
|
+
super("Lesson rule must not be empty \u2014 pass one imperative sentence.");
|
|
34957
|
+
this.name = "EmptyRuleError";
|
|
34958
|
+
}
|
|
34959
|
+
};
|
|
34960
|
+
var BroadCommandPatternError = class extends Error {
|
|
34961
|
+
constructor(pattern) {
|
|
34962
|
+
super(
|
|
34963
|
+
`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").`
|
|
34964
|
+
);
|
|
34965
|
+
this.pattern = pattern;
|
|
34966
|
+
this.name = "BroadCommandPatternError";
|
|
34967
|
+
}
|
|
34968
|
+
code = "BROAD_COMMAND_PATTERN";
|
|
34969
|
+
};
|
|
34581
34970
|
var UnknownTopicError = class extends Error {
|
|
34582
34971
|
constructor(topic) {
|
|
34583
34972
|
super(`Unknown topic: ${topic}. Pass allowNewTopic + topicSummary to create it.`);
|
|
@@ -34617,6 +35006,77 @@ var UnrecallableLessonError = class extends Error {
|
|
|
34617
35006
|
code = "UNRECALLABLE_LESSON";
|
|
34618
35007
|
};
|
|
34619
35008
|
|
|
35009
|
+
// src/lessons/trigger-effectiveness.ts
|
|
35010
|
+
function ineffectiveTriggers(graph, triggerIds) {
|
|
35011
|
+
const out2 = [];
|
|
35012
|
+
for (const id of triggerIds) {
|
|
35013
|
+
const trigger = graph.triggers[id];
|
|
35014
|
+
if (trigger === void 0) continue;
|
|
35015
|
+
const reason = ineffectiveReason(trigger.kind, trigger.pattern);
|
|
35016
|
+
if (reason !== null) out2.push({ id, kind: trigger.kind, pattern: trigger.pattern, reason });
|
|
35017
|
+
}
|
|
35018
|
+
return out2;
|
|
35019
|
+
}
|
|
35020
|
+
function ineffectiveReason(kind, pattern) {
|
|
35021
|
+
if (kind === "keyword") {
|
|
35022
|
+
if (tokenize(pattern).length === 0) {
|
|
35023
|
+
return "keyword has no matchable token after stopword filtering \u2014 it cannot fire on the mandatory --file/--cmd recall path";
|
|
35024
|
+
}
|
|
35025
|
+
if (keywordNeedleLosesTokens(pattern)) {
|
|
35026
|
+
return "keyword contains stopwords/short words, so its needle can never appear as a contiguous run on the mandatory --file/--cmd recall path";
|
|
35027
|
+
}
|
|
35028
|
+
return null;
|
|
35029
|
+
}
|
|
35030
|
+
if (kind === "command_pattern") {
|
|
35031
|
+
let valid = true;
|
|
35032
|
+
try {
|
|
35033
|
+
new RegExp(pattern);
|
|
35034
|
+
} catch {
|
|
35035
|
+
valid = false;
|
|
35036
|
+
}
|
|
35037
|
+
if (!valid) {
|
|
35038
|
+
return "invalid regex \u2014 recall compiles it with new RegExp and swallows the throw as a non-match, so it never fires";
|
|
35039
|
+
}
|
|
35040
|
+
if (!isSafeRegexPattern(pattern)) {
|
|
35041
|
+
return "regex is outside the provably-linear engine \u2014 recall skips it (ReDoS guard), so it never fires";
|
|
35042
|
+
}
|
|
35043
|
+
return null;
|
|
35044
|
+
}
|
|
35045
|
+
return null;
|
|
35046
|
+
}
|
|
35047
|
+
function blockingDeadTriggers(graph, triggerIds) {
|
|
35048
|
+
return ineffectiveTriggers(graph, triggerIds).filter((t) => t.kind !== "command_pattern");
|
|
35049
|
+
}
|
|
35050
|
+
|
|
35051
|
+
// src/lessons/add-gates.ts
|
|
35052
|
+
function assertRuleShape(rule) {
|
|
35053
|
+
const trimmed = rule.trim();
|
|
35054
|
+
if (trimmed.length === 0) throw new EmptyRuleError();
|
|
35055
|
+
if (trimmed.length > MAX_RULE_LENGTH) throw new RuleTooLongError(trimmed.length, MAX_RULE_LENGTH);
|
|
35056
|
+
return trimmed;
|
|
35057
|
+
}
|
|
35058
|
+
function skipsTriggerGates(input, options) {
|
|
35059
|
+
return options.allowNoTrigger === true || input.scope === "always";
|
|
35060
|
+
}
|
|
35061
|
+
function countInputTriggers(triggers) {
|
|
35062
|
+
return (triggers.files?.length ?? 0) + (triggers.commands?.length ?? 0) + (triggers.keywords?.length ?? 0);
|
|
35063
|
+
}
|
|
35064
|
+
function assertTriggerInputs(input, options, existingTriggerCount) {
|
|
35065
|
+
if (!skipsTriggerGates(input, options) && countInputTriggers(input.triggers) === 0 && existingTriggerCount === 0) {
|
|
35066
|
+
throw new NoTriggerError();
|
|
35067
|
+
}
|
|
35068
|
+
if (options.allowNoTrigger !== true) {
|
|
35069
|
+
const broad = (input.triggers.commands ?? []).find(isBroadCommandPattern);
|
|
35070
|
+
if (broad !== void 0) throw new BroadCommandPatternError(broad);
|
|
35071
|
+
}
|
|
35072
|
+
}
|
|
35073
|
+
function assertRecallable(graph, resultingTriggers) {
|
|
35074
|
+
const blockingDead = blockingDeadTriggers(graph, resultingTriggers);
|
|
35075
|
+
if (resultingTriggers.length > 0 && blockingDead.length === resultingTriggers.length) {
|
|
35076
|
+
throw new UnrecallableLessonError(blockingDead);
|
|
35077
|
+
}
|
|
35078
|
+
}
|
|
35079
|
+
|
|
34620
35080
|
// src/lessons/capture-guardrails.ts
|
|
34621
35081
|
var WIDE_GLOB_MATCH_COUNT = 40;
|
|
34622
35082
|
var MAX_RECOMMENDED_TRIGGERS = 8;
|
|
@@ -34655,7 +35115,7 @@ function inspectCapturedLesson(graph, lessonId, knownPaths) {
|
|
|
34655
35115
|
if (lowSignal.length > 0) {
|
|
34656
35116
|
warnings.push({
|
|
34657
35117
|
code: "LOW_SIGNAL_KEYWORD",
|
|
34658
|
-
message: `Lesson "${lessonId}" has long keyword trigger(s) (${lowSignal.join(", ")}); recall matches a keyword only as a
|
|
35118
|
+
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
35119
|
});
|
|
34660
35120
|
}
|
|
34661
35121
|
const stopworded = triggers.filter((t) => t.kind === "keyword" && keywordNeedleLosesTokens(t.pattern)).map((t) => t.pattern);
|
|
@@ -34716,7 +35176,7 @@ function jaccard(a, b) {
|
|
|
34716
35176
|
|
|
34717
35177
|
// src/utils/filesystem/process-lock.ts
|
|
34718
35178
|
init_errors();
|
|
34719
|
-
var DEFAULT_STALE_MS =
|
|
35179
|
+
var DEFAULT_STALE_MS = 6 * 60 * 60 * 1e3;
|
|
34720
35180
|
var DEFAULT_RETRIES = 30;
|
|
34721
35181
|
var DEFAULT_RETRY_DELAY_MS = 200;
|
|
34722
35182
|
var YOUNG_LOCK_GRACE_MS = 2e3;
|
|
@@ -34801,10 +35261,9 @@ async function inspectLock(lockPath) {
|
|
|
34801
35261
|
}
|
|
34802
35262
|
function isStale(meta, staleMs) {
|
|
34803
35263
|
if (!meta) return true;
|
|
34804
|
-
const
|
|
34805
|
-
if (
|
|
34806
|
-
|
|
34807
|
-
return !isProcessAlive(meta.pid);
|
|
35264
|
+
const sameHost = !meta.hostname || meta.hostname === getHostname();
|
|
35265
|
+
if (sameHost && !isProcessAlive(meta.pid)) return true;
|
|
35266
|
+
return Date.now() - meta.started > staleMs;
|
|
34808
35267
|
}
|
|
34809
35268
|
function isProcessAlive(pid) {
|
|
34810
35269
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
@@ -34881,52 +35340,7 @@ async function mutateLessonsGraph(projectRoot, mutator, options = {}) {
|
|
|
34881
35340
|
return mutateLessonsGraphLocked(projectRoot, mutator, options);
|
|
34882
35341
|
}
|
|
34883
35342
|
|
|
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
35343
|
// src/lessons/add.ts
|
|
34927
|
-
function countInputTriggers(triggers) {
|
|
34928
|
-
return (triggers.files?.length ?? 0) + (triggers.commands?.length ?? 0) + (triggers.keywords?.length ?? 0);
|
|
34929
|
-
}
|
|
34930
35344
|
async function addLesson(projectRoot, input, options = {}) {
|
|
34931
35345
|
return mutateLessonsGraph(projectRoot, (graph) => addLessonInto(graph, input, options), {
|
|
34932
35346
|
retries: options.retries
|
|
@@ -34934,10 +35348,7 @@ async function addLesson(projectRoot, input, options = {}) {
|
|
|
34934
35348
|
}
|
|
34935
35349
|
function addLessonInto(graph, input, options) {
|
|
34936
35350
|
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
|
-
}
|
|
35351
|
+
const trimmedRule = assertRuleShape(input.rule);
|
|
34941
35352
|
const existingId = findExistingLessonByRule(graph, ruleKey2);
|
|
34942
35353
|
const isNewTopic = graph.topics[input.topic] === void 0;
|
|
34943
35354
|
if (isNewTopic) {
|
|
@@ -34947,29 +35358,23 @@ function addLessonInto(graph, input, options) {
|
|
|
34947
35358
|
}
|
|
34948
35359
|
graph.topics[input.topic] = { summary: options.topicSummary };
|
|
34949
35360
|
}
|
|
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
|
-
}
|
|
35361
|
+
const existing = existingId !== null ? graph.lessons[existingId] : void 0;
|
|
35362
|
+
assertTriggerInputs(input, options, existing?.triggers.length ?? 0);
|
|
34957
35363
|
const { triggerIds, newTriggerIds } = mergeTriggers(graph, input.triggers);
|
|
34958
|
-
if (!
|
|
34959
|
-
|
|
34960
|
-
|
|
34961
|
-
|
|
34962
|
-
|
|
34963
|
-
}
|
|
35364
|
+
if (!skipsTriggerGates(input, options)) {
|
|
35365
|
+
assertRecallable(
|
|
35366
|
+
graph,
|
|
35367
|
+
existing === void 0 ? triggerIds : union(existing.triggers, triggerIds)
|
|
35368
|
+
);
|
|
34964
35369
|
}
|
|
34965
35370
|
if (existingId !== null) {
|
|
34966
|
-
const
|
|
35371
|
+
const existing2 = graph.lessons[existingId];
|
|
34967
35372
|
graph.lessons[existingId] = {
|
|
34968
|
-
...
|
|
34969
|
-
topics: union(
|
|
34970
|
-
triggers: union(
|
|
34971
|
-
evidence: union(
|
|
34972
|
-
...
|
|
35373
|
+
...existing2,
|
|
35374
|
+
topics: union(existing2.topics, [input.topic]),
|
|
35375
|
+
triggers: union(existing2.triggers, triggerIds),
|
|
35376
|
+
evidence: union(existing2.evidence, input.evidence ?? []),
|
|
35377
|
+
...existing2.rationale === void 0 && input.rationale !== void 0 ? { rationale: input.rationale } : {},
|
|
34973
35378
|
// Re-capturing a rule with --scope always promotes it to always-on.
|
|
34974
35379
|
...input.scope === "always" ? { scope: "always" } : {}
|
|
34975
35380
|
};
|
|
@@ -35150,14 +35555,16 @@ function deriveHaystackTokens(query) {
|
|
|
35150
35555
|
if (query.command !== void 0) parts.push(query.command);
|
|
35151
35556
|
if (parts.length === 0) return [];
|
|
35152
35557
|
const out2 = [];
|
|
35153
|
-
for (const raw of parts.join(" ")
|
|
35154
|
-
if (raw.length === 0) continue;
|
|
35558
|
+
for (const raw of splitTokens(parts.join(" "))) {
|
|
35155
35559
|
out2.push(raw.toLowerCase());
|
|
35156
35560
|
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
35561
|
if (sub.length > 1) out2.push(...sub);
|
|
35158
35562
|
}
|
|
35159
35563
|
return out2;
|
|
35160
35564
|
}
|
|
35565
|
+
function splitTokens(text) {
|
|
35566
|
+
return text.split(/[^A-Za-z0-9]+/).filter((t) => t.length > 0);
|
|
35567
|
+
}
|
|
35161
35568
|
function containsRun(needle, hay) {
|
|
35162
35569
|
if (needle.length === 0) return false;
|
|
35163
35570
|
for (let i = 0; i + needle.length <= hay.length; i += 1) {
|
|
@@ -35173,10 +35580,11 @@ function containsRun(needle, hay) {
|
|
|
35173
35580
|
return false;
|
|
35174
35581
|
}
|
|
35175
35582
|
function keywordMatches(pattern, query) {
|
|
35176
|
-
|
|
35583
|
+
const needle = tokenize(pattern);
|
|
35584
|
+
if (query.keyword !== void 0 && containsRun(needle, splitTokens(query.keyword.toLowerCase()))) {
|
|
35177
35585
|
return true;
|
|
35178
35586
|
}
|
|
35179
|
-
return containsRun(
|
|
35587
|
+
return containsRun(needle, deriveHaystackTokens(query));
|
|
35180
35588
|
}
|
|
35181
35589
|
|
|
35182
35590
|
// src/lessons/query.ts
|
|
@@ -35242,6 +35650,15 @@ function buildFanout(graph) {
|
|
|
35242
35650
|
}
|
|
35243
35651
|
return fanout;
|
|
35244
35652
|
}
|
|
35653
|
+
var KEYWORD_NARROWNESS = 0.4;
|
|
35654
|
+
function buildNarrowness(graph) {
|
|
35655
|
+
const narrowness = /* @__PURE__ */ new Map();
|
|
35656
|
+
for (const [id, trigger] of Object.entries(graph.triggers)) {
|
|
35657
|
+
if (trigger.kind === "file_glob") narrowness.set(id, globNarrowness(trigger.pattern));
|
|
35658
|
+
else narrowness.set(id, trigger.kind === "keyword" ? KEYWORD_NARROWNESS : 1);
|
|
35659
|
+
}
|
|
35660
|
+
return narrowness;
|
|
35661
|
+
}
|
|
35245
35662
|
function buildTopicCoherence(matches) {
|
|
35246
35663
|
const topicCount = /* @__PURE__ */ new Map();
|
|
35247
35664
|
for (const { lesson } of matches) {
|
|
@@ -35260,7 +35677,7 @@ function buildTopicCoherence(matches) {
|
|
|
35260
35677
|
var DEFAULT_RECALL_LIMIT = 10;
|
|
35261
35678
|
var DEFAULT_RECALL_MAX_TOKENS = 400;
|
|
35262
35679
|
var RRF_K = 60;
|
|
35263
|
-
var SPECIFICITY_WEIGHT =
|
|
35680
|
+
var SPECIFICITY_WEIGHT = 5;
|
|
35264
35681
|
var TOPIC_COHERENCE_WEIGHT = 2;
|
|
35265
35682
|
var BM25_WEIGHT = 1;
|
|
35266
35683
|
var EFFECTIVENESS_WEIGHT = 1;
|
|
@@ -35287,12 +35704,15 @@ function rankLessons(graph, query, matches, options = {}) {
|
|
|
35287
35704
|
const terms = queryTerms(query);
|
|
35288
35705
|
const corpus = buildCorpus(graph);
|
|
35289
35706
|
const fanout = buildFanout(graph);
|
|
35707
|
+
const narrowness = buildNarrowness(graph);
|
|
35290
35708
|
const coherence = buildTopicCoherence(matches);
|
|
35291
35709
|
const matchedTriggerIds = collectMatchedTriggerIds(graph, query);
|
|
35292
35710
|
const scored = matches.map(({ id, lesson }) => {
|
|
35293
35711
|
const hitTriggers = lesson.triggers.filter((t) => matchedTriggerIds.has(t));
|
|
35294
35712
|
let specificity = 0;
|
|
35295
|
-
for (const t of hitTriggers)
|
|
35713
|
+
for (const t of hitTriggers) {
|
|
35714
|
+
specificity = Math.max(specificity, (narrowness.get(t) ?? 1) / fanout.get(t));
|
|
35715
|
+
}
|
|
35296
35716
|
return {
|
|
35297
35717
|
id,
|
|
35298
35718
|
lesson,
|
|
@@ -35350,7 +35770,8 @@ function defaultLessonsConfig() {
|
|
|
35350
35770
|
recallLimit: DEFAULT_RECALL_LIMIT,
|
|
35351
35771
|
recallMaxTokens: DEFAULT_RECALL_MAX_TOKENS,
|
|
35352
35772
|
autoPrune: false,
|
|
35353
|
-
repairTriggers: false
|
|
35773
|
+
repairTriggers: false,
|
|
35774
|
+
telemetry: false
|
|
35354
35775
|
};
|
|
35355
35776
|
}
|
|
35356
35777
|
function outcomeLogPath(projectRoot) {
|
|
@@ -35404,9 +35825,9 @@ var LINE_REFS = String.raw`L\d+(?:\s*,\s*L\d+)*`;
|
|
|
35404
35825
|
var LINE_REF_PATTERNS = [
|
|
35405
35826
|
new RegExp(String.raw`\s*\bSee\s+${LINE_REFS}\.?`, "g"),
|
|
35406
35827
|
// " See L128." / " See L140, L149"
|
|
35407
|
-
new RegExp(String.raw`\s*\((?:${LINE_REFS})\)
|
|
35828
|
+
new RegExp(String.raw`\s*\((?:${LINE_REFS})\)`, "g"),
|
|
35408
35829
|
// " (L174)" / " (L92, L163)"
|
|
35409
|
-
new RegExp(String.raw`\s*\[(?:${LINE_REFS})\]
|
|
35830
|
+
new RegExp(String.raw`\s*\[(?:${LINE_REFS})\]`, "g")
|
|
35410
35831
|
// " [L161, L208]"
|
|
35411
35832
|
];
|
|
35412
35833
|
var ALSO_RELEVANT_PATTERN = /\s*\(also relevant[^)]*\)\s*/g;
|
|
@@ -35440,42 +35861,7 @@ async function stripMarkersInGraph(projectRoot, options = {}) {
|
|
|
35440
35861
|
});
|
|
35441
35862
|
return { changedIds, changedCount: changedIds.length };
|
|
35442
35863
|
}
|
|
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
|
-
}
|
|
35864
|
+
init_recall_hook_scaffold();
|
|
35479
35865
|
|
|
35480
35866
|
// src/lessons/merge-driver-setup.ts
|
|
35481
35867
|
var LESSONS_MERGE_DRIVER = "agentsmesh-lessons";
|