agentsmesh 0.21.0 → 0.23.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,494 @@
1
+ import { z } from 'zod';
2
+
3
+ declare const TopicSchema: z.ZodObject<{
4
+ summary: z.ZodString;
5
+ }, z.core.$strict>;
6
+ declare const TriggerKindSchema: z.ZodEnum<{
7
+ file_glob: "file_glob";
8
+ command_pattern: "command_pattern";
9
+ keyword: "keyword";
10
+ }>;
11
+ declare const TriggerSchema: z.ZodObject<{
12
+ kind: z.ZodEnum<{
13
+ file_glob: "file_glob";
14
+ command_pattern: "command_pattern";
15
+ keyword: "keyword";
16
+ }>;
17
+ pattern: z.ZodString;
18
+ }, z.core.$strict>;
19
+ declare const LessonStatusSchema: z.ZodEnum<{
20
+ active: "active";
21
+ deprecated: "deprecated";
22
+ superseded: "superseded";
23
+ }>;
24
+ declare const LessonSchema: z.ZodObject<{
25
+ rule: z.ZodString;
26
+ rationale: z.ZodOptional<z.ZodString>;
27
+ topics: z.ZodArray<z.ZodString>;
28
+ triggers: z.ZodArray<z.ZodString>;
29
+ evidence: z.ZodArray<z.ZodString>;
30
+ status: z.ZodEnum<{
31
+ active: "active";
32
+ deprecated: "deprecated";
33
+ superseded: "superseded";
34
+ }>;
35
+ supersededBy: z.ZodOptional<z.ZodString>;
36
+ createdAt: z.ZodString;
37
+ }, z.core.$strict>;
38
+ declare const LessonsGraphSchema: z.ZodObject<{
39
+ version: z.ZodLiteral<1>;
40
+ lessons: z.ZodRecord<z.ZodString, z.ZodObject<{
41
+ rule: z.ZodString;
42
+ rationale: z.ZodOptional<z.ZodString>;
43
+ topics: z.ZodArray<z.ZodString>;
44
+ triggers: z.ZodArray<z.ZodString>;
45
+ evidence: z.ZodArray<z.ZodString>;
46
+ status: z.ZodEnum<{
47
+ active: "active";
48
+ deprecated: "deprecated";
49
+ superseded: "superseded";
50
+ }>;
51
+ supersededBy: z.ZodOptional<z.ZodString>;
52
+ createdAt: z.ZodString;
53
+ }, z.core.$strict>>;
54
+ topics: z.ZodRecord<z.ZodString, z.ZodObject<{
55
+ summary: z.ZodString;
56
+ }, z.core.$strict>>;
57
+ triggers: z.ZodRecord<z.ZodString, z.ZodObject<{
58
+ kind: z.ZodEnum<{
59
+ file_glob: "file_glob";
60
+ command_pattern: "command_pattern";
61
+ keyword: "keyword";
62
+ }>;
63
+ pattern: z.ZodString;
64
+ }, z.core.$strict>>;
65
+ }, z.core.$strict>;
66
+ type LessonsGraph = z.infer<typeof LessonsGraphSchema>;
67
+ type Lesson = z.infer<typeof LessonSchema>;
68
+ type Topic = z.infer<typeof TopicSchema>;
69
+ type Trigger = z.infer<typeof TriggerSchema>;
70
+ type TriggerKind = z.infer<typeof TriggerKindSchema>;
71
+ type LessonStatus = z.infer<typeof LessonStatusSchema>;
72
+ declare function parseGraph(raw: unknown): LessonsGraph;
73
+
74
+ interface AutoPruneSummary {
75
+ readonly removedTriggers: number;
76
+ readonly removedTopics: number;
77
+ readonly detachedDeadGlobs: number;
78
+ }
79
+
80
+ /**
81
+ * Capture guardrails — mostly WARNINGS, steering authors toward a few specific
82
+ * triggers. Over-triggering (too many triggers per lesson, broad globs,
83
+ * keyword-only triggers that fire less reliably on the mandatory `--file`/`--cmd`
84
+ * path, long/stopworded keyword patterns) is the single biggest threat to recall
85
+ * precision, and a paraphrase silently accumulates a near-duplicate; these are
86
+ * surfaced without blocking, because an over-broad lesson is better than a lost
87
+ * one.
88
+ *
89
+ * The ONE blocking case lives elsewhere (`addLessonInto` → `UnrecallableLessonError`):
90
+ * a capture whose EVERY trigger is dead on the mandatory recall path is rejected,
91
+ * because that lesson is captured then silently never recalled. These guardrails
92
+ * are the warn-only complement to that single hard block.
93
+ */
94
+ type GuardrailCode = 'OVERSIZED_LESSON_TRIGGERS' | 'BROAD_GLOB_TRIGGER' | 'KEYWORD_ONLY_LESSON' | 'LOW_SIGNAL_KEYWORD' | 'STOPWORD_KEYWORD' | 'DEAD_GLOB' | 'NEAR_DUPLICATE_LESSON';
95
+ interface GuardrailWarning {
96
+ readonly code: GuardrailCode;
97
+ readonly message: string;
98
+ }
99
+
100
+ /**
101
+ * Capture rejection errors thrown by {@link addLessonInto}. Kept in their own
102
+ * module so `add.ts` stays focused on the mutation logic; re-exported from
103
+ * `add.js` so every existing importer keeps its `from './add.js'` path.
104
+ */
105
+ declare class UnknownTopicError extends Error {
106
+ readonly topic: string;
107
+ readonly code = "UNKNOWN_TOPIC";
108
+ constructor(topic: string);
109
+ }
110
+
111
+ interface AddLessonTriggers {
112
+ readonly files?: readonly string[];
113
+ readonly commands?: readonly string[];
114
+ readonly keywords?: readonly string[];
115
+ }
116
+ interface AddLessonInput {
117
+ readonly rule: string;
118
+ readonly topic: string;
119
+ readonly triggers: AddLessonTriggers;
120
+ readonly evidence?: readonly string[];
121
+ readonly rationale?: string;
122
+ readonly createdAt?: string;
123
+ }
124
+ interface AddLessonOptions {
125
+ readonly allowNewTopic?: boolean;
126
+ readonly topicSummary?: string;
127
+ readonly retries?: number;
128
+ /**
129
+ * Skip the "a lesson needs at least one trigger" guard. Set only by the
130
+ * legacy-merge recovery path, which folds historical lessons that may predate
131
+ * the requirement; interactive capture (CLI/MCP) always enforces it so a fresh
132
+ * agent cannot create an unreachable lesson.
133
+ */
134
+ readonly allowNoTrigger?: boolean;
135
+ /**
136
+ * Working-tree file list (project-relative, forward-slash) enabling the
137
+ * warn-only DEAD_GLOB guardrail. The capture entry point supplies it; the
138
+ * legacy-merge path omits it, so no tree walk happens off the capture path.
139
+ */
140
+ readonly knownPaths?: ReadonlySet<string>;
141
+ }
142
+ interface AddLessonResult {
143
+ readonly id: string;
144
+ readonly isNewLesson: boolean;
145
+ readonly isNewTopic: boolean;
146
+ readonly newTriggerIds: string[];
147
+ /** Non-blocking capture guardrail warnings for the resulting (merged) lesson. */
148
+ readonly warnings: GuardrailWarning[];
149
+ /**
150
+ * Counts of structural cruft the opt-in auto-prune cleaned up right after this
151
+ * capture (config `autoPrune: true`). Present only when something was pruned;
152
+ * absent when auto-prune is off or there was nothing to clean.
153
+ */
154
+ readonly autoPruned?: AutoPruneSummary;
155
+ }
156
+ declare function addLesson(projectRoot: string, input: AddLessonInput, options?: AddLessonOptions): Promise<AddLessonResult>;
157
+
158
+ interface LessonsQuery {
159
+ /** Project-relative path of the file about to be edited. */
160
+ readonly file?: string;
161
+ /** Shell command about to be executed. */
162
+ readonly command?: string;
163
+ /** Free-form text describing the current task. */
164
+ readonly keyword?: string;
165
+ }
166
+ interface MatchedLesson {
167
+ readonly id: string;
168
+ readonly lesson: Lesson;
169
+ }
170
+ /**
171
+ * Recall primitive — returns every active lesson whose triggers match any of
172
+ * the supplied query fields. The query fields combine as OR across triggers:
173
+ * a lesson matches if ANY of its triggers match ANY supplied field.
174
+ * Deprecated and superseded lessons are excluded.
175
+ */
176
+ declare function queryLessons(graph: LessonsGraph, query: LessonsQuery): MatchedLesson[];
177
+
178
+ interface RankReason {
179
+ readonly matchedTriggers: string[];
180
+ readonly bm25: number;
181
+ readonly specificity: number;
182
+ readonly topicCoherence: number;
183
+ }
184
+ interface RankedLesson {
185
+ readonly id: string;
186
+ readonly lesson: Lesson;
187
+ readonly score: number;
188
+ readonly reason: RankReason;
189
+ }
190
+ interface RankOptions {
191
+ /** Keep at most this many results (after ranking). */
192
+ readonly limit?: number;
193
+ /**
194
+ * Best-effort token budget: keep results while their cumulative estimated
195
+ * rule-token cost fits. The single most-relevant result is ALWAYS returned
196
+ * even if it alone exceeds the budget — an empty recall for a matched query is
197
+ * worse for the agent than one slightly-over-budget rule.
198
+ */
199
+ readonly maxTokens?: number;
200
+ }
201
+ /** Default recall cap: a broad trigger match returns the most-relevant few, not the whole topic. */
202
+ declare const DEFAULT_RECALL_LIMIT = 10;
203
+ /**
204
+ * Default recall token budget applied by the application APIs (CLI `query`, MCP
205
+ * `lessons_query`, {@link recallLessons}) when the caller does not specify one.
206
+ * Mandatory recall runs before every edit/command, so its payload must stay
207
+ * lean; without a budget a broad match can return ~450+ rule-tokens. `--all`
208
+ * (CLI) bypasses both caps. The top result is always kept (see RankOptions).
209
+ */
210
+ declare const DEFAULT_RECALL_MAX_TOKENS = 400;
211
+ /**
212
+ * Rank matched lessons by relevance and apply optional caps. Three lightweight
213
+ * signals are weighted-reciprocal-rank-fused (RRF): trigger specificity (inverse
214
+ * fanout — a discriminating trigger beats a topic-wide one; highest weight),
215
+ * per-query topic coherence (a lesson in the topic that dominates this query's
216
+ * matched set is boosted; middle weight), and BM25 over the rule text (so the
217
+ * lesson whose wording best fits breaks ties the structural signals cannot;
218
+ * lowest weight). Ties break by recency (createdAt) then id. No embeddings, no
219
+ * I/O — pure and sub-millisecond.
220
+ */
221
+ declare function rankLessons(graph: LessonsGraph, query: LessonsQuery, matches: readonly MatchedLesson[], options?: RankOptions): RankedLesson[];
222
+
223
+ interface MutateOptions {
224
+ readonly retries?: number;
225
+ }
226
+ /**
227
+ * The public transactional write path: migrate a legacy store FIRST (so even a
228
+ * first raw mutation cannot create an empty `lessons.json` and strand
229
+ * `index.yaml`), then run the locked transaction. Every mutating operation
230
+ * (add, merge, deprecate, strip-markers, …) and any third-party consumer routes
231
+ * through here. `maybeAutoMigrateLessons` is a no-op (one stat) when a graph
232
+ * already exists or no legacy index is present, and runs before the lock is
233
+ * taken, so there is no re-entrancy with the migrator's own locked write.
234
+ */
235
+ declare function mutateLessonsGraph<T>(projectRoot: string, mutator: (graph: LessonsGraph) => T | Promise<T>, options?: MutateOptions): Promise<Awaited<T>>;
236
+
237
+ declare function graphFilePath(projectRoot: string): string;
238
+ declare function loadLessonsGraph(projectRoot: string): LessonsGraph;
239
+ declare function tryLoadLessonsGraph(projectRoot: string): LessonsGraph | null;
240
+ /**
241
+ * Deterministic serialization: every object's keys are emitted in alphabetical
242
+ * order so diffs reflect content changes rather than insertion order. Output
243
+ * always ends with a single trailing newline to keep CRLF/POSIX diffs clean.
244
+ */
245
+ declare function serializeGraph(graph: LessonsGraph): string;
246
+
247
+ interface MergeLessonsResult {
248
+ readonly loserId: string;
249
+ readonly keeperId: string;
250
+ }
251
+ interface MergeLessonsOptions {
252
+ readonly retries?: number;
253
+ }
254
+ /**
255
+ * Fold a duplicate lesson (`loserId`) into its canonical twin (`keeperId`):
256
+ * union the loser's triggers, topics, and evidence onto the keeper, then mark
257
+ * the loser `superseded` with `supersededBy` pointing at the keeper.
258
+ *
259
+ * Unioning reachability BEFORE superseding is the whole point — a raw
260
+ * supersede would silently drop the keeper out of recall for queries that only
261
+ * matched the loser's (often different-topic) triggers.
262
+ */
263
+ declare function mergeLessons(projectRoot: string, loserId: string, keeperId: string, options?: MergeLessonsOptions): Promise<MergeLessonsResult>;
264
+
265
+ interface StripMarkersReport {
266
+ readonly changedIds: string[];
267
+ readonly changedCount: number;
268
+ }
269
+ interface StripMarkersOptions {
270
+ readonly dryRun?: boolean;
271
+ }
272
+ /**
273
+ * Strip dead legacy provenance markers from a lesson's prose. The migration
274
+ * from the line-numbered Markdown store left inline `See L\d+`, `(L\d+)`,
275
+ * `[L\d+]`, and "(also relevant …)" pointers that no longer resolve to
276
+ * anything. Real provenance lives in the `evidence` array, not the rule text.
277
+ *
278
+ * Strictly conservative: ONLY the four marker shapes are touched. Legitimate
279
+ * parentheticals like `(CWE-78)`, ellipses inside code spans, block-comment
280
+ * fences, and shell punctuation (`exit 1 ;;`) are left byte-for-byte intact.
281
+ * Idempotent.
282
+ */
283
+ declare function stripLegacyMarkers(rule: string): string;
284
+ /**
285
+ * Apply {@link stripLegacyMarkers} to every lesson. With `dryRun`, computes the
286
+ * change set without persisting; otherwise writes through the transactional
287
+ * path (locked + validated + atomic). A legacy-only store is migrated first (the
288
+ * universal first-access upgrade) so its lessons are stripped rather than
289
+ * silently ignored. No-op (and no file creation) when neither graph nor legacy
290
+ * store exists.
291
+ */
292
+ declare function stripMarkersInGraph(projectRoot: string, options?: StripMarkersOptions): Promise<StripMarkersReport>;
293
+
294
+ type ValidationLevel = 'error' | 'warning';
295
+ interface ValidationFinding {
296
+ readonly level: ValidationLevel;
297
+ readonly code: string;
298
+ readonly message: string;
299
+ readonly lessonId?: string;
300
+ readonly topicId?: string;
301
+ readonly triggerId?: string;
302
+ }
303
+ interface ValidationReport {
304
+ /** True when no `error`-level findings exist (warnings do not affect `ok`). */
305
+ readonly ok: boolean;
306
+ readonly findings: ValidationFinding[];
307
+ }
308
+ interface ValidateOptions {
309
+ /**
310
+ * Working-tree file list (project-relative, forward-slash) for the dead-glob
311
+ * liveness check. When omitted the check is SKIPPED — the pure write-barrier
312
+ * call in `mutate.ts` passes nothing, so `add` never walks the tree and a
313
+ * liveness warning can never block a write. The CLI/lint callers supply it.
314
+ */
315
+ readonly knownPaths?: ReadonlySet<string>;
316
+ }
317
+ declare function validateLessonsGraph(graph: LessonsGraph, options?: ValidateOptions): ValidationReport;
318
+
319
+ interface ImportLegacyOptions {
320
+ /** ISO date stamped onto every imported lesson's `createdAt`. */
321
+ readonly migratedAt: string;
322
+ /**
323
+ * When `true` (default), delete the legacy `index.yaml`, `journal.md`,
324
+ * `topics/`, `distill-ledger.yaml`, and `distill-proposal.md` after a
325
+ * successful migration. Pass `false` to leave them in place (test-only).
326
+ */
327
+ readonly deleteLegacy?: boolean;
328
+ /**
329
+ * Overwrite an existing non-empty graph. Default `false`: migration refuses
330
+ * (throws {@link LessonsGraphExistsError}) when, AT WRITE TIME UNDER THE LOCK,
331
+ * the graph already has lessons — so a concurrent capture is never erased.
332
+ */
333
+ readonly force?: boolean;
334
+ /**
335
+ * MERGE legacy lessons INTO an existing graph instead of replacing it. This is
336
+ * the recovery path for a stranded state — legacy `index.yaml` coexisting with
337
+ * a populated `lessons.json` (an old binary could create this). Each legacy
338
+ * lesson is folded in via the normal capture path: rules dedup by text,
339
+ * triggers content-address and dedup, topics union. Never overwrites graph
340
+ * data; `force` is irrelevant in this mode.
341
+ */
342
+ readonly merge?: boolean;
343
+ }
344
+ /** Thrown when migration would overwrite an already-populated graph without `force`. */
345
+ declare class LessonsGraphExistsError extends Error {
346
+ readonly code = "LESSONS_GRAPH_EXISTS";
347
+ constructor();
348
+ }
349
+ interface ImportLegacyReport {
350
+ readonly wroteGraphPath: string;
351
+ readonly deletedPaths: string[];
352
+ readonly topicCount: number;
353
+ readonly lessonCount: number;
354
+ readonly triggerCount: number;
355
+ }
356
+ /**
357
+ * One-shot upgrade migrator: reads the legacy YAML index + per-topic
358
+ * Markdown files, emits the new JSON graph, and (by default) removes every
359
+ * legacy artifact so the project lands in a clean state on the new system.
360
+ *
361
+ * Deterministic over (input, options): the graph output and deletion list are
362
+ * fixed. Requires the legacy `index.yaml` to exist — it is read unconditionally
363
+ * and a missing index throws (ENOENT). Callers MUST guard with
364
+ * `existsSync(lessonsPaths(root).index)` before invoking (see
365
+ * `maybeAutoMigrateLessons` and the `import-md` handler); re-running on a
366
+ * post-migration tree, where the legacy files are already gone, throws.
367
+ */
368
+ declare function importLegacyLessons(projectRoot: string, options: ImportLegacyOptions): Promise<ImportLegacyReport>;
369
+
370
+ /**
371
+ * Cross-platform process lock backed by an atomic mkdir.
372
+ *
373
+ * Acquire semantics: mkdir with no `recursive` option is atomic per POSIX and
374
+ * returns EEXIST if the directory already exists. That makes it a reliable
375
+ * cross-process mutex without any third-party dependency.
376
+ *
377
+ * Stale recovery: the holder writes its PID and start timestamp into the lock
378
+ * dir. On contention we peek at the PID; if the process is gone or the lock is
379
+ * older than `staleMs`, we evict it and retry.
380
+ */
381
+ interface LockOptions {
382
+ /** Maximum retry attempts before throwing LockAcquisitionError. */
383
+ retries?: number;
384
+ /** Delay between retries in ms. */
385
+ retryDelayMs?: number;
386
+ /** Lock age beyond which an existing lock is treated as stale and evicted. */
387
+ staleMs?: number;
388
+ /** Human-readable lock name surfaced in LockAcquisitionError, e.g. "lessons lock". */
389
+ label?: string;
390
+ }
391
+ type LockRelease = () => Promise<void>;
392
+
393
+ /**
394
+ * Process lock for lessons-graph writes.
395
+ *
396
+ * Multiple harnesses can call `agentsmesh lessons add` concurrently — capture
397
+ * the rule once per failure, even when a hooked CI step and the user's editor
398
+ * race for the same `lessons.json`. The lock lives at
399
+ * `.agentsmesh/lessons/.lessons.lock` and reuses the same `acquireProcessLock`
400
+ * primitive as `.install.lock` / `.generate.lock`, so stale-eviction, signal
401
+ * cleanup, and PID metadata all behave identically.
402
+ */
403
+
404
+ declare const LESSONS_LOCK_FILENAME = ".lessons.lock";
405
+ declare function lessonsLockPath(projectRoot: string): string;
406
+ declare function acquireLessonsLock(projectRoot: string, opts?: LockOptions): Promise<LockRelease>;
407
+
408
+ /**
409
+ * Default on-disk locations for the lessons subsystem.
410
+ *
411
+ * Canonical store: `<projectRoot>/.agentsmesh/lessons/lessons.json` (the JSON
412
+ * graph). The legacy paths (`journal`, `index`, `topicsDir`) are retained for
413
+ * the one-shot upgrade migrator only; fresh projects never create them.
414
+ */
415
+ interface LessonsPaths {
416
+ /** Directory containing every lessons artifact. */
417
+ readonly base: string;
418
+ /** Canonical JSON graph — the single source of truth. */
419
+ readonly graph: string;
420
+ /** Optional per-project recall tuning (recallLimit / recallMaxTokens). */
421
+ readonly config: string;
422
+ /** Legacy append-only journal. Used by the migrator only. */
423
+ readonly journal: string;
424
+ /** Legacy YAML trigger index. Used by the migrator only. */
425
+ readonly index: string;
426
+ /** Legacy per-topic Markdown directory. Used by the migrator only. */
427
+ readonly topicsDir: string;
428
+ }
429
+ declare function lessonsPaths(projectRoot: string): LessonsPaths;
430
+ /**
431
+ * Project-relative path for a given absolute path, normalized to forward
432
+ * slashes for cross-platform consistency in markdown rule files.
433
+ */
434
+ declare function toRelPath(projectRoot: string, absolute: string): string;
435
+ /**
436
+ * Tier 1 of the lessons contract: the always-on trigger.
437
+ *
438
+ * Lives in the project's root rule (`.agentsmesh/rules/_root.md`) as a managed
439
+ * block, so it reaches every target through canonical rule generation. Universal:
440
+ * only requires the ability to shell-exec the `agentsmesh` CLI. No `Skill` tool,
441
+ * no description-match, no per-target projection.
442
+ *
443
+ * Deliberately minimal — it carries only the BINDING essentials (both commands,
444
+ * the BLOCKING framing, the recall scope, the broad capture scope, the graph
445
+ * path, the MCP fallback). The expansive how-to (full command set, topic
446
+ * workflow, trigger-flag mechanics, the complete rejected-excuse enumeration, and
447
+ * the rebuttal pedagogy) lives in the `lessons` skill (`LESSONS_SKILL_BODY`) so
448
+ * the manual can grow without bloating every target's always-on context.
449
+ *
450
+ * Recall is scoped to MUTATING actions — file edits and state-changing commands —
451
+ * with pure-read commands and the recall query itself explicitly exempt. This
452
+ * removes the infinite regress (recall before the recall command) and the
453
+ * most-flouted "read-only included" clause, cutting guarded actions roughly in
454
+ * half (exploration is read-heavy) while keeping recall where it changes outcomes.
455
+ *
456
+ * Structural shape (heading + intro + Recall block + Capture block + closing)
457
+ * survives generate → import → generate round-trip; only the wording inside each
458
+ * block is tightened for maximum agent compliance.
459
+ */
460
+ declare const LESSONS_PROCEDURAL_RULE = "## Lessons (BLOCKING REQUIREMENT \u2014 MUST run both, no exceptions; the user will check)\n\nGraph `.agentsmesh/lessons/lessons.json` is canonical \u2014 never hand-edit. Manual: the `lessons` skill.\n\n**Recall \u2014 MUST run before every file edit and every state-changing command** (build/test/install/migrate/git-write): `agentsmesh lessons query --file <path> --cmd <command>`, then apply every rule. Pure-read commands (cat/ls/grep/git-log) and the recall query itself are exempt.\n\n**Capture \u2014 MUST run immediately after any failure** (a failing test/CI/lint/typecheck, a code review, a user correction, a regression, or a wrong assumption \u2014 yours or anyone's): `agentsmesh lessons add \"<rule>\" --topic <id> --trigger-file <glob> --evidence <sha|lesson-id>`.\n\nNo shell? Use the `lessons_query` / `lessons_add` MCP tools. Skip either and the system does not exist.";
461
+
462
+ interface ScaffoldLessonsResult {
463
+ readonly created: string[];
464
+ /** Managed artifacts rewritten to the current wording (e.g. a stale skill). */
465
+ readonly updated: string[];
466
+ readonly skipped: string[];
467
+ readonly rootRuleUpdated: boolean;
468
+ /** True when the recall-log gitignore entry was added to `.gitignore`. */
469
+ readonly gitignoreUpdated: boolean;
470
+ /** True when the PostToolUse recall hook was injected into `hooks.yaml`. */
471
+ readonly recallHookInjected: boolean;
472
+ }
473
+ /**
474
+ * Idempotent scaffolder for the lessons subsystem. Backs `agentsmesh init
475
+ * --lessons`, the lessons-only retrofit, and the import safety net.
476
+ *
477
+ * - Creates `.agentsmesh/lessons/lessons.json` (an empty graph) if missing.
478
+ * - Injects the lessons ritual (Tier 1 — the always-on trigger) into
479
+ * `.agentsmesh/rules/_root.md` as a managed block
480
+ * (`<!-- agentsmesh:lessons-contract:start -->` … `:end -->`). The block is
481
+ * canonical content so it reaches every target — including rules-directory
482
+ * targets the generation-contract decorator skips — while the sentinels keep
483
+ * the block an identifiable unit for clean round-trip and let re-running
484
+ * scaffold refresh the wording from one constant.
485
+ * - Writes `.agentsmesh/skills/lessons/SKILL.md` (Tier 2 — the on-demand manual).
486
+ * Like the Tier-1 paragraph, this is a MANAGED artifact: every run rewrites it
487
+ * to the current manual so an upgraded agentsmesh propagates the new wording
488
+ * (reported as `updated` when it changed, `skipped` when already current).
489
+ * The graph, by contrast, is user data and stays create-if-missing. Targets
490
+ * without skills still get Tier 1.
491
+ */
492
+ declare function scaffoldLessons(projectRoot: string): Promise<ScaffoldLessonsResult>;
493
+
494
+ export { type AddLessonInput as A, loadLessonsGraph as B, mergeLessons as C, DEFAULT_RECALL_LIMIT as D, mutateLessonsGraph as E, parseGraph as F, queryLessons as G, rankLessons as H, type ImportLegacyOptions as I, scaffoldLessons as J, serializeGraph as K, LESSONS_PROCEDURAL_RULE as L, type MatchedLesson as M, stripLegacyMarkers as N, stripMarkersInGraph as O, toRelPath as P, tryLoadLessonsGraph as Q, type RankOptions as R, type ScaffoldLessonsResult as S, type Topic as T, UnknownTopicError as U, type ValidationFinding as V, validateLessonsGraph as W, DEFAULT_RECALL_MAX_TOKENS as X, LESSONS_LOCK_FILENAME as Y, LessonsGraphExistsError as Z, lessonsLockPath as _, type AddLessonOptions as a, type AddLessonResult as b, type AddLessonTriggers as c, type ImportLegacyReport as d, type Lesson as e, type LessonStatus as f, type LessonsGraph as g, LessonsGraphSchema as h, type LessonsPaths as i, type LessonsQuery as j, type MergeLessonsOptions as k, type MergeLessonsResult as l, type MutateOptions as m, type RankReason as n, type RankedLesson as o, type StripMarkersOptions as p, type StripMarkersReport as q, type Trigger as r, type TriggerKind as s, type ValidationLevel as t, type ValidationReport as u, acquireLessonsLock as v, addLesson as w, graphFilePath as x, importLegacyLessons as y, lessonsPaths as z };
@@ -0,0 +1,106 @@
1
+ import { o as RankedLesson, A as AddLessonInput, a as AddLessonOptions, b as AddLessonResult, j as LessonsQuery } from './init-YKxF2zpQ.js';
2
+ export { c as AddLessonTriggers, D as DEFAULT_RECALL_LIMIT, X as DEFAULT_RECALL_MAX_TOKENS, I as ImportLegacyOptions, d as ImportLegacyReport, Y as LESSONS_LOCK_FILENAME, L as LESSONS_PROCEDURAL_RULE, e as Lesson, f as LessonStatus, g as LessonsGraph, Z as LessonsGraphExistsError, h as LessonsGraphSchema, i as LessonsPaths, M as MatchedLesson, k as MergeLessonsOptions, l as MergeLessonsResult, m as MutateOptions, R as RankOptions, n as RankReason, S as ScaffoldLessonsResult, p as StripMarkersOptions, q as StripMarkersReport, T as Topic, r as Trigger, s as TriggerKind, U as UnknownTopicError, V as ValidationFinding, t as ValidationLevel, u as ValidationReport, v as acquireLessonsLock, w as addLesson, x as graphFilePath, y as importLegacyLessons, _ as lessonsLockPath, z as lessonsPaths, B as loadLessonsGraph, C as mergeLessons, E as mutateLessonsGraph, F as parseGraph, G as queryLessons, H as rankLessons, J as scaffoldLessons, K as serializeGraph, N as stripLegacyMarkers, O as stripMarkersInGraph, P as toRelPath, Q as tryLoadLessonsGraph, W as validateLessonsGraph } from './init-YKxF2zpQ.js';
3
+ import 'zod';
4
+
5
+ /**
6
+ * Migration-aware application APIs for the lessons subsystem.
7
+ *
8
+ * The WRITE path migrates automatically: `mutateLessonsGraph` (and everything
9
+ * built on it — `addLesson`, `mergeLessons`, deprecate, strip-markers) runs the
10
+ * legacy→JSON migration before mutating, so even a first raw write can never
11
+ * create an empty `lessons.json` over an unmigrated `index.yaml`. (The migrator
12
+ * and scaffolding use the internal `mutateLessonsGraphLocked` to avoid
13
+ * recursing.)
14
+ *
15
+ * The low-level READ primitives (`tryLoadLessonsGraph`, `loadLessonsGraph`,
16
+ * `queryLessons`) do NOT migrate — a first read through them on a legacy project
17
+ * would see no graph. `recallLessons` closes that: it migrates first, then
18
+ * loads + ranks. `captureLesson` is the symmetric capture entry point. Prefer
19
+ * these application APIs; reach for the read primitives only post-migration.
20
+ */
21
+ interface RecallOptions {
22
+ /** Max ranked lessons to return. Defaults to {@link DEFAULT_RECALL_LIMIT}. */
23
+ readonly limit?: number;
24
+ /**
25
+ * Cumulative rule-token budget. Defaults to {@link DEFAULT_RECALL_MAX_TOKENS};
26
+ * pass `null` to disable the budget (return up to `limit` results).
27
+ */
28
+ readonly maxTokens?: number | null;
29
+ /**
30
+ * Session correlator for recall dedup. Defaults to `AGENTSMESH_SESSION_ID`;
31
+ * when set, lessons already delivered this session are suppressed.
32
+ */
33
+ readonly sessionId?: string;
34
+ /** Force dedup off even when a session correlator is present. */
35
+ readonly noDedup?: boolean;
36
+ }
37
+ interface RecallResult {
38
+ /** Relevance-ranked, capped lessons (compact metadata lives on each entry). */
39
+ readonly lessons: RankedLesson[];
40
+ /** How many active lessons matched the query before the caps were applied. */
41
+ readonly totalMatches: number;
42
+ /**
43
+ * True when the canonical graph existed but could not be read (corrupt JSON /
44
+ * schema drift). Recall degrades to empty instead of throwing; callers surface
45
+ * this so a corrupt graph is a visible warning, not silent zero recall.
46
+ */
47
+ readonly corrupt?: boolean;
48
+ /**
49
+ * Set to the on-disk schema version when the graph is newer than this build
50
+ * understands. Recall degrades to empty (like `corrupt`) but callers surface
51
+ * an upgrade hint rather than a "corrupt" warning.
52
+ */
53
+ readonly newerVersion?: number;
54
+ /**
55
+ * Count of matched lessons suppressed because they were already delivered
56
+ * earlier in this session (dedup). 0 when dedup is off or nothing repeated.
57
+ */
58
+ readonly suppressed: number;
59
+ }
60
+ /**
61
+ * Recall primitive for applications: migrate if needed, then return the active
62
+ * lessons matching `query`, relevance-ranked and capped by limit + token budget.
63
+ */
64
+ declare function recallLessons(projectRoot: string, query: LessonsQuery, options?: RecallOptions): Promise<RecallResult>;
65
+ /**
66
+ * Capture primitive for applications: migrate if needed, then add the lesson
67
+ * through the transactional write path. Idempotent on repeat (same rule+topic).
68
+ *
69
+ * Both CLI `lessons add` and MCP `lessons_add` route through here, so capture
70
+ * telemetry is recorded once at this single entry point (mirroring how every
71
+ * recall records through `recallLessons`). Every rejection — a dead trigger, an
72
+ * unknown topic, a write-barrier failure — is recorded as a BLOCKED capture
73
+ * before being rethrown, so `stats` never undercounts blocks.
74
+ */
75
+ declare function captureLesson(projectRoot: string, input: AddLessonInput, options?: AddLessonOptions): Promise<AddLessonResult>;
76
+
77
+ /**
78
+ * One-shot legacy→JSON migration on first access. Shared by the CLI dispatcher
79
+ * AND the MCP handlers so that an MCP-only agent (query/add) does not strand a
80
+ * legacy store: if it added first it would create `lessons.json`, which then
81
+ * permanently blocks the absent-graph auto-migration. Returns true if it
82
+ * migrated. No-op when a graph already exists or no legacy index is present.
83
+ */
84
+ declare function maybeAutoMigrateLessons(projectRoot: string): Promise<boolean>;
85
+
86
+ /**
87
+ * Safety gate for `command_pattern` triggers.
88
+ *
89
+ * Recall is mandatory (it runs before every edit/command) and matches
90
+ * author-supplied patterns against the command string. A backtracking `RegExp`
91
+ * can be driven super-linear by a crafted pattern — `(a+)+`, `(a|aa)+`, `a+a+`,
92
+ * or even `a+b` (quadratic on a long non-matching input) — so one lesson could
93
+ * hang recall. Local inspection of quantifier shape CANNOT prove a backtracking
94
+ * regex linear, so we do not try: command patterns are matched by an in-repo
95
+ * non-backtracking engine ({@link compileLinearMatcher}), which is provably
96
+ * linear in the input length for any pattern it can compile.
97
+ *
98
+ * A pattern is "safe" iff the linear engine can compile it. Patterns it cannot
99
+ * evaluate (invalid syntax, backreferences, lookarounds) are rejected at capture
100
+ * (UNSAFE_TRIGGER_PATTERN) and skipped at read time — fail closed.
101
+ */
102
+
103
+ /** True when `pattern` can be matched by the linear engine (no ReDoS risk). */
104
+ declare function isSafeRegexPattern(pattern: string): boolean;
105
+
106
+ export { AddLessonInput, AddLessonOptions, AddLessonResult, LessonsQuery, RankedLesson, type RecallOptions, type RecallResult, captureLesson, isSafeRegexPattern, maybeAutoMigrateLessons, recallLessons };