mnotes-cli 1.9.1 → 1.9.2

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.
@@ -79,11 +79,11 @@ const DEFAULT_OPTS = {
79
79
  const skills = (0, skills_1.generateSkillTemplates)(DEFAULT_OPTS);
80
80
  (0, vitest_1.expect)(skills).toHaveLength(2);
81
81
  });
82
- (0, vitest_1.it)("generates store and recall skills", () => {
82
+ (0, vitest_1.it)("generates store and recall skills as SKILL.md inside their own folder (AC-1)", () => {
83
83
  const skills = (0, skills_1.generateSkillTemplates)(DEFAULT_OPTS);
84
- const filenames = skills.map((s) => s.filename);
85
- (0, vitest_1.expect)(filenames).toContain("mnotes-store.md");
86
- (0, vitest_1.expect)(filenames).toContain("mnotes-recall.md");
84
+ const paths = skills.map((s) => s.path);
85
+ (0, vitest_1.expect)(paths).toContain("mnotes-store/SKILL.md");
86
+ (0, vitest_1.expect)(paths).toContain("mnotes-recall/SKILL.md");
87
87
  });
88
88
  (0, vitest_1.it)("includes m-notes generated header (AC-8)", () => {
89
89
  const skills = (0, skills_1.generateSkillTemplates)(DEFAULT_OPTS);
@@ -97,12 +97,13 @@ const DEFAULT_OPTS = {
97
97
  (0, vitest_1.expect)(skill.content).toContain("ws-test-123");
98
98
  }
99
99
  });
100
- (0, vitest_1.it)("includes frontmatter with name and trigger", () => {
100
+ (0, vitest_1.it)("includes frontmatter with name and description and omits non-standard fields", () => {
101
101
  const skills = (0, skills_1.generateSkillTemplates)(DEFAULT_OPTS);
102
102
  for (const skill of skills) {
103
103
  (0, vitest_1.expect)(skill.content).toContain("---");
104
104
  (0, vitest_1.expect)(skill.content).toContain("name:");
105
- (0, vitest_1.expect)(skill.content).toContain("trigger:");
105
+ (0, vitest_1.expect)(skill.content).toContain("description:");
106
+ (0, vitest_1.expect)(skill.content).not.toMatch(/^trigger:/m);
106
107
  }
107
108
  });
108
109
  });
@@ -211,38 +212,38 @@ const DEFAULT_OPTS = {
211
212
  (0, vitest_1.afterEach)(() => {
212
213
  cleanTmpDir(tmpDir);
213
214
  });
214
- (0, vitest_1.it)("creates skill files in .claude/skills/ (AC-4)", () => {
215
+ (0, vitest_1.it)("creates skill files as .claude/skills/<name>/SKILL.md (AC-4)", () => {
215
216
  const results = (0, wizard_1.scaffoldItems)(tmpDir, ["skills"], DEFAULT_OPTS);
216
217
  (0, vitest_1.expect)(results).toHaveLength(1);
217
218
  (0, vitest_1.expect)(results[0].item).toBe("skills");
218
219
  (0, vitest_1.expect)(results[0].filesWritten.length).toBeGreaterThan(0);
219
220
  const skillsDir = path.join(tmpDir, ".claude", "skills");
220
- (0, vitest_1.expect)(fs.existsSync(path.join(skillsDir, "mnotes-store.md"))).toBe(true);
221
- (0, vitest_1.expect)(fs.existsSync(path.join(skillsDir, "mnotes-recall.md"))).toBe(true);
221
+ (0, vitest_1.expect)(fs.existsSync(path.join(skillsDir, "mnotes-store", "SKILL.md"))).toBe(true);
222
+ (0, vitest_1.expect)(fs.existsSync(path.join(skillsDir, "mnotes-recall", "SKILL.md"))).toBe(true);
222
223
  });
223
224
  (0, vitest_1.it)("generated skill files include header (AC-8)", () => {
224
225
  (0, wizard_1.scaffoldItems)(tmpDir, ["skills"], DEFAULT_OPTS);
225
- const storeContent = fs.readFileSync(path.join(tmpDir, ".claude", "skills", "mnotes-store.md"), "utf-8");
226
+ const storeContent = fs.readFileSync(path.join(tmpDir, ".claude", "skills", "mnotes-store", "SKILL.md"), "utf-8");
226
227
  (0, vitest_1.expect)(storeContent).toContain("Generated by m-notes CLI");
227
228
  });
228
229
  (0, vitest_1.it)("preserves user-created skill files (AC-5)", () => {
229
- const skillsDir = path.join(tmpDir, ".claude", "skills");
230
- fs.mkdirSync(skillsDir, { recursive: true });
231
- // Write a user-created file with the same name
232
- fs.writeFileSync(path.join(skillsDir, "mnotes-store.md"), "# My custom store skill\nCustom content here", "utf-8");
233
- const results = (0, wizard_1.scaffoldItems)(tmpDir, ["skills"], DEFAULT_OPTS);
230
+ const storeDir = path.join(tmpDir, ".claude", "skills", "mnotes-store");
231
+ fs.mkdirSync(storeDir, { recursive: true });
232
+ // Write a user-created file at the same path
233
+ fs.writeFileSync(path.join(storeDir, "SKILL.md"), "# My custom store skill\nCustom content here", "utf-8");
234
+ (0, wizard_1.scaffoldItems)(tmpDir, ["skills"], DEFAULT_OPTS);
234
235
  // The user file should be preserved
235
- const content = fs.readFileSync(path.join(skillsDir, "mnotes-store.md"), "utf-8");
236
+ const content = fs.readFileSync(path.join(storeDir, "SKILL.md"), "utf-8");
236
237
  (0, vitest_1.expect)(content).toContain("My custom store skill");
237
238
  (0, vitest_1.expect)(content).not.toContain("Generated by m-notes CLI");
238
239
  // The recall skill should still be written (new file)
239
- (0, vitest_1.expect)(fs.existsSync(path.join(skillsDir, "mnotes-recall.md"))).toBe(true);
240
+ (0, vitest_1.expect)(fs.existsSync(path.join(tmpDir, ".claude", "skills", "mnotes-recall", "SKILL.md"))).toBe(true);
240
241
  });
241
242
  (0, vitest_1.it)("overwrites m-notes generated skill files on re-run (AC-5)", () => {
242
243
  (0, wizard_1.scaffoldItems)(tmpDir, ["skills"], DEFAULT_OPTS);
243
244
  // Re-run with different workspaceId
244
245
  (0, wizard_1.scaffoldItems)(tmpDir, ["skills"], { url: "http://new-url.com", workspaceId: "ws-new" });
245
- const content = fs.readFileSync(path.join(tmpDir, ".claude", "skills", "mnotes-store.md"), "utf-8");
246
+ const content = fs.readFileSync(path.join(tmpDir, ".claude", "skills", "mnotes-store", "SKILL.md"), "utf-8");
246
247
  (0, vitest_1.expect)(content).toContain("ws-new");
247
248
  });
248
249
  });
@@ -298,8 +299,8 @@ const DEFAULT_OPTS = {
298
299
  (0, vitest_1.expect)(items).toContain("agents");
299
300
  // Verify files exist
300
301
  (0, vitest_1.expect)(fs.existsSync(path.join(tmpDir, ".claude", "settings.json"))).toBe(true);
301
- (0, vitest_1.expect)(fs.existsSync(path.join(tmpDir, ".claude", "skills", "mnotes-store.md"))).toBe(true);
302
- (0, vitest_1.expect)(fs.existsSync(path.join(tmpDir, ".claude", "skills", "mnotes-recall.md"))).toBe(true);
302
+ (0, vitest_1.expect)(fs.existsSync(path.join(tmpDir, ".claude", "skills", "mnotes-store", "SKILL.md"))).toBe(true);
303
+ (0, vitest_1.expect)(fs.existsSync(path.join(tmpDir, ".claude", "skills", "mnotes-recall", "SKILL.md"))).toBe(true);
303
304
  (0, vitest_1.expect)(fs.existsSync(path.join(tmpDir, ".claude", "agents", "knowledge-manager.md"))).toBe(true);
304
305
  });
305
306
  (0, vitest_1.it)("scaffolds partial selection", () => {
@@ -386,8 +387,8 @@ const DEFAULT_OPTS = {
386
387
  (0, vitest_1.expect)(fs.existsSync(path.join(tmpDir, "CLAUDE.md"))).toBe(true);
387
388
  // All extras should exist
388
389
  (0, vitest_1.expect)(fs.existsSync(path.join(tmpDir, ".claude", "settings.json"))).toBe(true);
389
- (0, vitest_1.expect)(fs.existsSync(path.join(tmpDir, ".claude", "skills", "mnotes-store.md"))).toBe(true);
390
- (0, vitest_1.expect)(fs.existsSync(path.join(tmpDir, ".claude", "skills", "mnotes-recall.md"))).toBe(true);
390
+ (0, vitest_1.expect)(fs.existsSync(path.join(tmpDir, ".claude", "skills", "mnotes-store", "SKILL.md"))).toBe(true);
391
+ (0, vitest_1.expect)(fs.existsSync(path.join(tmpDir, ".claude", "skills", "mnotes-recall", "SKILL.md"))).toBe(true);
391
392
  (0, vitest_1.expect)(fs.existsSync(path.join(tmpDir, ".claude", "agents", "knowledge-manager.md"))).toBe(true);
392
393
  // Output should mention extras
393
394
  (0, vitest_1.expect)(output).toContain("Extras installed:");
@@ -465,7 +466,7 @@ const DEFAULT_OPTS = {
465
466
  }
466
467
  // All extras should exist
467
468
  (0, vitest_1.expect)(fs.existsSync(path.join(tmpDir, ".claude", "settings.json"))).toBe(true);
468
- (0, vitest_1.expect)(fs.existsSync(path.join(tmpDir, ".claude", "skills", "mnotes-store.md"))).toBe(true);
469
+ (0, vitest_1.expect)(fs.existsSync(path.join(tmpDir, ".claude", "skills", "mnotes-store", "SKILL.md"))).toBe(true);
469
470
  (0, vitest_1.expect)(fs.existsSync(path.join(tmpDir, ".claude", "agents", "knowledge-manager.md"))).toBe(true);
470
471
  });
471
472
  });
@@ -170,8 +170,9 @@ function scaffoldSkills(dir, opts) {
170
170
  const skills = (0, index_1.generateSkillTemplates)(opts);
171
171
  const filesWritten = [];
172
172
  for (const skill of skills) {
173
- const filePath = path.join(skillsDir, skill.filename);
173
+ const filePath = path.join(skillsDir, skill.path);
174
174
  if (shouldWriteGeneratedFile(filePath)) {
175
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
175
176
  fs.writeFileSync(filePath, skill.content, "utf-8");
176
177
  filesWritten.push(filePath);
177
178
  }
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Templates for Claude Code skills.
3
- * Written to `.claude/skills/` as markdown files.
3
+ * Written to `.claude/skills/<skill-name>/SKILL.md` Claude Code's skill loader
4
+ * only discovers skills that live in their own folder as `SKILL.md`.
4
5
  * Generated by m-notes CLI — do not edit manually.
5
6
  */
6
7
  export interface SkillTemplateOpts {
@@ -8,11 +9,14 @@ export interface SkillTemplateOpts {
8
9
  workspaceId: string;
9
10
  }
10
11
  export interface SkillFile {
11
- filename: string;
12
+ /** Relative path within `.claude/skills/`, e.g. `mnotes-store/SKILL.md`. */
13
+ path: string;
12
14
  content: string;
13
15
  }
14
16
  /**
15
17
  * Generates skill files for m-notes integration.
16
- * Returns an array of { filename, content } pairs.
18
+ * Returns an array of { path, content } pairs. `path` is relative to
19
+ * `.claude/skills/` and always ends in `SKILL.md` so Claude Code's loader
20
+ * discovers the skill.
17
21
  */
18
22
  export declare function generateSkillTemplates(opts: SkillTemplateOpts): SkillFile[];
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  /**
3
3
  * Templates for Claude Code skills.
4
- * Written to `.claude/skills/` as markdown files.
4
+ * Written to `.claude/skills/<skill-name>/SKILL.md` Claude Code's skill loader
5
+ * only discovers skills that live in their own folder as `SKILL.md`.
5
6
  * Generated by m-notes CLI — do not edit manually.
6
7
  */
7
8
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -9,17 +10,18 @@ exports.generateSkillTemplates = generateSkillTemplates;
9
10
  const GENERATED_HEADER = "<!-- Generated by m-notes CLI. Do not edit manually. -->";
10
11
  /**
11
12
  * Generates skill files for m-notes integration.
12
- * Returns an array of { filename, content } pairs.
13
+ * Returns an array of { path, content } pairs. `path` is relative to
14
+ * `.claude/skills/` and always ends in `SKILL.md` so Claude Code's loader
15
+ * discovers the skill.
13
16
  */
14
17
  function generateSkillTemplates(opts) {
15
18
  return [
16
19
  {
17
- filename: "mnotes-store.md",
20
+ path: "mnotes-store/SKILL.md",
18
21
  content: `${GENERATED_HEADER}
19
22
  ---
20
23
  name: mnotes-store
21
- description: Store a knowledge entry in m-notes
22
- trigger: When the user says /store or asks to save/store knowledge, decisions, patterns, or context
24
+ description: Store a knowledge entry in m-notes. Use when the user says /store or asks to save/store knowledge, decisions, patterns, or context.
23
25
  ---
24
26
 
25
27
  # Store Knowledge in m-notes
@@ -44,12 +46,11 @@ Use the \`knowledge_store\` MCP tool to save knowledge.
44
46
  `,
45
47
  },
46
48
  {
47
- filename: "mnotes-recall.md",
49
+ path: "mnotes-recall/SKILL.md",
48
50
  content: `${GENERATED_HEADER}
49
51
  ---
50
52
  name: mnotes-recall
51
- description: Recall knowledge from m-notes using semantic search
52
- trigger: When the user says /recall or asks to find/recall/search knowledge, or needs context about a topic
53
+ description: Recall knowledge from m-notes using semantic search. Use when the user says /recall or asks to find/recall/search knowledge, or needs context about a topic.
53
54
  ---
54
55
 
55
56
  # Recall Knowledge from m-notes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mnotes-cli",
3
- "version": "1.9.1",
3
+ "version": "1.9.2",
4
4
  "description": "CLI for m-notes AI knowledge base — manage notes, search, and CRUD from the terminal",
5
5
  "bin": {
6
6
  "mnotes": "./dist/index.js"