agentsmesh 0.41.0 → 0.42.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 +36 -0
- package/README.md +2 -2
- package/dist/canonical.js +369 -194
- package/dist/canonical.js.map +1 -1
- package/dist/cli.js +267 -258
- package/dist/engine.d.ts +6 -1
- package/dist/engine.js +505 -262
- package/dist/engine.js.map +1 -1
- package/dist/index.js +625 -320
- package/dist/index.js.map +1 -1
- package/dist/lessons.d.ts +5 -0
- package/dist/lessons.js +170 -107
- package/dist/lessons.js.map +1 -1
- package/dist/targets.js +437 -252
- package/dist/targets.js.map +1 -1
- package/package.json +1 -1
package/dist/lessons.d.ts
CHANGED
|
@@ -42,6 +42,11 @@ interface RecallOptions {
|
|
|
42
42
|
* compact/clear, which is the exact signal a wall-clock TTL only approximates.
|
|
43
43
|
*/
|
|
44
44
|
readonly ttlMs?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Migrate a legacy store first (default true). The hook passes false: the
|
|
47
|
+
* migration deletes files, so it only runs from a command the user runs.
|
|
48
|
+
*/
|
|
49
|
+
readonly autoMigrate?: boolean;
|
|
45
50
|
}
|
|
46
51
|
interface RecallResult {
|
|
47
52
|
/** Relevance-ranked, capped lessons (compact metadata lives on each entry). */
|
package/dist/lessons.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { createHash, randomUUID } from 'crypto';
|
|
3
|
-
import { readFileSync, existsSync, mkdirSync, writeFileSync, rmSync, statSync, renameSync, readdirSync,
|
|
4
|
-
import {
|
|
3
|
+
import { readFileSync, existsSync, mkdirSync, writeFileSync, rmSync, statSync, renameSync, readdirSync, realpathSync, chmodSync, accessSync, constants, appendFileSync, lstatSync, rmdirSync, unlinkSync, openSync, readSync, closeSync } from 'fs';
|
|
4
|
+
import { join, relative, sep, resolve, dirname, posix, basename, extname } from 'path';
|
|
5
|
+
import { mkdir, writeFile, rm, readFile, lstat, open, readdir, rmdir, unlink, stat, rename, realpath } from 'fs/promises';
|
|
6
|
+
import { hostname, tmpdir } from 'os';
|
|
5
7
|
import picomatch from 'picomatch';
|
|
6
8
|
import { execFile, spawnSync } from 'child_process';
|
|
7
|
-
import { hostname, tmpdir } from 'os';
|
|
8
|
-
import { mkdir, writeFile, rm, readFile, lstat, open, readdir, rmdir, unlink, stat, rename, realpath } from 'fs/promises';
|
|
9
9
|
import { setTimeout } from 'timers/promises';
|
|
10
10
|
import { promisify } from 'util';
|
|
11
11
|
import { stringify, parse, parseDocument, YAMLSeq, YAMLMap } from 'yaml';
|
|
@@ -566,6 +566,115 @@ function executableModeFor(path) {
|
|
|
566
566
|
function stripBom(text) {
|
|
567
567
|
return text.startsWith(UTF8_BOM) ? text.slice(UTF8_BOM.length) : text;
|
|
568
568
|
}
|
|
569
|
+
async function canonicalizePath(path) {
|
|
570
|
+
try {
|
|
571
|
+
return await realpath(path);
|
|
572
|
+
} catch (error) {
|
|
573
|
+
if (error.code !== "ENOENT") throw error;
|
|
574
|
+
const parent = dirname(path);
|
|
575
|
+
if (parent === path) return resolve(path);
|
|
576
|
+
return join(await canonicalizePath(parent), basename(path));
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
function canonicalizePathSync(path) {
|
|
580
|
+
try {
|
|
581
|
+
return realpathSync(path);
|
|
582
|
+
} catch (error) {
|
|
583
|
+
if (error.code !== "ENOENT") return null;
|
|
584
|
+
}
|
|
585
|
+
try {
|
|
586
|
+
lstatSync(path);
|
|
587
|
+
return null;
|
|
588
|
+
} catch {
|
|
589
|
+
const parent = dirname(path);
|
|
590
|
+
if (parent === path) return resolve(path);
|
|
591
|
+
const realParent = canonicalizePathSync(parent);
|
|
592
|
+
return realParent === null ? null : join(realParent, basename(path));
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
function resolvesInsideRootSync(root, target) {
|
|
596
|
+
const realRoot = canonicalizePathSync(resolve(root));
|
|
597
|
+
const realTarget = canonicalizePathSync(resolve(target));
|
|
598
|
+
return realRoot !== null && realTarget !== null && isPathInside(realTarget, realRoot);
|
|
599
|
+
}
|
|
600
|
+
function isPathInside(target, root) {
|
|
601
|
+
return target === root || target.startsWith(root.endsWith(sep) ? root : `${root}${sep}`);
|
|
602
|
+
}
|
|
603
|
+
var display = (path) => path.replaceAll("\\", "/");
|
|
604
|
+
async function assertPathInsideRoot(root, target) {
|
|
605
|
+
const rootAbs = resolve(root);
|
|
606
|
+
const targetAbs = resolve(target);
|
|
607
|
+
if (!isPathInside(targetAbs, rootAbs)) {
|
|
608
|
+
throw new Error(`Unsafe filesystem path: ${display(target)} is outside ${display(rootAbs)}`);
|
|
609
|
+
}
|
|
610
|
+
let realTarget;
|
|
611
|
+
let realRoot;
|
|
612
|
+
try {
|
|
613
|
+
[realTarget, realRoot] = await Promise.all([
|
|
614
|
+
canonicalizePath(targetAbs),
|
|
615
|
+
canonicalizePath(rootAbs)
|
|
616
|
+
]);
|
|
617
|
+
} catch (cause) {
|
|
618
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
619
|
+
throw new Error(
|
|
620
|
+
`Unsafe filesystem path: ${display(target)} could not be resolved (${detail})`,
|
|
621
|
+
{ cause }
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
if (isPathInside(realTarget, realRoot)) return;
|
|
625
|
+
throw new Error(
|
|
626
|
+
`Unsafe filesystem path: ${display(target)} resolves to ${display(realTarget)} outside ${display(realRoot)}`
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
var BASE_REL = ".agentsmesh/lessons";
|
|
630
|
+
function lessonsPaths(projectRoot) {
|
|
631
|
+
const base = join(projectRoot, BASE_REL);
|
|
632
|
+
return {
|
|
633
|
+
base,
|
|
634
|
+
graph: join(base, "lessons.json"),
|
|
635
|
+
config: join(base, "config.json"),
|
|
636
|
+
journal: join(base, "journal.md"),
|
|
637
|
+
index: join(base, "index.yaml"),
|
|
638
|
+
topicsDir: join(base, "topics")
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
function toRelPath(projectRoot, absolute) {
|
|
642
|
+
return relative(projectRoot, absolute).split(sep).join("/");
|
|
643
|
+
}
|
|
644
|
+
var LESSONS_PROCEDURAL_RULE = `## Lessons (BLOCKING)
|
|
645
|
+
|
|
646
|
+
Graph \`.agentsmesh/lessons/lessons.json\` is canonical; never hand-edit it. Manual: \`lessons\` skill.
|
|
647
|
+
|
|
648
|
+
**Recall:** before every file edit or state-changing command, MUST run \`agentsmesh lessons query --file <path> --cmd <command> --session auto\` and obey matches; at task start, ALSO run \`agentsmesh lessons query --keyword "<task terms>" --always --session auto\` for conceptual + universal rules no path/command names. Pure-read commands and recall itself are exempt.
|
|
649
|
+
|
|
650
|
+
**Capture:** after any failure, user correction, regression, wrong assumption, useful surprise, repeated friction, or non-obvious fix, MUST self-critique and run \`agentsmesh lessons add "<imperative rule>" --topic <id> --trigger-file <glob> --evidence <sha|lesson-id>\`.
|
|
651
|
+
|
|
652
|
+
**Before final:** report \`Lesson: captured <id>\` or \`Lesson: none\`. No recall/capture gate = task incomplete. No shell: use \`lessons_query\` / \`lessons_add\`.`;
|
|
653
|
+
|
|
654
|
+
// src/lessons/lessons-dir-guard.ts
|
|
655
|
+
function lessonsDirInsideProject(projectRoot) {
|
|
656
|
+
return resolvesInsideRootSync(projectRoot, lessonsPaths(projectRoot).base);
|
|
657
|
+
}
|
|
658
|
+
var LessonsDirOutsideProjectError = class extends Error {
|
|
659
|
+
code = "LESSONS_DIR_OUTSIDE_PROJECT";
|
|
660
|
+
constructor(projectRoot) {
|
|
661
|
+
const base = lessonsPaths(projectRoot).base;
|
|
662
|
+
let where = "a path that does not exist";
|
|
663
|
+
try {
|
|
664
|
+
where = realpathSync(base).replaceAll("\\", "/");
|
|
665
|
+
} catch {
|
|
666
|
+
}
|
|
667
|
+
super(
|
|
668
|
+
`${base.replaceAll("\\", "/")} resolves to ${where}, outside the project ${projectRoot.replaceAll("\\", "/")}. agentsmesh only writes lessons inside the project; replace the link with a real folder.`
|
|
669
|
+
);
|
|
670
|
+
this.name = "LessonsDirOutsideProjectError";
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
function assertLessonsDirInsideProject(projectRoot) {
|
|
674
|
+
if (!lessonsDirInsideProject(projectRoot)) throw new LessonsDirOutsideProjectError(projectRoot);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// src/lessons/graph-store.ts
|
|
569
678
|
var LESSONS_GRAPH_PATH = ".agentsmesh/lessons/lessons.json";
|
|
570
679
|
function graphFilePath(projectRoot) {
|
|
571
680
|
return resolve(projectRoot, LESSONS_GRAPH_PATH);
|
|
@@ -620,6 +729,7 @@ function isWritable(path) {
|
|
|
620
729
|
}
|
|
621
730
|
}
|
|
622
731
|
function saveLessonsGraph(projectRoot, graph) {
|
|
732
|
+
assertLessonsDirInsideProject(projectRoot);
|
|
623
733
|
const path = graphFilePath(projectRoot);
|
|
624
734
|
mkdirSync(dirname(path), { recursive: true });
|
|
625
735
|
const mode = fileMode(path);
|
|
@@ -693,42 +803,8 @@ function makeTriggerId2(spec) {
|
|
|
693
803
|
const hash = createHash("sha1").update(`${spec.kind}|${spec.pattern}`).digest("hex").slice(0, 8);
|
|
694
804
|
return `t-${TRIGGER_PREFIX2[spec.kind]}-${hash}`;
|
|
695
805
|
}
|
|
696
|
-
var RULE_HEADING_RE = /^##\s+Rules\b.*$/i;
|
|
697
|
-
var NEXT_HEADING_RE = /^##\s+/;
|
|
698
|
-
var RULE_LINE_RE = /^(\d+)\.\s+(.+?)\s*$/;
|
|
699
|
-
var EVIDENCE_TAIL_RE = /\s*\(Evidence:?\s+([^)]+)\)\s*$/;
|
|
700
|
-
var EVIDENCE_REF_RE = /L\d+/g;
|
|
701
|
-
function parseRulesSection(markdown) {
|
|
702
|
-
const lines = markdown.split(/\r?\n/);
|
|
703
|
-
let inRules = false;
|
|
704
|
-
const rules = [];
|
|
705
|
-
for (const line of lines) {
|
|
706
|
-
if (!inRules) {
|
|
707
|
-
if (RULE_HEADING_RE.test(line)) inRules = true;
|
|
708
|
-
continue;
|
|
709
|
-
}
|
|
710
|
-
if (NEXT_HEADING_RE.test(line)) break;
|
|
711
|
-
const m = RULE_LINE_RE.exec(line);
|
|
712
|
-
if (m === null) continue;
|
|
713
|
-
const ruleIndex = Number(m[1]);
|
|
714
|
-
let body = m[2];
|
|
715
|
-
const evidence = [];
|
|
716
|
-
let tail = EVIDENCE_TAIL_RE.exec(body);
|
|
717
|
-
while (tail !== null) {
|
|
718
|
-
const refs = tail[1];
|
|
719
|
-
const matches = refs.match(EVIDENCE_REF_RE);
|
|
720
|
-
if (matches !== null) evidence.unshift(...matches);
|
|
721
|
-
body = body.slice(0, tail.index).trimEnd();
|
|
722
|
-
tail = EVIDENCE_TAIL_RE.exec(body);
|
|
723
|
-
}
|
|
724
|
-
rules.push({ index: ruleIndex, body, evidence });
|
|
725
|
-
}
|
|
726
|
-
return rules;
|
|
727
|
-
}
|
|
728
806
|
var LEGACY_ARTIFACT_REL = [
|
|
729
807
|
"index.yaml",
|
|
730
|
-
"journal.md",
|
|
731
|
-
"journal.legacy.md",
|
|
732
808
|
"topics",
|
|
733
809
|
"distill-ledger.yaml",
|
|
734
810
|
"distill-proposal.md"
|
|
@@ -1681,30 +1757,6 @@ function parseRemovals(out2) {
|
|
|
1681
1757
|
}
|
|
1682
1758
|
return { deleted, renamedAway };
|
|
1683
1759
|
}
|
|
1684
|
-
var BASE_REL = ".agentsmesh/lessons";
|
|
1685
|
-
function lessonsPaths(projectRoot) {
|
|
1686
|
-
const base = join(projectRoot, BASE_REL);
|
|
1687
|
-
return {
|
|
1688
|
-
base,
|
|
1689
|
-
graph: join(base, "lessons.json"),
|
|
1690
|
-
config: join(base, "config.json"),
|
|
1691
|
-
journal: join(base, "journal.md"),
|
|
1692
|
-
index: join(base, "index.yaml"),
|
|
1693
|
-
topicsDir: join(base, "topics")
|
|
1694
|
-
};
|
|
1695
|
-
}
|
|
1696
|
-
function toRelPath(projectRoot, absolute) {
|
|
1697
|
-
return relative(projectRoot, absolute).split(sep).join("/");
|
|
1698
|
-
}
|
|
1699
|
-
var LESSONS_PROCEDURAL_RULE = `## Lessons (BLOCKING)
|
|
1700
|
-
|
|
1701
|
-
Graph \`.agentsmesh/lessons/lessons.json\` is canonical; never hand-edit it. Manual: \`lessons\` skill.
|
|
1702
|
-
|
|
1703
|
-
**Recall:** before every file edit or state-changing command, MUST run \`agentsmesh lessons query --file <path> --cmd <command> --session auto\` and obey matches; at task start, ALSO run \`agentsmesh lessons query --keyword "<task terms>" --always --session auto\` for conceptual + universal rules no path/command names. Pure-read commands and recall itself are exempt.
|
|
1704
|
-
|
|
1705
|
-
**Capture:** after any failure, user correction, regression, wrong assumption, useful surprise, repeated friction, or non-obvious fix, MUST self-critique and run \`agentsmesh lessons add "<imperative rule>" --topic <id> --trigger-file <glob> --evidence <sha|lesson-id>\`.
|
|
1706
|
-
|
|
1707
|
-
**Before final:** report \`Lesson: captured <id>\` or \`Lesson: none\`. No recall/capture gate = task incomplete. No shell: use \`lessons_query\` / \`lessons_add\`.`;
|
|
1708
1760
|
|
|
1709
1761
|
// src/lessons/project-files.ts
|
|
1710
1762
|
var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
@@ -2448,6 +2500,7 @@ function lessonsLockPath(projectRoot) {
|
|
|
2448
2500
|
return resolve(projectRoot, ".agentsmesh/lessons", LESSONS_LOCK_FILENAME);
|
|
2449
2501
|
}
|
|
2450
2502
|
async function acquireLessonsLock(projectRoot, opts = {}) {
|
|
2503
|
+
assertLessonsDirInsideProject(projectRoot);
|
|
2451
2504
|
return acquireProcessLock(lessonsLockPath(projectRoot), {
|
|
2452
2505
|
retries: opts.retries ?? LESSONS_LOCK_OPTIONS.retries,
|
|
2453
2506
|
retryDelayMs: opts.retryDelayMs ?? LESSONS_LOCK_OPTIONS.retryDelayMs,
|
|
@@ -2987,44 +3040,47 @@ async function mergeLegacy(projectRoot, paths, specs, summaryByTopic, options) {
|
|
|
2987
3040
|
triggerCount: addedTriggers.size
|
|
2988
3041
|
};
|
|
2989
3042
|
}
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3043
|
+
|
|
3044
|
+
// src/lessons/import-legacy-rules.ts
|
|
3045
|
+
var HEADING = /^#{1,6}\s/;
|
|
3046
|
+
var SECTION_HEADING = /^#{1,2}\s/;
|
|
3047
|
+
var RULES_HEADING = /^##\s+(?:Rules|Lessons)\b/i;
|
|
3048
|
+
var ITEM = /^\s{0,3}(?:\d+[.)]|[-*+])\s+(.+?)\s*$/;
|
|
3049
|
+
var EVIDENCE_TAIL = /\s*\(Evidence:?\s+([^)]+)\)\s*$/;
|
|
3050
|
+
var EVIDENCE_REF = /L\d+/g;
|
|
3051
|
+
function withEvidence(index, text) {
|
|
3052
|
+
let body = text;
|
|
3053
|
+
const evidence = [];
|
|
3054
|
+
let tail = EVIDENCE_TAIL.exec(body);
|
|
3055
|
+
while (tail !== null) {
|
|
3056
|
+
evidence.unshift(...tail[1].match(EVIDENCE_REF) ?? []);
|
|
3057
|
+
body = body.slice(0, tail.index).trimEnd();
|
|
3058
|
+
tail = EVIDENCE_TAIL.exec(body);
|
|
3059
|
+
}
|
|
3060
|
+
return { index, body, evidence };
|
|
3002
3061
|
}
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
)
|
|
3023
|
-
}
|
|
3024
|
-
|
|
3025
|
-
throw new Error(
|
|
3026
|
-
`Unsafe filesystem path: ${display(target)} resolves to ${display(realTarget)} outside ${display(realRoot)}`
|
|
3027
|
-
);
|
|
3062
|
+
function parseRulesSection(markdown) {
|
|
3063
|
+
const items = [];
|
|
3064
|
+
let inRules = false;
|
|
3065
|
+
let open2 = false;
|
|
3066
|
+
let strayLine = null;
|
|
3067
|
+
markdown.split(/\r?\n/).forEach((line, i) => {
|
|
3068
|
+
if (HEADING.test(line)) {
|
|
3069
|
+
if (SECTION_HEADING.test(line)) inRules = RULES_HEADING.test(line);
|
|
3070
|
+
open2 = false;
|
|
3071
|
+
return;
|
|
3072
|
+
}
|
|
3073
|
+
const item = ITEM.exec(line);
|
|
3074
|
+
if (item !== null) {
|
|
3075
|
+
if (inRules) items.push(item[1]);
|
|
3076
|
+
else strayLine ??= i + 1;
|
|
3077
|
+
open2 = inRules;
|
|
3078
|
+
return;
|
|
3079
|
+
}
|
|
3080
|
+
if (line.trim() === "") open2 = false;
|
|
3081
|
+
else if (open2) items[items.length - 1] += ` ${line.trim()}`;
|
|
3082
|
+
});
|
|
3083
|
+
return { rules: items.map((text, i) => withEvidence(i + 1, text)), strayLine };
|
|
3028
3084
|
}
|
|
3029
3085
|
|
|
3030
3086
|
// src/lessons/import-legacy-read.ts
|
|
@@ -3071,9 +3127,13 @@ async function readLegacySource(projectRoot, migratedAt) {
|
|
|
3071
3127
|
`Legacy topic file is missing: ${cluster.file}. Refusing to migrate (legacy artifacts left intact).`
|
|
3072
3128
|
);
|
|
3073
3129
|
}
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3130
|
+
const parsed = parseRulesSection(readFileSync(topicFile, "utf8"));
|
|
3131
|
+
if (parsed.strayLine !== null) {
|
|
3132
|
+
throw new Error(
|
|
3133
|
+
`Legacy lessons were not migrated: ${cluster.file} line ${parsed.strayLine} is a list item outside a "## Rules" or "## Lessons" section. Move it under one of them or delete it, then run \`agentsmesh lessons import-md\`. Nothing was changed.`
|
|
3134
|
+
);
|
|
3135
|
+
}
|
|
3136
|
+
for (const { index: ruleIndex, body, evidence } of parsed.rules) {
|
|
3077
3137
|
const lessonEvidence = [
|
|
3078
3138
|
`legacy:${cluster.file}#rule-${ruleIndex}`,
|
|
3079
3139
|
...evidence.map((e) => `legacy:${e}`)
|
|
@@ -3477,7 +3537,7 @@ function isTelemetryEnabled(env = process.env, projectRoot) {
|
|
|
3477
3537
|
return envOverride(env[TELEMETRY_ENV]) ?? (projectRoot !== void 0 && configFlag(projectRoot, "telemetry") === true);
|
|
3478
3538
|
}
|
|
3479
3539
|
function appendRecallRecord(projectRoot, record, env = process.env) {
|
|
3480
|
-
if (!isTelemetryEnabled(env, projectRoot)) return;
|
|
3540
|
+
if (!isTelemetryEnabled(env, projectRoot) || !lessonsDirInsideProject(projectRoot)) return;
|
|
3481
3541
|
appendJsonl(recallLogPath(projectRoot), record, {
|
|
3482
3542
|
maxRecords: MAX_RECALL_LOG_RECORDS,
|
|
3483
3543
|
trimTriggerBytes: RECALL_LOG_TRIM_TRIGGER_BYTES
|
|
@@ -4072,7 +4132,7 @@ var EMPTY = { ids: /* @__PURE__ */ new Set(), stamps: null };
|
|
|
4072
4132
|
// src/lessons/recall.ts
|
|
4073
4133
|
async function recallLessons(projectRoot, query, options = {}) {
|
|
4074
4134
|
try {
|
|
4075
|
-
await maybeAutoMigrateLessons(projectRoot);
|
|
4135
|
+
if (options.autoMigrate !== false) await maybeAutoMigrateLessons(projectRoot);
|
|
4076
4136
|
} catch {
|
|
4077
4137
|
}
|
|
4078
4138
|
const preReadStamp = currentGraphStamp(projectRoot);
|
|
@@ -4226,7 +4286,7 @@ function captureLogPath(projectRoot) {
|
|
|
4226
4286
|
return join(lessonsPaths(projectRoot).base, "capture-log.jsonl");
|
|
4227
4287
|
}
|
|
4228
4288
|
function appendCaptureRecord(projectRoot, record, env = process.env) {
|
|
4229
|
-
if (!isTelemetryEnabled(env, projectRoot)) return;
|
|
4289
|
+
if (!isTelemetryEnabled(env, projectRoot) || !lessonsDirInsideProject(projectRoot)) return;
|
|
4230
4290
|
appendJsonl(captureLogPath(projectRoot), record, {
|
|
4231
4291
|
maxRecords: MAX_CAPTURE_LOG_RECORDS,
|
|
4232
4292
|
trimTriggerBytes: CAPTURE_LOG_TRIM_TRIGGER_BYTES
|
|
@@ -4665,11 +4725,13 @@ ${yamlStr}
|
|
|
4665
4725
|
|
|
4666
4726
|
${body}`;
|
|
4667
4727
|
}
|
|
4668
|
-
var LESSONS_CONTRACT_START = "<!-- agentsmesh:lessons-contract:start -->";
|
|
4669
|
-
var LESSONS_CONTRACT_END = "<!-- agentsmesh:lessons-contract:end -->";
|
|
4670
4728
|
function escapeRegExp(value) {
|
|
4671
4729
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4672
4730
|
}
|
|
4731
|
+
|
|
4732
|
+
// src/targets/projection/managed-blocks.ts
|
|
4733
|
+
var LESSONS_CONTRACT_START = "<!-- agentsmesh:lessons-contract:start -->";
|
|
4734
|
+
var LESSONS_CONTRACT_END = "<!-- agentsmesh:lessons-contract:end -->";
|
|
4673
4735
|
function managedBlockPattern(start, end) {
|
|
4674
4736
|
return new RegExp(`${escapeRegExp(start)}[\\s\\S]*?${escapeRegExp(end)}`, "g");
|
|
4675
4737
|
}
|
|
@@ -4814,6 +4876,7 @@ async function scaffoldLessons(projectRoot) {
|
|
|
4814
4876
|
const created = [];
|
|
4815
4877
|
const updated = [];
|
|
4816
4878
|
const skipped = [];
|
|
4879
|
+
assertLessonsDirInsideProject(projectRoot);
|
|
4817
4880
|
mkdirSync(paths.base, { recursive: true });
|
|
4818
4881
|
await maybeAutoMigrateLessons(projectRoot);
|
|
4819
4882
|
if (existsSync(paths.graph)) {
|