agentsmesh 0.21.0 → 0.22.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.
@@ -0,0 +1,155 @@
1
+ import { z } from 'zod';
2
+
3
+ declare function hashBullet(bullet: string): string;
4
+
5
+ interface ParsedBullet {
6
+ text: string;
7
+ lineNumber: number;
8
+ }
9
+ declare function parseBullets(markdown: string): ParsedBullet[];
10
+
11
+ declare const ClusterSchema: z.ZodObject<{
12
+ topic: z.ZodString;
13
+ file: z.ZodString;
14
+ summary: z.ZodString;
15
+ triggers: z.ZodObject<{
16
+ file_globs: z.ZodArray<z.ZodString>;
17
+ command_patterns: z.ZodArray<z.ZodString>;
18
+ keywords: z.ZodArray<z.ZodString>;
19
+ }, z.core.$strip>;
20
+ }, z.core.$strip>;
21
+ declare const LessonsIndexSchema: z.ZodObject<{
22
+ version: z.ZodLiteral<1>;
23
+ clusters: z.ZodArray<z.ZodObject<{
24
+ topic: z.ZodString;
25
+ file: z.ZodString;
26
+ summary: z.ZodString;
27
+ triggers: z.ZodObject<{
28
+ file_globs: z.ZodArray<z.ZodString>;
29
+ command_patterns: z.ZodArray<z.ZodString>;
30
+ keywords: z.ZodArray<z.ZodString>;
31
+ }, z.core.$strip>;
32
+ }, z.core.$strip>>;
33
+ }, z.core.$strip>;
34
+ type LessonsIndex = z.infer<typeof LessonsIndexSchema>;
35
+ type LessonsCluster = z.infer<typeof ClusterSchema>;
36
+ declare function parseIndex(raw: unknown): LessonsIndex;
37
+
38
+ type ToolEvent = {
39
+ kind: 'edit' | 'write';
40
+ filePath: string;
41
+ } | {
42
+ kind: 'bash';
43
+ command: string;
44
+ } | {
45
+ kind: 'task';
46
+ text: string;
47
+ };
48
+ declare function matchTriggers(clusters: readonly LessonsCluster[], event: ToolEvent): LessonsCluster[];
49
+
50
+ declare const LedgerSchema: z.ZodObject<{
51
+ version: z.ZodLiteral<1>;
52
+ assignments: z.ZodRecord<z.ZodString, z.ZodString>;
53
+ }, z.core.$strip>;
54
+ type Ledger = z.infer<typeof LedgerSchema>;
55
+ declare function loadLedger(path: string): Ledger;
56
+ declare function saveLedger(path: string, ledger: Ledger): void;
57
+
58
+ interface ScoredCluster {
59
+ cluster: LessonsCluster;
60
+ score: number;
61
+ }
62
+ declare function scoreBullet(bullet: string, clusters: readonly LessonsCluster[]): ScoredCluster[];
63
+
64
+ interface TriggeredLesson {
65
+ readonly cluster: LessonsCluster;
66
+ readonly relativePath: string;
67
+ readonly filePath: string;
68
+ readonly content: string;
69
+ }
70
+ interface LessonCaptureInput {
71
+ readonly heading: string;
72
+ readonly whatWentWrong: string;
73
+ readonly rootCause: string;
74
+ readonly rule: string;
75
+ }
76
+ interface AppendLessonResult {
77
+ readonly journalPath: string;
78
+ readonly bullet: string;
79
+ readonly lineNumber: number;
80
+ }
81
+ declare function loadLessonsIndex(projectRoot: string): LessonsIndex;
82
+ declare function readTriggeredLessons(projectRoot: string, event: ToolEvent): TriggeredLesson[];
83
+ declare function formatLessonBullet(input: LessonCaptureInput): string;
84
+ declare function appendLessonToJournal(projectRoot: string, input: LessonCaptureInput): AppendLessonResult;
85
+
86
+ /**
87
+ * Default on-disk locations for the lessons subsystem.
88
+ *
89
+ * All artifacts live under `<projectRoot>/.agentsmesh/lessons/`, so the lessons
90
+ * subsystem is a self-contained canonical feature — `agentsmesh init --lessons`
91
+ * scaffolds this directory and the procedural rule, and removal is a single
92
+ * directory delete.
93
+ *
94
+ * Path values are absolute; `*Rel` helpers return forward-slash project-relative
95
+ * paths suitable for embedding in markdown rules consumed by any agent target.
96
+ */
97
+ interface LessonsPaths {
98
+ /** Directory containing every lessons artifact. */
99
+ readonly base: string;
100
+ /** Append-only journal — point of capture for new lessons. */
101
+ readonly journal: string;
102
+ /** Trigger index — maps file_globs / command_patterns / keywords to topic files. */
103
+ readonly index: string;
104
+ /** Distill ledger — bullet hash → assigned topic (or "skip"). */
105
+ readonly ledger: string;
106
+ /** Distill proposal file — generated by `distill`, consumed by `distill:apply`. */
107
+ readonly proposal: string;
108
+ /** Directory holding one markdown file per topic cluster. */
109
+ readonly topicsDir: string;
110
+ }
111
+ declare function lessonsPaths(projectRoot: string): LessonsPaths;
112
+ /**
113
+ * Project-relative path for a given absolute path, normalized to forward
114
+ * slashes for cross-platform consistency in markdown rule files.
115
+ */
116
+ declare function toRelPath(projectRoot: string, absolute: string): string;
117
+ /**
118
+ * Empty-journal contents used by init scaffolding. The journal grows from here
119
+ * as failures get recorded.
120
+ */
121
+ declare const LESSONS_JOURNAL_TEMPLATE = "# Lessons Learned\n\n";
122
+ /**
123
+ * Empty index used by init scaffolding. Schema allows zero clusters; topics
124
+ * accumulate via `distill:apply` as failures are captured.
125
+ */
126
+ declare const LESSONS_INDEX_TEMPLATE = "version: 1\nclusters: []\n";
127
+ /**
128
+ * Procedural rule paragraph that must live in the project's root rule
129
+ * (`.agentsmesh/rules/_root.md`) for both recall and capture to be enforced.
130
+ *
131
+ * Universal across every agent target: only requires the ability to read
132
+ * project files. No `Skill` tool, no description-match, no per-target
133
+ * projection.
134
+ */
135
+ declare const LESSONS_PROCEDURAL_RULE = "## Lessons (MUST do \u2014 non-negotiable)\n\nTwo rituals. Universal across every agent harness \u2014 described in tool-agnostic action verbs (\"read a file\", \"edit a file\", \"run a shell command\"); use whatever read / write-patch / shell-exec tool your harness exposes. Skipping either ritual is a process violation; the same paid-for failure recurs next session.\n\n**Recall \u2014 BEFORE editing any file or running any shell command:**\n\n1. Read `.agentsmesh/lessons/index.yaml`.\n2. For every cluster whose triggers match \u2014 `file_globs` against the target file path, `command_patterns` (regex) against the shell command, or `keywords` (case-insensitive substring) against the active task description \u2014 read its `file` (e.g. `.agentsmesh/lessons/topics/<topic>.md`).\n3. Apply the loaded rules to the action.\n4. Then \u2014 and only then \u2014 perform the edit or run the command.\n\nRejected excuses: *\"the edit is small\"* \u2192 apply it. *\"I read this earlier in the session\"* \u2192 context may have compacted; re-read. *\"no trigger fires\"* \u2192 proceed; you've done your part.\n\n**Capture \u2014 IMMEDIATELY after any failure** (test, CI, lint, code review, user correction, regression you spotted yourself, OR any mistake by anyone in the loop):\n\n1. Append one bullet to `.agentsmesh/lessons/journal.md` in this exact shape:\n `- **<short heading>**: <what went wrong>. <root cause>. <imperative rule that prevents recurrence>.`\n2. Read `.agentsmesh/lessons/index.yaml` and identify which topic the lesson belongs to (use the `summary:` fields).\n3. If the lesson teaches a NEW rule: edit that topic's `## Rules` section to add it, citing `(Evidence: L<journal-line>)`. If it only reinforces an existing rule, the journal entry alone suffices.\n\nRejected excuses: *\"it wasn't really a failure\"* \u2192 if you fixed something, it was. *\"I'll capture it later\"* \u2192 no; do it now while context is fresh. *\"no topic fits\"* \u2192 still record in journal; an unrouted bullet is recoverable, a forgotten one is not.\n\n**These two rituals are load-bearing. Treat them with the same discipline as TDD.**";
136
+
137
+ interface ScaffoldLessonsResult {
138
+ readonly created: string[];
139
+ readonly skipped: string[];
140
+ readonly rootRuleUpdated: boolean;
141
+ }
142
+ /**
143
+ * Idempotent scaffolder for the lessons subsystem. Intended for a future
144
+ * `agentsmesh init --lessons` flag (project mode); safe to call repeatedly.
145
+ *
146
+ * Creates `.agentsmesh/lessons/` with an empty journal, an empty index, and a
147
+ * `topics/` directory. Appends the procedural rule to
148
+ * `.agentsmesh/rules/_root.md` if not already present.
149
+ *
150
+ * Never overwrites existing files. The ledger and proposal are not created —
151
+ * they are auto-managed by the distill tool on first run.
152
+ */
153
+ declare function scaffoldLessons(projectRoot: string): ScaffoldLessonsResult;
154
+
155
+ export { type AppendLessonResult, LESSONS_INDEX_TEMPLATE, LESSONS_JOURNAL_TEMPLATE, LESSONS_PROCEDURAL_RULE, type Ledger, type LessonCaptureInput, type LessonsCluster, type LessonsIndex, LessonsIndexSchema, type LessonsPaths, type ParsedBullet, type ScaffoldLessonsResult, type ScoredCluster, type ToolEvent, type TriggeredLesson, appendLessonToJournal, formatLessonBullet, hashBullet, lessonsPaths, loadLedger, loadLessonsIndex, matchTriggers, parseBullets, parseIndex, readTriggeredLessons, saveLedger, scaffoldLessons, scoreBullet, toRelPath };
@@ -0,0 +1,305 @@
1
+ import { createHash } from 'crypto';
2
+ import { z } from 'zod';
3
+ import picomatch from 'picomatch';
4
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, appendFileSync } from 'fs';
5
+ import { parse, stringify } from 'yaml';
6
+ import { join, relative, sep, dirname, isAbsolute, resolve } from 'path';
7
+
8
+ // src/lessons/bullet-hash.ts
9
+ function normalize(bullet) {
10
+ return bullet.split("\n").map((line) => line.replace(/\s+$/, "")).join("\n").trim().replace(/^[-*]\s+/, "");
11
+ }
12
+ function hashBullet(bullet) {
13
+ return createHash("sha256").update(normalize(bullet)).digest("hex").slice(0, 16);
14
+ }
15
+
16
+ // src/lessons/bullet-parser.ts
17
+ function parseBullets(markdown) {
18
+ const bullets = [];
19
+ let current = null;
20
+ let lineNumber = 0;
21
+ for (const line of markdown.split("\n")) {
22
+ lineNumber += 1;
23
+ if (/^[-*]\s+/.test(line)) {
24
+ if (current !== null) bullets.push(current);
25
+ current = { text: line, lineNumber };
26
+ } else if (current !== null) {
27
+ if (line.length === 0) {
28
+ bullets.push(current);
29
+ current = null;
30
+ } else if (/^\s+\S/.test(line)) {
31
+ current.text += `
32
+ ${line}`;
33
+ } else {
34
+ bullets.push(current);
35
+ current = null;
36
+ }
37
+ }
38
+ }
39
+ if (current !== null) bullets.push(current);
40
+ return bullets;
41
+ }
42
+ var TriggersSchema = z.object({
43
+ file_globs: z.array(z.string()),
44
+ command_patterns: z.array(z.string()),
45
+ keywords: z.array(z.string())
46
+ }).refine((t) => t.file_globs.length + t.command_patterns.length + t.keywords.length > 0, {
47
+ message: "cluster must declare at least one trigger of any type"
48
+ });
49
+ var ClusterSchema = z.object({
50
+ topic: z.string().regex(/^[a-z0-9-]+$/, "topic must be kebab-case"),
51
+ /**
52
+ * Project-relative path (forward slashes) to the cluster's markdown body.
53
+ * Conventionally `.agentsmesh/lessons/topics/<topic>.md`, but any project
54
+ * path is accepted — universal across every agent target.
55
+ */
56
+ file: z.string().regex(/\.md$/, "file must be a .md path"),
57
+ summary: z.string().min(1),
58
+ triggers: TriggersSchema
59
+ });
60
+ var LessonsIndexSchema = z.object({
61
+ version: z.literal(1),
62
+ /**
63
+ * Zero clusters is valid — supports `agentsmesh init --lessons` scaffolding a
64
+ * fresh project. Topics accumulate via `distill:apply` as failures are
65
+ * captured.
66
+ */
67
+ clusters: z.array(ClusterSchema)
68
+ });
69
+ function parseIndex(raw) {
70
+ return LessonsIndexSchema.parse(raw);
71
+ }
72
+ function fileMatch(globs, path) {
73
+ return globs.some((g) => picomatch(g, { dot: true })(path));
74
+ }
75
+ function cmdMatch(patterns, cmd) {
76
+ return patterns.some((p) => {
77
+ try {
78
+ return new RegExp(p).test(cmd);
79
+ } catch {
80
+ return false;
81
+ }
82
+ });
83
+ }
84
+ function kwMatch(keywords, text) {
85
+ const lower = text.toLowerCase();
86
+ return keywords.some((k) => lower.includes(k.toLowerCase()));
87
+ }
88
+ function matchTriggers(clusters, event) {
89
+ return clusters.filter((c) => {
90
+ const t = c.triggers;
91
+ switch (event.kind) {
92
+ case "edit":
93
+ case "write":
94
+ return fileMatch(t.file_globs, event.filePath);
95
+ case "bash":
96
+ return cmdMatch(t.command_patterns, event.command);
97
+ case "task":
98
+ return kwMatch(t.keywords, event.text);
99
+ }
100
+ });
101
+ }
102
+ var LedgerSchema = z.object({
103
+ version: z.literal(1),
104
+ assignments: z.record(z.string(), z.string())
105
+ });
106
+ function loadLedger(path) {
107
+ if (!existsSync(path)) return { version: 1, assignments: {} };
108
+ return LedgerSchema.parse(parse(readFileSync(path, "utf8")));
109
+ }
110
+ function saveLedger(path, ledger) {
111
+ writeFileSync(path, stringify(ledger), "utf8");
112
+ }
113
+
114
+ // src/lessons/scoring.ts
115
+ function scoreBullet(bullet, clusters) {
116
+ const lower = bullet.toLowerCase();
117
+ return clusters.map((cluster) => {
118
+ const t = cluster.triggers;
119
+ const kwHits = t.keywords.filter((k) => lower.includes(k.toLowerCase())).length;
120
+ const pathHits = t.file_globs.filter((g) => {
121
+ const stem = g.replace(/[*{}[\]?!]/g, "").replace(/\/+/g, "/").trim();
122
+ return stem.length > 2 && lower.includes(stem.toLowerCase());
123
+ }).length;
124
+ const cmdHits = t.command_patterns.filter((p) => {
125
+ try {
126
+ return new RegExp(p, "i").test(bullet);
127
+ } catch {
128
+ return false;
129
+ }
130
+ }).length;
131
+ return { cluster, score: kwHits * 2 + pathHits + cmdHits };
132
+ }).filter((s) => s.score > 0).sort((a, b) => b.score - a.score);
133
+ }
134
+ var BASE_REL = ".agentsmesh/lessons";
135
+ function lessonsPaths(projectRoot) {
136
+ const base = join(projectRoot, BASE_REL);
137
+ return {
138
+ base,
139
+ journal: join(base, "journal.md"),
140
+ index: join(base, "index.yaml"),
141
+ ledger: join(base, "distill-ledger.yaml"),
142
+ proposal: join(base, "distill-proposal.md"),
143
+ topicsDir: join(base, "topics")
144
+ };
145
+ }
146
+ function toRelPath(projectRoot, absolute) {
147
+ return relative(projectRoot, absolute).split(sep).join("/");
148
+ }
149
+ var LESSONS_JOURNAL_TEMPLATE = "# Lessons Learned\n\n";
150
+ var LESSONS_INDEX_TEMPLATE = "version: 1\nclusters: []\n";
151
+ var LESSONS_PROCEDURAL_RULE = `## Lessons (MUST do \u2014 non-negotiable)
152
+
153
+ Two rituals. Universal across every agent harness \u2014 described in tool-agnostic action verbs ("read a file", "edit a file", "run a shell command"); use whatever read / write-patch / shell-exec tool your harness exposes. Skipping either ritual is a process violation; the same paid-for failure recurs next session.
154
+
155
+ **Recall \u2014 BEFORE editing any file or running any shell command:**
156
+
157
+ 1. Read \`.agentsmesh/lessons/index.yaml\`.
158
+ 2. For every cluster whose triggers match \u2014 \`file_globs\` against the target file path, \`command_patterns\` (regex) against the shell command, or \`keywords\` (case-insensitive substring) against the active task description \u2014 read its \`file\` (e.g. \`.agentsmesh/lessons/topics/<topic>.md\`).
159
+ 3. Apply the loaded rules to the action.
160
+ 4. Then \u2014 and only then \u2014 perform the edit or run the command.
161
+
162
+ Rejected excuses: *"the edit is small"* \u2192 apply it. *"I read this earlier in the session"* \u2192 context may have compacted; re-read. *"no trigger fires"* \u2192 proceed; you've done your part.
163
+
164
+ **Capture \u2014 IMMEDIATELY after any failure** (test, CI, lint, code review, user correction, regression you spotted yourself, OR any mistake by anyone in the loop):
165
+
166
+ 1. Append one bullet to \`.agentsmesh/lessons/journal.md\` in this exact shape:
167
+ \`- **<short heading>**: <what went wrong>. <root cause>. <imperative rule that prevents recurrence>.\`
168
+ 2. Read \`.agentsmesh/lessons/index.yaml\` and identify which topic the lesson belongs to (use the \`summary:\` fields).
169
+ 3. If the lesson teaches a NEW rule: edit that topic's \`## Rules\` section to add it, citing \`(Evidence: L<journal-line>)\`. If it only reinforces an existing rule, the journal entry alone suffices.
170
+
171
+ Rejected excuses: *"it wasn't really a failure"* \u2192 if you fixed something, it was. *"I'll capture it later"* \u2192 no; do it now while context is fresh. *"no topic fits"* \u2192 still record in journal; an unrouted bullet is recoverable, a forgotten one is not.
172
+
173
+ **These two rituals are load-bearing. Treat them with the same discipline as TDD.**`;
174
+
175
+ // src/lessons/store.ts
176
+ function loadLessonsIndex(projectRoot) {
177
+ const raw = readFileSync(lessonsPaths(projectRoot).index, "utf8");
178
+ return parseIndex(parse(raw));
179
+ }
180
+ function readTriggeredLessons(projectRoot, event) {
181
+ const index = loadLessonsIndex(projectRoot);
182
+ const contentByPath = /* @__PURE__ */ new Map();
183
+ return matchTriggers(index.clusters, normalizeToolEvent(projectRoot, event)).map(
184
+ (cluster) => {
185
+ const filePath = resolveProjectFile(projectRoot, cluster.file);
186
+ let content = contentByPath.get(cluster.file);
187
+ if (content === void 0) {
188
+ content = readFileSync(filePath, "utf8");
189
+ contentByPath.set(cluster.file, content);
190
+ }
191
+ return {
192
+ cluster,
193
+ relativePath: cluster.file,
194
+ filePath,
195
+ content
196
+ };
197
+ }
198
+ );
199
+ }
200
+ function formatLessonBullet(input) {
201
+ const heading = compact(input.heading);
202
+ if (heading.length === 0) throw new Error("Lesson heading must not be empty.");
203
+ return [
204
+ `- **${heading}**:`,
205
+ sentence(input.whatWentWrong),
206
+ sentence(input.rootCause),
207
+ sentence(input.rule)
208
+ ].join(" ");
209
+ }
210
+ function appendLessonToJournal(projectRoot, input) {
211
+ const journalPath = lessonsPaths(projectRoot).journal;
212
+ const bullet = formatLessonBullet(input);
213
+ const current = existsSync(journalPath) ? readFileSync(journalPath, "utf8") : "";
214
+ const prefix = current.length === 0 || current.endsWith("\n") ? "" : "\n";
215
+ const lineNumber = nextLineNumber(current);
216
+ mkdirSync(dirname(journalPath), { recursive: true });
217
+ appendFileSync(journalPath, `${prefix}${bullet}
218
+ `, "utf8");
219
+ return { journalPath, bullet, lineNumber };
220
+ }
221
+ function resolveProjectFile(projectRoot, relPath) {
222
+ if (isAbsolute(relPath) || /^[A-Za-z]:[\\/]/.test(relPath)) {
223
+ throw new Error(`Lessons file must be project-relative: ${relPath}`);
224
+ }
225
+ const root = resolve(projectRoot);
226
+ const filePath = resolve(root, relPath);
227
+ const backToRoot = relative(root, filePath);
228
+ if (backToRoot === "" || backToRoot.startsWith("..") || isAbsolute(backToRoot)) {
229
+ throw new Error(`Lessons file escapes the project root: ${relPath}`);
230
+ }
231
+ return filePath;
232
+ }
233
+ function normalizeToolEvent(projectRoot, event) {
234
+ if (event.kind !== "edit" && event.kind !== "write") return event;
235
+ return { ...event, filePath: normalizeEventPath(projectRoot, event.filePath) };
236
+ }
237
+ function normalizeEventPath(projectRoot, filePath) {
238
+ const normalized = filePath.replaceAll("\\", "/");
239
+ const root = resolve(projectRoot).replaceAll("\\", "/");
240
+ if (normalized === root) return ".";
241
+ if (normalized.startsWith(`${root}/`)) return normalized.slice(root.length + 1);
242
+ return normalized.replace(/^\.\//, "");
243
+ }
244
+ function compact(value) {
245
+ return value.replace(/\s+/g, " ").trim();
246
+ }
247
+ function sentence(value) {
248
+ const compacted = compact(value);
249
+ if (compacted.length === 0) throw new Error("Lesson sentence must not be empty.");
250
+ return /[.!?]$/.test(compacted) ? compacted : `${compacted}.`;
251
+ }
252
+ function nextLineNumber(current) {
253
+ if (current.length === 0) return 1;
254
+ const lineCount = current.split("\n").length;
255
+ return current.endsWith("\n") ? lineCount : lineCount + 1;
256
+ }
257
+ function scaffoldLessons(projectRoot) {
258
+ const paths = lessonsPaths(projectRoot);
259
+ const created = [];
260
+ const skipped = [];
261
+ mkdirSync(paths.topicsDir, { recursive: true });
262
+ for (const [path, template] of [
263
+ [paths.journal, LESSONS_JOURNAL_TEMPLATE],
264
+ [paths.index, LESSONS_INDEX_TEMPLATE]
265
+ ]) {
266
+ if (existsSync(path)) {
267
+ skipped.push(path);
268
+ } else {
269
+ mkdirSync(dirname(path), { recursive: true });
270
+ writeFileSync(path, template, "utf8");
271
+ created.push(path);
272
+ }
273
+ }
274
+ const rootRuleUpdated = appendProceduralRule(projectRoot);
275
+ return { created, skipped, rootRuleUpdated };
276
+ }
277
+ function appendProceduralRule(projectRoot) {
278
+ const rootRule = join(projectRoot, ".agentsmesh/rules/_root.md");
279
+ if (!existsSync(rootRule)) {
280
+ mkdirSync(dirname(rootRule), { recursive: true });
281
+ const seeded = `---
282
+ root: true
283
+ description: ""
284
+ ---
285
+
286
+ # Operational Guidelines
287
+
288
+ ${LESSONS_PROCEDURAL_RULE}
289
+ `;
290
+ writeFileSync(rootRule, seeded, "utf8");
291
+ return true;
292
+ }
293
+ const current = readFileSync(rootRule, "utf8");
294
+ if (/^## Lessons \(/m.test(current)) return false;
295
+ const next = current.endsWith("\n") ? current : `${current}
296
+ `;
297
+ writeFileSync(rootRule, `${next}
298
+ ${LESSONS_PROCEDURAL_RULE}
299
+ `, "utf8");
300
+ return true;
301
+ }
302
+
303
+ export { LESSONS_INDEX_TEMPLATE, LESSONS_JOURNAL_TEMPLATE, LESSONS_PROCEDURAL_RULE, LessonsIndexSchema, appendLessonToJournal, formatLessonBullet, hashBullet, lessonsPaths, loadLedger, loadLessonsIndex, matchTriggers, parseBullets, parseIndex, readTriggeredLessons, saveLedger, scaffoldLessons, scoreBullet, toRelPath };
304
+ //# sourceMappingURL=lessons.js.map
305
+ //# sourceMappingURL=lessons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lessons/bullet-hash.ts","../src/lessons/bullet-parser.ts","../src/lessons/index-schema.ts","../src/lessons/matcher.ts","../src/lessons/ledger.ts","../src/lessons/scoring.ts","../src/lessons/paths.ts","../src/lessons/store.ts","../src/lessons/init.ts"],"names":["z","parseYaml","yamlStringify","readFileSync","existsSync","relative","mkdirSync","dirname","writeFileSync","join"],"mappings":"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","file":"lessons.js","sourcesContent":["import { createHash } from 'node:crypto';\n\nfunction normalize(bullet: string): string {\n return bullet\n .split('\\n')\n .map((line) => line.replace(/\\s+$/, ''))\n .join('\\n')\n .trim()\n .replace(/^[-*]\\s+/, '');\n}\n\nexport function hashBullet(bullet: string): string {\n return createHash('sha256').update(normalize(bullet)).digest('hex').slice(0, 16);\n}\n","export interface ParsedBullet {\n text: string;\n lineNumber: number;\n}\n\nexport function parseBullets(markdown: string): ParsedBullet[] {\n const bullets: ParsedBullet[] = [];\n let current: ParsedBullet | null = null;\n let lineNumber = 0;\n\n for (const line of markdown.split('\\n')) {\n lineNumber += 1;\n if (/^[-*]\\s+/.test(line)) {\n if (current !== null) bullets.push(current);\n current = { text: line, lineNumber };\n } else if (current !== null) {\n if (line.length === 0) {\n bullets.push(current);\n current = null;\n } else if (/^\\s+\\S/.test(line)) {\n current.text += `\\n${line}`;\n } else {\n bullets.push(current);\n current = null;\n }\n }\n }\n if (current !== null) bullets.push(current);\n return bullets;\n}\n","import { z } from 'zod';\n\nconst TriggersSchema = z\n .object({\n file_globs: z.array(z.string()),\n command_patterns: z.array(z.string()),\n keywords: z.array(z.string()),\n })\n .refine((t) => t.file_globs.length + t.command_patterns.length + t.keywords.length > 0, {\n message: 'cluster must declare at least one trigger of any type',\n });\n\nconst ClusterSchema = z.object({\n topic: z.string().regex(/^[a-z0-9-]+$/, 'topic must be kebab-case'),\n /**\n * Project-relative path (forward slashes) to the cluster's markdown body.\n * Conventionally `.agentsmesh/lessons/topics/<topic>.md`, but any project\n * path is accepted — universal across every agent target.\n */\n file: z.string().regex(/\\.md$/, 'file must be a .md path'),\n summary: z.string().min(1),\n triggers: TriggersSchema,\n});\n\nexport const LessonsIndexSchema = z.object({\n version: z.literal(1),\n /**\n * Zero clusters is valid — supports `agentsmesh init --lessons` scaffolding a\n * fresh project. Topics accumulate via `distill:apply` as failures are\n * captured.\n */\n clusters: z.array(ClusterSchema),\n});\n\nexport type LessonsIndex = z.infer<typeof LessonsIndexSchema>;\nexport type LessonsCluster = z.infer<typeof ClusterSchema>;\n\nexport function parseIndex(raw: unknown): LessonsIndex {\n return LessonsIndexSchema.parse(raw);\n}\n","import picomatch from 'picomatch';\nimport type { LessonsCluster } from './index-schema.js';\n\nexport type ToolEvent =\n | { kind: 'edit' | 'write'; filePath: string }\n | { kind: 'bash'; command: string }\n | { kind: 'task'; text: string };\n\nfunction fileMatch(globs: readonly string[], path: string): boolean {\n return globs.some((g) => picomatch(g, { dot: true })(path));\n}\n\nfunction cmdMatch(patterns: readonly string[], cmd: string): boolean {\n return patterns.some((p) => {\n try {\n return new RegExp(p).test(cmd);\n } catch {\n return false;\n }\n });\n}\n\nfunction kwMatch(keywords: readonly string[], text: string): boolean {\n const lower = text.toLowerCase();\n return keywords.some((k) => lower.includes(k.toLowerCase()));\n}\n\nexport function matchTriggers(\n clusters: readonly LessonsCluster[],\n event: ToolEvent,\n): LessonsCluster[] {\n return clusters.filter((c) => {\n const t = c.triggers;\n switch (event.kind) {\n case 'edit':\n case 'write':\n return fileMatch(t.file_globs, event.filePath);\n case 'bash':\n return cmdMatch(t.command_patterns, event.command);\n case 'task':\n return kwMatch(t.keywords, event.text);\n }\n });\n}\n","import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { parse as parseYaml, stringify as yamlStringify } from 'yaml';\nimport { z } from 'zod';\n\nconst LedgerSchema = z.object({\n version: z.literal(1),\n assignments: z.record(z.string(), z.string()),\n});\n\nexport type Ledger = z.infer<typeof LedgerSchema>;\n\nexport function loadLedger(path: string): Ledger {\n if (!existsSync(path)) return { version: 1, assignments: {} };\n return LedgerSchema.parse(parseYaml(readFileSync(path, 'utf8')));\n}\n\nexport function saveLedger(path: string, ledger: Ledger): void {\n writeFileSync(path, yamlStringify(ledger), 'utf8');\n}\n","import type { LessonsCluster } from './index-schema.js';\n\nexport interface ScoredCluster {\n cluster: LessonsCluster;\n score: number;\n}\n\nexport function scoreBullet(bullet: string, clusters: readonly LessonsCluster[]): ScoredCluster[] {\n const lower = bullet.toLowerCase();\n return clusters\n .map((cluster): ScoredCluster => {\n const t = cluster.triggers;\n const kwHits = t.keywords.filter((k) => lower.includes(k.toLowerCase())).length;\n const pathHits = t.file_globs.filter((g) => {\n const stem = g\n .replace(/[*{}[\\]?!]/g, '')\n .replace(/\\/+/g, '/')\n .trim();\n return stem.length > 2 && lower.includes(stem.toLowerCase());\n }).length;\n const cmdHits = t.command_patterns.filter((p) => {\n try {\n return new RegExp(p, 'i').test(bullet);\n } catch {\n return false;\n }\n }).length;\n return { cluster, score: kwHits * 2 + pathHits + cmdHits };\n })\n .filter((s) => s.score > 0)\n .sort((a, b) => b.score - a.score);\n}\n","import { join, relative, sep } from 'node:path';\n\n/**\n * Default on-disk locations for the lessons subsystem.\n *\n * All artifacts live under `<projectRoot>/.agentsmesh/lessons/`, so the lessons\n * subsystem is a self-contained canonical feature — `agentsmesh init --lessons`\n * scaffolds this directory and the procedural rule, and removal is a single\n * directory delete.\n *\n * Path values are absolute; `*Rel` helpers return forward-slash project-relative\n * paths suitable for embedding in markdown rules consumed by any agent target.\n */\nexport interface LessonsPaths {\n /** Directory containing every lessons artifact. */\n readonly base: string;\n /** Append-only journal — point of capture for new lessons. */\n readonly journal: string;\n /** Trigger index — maps file_globs / command_patterns / keywords to topic files. */\n readonly index: string;\n /** Distill ledger — bullet hash → assigned topic (or \"skip\"). */\n readonly ledger: string;\n /** Distill proposal file — generated by `distill`, consumed by `distill:apply`. */\n readonly proposal: string;\n /** Directory holding one markdown file per topic cluster. */\n readonly topicsDir: string;\n}\n\nconst BASE_REL = '.agentsmesh/lessons';\n\nexport function lessonsPaths(projectRoot: string): LessonsPaths {\n const base = join(projectRoot, BASE_REL);\n return {\n base,\n journal: join(base, 'journal.md'),\n index: join(base, 'index.yaml'),\n ledger: join(base, 'distill-ledger.yaml'),\n proposal: join(base, 'distill-proposal.md'),\n topicsDir: join(base, 'topics'),\n };\n}\n\n/**\n * Project-relative path for a given absolute path, normalized to forward\n * slashes for cross-platform consistency in markdown rule files.\n */\nexport function toRelPath(projectRoot: string, absolute: string): string {\n return relative(projectRoot, absolute).split(sep).join('/');\n}\n\n/**\n * Empty-journal contents used by init scaffolding. The journal grows from here\n * as failures get recorded.\n */\nexport const LESSONS_JOURNAL_TEMPLATE = '# Lessons Learned\\n\\n';\n\n/**\n * Empty index used by init scaffolding. Schema allows zero clusters; topics\n * accumulate via `distill:apply` as failures are captured.\n */\nexport const LESSONS_INDEX_TEMPLATE = 'version: 1\\nclusters: []\\n';\n\n/**\n * Procedural rule paragraph that must live in the project's root rule\n * (`.agentsmesh/rules/_root.md`) for both recall and capture to be enforced.\n *\n * Universal across every agent target: only requires the ability to read\n * project files. No `Skill` tool, no description-match, no per-target\n * projection.\n */\nexport const LESSONS_PROCEDURAL_RULE = `## Lessons (MUST do — non-negotiable)\n\nTwo rituals. Universal across every agent harness — described in tool-agnostic action verbs (\"read a file\", \"edit a file\", \"run a shell command\"); use whatever read / write-patch / shell-exec tool your harness exposes. Skipping either ritual is a process violation; the same paid-for failure recurs next session.\n\n**Recall — BEFORE editing any file or running any shell command:**\n\n1. Read \\`.agentsmesh/lessons/index.yaml\\`.\n2. For every cluster whose triggers match — \\`file_globs\\` against the target file path, \\`command_patterns\\` (regex) against the shell command, or \\`keywords\\` (case-insensitive substring) against the active task description — read its \\`file\\` (e.g. \\`.agentsmesh/lessons/topics/<topic>.md\\`).\n3. Apply the loaded rules to the action.\n4. Then — and only then — perform the edit or run the command.\n\nRejected excuses: *\"the edit is small\"* → apply it. *\"I read this earlier in the session\"* → context may have compacted; re-read. *\"no trigger fires\"* → proceed; you've done your part.\n\n**Capture — IMMEDIATELY after any failure** (test, CI, lint, code review, user correction, regression you spotted yourself, OR any mistake by anyone in the loop):\n\n1. Append one bullet to \\`.agentsmesh/lessons/journal.md\\` in this exact shape:\n \\`- **<short heading>**: <what went wrong>. <root cause>. <imperative rule that prevents recurrence>.\\`\n2. Read \\`.agentsmesh/lessons/index.yaml\\` and identify which topic the lesson belongs to (use the \\`summary:\\` fields).\n3. If the lesson teaches a NEW rule: edit that topic's \\`## Rules\\` section to add it, citing \\`(Evidence: L<journal-line>)\\`. If it only reinforces an existing rule, the journal entry alone suffices.\n\nRejected excuses: *\"it wasn't really a failure\"* → if you fixed something, it was. *\"I'll capture it later\"* → no; do it now while context is fresh. *\"no topic fits\"* → still record in journal; an unrouted bullet is recoverable, a forgotten one is not.\n\n**These two rituals are load-bearing. Treat them with the same discipline as TDD.**`;\n","import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';\nimport { dirname, isAbsolute, relative, resolve } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport { parseIndex, type LessonsCluster, type LessonsIndex } from './index-schema.js';\nimport { matchTriggers, type ToolEvent } from './matcher.js';\nimport { lessonsPaths } from './paths.js';\n\nexport interface TriggeredLesson {\n readonly cluster: LessonsCluster;\n readonly relativePath: string;\n readonly filePath: string;\n readonly content: string;\n}\n\nexport interface LessonCaptureInput {\n readonly heading: string;\n readonly whatWentWrong: string;\n readonly rootCause: string;\n readonly rule: string;\n}\n\nexport interface AppendLessonResult {\n readonly journalPath: string;\n readonly bullet: string;\n readonly lineNumber: number;\n}\n\nexport function loadLessonsIndex(projectRoot: string): LessonsIndex {\n const raw = readFileSync(lessonsPaths(projectRoot).index, 'utf8');\n return parseIndex(parseYaml(raw) as unknown);\n}\n\nexport function readTriggeredLessons(projectRoot: string, event: ToolEvent): TriggeredLesson[] {\n const index = loadLessonsIndex(projectRoot);\n const contentByPath = new Map<string, string>();\n\n return matchTriggers(index.clusters, normalizeToolEvent(projectRoot, event)).map(\n (cluster): TriggeredLesson => {\n const filePath = resolveProjectFile(projectRoot, cluster.file);\n let content = contentByPath.get(cluster.file);\n if (content === undefined) {\n content = readFileSync(filePath, 'utf8');\n contentByPath.set(cluster.file, content);\n }\n\n return {\n cluster,\n relativePath: cluster.file,\n filePath,\n content,\n };\n },\n );\n}\n\nexport function formatLessonBullet(input: LessonCaptureInput): string {\n const heading = compact(input.heading);\n if (heading.length === 0) throw new Error('Lesson heading must not be empty.');\n\n return [\n `- **${heading}**:`,\n sentence(input.whatWentWrong),\n sentence(input.rootCause),\n sentence(input.rule),\n ].join(' ');\n}\n\nexport function appendLessonToJournal(\n projectRoot: string,\n input: LessonCaptureInput,\n): AppendLessonResult {\n const journalPath = lessonsPaths(projectRoot).journal;\n const bullet = formatLessonBullet(input);\n const current = existsSync(journalPath) ? readFileSync(journalPath, 'utf8') : '';\n const prefix = current.length === 0 || current.endsWith('\\n') ? '' : '\\n';\n const lineNumber = nextLineNumber(current);\n\n mkdirSync(dirname(journalPath), { recursive: true });\n appendFileSync(journalPath, `${prefix}${bullet}\\n`, 'utf8');\n\n return { journalPath, bullet, lineNumber };\n}\n\nfunction resolveProjectFile(projectRoot: string, relPath: string): string {\n if (isAbsolute(relPath) || /^[A-Za-z]:[\\\\/]/.test(relPath)) {\n throw new Error(`Lessons file must be project-relative: ${relPath}`);\n }\n\n const root = resolve(projectRoot);\n const filePath = resolve(root, relPath);\n const backToRoot = relative(root, filePath);\n if (backToRoot === '' || backToRoot.startsWith('..') || isAbsolute(backToRoot)) {\n throw new Error(`Lessons file escapes the project root: ${relPath}`);\n }\n\n return filePath;\n}\n\nfunction normalizeToolEvent(projectRoot: string, event: ToolEvent): ToolEvent {\n if (event.kind !== 'edit' && event.kind !== 'write') return event;\n return { ...event, filePath: normalizeEventPath(projectRoot, event.filePath) };\n}\n\nfunction normalizeEventPath(projectRoot: string, filePath: string): string {\n const normalized = filePath.replaceAll('\\\\', '/');\n const root = resolve(projectRoot).replaceAll('\\\\', '/');\n if (normalized === root) return '.';\n if (normalized.startsWith(`${root}/`)) return normalized.slice(root.length + 1);\n return normalized.replace(/^\\.\\//, '');\n}\n\nfunction compact(value: string): string {\n return value.replace(/\\s+/g, ' ').trim();\n}\n\nfunction sentence(value: string): string {\n const compacted = compact(value);\n if (compacted.length === 0) throw new Error('Lesson sentence must not be empty.');\n return /[.!?]$/.test(compacted) ? compacted : `${compacted}.`;\n}\n\nfunction nextLineNumber(current: string): number {\n if (current.length === 0) return 1;\n const lineCount = current.split('\\n').length;\n return current.endsWith('\\n') ? lineCount : lineCount + 1;\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport {\n LESSONS_INDEX_TEMPLATE,\n LESSONS_JOURNAL_TEMPLATE,\n LESSONS_PROCEDURAL_RULE,\n lessonsPaths,\n} from './paths.js';\n\nexport interface ScaffoldLessonsResult {\n readonly created: string[];\n readonly skipped: string[];\n readonly rootRuleUpdated: boolean;\n}\n\n/**\n * Idempotent scaffolder for the lessons subsystem. Intended for a future\n * `agentsmesh init --lessons` flag (project mode); safe to call repeatedly.\n *\n * Creates `.agentsmesh/lessons/` with an empty journal, an empty index, and a\n * `topics/` directory. Appends the procedural rule to\n * `.agentsmesh/rules/_root.md` if not already present.\n *\n * Never overwrites existing files. The ledger and proposal are not created —\n * they are auto-managed by the distill tool on first run.\n */\nexport function scaffoldLessons(projectRoot: string): ScaffoldLessonsResult {\n const paths = lessonsPaths(projectRoot);\n const created: string[] = [];\n const skipped: string[] = [];\n\n mkdirSync(paths.topicsDir, { recursive: true });\n\n for (const [path, template] of [\n [paths.journal, LESSONS_JOURNAL_TEMPLATE],\n [paths.index, LESSONS_INDEX_TEMPLATE],\n ] as const) {\n if (existsSync(path)) {\n skipped.push(path);\n } else {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, template, 'utf8');\n created.push(path);\n }\n }\n\n const rootRuleUpdated = appendProceduralRule(projectRoot);\n return { created, skipped, rootRuleUpdated };\n}\n\nfunction appendProceduralRule(projectRoot: string): boolean {\n const rootRule = join(projectRoot, '.agentsmesh/rules/_root.md');\n if (!existsSync(rootRule)) {\n mkdirSync(dirname(rootRule), { recursive: true });\n const seeded = `---\\nroot: true\\ndescription: \"\"\\n---\\n\\n# Operational Guidelines\\n\\n${LESSONS_PROCEDURAL_RULE}\\n`;\n writeFileSync(rootRule, seeded, 'utf8');\n return true;\n }\n const current = readFileSync(rootRule, 'utf8');\n // Match any '## Lessons (…)' heading — the parenthetical wording may evolve\n // (mandatory / MUST do / etc.) but the H2 itself is the stable idempotency\n // anchor for this paragraph.\n if (/^## Lessons \\(/m.test(current)) return false;\n const next = current.endsWith('\\n') ? current : `${current}\\n`;\n writeFileSync(rootRule, `${next}\\n${LESSONS_PROCEDURAL_RULE}\\n`, 'utf8');\n return true;\n}\n"]}
package/dist/targets.js CHANGED
@@ -885,7 +885,7 @@ ${legacy}`, "");
885
885
  }
886
886
  return result.trim();
887
887
  }
888
- var ROOT_INSTRUCTION_BODY_V1, ROOT_INSTRUCTION_BODY_V2, ROOT_INSTRUCTION_BODY_V3, ROOT_INSTRUCTION_BODY_V4, ROOT_INSTRUCTION_BODY_V5, ROOT_INSTRUCTION_BODY_V6, ROOT_INSTRUCTION_BODY_V7, ROOT_INSTRUCTION_BODY_V8, ROOT_INSTRUCTION_BODY, LEGACY_AGENTSMESH_ROOT_INSTRUCTION_PARAGRAPH, LEGACY_AGENTSMESH_ROOT_INSTRUCTION_SECTION, AGENTSMESH_CONTRACT_WITH_V1_BODY, AGENTSMESH_CONTRACT_WITH_V2_BODY, AGENTSMESH_CONTRACT_WITH_V3_BODY, AGENTSMESH_CONTRACT_WITH_V4_BODY, AGENTSMESH_CONTRACT_WITH_V5_BODY, AGENTSMESH_CONTRACT_WITH_V6_BODY, AGENTSMESH_CONTRACT_WITH_V7_BODY, AGENTSMESH_CONTRACT_WITH_V8_BODY, AGENTSMESH_ROOT_INSTRUCTION_PARAGRAPH, LEGACY_FORMS;
888
+ var ROOT_INSTRUCTION_BODY_V1, ROOT_INSTRUCTION_BODY_V2, ROOT_INSTRUCTION_BODY_V3, ROOT_INSTRUCTION_BODY_V4, ROOT_INSTRUCTION_BODY_V5, ROOT_INSTRUCTION_BODY_V6, ROOT_INSTRUCTION_BODY_V7, ROOT_INSTRUCTION_BODY_V8, ROOT_INSTRUCTION_BODY_V9, ROOT_INSTRUCTION_BODY_V10, ROOT_INSTRUCTION_BODY, LEGACY_AGENTSMESH_ROOT_INSTRUCTION_PARAGRAPH, LEGACY_AGENTSMESH_ROOT_INSTRUCTION_SECTION, AGENTSMESH_CONTRACT_WITH_V1_BODY, AGENTSMESH_CONTRACT_WITH_V2_BODY, AGENTSMESH_CONTRACT_WITH_V3_BODY, AGENTSMESH_CONTRACT_WITH_V4_BODY, AGENTSMESH_CONTRACT_WITH_V5_BODY, AGENTSMESH_CONTRACT_WITH_V6_BODY, AGENTSMESH_CONTRACT_WITH_V7_BODY, AGENTSMESH_CONTRACT_WITH_V8_BODY, AGENTSMESH_CONTRACT_WITH_V9_BODY, AGENTSMESH_CONTRACT_WITH_V10_BODY, AGENTSMESH_ROOT_INSTRUCTION_PARAGRAPH, LEGACY_FORMS;
889
889
  var init_root_instruction_paragraph = __esm({
890
890
  "src/targets/projection/root-instruction-paragraph.ts"() {
891
891
  init_managed_blocks();
@@ -897,7 +897,9 @@ var init_root_instruction_paragraph = __esm({
897
897
  ROOT_INSTRUCTION_BODY_V6 = "Create canonical files in `.agentsmesh`: `rules/_root.md` and `rules/*.md` are Markdown rules; `commands/*.md`, `agents/*.md`, and `skills/*/SKILL.md` plus supporting files use Claude-style frontmatter Markdown; `mcp.json` is MCP JSON; `hooks.yaml` and `permissions.yaml` are YAML; `ignore` is gitignore-style text. Then run `agentsmesh generate`.";
898
898
  ROOT_INSTRUCTION_BODY_V7 = "`.agentsmesh` is the only folder you edit or add these files in: `rules/_root.md` and `rules/*.md` are Markdown rules; `commands/*.md`, `agents/*.md`, and `skills/*/SKILL.md` plus supporting files use Claude-style frontmatter Markdown; `mcp.json` is MCP JSON; `hooks.yaml` and `permissions.yaml` are YAML; `ignore` is gitignore-style text. Do not edit generated tool files; run `agentsmesh generate`.";
899
899
  ROOT_INSTRUCTION_BODY_V8 = "`agentsmesh.yaml` selects targets/features (`agentsmesh.local.yaml` overrides locally), and `.agentsmesh` is the only place to add or edit canonical items: `rules/_root.md`, `rules/*.md`, `commands/*.md`, `agents/*.md`, `skills/*/SKILL.md` plus supporting files, `mcp.json`, `hooks.yaml`, `permissions.yaml`, and `ignore`; if missing run `agentsmesh init`, use `agentsmesh import --from <tool>` for native configs, `agentsmesh install <source>` or `install --sync` for reusable packs, then run `agentsmesh generate`. Use `diff`, `lint`, `check`, `watch`, `matrix`, and `merge` as needed; never edit generated tool files.";
900
- ROOT_INSTRUCTION_BODY = "`agentsmesh.yaml` selects targets/features (`agentsmesh.local.yaml` overrides locally), and `.agentsmesh` is the only place to add or edit canonical items: `rules/_root.md`, `rules/*.md`, `commands/*.md`, `agents/*.md`, `skills/*/SKILL.md` plus supporting files, `mcp.json`, `hooks.yaml`, `permissions.yaml`, and `ignore`; if missing run `agentsmesh init`, use `agentsmesh import --from <tool>` for native configs, `agentsmesh install <source>` or `install --sync` for reusable packs, then run `agentsmesh generate`. Use `diff`, `lint`, `check`, `watch`, `matrix`, `merge`, and `refresh` as needed; never edit generated tool files.";
900
+ ROOT_INSTRUCTION_BODY_V9 = "`agentsmesh.yaml` selects targets/features (`agentsmesh.local.yaml` overrides locally), and `.agentsmesh` is the only place to add or edit canonical items: `rules/_root.md`, `rules/*.md`, `commands/*.md`, `agents/*.md`, `skills/*/SKILL.md` plus supporting files, `mcp.json`, `hooks.yaml`, `permissions.yaml`, and `ignore`; if missing run `agentsmesh init`, use `agentsmesh import --from <tool>` for native configs, `agentsmesh install <source>` or `install --sync` for reusable packs, then run `agentsmesh generate`. Use `diff`, `lint`, `check`, `watch`, `matrix`, `merge`, and `refresh` as needed; never edit generated tool files.";
901
+ ROOT_INSTRUCTION_BODY_V10 = "**MUST follow when changing any rule, agent, command, skill, hook, MCP server, permission, or ignore pattern.** `agentsmesh.yaml` selects targets/features (`agentsmesh.local.yaml` overrides locally), and `.agentsmesh` is the only place to add or edit canonical items: `rules/_root.md`, `rules/*.md`, `commands/*.md`, `agents/*.md`, `skills/*/SKILL.md` plus supporting files, `mcp.json`, `hooks.yaml`, `permissions.yaml`, and `ignore`; if missing run `agentsmesh init`, use `agentsmesh import --from <tool>` for native configs, `agentsmesh install <source>` or `install --sync` for reusable packs, then run `agentsmesh generate`. Use `diff`, `lint`, `check`, `watch`, `matrix`, `merge`, and `refresh` as needed; never edit generated tool files.";
902
+ ROOT_INSTRUCTION_BODY = "**NEVER edit generated files** (`.claude/`, `.cursor/`, `AGENTS.md`, `.github/copilot-instructions.md`, and similar target outputs) \u2014 `agentsmesh generate` overwrites them. **All changes MUST go through `.agentsmesh` first**: edit `rules/_root.md`, `rules/*.md`, `commands/*.md`, `agents/*.md`, `skills/*/SKILL.md` plus supporting files, `mcp.json`, `hooks.yaml`, `permissions.yaml`, and `ignore`; `agentsmesh.yaml` selects targets/features (`agentsmesh.local.yaml` overrides locally); if missing run `agentsmesh init`, use `agentsmesh import --from <tool>` for native configs, `agentsmesh install <source>` or `install --sync` for reusable packs, then run `agentsmesh generate`. Use `diff`, `lint`, `check`, `watch`, `matrix`, `merge`, and `refresh` as needed.";
901
903
  LEGACY_AGENTSMESH_ROOT_INSTRUCTION_PARAGRAPH = ROOT_INSTRUCTION_BODY_V1;
902
904
  LEGACY_AGENTSMESH_ROOT_INSTRUCTION_SECTION = `## Project-Specific Rules
903
905
 
@@ -926,12 +928,20 @@ ${ROOT_INSTRUCTION_BODY_V7}`;
926
928
  AGENTSMESH_CONTRACT_WITH_V8_BODY = `## AgentsMesh Generation Contract
927
929
 
928
930
  ${ROOT_INSTRUCTION_BODY_V8}`;
931
+ AGENTSMESH_CONTRACT_WITH_V9_BODY = `## AgentsMesh Generation Contract
932
+
933
+ ${ROOT_INSTRUCTION_BODY_V9}`;
934
+ AGENTSMESH_CONTRACT_WITH_V10_BODY = `## AgentsMesh Generation Contract
935
+
936
+ ${ROOT_INSTRUCTION_BODY_V10}`;
929
937
  AGENTSMESH_ROOT_INSTRUCTION_PARAGRAPH = `${ROOT_CONTRACT_START}
930
938
  ## AgentsMesh Generation Contract
931
939
 
932
940
  ${ROOT_INSTRUCTION_BODY}
933
941
  ${ROOT_CONTRACT_END}`;
934
942
  LEGACY_FORMS = [
943
+ AGENTSMESH_CONTRACT_WITH_V10_BODY,
944
+ AGENTSMESH_CONTRACT_WITH_V9_BODY,
935
945
  AGENTSMESH_CONTRACT_WITH_V8_BODY,
936
946
  AGENTSMESH_CONTRACT_WITH_V7_BODY,
937
947
  AGENTSMESH_CONTRACT_WITH_V6_BODY,
@@ -15693,9 +15703,18 @@ function generateIgnore12(canonical) {
15693
15703
  if (!canonical.ignore || canonical.ignore.length === 0) return [];
15694
15704
  return [{ path: QWEN_IGNORE, content: canonical.ignore.join("\n") }];
15695
15705
  }
15706
+ function renderQwenGlobalInstructions(canonical) {
15707
+ const root = canonical.rules.find((rule) => rule.root);
15708
+ const nonRootRules = canonical.rules.filter((rule) => {
15709
+ if (rule.root) return false;
15710
+ return rule.targets.length === 0 || rule.targets.includes(QWEN_CODE_TARGET);
15711
+ });
15712
+ return appendEmbeddedRulesBlock(root?.body.trim() ?? "", nonRootRules);
15713
+ }
15696
15714
  var init_generator26 = __esm({
15697
15715
  "src/targets/qwen-code/generator.ts"() {
15698
15716
  init_markdown();
15717
+ init_managed_blocks();
15699
15718
  init_constants21();
15700
15719
  }
15701
15720
  });
@@ -15778,6 +15797,7 @@ var init_qwen_code2 = __esm({
15778
15797
  };
15779
15798
  globalLayout22 = {
15780
15799
  rootInstructionPath: QWEN_GLOBAL_ROOT,
15800
+ renderPrimaryRootInstruction: renderQwenGlobalInstructions,
15781
15801
  skillDir: QWEN_GLOBAL_SKILLS_DIR,
15782
15802
  managedOutputs: {
15783
15803
  dirs: [QWEN_GLOBAL_COMMANDS_DIR, QWEN_GLOBAL_AGENTS_DIR, QWEN_GLOBAL_SKILLS_DIR],