grounder 0.3.0 → 0.5.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.
Files changed (146) hide show
  1. package/README.md +157 -215
  2. package/dist/agents/claude.d.ts.map +1 -1
  3. package/dist/agents/claude.js +3 -3
  4. package/dist/agents/claude.js.map +1 -1
  5. package/dist/agents/cursor.d.ts.map +1 -1
  6. package/dist/agents/cursor.js +3 -3
  7. package/dist/agents/cursor.js.map +1 -1
  8. package/dist/agents/hook-runtime.d.ts +3 -3
  9. package/dist/agents/hook-runtime.js +4 -4
  10. package/dist/agents/hook-runtime.js.map +1 -1
  11. package/dist/agents/install-command.d.ts +0 -2
  12. package/dist/agents/install-command.d.ts.map +1 -1
  13. package/dist/agents/install-command.js.map +1 -1
  14. package/dist/cli.js +15 -10
  15. package/dist/cli.js.map +1 -1
  16. package/dist/commands/apply-agent-installs.d.ts +2 -2
  17. package/dist/commands/apply-agent-installs.d.ts.map +1 -1
  18. package/dist/commands/apply-agent-installs.js +1 -1
  19. package/dist/commands/doctor.d.ts.map +1 -1
  20. package/dist/commands/doctor.js +120 -50
  21. package/dist/commands/doctor.js.map +1 -1
  22. package/dist/commands/handoff/list.d.ts +27 -9
  23. package/dist/commands/handoff/list.d.ts.map +1 -1
  24. package/dist/commands/handoff/list.js +31 -16
  25. package/dist/commands/handoff/list.js.map +1 -1
  26. package/dist/commands/handoff.d.ts +2 -0
  27. package/dist/commands/handoff.d.ts.map +1 -1
  28. package/dist/commands/handoff.js +10 -1
  29. package/dist/commands/handoff.js.map +1 -1
  30. package/dist/commands/link.d.ts +12 -0
  31. package/dist/commands/link.d.ts.map +1 -0
  32. package/dist/commands/{repo/init.js → link.js} +35 -27
  33. package/dist/commands/link.js.map +1 -0
  34. package/dist/commands/migrate.js +3 -5
  35. package/dist/commands/migrate.js.map +1 -1
  36. package/dist/commands/note/list.d.ts +36 -0
  37. package/dist/commands/note/list.d.ts.map +1 -0
  38. package/dist/commands/note/list.js +77 -0
  39. package/dist/commands/note/list.js.map +1 -0
  40. package/dist/commands/note.d.ts +2 -0
  41. package/dist/commands/note.d.ts.map +1 -1
  42. package/dist/commands/note.js +10 -1
  43. package/dist/commands/note.js.map +1 -1
  44. package/dist/commands/output.d.ts +47 -0
  45. package/dist/commands/output.d.ts.map +1 -0
  46. package/dist/commands/output.js +63 -0
  47. package/dist/commands/output.js.map +1 -0
  48. package/dist/commands/plan/list.d.ts +21 -5
  49. package/dist/commands/plan/list.d.ts.map +1 -1
  50. package/dist/commands/plan/list.js +24 -13
  51. package/dist/commands/plan/list.js.map +1 -1
  52. package/dist/commands/plan.d.ts +2 -0
  53. package/dist/commands/plan.d.ts.map +1 -1
  54. package/dist/commands/plan.js +12 -2
  55. package/dist/commands/plan.js.map +1 -1
  56. package/dist/commands/require-linked.js +2 -2
  57. package/dist/commands/require-linked.js.map +1 -1
  58. package/dist/commands/search.d.ts +23 -0
  59. package/dist/commands/search.d.ts.map +1 -0
  60. package/dist/commands/search.js +277 -0
  61. package/dist/commands/search.js.map +1 -0
  62. package/dist/commands/{vault/init.d.ts → setup.d.ts} +5 -4
  63. package/dist/commands/setup.d.ts.map +1 -0
  64. package/dist/commands/setup.js +137 -0
  65. package/dist/commands/setup.js.map +1 -0
  66. package/dist/commands/status.d.ts.map +1 -1
  67. package/dist/commands/status.js +39 -29
  68. package/dist/commands/status.js.map +1 -1
  69. package/dist/commands/upgrade-banner.d.ts +2 -2
  70. package/dist/commands/upgrade-banner.js +2 -2
  71. package/dist/connector/home.d.ts +12 -0
  72. package/dist/connector/home.d.ts.map +1 -1
  73. package/dist/connector/home.js +26 -2
  74. package/dist/connector/home.js.map +1 -1
  75. package/dist/connector/state.d.ts +4 -13
  76. package/dist/connector/state.d.ts.map +1 -1
  77. package/dist/connector/state.js +3 -21
  78. package/dist/connector/state.js.map +1 -1
  79. package/dist/connector/vault.d.ts +2 -0
  80. package/dist/connector/vault.d.ts.map +1 -1
  81. package/dist/connector/vault.js +6 -1
  82. package/dist/connector/vault.js.map +1 -1
  83. package/dist/help.d.ts +3 -3
  84. package/dist/help.d.ts.map +1 -1
  85. package/dist/help.js +80 -48
  86. package/dist/help.js.map +1 -1
  87. package/dist/util/frontmatter.d.ts +9 -7
  88. package/dist/util/frontmatter.d.ts.map +1 -1
  89. package/dist/util/frontmatter.js +29 -4
  90. package/dist/util/frontmatter.js.map +1 -1
  91. package/dist/util/parse-args.d.ts.map +1 -1
  92. package/dist/util/parse-args.js +11 -2
  93. package/dist/util/parse-args.js.map +1 -1
  94. package/dist/util/path.d.ts +23 -0
  95. package/dist/util/path.d.ts.map +1 -1
  96. package/dist/util/path.js +39 -0
  97. package/dist/util/path.js.map +1 -1
  98. package/dist/vault/list-handoffs.d.ts +3 -1
  99. package/dist/vault/list-handoffs.d.ts.map +1 -1
  100. package/dist/vault/list-handoffs.js +17 -15
  101. package/dist/vault/list-handoffs.js.map +1 -1
  102. package/dist/vault/list-markdown.d.ts +8 -0
  103. package/dist/vault/list-markdown.d.ts.map +1 -0
  104. package/dist/vault/list-markdown.js +35 -0
  105. package/dist/vault/list-markdown.js.map +1 -0
  106. package/dist/vault/list-notes.d.ts +15 -0
  107. package/dist/vault/list-notes.d.ts.map +1 -0
  108. package/dist/vault/list-notes.js +34 -0
  109. package/dist/vault/list-notes.js.map +1 -0
  110. package/dist/vault/list-plans.d.ts +2 -2
  111. package/dist/vault/list-plans.d.ts.map +1 -1
  112. package/dist/vault/list-plans.js +9 -19
  113. package/dist/vault/list-plans.js.map +1 -1
  114. package/dist/vault/search.d.ts +55 -0
  115. package/dist/vault/search.d.ts.map +1 -0
  116. package/dist/vault/search.js +328 -0
  117. package/dist/vault/search.js.map +1 -0
  118. package/dist/vault/write-handoff.d.ts +2 -0
  119. package/dist/vault/write-handoff.d.ts.map +1 -1
  120. package/dist/vault/write-handoff.js +5 -0
  121. package/dist/vault/write-handoff.js.map +1 -1
  122. package/dist/vault/write-note.d.ts +2 -0
  123. package/dist/vault/write-note.d.ts.map +1 -1
  124. package/dist/vault/write-note.js +9 -1
  125. package/dist/vault/write-note.js.map +1 -1
  126. package/dist/vault/write-plan.d.ts +4 -0
  127. package/dist/vault/write-plan.d.ts.map +1 -1
  128. package/dist/vault/write-plan.js +14 -1
  129. package/dist/vault/write-plan.js.map +1 -1
  130. package/package.json +13 -4
  131. package/templates/agents/claude/commands/grounder-note.md +30 -4
  132. package/templates/agents/claude/commands/grounder-plan.md +23 -17
  133. package/templates/agents/claude/commands/grounder-search.md +144 -0
  134. package/templates/agents/claude/commands/grounder-task-handoff.md +5 -1
  135. package/templates/agents/claude/commands/grounder-task.md +4 -2
  136. package/templates/agents/cursor/commands/grounder-note.md +31 -5
  137. package/templates/agents/cursor/commands/grounder-plan.md +24 -18
  138. package/templates/agents/cursor/commands/grounder-search.md +144 -0
  139. package/templates/agents/cursor/commands/grounder-task-handoff.md +6 -2
  140. package/templates/agents/cursor/commands/grounder-task.md +5 -3
  141. package/dist/commands/repo/init.d.ts +0 -11
  142. package/dist/commands/repo/init.d.ts.map +0 -1
  143. package/dist/commands/repo/init.js.map +0 -1
  144. package/dist/commands/vault/init.d.ts.map +0 -1
  145. package/dist/commands/vault/init.js +0 -90
  146. package/dist/commands/vault/init.js.map +0 -1
@@ -0,0 +1,328 @@
1
+ import { readFile, stat } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { parseHandoffFrontmatter } from "../util/frontmatter.js";
4
+ import { listMarkdownFiles } from "./list-markdown.js";
5
+ const DEFAULT_LIMIT = 10;
6
+ const DEFAULT_MAX_HITS = 50;
7
+ const DEFAULT_CONTEXT = 1;
8
+ const DEFAULT_MAX_HITS_PER_FILE = 1;
9
+ /** Cap line hits stored per file during scan (counts stay full for ranking). */
10
+ const MAX_STORED_HITS_PER_FILE = 50;
11
+ /** Known vault subfolders — light tiebreaker after recency. */
12
+ const FOLDER_SIGNAL = {
13
+ notes: 2,
14
+ plans: 2,
15
+ logs: 1,
16
+ };
17
+ function escapeRegex(value) {
18
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
19
+ }
20
+ /** Single-token terms only — phrases may contain spaces or punctuation. */
21
+ function isWordLikeTerm(term) {
22
+ return /^[\w-]+$/.test(term);
23
+ }
24
+ function pruneOverlappingTerms(query, terms) {
25
+ const queryKey = query.trim().toLowerCase();
26
+ return terms.filter((term) => {
27
+ const lower = term.toLowerCase();
28
+ if (lower === queryKey) {
29
+ return true;
30
+ }
31
+ return !terms.some((other) => {
32
+ if (other === term) {
33
+ return false;
34
+ }
35
+ const otherLower = other.toLowerCase();
36
+ if (otherLower.length <= lower.length || !isWordLikeTerm(term) || !isWordLikeTerm(other)) {
37
+ return false;
38
+ }
39
+ return otherLower.startsWith(lower);
40
+ });
41
+ });
42
+ }
43
+ function normalizeTerms(query, extra) {
44
+ const seen = new Set();
45
+ const out = [];
46
+ // Always include the query — multi-word phrases use substring matching in
47
+ // termMatchesLine, so exact phrases only match lines that contain them verbatim.
48
+ for (const raw of [query, ...(extra ?? [])]) {
49
+ const term = raw.trim();
50
+ if (!term) {
51
+ continue;
52
+ }
53
+ const key = term.toLowerCase();
54
+ if (seen.has(key)) {
55
+ continue;
56
+ }
57
+ seen.add(key);
58
+ out.push(term);
59
+ }
60
+ return pruneOverlappingTerms(query, out);
61
+ }
62
+ /**
63
+ * Soft-demote search dogfood notes when the user is not asking about search.
64
+ * (`discussions/search/…`, `search-feature.md`, `Search Results.md`)
65
+ */
66
+ function searchMetaPenalty(rootDir, filePath, query) {
67
+ if (/\bsearch\b/i.test(query)) {
68
+ return 0;
69
+ }
70
+ const rel = path.relative(rootDir, filePath);
71
+ const segments = rel.split(path.sep).map((segment) => segment.toLowerCase());
72
+ for (let i = 0; i < segments.length - 1; i++) {
73
+ if (segments[i] === "discussions" && segments[i + 1] === "search") {
74
+ return 1;
75
+ }
76
+ }
77
+ const stem = path.basename(filePath, path.extname(filePath)).toLowerCase();
78
+ if (stem === "search-feature" || stem === "search results") {
79
+ return 1;
80
+ }
81
+ return 0;
82
+ }
83
+ function termMatchesLine(line, term) {
84
+ if (term.includes(" ")) {
85
+ return line.toLowerCase().includes(term.toLowerCase());
86
+ }
87
+ const re = new RegExp(`\\b${escapeRegex(term)}\\b`, "i");
88
+ return re.test(line);
89
+ }
90
+ function matchingTermsOnLine(line, terms) {
91
+ return terms.filter((term) => termMatchesLine(line, term));
92
+ }
93
+ function longestTerm(matches) {
94
+ return matches.reduce((best, term) => (term.length > best.length ? term : best));
95
+ }
96
+ function topicsMatchTerms(topics, terms) {
97
+ if (!topics?.length) {
98
+ return false;
99
+ }
100
+ const lowered = topics.map((t) => t.toLowerCase());
101
+ return terms.some((term) => lowered.includes(term.toLowerCase()));
102
+ }
103
+ function folderSignal(rootDir, filePath) {
104
+ const rel = path.relative(rootDir, filePath);
105
+ const first = rel.split(path.sep)[0];
106
+ return first ? (FOLDER_SIGNAL[first] ?? 0) : 0;
107
+ }
108
+ /** Deprioritize archived plans/notes — still searchable, ranked lower. */
109
+ function archivePenalty(rootDir, filePath) {
110
+ const rel = path.relative(rootDir, filePath);
111
+ const segments = rel.split(path.sep);
112
+ return segments.some((segment) => segment.toLowerCase() === "archive") ? 1 : 0;
113
+ }
114
+ function buildSnippet(lines, lineIndex, context) {
115
+ const start = Math.max(0, lineIndex - context);
116
+ const end = Math.min(lines.length - 1, lineIndex + context);
117
+ const parts = [];
118
+ for (let i = start; i <= end; i++) {
119
+ parts.push(lines[i] ?? "");
120
+ }
121
+ return parts.join("\n");
122
+ }
123
+ function countFilenameTermMatches(rootDir, filePath, terms) {
124
+ const rel = path.relative(rootDir, filePath);
125
+ const haystack = rel.replace(/[/\\]/g, " ").replace(/\.md$/i, "").toLowerCase();
126
+ let count = 0;
127
+ for (const term of terms) {
128
+ const lower = term.toLowerCase();
129
+ if (lower.includes(" ")) {
130
+ if (haystack.includes(lower)) {
131
+ count++;
132
+ }
133
+ continue;
134
+ }
135
+ const re = new RegExp(`\\b${escapeRegex(lower)}\\b`, "i");
136
+ if (re.test(haystack)) {
137
+ count++;
138
+ }
139
+ }
140
+ return count;
141
+ }
142
+ function contentHasPhrase(content, query) {
143
+ const trimmed = query.trim();
144
+ if (!trimmed.includes(" ")) {
145
+ return false;
146
+ }
147
+ return content.toLowerCase().includes(trimmed.toLowerCase());
148
+ }
149
+ /**
150
+ * Partial n-gram match: 2 consecutive words for 3-word queries, 3 consecutive
151
+ * words for 4+ word queries. Trigrams cut false positives where a doc only
152
+ * shares a loose bigram (e.g. "slash commands") without migration context.
153
+ */
154
+ function contentHasPartialPhrase(content, query) {
155
+ const words = query.trim().split(/\s+/).filter(Boolean);
156
+ if (words.length < 3) {
157
+ return false;
158
+ }
159
+ const lower = content.toLowerCase();
160
+ const windowSize = words.length >= 4 ? 3 : 2;
161
+ for (let i = 0; i <= words.length - windowSize; i++) {
162
+ const ngram = words
163
+ .slice(i, i + windowSize)
164
+ .join(" ")
165
+ .toLowerCase();
166
+ if (lower.includes(ngram)) {
167
+ return true;
168
+ }
169
+ }
170
+ return false;
171
+ }
172
+ function relevanceScore(file, rootDir, query) {
173
+ return (file.distinctTermCount * 1000 +
174
+ Math.min(file.idfDensity, 100) * 10 +
175
+ (file.topicsMatch ? 800 : 0) +
176
+ file.filenameTermCount * 200 +
177
+ (file.phraseMatch ? 300 : 0) +
178
+ (file.partialPhraseMatch ? 100 : 0) -
179
+ // Strong enough to lose to real notes with similar distinct-term coverage
180
+ // (meta dumps / search dogfood often win on raw hit density alone).
181
+ searchMetaPenalty(rootDir, file.filePath, query) * 5000);
182
+ }
183
+ function pickBestHits(hits, maxHitsPerFile) {
184
+ if (hits.length <= maxHitsPerFile) {
185
+ return hits;
186
+ }
187
+ const sorted = [...hits].sort((a, b) => b.matchedTerm.length - a.matchedTerm.length);
188
+ return sorted.slice(0, maxHitsPerFile);
189
+ }
190
+ /**
191
+ * Case-insensitive markdown scan under `rootDir`.
192
+ * Frontmatter `topics:` matches boost file ranking; body and frontmatter lines
193
+ * both contribute line hits. Files rank by relevance (distinct terms, hit
194
+ * density, filename/path terms, topics; demotes search dogfood when query is
195
+ * unrelated), then archive, recency, folder. Pure fs — no index, no deps.
196
+ */
197
+ export async function searchVault(options) {
198
+ const terms = normalizeTerms(options.query, options.terms);
199
+ const limit = options.limit ?? DEFAULT_LIMIT;
200
+ const maxHits = options.maxHits ?? DEFAULT_MAX_HITS;
201
+ const context = options.context ?? DEFAULT_CONTEXT;
202
+ const maxHitsPerFile = options.maxHitsPerFile ?? DEFAULT_MAX_HITS_PER_FILE;
203
+ const perFileStoreCap = Math.min(MAX_STORED_HITS_PER_FILE, maxHits);
204
+ if (terms.length === 0) {
205
+ return {
206
+ query: options.query,
207
+ terms: [],
208
+ termHitCounts: {},
209
+ totalMatchCount: 0,
210
+ totalFileCount: 0,
211
+ files: [],
212
+ truncated: false,
213
+ };
214
+ }
215
+ const filePaths = await listMarkdownFiles(options.rootDir);
216
+ const rawFiles = [];
217
+ let totalMatchCount = 0;
218
+ // Lowercased doc-freq for ranking; JSON output remaps to original `terms` spelling.
219
+ const termDocFreq = Object.fromEntries(terms.map((t) => [t.toLowerCase(), 0]));
220
+ for (const filePath of filePaths) {
221
+ let content;
222
+ let mtimeMs;
223
+ try {
224
+ [content, { mtimeMs }] = await Promise.all([readFile(filePath, "utf8"), stat(filePath)]);
225
+ }
226
+ catch {
227
+ continue;
228
+ }
229
+ if (options.since !== undefined && mtimeMs < options.since.getTime()) {
230
+ continue;
231
+ }
232
+ const lines = content.split(/\r?\n/);
233
+ const frontmatter = parseHandoffFrontmatter(content);
234
+ const topicsMatch = topicsMatchTerms(frontmatter.topics, terms);
235
+ const fileHits = [];
236
+ const matchedTerms = new Set();
237
+ const perTermHits = new Map();
238
+ let totalHitCount = 0;
239
+ for (let i = 0; i < lines.length; i++) {
240
+ const line = lines[i] ?? "";
241
+ const lineMatches = matchingTermsOnLine(line, terms);
242
+ if (lineMatches.length === 0) {
243
+ continue;
244
+ }
245
+ for (const term of lineMatches) {
246
+ const termKey = term.toLowerCase();
247
+ matchedTerms.add(termKey);
248
+ perTermHits.set(termKey, (perTermHits.get(termKey) ?? 0) + 1);
249
+ }
250
+ totalHitCount++;
251
+ totalMatchCount++;
252
+ if (fileHits.length < perFileStoreCap) {
253
+ fileHits.push({
254
+ line: i + 1,
255
+ matchedTerm: longestTerm(lineMatches),
256
+ snippet: buildSnippet(lines, i, context),
257
+ });
258
+ }
259
+ }
260
+ if (matchedTerms.size > 0) {
261
+ for (const t of matchedTerms) {
262
+ termDocFreq[t] = (termDocFreq[t] ?? 0) + 1;
263
+ }
264
+ rawFiles.push({
265
+ filePath,
266
+ mtimeMs,
267
+ topicsMatch,
268
+ hits: fileHits,
269
+ distinctTermCount: matchedTerms.size,
270
+ totalHitCount,
271
+ idfDensity: 0,
272
+ filenameTermCount: countFilenameTermMatches(options.rootDir, filePath, terms),
273
+ phraseMatch: contentHasPhrase(content, options.query),
274
+ partialPhraseMatch: contentHasPartialPhrase(content, options.query),
275
+ perTermHits,
276
+ });
277
+ }
278
+ }
279
+ // Scale hit density by IDF so rare identifiers outweigh common tokens like "grounder".
280
+ for (const file of rawFiles) {
281
+ let density = 0;
282
+ for (const [term, count] of file.perTermHits) {
283
+ const df = termDocFreq[term] ?? 1;
284
+ density += count / Math.log(1 + df);
285
+ }
286
+ file.idfDensity = density;
287
+ }
288
+ rawFiles.sort((a, b) => {
289
+ const scoreDiff = relevanceScore(b, options.rootDir, options.query) -
290
+ relevanceScore(a, options.rootDir, options.query);
291
+ if (scoreDiff !== 0) {
292
+ return scoreDiff;
293
+ }
294
+ const archiveDiff = archivePenalty(options.rootDir, a.filePath) - archivePenalty(options.rootDir, b.filePath);
295
+ if (archiveDiff !== 0) {
296
+ return archiveDiff;
297
+ }
298
+ if (a.mtimeMs !== b.mtimeMs) {
299
+ return b.mtimeMs - a.mtimeMs;
300
+ }
301
+ const folderDiff = folderSignal(options.rootDir, b.filePath) - folderSignal(options.rootDir, a.filePath);
302
+ if (folderDiff !== 0) {
303
+ return folderDiff;
304
+ }
305
+ return a.filePath.localeCompare(b.filePath);
306
+ });
307
+ const totalFileCount = rawFiles.length;
308
+ const truncated = totalFileCount > limit;
309
+ const files = rawFiles.slice(0, limit).map((file) => ({
310
+ filePath: file.filePath,
311
+ mtimeMs: file.mtimeMs,
312
+ topicsMatch: file.topicsMatch,
313
+ hits: pickBestHits(file.hits, maxHitsPerFile),
314
+ matchedTerms: terms
315
+ .filter((term) => file.perTermHits.has(term.toLowerCase()))
316
+ .sort((a, b) => b.length - a.length),
317
+ }));
318
+ return {
319
+ query: options.query,
320
+ terms,
321
+ termHitCounts: Object.fromEntries(terms.map((term) => [term, termDocFreq[term.toLowerCase()] ?? 0])),
322
+ totalMatchCount,
323
+ totalFileCount,
324
+ files,
325
+ truncated,
326
+ };
327
+ }
328
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/vault/search.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,8 @@ export interface WriteHandoffOptions {
6
6
  title?: string;
7
7
  /** Git branch when known; omitted from frontmatter if unset. */
8
8
  branch?: string;
9
+ /** 3-5 topic keywords for search (flat list, omitted when empty/unset). */
10
+ topics?: string[];
9
11
  /** Timestamp for filename prefix and `created` (default: now). */
10
12
  now?: Date;
11
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"write-handoff.d.ts","sourceRoot":"","sources":["../../src/vault/write-handoff.ts"],"names":[],"mappings":"AAKA,wCAAwC;AACxC,MAAM,WAAW,mBAAmB;IAClC,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAoBD;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,MAAM,CAAC,CAiBjB"}
1
+ {"version":3,"file":"write-handoff.d.ts","sourceRoot":"","sources":["../../src/vault/write-handoff.ts"],"names":[],"mappings":"AAKA,wCAAwC;AACxC,MAAM,WAAW,mBAAmB;IAClC,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,kEAAkE;IAClE,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAyBD;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,MAAM,CAAC,CAkBjB"}
@@ -11,6 +11,10 @@ function buildFrontmatter(options) {
11
11
  if (options.title) {
12
12
  lines.push(`title: ${yamlDoubleQuoted(options.title)}`);
13
13
  }
14
+ if (options.topics && options.topics.length > 0) {
15
+ const items = options.topics.map((t) => yamlDoubleQuoted(t)).join(", ");
16
+ lines.push(`topics: [${items}]`);
17
+ }
14
18
  lines.push("---");
15
19
  return `${lines.join("\n")}\n\n`;
16
20
  }
@@ -34,6 +38,7 @@ export async function writeHandoff(logsDir, body, options) {
34
38
  branch: options.branch,
35
39
  created: now.toISOString(),
36
40
  title: options.title,
41
+ topics: options.topics,
37
42
  }) + body;
38
43
  return writeUniqueMarkdown(logsDir, basename, content);
39
44
  }
@@ -1 +1 @@
1
- {"version":3,"file":"write-handoff.js","sourceRoot":"","sources":["../../src/vault/write-handoff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAcnD,SAAS,gBAAgB,CAAC,OAKzB;IACC,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,YAAY,gBAAgB,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACzE,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,KAAK,CAAC,IAAI,CAAC,WAAW,gBAAgB,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC5D,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,YAAY,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC5D,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,KAAK,CAAC,IAAI,CAAC,UAAU,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;AACnC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAe,EACf,IAAY,EACZ,OAA4B;IAE5B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IACtC,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1C,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,EAAE;QACzC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,GAAG;KACJ,CAAC,CAAC;IACH,MAAM,OAAO,GACX,gBAAgB,CAAC;QACf,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO,EAAE,GAAG,CAAC,WAAW,EAAE;QAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;KACrB,CAAC,GAAG,IAAI,CAAC;IAEZ,OAAO,mBAAmB,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AACzD,CAAC"}
1
+ {"version":3,"file":"write-handoff.js","sourceRoot":"","sources":["../../src/vault/write-handoff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAgBnD,SAAS,gBAAgB,CAAC,OAMzB;IACC,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,YAAY,gBAAgB,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACzE,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,KAAK,CAAC,IAAI,CAAC,WAAW,gBAAgB,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC5D,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,YAAY,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC5D,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,KAAK,CAAC,IAAI,CAAC,UAAU,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChD,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxE,KAAK,CAAC,IAAI,CAAC,YAAY,KAAK,GAAG,CAAC,CAAC;IACnC,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;AACnC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAe,EACf,IAAY,EACZ,OAA4B;IAE5B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IACtC,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1C,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,EAAE;QACzC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,GAAG;KACJ,CAAC,CAAC;IACH,MAAM,OAAO,GACX,gBAAgB,CAAC;QACf,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO,EAAE,GAAG,CAAC,WAAW,EAAE;QAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,GAAG,IAAI,CAAC;IAEZ,OAAO,mBAAmB,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AACzD,CAAC"}
@@ -1,5 +1,7 @@
1
1
  export interface WriteNoteOptions {
2
2
  title?: string;
3
+ /** 3-5 topic keywords for search (flat list, omitted when empty/unset). */
4
+ topics?: string[];
3
5
  now?: Date;
4
6
  }
5
7
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"write-note.d.ts","sourceRoot":"","sources":["../../src/vault/write-note.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,MAAM,CAAC,CAMjB"}
1
+ {"version":3,"file":"write-note.d.ts","sourceRoot":"","sources":["../../src/vault/write-note.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAQD;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,MAAM,CAAC,CAOjB"}
@@ -1,6 +1,13 @@
1
1
  import { mkdir } from "node:fs/promises";
2
2
  import { writeUniqueMarkdown } from "../util/fs.js";
3
3
  import { timestampedBasename } from "../util/timestamp-slug.js";
4
+ import { yamlDoubleQuoted } from "../util/yaml.js";
5
+ function buildNoteContent(text, topics) {
6
+ if (!topics || topics.length === 0)
7
+ return text;
8
+ const items = topics.map((t) => yamlDoubleQuoted(t)).join(", ");
9
+ return `---\ntopics: [${items}]\n---\n\n${text}`;
10
+ }
4
11
  /**
5
12
  * Writes a new note markdown file under `notesDir` (created if missing).
6
13
  * Never overwrites — exclusive create with `_NN` on collision.
@@ -10,6 +17,7 @@ export async function writeNote(notesDir, text, options = {}) {
10
17
  const now = options.now ?? new Date();
11
18
  await mkdir(notesDir, { recursive: true });
12
19
  const basename = timestampedBasename(text, { title: options.title, now });
13
- return writeUniqueMarkdown(notesDir, basename, text);
20
+ const content = buildNoteContent(text, options.topics);
21
+ return writeUniqueMarkdown(notesDir, basename, content);
14
22
  }
15
23
  //# sourceMappingURL=write-note.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"write-note.js","sourceRoot":"","sources":["../../src/vault/write-note.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAOhE;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,QAAgB,EAChB,IAAY,EACZ,UAA4B,EAAE;IAE9B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IACtC,MAAM,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3C,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1E,OAAO,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;AACvD,CAAC"}
1
+ {"version":3,"file":"write-note.js","sourceRoot":"","sources":["../../src/vault/write-note.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AASnD,SAAS,gBAAgB,CAAC,IAAY,EAAE,MAAiB;IACvD,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAChD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChE,OAAO,iBAAiB,KAAK,aAAa,IAAI,EAAE,CAAC;AACnD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,QAAgB,EAChB,IAAY,EACZ,UAA4B,EAAE;IAE9B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IACtC,MAAM,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3C,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1E,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACvD,OAAO,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC1D,CAAC"}
@@ -4,6 +4,8 @@ export interface WritePlanOptions {
4
4
  projectId: string;
5
5
  /** When true, overwrite an existing plan (preserving original `created`). */
6
6
  force?: boolean;
7
+ /** 3-5 topic keywords for search (flat list, omitted when empty/unset). */
8
+ topics?: string[];
7
9
  /** Timestamp for `created` / `updated` (default: now). */
8
10
  now?: Date;
9
11
  }
@@ -15,6 +17,7 @@ export interface WritePlanResult {
15
17
  /**
16
18
  * Overwrites an existing plan at an absolute path.
17
19
  * Preserves original `created` (falls back to `now` if missing) and sets `updated`.
20
+ * Omitting `topics` keeps existing frontmatter topics; passing `topics` replaces them.
18
21
  * Caller must validate the path (e.g. inside this project's `plans/` dir).
19
22
  */
20
23
  export declare function updatePlanAtPath(filePath: string, body: string, options: Omit<WritePlanOptions, "force">): Promise<WritePlanResult>;
@@ -23,6 +26,7 @@ export declare function updatePlanAtPath(filePath: string, body: string, options
23
26
  * Target is always `plansDir/<name>.md` — no collision suffixes.
24
27
  * Without `force`, an existing file is left untouched and status `"exists"` is returned.
25
28
  * With `force`, overwrites the body, preserves original `created`, and sets `updated`.
29
+ * Omitting `topics` on overwrite keeps existing frontmatter topics.
26
30
  */
27
31
  export declare function writePlan(plansDir: string, name: string, body: string, options: WritePlanOptions): Promise<WritePlanResult>;
28
32
  //# sourceMappingURL=write-plan.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"write-plan.d.ts","sourceRoot":"","sources":["../../src/vault/write-plan.ts"],"names":[],"mappings":"AAMA,qCAAqC;AACrC,MAAM,WAAW,gBAAgB;IAC/B,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,0DAA0D;IAC1D,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,aAAa,GAAG,QAAQ,CAAC;AAEnE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;CACzB;AAmBD;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC,GACvC,OAAO,CAAC,eAAe,CAAC,CAgB1B;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,eAAe,CAAC,CAuB1B"}
1
+ {"version":3,"file":"write-plan.d.ts","sourceRoot":"","sources":["../../src/vault/write-plan.ts"],"names":[],"mappings":"AAMA,qCAAqC;AACrC,MAAM,WAAW,gBAAgB;IAC/B,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,0DAA0D;IAC1D,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,aAAa,GAAG,QAAQ,CAAC;AAEnE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;CACzB;AAwBD;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC,GACvC,OAAO,CAAC,eAAe,CAAC,CAkB1B;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,eAAe,CAAC,CA4B1B"}
@@ -12,12 +12,17 @@ function buildFrontmatter(options) {
12
12
  if (options.updated) {
13
13
  lines.push(`updated: ${yamlDoubleQuoted(options.updated)}`);
14
14
  }
15
+ if (options.topics && options.topics.length > 0) {
16
+ const items = options.topics.map((t) => yamlDoubleQuoted(t)).join(", ");
17
+ lines.push(`topics: [${items}]`);
18
+ }
15
19
  lines.push("---");
16
20
  return `${lines.join("\n")}\n\n`;
17
21
  }
18
22
  /**
19
23
  * Overwrites an existing plan at an absolute path.
20
24
  * Preserves original `created` (falls back to `now` if missing) and sets `updated`.
25
+ * Omitting `topics` keeps existing frontmatter topics; passing `topics` replaces them.
21
26
  * Caller must validate the path (e.g. inside this project's `plans/` dir).
22
27
  */
23
28
  export async function updatePlanAtPath(filePath, body, options) {
@@ -26,10 +31,12 @@ export async function updatePlanAtPath(filePath, body, options) {
26
31
  const fm = parseHandoffFrontmatter(existing);
27
32
  const created = fm.created ?? now.toISOString();
28
33
  const updated = now.toISOString();
34
+ const topics = options.topics ?? fm.topics;
29
35
  const content = buildFrontmatter({
30
36
  projectId: options.projectId,
31
37
  created,
32
38
  updated,
39
+ topics,
33
40
  }) + body;
34
41
  await writeFile(filePath, content, "utf8");
35
42
  return { path: filePath, status: "overwritten" };
@@ -39,6 +46,7 @@ export async function updatePlanAtPath(filePath, body, options) {
39
46
  * Target is always `plansDir/<name>.md` — no collision suffixes.
40
47
  * Without `force`, an existing file is left untouched and status `"exists"` is returned.
41
48
  * With `force`, overwrites the body, preserves original `created`, and sets `updated`.
49
+ * Omitting `topics` on overwrite keeps existing frontmatter topics.
42
50
  */
43
51
  export async function writePlan(plansDir, name, body, options) {
44
52
  const now = options.now ?? new Date();
@@ -49,11 +57,16 @@ export async function writePlan(plansDir, name, body, options) {
49
57
  return { path: filePath, status: "exists" };
50
58
  }
51
59
  if (exists && options.force) {
52
- return updatePlanAtPath(filePath, body, { projectId: options.projectId, now });
60
+ return updatePlanAtPath(filePath, body, {
61
+ projectId: options.projectId,
62
+ topics: options.topics,
63
+ now,
64
+ });
53
65
  }
54
66
  const content = buildFrontmatter({
55
67
  projectId: options.projectId,
56
68
  created: now.toISOString(),
69
+ topics: options.topics,
57
70
  }) + body;
58
71
  await writeFile(filePath, content, "utf8");
59
72
  return { path: filePath, status: "created" };
@@ -1 +1 @@
1
- {"version":3,"file":"write-plan.js","sourceRoot":"","sources":["../../src/vault/write-plan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAmBnD,SAAS,gBAAgB,CAAC,OAIzB;IACC,MAAM,KAAK,GAAG;QACZ,KAAK;QACL,YAAY,gBAAgB,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE;QACjD,YAAY,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;KAChD,CAAC;IACF,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,YAAY,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;AACnC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,QAAgB,EAChB,IAAY,EACZ,OAAwC;IAExC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IACtC,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,EAAE,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;IAChD,MAAM,OAAO,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAElC,MAAM,OAAO,GACX,gBAAgB,CAAC;QACf,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,OAAO;QACP,OAAO;KACR,CAAC,GAAG,IAAI,CAAC;IAEZ,MAAM,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC3C,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;AACnD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,QAAgB,EAChB,IAAY,EACZ,IAAY,EACZ,OAAyB;IAEzB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IACtC,MAAM,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,IAAI,KAAK,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;IAE1C,IAAI,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAC7B,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IAC9C,CAAC;IAED,IAAI,MAAM,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAC5B,OAAO,gBAAgB,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IACjF,CAAC;IAED,MAAM,OAAO,GACX,gBAAgB,CAAC;QACf,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,OAAO,EAAE,GAAG,CAAC,WAAW,EAAE;KAC3B,CAAC,GAAG,IAAI,CAAC;IAEZ,MAAM,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC3C,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC/C,CAAC"}
1
+ {"version":3,"file":"write-plan.js","sourceRoot":"","sources":["../../src/vault/write-plan.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "grounder",
3
- "version": "0.3.0",
4
- "description": "Connect git projects to Obsidian dev vaults for AI agent memory",
3
+ "version": "0.5.0",
4
+ "description": "Connect projects to an Obsidian vault (or any markdown folder) for AI agent memory — session handoffs, plans, and notes in files you own",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "grounder": "./dist/cli.js"
@@ -16,9 +16,18 @@
16
16
  "keywords": [
17
17
  "obsidian",
18
18
  "cursor",
19
- "mcp",
19
+ "claude-code",
20
+ "handoff",
21
+ "developer-tools",
20
22
  "ai",
21
- "cli"
23
+ "cli",
24
+ "agent-memory",
25
+ "local-first",
26
+ "vault",
27
+ "session-handoff",
28
+ "context-management",
29
+ "second-brain",
30
+ "pkm"
22
31
  ],
23
32
  "license": "MIT",
24
33
  "repository": {
@@ -1,11 +1,37 @@
1
- Save a note to the Obsidian vault for this project.
1
+ Save a note to the markdown vault for this project.
2
2
 
3
- Run from the linked project folder or any subdirectory beneath it:
3
+ Distill the instruction after `/grounder-note` into a clean note body — not a chat transcript.
4
+ Do not dump tool traces, full conversation, or false starts.
4
5
 
5
- {{GROUNDER_CLI}} note "<user text>"
6
+ **Special case: the instruction asks to view existing notes, not write one** (`list`, `list 3 oldest`, `show notes`, etc.) → run `{{GROUNDER_CLI}} note list --limit <N> --markdown` (N = count named, else 5; ignore order words like "oldest" — output is always newest-first, never resort or relabel it) and stop — no note write. Relay the CLI stdout as-is (it already includes the count header; title lines are clickable `[relativePath](fileUri)` links).
7
+
8
+ Rules:
9
+ - Default → distill the args into a clean note body
10
+ - Exact wording marked to keep (quoted, "save exactly:"/"verbatim:", or a fenced block) → use that verbatim instead, unmodified
11
+ - Empty args → distill the central point of the current thread instead
12
+
13
+ Then run from the linked project folder or any subdirectory beneath it:
14
+
15
+ {{GROUNDER_CLI}} note "<body>"
16
+
17
+ Optional short title slug (filename):
18
+
19
+ {{GROUNDER_CLI}} note --title <slug> "<body>"
20
+
21
+ Always include `--topics` with 3-5 comma-separated lowercase keywords that capture the note's core concepts (e.g. `--topics "schema,migration,postgres"`). Pick terms a future search would use — concrete nouns and technical terms, not verbs or filler:
22
+
23
+ {{GROUNDER_CLI}} note --topics "keyword1,keyword2,keyword3" "<body>"
24
+
25
+ For multi-line bodies, prefer a shell heredoc so quoting does not break:
26
+
27
+ ```bash
28
+ {{GROUNDER_CLI}} note "$(cat <<'EOF'
29
+
30
+ EOF
31
+ )"
32
+ ```
6
33
 
7
34
  The vault is outside the workspace — grant shell permissions if Claude Code prompts you.
8
35
 
9
- Use the text after `/grounder-note` as the note body.
10
36
  Do not compute vault paths or write files yourself — the CLI handles it.
11
37
  Report the CLI output path from stdout to the user.