agentsmesh 0.28.0 → 0.30.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 +42 -0
- package/README.md +64 -11
- package/dist/canonical.d.ts +2 -2
- package/dist/canonical.js +91 -13
- package/dist/canonical.js.map +1 -1
- package/dist/cli.js +223 -199
- package/dist/engine.d.ts +2 -2
- package/dist/engine.js +113 -17
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +235 -49
- package/dist/index.js.map +1 -1
- package/dist/{init-B0aI-g8W.d.ts → init-PvpXanVd.d.ts} +17 -3
- package/dist/lessons.d.ts +2 -2
- package/dist/lessons.js +195 -40
- package/dist/lessons.js.map +1 -1
- package/dist/{schema-CzaoYJlG.d.ts → schema-CH_JJep8.d.ts} +5 -0
- package/dist/{target-descriptor-ItDY3NR0.d.ts → target-descriptor-CaLUz7SR.d.ts} +1 -1
- package/dist/targets.d.ts +3 -3
- package/dist/targets.js +28 -9
- package/dist/targets.js.map +1 -1
- package/package.json +1 -1
- package/schemas/agentsmesh.json +11 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { AgentsMeshError, AgentsMeshErrorCode, CheckLockSyncOptions, ComputeDiffResult, ConfigNotFoundError, ConfigValidationError, DiffEntry, DiffSummary, FileSystemError, GenerateContext, GenerationError, ImportError, LintOptions, LintResult, LoadProjectContextOptions, LockAcquisitionError, LockSyncReport, ProjectContext, RemoteFetchError, TargetNotFoundError, check, computeDiff, diff, formatDiffSummary, generate, importFrom, lint, loadConfig, loadConfigFromDirectory, loadProjectContext, resolveOutputCollisions } from './engine.js';
|
|
2
2
|
export { LoadCanonicalOptions, loadCanonical, loadCanonicalFiles } from './canonical.js';
|
|
3
3
|
export { getAllDescriptors, getDescriptor, getTargetCatalog, registerTargetDescriptor } from './targets.js';
|
|
4
|
-
export { A as AddLessonInput, a as AddLessonOptions, b as AddLessonResult, c as AddLessonTriggers, D as DEFAULT_RECALL_LIMIT, I as ImportLegacyOptions, d as ImportLegacyReport, L as LESSONS_PROCEDURAL_RULE, e as Lesson, f as LessonStatus, g as LessonsGraph, h as LessonsGraphSchema, i as LessonsPaths, j as LessonsQuery, M as MatchedLesson, k as MergeLessonsOptions, l as MergeLessonsResult, m as MutateOptions, R as RankOptions, n as RankReason, o as RankedLesson, 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, 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-
|
|
5
|
-
export { C as CanonicalAgent, a as CanonicalCommand, b as CanonicalFiles, c as CanonicalRule, d as CanonicalSkill, H as HookEntry, e as Hooks, I as IgnorePatterns, M as McpConfig, f as McpServer, P as Permissions, S as SkillSupportingFile, g as StdioMcpServer, U as UrlMcpServer, V as ValidatedConfig } from './schema-
|
|
6
|
-
export { E as ExtraRuleOutputContext, a as ExtraRuleOutputResolver, F as FeatureLinter, G as GenerateResult, b as GeneratedOutputMerger, c as GlobalTargetSupport, I as ImportPathBuilder, d as ImportResult, L as LintDiagnostic, R as RuleLinter, S as ScopeExtrasFn, T as TargetCapabilities, e as TargetDescriptor, f as TargetGenerators, g as TargetLayout, h as TargetLayoutScope, i as TargetLintHooks, j as TargetManagedOutputs, k as TargetOutputFamily, l as TargetPathResolvers } from './target-descriptor-
|
|
4
|
+
export { A as AddLessonInput, a as AddLessonOptions, b as AddLessonResult, c as AddLessonTriggers, D as DEFAULT_RECALL_LIMIT, I as ImportLegacyOptions, d as ImportLegacyReport, L as LESSONS_PROCEDURAL_RULE, e as Lesson, f as LessonStatus, g as LessonsGraph, h as LessonsGraphSchema, i as LessonsPaths, j as LessonsQuery, M as MatchedLesson, k as MergeLessonsOptions, l as MergeLessonsResult, m as MutateOptions, R as RankOptions, n as RankReason, o as RankedLesson, 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, 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-PvpXanVd.js';
|
|
5
|
+
export { C as CanonicalAgent, a as CanonicalCommand, b as CanonicalFiles, c as CanonicalRule, d as CanonicalSkill, H as HookEntry, e as Hooks, I as IgnorePatterns, M as McpConfig, f as McpServer, P as Permissions, S as SkillSupportingFile, g as StdioMcpServer, U as UrlMcpServer, V as ValidatedConfig } from './schema-CH_JJep8.js';
|
|
6
|
+
export { E as ExtraRuleOutputContext, a as ExtraRuleOutputResolver, F as FeatureLinter, G as GenerateResult, b as GeneratedOutputMerger, c as GlobalTargetSupport, I as ImportPathBuilder, d as ImportResult, L as LintDiagnostic, R as RuleLinter, S as ScopeExtrasFn, T as TargetCapabilities, e as TargetDescriptor, f as TargetGenerators, g as TargetLayout, h as TargetLayoutScope, i as TargetLintHooks, j as TargetManagedOutputs, k as TargetOutputFamily, l as TargetPathResolvers } from './target-descriptor-CaLUz7SR.js';
|
|
7
7
|
import 'zod';
|
package/dist/index.js
CHANGED
|
@@ -2942,6 +2942,18 @@ var init_linter = __esm({
|
|
|
2942
2942
|
}
|
|
2943
2943
|
});
|
|
2944
2944
|
|
|
2945
|
+
// src/core/hook-types.ts
|
|
2946
|
+
var BEST_EFFORT_HOOK_EVENTS;
|
|
2947
|
+
var init_hook_types = __esm({
|
|
2948
|
+
"src/core/hook-types.ts"() {
|
|
2949
|
+
BEST_EFFORT_HOOK_EVENTS = /* @__PURE__ */ new Set([
|
|
2950
|
+
"UserPromptSubmit",
|
|
2951
|
+
"PostToolUseFailure",
|
|
2952
|
+
"SessionStart"
|
|
2953
|
+
]);
|
|
2954
|
+
}
|
|
2955
|
+
});
|
|
2956
|
+
|
|
2945
2957
|
// src/core/lint/shared/helpers.ts
|
|
2946
2958
|
function createWarning(file, target31, message) {
|
|
2947
2959
|
return {
|
|
@@ -2957,6 +2969,13 @@ function formatOxfordComma(items) {
|
|
|
2957
2969
|
if (items.length === 2) return `${items[0]} and ${items[1]}`;
|
|
2958
2970
|
return `${items.slice(0, -1).join(", ")}, and ${items[items.length - 1]}`;
|
|
2959
2971
|
}
|
|
2972
|
+
function unsupportedHookEventNames(hooks, supportedEvents) {
|
|
2973
|
+
if (!hooks) return [];
|
|
2974
|
+
const supported = new Set(supportedEvents);
|
|
2975
|
+
return Object.keys(hooks).filter(
|
|
2976
|
+
(event) => !supported.has(event) && !BEST_EFFORT_HOOK_EVENTS.has(event)
|
|
2977
|
+
);
|
|
2978
|
+
}
|
|
2960
2979
|
function createUnsupportedHookWarning(event, target31, supportedEvents, options) {
|
|
2961
2980
|
const by = options?.unsupportedBy ?? target31;
|
|
2962
2981
|
const supported = formatOxfordComma(supportedEvents);
|
|
@@ -2968,6 +2987,7 @@ function createUnsupportedHookWarning(event, target31, supportedEvents, options)
|
|
|
2968
2987
|
}
|
|
2969
2988
|
var init_helpers = __esm({
|
|
2970
2989
|
"src/core/lint/shared/helpers.ts"() {
|
|
2990
|
+
init_hook_types();
|
|
2971
2991
|
}
|
|
2972
2992
|
});
|
|
2973
2993
|
|
|
@@ -6311,8 +6331,7 @@ var init_linter5 = __esm({
|
|
|
6311
6331
|
// src/targets/augment-code/lint.ts
|
|
6312
6332
|
function lintHooks3(canonical) {
|
|
6313
6333
|
if (!canonical.hooks || Object.keys(canonical.hooks).length === 0) return [];
|
|
6314
|
-
|
|
6315
|
-
return Object.keys(canonical.hooks).filter((event) => !supportedSet.has(event)).map(
|
|
6334
|
+
return unsupportedHookEventNames(canonical.hooks, SUPPORTED_HOOK_EVENTS).map(
|
|
6316
6335
|
(event) => createUnsupportedHookWarning(event, "augment-code", SUPPORTED_HOOK_EVENTS, {
|
|
6317
6336
|
unsupportedBy: "AugmentCode hooks"
|
|
6318
6337
|
})
|
|
@@ -10317,8 +10336,7 @@ function lintCommands3(canonical) {
|
|
|
10317
10336
|
function lintHooks5(canonical) {
|
|
10318
10337
|
if (!canonical.hooks || Object.keys(canonical.hooks).length === 0) return [];
|
|
10319
10338
|
const supported = ["PreToolUse", "PostToolUse", "Notification", "UserPromptSubmit"];
|
|
10320
|
-
const
|
|
10321
|
-
const diagnostics = Object.keys(canonical.hooks).filter((event) => !supportedSet.has(event)).map(
|
|
10339
|
+
const diagnostics = unsupportedHookEventNames(canonical.hooks, supported).map(
|
|
10322
10340
|
(event) => createUnsupportedHookWarning(event, "copilot", supported, {
|
|
10323
10341
|
unsupportedBy: "Copilot hooks"
|
|
10324
10342
|
})
|
|
@@ -11436,7 +11454,7 @@ var init_permissions2 = __esm({
|
|
|
11436
11454
|
// src/targets/cursor/hook-format.ts
|
|
11437
11455
|
function unmappedCursorHookEvents(hooks) {
|
|
11438
11456
|
return Object.keys(hooks).filter(
|
|
11439
|
-
(event) => Array.isArray(hooks[event]) && hooks[event].length > 0 && !(event in CANONICAL_TO_CURSOR)
|
|
11457
|
+
(event) => Array.isArray(hooks[event]) && hooks[event].length > 0 && !(event in CANONICAL_TO_CURSOR) && !BEST_EFFORT_HOOK_EVENTS.has(event)
|
|
11440
11458
|
);
|
|
11441
11459
|
}
|
|
11442
11460
|
function toCursorHooks(hooks) {
|
|
@@ -11484,6 +11502,7 @@ function cursorHooksToCanonical(hooks) {
|
|
|
11484
11502
|
var CANONICAL_TO_CURSOR, CURSOR_TO_CANONICAL;
|
|
11485
11503
|
var init_hook_format = __esm({
|
|
11486
11504
|
"src/targets/cursor/hook-format.ts"() {
|
|
11505
|
+
init_hook_types();
|
|
11487
11506
|
init_hook_command();
|
|
11488
11507
|
CANONICAL_TO_CURSOR = {
|
|
11489
11508
|
PreToolUse: "preToolUse",
|
|
@@ -13934,8 +13953,9 @@ function lintCommands6(canonical) {
|
|
|
13934
13953
|
function lintHooks7(canonical) {
|
|
13935
13954
|
if (!canonical.hooks || Object.keys(canonical.hooks).length === 0) return [];
|
|
13936
13955
|
const supported = ["PreToolUse", "PostToolUse", "Notification"];
|
|
13937
|
-
|
|
13938
|
-
|
|
13956
|
+
return unsupportedHookEventNames(canonical.hooks, supported).map(
|
|
13957
|
+
(event) => createUnsupportedHookWarning(event, "gemini-cli", supported)
|
|
13958
|
+
);
|
|
13939
13959
|
}
|
|
13940
13960
|
var init_lint14 = __esm({
|
|
13941
13961
|
"src/targets/gemini-cli/lint.ts"() {
|
|
@@ -16187,8 +16207,7 @@ var init_linter20 = __esm({
|
|
|
16187
16207
|
function lintHooks10(canonical) {
|
|
16188
16208
|
if (!canonical.hooks || Object.keys(canonical.hooks).length === 0) return [];
|
|
16189
16209
|
const supported = ["PreToolUse", "PostToolUse", "UserPromptSubmit", "SubagentStop"];
|
|
16190
|
-
|
|
16191
|
-
return Object.keys(canonical.hooks).filter((event) => !supportedSet.has(event)).map(
|
|
16210
|
+
return unsupportedHookEventNames(canonical.hooks, supported).map(
|
|
16192
16211
|
(event) => createUnsupportedHookWarning(event, "kiro", supported, { unsupportedBy: "Kiro hooks" })
|
|
16193
16212
|
);
|
|
16194
16213
|
}
|
|
@@ -21595,7 +21614,14 @@ var extendSourceSchema = z.object({
|
|
|
21595
21614
|
features: z.array(featureSchema),
|
|
21596
21615
|
/** Repo-relative POSIX path for discovery (skill packs, nested .agentsmesh). */
|
|
21597
21616
|
path: z.string().optional(),
|
|
21598
|
-
pick: extendPickSchema.optional()
|
|
21617
|
+
pick: extendPickSchema.optional(),
|
|
21618
|
+
/**
|
|
21619
|
+
* Per-entry consent for "elevated" artifacts (`hooks`, `permissions`, `mcp`)
|
|
21620
|
+
* from a remote source. These control local code execution at generate time,
|
|
21621
|
+
* so for non-local extends they are stripped by default and only merged for
|
|
21622
|
+
* the artifacts listed here. Local extends are trusted and ignore this field.
|
|
21623
|
+
*/
|
|
21624
|
+
accept: z.array(z.enum(["hooks", "permissions", "mcp"])).optional()
|
|
21599
21625
|
});
|
|
21600
21626
|
var collaborationSchema = z.object({
|
|
21601
21627
|
strategy: z.enum(["merge", "lock", "last-wins"]).default("merge"),
|
|
@@ -22304,7 +22330,9 @@ async function resolveExtendPaths(config, configDir, options = {}) {
|
|
|
22304
22330
|
as: ext.as,
|
|
22305
22331
|
version: fetched.version,
|
|
22306
22332
|
path: ext.path,
|
|
22307
|
-
pick: ext.pick
|
|
22333
|
+
pick: ext.pick,
|
|
22334
|
+
isRemote: true,
|
|
22335
|
+
accept: ext.accept
|
|
22308
22336
|
});
|
|
22309
22337
|
continue;
|
|
22310
22338
|
}
|
|
@@ -22321,7 +22349,9 @@ async function resolveExtendPaths(config, configDir, options = {}) {
|
|
|
22321
22349
|
target: ext.target,
|
|
22322
22350
|
as: ext.as,
|
|
22323
22351
|
path: ext.path,
|
|
22324
|
-
pick: ext.pick
|
|
22352
|
+
pick: ext.pick,
|
|
22353
|
+
isRemote: false,
|
|
22354
|
+
accept: ext.accept
|
|
22325
22355
|
});
|
|
22326
22356
|
}
|
|
22327
22357
|
return result;
|
|
@@ -23770,6 +23800,47 @@ function applyExtendPick(canonical, features, pick, extendName) {
|
|
|
23770
23800
|
return next;
|
|
23771
23801
|
}
|
|
23772
23802
|
|
|
23803
|
+
// src/install/core/elevated-artifacts.ts
|
|
23804
|
+
function stripUntrustedElevatedArtifacts(canonical, options) {
|
|
23805
|
+
if (options.sourceKind === "local") {
|
|
23806
|
+
return { canonical, stripped: [] };
|
|
23807
|
+
}
|
|
23808
|
+
const stripped = [];
|
|
23809
|
+
let hooks = canonical.hooks;
|
|
23810
|
+
let permissions = canonical.permissions;
|
|
23811
|
+
let mcp = canonical.mcp;
|
|
23812
|
+
if (hooks !== null && !options.acceptHooks) {
|
|
23813
|
+
hooks = null;
|
|
23814
|
+
stripped.push("hooks");
|
|
23815
|
+
}
|
|
23816
|
+
if (permissions !== null && !options.acceptPermissions) {
|
|
23817
|
+
permissions = null;
|
|
23818
|
+
stripped.push("permissions");
|
|
23819
|
+
}
|
|
23820
|
+
if (mcp !== null && !options.acceptMcp) {
|
|
23821
|
+
mcp = null;
|
|
23822
|
+
stripped.push("mcp");
|
|
23823
|
+
}
|
|
23824
|
+
if (stripped.length === 0) {
|
|
23825
|
+
return { canonical, stripped: [] };
|
|
23826
|
+
}
|
|
23827
|
+
return {
|
|
23828
|
+
canonical: { ...canonical, hooks, permissions, mcp },
|
|
23829
|
+
stripped
|
|
23830
|
+
};
|
|
23831
|
+
}
|
|
23832
|
+
|
|
23833
|
+
// src/canonical/extends/extend-elevated.ts
|
|
23834
|
+
function gateExtendElevatedArtifacts(canonical, ext) {
|
|
23835
|
+
const accept = ext.accept ?? [];
|
|
23836
|
+
return stripUntrustedElevatedArtifacts(canonical, {
|
|
23837
|
+
sourceKind: ext.isRemote ? "git" : "local",
|
|
23838
|
+
acceptHooks: accept.includes("hooks"),
|
|
23839
|
+
acceptPermissions: accept.includes("permissions"),
|
|
23840
|
+
acceptMcp: accept.includes("mcp")
|
|
23841
|
+
});
|
|
23842
|
+
}
|
|
23843
|
+
|
|
23773
23844
|
// src/install/pack/pack-reader.ts
|
|
23774
23845
|
init_fs();
|
|
23775
23846
|
var MANUAL_INSTALL_AS = ["rules", "commands", "agents", "skills"];
|
|
@@ -23919,7 +23990,14 @@ async function loadCanonicalWithExtends(config, configDir, options = {}, canonic
|
|
|
23919
23990
|
for (const ext of resolvedExtends) {
|
|
23920
23991
|
const extCanonical = await loadCanonicalForExtend(ext);
|
|
23921
23992
|
const filtered = filterCanonicalByFeatures(extCanonical, ext.features);
|
|
23922
|
-
const
|
|
23993
|
+
const gated = gateExtendElevatedArtifacts(filtered, ext);
|
|
23994
|
+
if (gated.stripped.length > 0) {
|
|
23995
|
+
const list = gated.stripped.join(", ");
|
|
23996
|
+
logger.warn(
|
|
23997
|
+
`[agentsmesh] Extend "${ext.name}": stripped elevated artifacts from a remote source: ${list}. Add \`accept: [${list}]\` to this extends entry in agentsmesh.yaml to keep them.`
|
|
23998
|
+
);
|
|
23999
|
+
}
|
|
24000
|
+
const picked = applyExtendPick(gated.canonical, ext.features, ext.pick, ext.name);
|
|
23923
24001
|
merged = mergeCanonicalFiles(merged, picked);
|
|
23924
24002
|
}
|
|
23925
24003
|
const packsCanonical = await loadPacksCanonical(canonicalDir);
|
|
@@ -23955,7 +24033,18 @@ function canonicalize(path) {
|
|
|
23955
24033
|
try {
|
|
23956
24034
|
return realpathSync(path);
|
|
23957
24035
|
} catch {
|
|
23958
|
-
|
|
24036
|
+
let current = resolve(path);
|
|
24037
|
+
const tail = [];
|
|
24038
|
+
for (; ; ) {
|
|
24039
|
+
const parent = dirname(current);
|
|
24040
|
+
if (parent === current) return resolve(path);
|
|
24041
|
+
tail.unshift(basename(current));
|
|
24042
|
+
try {
|
|
24043
|
+
return join(realpathSync(parent), ...tail);
|
|
24044
|
+
} catch {
|
|
24045
|
+
current = parent;
|
|
24046
|
+
}
|
|
24047
|
+
}
|
|
23959
24048
|
}
|
|
23960
24049
|
}
|
|
23961
24050
|
function assertSourceInsideProjectRoot(resolvedPath, projectRoot) {
|
|
@@ -24213,7 +24302,8 @@ function lintRuleScopeInversion(input) {
|
|
|
24213
24302
|
}
|
|
24214
24303
|
return out2;
|
|
24215
24304
|
}
|
|
24216
|
-
var CURRENT_GRAPH_VERSION =
|
|
24305
|
+
var CURRENT_GRAPH_VERSION = 2;
|
|
24306
|
+
var VersionSchema = z.union([z.literal(1), z.literal(2)]);
|
|
24217
24307
|
var MAX_RULE_LENGTH = 2e3;
|
|
24218
24308
|
var IdSchema = z.string().regex(/^[a-z0-9-]+$/, "id must be kebab-case");
|
|
24219
24309
|
var DateSchema = z.string().regex(
|
|
@@ -24237,10 +24327,17 @@ var LessonSchema = z.object({
|
|
|
24237
24327
|
evidence: z.array(z.string().min(1)),
|
|
24238
24328
|
status: LessonStatusSchema,
|
|
24239
24329
|
supersededBy: IdSchema.optional(),
|
|
24240
|
-
createdAt: DateSchema
|
|
24330
|
+
createdAt: DateSchema,
|
|
24331
|
+
/**
|
|
24332
|
+
* `'always'` marks an ALWAYS-ON lesson: a universal standard delivered on
|
|
24333
|
+
* every task (via the UserPromptSubmit hook / a `--always` recall) rather than
|
|
24334
|
+
* matched by triggers. Such a lesson needs no trigger and is excluded from
|
|
24335
|
+
* triggered recall. Absent = a normal triggered lesson.
|
|
24336
|
+
*/
|
|
24337
|
+
scope: z.literal("always").optional()
|
|
24241
24338
|
}).strict();
|
|
24242
24339
|
var LessonsGraphSchema = z.object({
|
|
24243
|
-
version:
|
|
24340
|
+
version: VersionSchema,
|
|
24244
24341
|
lessons: z.record(IdSchema, LessonSchema),
|
|
24245
24342
|
topics: z.record(IdSchema, TopicSchema),
|
|
24246
24343
|
triggers: z.record(IdSchema, TriggerSchema)
|
|
@@ -24305,7 +24402,7 @@ var LESSONS_PROCEDURAL_RULE = `## Lessons (BLOCKING)
|
|
|
24305
24402
|
|
|
24306
24403
|
Graph \`.agentsmesh/lessons/lessons.json\` is canonical; never hand-edit it. Manual: \`lessons\` skill.
|
|
24307
24404
|
|
|
24308
|
-
**Recall:** before every file edit or state-changing command, MUST run \`agentsmesh lessons query --file <path> --cmd <command>\` and obey matches. Pure-read commands and recall itself are exempt.
|
|
24405
|
+
**Recall:** before every file edit or state-changing command, MUST run \`agentsmesh lessons query --file <path> --cmd <command>\` and obey matches; at task start, ALSO run \`agentsmesh lessons query --keyword "<task terms>" --always\` for conceptual + universal rules no path/command names. Pure-read commands and recall itself are exempt.
|
|
24309
24406
|
|
|
24310
24407
|
**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>\`.
|
|
24311
24408
|
|
|
@@ -25181,6 +25278,12 @@ function collectDeadFileGlobs(graph, findings, knownPaths) {
|
|
|
25181
25278
|
});
|
|
25182
25279
|
}
|
|
25183
25280
|
}
|
|
25281
|
+
function fileGlobMatchCount(pattern, knownPaths) {
|
|
25282
|
+
const isMatch = picomatch(pattern, { dot: true });
|
|
25283
|
+
let n = 0;
|
|
25284
|
+
for (const p of knownPaths) if (isMatch(p)) n += 1;
|
|
25285
|
+
return n;
|
|
25286
|
+
}
|
|
25184
25287
|
var RUNNER_ANCHOR = /^\^(pnpm|npm|npx|yarn|bun)\b/;
|
|
25185
25288
|
function collectRunnerAnchoredPatterns(graph, findings) {
|
|
25186
25289
|
const active = activeTriggerIds(graph);
|
|
@@ -25811,14 +25914,14 @@ var UnrecallableLessonError = class extends Error {
|
|
|
25811
25914
|
};
|
|
25812
25915
|
|
|
25813
25916
|
// src/lessons/capture-guardrails.ts
|
|
25814
|
-
var
|
|
25917
|
+
var WIDE_GLOB_MATCH_COUNT = 40;
|
|
25815
25918
|
var MAX_RECOMMENDED_TRIGGERS = 8;
|
|
25816
25919
|
function isBroadGlob(pattern) {
|
|
25817
25920
|
const p = pattern.trim();
|
|
25818
25921
|
if (p === "*" || p === "**") return true;
|
|
25819
25922
|
if (!p.includes("**")) return false;
|
|
25820
|
-
const
|
|
25821
|
-
return
|
|
25923
|
+
const basename71 = p.slice(p.lastIndexOf("/") + 1);
|
|
25924
|
+
return basename71.startsWith("*");
|
|
25822
25925
|
}
|
|
25823
25926
|
function inspectCapturedLesson(graph, lessonId, knownPaths) {
|
|
25824
25927
|
const lesson = graph.lessons[lessonId];
|
|
@@ -25867,9 +25970,19 @@ function inspectCapturedLesson(graph, lessonId, knownPaths) {
|
|
|
25867
25970
|
message: `Lesson "${lessonId}" has file_glob trigger(s) (${deadHere.join(", ")}) that match no file in the working tree \u2014 likely a rename. Re-point them at the current path, or the lesson is unreachable via those globs.`
|
|
25868
25971
|
});
|
|
25869
25972
|
}
|
|
25973
|
+
const wide = triggers.filter((t) => t.kind === "file_glob" && !isBroadGlob(t.pattern)).filter((t) => fileGlobMatchCount(t.pattern, knownPaths) > WIDE_GLOB_MATCH_COUNT).map((t) => t.pattern);
|
|
25974
|
+
if (wide.length > 0) {
|
|
25975
|
+
warnings.push({
|
|
25976
|
+
code: "WIDE_GLOB_MATCH",
|
|
25977
|
+
message: `Lesson "${lessonId}" has file glob(s) (${wide.join(", ")}) matching more than ${WIDE_GLOB_MATCH_COUNT} files in the working tree; narrow to the file-CLASS where the rule actually applies so it does not fire on unrelated edits.`
|
|
25978
|
+
});
|
|
25979
|
+
}
|
|
25870
25980
|
}
|
|
25871
25981
|
return warnings;
|
|
25872
25982
|
}
|
|
25983
|
+
|
|
25984
|
+
// src/lessons/capture-near-duplicate.ts
|
|
25985
|
+
var NEAR_DUPLICATE_THRESHOLD = 0.6;
|
|
25873
25986
|
function nearDuplicateWarning(graph, lessonId) {
|
|
25874
25987
|
const subject = graph.lessons[lessonId];
|
|
25875
25988
|
if (subject === void 0) return null;
|
|
@@ -26089,7 +26202,7 @@ async function importLegacyLessons(projectRoot, options) {
|
|
|
26089
26202
|
if (options.force !== true && populated) {
|
|
26090
26203
|
throw new LessonsGraphExistsError();
|
|
26091
26204
|
}
|
|
26092
|
-
g.version =
|
|
26205
|
+
g.version = CURRENT_GRAPH_VERSION;
|
|
26093
26206
|
g.lessons = lessons;
|
|
26094
26207
|
g.topics = topics;
|
|
26095
26208
|
g.triggers = triggers;
|
|
@@ -26240,7 +26353,7 @@ function getHostname() {
|
|
|
26240
26353
|
return hostname();
|
|
26241
26354
|
}
|
|
26242
26355
|
function sleep2(ms) {
|
|
26243
|
-
return new Promise((
|
|
26356
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
26244
26357
|
}
|
|
26245
26358
|
|
|
26246
26359
|
// src/lessons/lessons-lock.ts
|
|
@@ -26256,7 +26369,7 @@ async function acquireLessonsLock(projectRoot, opts = {}) {
|
|
|
26256
26369
|
|
|
26257
26370
|
// src/lessons/mutate.ts
|
|
26258
26371
|
function emptyGraph() {
|
|
26259
|
-
return { version:
|
|
26372
|
+
return { version: CURRENT_GRAPH_VERSION, lessons: {}, topics: {}, triggers: {} };
|
|
26260
26373
|
}
|
|
26261
26374
|
function findingKey(f) {
|
|
26262
26375
|
return `${f.code}|${f.triggerId ?? ""}|${f.lessonId ?? ""}`;
|
|
@@ -26280,6 +26393,7 @@ async function mutateLessonsGraphLocked(projectRoot, mutator, options = {}) {
|
|
|
26280
26393
|
`mutateLessonsGraph: refusing to write \u2014 this change introduces ${errors}. (Pre-existing graph issues are not blocking; run \`agentsmesh lessons validate\` to review and \`lessons untrigger\`/\`prune\` to repair them.)`
|
|
26281
26394
|
);
|
|
26282
26395
|
}
|
|
26396
|
+
graph.version = CURRENT_GRAPH_VERSION;
|
|
26283
26397
|
saveLessonsGraph(projectRoot, graph);
|
|
26284
26398
|
return result;
|
|
26285
26399
|
} finally {
|
|
@@ -26357,14 +26471,15 @@ function addLessonInto(graph, input, options) {
|
|
|
26357
26471
|
}
|
|
26358
26472
|
graph.topics[input.topic] = { summary: options.topicSummary };
|
|
26359
26473
|
}
|
|
26360
|
-
|
|
26474
|
+
const skipTriggerGates = options.allowNoTrigger === true || input.scope === "always";
|
|
26475
|
+
if (!skipTriggerGates) {
|
|
26361
26476
|
const existingTriggers = existingId !== null ? graph.lessons[existingId]?.triggers.length ?? 0 : 0;
|
|
26362
26477
|
if (countInputTriggers(input.triggers) === 0 && existingTriggers === 0) {
|
|
26363
26478
|
throw new NoTriggerError();
|
|
26364
26479
|
}
|
|
26365
26480
|
}
|
|
26366
26481
|
const { triggerIds, newTriggerIds } = mergeTriggers(graph, input.triggers);
|
|
26367
|
-
if (
|
|
26482
|
+
if (!skipTriggerGates) {
|
|
26368
26483
|
const resultingTriggers = existingId !== null ? union(graph.lessons[existingId].triggers, triggerIds) : triggerIds;
|
|
26369
26484
|
const blockingDead = blockingDeadTriggers(graph, resultingTriggers);
|
|
26370
26485
|
if (resultingTriggers.length > 0 && blockingDead.length === resultingTriggers.length) {
|
|
@@ -26378,7 +26493,9 @@ function addLessonInto(graph, input, options) {
|
|
|
26378
26493
|
topics: union(existing.topics, [input.topic]),
|
|
26379
26494
|
triggers: union(existing.triggers, triggerIds),
|
|
26380
26495
|
evidence: union(existing.evidence, input.evidence ?? []),
|
|
26381
|
-
...existing.rationale === void 0 && input.rationale !== void 0 ? { rationale: input.rationale } : {}
|
|
26496
|
+
...existing.rationale === void 0 && input.rationale !== void 0 ? { rationale: input.rationale } : {},
|
|
26497
|
+
// Re-capturing a rule with --scope always promotes it to always-on.
|
|
26498
|
+
...input.scope === "always" ? { scope: "always" } : {}
|
|
26382
26499
|
};
|
|
26383
26500
|
return {
|
|
26384
26501
|
id: existingId,
|
|
@@ -26398,7 +26515,8 @@ function addLessonInto(graph, input, options) {
|
|
|
26398
26515
|
evidence: input.evidence === void 0 ? [] : [...input.evidence],
|
|
26399
26516
|
status: "active",
|
|
26400
26517
|
createdAt: input.createdAt ?? todayIso(),
|
|
26401
|
-
...input.rationale === void 0 ? {} : { rationale: input.rationale }
|
|
26518
|
+
...input.rationale === void 0 ? {} : { rationale: input.rationale },
|
|
26519
|
+
...input.scope === "always" ? { scope: "always" } : {}
|
|
26402
26520
|
};
|
|
26403
26521
|
const warnings = inspectCapturedLesson(graph, id, options.knownPaths);
|
|
26404
26522
|
const nearDup = nearDuplicateWarning(graph, id);
|
|
@@ -26450,14 +26568,19 @@ function captureLogPath(projectRoot) {
|
|
|
26450
26568
|
}
|
|
26451
26569
|
|
|
26452
26570
|
// src/lessons/keyword-match.ts
|
|
26453
|
-
function splitTokens(text) {
|
|
26454
|
-
return text.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 0);
|
|
26455
|
-
}
|
|
26456
26571
|
function deriveHaystackTokens(query) {
|
|
26457
26572
|
const parts = [];
|
|
26458
26573
|
if (query.file !== void 0) parts.push(query.file);
|
|
26459
26574
|
if (query.command !== void 0) parts.push(query.command);
|
|
26460
|
-
|
|
26575
|
+
if (parts.length === 0) return [];
|
|
26576
|
+
const out2 = [];
|
|
26577
|
+
for (const raw of parts.join(" ").split(/[^A-Za-z0-9]+/)) {
|
|
26578
|
+
if (raw.length === 0) continue;
|
|
26579
|
+
out2.push(raw.toLowerCase());
|
|
26580
|
+
const sub = raw.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").toLowerCase().split(" ").filter((t) => t.length > 0);
|
|
26581
|
+
if (sub.length > 1) out2.push(...sub);
|
|
26582
|
+
}
|
|
26583
|
+
return out2;
|
|
26461
26584
|
}
|
|
26462
26585
|
function containsRun(needle, hay) {
|
|
26463
26586
|
if (needle.length === 0) return false;
|
|
@@ -26490,6 +26613,7 @@ function queryLessons(graph, query) {
|
|
|
26490
26613
|
const matched = [];
|
|
26491
26614
|
for (const [id, lesson] of Object.entries(graph.lessons)) {
|
|
26492
26615
|
if (lesson.status !== "active") continue;
|
|
26616
|
+
if (lesson.scope === "always") continue;
|
|
26493
26617
|
if (lesson.triggers.some((t) => matchedTriggerIds.has(t))) {
|
|
26494
26618
|
matched.push({ id, lesson });
|
|
26495
26619
|
}
|
|
@@ -26535,6 +26659,7 @@ var RRF_K = 60;
|
|
|
26535
26659
|
var SPECIFICITY_WEIGHT = 3;
|
|
26536
26660
|
var TOPIC_COHERENCE_WEIGHT = 2;
|
|
26537
26661
|
var BM25_WEIGHT = 1;
|
|
26662
|
+
var EFFECTIVENESS_WEIGHT = 1;
|
|
26538
26663
|
function rankMap(items) {
|
|
26539
26664
|
const sorted = [...items].sort(
|
|
26540
26665
|
(a, b) => b.value !== a.value ? b.value - a.value : a.id < b.id ? -1 : 1
|
|
@@ -26571,16 +26696,20 @@ function rankLessons(graph, query, matches, options = {}) {
|
|
|
26571
26696
|
specificity,
|
|
26572
26697
|
// `id` is a matched lesson, and buildTopicCoherence keys every matched id.
|
|
26573
26698
|
topicCoherence: coherence.get(id),
|
|
26574
|
-
matchedTriggers: hitTriggers
|
|
26699
|
+
matchedTriggers: hitTriggers,
|
|
26700
|
+
// Absent from the log ⇒ neutral 1, so a lesson with no outcome data never
|
|
26701
|
+
// sinks below one that has merely been recorded.
|
|
26702
|
+
effectiveness: options.effectiveness?.get(id) ?? 1
|
|
26575
26703
|
};
|
|
26576
26704
|
});
|
|
26577
26705
|
const bm25Ranks = rankMap(scored.map((s) => ({ id: s.id, value: s.bm25 })));
|
|
26578
26706
|
const specRanks = rankMap(scored.map((s) => ({ id: s.id, value: s.specificity })));
|
|
26579
26707
|
const topicRanks = rankMap(scored.map((s) => ({ id: s.id, value: s.topicCoherence })));
|
|
26708
|
+
const effRanks = rankMap(scored.map((s) => ({ id: s.id, value: s.effectiveness })));
|
|
26580
26709
|
const ranked = scored.map((s) => ({
|
|
26581
26710
|
id: s.id,
|
|
26582
26711
|
lesson: s.lesson,
|
|
26583
|
-
score: SPECIFICITY_WEIGHT / (RRF_K + specRanks.get(s.id)) + TOPIC_COHERENCE_WEIGHT / (RRF_K + topicRanks.get(s.id)) + BM25_WEIGHT / (RRF_K + bm25Ranks.get(s.id)),
|
|
26712
|
+
score: SPECIFICITY_WEIGHT / (RRF_K + specRanks.get(s.id)) + TOPIC_COHERENCE_WEIGHT / (RRF_K + topicRanks.get(s.id)) + BM25_WEIGHT / (RRF_K + bm25Ranks.get(s.id)) + EFFECTIVENESS_WEIGHT / (RRF_K + effRanks.get(s.id)),
|
|
26584
26713
|
reason: {
|
|
26585
26714
|
matchedTriggers: s.matchedTriggers,
|
|
26586
26715
|
bm25: s.bm25,
|
|
@@ -26619,6 +26748,9 @@ function defaultLessonsConfig() {
|
|
|
26619
26748
|
autoPrune: false
|
|
26620
26749
|
};
|
|
26621
26750
|
}
|
|
26751
|
+
function outcomeLogPath(projectRoot) {
|
|
26752
|
+
return join(lessonsPaths(projectRoot).base, "outcome-log.jsonl");
|
|
26753
|
+
}
|
|
26622
26754
|
|
|
26623
26755
|
// src/lessons/merge.ts
|
|
26624
26756
|
async function mergeLessons(projectRoot, loserId, keeperId, options = {}) {
|
|
@@ -26701,18 +26833,27 @@ async function stripMarkersInGraph(projectRoot, options = {}) {
|
|
|
26701
26833
|
return { changedIds, changedCount: changedIds.length };
|
|
26702
26834
|
}
|
|
26703
26835
|
var RECALL_HOOK_COMMAND = "agentsmesh lessons hook";
|
|
26704
|
-
var
|
|
26705
|
-
var RECALL_EVENTS = [
|
|
26706
|
-
|
|
26836
|
+
var RECALL_HOOK_TOOL_MATCHER = "Edit|Write|Bash";
|
|
26837
|
+
var RECALL_EVENTS = [
|
|
26838
|
+
{ event: "PreToolUse", matcher: RECALL_HOOK_TOOL_MATCHER },
|
|
26839
|
+
{ event: "PostToolUse", matcher: RECALL_HOOK_TOOL_MATCHER },
|
|
26840
|
+
{ event: "UserPromptSubmit", matcher: "*" },
|
|
26841
|
+
// Capture-on-failure nudge (see capture-nudge.ts). BEST-EFFORT: only Claude
|
|
26842
|
+
// Code's passthrough hooks emit it; whitelist targets drop it without warning
|
|
26843
|
+
// (BEST_EFFORT_HOOK_EVENTS). PostToolUse is success-only, so failures need this.
|
|
26844
|
+
{ event: "PostToolUseFailure", matcher: "*" },
|
|
26845
|
+
// Reset recall dedup after a context compaction/clear (see hook.ts SessionStart).
|
|
26846
|
+
// BEST-EFFORT: targets that can't represent SessionStart just keep dedup as-is.
|
|
26847
|
+
{ event: "SessionStart", matcher: "*" }
|
|
26848
|
+
];
|
|
26849
|
+
function injectEvent(doc, event, matcher) {
|
|
26707
26850
|
const existing = doc.get(event);
|
|
26708
26851
|
const seq = existing instanceof YAMLSeq ? existing : new YAMLSeq();
|
|
26709
26852
|
const present = seq.items.some(
|
|
26710
26853
|
(item) => item instanceof YAMLMap && item.get("command") === RECALL_HOOK_COMMAND
|
|
26711
26854
|
);
|
|
26712
26855
|
if (present) return false;
|
|
26713
|
-
seq.add(
|
|
26714
|
-
doc.createNode({ matcher: RECALL_HOOK_MATCHER, type: "command", command: RECALL_HOOK_COMMAND })
|
|
26715
|
-
);
|
|
26856
|
+
seq.add(doc.createNode({ matcher, type: "command", command: RECALL_HOOK_COMMAND }));
|
|
26716
26857
|
doc.set(event, seq);
|
|
26717
26858
|
return true;
|
|
26718
26859
|
}
|
|
@@ -26721,13 +26862,32 @@ function injectRecallHook(projectRoot) {
|
|
|
26721
26862
|
if (!existsSync(path)) return false;
|
|
26722
26863
|
const doc = parseDocument(readFileSync(path, "utf8"));
|
|
26723
26864
|
let changed = false;
|
|
26724
|
-
for (const event of RECALL_EVENTS) {
|
|
26725
|
-
if (injectEvent(doc, event)) changed = true;
|
|
26865
|
+
for (const { event, matcher } of RECALL_EVENTS) {
|
|
26866
|
+
if (injectEvent(doc, event, matcher)) changed = true;
|
|
26726
26867
|
}
|
|
26727
26868
|
if (changed) writeFileSync(path, String(doc), "utf8");
|
|
26728
26869
|
return changed;
|
|
26729
26870
|
}
|
|
26730
26871
|
|
|
26872
|
+
// src/lessons/merge-driver-setup.ts
|
|
26873
|
+
var LESSONS_MERGE_DRIVER = "agentsmesh-lessons";
|
|
26874
|
+
var LESSONS_GITATTRIBUTES_ENTRY = `.agentsmesh/lessons/lessons.json merge=${LESSONS_MERGE_DRIVER}`;
|
|
26875
|
+
|
|
26876
|
+
// src/utils/filesystem/gitattributes.ts
|
|
26877
|
+
init_fs();
|
|
26878
|
+
async function ensureGitattributesEntries(projectRoot, entries) {
|
|
26879
|
+
const path = join(projectRoot, ".gitattributes");
|
|
26880
|
+
const current = await readFileSafe(path) ?? "";
|
|
26881
|
+
const existing = new Set(
|
|
26882
|
+
current.split("\n").map((s) => s.trim()).filter((s) => s.length > 0 && !s.startsWith("#"))
|
|
26883
|
+
);
|
|
26884
|
+
const toAdd = entries.filter((e) => !existing.has(e.trim()));
|
|
26885
|
+
if (toAdd.length === 0) return false;
|
|
26886
|
+
const suffix = current.endsWith("\n") || current === "" ? "" : "\n";
|
|
26887
|
+
await writeFileAtomic(path, current + suffix + toAdd.join("\n") + "\n");
|
|
26888
|
+
return true;
|
|
26889
|
+
}
|
|
26890
|
+
|
|
26731
26891
|
// src/utils/filesystem/gitignore.ts
|
|
26732
26892
|
init_fs();
|
|
26733
26893
|
async function ensureGitignoreEntries(projectRoot, entries) {
|
|
@@ -26804,23 +26964,37 @@ regression / wrong assumption / surprise and you have not captured (nor stated
|
|
|
26804
26964
|
\`Lesson: none\`)? The task is INCOMPLETE \u2014 and the user will check. The graph
|
|
26805
26965
|
\`.agentsmesh/lessons/lessons.json\` is canonical \u2014 never hand-edit.
|
|
26806
26966
|
|
|
26807
|
-
## Recall \u2014 before each
|
|
26967
|
+
## Recall \u2014 before each edit/command, and at task start
|
|
26808
26968
|
|
|
26809
26969
|
\`agentsmesh lessons query --file <path> --cmd <command>\`, then apply every rule.
|
|
26810
26970
|
Pure-read commands (read-only) and the query itself are exempt. **keyword-only recall
|
|
26811
|
-
is the anti-pattern** \u2014
|
|
26971
|
+
for a specific edit is the anti-pattern** \u2014 anchor those to \`--file\`/\`--cmd\`. But at the
|
|
26972
|
+
START of a task (or when planning), run \`agentsmesh lessons query --keyword "<the task's
|
|
26973
|
+
key terms>" --always\`: that surfaces the conceptual rules no file/command names PLUS the
|
|
26974
|
+
universal always-on lessons \u2014 the manual equivalent of the automatic prompt recall on
|
|
26975
|
+
hook-capable tools. Author a \`keyword\` trigger beside a \`file_glob\` on conceptual lessons
|
|
26976
|
+
so they are reachable both ways. No shell \u2192 MCP \`lessons_query\` (\`file\`/\`command\`/\`keyword\`/\`always\`).
|
|
26812
26977
|
|
|
26813
26978
|
## Capture \u2014 Gate Function (before any completion claim)
|
|
26814
26979
|
|
|
26815
26980
|
1. **SELF-CRITIQUE**: any failure, correction, regression, wrong assumption,
|
|
26816
26981
|
useful surprise, repeated friction, or non-obvious fix? Failing
|
|
26817
26982
|
tests/lint/typecheck and user/review corrections \u2014 yours or anyone's \u2014 all count.
|
|
26983
|
+
PROCESS/TOOLING surprises count too (a sandbox command that silently no-op'd, a port
|
|
26984
|
+
already in use, a library's argument shape) \u2014 trigger those with \`--trigger-cmd\`. So
|
|
26985
|
+
does a review finding you REVIEWED then deliberately REJECTED: capture it as a "do NOT
|
|
26986
|
+
refactor" guard so the reasoning survives the session.
|
|
26818
26987
|
2. **CAPTURE** a reusable imperative rule with an effective trigger (else say so):
|
|
26819
|
-
\`agentsmesh lessons add "<rule>" --topic <id> --trigger-file <glob
|
|
26988
|
+
\`agentsmesh lessons add "<rule>" --topic <id> --trigger-file <glob>\`. Trigger on the
|
|
26989
|
+
file-CLASS where it will RECUR (e.g. \`**/lib/use*Form*.ts\`), NOT the single file you
|
|
26990
|
+
discovered it in \u2014 and NOT a broad \`src/**\`. If the rule is a UNIVERSAL standard that
|
|
26991
|
+
applies to EVERY task (a comment/test/style convention no file or command names),
|
|
26992
|
+
capture it with \`--scope always\` instead \u2014 it needs no trigger and is delivered on
|
|
26993
|
+
every task.
|
|
26820
26994
|
3. **RECEIPT**: emit \`Lesson: captured <id>\` or \`Lesson: none\`.
|
|
26821
26995
|
|
|
26822
|
-
At least one _effective_ trigger is required or
|
|
26823
|
-
(\`UNRECALLABLE_LESSON\`); prefer \`--trigger-file\`. No shell \u2192 MCP \`lessons_query\`,
|
|
26996
|
+
At least one _effective_ trigger is required (or \`--scope always\` for a universal rule) or
|
|
26997
|
+
the capture is rejected (\`UNRECALLABLE_LESSON\`); prefer \`--trigger-file\`. No shell \u2192 MCP \`lessons_query\`,
|
|
26824
26998
|
\`lessons_add\`, \`lessons_topics\`, \`lessons_show\`, \`lessons_deprecate\`. Run
|
|
26825
26999
|
\`agentsmesh lessons --help\` for every subcommand and flag: query, add, topics, show,
|
|
26826
27000
|
deprecate, merge, untrigger, strip-markers, prune, journal, validate, stats, import-md.
|
|
@@ -26866,9 +27040,21 @@ async function scaffoldLessons(projectRoot) {
|
|
|
26866
27040
|
const recallHookInjected = injectRecallHook(projectRoot);
|
|
26867
27041
|
const gitignoreUpdated = await ensureGitignoreEntries(projectRoot, [
|
|
26868
27042
|
toRelPath(projectRoot, recallLogPath(projectRoot)),
|
|
26869
|
-
toRelPath(projectRoot, captureLogPath(projectRoot))
|
|
27043
|
+
toRelPath(projectRoot, captureLogPath(projectRoot)),
|
|
27044
|
+
toRelPath(projectRoot, outcomeLogPath(projectRoot))
|
|
26870
27045
|
]);
|
|
26871
|
-
|
|
27046
|
+
const gitattributesUpdated = await ensureGitattributesEntries(projectRoot, [
|
|
27047
|
+
LESSONS_GITATTRIBUTES_ENTRY
|
|
27048
|
+
]);
|
|
27049
|
+
return {
|
|
27050
|
+
created,
|
|
27051
|
+
updated,
|
|
27052
|
+
skipped,
|
|
27053
|
+
rootRuleUpdated,
|
|
27054
|
+
gitignoreUpdated,
|
|
27055
|
+
gitattributesUpdated,
|
|
27056
|
+
recallHookInjected
|
|
27057
|
+
};
|
|
26872
27058
|
}
|
|
26873
27059
|
function seedLessonsSkill(projectRoot, created, updated, skipped) {
|
|
26874
27060
|
const skillPath = join(projectRoot, ".agentsmesh/skills", LESSONS_SKILL_NAME, "SKILL.md");
|