@gopherine/obsidian-mcp 0.1.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 (223) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/LICENSE +661 -0
  3. package/README.md +235 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +805 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/brainstorm.d.ts +9 -0
  8. package/dist/commands/brainstorm.js +37 -0
  9. package/dist/commands/brainstorm.js.map +1 -0
  10. package/dist/commands/brainstorm.test.d.ts +1 -0
  11. package/dist/commands/brainstorm.test.js +110 -0
  12. package/dist/commands/brainstorm.test.js.map +1 -0
  13. package/dist/commands/context.d.ts +18 -0
  14. package/dist/commands/context.js +88 -0
  15. package/dist/commands/context.js.map +1 -0
  16. package/dist/commands/context.test.d.ts +1 -0
  17. package/dist/commands/context.test.js +230 -0
  18. package/dist/commands/context.test.js.map +1 -0
  19. package/dist/commands/decide.d.ts +12 -0
  20. package/dist/commands/decide.js +40 -0
  21. package/dist/commands/decide.js.map +1 -0
  22. package/dist/commands/decide.test.d.ts +1 -0
  23. package/dist/commands/decide.test.js +109 -0
  24. package/dist/commands/decide.test.js.map +1 -0
  25. package/dist/commands/graph.d.ts +15 -0
  26. package/dist/commands/graph.js +89 -0
  27. package/dist/commands/graph.js.map +1 -0
  28. package/dist/commands/graph.test.d.ts +1 -0
  29. package/dist/commands/graph.test.js +215 -0
  30. package/dist/commands/graph.test.js.map +1 -0
  31. package/dist/commands/init.d.ts +11 -0
  32. package/dist/commands/init.js +256 -0
  33. package/dist/commands/init.js.map +1 -0
  34. package/dist/commands/learn.d.ts +25 -0
  35. package/dist/commands/learn.js +92 -0
  36. package/dist/commands/learn.js.map +1 -0
  37. package/dist/commands/learn.test.d.ts +1 -0
  38. package/dist/commands/learn.test.js +241 -0
  39. package/dist/commands/learn.test.js.map +1 -0
  40. package/dist/commands/prune.d.ts +58 -0
  41. package/dist/commands/prune.js +246 -0
  42. package/dist/commands/prune.js.map +1 -0
  43. package/dist/commands/read.d.ts +8 -0
  44. package/dist/commands/read.js +7 -0
  45. package/dist/commands/read.js.map +1 -0
  46. package/dist/commands/read.test.d.ts +1 -0
  47. package/dist/commands/read.test.js +48 -0
  48. package/dist/commands/read.test.js.map +1 -0
  49. package/dist/commands/resume.d.ts +20 -0
  50. package/dist/commands/resume.js +138 -0
  51. package/dist/commands/resume.js.map +1 -0
  52. package/dist/commands/resume.test.d.ts +1 -0
  53. package/dist/commands/resume.test.js +243 -0
  54. package/dist/commands/resume.test.js.map +1 -0
  55. package/dist/commands/search.d.ts +8 -0
  56. package/dist/commands/search.js +25 -0
  57. package/dist/commands/search.js.map +1 -0
  58. package/dist/commands/search.test.d.ts +1 -0
  59. package/dist/commands/search.test.js +61 -0
  60. package/dist/commands/search.test.js.map +1 -0
  61. package/dist/commands/session.d.ts +22 -0
  62. package/dist/commands/session.js +79 -0
  63. package/dist/commands/session.js.map +1 -0
  64. package/dist/commands/session.test.d.ts +1 -0
  65. package/dist/commands/session.test.js +185 -0
  66. package/dist/commands/session.test.js.map +1 -0
  67. package/dist/commands/skill/index.d.ts +30 -0
  68. package/dist/commands/skill/index.js +61 -0
  69. package/dist/commands/skill/index.js.map +1 -0
  70. package/dist/commands/skill/index.test.d.ts +1 -0
  71. package/dist/commands/skill/index.test.js +137 -0
  72. package/dist/commands/skill/index.test.js.map +1 -0
  73. package/dist/commands/skill/install.d.ts +14 -0
  74. package/dist/commands/skill/install.js +107 -0
  75. package/dist/commands/skill/install.js.map +1 -0
  76. package/dist/commands/skill/install.test.d.ts +1 -0
  77. package/dist/commands/skill/install.test.js +225 -0
  78. package/dist/commands/skill/install.test.js.map +1 -0
  79. package/dist/commands/skill/list.d.ts +6 -0
  80. package/dist/commands/skill/list.js +5 -0
  81. package/dist/commands/skill/list.js.map +1 -0
  82. package/dist/commands/skill/list.test.d.ts +1 -0
  83. package/dist/commands/skill/list.test.js +101 -0
  84. package/dist/commands/skill/list.test.js.map +1 -0
  85. package/dist/commands/skill/schema.d.ts +27 -0
  86. package/dist/commands/skill/schema.js +55 -0
  87. package/dist/commands/skill/schema.js.map +1 -0
  88. package/dist/commands/skill/schema.test.d.ts +1 -0
  89. package/dist/commands/skill/schema.test.js +142 -0
  90. package/dist/commands/skill/schema.test.js.map +1 -0
  91. package/dist/commands/skill/validate.d.ts +10 -0
  92. package/dist/commands/skill/validate.js +39 -0
  93. package/dist/commands/skill/validate.js.map +1 -0
  94. package/dist/commands/skill/validate.test.d.ts +1 -0
  95. package/dist/commands/skill/validate.test.js +171 -0
  96. package/dist/commands/skill/validate.test.js.map +1 -0
  97. package/dist/commands/task.d.ts +34 -0
  98. package/dist/commands/task.js +160 -0
  99. package/dist/commands/task.js.map +1 -0
  100. package/dist/commands/task.test.d.ts +1 -0
  101. package/dist/commands/task.test.js +395 -0
  102. package/dist/commands/task.test.js.map +1 -0
  103. package/dist/commands/todo.d.ts +15 -0
  104. package/dist/commands/todo.js +99 -0
  105. package/dist/commands/todo.js.map +1 -0
  106. package/dist/commands/todo.test.d.ts +1 -0
  107. package/dist/commands/todo.test.js +324 -0
  108. package/dist/commands/todo.test.js.map +1 -0
  109. package/dist/commands/write.d.ts +12 -0
  110. package/dist/commands/write.js +40 -0
  111. package/dist/commands/write.js.map +1 -0
  112. package/dist/commands/write.test.d.ts +1 -0
  113. package/dist/commands/write.test.js +79 -0
  114. package/dist/commands/write.test.js.map +1 -0
  115. package/dist/config.d.ts +15 -0
  116. package/dist/config.js +48 -0
  117. package/dist/config.js.map +1 -0
  118. package/dist/config.test.d.ts +1 -0
  119. package/dist/config.test.js +147 -0
  120. package/dist/config.test.js.map +1 -0
  121. package/dist/core/executor.d.ts +11 -0
  122. package/dist/core/executor.js +42 -0
  123. package/dist/core/executor.js.map +1 -0
  124. package/dist/core/executor.test.d.ts +1 -0
  125. package/dist/core/executor.test.js +206 -0
  126. package/dist/core/executor.test.js.map +1 -0
  127. package/dist/core/middleware/error-handler.d.ts +2 -0
  128. package/dist/core/middleware/error-handler.js +29 -0
  129. package/dist/core/middleware/error-handler.js.map +1 -0
  130. package/dist/core/middleware/index.d.ts +2 -0
  131. package/dist/core/middleware/index.js +3 -0
  132. package/dist/core/middleware/index.js.map +1 -0
  133. package/dist/core/middleware/logging.d.ts +2 -0
  134. package/dist/core/middleware/logging.js +18 -0
  135. package/dist/core/middleware/logging.js.map +1 -0
  136. package/dist/core/registry.d.ts +12 -0
  137. package/dist/core/registry.js +407 -0
  138. package/dist/core/registry.js.map +1 -0
  139. package/dist/core/registry.test.d.ts +1 -0
  140. package/dist/core/registry.test.js +162 -0
  141. package/dist/core/registry.test.js.map +1 -0
  142. package/dist/core/types.d.ts +38 -0
  143. package/dist/core/types.js +2 -0
  144. package/dist/core/types.js.map +1 -0
  145. package/dist/integration/coordination.test.d.ts +1 -0
  146. package/dist/integration/coordination.test.js +286 -0
  147. package/dist/integration/coordination.test.js.map +1 -0
  148. package/dist/integration/project-artifacts.test.d.ts +1 -0
  149. package/dist/integration/project-artifacts.test.js +275 -0
  150. package/dist/integration/project-artifacts.test.js.map +1 -0
  151. package/dist/integration/registry-graph.test.d.ts +1 -0
  152. package/dist/integration/registry-graph.test.js +157 -0
  153. package/dist/integration/registry-graph.test.js.map +1 -0
  154. package/dist/integration/vault-lifecycle.test.d.ts +1 -0
  155. package/dist/integration/vault-lifecycle.test.js +183 -0
  156. package/dist/integration/vault-lifecycle.test.js.map +1 -0
  157. package/dist/lib/auto-number.d.ts +10 -0
  158. package/dist/lib/auto-number.js +33 -0
  159. package/dist/lib/auto-number.js.map +1 -0
  160. package/dist/lib/auto-number.test.d.ts +1 -0
  161. package/dist/lib/auto-number.test.js +88 -0
  162. package/dist/lib/auto-number.test.js.map +1 -0
  163. package/dist/lib/escape-regex.d.ts +4 -0
  164. package/dist/lib/escape-regex.js +7 -0
  165. package/dist/lib/escape-regex.js.map +1 -0
  166. package/dist/lib/escape-regex.test.d.ts +1 -0
  167. package/dist/lib/escape-regex.test.js +27 -0
  168. package/dist/lib/escape-regex.test.js.map +1 -0
  169. package/dist/lib/frontmatter.d.ts +34 -0
  170. package/dist/lib/frontmatter.js +74 -0
  171. package/dist/lib/frontmatter.js.map +1 -0
  172. package/dist/lib/frontmatter.test.d.ts +1 -0
  173. package/dist/lib/frontmatter.test.js +192 -0
  174. package/dist/lib/frontmatter.test.js.map +1 -0
  175. package/dist/lib/project-detector.d.ts +12 -0
  176. package/dist/lib/project-detector.js +123 -0
  177. package/dist/lib/project-detector.js.map +1 -0
  178. package/dist/lib/project-detector.test.d.ts +1 -0
  179. package/dist/lib/project-detector.test.js +117 -0
  180. package/dist/lib/project-detector.test.js.map +1 -0
  181. package/dist/lib/safe-external-path.d.ts +10 -0
  182. package/dist/lib/safe-external-path.js +46 -0
  183. package/dist/lib/safe-external-path.js.map +1 -0
  184. package/dist/lib/safe-external-path.test.d.ts +1 -0
  185. package/dist/lib/safe-external-path.test.js +99 -0
  186. package/dist/lib/safe-external-path.test.js.map +1 -0
  187. package/dist/lib/search-engine.d.ts +19 -0
  188. package/dist/lib/search-engine.js +157 -0
  189. package/dist/lib/search-engine.js.map +1 -0
  190. package/dist/lib/search-engine.test.d.ts +1 -0
  191. package/dist/lib/search-engine.test.js +120 -0
  192. package/dist/lib/search-engine.test.js.map +1 -0
  193. package/dist/lib/session-registry.d.ts +59 -0
  194. package/dist/lib/session-registry.js +230 -0
  195. package/dist/lib/session-registry.js.map +1 -0
  196. package/dist/lib/session-registry.test.d.ts +1 -0
  197. package/dist/lib/session-registry.test.js +199 -0
  198. package/dist/lib/session-registry.test.js.map +1 -0
  199. package/dist/lib/skill-registry.d.ts +13 -0
  200. package/dist/lib/skill-registry.js +76 -0
  201. package/dist/lib/skill-registry.js.map +1 -0
  202. package/dist/lib/token-estimator.d.ts +19 -0
  203. package/dist/lib/token-estimator.js +58 -0
  204. package/dist/lib/token-estimator.js.map +1 -0
  205. package/dist/lib/token-estimator.test.d.ts +1 -0
  206. package/dist/lib/token-estimator.test.js +65 -0
  207. package/dist/lib/token-estimator.test.js.map +1 -0
  208. package/dist/lib/vault-fs.d.ts +39 -0
  209. package/dist/lib/vault-fs.js +183 -0
  210. package/dist/lib/vault-fs.js.map +1 -0
  211. package/dist/lib/vault-fs.test.d.ts +1 -0
  212. package/dist/lib/vault-fs.test.js +210 -0
  213. package/dist/lib/vault-fs.test.js.map +1 -0
  214. package/dist/mcp-server.d.ts +2 -0
  215. package/dist/mcp-server.js +385 -0
  216. package/dist/mcp-server.js.map +1 -0
  217. package/dist/test-helpers.d.ts +20 -0
  218. package/dist/test-helpers.js +46 -0
  219. package/dist/test-helpers.js.map +1 -0
  220. package/dist/test-helpers.test.d.ts +1 -0
  221. package/dist/test-helpers.test.js +90 -0
  222. package/dist/test-helpers.test.js.map +1 -0
  223. package/package.json +72 -0
@@ -0,0 +1,34 @@
1
+ export interface Frontmatter {
2
+ type?: string;
3
+ project?: string;
4
+ status?: string;
5
+ created?: string;
6
+ updated?: string;
7
+ tags?: string[];
8
+ related?: string[];
9
+ [key: string]: unknown;
10
+ }
11
+ /**
12
+ * Parse markdown content with YAML frontmatter.
13
+ */
14
+ export declare function parseFrontmatter(content: string): {
15
+ data: Frontmatter;
16
+ content: string;
17
+ };
18
+ /**
19
+ * Serialize frontmatter + content back to markdown.
20
+ */
21
+ export declare function serializeFrontmatter(data: Frontmatter, content: string): string;
22
+ /**
23
+ * Create frontmatter with defaults for a new note.
24
+ */
25
+ export declare function createFrontmatter(overrides: Partial<Frontmatter>): Frontmatter;
26
+ /**
27
+ * Merge new frontmatter values into existing frontmatter.
28
+ * Updates the `updated` field automatically.
29
+ */
30
+ export declare function mergeFrontmatter(existing: Frontmatter, updates: Partial<Frontmatter>): Frontmatter;
31
+ /**
32
+ * Validate frontmatter fields.
33
+ */
34
+ export declare function validateFrontmatter(data: Frontmatter): string[];
@@ -0,0 +1,74 @@
1
+ import matter from "gray-matter";
2
+ const VALID_TYPES = [
3
+ "context", "adr", "brainstorm", "decision", "todo",
4
+ "incident", "pattern", "evaluation", "index",
5
+ "task", "learning", "session",
6
+ ];
7
+ const VALID_STATUSES = [
8
+ "active", "resolved", "deprecated", "draft",
9
+ "backlog", "in-progress", "blocked", "done", "cancelled", "completed",
10
+ ];
11
+ /**
12
+ * Parse markdown content with YAML frontmatter.
13
+ */
14
+ export function parseFrontmatter(content) {
15
+ try {
16
+ const parsed = matter(content);
17
+ return { data: parsed.data, content: parsed.content };
18
+ }
19
+ catch {
20
+ // Malformed YAML — return empty frontmatter and raw content
21
+ return { data: {}, content };
22
+ }
23
+ }
24
+ /**
25
+ * Serialize frontmatter + content back to markdown.
26
+ */
27
+ export function serializeFrontmatter(data, content) {
28
+ return matter.stringify(content, data);
29
+ }
30
+ /**
31
+ * Create frontmatter with defaults for a new note.
32
+ */
33
+ export function createFrontmatter(overrides) {
34
+ const today = new Date().toISOString().slice(0, 10);
35
+ return {
36
+ type: overrides.type ?? "index",
37
+ status: overrides.status ?? "active",
38
+ created: overrides.created ?? today,
39
+ updated: today,
40
+ ...overrides,
41
+ };
42
+ }
43
+ /**
44
+ * Merge new frontmatter values into existing frontmatter.
45
+ * Updates the `updated` field automatically.
46
+ */
47
+ export function mergeFrontmatter(existing, updates) {
48
+ const today = new Date().toISOString().slice(0, 10);
49
+ return {
50
+ ...existing,
51
+ ...updates,
52
+ updated: today,
53
+ };
54
+ }
55
+ /**
56
+ * Validate frontmatter fields.
57
+ */
58
+ export function validateFrontmatter(data) {
59
+ const errors = [];
60
+ if (data.type && !VALID_TYPES.includes(data.type)) {
61
+ errors.push(`Invalid type "${data.type}". Must be one of: ${VALID_TYPES.join(", ")}`);
62
+ }
63
+ if (data.status && !VALID_STATUSES.includes(data.status)) {
64
+ errors.push(`Invalid status "${data.status}". Must be one of: ${VALID_STATUSES.join(", ")}`);
65
+ }
66
+ if (data.tags && !Array.isArray(data.tags)) {
67
+ errors.push(`Tags must be an array`);
68
+ }
69
+ if (data.related && !Array.isArray(data.related)) {
70
+ errors.push(`Related must be an array`);
71
+ }
72
+ return errors;
73
+ }
74
+ //# sourceMappingURL=frontmatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/lib/frontmatter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,192 @@
1
+ import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
2
+ import { parseFrontmatter, serializeFrontmatter, createFrontmatter, mergeFrontmatter, validateFrontmatter } from "./frontmatter.js";
3
+ describe("parseFrontmatter", () => {
4
+ const cases = [
5
+ {
6
+ name: "with frontmatter",
7
+ input: "---\ntitle: Test\nstatus: active\n---\n\nContent here",
8
+ expectedData: { title: "Test", status: "active" },
9
+ expectedContent: "\nContent here",
10
+ },
11
+ {
12
+ name: "without frontmatter",
13
+ input: "# Just markdown\n\nNo frontmatter",
14
+ expectedData: {},
15
+ expectedContent: "# Just markdown\n\nNo frontmatter",
16
+ },
17
+ {
18
+ name: "empty string",
19
+ input: "",
20
+ expectedData: {},
21
+ expectedContent: "",
22
+ },
23
+ {
24
+ name: "only frontmatter dashes",
25
+ input: "---\n---\n",
26
+ expectedData: {},
27
+ expectedContent: "",
28
+ },
29
+ {
30
+ name: "malformed YAML",
31
+ input: "---\ninvalid: [unclosed\n---\nContent",
32
+ expectedData: {},
33
+ expectedContent: "---\ninvalid: [unclosed\n---\nContent",
34
+ },
35
+ {
36
+ name: "with array field",
37
+ input: "---\ntags:\n - tag1\n - tag2\n---\nContent",
38
+ expectedData: { tags: ["tag1", "tag2"] },
39
+ expectedContent: "Content",
40
+ },
41
+ {
42
+ name: "with nested object",
43
+ input: "---\nmeta:\n key: value\n---\nContent",
44
+ expectedData: { meta: { key: "value" } },
45
+ expectedContent: "Content",
46
+ },
47
+ ];
48
+ for (const c of cases) {
49
+ it(c.name, () => {
50
+ const result = parseFrontmatter(c.input);
51
+ expect(result.data).toEqual(c.expectedData);
52
+ expect(result.content).toBe(c.expectedContent);
53
+ });
54
+ }
55
+ });
56
+ describe("serializeFrontmatter", () => {
57
+ it("serializes with content", () => {
58
+ const data = { type: "adr", status: "active" };
59
+ const content = "# My ADR\n\nContent";
60
+ const result = serializeFrontmatter(data, content);
61
+ expect(result).toContain("---");
62
+ expect(result).toContain("type: adr");
63
+ expect(result).toContain("status: active");
64
+ expect(result).toContain("# My ADR");
65
+ });
66
+ it("serializes with empty content", () => {
67
+ const data = { type: "task" };
68
+ const result = serializeFrontmatter(data, "");
69
+ expect(result).toContain("type: task");
70
+ });
71
+ it("serializes array fields", () => {
72
+ const data = { tags: ["a", "b"] };
73
+ const result = serializeFrontmatter(data, "content");
74
+ expect(result).toContain("tags:");
75
+ expect(result).toContain("- a");
76
+ expect(result).toContain("- b");
77
+ });
78
+ });
79
+ describe("createFrontmatter", () => {
80
+ beforeEach(() => {
81
+ vi.useFakeTimers();
82
+ vi.setSystemTime(new Date("2024-01-15"));
83
+ });
84
+ afterEach(() => {
85
+ vi.useRealTimers();
86
+ });
87
+ it("creates frontmatter with defaults", () => {
88
+ const result = createFrontmatter({});
89
+ expect(result.type).toBe("index");
90
+ expect(result.status).toBe("active");
91
+ expect(result.created).toBe("2024-01-15");
92
+ expect(result.updated).toBe("2024-01-15");
93
+ });
94
+ it("overrides defaults", () => {
95
+ const result = createFrontmatter({ type: "adr", status: "draft" });
96
+ expect(result.type).toBe("adr");
97
+ expect(result.status).toBe("draft");
98
+ });
99
+ it("preserves custom fields", () => {
100
+ const result = createFrontmatter({ custom: "value" });
101
+ expect(result.custom).toBe("value");
102
+ });
103
+ it("uses provided created date", () => {
104
+ const result = createFrontmatter({ created: "2023-12-01" });
105
+ expect(result.created).toBe("2023-12-01");
106
+ expect(result.updated).toBe("2024-01-15");
107
+ });
108
+ });
109
+ describe("mergeFrontmatter", () => {
110
+ beforeEach(() => {
111
+ vi.useFakeTimers();
112
+ vi.setSystemTime(new Date("2024-01-15"));
113
+ });
114
+ afterEach(() => {
115
+ vi.useRealTimers();
116
+ });
117
+ it("merges new fields", () => {
118
+ const existing = { type: "adr", status: "draft" };
119
+ const result = mergeFrontmatter(existing, { status: "active" });
120
+ expect(result.type).toBe("adr");
121
+ expect(result.status).toBe("active");
122
+ });
123
+ it("adds new fields", () => {
124
+ const existing = { type: "task" };
125
+ const result = mergeFrontmatter(existing, { tags: ["urgent"] });
126
+ expect(result.type).toBe("task");
127
+ expect(result.tags).toEqual(["urgent"]);
128
+ });
129
+ it("updates the updated timestamp", () => {
130
+ const existing = { type: "adr", updated: "2023-01-01" };
131
+ const result = mergeFrontmatter(existing, {});
132
+ expect(result.updated).toBe("2024-01-15");
133
+ });
134
+ it("preserves existing fields not in updates", () => {
135
+ const existing = { type: "adr", project: "test", created: "2023-01-01" };
136
+ const result = mergeFrontmatter(existing, { status: "done" });
137
+ expect(result.type).toBe("adr");
138
+ expect(result.project).toBe("test");
139
+ expect(result.created).toBe("2023-01-01");
140
+ expect(result.status).toBe("done");
141
+ });
142
+ });
143
+ describe("validateFrontmatter", () => {
144
+ it("returns empty array for valid frontmatter", () => {
145
+ const data = { type: "adr", status: "active" };
146
+ expect(validateFrontmatter(data)).toEqual([]);
147
+ });
148
+ it("returns error for invalid type", () => {
149
+ const data = { type: "invalid" };
150
+ const errors = validateFrontmatter(data);
151
+ expect(errors.length).toBe(1);
152
+ expect(errors[0]).toContain("Invalid type");
153
+ });
154
+ it("returns error for invalid status", () => {
155
+ const data = { status: "invalid" };
156
+ const errors = validateFrontmatter(data);
157
+ expect(errors.length).toBe(1);
158
+ expect(errors[0]).toContain("Invalid status");
159
+ });
160
+ it("returns error when tags is not array", () => {
161
+ const data = { tags: "not-array" };
162
+ const errors = validateFrontmatter(data);
163
+ expect(errors.length).toBe(1);
164
+ expect(errors[0]).toContain("Tags must be an array");
165
+ });
166
+ it("returns error when related is not array", () => {
167
+ const data = { related: "not-array" };
168
+ const errors = validateFrontmatter(data);
169
+ expect(errors.length).toBe(1);
170
+ expect(errors[0]).toContain("Related must be an array");
171
+ });
172
+ it("returns multiple errors", () => {
173
+ const data = { type: "bad", status: "worse", tags: "nope" };
174
+ const errors = validateFrontmatter(data);
175
+ expect(errors.length).toBe(3);
176
+ });
177
+ it("accepts all valid types", () => {
178
+ const validTypes = ["context", "adr", "brainstorm", "decision", "todo", "incident", "pattern", "evaluation", "index", "task", "learning", "session"];
179
+ for (const type of validTypes) {
180
+ const errors = validateFrontmatter({ type });
181
+ expect(errors).toEqual([]);
182
+ }
183
+ });
184
+ it("accepts all valid statuses", () => {
185
+ const validStatuses = ["active", "resolved", "deprecated", "draft", "backlog", "in-progress", "blocked", "done", "cancelled", "completed"];
186
+ for (const status of validStatuses) {
187
+ const errors = validateFrontmatter({ status });
188
+ expect(errors).toEqual([]);
189
+ }
190
+ });
191
+ });
192
+ //# sourceMappingURL=frontmatter.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.test.js","sourceRoot":"","sources":["../../src/lib/frontmatter.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export declare function invalidateProjectMapCache(): void;
2
+ /**
3
+ * Detect the project slug from a working directory.
4
+ *
5
+ * Strategy:
6
+ * 1. Strip worktree suffixes from cwd
7
+ * 2. Try exact match in project-map.json
8
+ * 3. Try matching cwd as a subdirectory of a mapped path
9
+ * 4. Try git root as a mapped path
10
+ * 5. Return null if no match
11
+ */
12
+ export declare function detectProject(cwd: string, vaultPath: string): Promise<string | null>;
@@ -0,0 +1,123 @@
1
+ import { readFile } from "fs/promises";
2
+ import { resolve } from "path";
3
+ import { execFile } from "child_process";
4
+ import { promisify } from "util";
5
+ const execFileAsync = promisify(execFile);
6
+ const CACHE_TTL_MS = 60_000; // 1 minute
7
+ let cache = null;
8
+ export function invalidateProjectMapCache() {
9
+ cache = null;
10
+ }
11
+ function setCache(entry) {
12
+ cache = entry;
13
+ }
14
+ function getCache(vaultPath) {
15
+ const now = Date.now();
16
+ if (cache && cache.vaultPath === vaultPath && now - cache.time < CACHE_TTL_MS) {
17
+ return cache.map;
18
+ }
19
+ return null;
20
+ }
21
+ /**
22
+ * Load the project map from the vault. Cache is keyed by vaultPath.
23
+ */
24
+ async function loadProjectMap(vaultPath) {
25
+ const cached = getCache(vaultPath);
26
+ if (cached)
27
+ return cached;
28
+ const mapPath = resolve(vaultPath, "project-map.json");
29
+ try {
30
+ const raw = await readFile(mapPath, "utf-8");
31
+ const parsed = JSON.parse(raw);
32
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
33
+ return {};
34
+ }
35
+ const map = {};
36
+ for (const [key, value] of Object.entries(parsed)) {
37
+ if (typeof value === "string") {
38
+ map[key] = value;
39
+ }
40
+ }
41
+ setCache({ map, time: Date.now(), vaultPath });
42
+ return map;
43
+ }
44
+ catch (e) {
45
+ if (e?.code === "EACCES") {
46
+ console.error(`Warning: permission denied reading ${mapPath}`);
47
+ }
48
+ else if (e?.code !== "ENOENT" && !(e instanceof SyntaxError)) {
49
+ throw e;
50
+ }
51
+ return {};
52
+ }
53
+ }
54
+ /**
55
+ * Strip worktree suffixes from a path.
56
+ */
57
+ function stripWorktreeSuffix(cwd) {
58
+ // Pattern: /path/to/project/.worktrees/feat-name
59
+ const worktreeMatch = cwd.match(/^(.+?)\/\.worktrees\/[^/]+/);
60
+ if (worktreeMatch)
61
+ return worktreeMatch[1];
62
+ // Pattern: /path/to/project/.claude/worktrees/feat/name
63
+ const claudeWorktreeMatch = cwd.match(/^(.+?)\/\.claude\/worktrees\/.+/);
64
+ if (claudeWorktreeMatch)
65
+ return claudeWorktreeMatch[1];
66
+ // Pattern: /path/to/project/.claude-worktrees/name
67
+ const oldWorktreeMatch = cwd.match(/^(.+?)\/\.claude-worktrees\/[^/]+/);
68
+ if (oldWorktreeMatch)
69
+ return oldWorktreeMatch[1];
70
+ return cwd;
71
+ }
72
+ /**
73
+ * Try to detect git root of current directory (async, non-blocking).
74
+ */
75
+ async function getGitRoot(cwd) {
76
+ try {
77
+ const { stdout } = await execFileAsync("git", ["rev-parse", "--show-toplevel"], {
78
+ cwd,
79
+ timeout: 3000,
80
+ });
81
+ return stdout.trim();
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ }
87
+ /**
88
+ * Detect the project slug from a working directory.
89
+ *
90
+ * Strategy:
91
+ * 1. Strip worktree suffixes from cwd
92
+ * 2. Try exact match in project-map.json
93
+ * 3. Try matching cwd as a subdirectory of a mapped path
94
+ * 4. Try git root as a mapped path
95
+ * 5. Return null if no match
96
+ */
97
+ export async function detectProject(cwd, vaultPath) {
98
+ const map = await loadProjectMap(vaultPath);
99
+ const cleanCwd = stripWorktreeSuffix(cwd);
100
+ // Exact match
101
+ if (map[cleanCwd])
102
+ return map[cleanCwd];
103
+ // Subdirectory match
104
+ for (const [path, slug] of Object.entries(map)) {
105
+ if (cleanCwd.startsWith(path + "/") || cleanCwd === path) {
106
+ return slug;
107
+ }
108
+ }
109
+ // Git root match
110
+ const gitRoot = await getGitRoot(cwd);
111
+ if (gitRoot) {
112
+ const cleanGitRoot = stripWorktreeSuffix(gitRoot);
113
+ if (map[cleanGitRoot])
114
+ return map[cleanGitRoot];
115
+ for (const [path, slug] of Object.entries(map)) {
116
+ if (cleanGitRoot.startsWith(path + "/") || cleanGitRoot === path) {
117
+ return slug;
118
+ }
119
+ }
120
+ }
121
+ return null;
122
+ }
123
+ //# sourceMappingURL=project-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-detector.js","sourceRoot":"","sources":["../../src/lib/project-detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,117 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdir, writeFile, rm } from "fs/promises";
3
+ import { tmpdir } from "os";
4
+ import { join } from "path";
5
+ import { detectProject, invalidateProjectMapCache } from "./project-detector.js";
6
+ describe("project-detector", () => {
7
+ let vaultRoot;
8
+ let originalCwd;
9
+ beforeEach(async () => {
10
+ vaultRoot = join(tmpdir(), `project-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
11
+ await mkdir(vaultRoot, { recursive: true });
12
+ originalCwd = process.cwd();
13
+ invalidateProjectMapCache();
14
+ });
15
+ afterEach(async () => {
16
+ process.chdir(originalCwd);
17
+ await rm(vaultRoot, { recursive: true, force: true });
18
+ invalidateProjectMapCache();
19
+ });
20
+ describe("detectProject", () => {
21
+ it("returns null when no project-map.json", async () => {
22
+ const result = await detectProject("/nonexistent/path", vaultRoot);
23
+ expect(result).toBeNull();
24
+ });
25
+ it("detects project from exact cwd match in project-map.json", async () => {
26
+ const projectMap = { "/Users/test/myproject": "myproject" };
27
+ await writeFile(join(vaultRoot, "project-map.json"), JSON.stringify(projectMap));
28
+ invalidateProjectMapCache();
29
+ const result = await detectProject("/Users/test/myproject", vaultRoot);
30
+ expect(result).toBe("myproject");
31
+ });
32
+ it("detects project from subdirectory of mapped path", async () => {
33
+ const projectMap = { "/Users/test/myproject": "myproject" };
34
+ await writeFile(join(vaultRoot, "project-map.json"), JSON.stringify(projectMap));
35
+ invalidateProjectMapCache();
36
+ const result = await detectProject("/Users/test/myproject/src/components", vaultRoot);
37
+ expect(result).toBe("myproject");
38
+ });
39
+ it("returns null for unmapped path", async () => {
40
+ const projectMap = { "/Users/test/other": "other" };
41
+ await writeFile(join(vaultRoot, "project-map.json"), JSON.stringify(projectMap));
42
+ invalidateProjectMapCache();
43
+ const result = await detectProject("/Users/test/unmapped", vaultRoot);
44
+ expect(result).toBeNull();
45
+ });
46
+ it("handles invalid JSON in project-map.json", async () => {
47
+ await writeFile(join(vaultRoot, "project-map.json"), "not valid json {{{");
48
+ invalidateProjectMapCache();
49
+ const result = await detectProject("/any/path", vaultRoot);
50
+ expect(result).toBeNull();
51
+ });
52
+ it("handles non-object project-map.json", async () => {
53
+ await writeFile(join(vaultRoot, "project-map.json"), JSON.stringify(["array", "not", "object"]));
54
+ invalidateProjectMapCache();
55
+ const result = await detectProject("/any/path", vaultRoot);
56
+ expect(result).toBeNull();
57
+ });
58
+ it("strips .worktrees suffix from cwd", async () => {
59
+ const projectMap = { "/Users/test/myproject": "myproject" };
60
+ await writeFile(join(vaultRoot, "project-map.json"), JSON.stringify(projectMap));
61
+ invalidateProjectMapCache();
62
+ const result = await detectProject("/Users/test/myproject/.worktrees/feat-x", vaultRoot);
63
+ expect(result).toBe("myproject");
64
+ });
65
+ it("strips .claude/worktrees suffix from cwd", async () => {
66
+ const projectMap = { "/Users/test/myproject": "myproject" };
67
+ await writeFile(join(vaultRoot, "project-map.json"), JSON.stringify(projectMap));
68
+ invalidateProjectMapCache();
69
+ const result = await detectProject("/Users/test/myproject/.claude/worktrees/feat/name", vaultRoot);
70
+ expect(result).toBe("myproject");
71
+ });
72
+ it("strips .claude-worktrees suffix from cwd", async () => {
73
+ const projectMap = { "/Users/test/myproject": "myproject" };
74
+ await writeFile(join(vaultRoot, "project-map.json"), JSON.stringify(projectMap));
75
+ invalidateProjectMapCache();
76
+ const result = await detectProject("/Users/test/myproject/.claude-worktrees/feat-name", vaultRoot);
77
+ expect(result).toBe("myproject");
78
+ });
79
+ it("ignores non-string values in project-map.json", async () => {
80
+ const projectMap = {
81
+ "/Users/test/good": "good",
82
+ "/Users/test/bad": 123,
83
+ "/Users/test/also-bad": null
84
+ };
85
+ await writeFile(join(vaultRoot, "project-map.json"), JSON.stringify(projectMap));
86
+ invalidateProjectMapCache();
87
+ const goodResult = await detectProject("/Users/test/good", vaultRoot);
88
+ expect(goodResult).toBe("good");
89
+ const badResult = await detectProject("/Users/test/bad", vaultRoot);
90
+ expect(badResult).toBeNull();
91
+ });
92
+ it("caches project-map.json", async () => {
93
+ const projectMap = { "/Users/test/cached": "cached" };
94
+ await writeFile(join(vaultRoot, "project-map.json"), JSON.stringify(projectMap));
95
+ invalidateProjectMapCache();
96
+ await detectProject("/Users/test/cached", vaultRoot);
97
+ await writeFile(join(vaultRoot, "project-map.json"), JSON.stringify({ "/Users/test/other": "other" }));
98
+ const result = await detectProject("/Users/test/cached", vaultRoot);
99
+ expect(result).toBe("cached");
100
+ });
101
+ it("cache invalidates for different vault path", async () => {
102
+ const projectMap1 = { "/Users/test/path1": "project1" };
103
+ await writeFile(join(vaultRoot, "project-map.json"), JSON.stringify(projectMap1));
104
+ invalidateProjectMapCache();
105
+ const vaultRoot2 = join(tmpdir(), `project-test-2-${Date.now()}`);
106
+ await mkdir(vaultRoot2, { recursive: true });
107
+ const projectMap2 = { "/Users/test/path1": "project2" };
108
+ await writeFile(join(vaultRoot2, "project-map.json"), JSON.stringify(projectMap2));
109
+ const result1 = await detectProject("/Users/test/path1", vaultRoot);
110
+ const result2 = await detectProject("/Users/test/path1", vaultRoot2);
111
+ expect(result1).toBe("project1");
112
+ expect(result2).toBe("project2");
113
+ await rm(vaultRoot2, { recursive: true, force: true });
114
+ });
115
+ });
116
+ });
117
+ //# sourceMappingURL=project-detector.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-detector.test.js","sourceRoot":"","sources":["../../src/lib/project-detector.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Validate that a path is safe to read from outside the vault.
3
+ * Used only by vault_init to scan git repos.
4
+ *
5
+ * Enforces:
6
+ * - Must be under $HOME
7
+ * - Must not contain .. or symlinks escaping home
8
+ * - Must be a git repository
9
+ */
10
+ export declare function safeExternalPath(rawPath: string): Promise<string>;