brains-mcp 1.61.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. package/README.md +95 -0
  2. package/dist/access/projectShares.d.ts +48 -0
  3. package/dist/access/projectShares.d.ts.map +1 -0
  4. package/dist/access/projectShares.js +203 -0
  5. package/dist/access/projectShares.js.map +1 -0
  6. package/dist/access/sharedInstructionsNote.d.ts +37 -0
  7. package/dist/access/sharedInstructionsNote.d.ts.map +1 -0
  8. package/dist/access/sharedInstructionsNote.js +76 -0
  9. package/dist/access/sharedInstructionsNote.js.map +1 -0
  10. package/dist/admin/seedUsers.d.ts +23 -0
  11. package/dist/admin/seedUsers.d.ts.map +1 -0
  12. package/dist/admin/seedUsers.js +168 -0
  13. package/dist/admin/seedUsers.js.map +1 -0
  14. package/dist/api/openapi.d.ts +2 -0
  15. package/dist/api/openapi.d.ts.map +1 -0
  16. package/dist/api/openapi.js +942 -0
  17. package/dist/api/openapi.js.map +1 -0
  18. package/dist/auth/clientStore.d.ts +43 -0
  19. package/dist/auth/clientStore.d.ts.map +1 -0
  20. package/dist/auth/clientStore.js +168 -0
  21. package/dist/auth/clientStore.js.map +1 -0
  22. package/dist/auth/deleteAccount.d.ts +34 -0
  23. package/dist/auth/deleteAccount.d.ts.map +1 -0
  24. package/dist/auth/deleteAccount.js +203 -0
  25. package/dist/auth/deleteAccount.js.map +1 -0
  26. package/dist/auth/oauth.d.ts +118 -0
  27. package/dist/auth/oauth.d.ts.map +1 -0
  28. package/dist/auth/oauth.js +491 -0
  29. package/dist/auth/oauth.js.map +1 -0
  30. package/dist/auth/patStore.d.ts +87 -0
  31. package/dist/auth/patStore.d.ts.map +1 -0
  32. package/dist/auth/patStore.js +308 -0
  33. package/dist/auth/patStore.js.map +1 -0
  34. package/dist/auth/supabase.d.ts +15 -0
  35. package/dist/auth/supabase.d.ts.map +1 -0
  36. package/dist/auth/supabase.js +66 -0
  37. package/dist/auth/supabase.js.map +1 -0
  38. package/dist/auth/tokenStore.d.ts +73 -0
  39. package/dist/auth/tokenStore.d.ts.map +1 -0
  40. package/dist/auth/tokenStore.js +261 -0
  41. package/dist/auth/tokenStore.js.map +1 -0
  42. package/dist/backup/drive.d.ts +24 -0
  43. package/dist/backup/drive.d.ts.map +1 -0
  44. package/dist/backup/drive.js +70 -0
  45. package/dist/backup/drive.js.map +1 -0
  46. package/dist/billing/tierStore.d.ts +59 -0
  47. package/dist/billing/tierStore.d.ts.map +1 -0
  48. package/dist/billing/tierStore.js +152 -0
  49. package/dist/billing/tierStore.js.map +1 -0
  50. package/dist/digest/activityDigest.d.ts +32 -0
  51. package/dist/digest/activityDigest.d.ts.map +1 -0
  52. package/dist/digest/activityDigest.js +299 -0
  53. package/dist/digest/activityDigest.js.map +1 -0
  54. package/dist/digest/digestCron.d.ts +40 -0
  55. package/dist/digest/digestCron.d.ts.map +1 -0
  56. package/dist/digest/digestCron.js +146 -0
  57. package/dist/digest/digestCron.js.map +1 -0
  58. package/dist/digest/digestPreferences.d.ts +25 -0
  59. package/dist/digest/digestPreferences.d.ts.map +1 -0
  60. package/dist/digest/digestPreferences.js +79 -0
  61. package/dist/digest/digestPreferences.js.map +1 -0
  62. package/dist/digest/emailSender.d.ts +20 -0
  63. package/dist/digest/emailSender.d.ts.map +1 -0
  64. package/dist/digest/emailSender.js +35 -0
  65. package/dist/digest/emailSender.js.map +1 -0
  66. package/dist/drive/client.d.ts +43 -0
  67. package/dist/drive/client.d.ts.map +1 -0
  68. package/dist/drive/client.js +282 -0
  69. package/dist/drive/client.js.map +1 -0
  70. package/dist/drive/errors.d.ts +27 -0
  71. package/dist/drive/errors.d.ts.map +1 -0
  72. package/dist/drive/errors.js +51 -0
  73. package/dist/drive/errors.js.map +1 -0
  74. package/dist/drive/fileCache.d.ts +22 -0
  75. package/dist/drive/fileCache.d.ts.map +1 -0
  76. package/dist/drive/fileCache.js +57 -0
  77. package/dist/drive/fileCache.js.map +1 -0
  78. package/dist/governance/accessControl.d.ts +87 -0
  79. package/dist/governance/accessControl.d.ts.map +1 -0
  80. package/dist/governance/accessControl.js +179 -0
  81. package/dist/governance/accessControl.js.map +1 -0
  82. package/dist/html/components.d.ts +8 -0
  83. package/dist/html/components.d.ts.map +1 -0
  84. package/dist/html/components.js +19 -0
  85. package/dist/html/components.js.map +1 -0
  86. package/dist/html/layout.d.ts +7 -0
  87. package/dist/html/layout.d.ts.map +1 -0
  88. package/dist/html/layout.js +24 -0
  89. package/dist/html/layout.js.map +1 -0
  90. package/dist/html/metricsUi.d.ts +8 -0
  91. package/dist/html/metricsUi.d.ts.map +1 -0
  92. package/dist/html/metricsUi.js +271 -0
  93. package/dist/html/metricsUi.js.map +1 -0
  94. package/dist/httpServer.d.ts +19 -0
  95. package/dist/httpServer.d.ts.map +1 -0
  96. package/dist/httpServer.js +3913 -0
  97. package/dist/httpServer.js.map +1 -0
  98. package/dist/index.d.ts +3 -0
  99. package/dist/index.d.ts.map +1 -0
  100. package/dist/index.js +40 -0
  101. package/dist/index.js.map +1 -0
  102. package/dist/jobs/asyncJobRegistry.d.ts +30 -0
  103. package/dist/jobs/asyncJobRegistry.d.ts.map +1 -0
  104. package/dist/jobs/asyncJobRegistry.js +70 -0
  105. package/dist/jobs/asyncJobRegistry.js.map +1 -0
  106. package/dist/observability/metricsQuery.d.ts +41 -0
  107. package/dist/observability/metricsQuery.d.ts.map +1 -0
  108. package/dist/observability/metricsQuery.js +106 -0
  109. package/dist/observability/metricsQuery.js.map +1 -0
  110. package/dist/observability/relevanceSignal.d.ts +26 -0
  111. package/dist/observability/relevanceSignal.d.ts.map +1 -0
  112. package/dist/observability/relevanceSignal.js +95 -0
  113. package/dist/observability/relevanceSignal.js.map +1 -0
  114. package/dist/observability/requestEvents.d.ts +31 -0
  115. package/dist/observability/requestEvents.d.ts.map +1 -0
  116. package/dist/observability/requestEvents.js +54 -0
  117. package/dist/observability/requestEvents.js.map +1 -0
  118. package/dist/observability/slowQueryAlert.d.ts +25 -0
  119. package/dist/observability/slowQueryAlert.d.ts.map +1 -0
  120. package/dist/observability/slowQueryAlert.js +67 -0
  121. package/dist/observability/slowQueryAlert.js.map +1 -0
  122. package/dist/observability/zeroResultTriage.d.ts +22 -0
  123. package/dist/observability/zeroResultTriage.d.ts.map +1 -0
  124. package/dist/observability/zeroResultTriage.js +116 -0
  125. package/dist/observability/zeroResultTriage.js.map +1 -0
  126. package/dist/search/embeddings.d.ts +31 -0
  127. package/dist/search/embeddings.d.ts.map +1 -0
  128. package/dist/search/embeddings.js +91 -0
  129. package/dist/search/embeddings.js.map +1 -0
  130. package/dist/search/fileWatcher.d.ts +28 -0
  131. package/dist/search/fileWatcher.d.ts.map +1 -0
  132. package/dist/search/fileWatcher.js +103 -0
  133. package/dist/search/fileWatcher.js.map +1 -0
  134. package/dist/search/hotQueryCache.d.ts +31 -0
  135. package/dist/search/hotQueryCache.d.ts.map +1 -0
  136. package/dist/search/hotQueryCache.js +103 -0
  137. package/dist/search/hotQueryCache.js.map +1 -0
  138. package/dist/search/postgresIndex.d.ts +439 -0
  139. package/dist/search/postgresIndex.d.ts.map +1 -0
  140. package/dist/search/postgresIndex.js +1610 -0
  141. package/dist/search/postgresIndex.js.map +1 -0
  142. package/dist/search/sqliteIndex.d.ts +66 -0
  143. package/dist/search/sqliteIndex.d.ts.map +1 -0
  144. package/dist/search/sqliteIndex.js +342 -0
  145. package/dist/search/sqliteIndex.js.map +1 -0
  146. package/dist/server.d.ts +43 -0
  147. package/dist/server.d.ts.map +1 -0
  148. package/dist/server.js +532 -0
  149. package/dist/server.js.map +1 -0
  150. package/dist/session/bundle.d.ts +53 -0
  151. package/dist/session/bundle.d.ts.map +1 -0
  152. package/dist/session/bundle.js +97 -0
  153. package/dist/session/bundle.js.map +1 -0
  154. package/dist/storage/client.d.ts +157 -0
  155. package/dist/storage/client.d.ts.map +1 -0
  156. package/dist/storage/client.js +825 -0
  157. package/dist/storage/client.js.map +1 -0
  158. package/dist/tools/addWebhookPattern.d.ts +15 -0
  159. package/dist/tools/addWebhookPattern.d.ts.map +1 -0
  160. package/dist/tools/addWebhookPattern.js +15 -0
  161. package/dist/tools/addWebhookPattern.js.map +1 -0
  162. package/dist/tools/appendLog.d.ts +18 -0
  163. package/dist/tools/appendLog.d.ts.map +1 -0
  164. package/dist/tools/appendLog.js +18 -0
  165. package/dist/tools/appendLog.js.map +1 -0
  166. package/dist/tools/auditIndex.d.ts +15 -0
  167. package/dist/tools/auditIndex.d.ts.map +1 -0
  168. package/dist/tools/auditIndex.js +165 -0
  169. package/dist/tools/auditIndex.js.map +1 -0
  170. package/dist/tools/batchDeletePages.d.ts +18 -0
  171. package/dist/tools/batchDeletePages.d.ts.map +1 -0
  172. package/dist/tools/batchDeletePages.js +143 -0
  173. package/dist/tools/batchDeletePages.js.map +1 -0
  174. package/dist/tools/coerce.d.ts +3 -0
  175. package/dist/tools/coerce.d.ts.map +1 -0
  176. package/dist/tools/coerce.js +12 -0
  177. package/dist/tools/coerce.js.map +1 -0
  178. package/dist/tools/createPage.d.ts +24 -0
  179. package/dist/tools/createPage.d.ts.map +1 -0
  180. package/dist/tools/createPage.js +99 -0
  181. package/dist/tools/createPage.js.map +1 -0
  182. package/dist/tools/createPages.d.ts +51 -0
  183. package/dist/tools/createPages.d.ts.map +1 -0
  184. package/dist/tools/createPages.js +91 -0
  185. package/dist/tools/createPages.js.map +1 -0
  186. package/dist/tools/deletePage.d.ts +19 -0
  187. package/dist/tools/deletePage.d.ts.map +1 -0
  188. package/dist/tools/deletePage.js +127 -0
  189. package/dist/tools/deletePage.js.map +1 -0
  190. package/dist/tools/exportWiki.d.ts +48 -0
  191. package/dist/tools/exportWiki.d.ts.map +1 -0
  192. package/dist/tools/exportWiki.js +233 -0
  193. package/dist/tools/exportWiki.js.map +1 -0
  194. package/dist/tools/extractLinks.d.ts +14 -0
  195. package/dist/tools/extractLinks.d.ts.map +1 -0
  196. package/dist/tools/extractLinks.js +25 -0
  197. package/dist/tools/extractLinks.js.map +1 -0
  198. package/dist/tools/generateClaudeInstructions.d.ts +27 -0
  199. package/dist/tools/generateClaudeInstructions.d.ts.map +1 -0
  200. package/dist/tools/generateClaudeInstructions.js +263 -0
  201. package/dist/tools/generateClaudeInstructions.js.map +1 -0
  202. package/dist/tools/generateGptInstructions.d.ts +27 -0
  203. package/dist/tools/generateGptInstructions.d.ts.map +1 -0
  204. package/dist/tools/generateGptInstructions.js +208 -0
  205. package/dist/tools/generateGptInstructions.js.map +1 -0
  206. package/dist/tools/getBacklinks.d.ts +12 -0
  207. package/dist/tools/getBacklinks.d.ts.map +1 -0
  208. package/dist/tools/getBacklinks.js +13 -0
  209. package/dist/tools/getBacklinks.js.map +1 -0
  210. package/dist/tools/getRelated.d.ts +12 -0
  211. package/dist/tools/getRelated.d.ts.map +1 -0
  212. package/dist/tools/getRelated.js +13 -0
  213. package/dist/tools/getRelated.js.map +1 -0
  214. package/dist/tools/healthCheck.d.ts +5 -0
  215. package/dist/tools/healthCheck.d.ts.map +1 -0
  216. package/dist/tools/healthCheck.js +167 -0
  217. package/dist/tools/healthCheck.js.map +1 -0
  218. package/dist/tools/importBrainSource.d.ts +36 -0
  219. package/dist/tools/importBrainSource.d.ts.map +1 -0
  220. package/dist/tools/importBrainSource.js +314 -0
  221. package/dist/tools/importBrainSource.js.map +1 -0
  222. package/dist/tools/importWikiZip.d.ts +60 -0
  223. package/dist/tools/importWikiZip.d.ts.map +1 -0
  224. package/dist/tools/importWikiZip.js +379 -0
  225. package/dist/tools/importWikiZip.js.map +1 -0
  226. package/dist/tools/initProject.d.ts +34 -0
  227. package/dist/tools/initProject.d.ts.map +1 -0
  228. package/dist/tools/initProject.js +443 -0
  229. package/dist/tools/initProject.js.map +1 -0
  230. package/dist/tools/instructionContext.d.ts +60 -0
  231. package/dist/tools/instructionContext.d.ts.map +1 -0
  232. package/dist/tools/instructionContext.js +210 -0
  233. package/dist/tools/instructionContext.js.map +1 -0
  234. package/dist/tools/listArchivedPages.d.ts +17 -0
  235. package/dist/tools/listArchivedPages.d.ts.map +1 -0
  236. package/dist/tools/listArchivedPages.js +51 -0
  237. package/dist/tools/listArchivedPages.js.map +1 -0
  238. package/dist/tools/listPages.d.ts +28 -0
  239. package/dist/tools/listPages.d.ts.map +1 -0
  240. package/dist/tools/listPages.js +166 -0
  241. package/dist/tools/listPages.js.map +1 -0
  242. package/dist/tools/listWebhooks.d.ts +7 -0
  243. package/dist/tools/listWebhooks.d.ts.map +1 -0
  244. package/dist/tools/listWebhooks.js +10 -0
  245. package/dist/tools/listWebhooks.js.map +1 -0
  246. package/dist/tools/movePage.d.ts +18 -0
  247. package/dist/tools/movePage.d.ts.map +1 -0
  248. package/dist/tools/movePage.js +182 -0
  249. package/dist/tools/movePage.js.map +1 -0
  250. package/dist/tools/pageHistory.d.ts +36 -0
  251. package/dist/tools/pageHistory.d.ts.map +1 -0
  252. package/dist/tools/pageHistory.js +60 -0
  253. package/dist/tools/pageHistory.js.map +1 -0
  254. package/dist/tools/patchPage.d.ts +141 -0
  255. package/dist/tools/patchPage.d.ts.map +1 -0
  256. package/dist/tools/patchPage.js +287 -0
  257. package/dist/tools/patchPage.js.map +1 -0
  258. package/dist/tools/pullSessionBundle.d.ts +34 -0
  259. package/dist/tools/pullSessionBundle.d.ts.map +1 -0
  260. package/dist/tools/pullSessionBundle.js +432 -0
  261. package/dist/tools/pullSessionBundle.js.map +1 -0
  262. package/dist/tools/purgeWiki.d.ts +23 -0
  263. package/dist/tools/purgeWiki.d.ts.map +1 -0
  264. package/dist/tools/purgeWiki.js +113 -0
  265. package/dist/tools/purgeWiki.js.map +1 -0
  266. package/dist/tools/pushSessionBundle.d.ts +139 -0
  267. package/dist/tools/pushSessionBundle.d.ts.map +1 -0
  268. package/dist/tools/pushSessionBundle.js +282 -0
  269. package/dist/tools/pushSessionBundle.js.map +1 -0
  270. package/dist/tools/readGovernanceAudit.d.ts +17 -0
  271. package/dist/tools/readGovernanceAudit.d.ts.map +1 -0
  272. package/dist/tools/readGovernanceAudit.js +21 -0
  273. package/dist/tools/readGovernanceAudit.js.map +1 -0
  274. package/dist/tools/readPage.d.ts +13 -0
  275. package/dist/tools/readPage.d.ts.map +1 -0
  276. package/dist/tools/readPage.js +46 -0
  277. package/dist/tools/readPage.js.map +1 -0
  278. package/dist/tools/removeWebhook.d.ts +14 -0
  279. package/dist/tools/removeWebhook.d.ts.map +1 -0
  280. package/dist/tools/removeWebhook.js +12 -0
  281. package/dist/tools/removeWebhook.js.map +1 -0
  282. package/dist/tools/resolveProject.d.ts +58 -0
  283. package/dist/tools/resolveProject.d.ts.map +1 -0
  284. package/dist/tools/resolveProject.js +175 -0
  285. package/dist/tools/resolveProject.js.map +1 -0
  286. package/dist/tools/restorePage.d.ts +47 -0
  287. package/dist/tools/restorePage.d.ts.map +1 -0
  288. package/dist/tools/restorePage.js +111 -0
  289. package/dist/tools/restorePage.js.map +1 -0
  290. package/dist/tools/searchPages.d.ts +28 -0
  291. package/dist/tools/searchPages.d.ts.map +1 -0
  292. package/dist/tools/searchPages.js +372 -0
  293. package/dist/tools/searchPages.js.map +1 -0
  294. package/dist/tools/setWebhook.d.ts +30 -0
  295. package/dist/tools/setWebhook.d.ts.map +1 -0
  296. package/dist/tools/setWebhook.js +30 -0
  297. package/dist/tools/setWebhook.js.map +1 -0
  298. package/dist/tools/shareProject.d.ts +25 -0
  299. package/dist/tools/shareProject.d.ts.map +1 -0
  300. package/dist/tools/shareProject.js +56 -0
  301. package/dist/tools/shareProject.js.map +1 -0
  302. package/dist/tools/suggestLinks.d.ts +29 -0
  303. package/dist/tools/suggestLinks.d.ts.map +1 -0
  304. package/dist/tools/suggestLinks.js +214 -0
  305. package/dist/tools/suggestLinks.js.map +1 -0
  306. package/dist/tools/updatePage.d.ts +27 -0
  307. package/dist/tools/updatePage.d.ts.map +1 -0
  308. package/dist/tools/updatePage.js +118 -0
  309. package/dist/tools/updatePage.js.map +1 -0
  310. package/dist/webhooks/dispatch.d.ts +49 -0
  311. package/dist/webhooks/dispatch.d.ts.map +1 -0
  312. package/dist/webhooks/dispatch.js +159 -0
  313. package/dist/webhooks/dispatch.js.map +1 -0
  314. package/dist/webhooks/webhookRules.d.ts +34 -0
  315. package/dist/webhooks/webhookRules.d.ts.map +1 -0
  316. package/dist/webhooks/webhookRules.js +129 -0
  317. package/dist/webhooks/webhookRules.js.map +1 -0
  318. package/dist/wiki/frontmatter.d.ts +45 -0
  319. package/dist/wiki/frontmatter.d.ts.map +1 -0
  320. package/dist/wiki/frontmatter.js +68 -0
  321. package/dist/wiki/frontmatter.js.map +1 -0
  322. package/dist/wiki/index.d.ts +22 -0
  323. package/dist/wiki/index.d.ts.map +1 -0
  324. package/dist/wiki/index.js +203 -0
  325. package/dist/wiki/index.js.map +1 -0
  326. package/dist/wiki/ingestPolicy.d.ts +18 -0
  327. package/dist/wiki/ingestPolicy.d.ts.map +1 -0
  328. package/dist/wiki/ingestPolicy.js +64 -0
  329. package/dist/wiki/ingestPolicy.js.map +1 -0
  330. package/dist/wiki/layoutLint.d.ts +51 -0
  331. package/dist/wiki/layoutLint.d.ts.map +1 -0
  332. package/dist/wiki/layoutLint.js +146 -0
  333. package/dist/wiki/layoutLint.js.map +1 -0
  334. package/dist/wiki/links.d.ts +6 -0
  335. package/dist/wiki/links.d.ts.map +1 -0
  336. package/dist/wiki/links.js +45 -0
  337. package/dist/wiki/links.js.map +1 -0
  338. package/dist/wiki/log.d.ts +58 -0
  339. package/dist/wiki/log.d.ts.map +1 -0
  340. package/dist/wiki/log.js +126 -0
  341. package/dist/wiki/log.js.map +1 -0
  342. package/dist/wiki/path.d.ts +8 -0
  343. package/dist/wiki/path.d.ts.map +1 -0
  344. package/dist/wiki/path.js +25 -0
  345. package/dist/wiki/path.js.map +1 -0
  346. package/dist/wiki/storageLimits.d.ts +163 -0
  347. package/dist/wiki/storageLimits.d.ts.map +1 -0
  348. package/dist/wiki/storageLimits.js +281 -0
  349. package/dist/wiki/storageLimits.js.map +1 -0
  350. package/package.json +81 -0
@@ -0,0 +1,233 @@
1
+ import fs from "fs/promises";
2
+ import path from "path";
3
+ import AdmZip from "adm-zip";
4
+ import { z } from "zod";
5
+ import matter from "gray-matter";
6
+ import { getPostgresSearchIndex } from "../search/postgresIndex.js";
7
+ import { listActiveGrantsForGrantee } from "../access/projectShares.js";
8
+ // Concurrency for parallel Supabase reads during export. Tunable via env var.
9
+ export const READ_CONCURRENCY = Math.max(1, parseInt(process.env.PULL_WIKI_READ_CONCURRENCY ?? "20", 10));
10
+ // Base64-encoding a ZIP adds ~33% overhead; large wikis can produce a response
11
+ // that silently overflows MCP client buffers (observed at ~3-5.5 MB). Reject
12
+ // before encoding and point the caller at the HTTP async export endpoint instead.
13
+ export const INLINE_ZIP_SIZE_LIMIT_BYTES = 3 * 1024 * 1024;
14
+ export const exportWikiInputSchema = z.object({
15
+ outputPath: z
16
+ .string()
17
+ .min(1)
18
+ .optional()
19
+ .describe("Optional: absolute or relative path where the ZIP archive will be written (e.g. '/tmp/wiki-export.zip'). When omitted, the ZIP is returned as a base64-encoded string in the response — no server filesystem access required."),
20
+ includeSystem: z
21
+ .boolean()
22
+ .default(true)
23
+ .describe("If true, includes system files index.md and log.md in the archive"),
24
+ folder: z
25
+ .string()
26
+ .optional()
27
+ .describe("Optional: filter to only pages whose path starts with this prefix (e.g. 'projects/brainsapp')"),
28
+ tags: z
29
+ .array(z.string())
30
+ .optional()
31
+ .describe("Optional: filter to only pages that have at least one of these tags in their frontmatter"),
32
+ });
33
+ function pageMatchesFilter(name, content, folder, tags) {
34
+ const hasFilter = folder !== undefined || (tags !== undefined && tags.length > 0);
35
+ if (!hasFilter)
36
+ return true;
37
+ const normalizedFolder = folder ? folder.replace(/\/$/, "") + "/" : undefined;
38
+ const matchesFolder = normalizedFolder ? name.startsWith(normalizedFolder) : false;
39
+ if (tags && tags.length > 0) {
40
+ try {
41
+ const parsed = matter(content);
42
+ const pageTags = parsed.data?.tags ?? [];
43
+ const matchesTags = tags.some((t) => pageTags.includes(t));
44
+ if (matchesTags)
45
+ return true;
46
+ }
47
+ catch {
48
+ // If frontmatter parsing fails, fall through to folder check only
49
+ }
50
+ }
51
+ return matchesFolder;
52
+ }
53
+ // Returns false when the file can be skipped without a content read.
54
+ function namePassesPreFilter(name, includeSystem, folder, tags) {
55
+ if (!includeSystem && (name === "index.md" || name === "log.md"))
56
+ return false;
57
+ // When a folder filter is set with no tags filter, we can reject by name alone.
58
+ if (folder && !(tags && tags.length > 0)) {
59
+ const normalizedFolder = folder.replace(/\/$/, "") + "/";
60
+ if (!name.startsWith(normalizedFolder))
61
+ return false;
62
+ }
63
+ return true;
64
+ }
65
+ // Run `fn` over `items` with at most `concurrency` items in-flight at a time.
66
+ export async function mapConcurrent(items, concurrency, fn) {
67
+ const results = new Array(items.length);
68
+ let nextIndex = 0;
69
+ const worker = async () => {
70
+ let index = nextIndex++;
71
+ while (index < items.length) {
72
+ results[index] = await fn(items[index]);
73
+ index = nextIndex++;
74
+ }
75
+ };
76
+ const workers = Array.from({ length: Math.min(concurrency, items.length) }, worker);
77
+ await Promise.all(workers);
78
+ return results;
79
+ }
80
+ /**
81
+ * Active project-share grants (BRA-368) for `userId`, narrowed to those
82
+ * relevant to `folder` (if set). Without this, the delta/`changedAfter` export
83
+ * path queries the Postgres index by owner subject only and silently drops
84
+ * pages from projects shared to `userId` — same root cause as BRA-379/#380
85
+ * and BRA-383/#384.
86
+ */
87
+ async function grantScopesFor(userId, folder) {
88
+ if (!userId)
89
+ return [];
90
+ const normalizedFolder = folder ? folder.replace(/\/$/, "") : undefined;
91
+ const grants = await listActiveGrantsForGrantee(userId);
92
+ return grants
93
+ .filter((g) => !normalizedFolder ||
94
+ g.project_path === normalizedFolder ||
95
+ g.project_path.startsWith(`${normalizedFolder}/`) ||
96
+ normalizedFolder.startsWith(`${g.project_path}/`))
97
+ .map((g) => ({ userId: g.owner_subject, folder: g.project_path }));
98
+ }
99
+ export async function buildExportZip(drive, options) {
100
+ const exportedAt = new Date().toISOString();
101
+ const includeSystem = options.includeSystem ?? false;
102
+ // Fetch Postgres content-modified timestamps when available — real edit dates
103
+ // rather than Supabase storage upload times (identical for all pages after bulk import).
104
+ const storageBackend = (process.env.STORAGE_BACKEND ?? "local").toLowerCase();
105
+ const pgIndex = getPostgresSearchIndex();
106
+ const usePg = storageBackend === "supabase" && pgIndex.isConfigured();
107
+ let pgTimestamps = null;
108
+ let changedSet = null;
109
+ if (usePg) {
110
+ try {
111
+ const grantScopes = await grantScopesFor(options.userId ?? "", options.folder);
112
+ const rows = await pgIndex.listPages({
113
+ userId: options.userId ?? "",
114
+ changedAfter: options.changedAfter,
115
+ compact: true,
116
+ grantScopes,
117
+ });
118
+ pgTimestamps = new Map(rows.map((r) => [r.name, r.modified]));
119
+ if (options.changedAfter) {
120
+ changedSet = new Set(rows.map((r) => r.name));
121
+ }
122
+ }
123
+ catch {
124
+ // Non-fatal — fall back to storage modifiedTime
125
+ }
126
+ }
127
+ const allEntries = await drive.listFiles();
128
+ // Pre-filter by name, Postgres delta set, or storage mtime.
129
+ const preFiltered = allEntries.filter((e) => {
130
+ if (!namePassesPreFilter(e.name, includeSystem, options.folder, options.tags))
131
+ return false;
132
+ if (changedSet !== null && !changedSet.has(e.name))
133
+ return false;
134
+ if (options.changedAfter && !usePg) {
135
+ const mtime = new Date(e.modifiedTime).getTime();
136
+ const cursor = new Date(options.changedAfter).getTime();
137
+ if (!isNaN(mtime) && !isNaN(cursor) && mtime <= cursor)
138
+ return false;
139
+ }
140
+ return true;
141
+ });
142
+ const nameSkipped = allEntries.length - preFiltered.length;
143
+ const readResults = await mapConcurrent(preFiltered, READ_CONCURRENCY, async (entry) => {
144
+ try {
145
+ const file = await drive.readFile(entry.name);
146
+ return { ok: true, name: entry.name, content: file.content, modifiedTime: file.modifiedTime };
147
+ }
148
+ catch (err) {
149
+ return { ok: false, name: entry.name, reason: err instanceof Error ? err.message : String(err) };
150
+ }
151
+ });
152
+ const zip = new AdmZip();
153
+ const index = [];
154
+ const errors = [];
155
+ let included = 0;
156
+ let skipped = nameSkipped;
157
+ for (const result of readResults) {
158
+ if (!result.ok) {
159
+ errors.push({ name: result.name, reason: result.reason });
160
+ continue;
161
+ }
162
+ if (!pageMatchesFilter(result.name, result.content, options.folder, options.tags)) {
163
+ skipped++;
164
+ continue;
165
+ }
166
+ const contentBuffer = Buffer.from(result.content, "utf8");
167
+ zip.addFile(result.name, contentBuffer);
168
+ // Use Postgres content-modified time over storage upload time
169
+ const modifiedTime = pgTimestamps?.get(result.name) || result.modifiedTime;
170
+ index.push({ name: result.name, size: contentBuffer.length, modifiedTime });
171
+ included++;
172
+ }
173
+ // Generate export-index.json inside the archive
174
+ const exportIndex = { exportedAt, fileCount: included, files: index };
175
+ if (options.changedAfter)
176
+ exportIndex.since = options.changedAfter;
177
+ zip.addFile("export-index.json", Buffer.from(JSON.stringify(exportIndex, null, 2), "utf8"));
178
+ return {
179
+ buffer: zip.toBuffer(),
180
+ pageCount: included,
181
+ skippedCount: skipped,
182
+ generatedAt: exportedAt,
183
+ ...(options.changedAfter ? { since: options.changedAfter } : {}),
184
+ files: index,
185
+ errors,
186
+ };
187
+ }
188
+ export async function exportWiki(drive, input) {
189
+ const startMs = Date.now();
190
+ const { buffer, pageCount, skippedCount, generatedAt, files, errors } = await buildExportZip(drive, {
191
+ includeSystem: input.includeSystem,
192
+ folder: input.folder,
193
+ tags: input.tags,
194
+ });
195
+ const durationMs = Date.now() - startMs;
196
+ const base = {
197
+ success: errors.length === 0,
198
+ exportedAt: generatedAt,
199
+ durationMs,
200
+ fileCount: pageCount,
201
+ skippedCount,
202
+ archiveSizeBytes: buffer.length,
203
+ files,
204
+ ...(errors.length > 0 ? { errors } : {}),
205
+ };
206
+ if (!input.outputPath) {
207
+ // Agent-friendly mode: return the zip as base64, no server filesystem write.
208
+ if (buffer.length > INLINE_ZIP_SIZE_LIMIT_BYTES) {
209
+ return {
210
+ ...base,
211
+ success: false,
212
+ error: `Export ZIP is ${(buffer.length / (1024 * 1024)).toFixed(1)} MB, which exceeds the ${INLINE_ZIP_SIZE_LIMIT_BYTES / (1024 * 1024)} MB limit for inline base64 responses and would silently overflow the response buffer. ` +
213
+ `Use the HTTP async export endpoint instead: POST /api/v1/export followed by GET /api/v1/export/download, ` +
214
+ `or pass an outputPath to write the ZIP to a local file.`,
215
+ };
216
+ }
217
+ return { ...base, zipData: buffer.toString("base64") };
218
+ }
219
+ // File-path mode: write to disk and return path metadata.
220
+ // Only here do we signal local-mode — a ZIP now exists on the local
221
+ // filesystem the agent can unpack and work on. (Not in base64 mode,
222
+ // where nothing is written to disk.)
223
+ const resolvedOutput = path.resolve(input.outputPath);
224
+ await fs.mkdir(path.dirname(resolvedOutput), { recursive: true });
225
+ await fs.writeFile(resolvedOutput, buffer);
226
+ return {
227
+ ...base,
228
+ outputPath: resolvedOutput,
229
+ local_mode: true,
230
+ _local_mode_instructions: `Wiki exported as a ZIP archive at ${resolvedOutput} (not an extracted folder). To edit locally: unzip it, then use filesystem ops on the extracted markdown — Read instead of read_page, Write/Edit instead of update_page/patch_page, Glob/Grep instead of search_pages/list_pages. When done, re-zip the files and call import_wiki with zipPath pointing to that ZIP to sync changes back.`,
231
+ };
232
+ }
233
+ //# sourceMappingURL=exportWiki.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exportWiki.js","sourceRoot":"","sources":["../../src/tools/exportWiki.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { z } from "zod";
2
+ export declare const extractLinksInputSchema: z.ZodObject<{
3
+ tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
4
+ folder: z.ZodOptional<z.ZodString>;
5
+ }, "strip", z.ZodTypeAny, {
6
+ tags?: string[] | undefined;
7
+ folder?: string | undefined;
8
+ }, {
9
+ tags?: string[] | undefined;
10
+ folder?: string | undefined;
11
+ }>;
12
+ export type ExtractLinksInput = z.infer<typeof extractLinksInputSchema>;
13
+ export declare function extractLinks(input: ExtractLinksInput, userId?: string): Promise<object>;
14
+ //# sourceMappingURL=extractLinks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractLinks.d.ts","sourceRoot":"","sources":["../../src/tools/extractLinks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,uBAAuB;;;;;;;;;EASlC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,wBAAsB,YAAY,CAChC,KAAK,EAAE,iBAAiB,EACxB,MAAM,SAAK,GACV,OAAO,CAAC,MAAM,CAAC,CAWjB"}
@@ -0,0 +1,25 @@
1
+ import { z } from "zod";
2
+ import { getPostgresSearchIndex } from "../search/postgresIndex.js";
3
+ export const extractLinksInputSchema = z.object({
4
+ tags: z
5
+ .array(z.string())
6
+ .optional()
7
+ .describe("Filter pages by tag (any match). At least one of tags or folder is required."),
8
+ folder: z
9
+ .string()
10
+ .optional()
11
+ .describe("Filter pages by name prefix (e.g. 'projects/brainsapp')."),
12
+ });
13
+ export async function extractLinks(input, userId = "") {
14
+ const tags = (input.tags ?? []).map((t) => t.trim()).filter((t) => t.length > 0);
15
+ const folder = input.folder?.trim();
16
+ if (tags.length === 0 && !folder) {
17
+ throw new Error("At least one filter is required: tags or folder");
18
+ }
19
+ return await getPostgresSearchIndex().extractLinksForStalePages({
20
+ tags,
21
+ folder,
22
+ userId,
23
+ });
24
+ }
25
+ //# sourceMappingURL=extractLinks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractLinks.js","sourceRoot":"","sources":["../../src/tools/extractLinks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAEpE,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,IAAI,EAAE,CAAC;SACJ,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,QAAQ,EAAE;SACV,QAAQ,CAAC,8EAA8E,CAAC;IAC3F,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,0DAA0D,CAAC;CACxE,CAAC,CAAC;AAIH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAwB,EACxB,MAAM,GAAG,EAAE;IAEX,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACjF,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;IACpC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,MAAM,sBAAsB,EAAE,CAAC,yBAAyB,CAAC;QAC9D,IAAI;QACJ,MAAM;QACN,MAAM;KACP,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { z } from "zod";
2
+ import { type StorageClient } from "../storage/client.js";
3
+ export declare const generateClaudeInstructionsInputSchema: z.ZodObject<{
4
+ projectName: z.ZodString;
5
+ variant: z.ZodDefault<z.ZodEnum<["minimal", "extended", "pointer"]>>;
6
+ projectPage: z.ZodOptional<z.ZodString>;
7
+ inScopePaths: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
8
+ materialOutcomeDefinition: z.ZodOptional<z.ZodString>;
9
+ includeExamples: z.ZodDefault<z.ZodBoolean>;
10
+ }, "strip", z.ZodTypeAny, {
11
+ projectName: string;
12
+ variant: "minimal" | "extended" | "pointer";
13
+ includeExamples: boolean;
14
+ projectPage?: string | undefined;
15
+ inScopePaths?: string[] | undefined;
16
+ materialOutcomeDefinition?: string | undefined;
17
+ }, {
18
+ projectName: string;
19
+ variant?: "minimal" | "extended" | "pointer" | undefined;
20
+ projectPage?: string | undefined;
21
+ inScopePaths?: string[] | undefined;
22
+ materialOutcomeDefinition?: string | undefined;
23
+ includeExamples?: boolean | undefined;
24
+ }>;
25
+ export type GenerateClaudeInstructionsInput = z.infer<typeof generateClaudeInstructionsInputSchema>;
26
+ export declare function generateClaudeInstructions(input: GenerateClaudeInstructionsInput, drive?: StorageClient, userId?: string): Promise<object>;
27
+ //# sourceMappingURL=generateClaudeInstructions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateClaudeInstructions.d.ts","sourceRoot":"","sources":["../../src/tools/generateClaudeInstructions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AA0C1D,eAAO,MAAM,qCAAqC;;;;;;;;;;;;;;;;;;;;;EA+BhD,CAAC;AAEH,MAAM,MAAM,+BAA+B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qCAAqC,CAAC,CAAC;AAgMpG,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,+BAA+B,EACtC,KAAK,CAAC,EAAE,aAAa,EACrB,MAAM,SAAK,GACV,OAAO,CAAC,MAAM,CAAC,CA2CjB"}
@@ -0,0 +1,263 @@
1
+ import { z } from "zod";
2
+ import { gatherProjectContext, } from "./instructionContext.js";
3
+ function slugifyProjectName(value) {
4
+ const slug = value
5
+ .trim()
6
+ .toLowerCase()
7
+ .replace(/[^a-z0-9-]+/g, "-")
8
+ .replace(/-+/g, "-")
9
+ .replace(/^-|-$/g, "");
10
+ return slug || "project";
11
+ }
12
+ /**
13
+ * The project's real slug.
14
+ *
15
+ * Prefer deriving it from projectPage, which callers set to the actual project
16
+ * location, over slugifying the display name. init_project accepts an explicit
17
+ * projectSlug that need not match slugify(projectName) — and when it did not,
18
+ * every write-back target in the emitted block pointed at a folder that does
19
+ * not exist. That is the "slug drift from generation" seen on cressida, whose
20
+ * block referenced projects/cressida-wagon/. Reproduced deterministically:
21
+ * projectSlug "index-probe-tmp" with projectName "Index Probe" emitted targets
22
+ * under projects/index-probe/ (GH #436).
23
+ *
24
+ * Handles both shapes callers pass: "projects/<slug>/readme.md" and the
25
+ * defaultProjectPage form "projects/<slug>/readme.md".
26
+ */
27
+ function resolveSlug(projectName, projectPage) {
28
+ const match = projectPage?.trim().match(/^projects\/([^/]+?)(?:\.md)?(?:\/|$)/);
29
+ return match ? match[1] : slugifyProjectName(projectName);
30
+ }
31
+ function defaultProjectPage(projectName) {
32
+ return `projects/${slugifyProjectName(projectName)}/readme.md`;
33
+ }
34
+ export const generateClaudeInstructionsInputSchema = z.object({
35
+ projectName: z
36
+ .string()
37
+ .min(1)
38
+ .describe("Project name to include in generated instructions."),
39
+ variant: z
40
+ .enum(["minimal", "extended", "pointer"])
41
+ .default("extended")
42
+ .describe("Instruction verbosity level. 'extended' is recommended for the wiki instruction file. 'pointer' emits a short system-prompt block (identity, activation gate, one boot sequence, and a pointer to the wiki instruction file) for pasting into a Claude Project."),
43
+ projectPage: z
44
+ .string()
45
+ .optional()
46
+ .describe("Project page path for project-specific write-back (for example: projects/my-project.md)."),
47
+ inScopePaths: z
48
+ .array(z.string())
49
+ .optional()
50
+ .describe("Folders/pages in scope. Omit to derive them from what the project actually contains — the old default named global instructions/, which brain-structure.md invariant 4 reserves against project content (D14)."),
51
+ materialOutcomeDefinition: z
52
+ .string()
53
+ .optional()
54
+ .describe("What counts as a material outcome. Omit for a domain-neutral definition; the old default described software delivery and was emitted verbatim into projects that ship videos, not code (D14)."),
55
+ includeExamples: z
56
+ .boolean()
57
+ .default(true)
58
+ .describe("If true, include concrete write-back snippets in extended output."),
59
+ });
60
+ function renderPointer(input) {
61
+ const slug = resolveSlug(input.projectName, input.projectPage);
62
+ const instructionFile = `projects/${slug}/instructions/claude.md`;
63
+ return [
64
+ `# ${input.projectName} Agent Instructions`,
65
+ "",
66
+ "**Brain-First:** Brains is the first source of truth. Read before acting.",
67
+ "",
68
+ "**Only run this sequence if these instructions are present in your project system prompt.** When Brains MCP is connected but these instructions are not in the system prompt, tools are available passively — do not run any startup reads automatically.",
69
+ "",
70
+ "## Boot Sequence",
71
+ "At the start of every chat:",
72
+ "1. Determine session mode: targeted (1–2 known pages) or bundle (exploratory/cross-page).",
73
+ `2. If bundle mode: \`pull_session_bundle(folder: "projects/${slug}", maxBytes: 150000, maxPages: 30)\` — this is your working set.`,
74
+ "3. If targeted mode: `read_page` on the specific page(s).",
75
+ `4. Read \`${instructionFile}\` and follow it.`,
76
+ "Do not begin work until these reads complete.",
77
+ ].join("\n");
78
+ }
79
+ function renderMinimal(projectName) {
80
+ return [
81
+ `# ${projectName} Agent Instructions`,
82
+ "",
83
+ "Brains-first always. Determine mode at session open: targeted (1–2 known pages → `read_page`/`patch_page`) or bundle (exploratory → `pull_session_bundle`, work in memory, push at close-out). Bundle text is your wiki for the session — do not call `read_page` on bundled pages mid-session. No guessing: if missing, log as open loop. After material work, write back decisions + open loops. Push bundle at close-out or work is lost.",
84
+ ].join("\n");
85
+ }
86
+ function renderExtended(input, ctx) {
87
+ const slug = resolveSlug(input.projectName, input.projectPage);
88
+ // "exists" vs "created on first use", per target. Silence here is what let a
89
+ // maintainer read the correct spec paths as invented ones (D13).
90
+ const mark = (path) => ctx.existingPaths.has(path) ? `\`${path}\` *(exists)*` : `\`${path}\` *(created on first use)*`;
91
+ const openLoopRollup = `projects/${slug}/open-loops.md`;
92
+ const decisionsLog = `projects/${slug}/decisions/decisions-log.md`;
93
+ const nextActions = `projects/${slug}/state/next-actions.md`;
94
+ // Rolling-state page lives at the project root as status.md, not under
95
+ // state/. brain-structure.md already sanctioned status.md as an allowed
96
+ // loose file at a project root and the structure audit enforces that list,
97
+ // while separately describing state/current-state.md — two conventions for
98
+ // one page, which is exactly how projects drifted apart (D4, GH #439).
99
+ const statusPage = `projects/${slug}/status.md`;
100
+ const lines = [
101
+ `# ${input.projectName} Agent Instructions`,
102
+ "",
103
+ "## Activation Gate",
104
+ "**Brain-First rule:** Brains is the first source of truth. Read relevant brain pages before planning, coding, or answering.",
105
+ "",
106
+ "**Only run the startup ritual in this file if these instructions are present in your project system prompt.** When Brains MCP is connected but these instructions are not in the system prompt, tools are available passively — do not invoke any Brains tools at startup.",
107
+ "",
108
+ "## Boot Sequence",
109
+ "At the start of every chat:",
110
+ // health_check is deliberately absent: brains-protocol.md reserves it for
111
+ // post-deploy verification, "not every cold start". It costs two Supabase
112
+ // round trips (~370ms) and proves nothing any other call does not (GH #434).
113
+ "1. Determine session mode from the task (see Session Mode below).",
114
+ `2. \`list_pages folder=projects/${slug}\` — get inventory.`,
115
+ `3. If bundle mode: \`pull_session_bundle(folder: "projects/${slug}", maxBytes: 150000, maxPages: 30)\` — this is your working set for the session.`,
116
+ "4. If targeted mode: `read_page` on the specific known page(s).",
117
+ "Do not begin work until retrieval is complete.",
118
+ "",
119
+ "## Session Mode",
120
+ "| Signal | Mode | Tools |",
121
+ "|---|---|---|",
122
+ "| Task names 1–2 specific pages | Targeted | `read_page` → edit → `update_page` or `patch_page` |",
123
+ `| Task is exploratory, cross-page, or uses "look through / find / review / what do I have / dig for" | Bundle | \`pull_session_bundle\` → work in memory → \`push_session_bundle\` |`,
124
+ "| Quick question, no writes | Remote-only | MCP reads directly |",
125
+ "",
126
+ "**When in doubt, use bundle mode.**",
127
+ "",
128
+ "## Working Set Contract (Bundle Mode)",
129
+ "- The bundle text IS your wiki for this session.",
130
+ "- Do NOT call `read_page` on pages already in the bundle.",
131
+ "- Do NOT call `patch_page` or `update_page` mid-session on bundle pages.",
132
+ "- Edit the bundle text in memory. Push the full modified bundle at close-out.",
133
+ "- New pages created mid-session (not in bundle) may be written via `create_page` immediately.",
134
+ "",
135
+ "## Project Scope",
136
+ `- Active project slug: \`${slug}\``,
137
+ ctx.subdirs.length > 0
138
+ ? `- Subdirs in use: ${ctx.subdirs.map((d) => `\`${d}/\``).join(", ")}.`
139
+ : "- Subdirs: `canonical/`, `decisions/`, `data/`, `history/`, `state/`, plus any the project needs (e.g. `events/`, `scripts/`, `resources/`).",
140
+ ...(ctx.subProjects.length > 0
141
+ ? [`- Sub-projects: ${ctx.subProjects.map((s) => `\`projects/${s}/\``).join(", ")}. Their pages count toward this project and arrive in a folder-scoped bundle for it.`]
142
+ : []),
143
+ `- All new pages must live under \`projects/${slug}/...\`.`,
144
+ "- Full layout spec: `instructions/brain-structure.md`.",
145
+ "",
146
+ "In-scope folders/pages:",
147
+ ...(input.inScopePaths && input.inScopePaths.length > 0
148
+ ? input.inScopePaths.map((p) => `- ${p}`)
149
+ : [
150
+ `- \`projects/${slug}/\` — everything this project owns`,
151
+ `- \`projects/${slug}/instructions/claude.md\` — this file`,
152
+ ]),
153
+ "",
154
+ `Material outcome: ${input.materialOutcomeDefinition ?? "Any result that changes this project's state, plans, or committed decisions — not a question answered or a page merely read."}`,
155
+ "",
156
+ "## Write-Back (mandatory after material work)",
157
+ // init_project scaffolds only readme.md and the instruction file. The
158
+ // targets below are created on first use, which the block previously left
159
+ // unsaid — an agent told to append to a decisions log would reach for
160
+ // patch_page, which fails on a page that does not exist, and could read
161
+ // that as being in the wrong project (D1).
162
+ //
163
+ // Stated rather than scaffolded: bundles now select by folder, so empty
164
+ // stubs would be pulled into every session for the project's whole life.
165
+ ctx.exists
166
+ ? "Each target below is marked with whether it exists right now. Use `create_page` for one that does not — `patch_page` fails on a missing page, and that failure reads like being in the wrong project."
167
+ : "**These pages are created on first use.** Only `readme.md` and this instruction file exist after `init_project`. Use `create_page` for a target that does not exist yet, and `patch_page` only once it does — `patch_page` fails on a missing page.",
168
+ "",
169
+ "After any session with writes, record:",
170
+ `- Decisions → ${mark(decisionsLog)} (append-only) + \`projects/${slug}/decisions/<topic>.md\` for rationale-heavy ones`,
171
+ `- Open loops → detail at \`projects/${slug}/open-loops/<loop-slug>.md\` (one page per loop) + one-line entry in ${mark(openLoopRollup)} (project rollup)`,
172
+ `- Next actions → ${mark(nextActions)}`,
173
+ `- Current state changes → ${mark(statusPage)}`,
174
+ "- Unanswered questions → inline as `- [ ] Question:` in the relevant project page",
175
+ `- Raw ingest → \`projects/${slug}/data/<file>.md\``,
176
+ `- Stable facts → \`projects/${slug}/canonical/<file>.md\``,
177
+ "",
178
+ "Hub-and-spoke for loops: detail page + rollup entry. No standalone aggregate files at brain root.",
179
+ "",
180
+ "## Close-Out Ritual",
181
+ 'Before ending any session where writes were made:',
182
+ '1. Summarise what changed: "Here\'s what we did: X. Ready to push?"',
183
+ '2. On confirmation: push in pages mode, carrying each page\'s `revision` token from the pull so concurrent writes are detected:',
184
+ ' `push_session_bundle(pages: [{name: "...", content: "...", revision: "<revision-from-pull>"}])`',
185
+ "3. Local work does not persist between chats — push or lose it.",
186
+ "",
187
+ "## Completion Gate",
188
+ "- [ ] Read relevant brain pages first (from bundle or targeted reads)",
189
+ "- [ ] No unsupported assumptions",
190
+ "- [ ] Decisions and open loops written back",
191
+ "- [ ] If bundle session: full modified bundle pushed via `push_session_bundle`",
192
+ "",
193
+ "Failure condition: write-back skipped, bundle not pushed — task is not done.",
194
+ "",
195
+ "## Scope Creep",
196
+ "If a new feature idea or architectural direction emerges mid-session that isn't already an open loop — name it, log it, do not design or implement it now.",
197
+ "",
198
+ "## API Notes",
199
+ "- **`patch_page` ops** — field sets vary by type:",
200
+ " - `replace`: `find`, `replace` (optionally `occurrence`, `all`)",
201
+ " - `insert_after` / `insert_before`: `anchor`, `text` (optionally `occurrence`)",
202
+ " - `append`: `text`",
203
+ " - `replace_lines`: `startLine`, `endLine`, `text`",
204
+ ];
205
+ if (input.includeExamples) {
206
+ lines.push("", "## Write-Back Snippets", "### Decision", "**Decision:** [what was decided]", "**Why:** [reason]", "**Implications:** [what changes now]", "", "### Open Loop", `- [ ] [what is unresolved] → detail at \`projects/${slug}/open-loops/<loop-slug>.md\``, "", "### Next Action", `- [ ] [single concrete action, owner if known] → \`${nextActions}\``);
207
+ }
208
+ // A declared filing system means this project is audited against that skill's
209
+ // layout, not the Operator invariants the block above assumes. Say so rather
210
+ // than quietly handing it the wrong write-back table.
211
+ if (ctx.filing) {
212
+ lines.push("", "## Filing Override", `This project declares \`filing: ${ctx.filing}\` in its readme, so it is governed and audited by that skill, **not** by the Operator layout. Treat the write-back paths above as Operator defaults that may not apply — follow \`${ctx.filing}\`'s own layout where they differ.`);
213
+ }
214
+ // Anything hand-written survives regeneration. Without this, a refresh returns
215
+ // a block with the project's voice, contacts and conventions missing — so
216
+ // nobody refreshes, the file rots, and the drift this generator exists to
217
+ // prevent happens to the generator's own output (D13).
218
+ if (ctx.preservedRegion) {
219
+ lines.push("", ctx.preservedRegion);
220
+ }
221
+ return lines.join("\n");
222
+ }
223
+ export async function generateClaudeInstructions(input, drive, userId = "") {
224
+ const projectPage = input.projectPage?.trim() || defaultProjectPage(input.projectName);
225
+ const slug = resolveSlug(input.projectName, input.projectPage);
226
+ const openLoopRollup = `projects/${slug}/open-loops.md`;
227
+ const decisionsLog = `projects/${slug}/decisions/decisions-log.md`;
228
+ const nextActions = `projects/${slug}/state/next-actions.md`;
229
+ const statusPage = `projects/${slug}/status.md`;
230
+ const candidatePaths = [decisionsLog, openLoopRollup, nextActions, statusPage, projectPage];
231
+ // Only the extended variant renders write-back targets or scope, so the
232
+ // minimal and pointer variants stay free of wiki round trips.
233
+ const ctx = drive && input.variant === "extended"
234
+ ? await gatherProjectContext(drive, slug, "claude", candidatePaths, userId)
235
+ : { exists: false, pageCount: 0, subdirs: [], subProjects: [], existingPaths: new Set() };
236
+ const markdown = input.variant === "minimal"
237
+ ? renderMinimal(input.projectName)
238
+ : input.variant === "pointer"
239
+ ? renderPointer(input)
240
+ : renderExtended(input, ctx);
241
+ return {
242
+ success: true,
243
+ platform: "claude",
244
+ variant: input.variant,
245
+ projectName: input.projectName,
246
+ projectPage,
247
+ writeBackTargets: [decisionsLog, openLoopRollup, projectPage],
248
+ // Reported alongside the block so a caller can see what the generator knew,
249
+ // rather than having to infer it from the prose.
250
+ projectContext: {
251
+ exists: ctx.exists,
252
+ pageCount: ctx.pageCount,
253
+ subdirs: ctx.subdirs,
254
+ subProjects: ctx.subProjects,
255
+ ...(ctx.filing ? { filing: ctx.filing } : {}),
256
+ existingTargets: candidatePaths.filter((p) => ctx.existingPaths.has(p)),
257
+ missingTargets: candidatePaths.filter((p) => !ctx.existingPaths.has(p)),
258
+ preservedDomainContext: ctx.preservedRegion !== undefined,
259
+ },
260
+ markdown,
261
+ };
262
+ }
263
+ //# sourceMappingURL=generateClaudeInstructions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateClaudeInstructions.js","sourceRoot":"","sources":["../../src/tools/generateClaudeInstructions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { z } from "zod";
2
+ import { type StorageClient } from "../storage/client.js";
3
+ export declare const generateGptInstructionsInputSchema: z.ZodObject<{
4
+ projectName: z.ZodString;
5
+ variant: z.ZodDefault<z.ZodEnum<["minimal", "extended", "pointer"]>>;
6
+ projectPage: z.ZodOptional<z.ZodString>;
7
+ inScopePaths: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
8
+ materialOutcomeDefinition: z.ZodOptional<z.ZodString>;
9
+ includeExamples: z.ZodDefault<z.ZodBoolean>;
10
+ }, "strip", z.ZodTypeAny, {
11
+ projectName: string;
12
+ variant: "minimal" | "extended" | "pointer";
13
+ includeExamples: boolean;
14
+ projectPage?: string | undefined;
15
+ inScopePaths?: string[] | undefined;
16
+ materialOutcomeDefinition?: string | undefined;
17
+ }, {
18
+ projectName: string;
19
+ variant?: "minimal" | "extended" | "pointer" | undefined;
20
+ projectPage?: string | undefined;
21
+ inScopePaths?: string[] | undefined;
22
+ materialOutcomeDefinition?: string | undefined;
23
+ includeExamples?: boolean | undefined;
24
+ }>;
25
+ export type GenerateGptInstructionsInput = z.infer<typeof generateGptInstructionsInputSchema>;
26
+ export declare function generateGptInstructions(input: GenerateGptInstructionsInput, drive?: StorageClient, userId?: string): Promise<object>;
27
+ //# sourceMappingURL=generateGptInstructions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateGptInstructions.d.ts","sourceRoot":"","sources":["../../src/tools/generateGptInstructions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAqC1D,eAAO,MAAM,kCAAkC;;;;;;;;;;;;;;;;;;;;;EA2B7C,CAAC;AAEH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AA4I9F,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,4BAA4B,EACnC,KAAK,CAAC,EAAE,aAAa,EACrB,MAAM,SAAK,GACV,OAAO,CAAC,MAAM,CAAC,CAuCjB"}