@theglitchking/semantic-memory 1.6.1 → 1.7.1

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.
Files changed (117) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +81 -0
  4. package/LICENSES/Apache-2.0.txt +202 -0
  5. package/README.md +19 -2
  6. package/THIRD_PARTY_NOTICES.md +17 -0
  7. package/commands/jev-mode.md +18 -0
  8. package/commands/skill-loop.md +23 -0
  9. package/dist/chunk-2BFG4IBC.js +295 -0
  10. package/dist/chunk-2BFG4IBC.js.map +1 -0
  11. package/dist/chunk-2OMDSTSS.js +118 -0
  12. package/dist/chunk-2OMDSTSS.js.map +1 -0
  13. package/dist/{chunk-TMMTOFHI.js → chunk-34VN47IW.js} +2 -2
  14. package/dist/chunk-3ML5ESXI.js +224 -0
  15. package/dist/chunk-3ML5ESXI.js.map +1 -0
  16. package/dist/{chunk-ZLIHQZ4C.js → chunk-6TIKWOY4.js} +5 -5
  17. package/dist/{chunk-KSH2QO6V.js → chunk-7UAVDOI6.js} +5 -5
  18. package/dist/chunk-ACZKDSMP.js +123 -0
  19. package/dist/chunk-ACZKDSMP.js.map +1 -0
  20. package/dist/{chunk-PGVIBUL3.js → chunk-AIXQFTSB.js} +5 -5
  21. package/dist/chunk-BSXIGPDY.js +137 -0
  22. package/dist/chunk-BSXIGPDY.js.map +1 -0
  23. package/dist/{chunk-HZ7B5KHY.js → chunk-EXAUVIGW.js} +4 -4
  24. package/dist/chunk-GYR7U6KA.js +80 -0
  25. package/dist/chunk-GYR7U6KA.js.map +1 -0
  26. package/dist/chunk-M2PC64DB.js +195 -0
  27. package/dist/chunk-M2PC64DB.js.map +1 -0
  28. package/dist/chunk-MTLYF357.js +130 -0
  29. package/dist/chunk-MTLYF357.js.map +1 -0
  30. package/dist/chunk-N77YCMSX.js +113 -0
  31. package/dist/chunk-N77YCMSX.js.map +1 -0
  32. package/dist/chunk-PJXYCCI2.js +177 -0
  33. package/dist/chunk-PJXYCCI2.js.map +1 -0
  34. package/dist/chunk-PMOZKUEM.js +170 -0
  35. package/dist/chunk-PMOZKUEM.js.map +1 -0
  36. package/dist/chunk-R6GNP7DS.js +87 -0
  37. package/dist/chunk-R6GNP7DS.js.map +1 -0
  38. package/dist/chunk-TKHWVISV.js +71 -0
  39. package/dist/chunk-TKHWVISV.js.map +1 -0
  40. package/dist/chunk-V6PSK4Y4.js +186 -0
  41. package/dist/chunk-V6PSK4Y4.js.map +1 -0
  42. package/dist/{chunk-GYBUBJYM.js → chunk-VBLCUWT4.js} +17 -8
  43. package/dist/chunk-VBLCUWT4.js.map +1 -0
  44. package/dist/chunk-VQCNWRTL.js +121 -0
  45. package/dist/chunk-VQCNWRTL.js.map +1 -0
  46. package/dist/{chunk-HUG7E25D.js → chunk-Y7H76Q7F.js} +1 -1
  47. package/dist/chunk-Y7H76Q7F.js.map +1 -0
  48. package/dist/chunk-ZSWI3Y4Y.js +232 -0
  49. package/dist/chunk-ZSWI3Y4Y.js.map +1 -0
  50. package/dist/cli/index.js +381 -52
  51. package/dist/cli/index.js.map +1 -1
  52. package/dist/core/features.d.ts +38 -0
  53. package/dist/core/features.js +28 -0
  54. package/dist/core/index.d.ts +285 -2
  55. package/dist/core/index.js +170 -21
  56. package/dist/core/index.js.map +1 -1
  57. package/dist/core/jev.d.ts +35 -0
  58. package/dist/core/jev.js +20 -0
  59. package/dist/core/skill-curator.d.ts +1 -0
  60. package/dist/core/skill-curator.js +41 -0
  61. package/dist/core/skill-evidence.d.ts +25 -0
  62. package/dist/core/skill-evidence.js +23 -0
  63. package/dist/core/skill-queue.d.ts +46 -0
  64. package/dist/core/skill-queue.js +42 -0
  65. package/dist/core/skill-review.d.ts +3 -0
  66. package/dist/core/skill-review.js +29 -0
  67. package/dist/core/skill-worker.d.ts +21 -0
  68. package/dist/core/skill-worker.js +70 -0
  69. package/dist/core/skill-worker.js.map +1 -0
  70. package/dist/{dossier-EN26WDKC.js → dossier-RXBLQOXC.js} +3 -3
  71. package/dist/failure-eval-IUJO47UC.js +18 -0
  72. package/dist/failure-eval-IUJO47UC.js.map +1 -0
  73. package/dist/{healthcheck-53OVXUQQ.js → healthcheck-S7D6YNTA.js} +7 -7
  74. package/dist/healthcheck-S7D6YNTA.js.map +1 -0
  75. package/dist/{lexicon-XNA5IFIA.js → lexicon-SEJSLBHO.js} +4 -4
  76. package/dist/lexicon-SEJSLBHO.js.map +1 -0
  77. package/dist/{lint-O4RQ47NX.js → lint-3JNLRM5U.js} +6 -6
  78. package/dist/lint-3JNLRM5U.js.map +1 -0
  79. package/dist/mcp/server.js +19 -17
  80. package/dist/mcp/server.js.map +1 -1
  81. package/dist/{profile-SIWAVD7O.js → profile-SNULTJ27.js} +4 -4
  82. package/dist/profile-SNULTJ27.js.map +1 -0
  83. package/dist/{schema-UJNYYRZZ.js → schema-RDYUK6CC.js} +2 -2
  84. package/dist/schema-RDYUK6CC.js.map +1 -0
  85. package/dist/skill-curator-D7HHZaUI.d.ts +71 -0
  86. package/dist/skill-land-EHXZW47F.js +31 -0
  87. package/dist/skill-land-EHXZW47F.js.map +1 -0
  88. package/dist/skill-ledger-AYXVNFX7.js +18 -0
  89. package/dist/skill-ledger-AYXVNFX7.js.map +1 -0
  90. package/dist/skill-merge-HBZBALZX.js +24 -0
  91. package/dist/skill-merge-HBZBALZX.js.map +1 -0
  92. package/dist/skill-review-BNvHipn9.d.ts +182 -0
  93. package/dist/skill-schema-RVB3EHEF.js +30 -0
  94. package/dist/skill-schema-RVB3EHEF.js.map +1 -0
  95. package/hooks/hooks.json +26 -1
  96. package/hooks/jev-bridge.py +109 -0
  97. package/hooks/session-start.js +5 -0
  98. package/hooks/skill-loop.js +65 -0
  99. package/hooks/vault-context.js +104 -6
  100. package/package.json +4 -2
  101. package/dist/chunk-5F5ZIAW2.js +0 -79
  102. package/dist/chunk-5F5ZIAW2.js.map +0 -1
  103. package/dist/chunk-GYBUBJYM.js.map +0 -1
  104. package/dist/chunk-HUG7E25D.js.map +0 -1
  105. package/dist/failure-eval-37JRNTIW.js +0 -10
  106. /package/dist/{chunk-TMMTOFHI.js.map → chunk-34VN47IW.js.map} +0 -0
  107. /package/dist/{chunk-ZLIHQZ4C.js.map → chunk-6TIKWOY4.js.map} +0 -0
  108. /package/dist/{chunk-KSH2QO6V.js.map → chunk-7UAVDOI6.js.map} +0 -0
  109. /package/dist/{chunk-PGVIBUL3.js.map → chunk-AIXQFTSB.js.map} +0 -0
  110. /package/dist/{chunk-HZ7B5KHY.js.map → chunk-EXAUVIGW.js.map} +0 -0
  111. /package/dist/{dossier-EN26WDKC.js.map → core/features.js.map} +0 -0
  112. /package/dist/{failure-eval-37JRNTIW.js.map → core/jev.js.map} +0 -0
  113. /package/dist/{healthcheck-53OVXUQQ.js.map → core/skill-curator.js.map} +0 -0
  114. /package/dist/{lexicon-XNA5IFIA.js.map → core/skill-evidence.js.map} +0 -0
  115. /package/dist/{lint-O4RQ47NX.js.map → core/skill-queue.js.map} +0 -0
  116. /package/dist/{profile-SIWAVD7O.js.map → core/skill-review.js.map} +0 -0
  117. /package/dist/{schema-UJNYYRZZ.js.map → dossier-RXBLQOXC.js.map} +0 -0
@@ -0,0 +1,232 @@
1
+ import {
2
+ matter_default
3
+ } from "./chunk-J7LYDRML.js";
4
+ import {
5
+ findSecrets
6
+ } from "./chunk-H2BES6FU.js";
7
+
8
+ // src/core/skill-schema.ts
9
+ import { existsSync, readFileSync, readdirSync, statSync } from "fs";
10
+ import { homedir } from "os";
11
+ import { join, basename } from "path";
12
+ import YAML from "yaml";
13
+ var SKILL_SCOPES = ["directory", "project", "global"];
14
+ var SKILL_STATUSES = ["draft", "active", "stale", "archived"];
15
+ var SKILL_SECTIONS = ["When to use", "Steps", "Pitfalls", "Verify"];
16
+ var GENERATED_BY = "semantic-memory";
17
+ var strings = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
18
+ function parseSkill(raw) {
19
+ let data = {};
20
+ let body = raw;
21
+ try {
22
+ const parsed = matter_default(raw);
23
+ data = parsed.data ?? {};
24
+ body = parsed.content;
25
+ } catch {
26
+ }
27
+ const m = data.skill_loop;
28
+ const meta = m && typeof m === "object" ? {
29
+ schema: m.schema,
30
+ scope: m.scope,
31
+ status: m.status,
32
+ pinned: m.pinned === true,
33
+ triggers: strings(m.triggers),
34
+ applies_to: { paths: strings(m.applies_to?.paths), commands: strings(m.applies_to?.commands) },
35
+ provenance: {
36
+ sessions: strings(m.provenance?.sessions),
37
+ failures: strings(m.provenance?.failures),
38
+ corrections: strings(m.provenance?.corrections)
39
+ },
40
+ cites: strings(m.cites),
41
+ supersedes: strings(m.supersedes)
42
+ } : null;
43
+ return {
44
+ name: typeof data.name === "string" ? data.name : "",
45
+ description: typeof data.description === "string" ? data.description : "",
46
+ meta,
47
+ rawMeta: m,
48
+ body,
49
+ sections: [...body.matchAll(/^##\s+(.+?)\s*$/gm)].map((x) => x[1]),
50
+ frontmatter: data
51
+ };
52
+ }
53
+ function renderSkill(skill) {
54
+ const fm = {
55
+ name: skill.name,
56
+ description: skill.description,
57
+ ...skill.extra ?? {},
58
+ metadata: { generated_by: GENERATED_BY },
59
+ skill_loop: {
60
+ schema: 1,
61
+ scope: skill.meta.scope,
62
+ status: skill.meta.status,
63
+ pinned: skill.meta.pinned,
64
+ triggers: skill.meta.triggers,
65
+ applies_to: skill.meta.applies_to,
66
+ provenance: skill.meta.provenance,
67
+ cites: skill.meta.cites,
68
+ supersedes: skill.meta.supersedes
69
+ }
70
+ };
71
+ return `---
72
+ ${YAML.stringify(fm, { lineWidth: 0 })}---
73
+
74
+ ${skill.body.trim()}
75
+ `;
76
+ }
77
+ var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".semantic-sidekick-index", ".planning"]);
78
+ function skillsIn(skillsDir, scope) {
79
+ let entries = [];
80
+ try {
81
+ entries = readdirSync(skillsDir);
82
+ } catch {
83
+ return [];
84
+ }
85
+ const out = [];
86
+ for (const e of entries) {
87
+ const path = join(skillsDir, e, "SKILL.md");
88
+ try {
89
+ const skill = parseSkill(readFileSync(path, "utf8"));
90
+ out.push({ path, dir: join(skillsDir, e), scope, learned: isLearned(skill), skill });
91
+ } catch {
92
+ }
93
+ }
94
+ return out;
95
+ }
96
+ var isLearned = (s) => s.meta !== null || s.frontmatter.metadata?.generated_by === GENERATED_BY;
97
+ function directorySkillDirs(root, depth = 6) {
98
+ const out = [];
99
+ const walk = (dir, d) => {
100
+ if (d > depth) return;
101
+ let entries = [];
102
+ try {
103
+ entries = readdirSync(dir);
104
+ } catch {
105
+ return;
106
+ }
107
+ for (const e of entries) {
108
+ if (SKIP_DIRS.has(e) || e.startsWith(".") && e !== ".claude") continue;
109
+ const p = join(dir, e);
110
+ let st;
111
+ try {
112
+ st = statSync(p);
113
+ } catch {
114
+ continue;
115
+ }
116
+ if (!st.isDirectory()) continue;
117
+ if (e === ".claude") {
118
+ if (dir !== root && existsSync(join(p, "skills"))) out.push(join(p, "skills"));
119
+ continue;
120
+ }
121
+ walk(p, d + 1);
122
+ }
123
+ };
124
+ walk(root, 0);
125
+ return out;
126
+ }
127
+ function findSkills(projectRoot, opts = {}) {
128
+ const home = opts.home ?? process.env.HOME ?? homedir();
129
+ return [
130
+ ...skillsIn(join(projectRoot, ".claude", "skills"), "project"),
131
+ ...directorySkillDirs(projectRoot).flatMap((d) => skillsIn(d, "directory")),
132
+ ...opts.global === false ? [] : skillsIn(join(home, ".claude", "skills"), "global")
133
+ ];
134
+ }
135
+ function factIds(projectRoot) {
136
+ let kb = join(projectRoot, ".documentation", "knowledge-base");
137
+ try {
138
+ const cfg = JSON.parse(readFileSync(join(projectRoot, ".claude", "hit-em-with-the-docs.json"), "utf8"));
139
+ if (typeof cfg?.vault?.root === "string") kb = join(projectRoot, cfg.vault.root);
140
+ } catch {
141
+ }
142
+ try {
143
+ return new Set(readdirSync(join(kb, "facts")).filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, "")));
144
+ } catch {
145
+ return /* @__PURE__ */ new Set();
146
+ }
147
+ }
148
+ var NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
149
+ var VAGUE = /\b(helps? with|handles?|deals? with|manages?|various|stuff|things|misc(ellaneous)?|general purpose)\b/i;
150
+ var DESCRIPTION_MAX = 1024;
151
+ var DESCRIPTION_MIN = 40;
152
+ function lintSkill(path, raw, ctx, dirName) {
153
+ const out = [];
154
+ const add = (rule, severity, message) => out.push({ path, rule, severity, message });
155
+ const s = parseSkill(raw);
156
+ if (!raw.startsWith("---")) add("frontmatter", "error", "no YAML frontmatter");
157
+ if (!s.name) add("name", "error", "missing `name`");
158
+ else if (!NAME_RE.test(s.name) || s.name.length > 64) add("name", "error", `name "${s.name}" is not kebab-case (a-z, 0-9, -; at most 64)`);
159
+ else if (dirName && dirName !== s.name) add("name", "error", `name "${s.name}" differs from its folder "${dirName}"`);
160
+ const d = s.description.trim();
161
+ if (!d) add("description", "error", "missing `description` (the only text Claude sees before loading the skill)");
162
+ else {
163
+ if (d.length < DESCRIPTION_MIN) add("description", "error", `description is ${d.length} chars; say what it does and when to use it (>= ${DESCRIPTION_MIN})`);
164
+ if (d.length > DESCRIPTION_MAX) add("description", "error", `description is ${d.length} chars (max ${DESCRIPTION_MAX})`);
165
+ if (!/\bwhen\b|\bbefore\b|\bafter\b|\bif\b/i.test(d)) add("description", "error", 'description does not say when to use it (e.g. "Use when \u2026")');
166
+ if (VAGUE.test(d)) add("description", "warn", `description uses a vague verb ("${d.match(VAGUE)[0]}"): name the concrete action`);
167
+ }
168
+ const m = s.meta;
169
+ if (!m) {
170
+ add("skill_loop", "error", "missing `skill_loop:` block");
171
+ return out;
172
+ }
173
+ if (m.schema !== 1) add("skill_loop", "error", `skill_loop.schema must be 1 (got ${JSON.stringify(m.schema)})`);
174
+ if (!SKILL_SCOPES.includes(m.scope)) add("skill_loop", "error", `skill_loop.scope must be one of ${SKILL_SCOPES.join(", ")}`);
175
+ if (!SKILL_STATUSES.includes(m.status)) add("skill_loop", "error", `skill_loop.status must be one of ${SKILL_STATUSES.join(", ")}`);
176
+ if (m.triggers.length === 0) add("skill_loop", "warn", "no `triggers`: list the literal errors, commands or phrases this skill answers");
177
+ const evidence = m.provenance.sessions.length + m.provenance.failures.length + m.provenance.corrections.length;
178
+ if (evidence === 0) add("provenance", "error", "no provenance: a learned skill needs the session, failure or correction it came from");
179
+ for (const want of SKILL_SECTIONS) {
180
+ if (!s.sections.some((h) => h.toLowerCase() === want.toLowerCase())) add("sections", "error", `missing section "## ${want}"`);
181
+ }
182
+ if (m.cites.length && m.scope !== "global") {
183
+ const facts = ctx.facts ?? factIds(ctx.projectRoot);
184
+ for (const c of m.cites) if (!facts.has(c.replace(/\.md$/, "").split("/").pop())) add("cites", "error", `cites "${c}", which is not a knowledge-base fact here`);
185
+ }
186
+ if (m.scope !== "global") {
187
+ for (const p of m.applies_to.paths) {
188
+ if (!existsSync(join(ctx.projectRoot, p))) add("applies_to", "error", `applies_to path "${p}" does not exist in this repo`);
189
+ }
190
+ if (m.scope === "directory" && m.applies_to.paths.length === 0) add("applies_to", "error", "a directory-scoped skill needs applies_to.paths");
191
+ }
192
+ for (const hit of findSecrets(raw)) add("secrets", "error", `contains a ${hit.kind} at line ${hit.line}`);
193
+ if (s.name) {
194
+ for (const o of ctx.others ?? []) {
195
+ if (o.path === path || o.learned) continue;
196
+ if (basename(o.dir) === s.name || o.skill.name === s.name) {
197
+ add("name_clash", "error", `a ${o.scope} skill not written by the skill loop already uses the name "${s.name}" (${o.path})`);
198
+ }
199
+ }
200
+ }
201
+ return out;
202
+ }
203
+ function lintSkills(projectRoot, opts = {}) {
204
+ const all = findSkills(projectRoot, { home: opts.home });
205
+ const facts = factIds(projectRoot);
206
+ const findings = [];
207
+ let files = 0;
208
+ for (const f of all.filter((x) => x.learned)) {
209
+ files++;
210
+ findings.push(...lintSkill(f.path, readFileSync(f.path, "utf8"), { projectRoot, facts, others: all }, basename(f.dir)));
211
+ }
212
+ for (const p of opts.extra ?? []) {
213
+ files++;
214
+ findings.push(...lintSkill(p, readFileSync(p, "utf8"), { projectRoot, facts, others: all }));
215
+ }
216
+ return { files, findings };
217
+ }
218
+
219
+ export {
220
+ SKILL_SCOPES,
221
+ SKILL_STATUSES,
222
+ SKILL_SECTIONS,
223
+ GENERATED_BY,
224
+ parseSkill,
225
+ renderSkill,
226
+ isLearned,
227
+ findSkills,
228
+ factIds,
229
+ lintSkill,
230
+ lintSkills
231
+ };
232
+ //# sourceMappingURL=chunk-ZSWI3Y4Y.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/skill-schema.ts"],"sourcesContent":["/**\n * The skill loop's skill format (v1.7): an ordinary Claude Code SKILL.md plus a `skill_loop:` frontmatter\n * block that marks it as learned and carries its scope, status and evidence. Claude Code ignores keys it\n * does not know (verified on 2.1.310), so a learned skill loads like any other. `metadata.generated_by`\n * is the string-only marker the agentskills.io spec reserves for clients.\n *\n * ---\n * name: <kebab-name>\n * description: <what it does + when to use it>\n * metadata: { generated_by: semantic-memory }\n * skill_loop:\n * schema: 1\n * scope: project # directory | project | global\n * status: active # draft | active | stale | archived\n * pinned: false\n * triggers: [...] # literal errors / commands / phrases (not `symptoms`: hewtd #46)\n * applies_to: { paths: [...], commands: [...] }\n * provenance: { sessions: [...], failures: [...], corrections: [...] }\n * cites: [<kb-fact-id>]\n * supersedes: []\n * ---\n * ## When to use · ## Steps · ## Pitfalls · ## Verify\n *\n * Counters (uses, last used) never live in the file: see skill-usage.jsonl.\n */\nimport { existsSync, readFileSync, readdirSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, basename } from \"node:path\";\nimport YAML from \"yaml\";\nimport matter from \"./matter.js\";\nimport { findSecrets } from \"./redact.js\";\n\nexport const SKILL_SCOPES = [\"directory\", \"project\", \"global\"] as const;\nexport const SKILL_STATUSES = [\"draft\", \"active\", \"stale\", \"archived\"] as const;\nexport const SKILL_SECTIONS = [\"When to use\", \"Steps\", \"Pitfalls\", \"Verify\"] as const;\nexport const GENERATED_BY = \"semantic-memory\";\n\nexport type SkillScope = (typeof SKILL_SCOPES)[number];\nexport type SkillStatus = (typeof SKILL_STATUSES)[number];\n\nexport interface SkillLoopMeta {\n schema: 1;\n scope: SkillScope;\n status: SkillStatus;\n pinned: boolean;\n triggers: string[];\n applies_to: { paths: string[]; commands: string[] };\n provenance: { sessions: string[]; failures: string[]; corrections: string[] };\n cites: string[];\n supersedes: string[];\n}\n\nexport interface ParsedSkill {\n name: string;\n description: string;\n /** null when the file has no `skill_loop:` block: not a learned skill */\n meta: SkillLoopMeta | null;\n /** the raw `skill_loop` value, for lint messages on a malformed block */\n rawMeta: unknown;\n body: string;\n /** `## ` headings in the body, in order */\n sections: string[];\n frontmatter: Record<string, unknown>;\n}\n\nconst strings = (v: unknown): string[] => (Array.isArray(v) ? v.filter((x) => typeof x === \"string\") : []);\n\nexport function parseSkill(raw: string): ParsedSkill {\n let data: Record<string, any> = {};\n let body = raw;\n try {\n const parsed = matter(raw);\n data = parsed.data ?? {};\n body = parsed.content;\n } catch {\n // malformed frontmatter: Claude Code still loads the skill with no fields; lint reports it\n }\n const m = data.skill_loop;\n const meta: SkillLoopMeta | null =\n m && typeof m === \"object\"\n ? {\n schema: m.schema,\n scope: m.scope,\n status: m.status,\n pinned: m.pinned === true,\n triggers: strings(m.triggers),\n applies_to: { paths: strings(m.applies_to?.paths), commands: strings(m.applies_to?.commands) },\n provenance: {\n sessions: strings(m.provenance?.sessions),\n failures: strings(m.provenance?.failures),\n corrections: strings(m.provenance?.corrections),\n },\n cites: strings(m.cites),\n supersedes: strings(m.supersedes),\n }\n : null;\n return {\n name: typeof data.name === \"string\" ? data.name : \"\",\n description: typeof data.description === \"string\" ? data.description : \"\",\n meta,\n rawMeta: m,\n body,\n sections: [...body.matchAll(/^##\\s+(.+?)\\s*$/gm)].map((x) => x[1]),\n frontmatter: data,\n };\n}\n\n/** A learned skill's SKILL.md text. Field order is fixed so a re-render of an unchanged skill is byte-identical. */\nexport function renderSkill(skill: { name: string; description: string; meta: SkillLoopMeta; body: string; extra?: Record<string, unknown> }): string {\n const fm = {\n name: skill.name,\n description: skill.description,\n ...(skill.extra ?? {}),\n metadata: { generated_by: GENERATED_BY },\n skill_loop: {\n schema: 1,\n scope: skill.meta.scope,\n status: skill.meta.status,\n pinned: skill.meta.pinned,\n triggers: skill.meta.triggers,\n applies_to: skill.meta.applies_to,\n provenance: skill.meta.provenance,\n cites: skill.meta.cites,\n supersedes: skill.meta.supersedes,\n },\n };\n return `---\\n${YAML.stringify(fm, { lineWidth: 0 })}---\\n\\n${skill.body.trim()}\\n`;\n}\n\n// --- discovery ---\n\nexport interface SkillFile {\n path: string;\n /** directory holding SKILL.md; its basename is the skill's name in Claude Code */\n dir: string;\n scope: SkillScope;\n learned: boolean;\n skill: ParsedSkill;\n}\n\nconst SKIP_DIRS = new Set([\"node_modules\", \".git\", \"dist\", \".semantic-sidekick-index\", \".planning\"]);\n\nfunction skillsIn(skillsDir: string, scope: SkillScope): SkillFile[] {\n let entries: string[] = [];\n try {\n entries = readdirSync(skillsDir);\n } catch {\n return [];\n }\n const out: SkillFile[] = [];\n for (const e of entries) {\n const path = join(skillsDir, e, \"SKILL.md\");\n try {\n const skill = parseSkill(readFileSync(path, \"utf8\"));\n out.push({ path, dir: join(skillsDir, e), scope, learned: isLearned(skill), skill });\n } catch {}\n }\n return out;\n}\n\nexport const isLearned = (s: ParsedSkill) =>\n s.meta !== null || (s.frontmatter.metadata as Record<string, unknown> | undefined)?.generated_by === GENERATED_BY;\n\n/** `<subdir>/.claude/skills` folders below the repo root (not the root's own), skipping vendored trees. */\nfunction directorySkillDirs(root: string, depth = 6): string[] {\n const out: string[] = [];\n const walk = (dir: string, d: number) => {\n if (d > depth) return;\n let entries: string[] = [];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const e of entries) {\n if (SKIP_DIRS.has(e) || (e.startsWith(\".\") && e !== \".claude\")) continue;\n const p = join(dir, e);\n let st;\n try {\n st = statSync(p);\n } catch {\n continue;\n }\n if (!st.isDirectory()) continue;\n if (e === \".claude\") {\n if (dir !== root && existsSync(join(p, \"skills\"))) out.push(join(p, \"skills\"));\n continue;\n }\n walk(p, d + 1);\n }\n };\n walk(root, 0);\n return out;\n}\n\n/** Every skill Claude Code would load for this repo: directory, project and global scope. */\nexport function findSkills(projectRoot: string, opts: { home?: string; global?: boolean } = {}): SkillFile[] {\n const home = opts.home ?? process.env.HOME ?? homedir();\n return [\n ...skillsIn(join(projectRoot, \".claude\", \"skills\"), \"project\"),\n ...directorySkillDirs(projectRoot).flatMap((d) => skillsIn(d, \"directory\")),\n ...(opts.global === false ? [] : skillsIn(join(home, \".claude\", \"skills\"), \"global\")),\n ];\n}\n\n/** Knowledge-base fact ids (file basenames) in this repo, for `cites`. */\nexport function factIds(projectRoot: string): Set<string> {\n let kb = join(projectRoot, \".documentation\", \"knowledge-base\");\n try {\n const cfg = JSON.parse(readFileSync(join(projectRoot, \".claude\", \"hit-em-with-the-docs.json\"), \"utf8\"));\n if (typeof cfg?.vault?.root === \"string\") kb = join(projectRoot, cfg.vault.root);\n } catch {}\n try {\n return new Set(readdirSync(join(kb, \"facts\")).filter((f) => f.endsWith(\".md\")).map((f) => f.replace(/\\.md$/, \"\")));\n } catch {\n return new Set();\n }\n}\n\n// --- lint ---\n\nexport interface SkillFinding {\n path: string;\n rule:\n | \"frontmatter\"\n | \"name\"\n | \"description\"\n | \"skill_loop\"\n | \"provenance\"\n | \"sections\"\n | \"cites\"\n | \"applies_to\"\n | \"secrets\"\n | \"name_clash\";\n severity: \"error\" | \"warn\";\n message: string;\n}\n\nconst NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;\n// verbs that say nothing about what the skill does: Claude cannot tell when to load it\nconst VAGUE = /\\b(helps? with|handles?|deals? with|manages?|various|stuff|things|misc(ellaneous)?|general purpose)\\b/i;\nconst DESCRIPTION_MAX = 1024;\nconst DESCRIPTION_MIN = 40;\n\nexport interface SkillLintContext {\n /** repo root the skill belongs to (applies_to paths resolve against it) */\n projectRoot: string;\n facts?: Set<string>;\n /** every other skill Claude Code would load alongside (for name clashes) */\n others?: SkillFile[];\n}\n\n/** Lint one learned skill. `dirName` is the folder name Claude Code will use (defaults to the skill's name). */\nexport function lintSkill(path: string, raw: string, ctx: SkillLintContext, dirName?: string): SkillFinding[] {\n const out: SkillFinding[] = [];\n const add = (rule: SkillFinding[\"rule\"], severity: SkillFinding[\"severity\"], message: string) => out.push({ path, rule, severity, message });\n const s = parseSkill(raw);\n if (!raw.startsWith(\"---\")) add(\"frontmatter\", \"error\", \"no YAML frontmatter\");\n if (!s.name) add(\"name\", \"error\", \"missing `name`\");\n else if (!NAME_RE.test(s.name) || s.name.length > 64) add(\"name\", \"error\", `name \"${s.name}\" is not kebab-case (a-z, 0-9, -; at most 64)`);\n else if (dirName && dirName !== s.name) add(\"name\", \"error\", `name \"${s.name}\" differs from its folder \"${dirName}\"`);\n\n const d = s.description.trim();\n if (!d) add(\"description\", \"error\", \"missing `description` (the only text Claude sees before loading the skill)\");\n else {\n if (d.length < DESCRIPTION_MIN) add(\"description\", \"error\", `description is ${d.length} chars; say what it does and when to use it (>= ${DESCRIPTION_MIN})`);\n if (d.length > DESCRIPTION_MAX) add(\"description\", \"error\", `description is ${d.length} chars (max ${DESCRIPTION_MAX})`);\n if (!/\\bwhen\\b|\\bbefore\\b|\\bafter\\b|\\bif\\b/i.test(d)) add(\"description\", \"error\", \"description does not say when to use it (e.g. \\\"Use when …\\\")\");\n if (VAGUE.test(d)) add(\"description\", \"warn\", `description uses a vague verb (\"${d.match(VAGUE)![0]}\"): name the concrete action`);\n }\n\n const m = s.meta;\n if (!m) {\n add(\"skill_loop\", \"error\", \"missing `skill_loop:` block\");\n return out;\n }\n if (m.schema !== 1) add(\"skill_loop\", \"error\", `skill_loop.schema must be 1 (got ${JSON.stringify(m.schema)})`);\n if (!SKILL_SCOPES.includes(m.scope)) add(\"skill_loop\", \"error\", `skill_loop.scope must be one of ${SKILL_SCOPES.join(\", \")}`);\n if (!SKILL_STATUSES.includes(m.status)) add(\"skill_loop\", \"error\", `skill_loop.status must be one of ${SKILL_STATUSES.join(\", \")}`);\n if (m.triggers.length === 0) add(\"skill_loop\", \"warn\", \"no `triggers`: list the literal errors, commands or phrases this skill answers\");\n const evidence = m.provenance.sessions.length + m.provenance.failures.length + m.provenance.corrections.length;\n if (evidence === 0) add(\"provenance\", \"error\", \"no provenance: a learned skill needs the session, failure or correction it came from\");\n\n for (const want of SKILL_SECTIONS) {\n if (!s.sections.some((h) => h.toLowerCase() === want.toLowerCase())) add(\"sections\", \"error\", `missing section \"## ${want}\"`);\n }\n\n // a global skill serves every repo: its facts are not this repo's, so cites resolve only for directory / project\n if (m.cites.length && m.scope !== \"global\") {\n const facts = ctx.facts ?? factIds(ctx.projectRoot);\n for (const c of m.cites) if (!facts.has(c.replace(/\\.md$/, \"\").split(\"/\").pop()!)) add(\"cites\", \"error\", `cites \"${c}\", which is not a knowledge-base fact here`);\n }\n\n if (m.scope !== \"global\") {\n for (const p of m.applies_to.paths) {\n if (!existsSync(join(ctx.projectRoot, p))) add(\"applies_to\", \"error\", `applies_to path \"${p}\" does not exist in this repo`);\n }\n if (m.scope === \"directory\" && m.applies_to.paths.length === 0) add(\"applies_to\", \"error\", \"a directory-scoped skill needs applies_to.paths\");\n }\n\n for (const hit of findSecrets(raw)) add(\"secrets\", \"error\", `contains a ${hit.kind} at line ${hit.line}`);\n\n if (s.name) {\n for (const o of ctx.others ?? []) {\n if (o.path === path || o.learned) continue;\n if (basename(o.dir) === s.name || o.skill.name === s.name) {\n add(\"name_clash\", \"error\", `a ${o.scope} skill not written by the skill loop already uses the name \"${s.name}\" (${o.path})`);\n }\n }\n }\n return out;\n}\n\n/** Lint every learned skill this repo would load (and its drafts, when given). */\nexport function lintSkills(projectRoot: string, opts: { home?: string; extra?: string[] } = {}): { files: number; findings: SkillFinding[] } {\n const all = findSkills(projectRoot, { home: opts.home });\n const facts = factIds(projectRoot);\n const findings: SkillFinding[] = [];\n let files = 0;\n for (const f of all.filter((x) => x.learned)) {\n files++;\n findings.push(...lintSkill(f.path, readFileSync(f.path, \"utf8\"), { projectRoot, facts, others: all }, basename(f.dir)));\n }\n for (const p of opts.extra ?? []) {\n files++;\n findings.push(...lintSkill(p, readFileSync(p, \"utf8\"), { projectRoot, facts, others: all }));\n }\n return { files, findings };\n}\n"],"mappings":"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","names":[]}