gitmem-mcp 1.0.3 → 1.0.5

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 (293) hide show
  1. package/CHANGELOG.md +14 -14
  2. package/dist/commands/check.d.ts +1 -1
  3. package/dist/commands/check.js +4 -3
  4. package/dist/diagnostics/anonymizer.d.ts +1 -1
  5. package/dist/diagnostics/anonymizer.js +1 -1
  6. package/dist/diagnostics/channels.d.ts +1 -1
  7. package/dist/diagnostics/channels.js +1 -1
  8. package/dist/diagnostics/collector.d.ts +1 -1
  9. package/dist/diagnostics/collector.js +1 -1
  10. package/dist/diagnostics/index.d.ts +1 -1
  11. package/dist/diagnostics/index.js +1 -1
  12. package/dist/hooks/quick-retrieve.js +2 -1
  13. package/dist/index.js +0 -0
  14. package/dist/schemas/active-sessions.d.ts +9 -9
  15. package/dist/schemas/active-sessions.js +1 -1
  16. package/dist/schemas/common.d.ts +2 -5
  17. package/dist/schemas/common.js +13 -7
  18. package/dist/schemas/create-learning.js +1 -1
  19. package/dist/schemas/session-close.d.ts +5 -8
  20. package/dist/schemas/session-close.js +7 -3
  21. package/dist/schemas/session-start.d.ts +4 -4
  22. package/dist/schemas/session-start.js +1 -1
  23. package/dist/schemas/thread.d.ts +1 -1
  24. package/dist/schemas/thread.js +1 -1
  25. package/dist/server.d.ts +2 -2
  26. package/dist/server.js +21 -10
  27. package/dist/services/active-sessions.js +3 -2
  28. package/dist/services/agent-briefing.d.ts +1 -1
  29. package/dist/services/agent-briefing.js +1 -1
  30. package/dist/services/agent-detection.d.ts +1 -0
  31. package/dist/services/agent-detection.js +21 -12
  32. package/dist/services/analytics.d.ts +1 -1
  33. package/dist/services/analytics.js +3 -3
  34. package/dist/services/behavioral-decay.js +2 -2
  35. package/dist/services/cache.d.ts +1 -1
  36. package/dist/services/cache.js +1 -1
  37. package/dist/services/compliance-validator.d.ts +1 -1
  38. package/dist/services/compliance-validator.js +5 -5
  39. package/dist/services/config.d.ts +1 -1
  40. package/dist/services/config.js +1 -1
  41. package/dist/services/file-lock.js +12 -0
  42. package/dist/services/gitmem-dir.d.ts +17 -4
  43. package/dist/services/gitmem-dir.js +43 -9
  44. package/dist/services/local-file-storage.d.ts +1 -1
  45. package/dist/services/local-file-storage.js +4 -3
  46. package/dist/services/local-vector-search.d.ts +1 -1
  47. package/dist/services/local-vector-search.js +2 -2
  48. package/dist/services/metrics.d.ts +6 -6
  49. package/dist/services/metrics.js +8 -8
  50. package/dist/services/session-state.d.ts +7 -7
  51. package/dist/services/session-state.js +19 -7
  52. package/dist/services/startup.d.ts +1 -1
  53. package/dist/services/startup.js +3 -2
  54. package/dist/services/supabase-client.d.ts +17 -6
  55. package/dist/services/supabase-client.js +44 -8
  56. package/dist/services/thread-manager.d.ts +1 -1
  57. package/dist/services/thread-manager.js +5 -5
  58. package/dist/services/thread-supabase.d.ts +1 -1
  59. package/dist/services/thread-supabase.js +2 -2
  60. package/dist/services/transcript-chunker.d.ts +1 -1
  61. package/dist/services/transcript-chunker.js +1 -1
  62. package/dist/services/variant-assignment.d.ts +6 -6
  63. package/dist/services/variant-assignment.js +9 -8
  64. package/dist/tools/analyze.d.ts +2 -2
  65. package/dist/tools/analyze.js +2 -2
  66. package/dist/tools/archive-learning.js +36 -22
  67. package/dist/tools/confirm-scars.js +1 -1
  68. package/dist/tools/create-decision.d.ts +1 -1
  69. package/dist/tools/create-decision.js +2 -2
  70. package/dist/tools/create-learning.d.ts +1 -1
  71. package/dist/tools/create-learning.js +4 -4
  72. package/dist/tools/create-linear-issue.d.ts +18 -0
  73. package/dist/tools/create-linear-issue.js +197 -0
  74. package/dist/tools/create-thread.d.ts +1 -1
  75. package/dist/tools/create-thread.js +2 -2
  76. package/dist/tools/definitions.js +50 -48
  77. package/dist/tools/get-transcript.d.ts +1 -1
  78. package/dist/tools/get-transcript.js +1 -1
  79. package/dist/tools/graph-traverse.d.ts +1 -1
  80. package/dist/tools/graph-traverse.js +18 -16
  81. package/dist/tools/list-threads.d.ts +2 -2
  82. package/dist/tools/list-threads.js +4 -4
  83. package/dist/tools/log.d.ts +1 -1
  84. package/dist/tools/log.js +1 -1
  85. package/dist/tools/prepare-context.d.ts +1 -1
  86. package/dist/tools/prepare-context.js +1 -1
  87. package/dist/tools/recall.d.ts +4 -4
  88. package/dist/tools/recall.js +25 -25
  89. package/dist/tools/record-scar-usage-batch.js +2 -2
  90. package/dist/tools/record-scar-usage.d.ts +1 -1
  91. package/dist/tools/record-scar-usage.js +3 -3
  92. package/dist/tools/resolve-thread.d.ts +2 -2
  93. package/dist/tools/resolve-thread.js +3 -3
  94. package/dist/tools/save-transcript.d.ts +1 -1
  95. package/dist/tools/save-transcript.js +1 -1
  96. package/dist/tools/search.d.ts +1 -1
  97. package/dist/tools/search.js +1 -1
  98. package/dist/tools/session-close.d.ts +1 -1
  99. package/dist/tools/session-close.js +31 -31
  100. package/dist/tools/session-start.d.ts +5 -5
  101. package/dist/tools/session-start.js +63 -61
  102. package/dist/types/index.d.ts +13 -13
  103. package/hooks/.claude-plugin/plugin.json +1 -1
  104. package/hooks/scripts/post-tool-use.sh +1 -1
  105. package/hooks/scripts/recall-check.sh +1 -1
  106. package/hooks/scripts/session-close-check.sh +1 -1
  107. package/hooks/scripts/session-start.sh +1 -1
  108. package/package.json +6 -3
  109. package/dist/commands/check.d.ts.map +0 -1
  110. package/dist/commands/check.js.map +0 -1
  111. package/dist/constants/closing-questions.d.ts.map +0 -1
  112. package/dist/constants/closing-questions.js.map +0 -1
  113. package/dist/diagnostics/anonymizer.d.ts.map +0 -1
  114. package/dist/diagnostics/anonymizer.js.map +0 -1
  115. package/dist/diagnostics/channels.d.ts.map +0 -1
  116. package/dist/diagnostics/channels.js.map +0 -1
  117. package/dist/diagnostics/collector.d.ts.map +0 -1
  118. package/dist/diagnostics/collector.js.map +0 -1
  119. package/dist/diagnostics/index.d.ts.map +0 -1
  120. package/dist/diagnostics/index.js.map +0 -1
  121. package/dist/hooks/format-utils.d.ts.map +0 -1
  122. package/dist/hooks/format-utils.js.map +0 -1
  123. package/dist/hooks/quick-retrieve.d.ts.map +0 -1
  124. package/dist/hooks/quick-retrieve.js.map +0 -1
  125. package/dist/index.d.ts.map +0 -1
  126. package/dist/index.js.map +0 -1
  127. package/dist/schemas/absorb-observations.d.ts.map +0 -1
  128. package/dist/schemas/absorb-observations.js.map +0 -1
  129. package/dist/schemas/active-sessions.d.ts.map +0 -1
  130. package/dist/schemas/active-sessions.js.map +0 -1
  131. package/dist/schemas/analyze.d.ts.map +0 -1
  132. package/dist/schemas/analyze.js.map +0 -1
  133. package/dist/schemas/common.d.ts.map +0 -1
  134. package/dist/schemas/common.js.map +0 -1
  135. package/dist/schemas/create-decision.d.ts.map +0 -1
  136. package/dist/schemas/create-decision.js.map +0 -1
  137. package/dist/schemas/create-learning.d.ts.map +0 -1
  138. package/dist/schemas/create-learning.js.map +0 -1
  139. package/dist/schemas/get-transcript.d.ts.map +0 -1
  140. package/dist/schemas/get-transcript.js.map +0 -1
  141. package/dist/schemas/index.d.ts.map +0 -1
  142. package/dist/schemas/index.js.map +0 -1
  143. package/dist/schemas/log.d.ts.map +0 -1
  144. package/dist/schemas/log.js.map +0 -1
  145. package/dist/schemas/prepare-context.d.ts.map +0 -1
  146. package/dist/schemas/prepare-context.js.map +0 -1
  147. package/dist/schemas/recall.d.ts.map +0 -1
  148. package/dist/schemas/recall.js.map +0 -1
  149. package/dist/schemas/record-scar-usage-batch.d.ts.map +0 -1
  150. package/dist/schemas/record-scar-usage-batch.js.map +0 -1
  151. package/dist/schemas/record-scar-usage.d.ts.map +0 -1
  152. package/dist/schemas/record-scar-usage.js.map +0 -1
  153. package/dist/schemas/registry.d.ts.map +0 -1
  154. package/dist/schemas/registry.js.map +0 -1
  155. package/dist/schemas/save-transcript.d.ts.map +0 -1
  156. package/dist/schemas/save-transcript.js.map +0 -1
  157. package/dist/schemas/search-transcripts.d.ts.map +0 -1
  158. package/dist/schemas/search-transcripts.js.map +0 -1
  159. package/dist/schemas/search.d.ts.map +0 -1
  160. package/dist/schemas/search.js.map +0 -1
  161. package/dist/schemas/session-close.d.ts.map +0 -1
  162. package/dist/schemas/session-close.js.map +0 -1
  163. package/dist/schemas/session-start.d.ts.map +0 -1
  164. package/dist/schemas/session-start.js.map +0 -1
  165. package/dist/schemas/thread.d.ts.map +0 -1
  166. package/dist/schemas/thread.js.map +0 -1
  167. package/dist/server.d.ts.map +0 -1
  168. package/dist/server.js.map +0 -1
  169. package/dist/services/active-sessions.d.ts.map +0 -1
  170. package/dist/services/active-sessions.js.map +0 -1
  171. package/dist/services/agent-briefing.d.ts.map +0 -1
  172. package/dist/services/agent-briefing.js.map +0 -1
  173. package/dist/services/agent-detection.d.ts.map +0 -1
  174. package/dist/services/agent-detection.js.map +0 -1
  175. package/dist/services/analytics.d.ts.map +0 -1
  176. package/dist/services/analytics.js.map +0 -1
  177. package/dist/services/behavioral-decay.d.ts.map +0 -1
  178. package/dist/services/behavioral-decay.js.map +0 -1
  179. package/dist/services/bm25.d.ts.map +0 -1
  180. package/dist/services/bm25.js.map +0 -1
  181. package/dist/services/cache.d.ts.map +0 -1
  182. package/dist/services/cache.js.map +0 -1
  183. package/dist/services/cache.test.d.ts +0 -8
  184. package/dist/services/cache.test.d.ts.map +0 -1
  185. package/dist/services/cache.test.js +0 -267
  186. package/dist/services/cache.test.js.map +0 -1
  187. package/dist/services/compliance-validator.d.ts.map +0 -1
  188. package/dist/services/compliance-validator.js.map +0 -1
  189. package/dist/services/config.d.ts.map +0 -1
  190. package/dist/services/config.js.map +0 -1
  191. package/dist/services/display-protocol.d.ts.map +0 -1
  192. package/dist/services/display-protocol.js.map +0 -1
  193. package/dist/services/effect-tracker.d.ts.map +0 -1
  194. package/dist/services/effect-tracker.js.map +0 -1
  195. package/dist/services/embedding.d.ts.map +0 -1
  196. package/dist/services/embedding.js.map +0 -1
  197. package/dist/services/file-lock.d.ts.map +0 -1
  198. package/dist/services/file-lock.js.map +0 -1
  199. package/dist/services/gitmem-dir.d.ts.map +0 -1
  200. package/dist/services/gitmem-dir.js.map +0 -1
  201. package/dist/services/local-file-storage.d.ts.map +0 -1
  202. package/dist/services/local-file-storage.js.map +0 -1
  203. package/dist/services/local-vector-search.d.ts.map +0 -1
  204. package/dist/services/local-vector-search.js.map +0 -1
  205. package/dist/services/metrics.d.ts.map +0 -1
  206. package/dist/services/metrics.js.map +0 -1
  207. package/dist/services/session-state.d.ts.map +0 -1
  208. package/dist/services/session-state.js.map +0 -1
  209. package/dist/services/startup.d.ts.map +0 -1
  210. package/dist/services/startup.js.map +0 -1
  211. package/dist/services/storage.d.ts.map +0 -1
  212. package/dist/services/storage.js.map +0 -1
  213. package/dist/services/supabase-client.d.ts.map +0 -1
  214. package/dist/services/supabase-client.js.map +0 -1
  215. package/dist/services/thread-dedup.d.ts.map +0 -1
  216. package/dist/services/thread-dedup.js.map +0 -1
  217. package/dist/services/thread-manager.d.ts.map +0 -1
  218. package/dist/services/thread-manager.js.map +0 -1
  219. package/dist/services/thread-suggestions.d.ts.map +0 -1
  220. package/dist/services/thread-suggestions.js.map +0 -1
  221. package/dist/services/thread-supabase.d.ts.map +0 -1
  222. package/dist/services/thread-supabase.js.map +0 -1
  223. package/dist/services/thread-vitality.d.ts.map +0 -1
  224. package/dist/services/thread-vitality.js.map +0 -1
  225. package/dist/services/tier.d.ts.map +0 -1
  226. package/dist/services/tier.js.map +0 -1
  227. package/dist/services/timezone.d.ts.map +0 -1
  228. package/dist/services/timezone.js.map +0 -1
  229. package/dist/services/transcript-chunker.d.ts.map +0 -1
  230. package/dist/services/transcript-chunker.js.map +0 -1
  231. package/dist/services/triple-writer.d.ts.map +0 -1
  232. package/dist/services/triple-writer.js.map +0 -1
  233. package/dist/services/variant-assignment.d.ts.map +0 -1
  234. package/dist/services/variant-assignment.js.map +0 -1
  235. package/dist/services/variant-generation.d.ts.map +0 -1
  236. package/dist/services/variant-generation.js.map +0 -1
  237. package/dist/tools/absorb-observations.d.ts.map +0 -1
  238. package/dist/tools/absorb-observations.js.map +0 -1
  239. package/dist/tools/analyze.d.ts.map +0 -1
  240. package/dist/tools/analyze.js.map +0 -1
  241. package/dist/tools/archive-learning.d.ts.map +0 -1
  242. package/dist/tools/archive-learning.js.map +0 -1
  243. package/dist/tools/cleanup-threads.d.ts.map +0 -1
  244. package/dist/tools/cleanup-threads.js.map +0 -1
  245. package/dist/tools/confirm-scars.d.ts.map +0 -1
  246. package/dist/tools/confirm-scars.js.map +0 -1
  247. package/dist/tools/create-decision.d.ts.map +0 -1
  248. package/dist/tools/create-decision.js.map +0 -1
  249. package/dist/tools/create-learning.d.ts.map +0 -1
  250. package/dist/tools/create-learning.js.map +0 -1
  251. package/dist/tools/create-thread.d.ts.map +0 -1
  252. package/dist/tools/create-thread.js.map +0 -1
  253. package/dist/tools/definitions.d.ts.map +0 -1
  254. package/dist/tools/definitions.js.map +0 -1
  255. package/dist/tools/dismiss-suggestion.d.ts.map +0 -1
  256. package/dist/tools/dismiss-suggestion.js.map +0 -1
  257. package/dist/tools/get-transcript.d.ts.map +0 -1
  258. package/dist/tools/get-transcript.js.map +0 -1
  259. package/dist/tools/graph-traverse.d.ts.map +0 -1
  260. package/dist/tools/graph-traverse.js.map +0 -1
  261. package/dist/tools/list-threads.d.ts.map +0 -1
  262. package/dist/tools/list-threads.js.map +0 -1
  263. package/dist/tools/log.d.ts.map +0 -1
  264. package/dist/tools/log.js.map +0 -1
  265. package/dist/tools/prepare-context.d.ts.map +0 -1
  266. package/dist/tools/prepare-context.js.map +0 -1
  267. package/dist/tools/promote-suggestion.d.ts.map +0 -1
  268. package/dist/tools/promote-suggestion.js.map +0 -1
  269. package/dist/tools/recall.d.ts.map +0 -1
  270. package/dist/tools/recall.js.map +0 -1
  271. package/dist/tools/recall.test.d.ts +0 -5
  272. package/dist/tools/recall.test.d.ts.map +0 -1
  273. package/dist/tools/recall.test.js +0 -155
  274. package/dist/tools/recall.test.js.map +0 -1
  275. package/dist/tools/record-scar-usage-batch.d.ts.map +0 -1
  276. package/dist/tools/record-scar-usage-batch.js.map +0 -1
  277. package/dist/tools/record-scar-usage.d.ts.map +0 -1
  278. package/dist/tools/record-scar-usage.js.map +0 -1
  279. package/dist/tools/resolve-thread.d.ts.map +0 -1
  280. package/dist/tools/resolve-thread.js.map +0 -1
  281. package/dist/tools/save-transcript.d.ts.map +0 -1
  282. package/dist/tools/save-transcript.js.map +0 -1
  283. package/dist/tools/search-transcripts.d.ts.map +0 -1
  284. package/dist/tools/search-transcripts.js.map +0 -1
  285. package/dist/tools/search.d.ts.map +0 -1
  286. package/dist/tools/search.js.map +0 -1
  287. package/dist/tools/session-close.d.ts.map +0 -1
  288. package/dist/tools/session-close.js.map +0 -1
  289. package/dist/tools/session-start.d.ts.map +0 -1
  290. package/dist/tools/session-start.js.map +0 -1
  291. package/dist/types/index.d.ts.map +0 -1
  292. package/dist/types/index.js.map +0 -1
  293. package/hooks/tests/test-hooks.sh +0 -577
@@ -10,7 +10,7 @@
10
10
  * - 500-employees-at-8AM (no Supabase contention during session_start)
11
11
  * - Cross-container consistency (same data = same results)
12
12
  *
13
- * Issue: OD-473
13
+ *
14
14
  */
15
15
  import type { Project } from "../types/index.js";
16
16
  /**
@@ -10,11 +10,12 @@
10
10
  * - 500-employees-at-8AM (no Supabase contention during session_start)
11
11
  * - Cross-container consistency (same data = same results)
12
12
  *
13
- * Issue: OD-473
13
+ *
14
14
  */
15
15
  import * as fs from "fs";
16
16
  import * as path from "path";
17
17
  import { isConfigured, loadScarsWithEmbeddings } from "./supabase-client.js";
18
+ import { getGitmemDir } from "./gitmem-dir.js";
18
19
  import { initializeLocalSearch, reinitializeLocalSearch, isLocalSearchReady, getLocalVectorSearch, getCacheMetadata, setCacheTtl, } from "./local-vector-search.js";
19
20
  import { getConfig, shouldUseLocalSearch } from "./config.js";
20
21
  // Track startup state — unified cache, no per-project partitioning
@@ -32,7 +33,7 @@ let backgroundRefreshInterval = null;
32
33
  */
33
34
  function persistScarsForHooks(scars) {
34
35
  try {
35
- const gitmemDir = path.join(process.cwd(), ".gitmem", "cache");
36
+ const gitmemDir = path.join(getGitmemDir(), "cache");
36
37
  if (!fs.existsSync(gitmemDir)) {
37
38
  fs.mkdirSync(gitmemDir, { recursive: true });
38
39
  }
@@ -5,9 +5,20 @@
5
5
  * agents/coda/src/services/supabase-mcp.js
6
6
  *
7
7
  * Uses JSON-RPC 2.0 protocol over HTTPS.
8
- * Integrates with CacheService for performance (OD-473).
8
+ * Integrates with CacheService for performance.
9
9
  */
10
10
  import type { Project, SupabaseListOptions, SupabaseSearchOptions } from "../types/index.js";
11
+ /**
12
+ * Build a safe PostgREST `in.(...)` filter from an array of UUIDs.
13
+ * Rejects any non-UUID values to prevent filter injection via comma-splitting.
14
+ */
15
+ export declare function safeInFilter(ids: string[]): string;
16
+ /**
17
+ * Escape PostgREST special characters in a value used within filter expressions.
18
+ * Prevents injection via characters that have structural meaning in PostgREST syntax:
19
+ * parentheses (group operators), commas (value separators), dots (operator delimiters).
20
+ */
21
+ export declare function escapePostgRESTValue(value: string): string;
11
22
  /**
12
23
  * Check if Supabase is configured
13
24
  */
@@ -54,7 +65,7 @@ export declare function directQueryAll<T = unknown>(table: string, options?: {
54
65
  order?: string;
55
66
  }, pageSize?: number, maxRows?: number): Promise<T[]>;
56
67
  /**
57
- * Knowledge triple from knowledge_triples table (OD-466)
68
+ * Knowledge triple from knowledge_triples table
58
69
  */
59
70
  export interface KnowledgeTriple {
60
71
  subject: string;
@@ -67,7 +78,7 @@ export interface KnowledgeTriple {
67
78
  source_id: string;
68
79
  }
69
80
  /**
70
- * Fetch related knowledge triples for a set of scar IDs (OD-466)
81
+ * Fetch related knowledge triples for a set of scar IDs
71
82
  *
72
83
  * Queries knowledge_triples table where source_id matches any of the provided scar IDs.
73
84
  * Returns triples grouped by source_id for easy attachment to scars.
@@ -99,7 +110,7 @@ export declare function directPatch<T = unknown>(table: string, filters: Record<
99
110
  * This bypasses ww-mcp because we need the embedding vectors for local search.
100
111
  * Returns learnings (scars, patterns, wins, anti-patterns) with full embedding data.
101
112
  *
102
- * NOTE: Changed from "scars only" to "all learning types" (OD-542 related fix)
113
+ * NOTE: Changed from "scars only" to "all learning types"
103
114
  */
104
115
  export declare function loadScarsWithEmbeddings<T = unknown>(project?: string, limit?: number): Promise<T[]>;
105
116
  /**
@@ -107,7 +118,7 @@ export declare function loadScarsWithEmbeddings<T = unknown>(project?: string, l
107
118
  */
108
119
  export declare function scarSearch<T = unknown>(query: string, matchCount?: number, project?: Project): Promise<T[]>;
109
120
  /**
110
- * Scar search with caching (OD-473)
121
+ * Scar search with caching
111
122
  *
112
123
  * Returns cached results if available, otherwise fetches and caches.
113
124
  */
@@ -117,7 +128,7 @@ export declare function cachedScarSearch<T = unknown>(query: string, matchCount?
117
128
  cache_age_ms?: number;
118
129
  }>;
119
130
  /**
120
- * List records with caching for decisions (OD-473)
131
+ * List records with caching for decisions
121
132
  */
122
133
  export declare function cachedListDecisions<T = unknown>(project?: Project, limit?: number): Promise<{
123
134
  data: T[];
@@ -5,9 +5,45 @@
5
5
  * agents/coda/src/services/supabase-mcp.js
6
6
  *
7
7
  * Uses JSON-RPC 2.0 protocol over HTTPS.
8
- * Integrates with CacheService for performance (OD-473).
8
+ * Integrates with CacheService for performance.
9
9
  */
10
10
  import { getCache } from "./cache.js";
11
+ // --- PostgREST Input Sanitization ---
12
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
13
+ /**
14
+ * Validate that a value is a UUID. Used to prevent injection in `in.(...)` filters
15
+ * where array elements are joined with commas.
16
+ */
17
+ function isValidUUID(value) {
18
+ return UUID_PATTERN.test(value);
19
+ }
20
+ /**
21
+ * Build a safe PostgREST `in.(...)` filter from an array of UUIDs.
22
+ * Rejects any non-UUID values to prevent filter injection via comma-splitting.
23
+ */
24
+ export function safeInFilter(ids) {
25
+ const valid = ids.filter(id => isValidUUID(id));
26
+ if (valid.length !== ids.length) {
27
+ console.warn(`[supabase-client] safeInFilter rejected ${ids.length - valid.length} non-UUID values`);
28
+ }
29
+ if (valid.length === 0)
30
+ return "in.()";
31
+ return `in.(${valid.join(",")})`;
32
+ }
33
+ /**
34
+ * Escape PostgREST special characters in a value used within filter expressions.
35
+ * Prevents injection via characters that have structural meaning in PostgREST syntax:
36
+ * parentheses (group operators), commas (value separators), dots (operator delimiters).
37
+ */
38
+ export function escapePostgRESTValue(value) {
39
+ // Reject null bytes
40
+ if (value.includes("\0")) {
41
+ throw new Error("PostgREST value must not contain null bytes");
42
+ }
43
+ // For ilike patterns, escape structural PostgREST chars that could break OR groups
44
+ // PostgREST uses ( ) , as structural delimiters in or=() expressions
45
+ return value.replace(/[(),]/g, "");
46
+ }
11
47
  // Configuration from environment
12
48
  const SUPABASE_URL = process.env.SUPABASE_URL || "";
13
49
  const SUPABASE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_KEY || "";
@@ -228,7 +264,7 @@ export async function directQueryAll(table, options = {}, pageSize = 1000, maxRo
228
264
  return allRows;
229
265
  }
230
266
  /**
231
- * Fetch related knowledge triples for a set of scar IDs (OD-466)
267
+ * Fetch related knowledge triples for a set of scar IDs
232
268
  *
233
269
  * Queries knowledge_triples table where source_id matches any of the provided scar IDs.
234
270
  * Returns triples grouped by source_id for easy attachment to scars.
@@ -242,7 +278,7 @@ export async function fetchRelatedTriples(scarIds) {
242
278
  const triples = await directQuery("knowledge_triples", {
243
279
  select: "subject,predicate,object,event_time,decay_weight,half_life_days,decay_floor,source_id",
244
280
  filters: {
245
- source_id: `in.(${scarIds.join(",")})`,
281
+ source_id: safeInFilter(scarIds),
246
282
  },
247
283
  });
248
284
  for (const triple of triples) {
@@ -283,7 +319,7 @@ export async function directUpsert(table, data) {
283
319
  throw new Error(`Supabase upsert error: ${response.status} - ${text.slice(0, 200)}`);
284
320
  }
285
321
  const result = await response.json();
286
- // OD-539: Validate that write actually happened
322
+ // Validate that write actually happened
287
323
  // Supabase can return 200 OK with empty array [] when:
288
324
  // - RLS policy blocks the write
289
325
  // - Constraint violation handled gracefully
@@ -341,7 +377,7 @@ export async function directPatch(table, filters, data) {
341
377
  * This bypasses ww-mcp because we need the embedding vectors for local search.
342
378
  * Returns learnings (scars, patterns, wins, anti-patterns) with full embedding data.
343
379
  *
344
- * NOTE: Changed from "scars only" to "all learning types" (OD-542 related fix)
380
+ * NOTE: Changed from "scars only" to "all learning types"
345
381
  */
346
382
  export async function loadScarsWithEmbeddings(project, limit = 500) {
347
383
  console.error(`[supabase-direct] Loading learnings with embeddings${project ? ` for project: ${project}` : " (all projects)"}`);
@@ -384,7 +420,7 @@ export async function scarSearch(query, matchCount = 5, project) {
384
420
  return result.results || [];
385
421
  }
386
422
  /**
387
- * Scar search with caching (OD-473)
423
+ * Scar search with caching
388
424
  *
389
425
  * Returns cached results if available, otherwise fetches and caches.
390
426
  */
@@ -394,7 +430,7 @@ export async function cachedScarSearch(query, matchCount = 5, project = "default
394
430
  return { results: data, cache_hit, cache_age_ms };
395
431
  }
396
432
  /**
397
- * List records with caching for decisions (OD-473)
433
+ * List records with caching for decisions
398
434
  */
399
435
  export async function cachedListDecisions(project = "default", limit = 5) {
400
436
  const cache = getCache();
@@ -424,7 +460,7 @@ export async function cachedListWins(project = "default", limit = 8, columns) {
424
460
  return { data, cache_hit, cache_age_ms };
425
461
  }
426
462
  // ============================================================================
427
- // STORAGE API (for transcript persistence - OD-467)
463
+ // STORAGE API
428
464
  // ============================================================================
429
465
  const TRANSCRIPT_BUCKET = "session-transcripts";
430
466
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Thread Lifecycle Manager (OD-thread-lifecycle)
2
+ * Thread Lifecycle Manager ()
3
3
  *
4
4
  * Core logic for thread lifecycle management:
5
5
  * - ID generation, migration from plain strings to ThreadObject
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Thread Lifecycle Manager (OD-thread-lifecycle)
2
+ * Thread Lifecycle Manager ()
3
3
  *
4
4
  * Core logic for thread lifecycle management:
5
5
  * - ID generation, migration from plain strings to ThreadObject
@@ -11,6 +11,7 @@ import * as fs from "fs";
11
11
  import * as path from "path";
12
12
  import * as crypto from "crypto";
13
13
  import { normalizeText } from "./thread-dedup.js";
14
+ import { getGitmemDir } from "./gitmem-dir.js";
14
15
  // ---------- ID Generation ----------
15
16
  /**
16
17
  * Generate a thread ID: "t-" + 8 hex chars
@@ -123,7 +124,7 @@ export function aggregateThreads(sessions, maxSessions = 5, maxAgeDays = 14) {
123
124
  const rawThreads = session.open_threads || [];
124
125
  const normalized = normalizeThreads(rawThreads, session.id);
125
126
  for (const thread of normalized) {
126
- // Skip PROJECT STATE threads (handled separately via OD-534)
127
+ // Skip PROJECT STATE threads
127
128
  if (thread.text.startsWith("PROJECT STATE:"))
128
129
  continue;
129
130
  // Deduplicate by both thread ID and normalized text content
@@ -184,8 +185,7 @@ export function resolveThread(threads, options) {
184
185
  // ---------- Local File Persistence ----------
185
186
  const THREADS_FILENAME = "threads.json";
186
187
  function getThreadsFilePath() {
187
- const gitmemDir = path.join(process.cwd(), ".gitmem");
188
- return path.join(gitmemDir, THREADS_FILENAME);
188
+ return path.join(getGitmemDir(), THREADS_FILENAME);
189
189
  }
190
190
  /**
191
191
  * Load threads from .gitmem/threads.json.
@@ -210,7 +210,7 @@ export function loadThreadsFile() {
210
210
  * Save threads to .gitmem/threads.json.
211
211
  */
212
212
  export function saveThreadsFile(threads) {
213
- const gitmemDir = path.join(process.cwd(), ".gitmem");
213
+ const gitmemDir = getGitmemDir();
214
214
  if (!fs.existsSync(gitmemDir)) {
215
215
  fs.mkdirSync(gitmemDir, { recursive: true });
216
216
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Thread Supabase Service (OD-620, OD-621, OD-622, OD-623, OD-624)
2
+ * Thread Supabase Service
3
3
  *
4
4
  * Provides Supabase CRUD operations for the orchestra_threads table.
5
5
  * Supabase is the source of truth; local .gitmem/threads.json is a cache.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Thread Supabase Service (OD-620, OD-621, OD-622, OD-623, OD-624)
2
+ * Thread Supabase Service
3
3
  *
4
4
  * Provides Supabase CRUD operations for the orchestra_threads table.
5
5
  * Supabase is the source of truth; local .gitmem/threads.json is a cache.
@@ -318,7 +318,7 @@ export async function syncThreadsToSupabase(threads, project = "default", sessio
318
318
  if (!hasSupabase() || !supabase.isConfigured() || threads.length === 0) {
319
319
  return;
320
320
  }
321
- // OD-thread-dedup: Load existing open threads once upfront for text-based dedup.
321
+ // Load existing open threads once upfront for text-based dedup.
322
322
  // Prevents duplicate creation when closing ceremony generates new thread IDs
323
323
  // for threads that already exist with the same (or similar) text.
324
324
  let existingOpenThreads = [];
@@ -4,7 +4,7 @@
4
4
  * Parses JSONL session transcripts, chunks them intelligently,
5
5
  * generates embeddings, and stores in orchestra_transcript_chunks.
6
6
  *
7
- * Issue: OD-540
7
+ *
8
8
  */
9
9
  import type { Project } from "../types/index.js";
10
10
  /**
@@ -4,7 +4,7 @@
4
4
  * Parses JSONL session transcripts, chunks them intelligently,
5
5
  * generates embeddings, and stores in orchestra_transcript_chunks.
6
6
  *
7
- * Issue: OD-540
7
+ *
8
8
  */
9
9
  // OpenRouter API configuration (same as local-vector-search)
10
10
  const OPENROUTER_API_URL = "https://openrouter.ai/api/v1/embeddings";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Scar Variant Assignment Service
3
- * OD-525: Integrates variant assignment into enforcement engine
4
- * OD-547: Switched from issue-keyed to agent-keyed assignment
3
+ * Integrates variant assignment into enforcement engine
4
+ * Switched from issue-keyed to agent-keyed assignment
5
5
  *
6
6
  * Handles:
7
7
  * - Random variant assignment for scars with variants
@@ -29,7 +29,7 @@ export interface ScarVariant {
29
29
  }
30
30
  /**
31
31
  * Assignment record
32
- * OD-547: Primary key is (agent_id, scar_id), issue_id/session_id are metadata
32
+ * Primary key is (agent_id, scar_id), issue_id/session_id are metadata
33
33
  */
34
34
  export interface VariantAssignment {
35
35
  id: string;
@@ -55,12 +55,12 @@ export interface ScarWithVariant {
55
55
  export declare function getActiveVariants(scarId: string): Promise<ScarVariant[]>;
56
56
  /**
57
57
  * Get existing assignment for an agent + scar pair
58
- * OD-547: Changed from issue-keyed to agent-keyed lookup
58
+ * Changed from issue-keyed to agent-keyed lookup
59
59
  */
60
60
  export declare function getExistingAssignment(agentId: string, scarId: string): Promise<VariantAssignment | null>;
61
61
  /**
62
62
  * Create random variant assignment
63
- * OD-547: Agent-keyed with optional issue/session metadata
63
+ * Agent-keyed with optional issue/session metadata
64
64
  */
65
65
  export declare function createVariantAssignment(agentId: string, scarId: string, variants: ScarVariant[], metadata?: {
66
66
  issueId?: string;
@@ -70,7 +70,7 @@ export declare function createVariantAssignment(agentId: string, scarId: string,
70
70
  * Get or create variant assignment for a scar
71
71
  * This is the main entry point for enforcement integration
72
72
  *
73
- * OD-547: Changed from issue-keyed to agent-keyed assignment.
73
+ * Changed from issue-keyed to agent-keyed assignment.
74
74
  * Agent identity is always available, so variants are always assigned.
75
75
  *
76
76
  * @param agentId - Agent identity (e.g., 'CLI', 'DAC', 'CODA-1')
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Scar Variant Assignment Service
3
- * OD-525: Integrates variant assignment into enforcement engine
4
- * OD-547: Switched from issue-keyed to agent-keyed assignment
3
+ * Integrates variant assignment into enforcement engine
4
+ * Switched from issue-keyed to agent-keyed assignment
5
5
  *
6
6
  * Handles:
7
7
  * - Random variant assignment for scars with variants
@@ -9,6 +9,7 @@
9
9
  * - Legacy scar fallback (scars without variants use original description)
10
10
  * - Blind testing (agent doesn't know which variant they received)
11
11
  */
12
+ import { randomInt } from "crypto";
12
13
  import * as supabase from "./supabase-client.js";
13
14
  /**
14
15
  * Get active variants for a scar
@@ -35,7 +36,7 @@ export async function getActiveVariants(scarId) {
35
36
  }
36
37
  /**
37
38
  * Get existing assignment for an agent + scar pair
38
- * OD-547: Changed from issue-keyed to agent-keyed lookup
39
+ * Changed from issue-keyed to agent-keyed lookup
39
40
  */
40
41
  export async function getExistingAssignment(agentId, scarId) {
41
42
  if (!supabase.isConfigured()) {
@@ -60,15 +61,15 @@ export async function getExistingAssignment(agentId, scarId) {
60
61
  }
61
62
  /**
62
63
  * Create random variant assignment
63
- * OD-547: Agent-keyed with optional issue/session metadata
64
+ * Agent-keyed with optional issue/session metadata
64
65
  */
65
66
  export async function createVariantAssignment(agentId, scarId, variants, metadata) {
66
67
  if (!supabase.isConfigured() || variants.length === 0) {
67
68
  return null;
68
69
  }
69
70
  try {
70
- // Random selection from active variants
71
- const randomIndex = Math.floor(Math.random() * variants.length);
71
+ // Cryptographically secure random selection for blind A/B testing integrity
72
+ const randomIndex = randomInt(0, variants.length);
72
73
  const selectedVariant = variants[randomIndex];
73
74
  const result = await supabase.directUpsert("variant_assignments", {
74
75
  agent_id: agentId,
@@ -86,7 +87,7 @@ export async function createVariantAssignment(agentId, scarId, variants, metadat
86
87
  console.error(`[variant-assignment] Assignment already exists (race condition), fetching...`);
87
88
  return await getExistingAssignment(agentId, scarId);
88
89
  }
89
- console.error(`[variant-assignment] Exception creating assignment:`, error);
90
+ console.error(`[variant-assignment] Failed to create assignment for scar ${scarId}`);
90
91
  return null;
91
92
  }
92
93
  }
@@ -94,7 +95,7 @@ export async function createVariantAssignment(agentId, scarId, variants, metadat
94
95
  * Get or create variant assignment for a scar
95
96
  * This is the main entry point for enforcement integration
96
97
  *
97
- * OD-547: Changed from issue-keyed to agent-keyed assignment.
98
+ * Changed from issue-keyed to agent-keyed assignment.
98
99
  * Agent identity is always available, so variants are always assigned.
99
100
  *
100
101
  * @param agentId - Agent identity (e.g., 'CLI', 'DAC', 'CODA-1')
@@ -1,11 +1,11 @@
1
1
  /**
2
- * analyze Tool (OD-567)
2
+ * analyze Tool
3
3
  *
4
4
  * Session analytics and insights engine. Provides structured analysis
5
5
  * of session history, closing reflections, agent patterns, and more.
6
6
  *
7
7
  * Starts with "summary" lens; expanded with additional lenses in
8
- * OD-568 (Tier 1), OD-569 (Tier 2), OD-570 (Tier 3), OD-572 (Tier 4).
8
+ * (Tier 1).
9
9
  *
10
10
  * Performance target: 3000ms
11
11
  */
@@ -1,11 +1,11 @@
1
1
  /**
2
- * analyze Tool (OD-567)
2
+ * analyze Tool
3
3
  *
4
4
  * Session analytics and insights engine. Provides structured analysis
5
5
  * of session history, closing reflections, agent patterns, and more.
6
6
  *
7
7
  * Starts with "summary" lens; expanded with additional lenses in
8
- * OD-568 (Tier 1), OD-569 (Tier 2), OD-570 (Tier 3), OD-572 (Tier 4).
8
+ * (Tier 1).
9
9
  *
10
10
  * Performance target: 3000ms
11
11
  */
@@ -10,6 +10,7 @@
10
10
  */
11
11
  import { directPatch, isConfigured } from "../services/supabase-client.js";
12
12
  import { hasSupabase } from "../services/tier.js";
13
+ import { getStorage } from "../services/storage.js";
13
14
  import { flushCache } from "../services/startup.js";
14
15
  import { Timer } from "../services/metrics.js";
15
16
  import { wrapDisplay } from "../services/display-protocol.js";
@@ -26,35 +27,48 @@ export async function archiveLearning(params) {
26
27
  performance_ms: timer.stop(),
27
28
  };
28
29
  }
29
- if (!hasSupabase() || !isConfigured()) {
30
- const msg = "archive_learning requires Supabase (pro/dev tier)";
31
- return {
32
- success: false,
33
- id: params.id,
34
- cache_flushed: false,
35
- display: wrapDisplay(msg),
36
- error: msg,
37
- performance_ms: timer.stop(),
38
- };
39
- }
40
30
  try {
41
31
  const archivedAt = new Date().toISOString();
42
- await directPatch("orchestra_learnings", { id: `eq.${params.id}` }, {
43
- is_active: false,
44
- archived_at: archivedAt,
45
- });
46
- // Flush local cache so archived scar is immediately excluded
47
32
  let cacheFlushed = false;
48
- try {
49
- await flushCache();
50
- cacheFlushed = true;
33
+ if (hasSupabase() && isConfigured()) {
34
+ // Pro/dev: patch in Supabase
35
+ await directPatch("orchestra_learnings", { id: `eq.${params.id}` }, {
36
+ is_active: false,
37
+ archived_at: archivedAt,
38
+ });
39
+ try {
40
+ await flushCache();
41
+ cacheFlushed = true;
42
+ }
43
+ catch {
44
+ console.error("[archive-learning] Cache flush failed (non-fatal)");
45
+ }
51
46
  }
52
- catch {
53
- console.error("[archive-learning] Cache flush failed (non-fatal)");
47
+ else {
48
+ // Free tier: update in local JSON
49
+ const storage = getStorage();
50
+ const existing = await storage.get("learnings", params.id);
51
+ if (!existing) {
52
+ const msg = `Learning ${params.id} not found in local storage`;
53
+ return {
54
+ success: false,
55
+ id: params.id,
56
+ cache_flushed: false,
57
+ display: wrapDisplay(msg),
58
+ error: msg,
59
+ performance_ms: timer.stop(),
60
+ };
61
+ }
62
+ await storage.upsert("learnings", {
63
+ ...existing,
64
+ id: params.id,
65
+ is_active: false,
66
+ archived_at: archivedAt,
67
+ });
54
68
  }
55
69
  const latencyMs = timer.stop();
56
70
  const reasonText = params.reason ? ` Reason: ${params.reason}` : "";
57
- const display = `Archived learning ${params.id}.${reasonText}\nCache ${cacheFlushed ? "flushed" : "flush failed"} (${latencyMs}ms)`;
71
+ const display = `Archived learning ${params.id}.${reasonText}\n(${latencyMs}ms)`;
58
72
  return {
59
73
  success: true,
60
74
  id: params.id,
@@ -176,7 +176,7 @@ export async function confirmScars(params) {
176
176
  errors.push(error);
177
177
  }
178
178
  else {
179
- // OD-690: Derive default relevance from decision if not provided
179
+ // Derive default relevance from decision if not provided
180
180
  const relevance = conf.relevance ??
181
181
  (conf.decision === "APPLYING" ? "high" : conf.decision === "N_A" ? "low" : "low");
182
182
  validConfirmations.push({
@@ -5,7 +5,7 @@
5
5
  * Generates embeddings client-side and writes directly to Supabase REST API,
6
6
  * eliminating the ww-mcp Edge Function dependency.
7
7
  *
8
- * Performance target: <3000ms (OD-429)
8
+ * Performance target: <3000ms
9
9
  */
10
10
  import type { CreateDecisionParams, CreateDecisionResult } from "../types/index.js";
11
11
  /**
@@ -5,7 +5,7 @@
5
5
  * Generates embeddings client-side and writes directly to Supabase REST API,
6
6
  * eliminating the ww-mcp Edge Function dependency.
7
7
  *
8
- * Performance target: <3000ms (OD-429)
8
+ * Performance target: <3000ms
9
9
  */
10
10
  import { v4 as uuidv4 } from "uuid";
11
11
  import * as supabase from "../services/supabase-client.js";
@@ -74,7 +74,7 @@ export async function createDecision(params) {
74
74
  cache_status: "not_applicable",
75
75
  network_call: true,
76
76
  };
77
- // OD-466: Auto-create knowledge triples (tracked fire-and-forget)
77
+ // Auto-create knowledge triples (tracked fire-and-forget)
78
78
  getEffectTracker().track("triple_write", "decision", () => writeTriplesForDecision({
79
79
  id: decisionId,
80
80
  title: params.title,
@@ -5,7 +5,7 @@
5
5
  * Generates embeddings client-side and writes directly to Supabase REST API,
6
6
  * eliminating the ww-mcp Edge Function dependency.
7
7
  *
8
- * Performance target: <3000ms (OD-429)
8
+ * Performance target: <3000ms
9
9
  */
10
10
  import type { CreateLearningParams, CreateLearningResult } from "../types/index.js";
11
11
  /**
@@ -5,7 +5,7 @@
5
5
  * Generates embeddings client-side and writes directly to Supabase REST API,
6
6
  * eliminating the ww-mcp Edge Function dependency.
7
7
  *
8
- * Performance target: <3000ms (OD-429)
8
+ * Performance target: <3000ms
9
9
  */
10
10
  import { v4 as uuidv4 } from "uuid";
11
11
  import * as supabase from "../services/supabase-client.js";
@@ -87,7 +87,7 @@ export async function createLearning(params) {
87
87
  created_at: new Date().toISOString(),
88
88
  persona_name: agentIdentity,
89
89
  source_date: new Date().toISOString().split("T")[0],
90
- // OD-508: LLM-cooperative enforcement fields (optional)
90
+ // LLM-cooperative enforcement fields (optional)
91
91
  ...(params.why_this_matters && { why_this_matters: params.why_this_matters }),
92
92
  ...(params.action_protocol && { action_protocol: params.action_protocol }),
93
93
  ...(params.self_check_criteria && { self_check_criteria: params.self_check_criteria }),
@@ -151,14 +151,14 @@ export async function createLearning(params) {
151
151
  cache_status: "not_applicable",
152
152
  network_call: true,
153
153
  };
154
- // OD-539: Defense in depth - verify write succeeded
154
+ // Defense in depth - verify write succeeded
155
155
  // directUpsert now throws on empty result, but explicit check documents expectation
156
156
  if (!writeResult || !writeResult.id) {
157
157
  throw new Error(`Write verification failed: directUpsert returned ${writeResult ? 'record without id' : 'null/undefined'}. ` +
158
158
  `Expected record with id field.`);
159
159
  }
160
160
  console.error(`[create_learning] directUpsert succeeded, verified ID: ${writeResult.id}`);
161
- // OD-466: Auto-create knowledge triples (tracked fire-and-forget)
161
+ // Auto-create knowledge triples (tracked fire-and-forget)
162
162
  getEffectTracker().track("triple_write", "learning", () => writeTriplesForLearning({
163
163
  id: learningId,
164
164
  learning_type: params.learning_type,
@@ -0,0 +1,18 @@
1
+ /**
2
+ * create_linear_issue Tool (OD-611)
3
+ *
4
+ * Proxy Linear issue creation via GraphQL API.
5
+ * Returns slim response (~50 tokens) instead of Linear MCP's ~1400 tokens.
6
+ *
7
+ * Pattern: File-based payload handoff (same as session_close).
8
+ * Agent writes description to .gitmem/issue-payload.json, then calls this
9
+ * tool with title + teamId inline. Tool reads, merges, calls Linear, deletes file.
10
+ *
11
+ * Performance target: <2000ms (one network call)
12
+ */
13
+ import type { CreateLinearIssueParams, CreateLinearIssueResult } from "../types/index.js";
14
+ /**
15
+ * Create a Linear issue via GraphQL API with slim response.
16
+ */
17
+ export declare function createLinearIssue(params: CreateLinearIssueParams): Promise<CreateLinearIssueResult>;
18
+ //# sourceMappingURL=create-linear-issue.d.ts.map