spidersan 0.20.1 → 0.20.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 (310) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/bin/command-registry.d.ts +25 -0
  3. package/dist/bin/command-registry.d.ts.map +1 -0
  4. package/dist/bin/command-registry.js +29 -0
  5. package/dist/bin/command-registry.js.map +1 -0
  6. package/dist/bin/spidersan.d.ts +16 -0
  7. package/dist/bin/spidersan.d.ts.map +1 -0
  8. package/dist/bin/spidersan.js +98 -0
  9. package/dist/bin/spidersan.js.map +1 -0
  10. package/dist/commands/abandon.d.ts +8 -0
  11. package/dist/commands/abandon.d.ts.map +1 -0
  12. package/dist/commands/abandon.js +43 -0
  13. package/dist/commands/abandon.js.map +1 -0
  14. package/dist/commands/cleanup.d.ts +8 -0
  15. package/dist/commands/cleanup.d.ts.map +1 -0
  16. package/dist/commands/cleanup.js +50 -0
  17. package/dist/commands/cleanup.js.map +1 -0
  18. package/dist/commands/config.d.ts +15 -0
  19. package/dist/commands/config.d.ts.map +1 -0
  20. package/dist/commands/config.js +309 -0
  21. package/dist/commands/config.js.map +1 -0
  22. package/dist/commands/conflicts.d.ts +34 -0
  23. package/dist/commands/conflicts.d.ts.map +1 -0
  24. package/dist/commands/conflicts.js +773 -0
  25. package/dist/commands/conflicts.js.map +1 -0
  26. package/dist/commands/context.d.ts +8 -0
  27. package/dist/commands/context.d.ts.map +1 -0
  28. package/dist/commands/context.js +104 -0
  29. package/dist/commands/context.js.map +1 -0
  30. package/dist/commands/cross-conflicts.d.ts +17 -0
  31. package/dist/commands/cross-conflicts.d.ts.map +1 -0
  32. package/dist/commands/cross-conflicts.js +273 -0
  33. package/dist/commands/cross-conflicts.js.map +1 -0
  34. package/dist/commands/depends.d.ts +11 -0
  35. package/dist/commands/depends.d.ts.map +1 -0
  36. package/dist/commands/depends.js +63 -0
  37. package/dist/commands/depends.js.map +1 -0
  38. package/dist/commands/doctor.d.ts +30 -0
  39. package/dist/commands/doctor.d.ts.map +1 -0
  40. package/dist/commands/doctor.js +558 -0
  41. package/dist/commands/doctor.js.map +1 -0
  42. package/dist/commands/git-watch.d.ts +24 -0
  43. package/dist/commands/git-watch.d.ts.map +1 -0
  44. package/dist/commands/git-watch.js +84 -0
  45. package/dist/commands/git-watch.js.map +1 -0
  46. package/dist/commands/github-sync.d.ts +42 -0
  47. package/dist/commands/github-sync.d.ts.map +1 -0
  48. package/dist/commands/github-sync.js +191 -0
  49. package/dist/commands/github-sync.js.map +1 -0
  50. package/dist/commands/index.d.ts +25 -0
  51. package/dist/commands/index.d.ts.map +1 -0
  52. package/dist/commands/index.js +29 -0
  53. package/dist/commands/index.js.map +1 -0
  54. package/dist/commands/init.d.ts +8 -0
  55. package/dist/commands/init.d.ts.map +1 -0
  56. package/dist/commands/init.js +23 -0
  57. package/dist/commands/init.js.map +1 -0
  58. package/dist/commands/list.d.ts +3 -0
  59. package/dist/commands/list.d.ts.map +1 -0
  60. package/dist/commands/list.js +45 -0
  61. package/dist/commands/list.js.map +1 -0
  62. package/dist/commands/log.d.ts +7 -0
  63. package/dist/commands/log.d.ts.map +1 -0
  64. package/dist/commands/log.js +110 -0
  65. package/dist/commands/log.js.map +1 -0
  66. package/dist/commands/merge-order.d.ts +9 -0
  67. package/dist/commands/merge-order.d.ts.map +1 -0
  68. package/dist/commands/merge-order.js +100 -0
  69. package/dist/commands/merge-order.js.map +1 -0
  70. package/dist/commands/merged.d.ts +8 -0
  71. package/dist/commands/merged.d.ts.map +1 -0
  72. package/dist/commands/merged.js +37 -0
  73. package/dist/commands/merged.js.map +1 -0
  74. package/dist/commands/pr-check.d.ts +24 -0
  75. package/dist/commands/pr-check.d.ts.map +1 -0
  76. package/dist/commands/pr-check.js +85 -0
  77. package/dist/commands/pr-check.js.map +1 -0
  78. package/dist/commands/ready-check.d.ts +11 -0
  79. package/dist/commands/ready-check.d.ts.map +1 -0
  80. package/dist/commands/ready-check.js +150 -0
  81. package/dist/commands/ready-check.js.map +1 -0
  82. package/dist/commands/rebase-helper.d.ts +3 -0
  83. package/dist/commands/rebase-helper.d.ts.map +1 -0
  84. package/dist/commands/rebase-helper.js +52 -0
  85. package/dist/commands/rebase-helper.js.map +1 -0
  86. package/dist/commands/register.d.ts +10 -0
  87. package/dist/commands/register.d.ts.map +1 -0
  88. package/dist/commands/register.js +176 -0
  89. package/dist/commands/register.js.map +1 -0
  90. package/dist/commands/registry-sync.d.ts +9 -0
  91. package/dist/commands/registry-sync.d.ts.map +1 -0
  92. package/dist/commands/registry-sync.js +129 -0
  93. package/dist/commands/registry-sync.js.map +1 -0
  94. package/dist/commands/stale.d.ts +17 -0
  95. package/dist/commands/stale.d.ts.map +1 -0
  96. package/dist/commands/stale.js +136 -0
  97. package/dist/commands/stale.js.map +1 -0
  98. package/dist/commands/sync.d.ts +21 -0
  99. package/dist/commands/sync.d.ts.map +1 -0
  100. package/dist/commands/sync.js +78 -0
  101. package/dist/commands/sync.js.map +1 -0
  102. package/dist/commands/verify-trunk.d.ts +18 -0
  103. package/dist/commands/verify-trunk.d.ts.map +1 -0
  104. package/dist/commands/verify-trunk.js +77 -0
  105. package/dist/commands/verify-trunk.js.map +1 -0
  106. package/dist/commands/watch.d.ts +8 -0
  107. package/dist/commands/watch.d.ts.map +1 -0
  108. package/dist/commands/watch.js +509 -0
  109. package/dist/commands/watch.js.map +1 -0
  110. package/dist/index.d.ts +8 -0
  111. package/dist/index.d.ts.map +1 -0
  112. package/dist/index.js +8 -0
  113. package/dist/index.js.map +1 -0
  114. package/dist/lib/activity.d.ts +53 -0
  115. package/dist/lib/activity.d.ts.map +1 -0
  116. package/dist/lib/activity.js +271 -0
  117. package/dist/lib/activity.js.map +1 -0
  118. package/dist/lib/agent-errors.d.ts +41 -0
  119. package/dist/lib/agent-errors.d.ts.map +1 -0
  120. package/dist/lib/agent-errors.js +145 -0
  121. package/dist/lib/agent-errors.js.map +1 -0
  122. package/dist/lib/ai/context-builder.d.ts +16 -0
  123. package/dist/lib/ai/context-builder.d.ts.map +1 -0
  124. package/dist/lib/ai/context-builder.js +229 -0
  125. package/dist/lib/ai/context-builder.js.map +1 -0
  126. package/dist/lib/ai/event-handler.d.ts +21 -0
  127. package/dist/lib/ai/event-handler.d.ts.map +1 -0
  128. package/dist/lib/ai/event-handler.js +111 -0
  129. package/dist/lib/ai/event-handler.js.map +1 -0
  130. package/dist/lib/ai/index.d.ts +13 -0
  131. package/dist/lib/ai/index.d.ts.map +1 -0
  132. package/dist/lib/ai/index.js +11 -0
  133. package/dist/lib/ai/index.js.map +1 -0
  134. package/dist/lib/ai/llm-client.d.ts +37 -0
  135. package/dist/lib/ai/llm-client.d.ts.map +1 -0
  136. package/dist/lib/ai/llm-client.js +225 -0
  137. package/dist/lib/ai/llm-client.js.map +1 -0
  138. package/dist/lib/ai/reasoner.d.ts +11 -0
  139. package/dist/lib/ai/reasoner.d.ts.map +1 -0
  140. package/dist/lib/ai/reasoner.js +227 -0
  141. package/dist/lib/ai/reasoner.js.map +1 -0
  142. package/dist/lib/ai/setup.d.ts +40 -0
  143. package/dist/lib/ai/setup.d.ts.map +1 -0
  144. package/dist/lib/ai/setup.js +154 -0
  145. package/dist/lib/ai/setup.js.map +1 -0
  146. package/dist/lib/ai/types.d.ts +135 -0
  147. package/dist/lib/ai/types.d.ts.map +1 -0
  148. package/dist/lib/ai/types.js +42 -0
  149. package/dist/lib/ai/types.js.map +1 -0
  150. package/dist/lib/ast.d.ts +44 -0
  151. package/dist/lib/ast.d.ts.map +1 -0
  152. package/dist/lib/ast.js +160 -0
  153. package/dist/lib/ast.js.map +1 -0
  154. package/dist/lib/carries.d.ts +22 -0
  155. package/dist/lib/carries.d.ts.map +1 -0
  156. package/dist/lib/carries.js +56 -0
  157. package/dist/lib/carries.js.map +1 -0
  158. package/dist/lib/colony-subscriber.d.ts +56 -0
  159. package/dist/lib/colony-subscriber.d.ts.map +1 -0
  160. package/dist/lib/colony-subscriber.js +265 -0
  161. package/dist/lib/colony-subscriber.js.map +1 -0
  162. package/dist/lib/config.d.ts +62 -0
  163. package/dist/lib/config.d.ts.map +1 -0
  164. package/dist/lib/config.js +235 -0
  165. package/dist/lib/config.js.map +1 -0
  166. package/dist/lib/conflict-analyzer.d.ts +33 -0
  167. package/dist/lib/conflict-analyzer.d.ts.map +1 -0
  168. package/dist/lib/conflict-analyzer.js +114 -0
  169. package/dist/lib/conflict-analyzer.js.map +1 -0
  170. package/dist/lib/conflict-renderer.d.ts +7 -0
  171. package/dist/lib/conflict-renderer.d.ts.map +1 -0
  172. package/dist/lib/conflict-renderer.js +145 -0
  173. package/dist/lib/conflict-renderer.js.map +1 -0
  174. package/dist/lib/conflict-tier.d.ts +20 -0
  175. package/dist/lib/conflict-tier.d.ts.map +1 -0
  176. package/dist/lib/conflict-tier.js +49 -0
  177. package/dist/lib/conflict-tier.js.map +1 -0
  178. package/dist/lib/crypto.d.ts +71 -0
  179. package/dist/lib/crypto.d.ts.map +1 -0
  180. package/dist/lib/crypto.js +176 -0
  181. package/dist/lib/crypto.js.map +1 -0
  182. package/dist/lib/ecosystem-env.d.ts +33 -0
  183. package/dist/lib/ecosystem-env.d.ts.map +1 -0
  184. package/dist/lib/ecosystem-env.js +62 -0
  185. package/dist/lib/ecosystem-env.js.map +1 -0
  186. package/dist/lib/errors.d.ts +35 -0
  187. package/dist/lib/errors.d.ts.map +1 -0
  188. package/dist/lib/errors.js +71 -0
  189. package/dist/lib/errors.js.map +1 -0
  190. package/dist/lib/git-events-subscriber.d.ts +86 -0
  191. package/dist/lib/git-events-subscriber.d.ts.map +1 -0
  192. package/dist/lib/git-events-subscriber.js +870 -0
  193. package/dist/lib/git-events-subscriber.js.map +1 -0
  194. package/dist/lib/git-merge-analyzer.d.ts +38 -0
  195. package/dist/lib/git-merge-analyzer.d.ts.map +1 -0
  196. package/dist/lib/git-merge-analyzer.js +23 -0
  197. package/dist/lib/git-merge-analyzer.js.map +1 -0
  198. package/dist/lib/git.d.ts +103 -0
  199. package/dist/lib/git.d.ts.map +1 -0
  200. package/dist/lib/git.js +670 -0
  201. package/dist/lib/git.js.map +1 -0
  202. package/dist/lib/github.d.ts +151 -0
  203. package/dist/lib/github.d.ts.map +1 -0
  204. package/dist/lib/github.js +425 -0
  205. package/dist/lib/github.js.map +1 -0
  206. package/dist/lib/graph.d.ts +38 -0
  207. package/dist/lib/graph.d.ts.map +1 -0
  208. package/dist/lib/graph.js +165 -0
  209. package/dist/lib/graph.js.map +1 -0
  210. package/dist/lib/machine.d.ts +14 -0
  211. package/dist/lib/machine.d.ts.map +1 -0
  212. package/dist/lib/machine.js +38 -0
  213. package/dist/lib/machine.js.map +1 -0
  214. package/dist/lib/notify.d.ts +68 -0
  215. package/dist/lib/notify.d.ts.map +1 -0
  216. package/dist/lib/notify.js +111 -0
  217. package/dist/lib/notify.js.map +1 -0
  218. package/dist/lib/outline.d.ts +6 -0
  219. package/dist/lib/outline.d.ts.map +1 -0
  220. package/dist/lib/outline.js +60 -0
  221. package/dist/lib/outline.js.map +1 -0
  222. package/dist/lib/push-registry.d.ts +22 -0
  223. package/dist/lib/push-registry.d.ts.map +1 -0
  224. package/dist/lib/push-registry.js +21 -0
  225. package/dist/lib/push-registry.js.map +1 -0
  226. package/dist/lib/reconcile.d.ts +100 -0
  227. package/dist/lib/reconcile.d.ts.map +1 -0
  228. package/dist/lib/reconcile.js +121 -0
  229. package/dist/lib/reconcile.js.map +1 -0
  230. package/dist/lib/regex-utils.d.ts +6 -0
  231. package/dist/lib/regex-utils.d.ts.map +1 -0
  232. package/dist/lib/regex-utils.js +24 -0
  233. package/dist/lib/regex-utils.js.map +1 -0
  234. package/dist/lib/register-renderer.d.ts +10 -0
  235. package/dist/lib/register-renderer.d.ts.map +1 -0
  236. package/dist/lib/register-renderer.js +19 -0
  237. package/dist/lib/register-renderer.js.map +1 -0
  238. package/dist/lib/remote-drift.d.ts +61 -0
  239. package/dist/lib/remote-drift.d.ts.map +1 -0
  240. package/dist/lib/remote-drift.js +227 -0
  241. package/dist/lib/remote-drift.js.map +1 -0
  242. package/dist/lib/security.d.ts +72 -0
  243. package/dist/lib/security.d.ts.map +1 -0
  244. package/dist/lib/security.js +180 -0
  245. package/dist/lib/security.js.map +1 -0
  246. package/dist/lib/semantic-analysis.d.ts +31 -0
  247. package/dist/lib/semantic-analysis.d.ts.map +1 -0
  248. package/dist/lib/semantic-analysis.js +120 -0
  249. package/dist/lib/semantic-analysis.js.map +1 -0
  250. package/dist/lib/supabase-credentials.d.ts +36 -0
  251. package/dist/lib/supabase-credentials.d.ts.map +1 -0
  252. package/dist/lib/supabase-credentials.js +43 -0
  253. package/dist/lib/supabase-credentials.js.map +1 -0
  254. package/dist/lib/trunk.d.ts +39 -0
  255. package/dist/lib/trunk.d.ts.map +1 -0
  256. package/dist/lib/trunk.js +116 -0
  257. package/dist/lib/trunk.js.map +1 -0
  258. package/dist/lib/update-check.d.ts +11 -0
  259. package/dist/lib/update-check.d.ts.map +1 -0
  260. package/dist/lib/update-check.js +113 -0
  261. package/dist/lib/update-check.js.map +1 -0
  262. package/dist/lib/watch-renderer.d.ts +13 -0
  263. package/dist/lib/watch-renderer.d.ts.map +1 -0
  264. package/dist/lib/watch-renderer.js +35 -0
  265. package/dist/lib/watch-renderer.js.map +1 -0
  266. package/dist/storage/adapter.d.ts +81 -0
  267. package/dist/storage/adapter.d.ts.map +1 -0
  268. package/dist/storage/adapter.js +8 -0
  269. package/dist/storage/adapter.js.map +1 -0
  270. package/dist/storage/branch-registry-store.d.ts +13 -0
  271. package/dist/storage/branch-registry-store.d.ts.map +1 -0
  272. package/dist/storage/branch-registry-store.js +2 -0
  273. package/dist/storage/branch-registry-store.js.map +1 -0
  274. package/dist/storage/factory.d.ts +6 -0
  275. package/dist/storage/factory.d.ts.map +1 -0
  276. package/dist/storage/factory.js +14 -0
  277. package/dist/storage/factory.js.map +1 -0
  278. package/dist/storage/index.d.ts +10 -0
  279. package/dist/storage/index.d.ts.map +1 -0
  280. package/dist/storage/index.js +11 -0
  281. package/dist/storage/index.js.map +1 -0
  282. package/dist/storage/json-branch-registry-store.d.ts +19 -0
  283. package/dist/storage/json-branch-registry-store.d.ts.map +1 -0
  284. package/dist/storage/json-branch-registry-store.js +138 -0
  285. package/dist/storage/json-branch-registry-store.js.map +1 -0
  286. package/dist/storage/local.d.ts +20 -0
  287. package/dist/storage/local.d.ts.map +1 -0
  288. package/dist/storage/local.js +40 -0
  289. package/dist/storage/local.js.map +1 -0
  290. package/dist/storage/memory-branch-registry-store.d.ts +16 -0
  291. package/dist/storage/memory-branch-registry-store.d.ts.map +1 -0
  292. package/dist/storage/memory-branch-registry-store.js +68 -0
  293. package/dist/storage/memory-branch-registry-store.js.map +1 -0
  294. package/dist/storage/supabase-registry-sync-client-impl.d.ts +40 -0
  295. package/dist/storage/supabase-registry-sync-client-impl.d.ts.map +1 -0
  296. package/dist/storage/supabase-registry-sync-client-impl.js +278 -0
  297. package/dist/storage/supabase-registry-sync-client-impl.js.map +1 -0
  298. package/dist/storage/supabase-registry-sync-client.d.ts +9 -0
  299. package/dist/storage/supabase-registry-sync-client.d.ts.map +1 -0
  300. package/dist/storage/supabase-registry-sync-client.js +2 -0
  301. package/dist/storage/supabase-registry-sync-client.js.map +1 -0
  302. package/dist/storage/supabase.d.ts +28 -0
  303. package/dist/storage/supabase.d.ts.map +1 -0
  304. package/dist/storage/supabase.js +56 -0
  305. package/dist/storage/supabase.js.map +1 -0
  306. package/dist/types/cloud.d.ts +102 -0
  307. package/dist/types/cloud.d.ts.map +1 -0
  308. package/dist/types/cloud.js +13 -0
  309. package/dist/types/cloud.js.map +1 -0
  310. package/package.json +1 -1
@@ -0,0 +1,38 @@
1
+ export type NodeId = string;
2
+ /**
3
+ * Build a conflict adjacency map: for each branch, which other branches
4
+ * share overlapping files?
5
+ * Pure function — takes branch data, returns a Map.
6
+ */
7
+ export declare function buildConflictGraph(branches: Array<{
8
+ name: string;
9
+ files: string[];
10
+ }>): Map<NodeId, NodeId[]>;
11
+ /**
12
+ * Fold declared dependencies into a conflict graph as DIRECTED edges.
13
+ * An edge u→v gives v in-degree, so v sorts after u: "A depends on B"
14
+ * becomes edge B→A (B merges first). Because conflict edges are symmetric,
15
+ * a dependent that ALSO file-overlaps its dep (the normal stacked-branch
16
+ * case) forms a 2-cycle that would swallow the dep edge and fall back to
17
+ * alphabetical cycle-breaking — so the opposing dependent→dep edge is
18
+ * REMOVED: the declared direction wins over the undirected conflict.
19
+ * Unknown/self deps are dropped by topologicalSort's normalization.
20
+ * Returns a new map; inputs untouched.
21
+ */
22
+ export declare function addDependencyEdges(graph: Map<NodeId, NodeId[]>, branches: Array<{
23
+ name: string;
24
+ dependsOn?: string[];
25
+ }>): Map<NodeId, NodeId[]>;
26
+ /**
27
+ * Topological sort with tie-breaking by blocking count.
28
+ * Returns branches in recommended merge order.
29
+ * Cycles are broken by inserting branches at earliest valid position.
30
+ * blockingCounts is optional; when omitted, sort is alphabetical within tied groups.
31
+ */
32
+ export declare function topologicalSort(nodes: NodeId[], graph: Map<NodeId, NodeId[]>, blockingCounts?: Map<NodeId, number>): NodeId[];
33
+ /**
34
+ * For each node, count how many other nodes are (transitively) blocked by it.
35
+ * Used for rarest-first prioritization.
36
+ */
37
+ export declare function calculateBlockingCounts(nodes: NodeId[], graph: Map<NodeId, NodeId[]>): Map<NodeId, number>;
38
+ //# sourceMappingURL=graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/lib/graph.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC;AA0C5B;;;;GAIG;AACH,wBAAgB,kBAAkB,CAC9B,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,GACnD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAqCvB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAC9B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAC5B,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,GACxD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAsBvB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC3B,KAAK,EAAE,MAAM,EAAE,EACf,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAC5B,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACrC,MAAM,EAAE,CAoDV;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACnC,KAAK,EAAE,MAAM,EAAE,EACf,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAC7B,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CA2BrB"}
@@ -0,0 +1,165 @@
1
+ function compareNodeIds(left, right, blockingCounts) {
2
+ if (blockingCounts) {
3
+ const leftCount = blockingCounts.get(left) ?? 0;
4
+ const rightCount = blockingCounts.get(right) ?? 0;
5
+ if (leftCount !== rightCount) {
6
+ return rightCount - leftCount;
7
+ }
8
+ }
9
+ return left.localeCompare(right);
10
+ }
11
+ function normalizeGraph(nodes, graph) {
12
+ const knownNodes = new Set(nodes);
13
+ const normalized = new Map();
14
+ for (const node of nodes) {
15
+ const neighbors = graph.get(node) ?? [];
16
+ const uniqueNeighbors = new Set();
17
+ for (const neighbor of neighbors) {
18
+ if (neighbor !== node && knownNodes.has(neighbor)) {
19
+ uniqueNeighbors.add(neighbor);
20
+ }
21
+ }
22
+ normalized.set(node, Array.from(uniqueNeighbors).sort((left, right) => left.localeCompare(right)));
23
+ }
24
+ return normalized;
25
+ }
26
+ /**
27
+ * Build a conflict adjacency map: for each branch, which other branches
28
+ * share overlapping files?
29
+ * Pure function — takes branch data, returns a Map.
30
+ */
31
+ export function buildConflictGraph(branches) {
32
+ const fileToBranches = new Map();
33
+ for (const branch of branches) {
34
+ for (const file of new Set(branch.files)) {
35
+ let branchNames = fileToBranches.get(file);
36
+ if (!branchNames) {
37
+ branchNames = new Set();
38
+ fileToBranches.set(file, branchNames);
39
+ }
40
+ branchNames.add(branch.name);
41
+ }
42
+ }
43
+ const graph = new Map();
44
+ for (const branch of branches) {
45
+ const conflicts = new Set();
46
+ for (const file of new Set(branch.files)) {
47
+ const branchNames = fileToBranches.get(file);
48
+ if (!branchNames) {
49
+ continue;
50
+ }
51
+ for (const otherBranch of branchNames) {
52
+ if (otherBranch !== branch.name) {
53
+ conflicts.add(otherBranch);
54
+ }
55
+ }
56
+ }
57
+ graph.set(branch.name, Array.from(conflicts).sort((left, right) => left.localeCompare(right)));
58
+ }
59
+ return graph;
60
+ }
61
+ /**
62
+ * Fold declared dependencies into a conflict graph as DIRECTED edges.
63
+ * An edge u→v gives v in-degree, so v sorts after u: "A depends on B"
64
+ * becomes edge B→A (B merges first). Because conflict edges are symmetric,
65
+ * a dependent that ALSO file-overlaps its dep (the normal stacked-branch
66
+ * case) forms a 2-cycle that would swallow the dep edge and fall back to
67
+ * alphabetical cycle-breaking — so the opposing dependent→dep edge is
68
+ * REMOVED: the declared direction wins over the undirected conflict.
69
+ * Unknown/self deps are dropped by topologicalSort's normalization.
70
+ * Returns a new map; inputs untouched.
71
+ */
72
+ export function addDependencyEdges(graph, branches) {
73
+ const merged = new Map();
74
+ for (const [node, neighbors] of graph.entries()) {
75
+ merged.set(node, [...neighbors]);
76
+ }
77
+ for (const branch of branches) {
78
+ for (const dep of branch.dependsOn ?? []) {
79
+ const out = merged.get(dep) ?? [];
80
+ if (!out.includes(branch.name)) {
81
+ out.push(branch.name);
82
+ }
83
+ merged.set(dep, out);
84
+ const back = merged.get(branch.name);
85
+ if (back) {
86
+ merged.set(branch.name, back.filter((n) => n !== dep));
87
+ }
88
+ }
89
+ }
90
+ return merged;
91
+ }
92
+ /**
93
+ * Topological sort with tie-breaking by blocking count.
94
+ * Returns branches in recommended merge order.
95
+ * Cycles are broken by inserting branches at earliest valid position.
96
+ * blockingCounts is optional; when omitted, sort is alphabetical within tied groups.
97
+ */
98
+ export function topologicalSort(nodes, graph, blockingCounts) {
99
+ const normalizedGraph = normalizeGraph(nodes, graph);
100
+ const inDegree = new Map();
101
+ const remaining = new Set(nodes);
102
+ const available = [];
103
+ const order = [];
104
+ for (const node of nodes) {
105
+ inDegree.set(node, 0);
106
+ }
107
+ for (const neighbors of normalizedGraph.values()) {
108
+ for (const neighbor of neighbors) {
109
+ inDegree.set(neighbor, (inDegree.get(neighbor) ?? 0) + 1);
110
+ }
111
+ }
112
+ for (const node of nodes) {
113
+ if ((inDegree.get(node) ?? 0) === 0) {
114
+ available.push(node);
115
+ }
116
+ }
117
+ available.sort((left, right) => compareNodeIds(left, right, blockingCounts));
118
+ while (remaining.size > 0) {
119
+ let current = available.shift();
120
+ if (!current) {
121
+ current = Array.from(remaining).sort((left, right) => compareNodeIds(left, right, blockingCounts))[0];
122
+ }
123
+ if (!current || !remaining.has(current)) {
124
+ continue;
125
+ }
126
+ remaining.delete(current);
127
+ order.push(current);
128
+ for (const neighbor of normalizedGraph.get(current) ?? []) {
129
+ const nextDegree = (inDegree.get(neighbor) ?? 0) - 1;
130
+ inDegree.set(neighbor, nextDegree);
131
+ if (nextDegree === 0 && remaining.has(neighbor)) {
132
+ available.push(neighbor);
133
+ }
134
+ }
135
+ available.sort((left, right) => compareNodeIds(left, right, blockingCounts));
136
+ }
137
+ return order;
138
+ }
139
+ /**
140
+ * For each node, count how many other nodes are (transitively) blocked by it.
141
+ * Used for rarest-first prioritization.
142
+ */
143
+ export function calculateBlockingCounts(nodes, graph) {
144
+ const normalizedGraph = normalizeGraph(nodes, graph);
145
+ const counts = new Map();
146
+ for (const node of nodes) {
147
+ const seen = new Set();
148
+ const stack = [...(normalizedGraph.get(node) ?? [])];
149
+ while (stack.length > 0) {
150
+ const current = stack.pop();
151
+ if (!current || current === node || seen.has(current)) {
152
+ continue;
153
+ }
154
+ seen.add(current);
155
+ for (const neighbor of normalizedGraph.get(current) ?? []) {
156
+ if (!seen.has(neighbor) && neighbor !== node) {
157
+ stack.push(neighbor);
158
+ }
159
+ }
160
+ }
161
+ counts.set(node, seen.size);
162
+ }
163
+ return counts;
164
+ }
165
+ //# sourceMappingURL=graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph.js","sourceRoot":"","sources":["../../src/lib/graph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Single source of truth for this machine's identity (~/.envoak/machine.json).
3
+ *
4
+ * Four near-copies of this loader had drifted apart (registry-sync, dashboard,
5
+ * github-sync, and an inline one in cross-conflicts): two read `id || machine_id`,
6
+ * one read `machine_id || id`, and the missing-file fallback was either
7
+ * `fallback-<hostname>` or the literal 'unknown'. The id is used to exclude THIS
8
+ * machine when pulling cross-machine registries, so a divergent fallback means a
9
+ * machine can fail to filter itself out and conflict against its own branches
10
+ * (tb-ly0b).
11
+ */
12
+ import type { MachineIdentity } from '../types/cloud.js';
13
+ export declare function loadMachineIdentity(): Promise<MachineIdentity>;
14
+ //# sourceMappingURL=machine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machine.d.ts","sourceRoot":"","sources":["../../src/lib/machine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,eAAe,CAAC,CAsBpE"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Single source of truth for this machine's identity (~/.envoak/machine.json).
3
+ *
4
+ * Four near-copies of this loader had drifted apart (registry-sync, dashboard,
5
+ * github-sync, and an inline one in cross-conflicts): two read `id || machine_id`,
6
+ * one read `machine_id || id`, and the missing-file fallback was either
7
+ * `fallback-<hostname>` or the literal 'unknown'. The id is used to exclude THIS
8
+ * machine when pulling cross-machine registries, so a divergent fallback means a
9
+ * machine can fail to filter itself out and conflict against its own branches
10
+ * (tb-ly0b).
11
+ */
12
+ import { readFile } from 'fs/promises';
13
+ import { existsSync } from 'fs';
14
+ import { join } from 'path';
15
+ import { homedir, hostname as osHostname } from 'os';
16
+ export async function loadMachineIdentity() {
17
+ const configPath = join(homedir(), '.envoak', 'machine.json');
18
+ if (existsSync(configPath)) {
19
+ try {
20
+ const data = JSON.parse(await readFile(configPath, 'utf-8'));
21
+ const id = data.id || data.machine_id;
22
+ if (id) {
23
+ return {
24
+ id,
25
+ name: data.name || data.machine_name || 'unknown',
26
+ hostname: data.hostname || 'unknown',
27
+ };
28
+ }
29
+ }
30
+ catch {
31
+ // Unreadable/corrupt machine.json — fall through to the hostname
32
+ // fallback rather than throwing; identity is advisory, not critical.
33
+ }
34
+ }
35
+ const host = osHostname();
36
+ return { id: `fallback-${host}`, name: host, hostname: host };
37
+ }
38
+ //# sourceMappingURL=machine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machine.js","sourceRoot":"","sources":["../../src/lib/machine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,QAAQ,IAAI,UAAU,EAAE,MAAM,IAAI,CAAC;AAGrD,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACrC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;IAE9D,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;YAC7D,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC;YACtC,IAAI,EAAE,EAAE,CAAC;gBACL,OAAO;oBACH,EAAE;oBACF,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,YAAY,IAAI,SAAS;oBACjD,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,SAAS;iBACvC,CAAC;YACN,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACL,iEAAiE;YACjE,qEAAqE;QACzE,CAAC;IACL,CAAC;IAED,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,OAAO,EAAE,EAAE,EAAE,YAAY,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAClE,CAAC"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * notify — the delivery seam for git-watch alerts.
3
+ *
4
+ * Every alert in git-events-subscriber.ts used to inline its own transport:
5
+ * `execFile('envoak', ['hive','signal',...], cb)` with the callback discarding
6
+ * the result. When `envoak hive signal` was removed (Envoak#148, ADR-0002) all
7
+ * five sites died at once, and because no site inspected its own result the
8
+ * daemon went on logging success for a month (sp-8iqm).
9
+ *
10
+ * So the transport lives behind one interface, and that interface returns a
11
+ * RESULT rather than swallowing it. A caller cannot accidentally claim success:
12
+ * there is a value it has to look at.
13
+ *
14
+ * Adapters are selected by env, so swapping transports is config, not code:
15
+ * SPIDERSAN_ROOM_TOKEN — post to a toak.me room (POST /api/chat/send).
16
+ * Name matches the vault secret toak/SPIDERSAN_ROOM_TOKEN,
17
+ * so `envoak vault inject` supplies it directly.
18
+ * unset — nullNotifier: reports undelivered, never pretends
19
+ */
20
+ export type Delivered = {
21
+ ok: true;
22
+ } | {
23
+ ok: false;
24
+ reason: string;
25
+ };
26
+ export interface NotifyEvent {
27
+ repo: string;
28
+ branch: string;
29
+ /** Conflict tier. Callers gate on this themselves — policy is not ours. */
30
+ tier: number;
31
+ /** Human-readable line. May be LLM-generated — never key off this. */
32
+ message: string;
33
+ /**
34
+ * Stable identity of the underlying event, for deduplication. MUST be
35
+ * built only from deterministic facts (repo, branch, sha, action) — the
36
+ * tier>=2 path escalates to an LLM, so `message` differs between two
37
+ * deliveries of the same event and cannot be used here.
38
+ */
39
+ dedupe: string;
40
+ /** GitHub login or agent that triggered it, when known. */
41
+ agent?: string;
42
+ files?: string[];
43
+ /** Extra fields passed through to the room as structured metadata. */
44
+ details?: Record<string, unknown>;
45
+ }
46
+ /**
47
+ * A transport. One function — nothing here needs a class.
48
+ *
49
+ * CONTRACT: a Notifier MUST NOT throw. It reports failure by returning
50
+ * `{ok:false, reason}`. Call sites sit inside `catch` blocks that swallow
51
+ * AI-layer errors, so a thrown transport error would be misread as an AI
52
+ * failure and vanish — the same silence this module exists to remove.
53
+ */
54
+ export type Notifier = (event: NotifyEvent) => Promise<Delivered>;
55
+ /** Post to a toak.me room. The room join token is a read+write secret. */
56
+ export declare function toakRoomNotifier(token: string, fetchImpl?: typeof fetch): Notifier;
57
+ /**
58
+ * No transport configured. Reports undelivered rather than pretending — the
59
+ * whole point of sp-8iqm is that a severed channel must be visible.
60
+ */
61
+ export declare const nullNotifier: Notifier;
62
+ export declare function createNotifier(): Notifier;
63
+ /**
64
+ * Deliver, then log the real outcome. This is the ONLY place allowed to say a
65
+ * notification was sent, and it says so only after looking at the result.
66
+ */
67
+ export declare function deliver(notifier: Notifier, event: NotifyEvent, log: (m: string) => void): Promise<Delivered>;
68
+ //# sourceMappingURL=notify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/lib/notify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,MAAM,MAAM,SAAS,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;AAmClE,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,GAAE,OAAO,KAAa,GAAG,QAAQ,CAiCzF;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,QAGzB,CAAC;AAEH,wBAAgB,cAAc,IAAI,QAAQ,CAGzC;AAED;;;GAGG;AACH,wBAAsB,OAAO,CACzB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GACzB,OAAO,CAAC,SAAS,CAAC,CAKpB"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * notify — the delivery seam for git-watch alerts.
3
+ *
4
+ * Every alert in git-events-subscriber.ts used to inline its own transport:
5
+ * `execFile('envoak', ['hive','signal',...], cb)` with the callback discarding
6
+ * the result. When `envoak hive signal` was removed (Envoak#148, ADR-0002) all
7
+ * five sites died at once, and because no site inspected its own result the
8
+ * daemon went on logging success for a month (sp-8iqm).
9
+ *
10
+ * So the transport lives behind one interface, and that interface returns a
11
+ * RESULT rather than swallowing it. A caller cannot accidentally claim success:
12
+ * there is a value it has to look at.
13
+ *
14
+ * Adapters are selected by env, so swapping transports is config, not code:
15
+ * SPIDERSAN_ROOM_TOKEN — post to a toak.me room (POST /api/chat/send).
16
+ * Name matches the vault secret toak/SPIDERSAN_ROOM_TOKEN,
17
+ * so `envoak vault inject` supplies it directly.
18
+ * unset — nullNotifier: reports undelivered, never pretends
19
+ */
20
+ import { createHash } from 'crypto';
21
+ import { hostname } from 'os';
22
+ const TOAK_URL = process.env.TOAK_CHAT_URL || 'https://toak.me/api/chat/send';
23
+ // Bare name, no `agent:` prefix. Room allowlists match the sender string
24
+ // EXACTLY — the `agent:<id>` form in toak's send route governs verification
25
+ // upgrade, not allowlist matching, and conflating the two means an approved
26
+ // `spidersan` would not admit a sender of `agent:spidersan`. The machine goes
27
+ // in metadata instead, so one approval covers every host.
28
+ const SENDER = process.env.SPIDERSAN_AGENT || 'spidersan';
29
+ const MACHINE = process.env.TREEBIRD_MACHINE || hostname();
30
+ /**
31
+ * Stable per event, so a catch-up poll that replays an event minutes later
32
+ * does not post it twice. The server's own default is a ~5s time bucket, far
33
+ * shorter than our reconnect window.
34
+ */
35
+ function idempotencyKey(e) {
36
+ return createHash('sha256')
37
+ .update([e.repo, e.branch, e.tier, e.dedupe].join(' '))
38
+ .digest('hex');
39
+ }
40
+ /**
41
+ * The original emits were fire-and-forget with `{ timeout: 10000 }`. These are
42
+ * awaited inside the event handlers, so an unbounded fetch would stall the
43
+ * handler and queue every event behind it. Same budget as before.
44
+ */
45
+ const TIMEOUT_MS = 10_000;
46
+ function format(e) {
47
+ const who = e.agent ? ` by ${e.agent}` : '';
48
+ const files = e.files?.length ? `\nfiles: ${e.files.slice(0, 10).join(', ')}` : '';
49
+ return `TIER ${e.tier} conflict - ${e.repo}/${e.branch}${who}\n${e.message}${files}`;
50
+ }
51
+ /** Post to a toak.me room. The room join token is a read+write secret. */
52
+ export function toakRoomNotifier(token, fetchImpl = fetch) {
53
+ return async (event) => {
54
+ try {
55
+ const res = await fetchImpl(TOAK_URL, {
56
+ method: 'POST',
57
+ headers: { 'Content-Type': 'application/json' },
58
+ signal: AbortSignal.timeout(TIMEOUT_MS),
59
+ body: JSON.stringify({
60
+ token,
61
+ sender: SENDER,
62
+ content: format(event),
63
+ idempotency_key: idempotencyKey(event),
64
+ metadata: {
65
+ machine: MACHINE,
66
+ repo: event.repo,
67
+ branch: event.branch,
68
+ tier: event.tier,
69
+ agent: event.agent,
70
+ files: event.files,
71
+ ...event.details,
72
+ },
73
+ }),
74
+ });
75
+ // The POST response IS the call-time liveness check — no probe needed.
76
+ if (!res.ok) {
77
+ const body = await res.text().catch(() => '');
78
+ return { ok: false, reason: `${res.status} ${body.slice(0, 120)}`.trim() };
79
+ }
80
+ return { ok: true };
81
+ }
82
+ catch (err) {
83
+ return { ok: false, reason: err instanceof Error ? err.message : String(err) };
84
+ }
85
+ };
86
+ }
87
+ /**
88
+ * No transport configured. Reports undelivered rather than pretending — the
89
+ * whole point of sp-8iqm is that a severed channel must be visible.
90
+ */
91
+ export const nullNotifier = async () => ({
92
+ ok: false,
93
+ reason: 'no transport configured (set SPIDERSAN_ROOM_TOKEN)',
94
+ });
95
+ export function createNotifier() {
96
+ const token = process.env.SPIDERSAN_ROOM_TOKEN;
97
+ return token ? toakRoomNotifier(token) : nullNotifier;
98
+ }
99
+ /**
100
+ * Deliver, then log the real outcome. This is the ONLY place allowed to say a
101
+ * notification was sent, and it says so only after looking at the result.
102
+ */
103
+ export async function deliver(notifier, event, log) {
104
+ const result = await notifier(event);
105
+ if (result.ok)
106
+ log(`notified: TIER ${event.tier} ${event.repo}/${event.branch}`);
107
+ else
108
+ log(`NOT NOTIFIED (TIER ${event.tier} ${event.repo}/${event.branch}): ${result.reason}`);
109
+ return result;
110
+ }
111
+ //# sourceMappingURL=notify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/lib/notify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { type SymbolConflict, type SymbolInfo } from './ast.js';
2
+ /** ext includes the dot, e.g. '.py'. Only function/class/method symbols are kept. */
3
+ export declare function outlineSymbols(content: string, ext: string): SymbolInfo[] | null;
4
+ /** Same contract as ASTParser.findSymbolConflicts, for any language ast-grep outlines. null = unavailable. */
5
+ export declare function findOutlineConflicts(ext: string, contentA: string, labelA: string, contentB: string, labelB: string, baseContent?: string): SymbolConflict[] | null;
6
+ //# sourceMappingURL=outline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outline.d.ts","sourceRoot":"","sources":["../../src/lib/outline.ts"],"names":[],"mappings":"AAUA,OAAO,EAAkB,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAShF,qFAAqF;AACrF,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,GAAG,IAAI,CAiChF;AAED,8GAA8G;AAC9G,wBAAgB,oBAAoB,CAChC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAChC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAChC,WAAW,CAAC,EAAE,MAAM,GACrB,cAAc,EAAE,GAAG,IAAI,CAMzB"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * outline.ts — spike: symbol extraction for non-TS languages via `ast-grep outline`.
3
+ *
4
+ * Optional: needs the `ast-grep` binary on PATH. Returns null when it is missing
5
+ * or fails, so callers can skip (never conclude "no conflicts" from null).
6
+ */
7
+ import { execFileSync } from 'child_process';
8
+ import { mkdtempSync, writeFileSync, rmSync } from 'fs';
9
+ import { tmpdir } from 'os';
10
+ import { join } from 'path';
11
+ import { compareSymbols } from './ast.js';
12
+ /** ext includes the dot, e.g. '.py'. Only function/class/method symbols are kept. */
13
+ export function outlineSymbols(content, ext) {
14
+ const dir = mkdtempSync(join(tmpdir(), 'spidersan-outline-'));
15
+ try {
16
+ const file = join(dir, `f${ext}`);
17
+ writeFileSync(file, content);
18
+ const out = execFileSync('ast-grep', ['outline', '--json=compact', file], {
19
+ encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'],
20
+ });
21
+ const files = JSON.parse(out);
22
+ // `[]` = ast-grep doesn't know this file type; that is "unknown", not "no symbols"
23
+ if (!Array.isArray(files) || files.length === 0)
24
+ return null;
25
+ const items = files[0].items ?? [];
26
+ const bytes = Buffer.from(content);
27
+ const symbols = [];
28
+ const add = (it, parent) => {
29
+ const type = it.symbolType;
30
+ if (type !== 'function' && type !== 'class' && type !== 'method')
31
+ return;
32
+ symbols.push({
33
+ name: it.name, type,
34
+ startLine: it.range.start.line + 1, endLine: it.range.end.line + 1,
35
+ ...(parent ? { parent } : {}),
36
+ // byteOffset, not line slicing: exact text of the symbol
37
+ content: bytes.subarray(it.range.byteOffset.start, it.range.byteOffset.end).toString('utf-8'),
38
+ });
39
+ it.members?.forEach(m => add(m, it.name));
40
+ };
41
+ items.forEach(it => add(it));
42
+ return symbols;
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ finally {
48
+ rmSync(dir, { recursive: true, force: true });
49
+ }
50
+ }
51
+ /** Same contract as ASTParser.findSymbolConflicts, for any language ast-grep outlines. null = unavailable. */
52
+ export function findOutlineConflicts(ext, contentA, labelA, contentB, labelB, baseContent) {
53
+ const a = outlineSymbols(contentA, ext);
54
+ const b = outlineSymbols(contentB, ext);
55
+ const base = baseContent === undefined ? undefined : outlineSymbols(baseContent, ext);
56
+ if (!a || !b || base === null)
57
+ return null;
58
+ return compareSymbols(a, labelA, b, labelB, base);
59
+ }
60
+ //# sourceMappingURL=outline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outline.js","sourceRoot":"","sources":["../../src/lib/outline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Pushed-branch rows in spider_registries — the reader side.
3
+ *
4
+ * The `spidersan-webhook` edge function (treebird repo) registers every branch pushed
5
+ * to a watched repo, whoever pushed it (Codex, Copilot, a cloud agent, the web editor,
6
+ * a person), under one virtual machine, `github-push`. The write stays server-side:
7
+ * GitHub's HMAC proves the event and the service key never leaves Supabase.
8
+ *
9
+ * Such a row is shadowed — dropped on read — whenever a real machine has the same
10
+ * branch, so a branch registered by the machine that pushed it never shows twice or
11
+ * conflicts with itself (see dropShadowedPushRows).
12
+ */
13
+ import type { MachineRegistryView } from '../types/cloud.js';
14
+ export declare const PUSH_MACHINE_ID = "github-push";
15
+ /**
16
+ * Drop pushed-branch rows a real registration already covers. Applied on every read
17
+ * (pull) BEFORE the caller's own machine is excluded, so a branch the caller itself
18
+ * registered doesn't come back as "github/<branch>" and conflict with itself.
19
+ * `ownBranches` covers the race where the webhook beats the pusher's own registry-sync.
20
+ */
21
+ export declare function dropShadowedPushRows(views: MachineRegistryView[], ownBranches?: Iterable<string>): MachineRegistryView[];
22
+ //# sourceMappingURL=push-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push-registry.d.ts","sourceRoot":"","sources":["../../src/lib/push-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAE7C;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,mBAAmB,EAAE,EAAE,WAAW,GAAE,QAAQ,CAAC,MAAM,CAAM,GAAG,mBAAmB,EAAE,CAS5H"}
@@ -0,0 +1,21 @@
1
+ export const PUSH_MACHINE_ID = 'github-push';
2
+ /**
3
+ * Drop pushed-branch rows a real registration already covers. Applied on every read
4
+ * (pull) BEFORE the caller's own machine is excluded, so a branch the caller itself
5
+ * registered doesn't come back as "github/<branch>" and conflict with itself.
6
+ * `ownBranches` covers the race where the webhook beats the pusher's own registry-sync.
7
+ */
8
+ export function dropShadowedPushRows(views, ownBranches = []) {
9
+ const covered = new Set(ownBranches);
10
+ for (const v of views) {
11
+ if (v.machine_id === PUSH_MACHINE_ID)
12
+ continue;
13
+ for (const b of v.branches)
14
+ if (b.status === 'active')
15
+ covered.add(b.name);
16
+ }
17
+ return views
18
+ .map(v => v.machine_id !== PUSH_MACHINE_ID ? v : { ...v, branches: v.branches.filter(b => !covered.has(b.name)) })
19
+ .filter(v => v.machine_id !== PUSH_MACHINE_ID || v.branches.length > 0);
20
+ }
21
+ //# sourceMappingURL=push-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push-registry.js","sourceRoot":"","sources":["../../src/lib/push-registry.ts"],"names":[],"mappings":"AAcA,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAC;AAE7C;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAA4B,EAAE,cAAgC,EAAE;IACjG,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,WAAW,CAAC,CAAC;IAC7C,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,CAAC,UAAU,KAAK,eAAe;YAAE,SAAS;QAC/C,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ;YAAE,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ;gBAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,KAAK;SACP,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;SACjH,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,eAAe,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAChF,CAAC"}