grounder 0.4.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 (138) hide show
  1. package/README.md +148 -270
  2. package/dist/agents/claude.d.ts.map +1 -1
  3. package/dist/agents/claude.js +2 -1
  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 +2 -1
  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/cli.js +11 -10
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/apply-agent-installs.d.ts +2 -2
  14. package/dist/commands/apply-agent-installs.d.ts.map +1 -1
  15. package/dist/commands/apply-agent-installs.js +1 -1
  16. package/dist/commands/doctor.d.ts.map +1 -1
  17. package/dist/commands/doctor.js +7 -4
  18. package/dist/commands/doctor.js.map +1 -1
  19. package/dist/commands/handoff/list.d.ts +23 -13
  20. package/dist/commands/handoff/list.d.ts.map +1 -1
  21. package/dist/commands/handoff/list.js +28 -16
  22. package/dist/commands/handoff/list.js.map +1 -1
  23. package/dist/commands/handoff.d.ts +2 -0
  24. package/dist/commands/handoff.d.ts.map +1 -1
  25. package/dist/commands/handoff.js +10 -1
  26. package/dist/commands/handoff.js.map +1 -1
  27. package/dist/commands/link.d.ts +12 -0
  28. package/dist/commands/link.d.ts.map +1 -0
  29. package/dist/commands/{repo/init.js → link.js} +35 -27
  30. package/dist/commands/link.js.map +1 -0
  31. package/dist/commands/migrate.js +3 -5
  32. package/dist/commands/migrate.js.map +1 -1
  33. package/dist/commands/note/list.d.ts +17 -9
  34. package/dist/commands/note/list.d.ts.map +1 -1
  35. package/dist/commands/note/list.js +19 -14
  36. package/dist/commands/note/list.js.map +1 -1
  37. package/dist/commands/note.d.ts +2 -0
  38. package/dist/commands/note.d.ts.map +1 -1
  39. package/dist/commands/note.js +10 -1
  40. package/dist/commands/note.js.map +1 -1
  41. package/dist/commands/output.d.ts +22 -2
  42. package/dist/commands/output.d.ts.map +1 -1
  43. package/dist/commands/output.js +18 -6
  44. package/dist/commands/output.js.map +1 -1
  45. package/dist/commands/plan/list.d.ts +20 -12
  46. package/dist/commands/plan/list.d.ts.map +1 -1
  47. package/dist/commands/plan/list.js +22 -17
  48. package/dist/commands/plan/list.js.map +1 -1
  49. package/dist/commands/plan.d.ts +2 -0
  50. package/dist/commands/plan.d.ts.map +1 -1
  51. package/dist/commands/plan.js +12 -2
  52. package/dist/commands/plan.js.map +1 -1
  53. package/dist/commands/require-linked.js +2 -2
  54. package/dist/commands/require-linked.js.map +1 -1
  55. package/dist/commands/search.d.ts +23 -0
  56. package/dist/commands/search.d.ts.map +1 -0
  57. package/dist/commands/search.js +277 -0
  58. package/dist/commands/search.js.map +1 -0
  59. package/dist/commands/{vault/init.d.ts → setup.d.ts} +5 -4
  60. package/dist/commands/setup.d.ts.map +1 -0
  61. package/dist/commands/setup.js +137 -0
  62. package/dist/commands/setup.js.map +1 -0
  63. package/dist/commands/status.d.ts.map +1 -1
  64. package/dist/commands/status.js +24 -15
  65. package/dist/commands/status.js.map +1 -1
  66. package/dist/commands/upgrade-banner.d.ts +2 -2
  67. package/dist/commands/upgrade-banner.js +2 -2
  68. package/dist/connector/home.d.ts +12 -0
  69. package/dist/connector/home.d.ts.map +1 -1
  70. package/dist/connector/home.js +26 -2
  71. package/dist/connector/home.js.map +1 -1
  72. package/dist/connector/state.d.ts +1 -1
  73. package/dist/connector/state.d.ts.map +1 -1
  74. package/dist/connector/vault.d.ts +2 -0
  75. package/dist/connector/vault.d.ts.map +1 -1
  76. package/dist/connector/vault.js +6 -1
  77. package/dist/connector/vault.js.map +1 -1
  78. package/dist/help.d.ts +3 -3
  79. package/dist/help.d.ts.map +1 -1
  80. package/dist/help.js +70 -51
  81. package/dist/help.js.map +1 -1
  82. package/dist/util/frontmatter.d.ts +9 -7
  83. package/dist/util/frontmatter.d.ts.map +1 -1
  84. package/dist/util/frontmatter.js +29 -4
  85. package/dist/util/frontmatter.js.map +1 -1
  86. package/dist/util/path.d.ts +23 -0
  87. package/dist/util/path.d.ts.map +1 -1
  88. package/dist/util/path.js +39 -0
  89. package/dist/util/path.js.map +1 -1
  90. package/dist/vault/list-handoffs.d.ts +3 -1
  91. package/dist/vault/list-handoffs.d.ts.map +1 -1
  92. package/dist/vault/list-handoffs.js +17 -15
  93. package/dist/vault/list-handoffs.js.map +1 -1
  94. package/dist/vault/list-markdown.d.ts +8 -0
  95. package/dist/vault/list-markdown.d.ts.map +1 -0
  96. package/dist/vault/list-markdown.js +35 -0
  97. package/dist/vault/list-markdown.js.map +1 -0
  98. package/dist/vault/list-notes.d.ts +5 -5
  99. package/dist/vault/list-notes.d.ts.map +1 -1
  100. package/dist/vault/list-notes.js +12 -22
  101. package/dist/vault/list-notes.js.map +1 -1
  102. package/dist/vault/list-plans.d.ts +2 -2
  103. package/dist/vault/list-plans.d.ts.map +1 -1
  104. package/dist/vault/list-plans.js +9 -19
  105. package/dist/vault/list-plans.js.map +1 -1
  106. package/dist/vault/search.d.ts +55 -0
  107. package/dist/vault/search.d.ts.map +1 -0
  108. package/dist/vault/search.js +328 -0
  109. package/dist/vault/search.js.map +1 -0
  110. package/dist/vault/write-handoff.d.ts +2 -0
  111. package/dist/vault/write-handoff.d.ts.map +1 -1
  112. package/dist/vault/write-handoff.js +5 -0
  113. package/dist/vault/write-handoff.js.map +1 -1
  114. package/dist/vault/write-note.d.ts +2 -0
  115. package/dist/vault/write-note.d.ts.map +1 -1
  116. package/dist/vault/write-note.js +9 -1
  117. package/dist/vault/write-note.js.map +1 -1
  118. package/dist/vault/write-plan.d.ts +4 -0
  119. package/dist/vault/write-plan.d.ts.map +1 -1
  120. package/dist/vault/write-plan.js +14 -1
  121. package/dist/vault/write-plan.js.map +1 -1
  122. package/package.json +10 -3
  123. package/templates/agents/claude/commands/grounder-note.md +6 -2
  124. package/templates/agents/claude/commands/grounder-plan.md +7 -5
  125. package/templates/agents/claude/commands/grounder-search.md +144 -0
  126. package/templates/agents/claude/commands/grounder-task-handoff.md +5 -1
  127. package/templates/agents/claude/commands/grounder-task.md +2 -2
  128. package/templates/agents/cursor/commands/grounder-note.md +6 -2
  129. package/templates/agents/cursor/commands/grounder-plan.md +7 -5
  130. package/templates/agents/cursor/commands/grounder-search.md +144 -0
  131. package/templates/agents/cursor/commands/grounder-task-handoff.md +5 -1
  132. package/templates/agents/cursor/commands/grounder-task.md +2 -2
  133. package/dist/commands/repo/init.d.ts +0 -11
  134. package/dist/commands/repo/init.d.ts.map +0 -1
  135. package/dist/commands/repo/init.js.map +0 -1
  136. package/dist/commands/vault/init.d.ts.map +0 -1
  137. package/dist/commands/vault/init.js +0 -90
  138. package/dist/commands/vault/init.js.map +0 -1
@@ -1,36 +1,26 @@
1
- import { readdir, stat } from "node:fs/promises";
2
- import path from "node:path";
1
+ import { stat } from "node:fs/promises";
2
+ import { vaultRelativePath } from "../util/path.js";
3
+ import { listMarkdownFiles } from "./list-markdown.js";
3
4
  /**
4
- * Lists note markdown files under `notesDir`, newest mtime first.
5
+ * Lists note markdown files under `notesDir` recursively, newest mtime first.
5
6
  * Returns absolute paths. Missing or empty dirs yield `[]`.
6
- * Ties break by filename descending for stable output.
7
+ * Ties break by vault-relative path descending for stable output.
7
8
  *
8
- * Filename-descending alone is a viable alternative for notes (timestamp
9
- * prefixes, like `listHandoffs`), but this intentionally matches `listPlans`
10
- * for consistency across list commands.
9
+ * Matches {@link listPlans} sorting for consistency across list commands
10
+ * (filename-descending alone is viable for timestamp-prefixed notes, like
11
+ * {@link listHandoffs}).
11
12
  */
12
13
  export async function listNotes(notesDir, options = {}) {
13
- let entries;
14
- try {
15
- entries = await readdir(notesDir);
16
- }
17
- catch (error) {
18
- if (error.code === "ENOENT") {
19
- return [];
20
- }
21
- throw error;
22
- }
23
- const mdNames = entries.filter((name) => name.endsWith(".md"));
24
- const withMtime = await Promise.all(mdNames.map(async (name) => {
25
- const filePath = path.join(notesDir, name);
14
+ const mdPaths = await listMarkdownFiles(notesDir);
15
+ const withMtime = await Promise.all(mdPaths.map(async (filePath) => {
26
16
  const { mtimeMs } = await stat(filePath);
27
- return { filePath, name, mtimeMs };
17
+ return { filePath, rel: vaultRelativePath(notesDir, filePath), mtimeMs };
28
18
  }));
29
19
  withMtime.sort((a, b) => {
30
20
  if (a.mtimeMs !== b.mtimeMs) {
31
21
  return b.mtimeMs - a.mtimeMs;
32
22
  }
33
- return a.name < b.name ? 1 : a.name > b.name ? -1 : 0;
23
+ return a.rel < b.rel ? 1 : a.rel > b.rel ? -1 : 0;
34
24
  });
35
25
  const paths = withMtime.map((entry) => entry.filePath);
36
26
  if (options.limit === undefined) {
@@ -1 +1 @@
1
- {"version":3,"file":"list-notes.js","sourceRoot":"","sources":["../../src/vault/list-notes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,IAAI,MAAM,WAAW,CAAC;AAO7B;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,QAAgB,EAChB,UAA4B,EAAE;IAE9B,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvD,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CACjC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC3C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACrC,CAAC,CAAC,CACH,CAAC;IAEF,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACtB,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;YAC5B,OAAO,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC;QAC/B,CAAC;QACD,OAAO,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAEvD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC"}
1
+ {"version":3,"file":"list-notes.js","sourceRoot":"","sources":["../../src/vault/list-notes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAOvD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,QAAgB,EAChB,UAA4B,EAAE;IAE9B,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CACjC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;QAC7B,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IAC3E,CAAC,CAAC,CACH,CAAC;IAEF,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACtB,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;YAC5B,OAAO,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC;QAC/B,CAAC;QACD,OAAO,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAEvD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC"}
@@ -3,9 +3,9 @@ export interface ListPlansOptions {
3
3
  limit?: number;
4
4
  }
5
5
  /**
6
- * Lists plan markdown files under `plansDir`, newest mtime first.
6
+ * Lists plan markdown files under `plansDir` recursively, newest mtime first.
7
7
  * Returns absolute paths. Missing or empty dirs yield `[]`.
8
- * Ties break by filename descending for stable output.
8
+ * Ties break by vault-relative path descending for stable output.
9
9
  */
10
10
  export declare function listPlans(plansDir: string, options?: ListPlansOptions): Promise<string[]>;
11
11
  //# sourceMappingURL=list-plans.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"list-plans.d.ts","sourceRoot":"","sources":["../../src/vault/list-plans.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,MAAM,EAAE,CAAC,CAoCnB"}
1
+ {"version":3,"file":"list-plans.d.ts","sourceRoot":"","sources":["../../src/vault/list-plans.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,MAAM,EAAE,CAAC,CAyBnB"}
@@ -1,32 +1,22 @@
1
- import { readdir, stat } from "node:fs/promises";
2
- import path from "node:path";
1
+ import { stat } from "node:fs/promises";
2
+ import { vaultRelativePath } from "../util/path.js";
3
+ import { listMarkdownFiles } from "./list-markdown.js";
3
4
  /**
4
- * Lists plan markdown files under `plansDir`, newest mtime first.
5
+ * Lists plan markdown files under `plansDir` recursively, newest mtime first.
5
6
  * Returns absolute paths. Missing or empty dirs yield `[]`.
6
- * Ties break by filename descending for stable output.
7
+ * Ties break by vault-relative path descending for stable output.
7
8
  */
8
9
  export async function listPlans(plansDir, options = {}) {
9
- let entries;
10
- try {
11
- entries = await readdir(plansDir);
12
- }
13
- catch (error) {
14
- if (error.code === "ENOENT") {
15
- return [];
16
- }
17
- throw error;
18
- }
19
- const mdNames = entries.filter((name) => name.endsWith(".md"));
20
- const withMtime = await Promise.all(mdNames.map(async (name) => {
21
- const filePath = path.join(plansDir, name);
10
+ const mdPaths = await listMarkdownFiles(plansDir);
11
+ const withMtime = await Promise.all(mdPaths.map(async (filePath) => {
22
12
  const { mtimeMs } = await stat(filePath);
23
- return { filePath, name, mtimeMs };
13
+ return { filePath, rel: vaultRelativePath(plansDir, filePath), mtimeMs };
24
14
  }));
25
15
  withMtime.sort((a, b) => {
26
16
  if (a.mtimeMs !== b.mtimeMs) {
27
17
  return b.mtimeMs - a.mtimeMs;
28
18
  }
29
- return a.name < b.name ? 1 : a.name > b.name ? -1 : 0;
19
+ return a.rel < b.rel ? 1 : a.rel > b.rel ? -1 : 0;
30
20
  });
31
21
  const paths = withMtime.map((entry) => entry.filePath);
32
22
  if (options.limit === undefined) {
@@ -1 +1 @@
1
- {"version":3,"file":"list-plans.js","sourceRoot":"","sources":["../../src/vault/list-plans.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,IAAI,MAAM,WAAW,CAAC;AAO7B;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,QAAgB,EAChB,UAA4B,EAAE;IAE9B,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvD,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CACjC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC3C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACrC,CAAC,CAAC,CACH,CAAC;IAEF,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACtB,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;YAC5B,OAAO,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC;QAC/B,CAAC;QACD,OAAO,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAEvD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC"}
1
+ {"version":3,"file":"list-plans.js","sourceRoot":"","sources":["../../src/vault/list-plans.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAOvD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,QAAgB,EAChB,UAA4B,EAAE;IAE9B,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CACjC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;QAC7B,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IAC3E,CAAC,CAAC,CACH,CAAC;IAEF,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACtB,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;YAC5B,OAAO,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC;QAC/B,CAAC;QACD,OAAO,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAEvD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,55 @@
1
+ /** One line-level match inside a markdown file. */
2
+ export interface SearchLineHit {
3
+ line: number;
4
+ matchedTerm: string;
5
+ snippet: string;
6
+ }
7
+ /** Matches grouped by file, after ranking and per-file dedupe. */
8
+ export interface SearchFileHit {
9
+ filePath: string;
10
+ mtimeMs: number;
11
+ topicsMatch: boolean;
12
+ hits: SearchLineHit[];
13
+ /** Distinct matching terms (original spelling), longest first. */
14
+ matchedTerms: string[];
15
+ }
16
+ export interface SearchOptions {
17
+ /** Project vault root — searched recursively for `*.md`. */
18
+ rootDir: string;
19
+ /** Primary query phrase. */
20
+ query: string;
21
+ /** Extra keyword variants (deduped with query). */
22
+ terms?: readonly string[];
23
+ /** Max distinct files in the result (default 10). */
24
+ limit?: number;
25
+ /**
26
+ * Cap stored line snippets per file during scan (default 50; hard-capped at
27
+ * 50). Does not abort the tree walk; ranking still uses full hit counts.
28
+ */
29
+ maxHits?: number;
30
+ /** Context lines around each match (default 1). */
31
+ context?: number;
32
+ /** Max line hits kept per file after grouping (default 1). */
33
+ maxHitsPerFile?: number;
34
+ /** Exclude files with mtime before this date. */
35
+ since?: Date;
36
+ }
37
+ export interface SearchOutcome {
38
+ query: string;
39
+ terms: string[];
40
+ /** How many files each term matched (keyed by lowercased term). */
41
+ termHitCounts: Record<string, number>;
42
+ totalMatchCount: number;
43
+ totalFileCount: number;
44
+ files: SearchFileHit[];
45
+ truncated: boolean;
46
+ }
47
+ /**
48
+ * Case-insensitive markdown scan under `rootDir`.
49
+ * Frontmatter `topics:` matches boost file ranking; body and frontmatter lines
50
+ * both contribute line hits. Files rank by relevance (distinct terms, hit
51
+ * density, filename/path terms, topics; demotes search dogfood when query is
52
+ * unrelated), then archive, recency, folder. Pure fs — no index, no deps.
53
+ */
54
+ export declare function searchVault(options: SearchOptions): Promise<SearchOutcome>;
55
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/vault/search.ts"],"names":[],"mappings":"AAKA,mDAAmD;AACnD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,kEAAkE;AAClE,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,kEAAkE;IAClE,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iDAAiD;IACjD,KAAK,CAAC,EAAE,IAAI,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,mEAAmE;IACnE,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;CACpB;AAuOD;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAqJhF"}
@@ -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"}