co-maintainer 0.4.0-beta.1

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 (294) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +105 -0
  3. package/dist/main.d.ts +2 -0
  4. package/dist/main.js +9 -0
  5. package/dist/mod.d.ts +4 -0
  6. package/dist/mod.js +2 -0
  7. package/dist/package.json +54 -0
  8. package/dist/src/ai/batch.d.ts +16 -0
  9. package/dist/src/ai/batch.js +117 -0
  10. package/dist/src/ai/fake.d.ts +7 -0
  11. package/dist/src/ai/fake.js +40 -0
  12. package/dist/src/ai/hetzner.d.ts +16 -0
  13. package/dist/src/ai/hetzner.js +69 -0
  14. package/dist/src/ai/mermaid.d.ts +27 -0
  15. package/dist/src/ai/mermaid.js +457 -0
  16. package/dist/src/ai/mermaid_loop.d.ts +12 -0
  17. package/dist/src/ai/mermaid_loop.js +100 -0
  18. package/dist/src/ai/openrouter.d.ts +9 -0
  19. package/dist/src/ai/openrouter.js +29 -0
  20. package/dist/src/ai/provider.d.ts +4 -0
  21. package/dist/src/ai/provider.js +63 -0
  22. package/dist/src/cli/args.d.ts +5 -0
  23. package/dist/src/cli/args.js +252 -0
  24. package/dist/src/cli/commands/probe.d.ts +2 -0
  25. package/dist/src/cli/commands/probe.js +122 -0
  26. package/dist/src/cli/commands/review.d.ts +3 -0
  27. package/dist/src/cli/commands/review.js +84 -0
  28. package/dist/src/cli/commands/serve.d.ts +18 -0
  29. package/dist/src/cli/commands/serve.js +183 -0
  30. package/dist/src/cli/commands/set.d.ts +7 -0
  31. package/dist/src/cli/commands/set.js +171 -0
  32. package/dist/src/cli/main.d.ts +1 -0
  33. package/dist/src/cli/main.js +27 -0
  34. package/dist/src/cli/prompt.d.ts +4 -0
  35. package/dist/src/cli/prompt.js +26 -0
  36. package/dist/src/cli/review_args.d.ts +28 -0
  37. package/dist/src/cli/review_args.js +84 -0
  38. package/dist/src/cli/review_output.d.ts +2 -0
  39. package/dist/src/cli/review_output.js +10 -0
  40. package/dist/src/cli/review_result.d.ts +81 -0
  41. package/dist/src/cli/review_result.js +281 -0
  42. package/dist/src/config.d.ts +108 -0
  43. package/dist/src/config.js +138 -0
  44. package/dist/src/git/default_branch.d.ts +5 -0
  45. package/dist/src/git/default_branch.js +22 -0
  46. package/dist/src/github/app.d.ts +43 -0
  47. package/dist/src/github/app.js +120 -0
  48. package/dist/src/github/client.d.ts +17 -0
  49. package/dist/src/github/client.js +66 -0
  50. package/dist/src/github/collect.d.ts +7 -0
  51. package/dist/src/github/collect.js +368 -0
  52. package/dist/src/github/gh.d.ts +8 -0
  53. package/dist/src/github/gh.js +63 -0
  54. package/dist/src/github/jwt.d.ts +12 -0
  55. package/dist/src/github/jwt.js +84 -0
  56. package/dist/src/github/oauth.d.ts +9 -0
  57. package/dist/src/github/oauth.js +51 -0
  58. package/dist/src/github/pat.d.ts +10 -0
  59. package/dist/src/github/pat.js +60 -0
  60. package/dist/src/knowledge/facts.d.ts +3 -0
  61. package/dist/src/knowledge/facts.js +325 -0
  62. package/dist/src/knowledge/guide.d.ts +6 -0
  63. package/dist/src/knowledge/guide.js +82 -0
  64. package/dist/src/knowledge/probe.d.ts +12 -0
  65. package/dist/src/knowledge/probe.js +144 -0
  66. package/dist/src/knowledge/sections.d.ts +2 -0
  67. package/dist/src/knowledge/sections.js +13 -0
  68. package/dist/src/knowledge/skill.d.ts +10 -0
  69. package/dist/src/knowledge/skill.js +106 -0
  70. package/dist/src/knowledge/synthesis.d.ts +7 -0
  71. package/dist/src/knowledge/synthesis.js +315 -0
  72. package/dist/src/knowledge/types.d.ts +49 -0
  73. package/dist/src/knowledge/types.js +1 -0
  74. package/dist/src/knowledge/validate.d.ts +6 -0
  75. package/dist/src/knowledge/validate.js +134 -0
  76. package/dist/src/local/carry_over_store.d.ts +18 -0
  77. package/dist/src/local/carry_over_store.js +40 -0
  78. package/dist/src/local/codegraph_exclude.d.ts +3 -0
  79. package/dist/src/local/codegraph_exclude.js +26 -0
  80. package/dist/src/local/codegraph_prepare.d.ts +17 -0
  81. package/dist/src/local/codegraph_prepare.js +51 -0
  82. package/dist/src/local/git_ops.d.ts +12 -0
  83. package/dist/src/local/git_ops.js +98 -0
  84. package/dist/src/local/git_parse.d.ts +27 -0
  85. package/dist/src/local/git_parse.js +131 -0
  86. package/dist/src/local/git_revision.d.ts +15 -0
  87. package/dist/src/local/git_revision.js +141 -0
  88. package/dist/src/local/git_untracked.d.ts +21 -0
  89. package/dist/src/local/git_untracked.js +79 -0
  90. package/dist/src/local/review_local.d.ts +4 -0
  91. package/dist/src/local/review_local.js +273 -0
  92. package/dist/src/local/review_lock.d.ts +7 -0
  93. package/dist/src/local/review_lock.js +45 -0
  94. package/dist/src/pr/checkout.d.ts +25 -0
  95. package/dist/src/pr/checkout.js +136 -0
  96. package/dist/src/pr/codegraph_tool_args.d.ts +8 -0
  97. package/dist/src/pr/codegraph_tool_args.js +34 -0
  98. package/dist/src/pr/codegraph_tools.d.ts +15 -0
  99. package/dist/src/pr/codegraph_tools.js +408 -0
  100. package/dist/src/pr/diff_summary.d.ts +6 -0
  101. package/dist/src/pr/diff_summary.js +52 -0
  102. package/dist/src/pr/findings.d.ts +14 -0
  103. package/dist/src/pr/findings.js +94 -0
  104. package/dist/src/pr/github_revision.d.ts +4 -0
  105. package/dist/src/pr/github_revision.js +39 -0
  106. package/dist/src/pr/hunks.d.ts +18 -0
  107. package/dist/src/pr/hunks.js +73 -0
  108. package/dist/src/pr/reviewer.d.ts +40 -0
  109. package/dist/src/pr/reviewer.js +582 -0
  110. package/dist/src/pr/rounds.d.ts +17 -0
  111. package/dist/src/pr/rounds.js +43 -0
  112. package/dist/src/pr/scope.d.ts +35 -0
  113. package/dist/src/pr/scope.js +84 -0
  114. package/dist/src/pr/snapshot.d.ts +11 -0
  115. package/dist/src/pr/snapshot.js +1 -0
  116. package/dist/src/pr/suggestion.d.ts +17 -0
  117. package/dist/src/pr/suggestion.js +70 -0
  118. package/dist/src/remote/client.d.ts +4 -0
  119. package/dist/src/remote/client.js +199 -0
  120. package/dist/src/remote/client_tools.d.ts +13 -0
  121. package/dist/src/remote/client_tools.js +30 -0
  122. package/dist/src/remote/fixtures.d.ts +1 -0
  123. package/dist/src/remote/fixtures.js +30 -0
  124. package/dist/src/remote/revision_from_submit.d.ts +2 -0
  125. package/dist/src/remote/revision_from_submit.js +26 -0
  126. package/dist/src/remote/schema.d.ts +14 -0
  127. package/dist/src/remote/schema.js +30 -0
  128. package/dist/src/remote/server/body.d.ts +18 -0
  129. package/dist/src/remote/server/body.js +73 -0
  130. package/dist/src/remote/server/routes.d.ts +1 -0
  131. package/dist/src/remote/server/routes.js +150 -0
  132. package/dist/src/remote/server/sessions.d.ts +16 -0
  133. package/dist/src/remote/server/sessions.js +48 -0
  134. package/dist/src/remote/server/sync.d.ts +3 -0
  135. package/dist/src/remote/server/sync.js +90 -0
  136. package/dist/src/remote/tool_bridge.d.ts +15 -0
  137. package/dist/src/remote/tool_bridge.js +84 -0
  138. package/dist/src/remote/validate.d.ts +8 -0
  139. package/dist/src/remote/validate.js +248 -0
  140. package/dist/src/review/carry_over.d.ts +63 -0
  141. package/dist/src/review/carry_over.js +372 -0
  142. package/dist/src/review/engine.d.ts +13 -0
  143. package/dist/src/review/engine.js +4 -0
  144. package/dist/src/review/guides.d.ts +12 -0
  145. package/dist/src/review/guides.js +44 -0
  146. package/dist/src/review/revision.d.ts +26 -0
  147. package/dist/src/review/revision.js +57 -0
  148. package/dist/src/server/api/activity.d.ts +1 -0
  149. package/dist/src/server/api/activity.js +12 -0
  150. package/dist/src/server/api/analytics.d.ts +1 -0
  151. package/dist/src/server/api/analytics.js +9 -0
  152. package/dist/src/server/api/installations.d.ts +4 -0
  153. package/dist/src/server/api/installations.js +24 -0
  154. package/dist/src/server/api/jobs.d.ts +1 -0
  155. package/dist/src/server/api/jobs.js +86 -0
  156. package/dist/src/server/api/json_body.d.ts +1 -0
  157. package/dist/src/server/api/json_body.js +14 -0
  158. package/dist/src/server/api/remote_tokens.d.ts +1 -0
  159. package/dist/src/server/api/remote_tokens.js +71 -0
  160. package/dist/src/server/api/repos.d.ts +4 -0
  161. package/dist/src/server/api/repos.js +136 -0
  162. package/dist/src/server/api/settings.d.ts +1 -0
  163. package/dist/src/server/api/settings.js +210 -0
  164. package/dist/src/server/app.d.ts +27 -0
  165. package/dist/src/server/app.js +147 -0
  166. package/dist/src/server/assets.d.ts +3 -0
  167. package/dist/src/server/assets.js +21 -0
  168. package/dist/src/server/auth.d.ts +44 -0
  169. package/dist/src/server/auth.js +188 -0
  170. package/dist/src/server/errors.d.ts +8 -0
  171. package/dist/src/server/errors.js +10 -0
  172. package/dist/src/server/http.d.ts +8 -0
  173. package/dist/src/server/http.js +117 -0
  174. package/dist/src/server/logo.d.ts +1 -0
  175. package/dist/src/server/logo.js +2 -0
  176. package/dist/src/server/pages/activity.d.ts +4 -0
  177. package/dist/src/server/pages/activity.js +200 -0
  178. package/dist/src/server/pages/add_repo.d.ts +9 -0
  179. package/dist/src/server/pages/add_repo.js +56 -0
  180. package/dist/src/server/pages/analytics.d.ts +2 -0
  181. package/dist/src/server/pages/analytics.js +193 -0
  182. package/dist/src/server/pages/client.d.ts +2 -0
  183. package/dist/src/server/pages/client.js +2 -0
  184. package/dist/src/server/pages/home.d.ts +16 -0
  185. package/dist/src/server/pages/home.js +87 -0
  186. package/dist/src/server/pages/knowledge.d.ts +2 -0
  187. package/dist/src/server/pages/knowledge.js +64 -0
  188. package/dist/src/server/pages/layout.d.ts +32 -0
  189. package/dist/src/server/pages/layout.js +230 -0
  190. package/dist/src/server/pages/pr.d.ts +2 -0
  191. package/dist/src/server/pages/pr.js +109 -0
  192. package/dist/src/server/pages/repo.d.ts +2 -0
  193. package/dist/src/server/pages/repo.js +92 -0
  194. package/dist/src/server/pages/repo_prs.d.ts +2 -0
  195. package/dist/src/server/pages/repo_prs.js +75 -0
  196. package/dist/src/server/pages/repo_remote.d.ts +2 -0
  197. package/dist/src/server/pages/repo_remote.js +38 -0
  198. package/dist/src/server/pages/repo_settings.d.ts +3 -0
  199. package/dist/src/server/pages/repo_settings.js +127 -0
  200. package/dist/src/server/pages/router.d.ts +17 -0
  201. package/dist/src/server/pages/router.js +312 -0
  202. package/dist/src/server/pages/settings.d.ts +2 -0
  203. package/dist/src/server/pages/settings.js +337 -0
  204. package/dist/src/server/pages/setup.d.ts +5 -0
  205. package/dist/src/server/pages/setup.js +38 -0
  206. package/dist/src/server/pages/styles.d.ts +2 -0
  207. package/dist/src/server/pages/styles.js +2 -0
  208. package/dist/src/server/webhook/index.d.ts +1 -0
  209. package/dist/src/server/webhook/index.js +64 -0
  210. package/dist/src/server/webhook/signature.d.ts +7 -0
  211. package/dist/src/server/webhook/signature.js +41 -0
  212. package/dist/src/services/credentials.d.ts +19 -0
  213. package/dist/src/services/credentials.js +146 -0
  214. package/dist/src/services/dashboard.d.ts +165 -0
  215. package/dist/src/services/dashboard.js +200 -0
  216. package/dist/src/services/drift.d.ts +13 -0
  217. package/dist/src/services/drift.js +76 -0
  218. package/dist/src/services/jobs.d.ts +34 -0
  219. package/dist/src/services/jobs.js +222 -0
  220. package/dist/src/services/remote_review.d.ts +10 -0
  221. package/dist/src/services/remote_review.js +274 -0
  222. package/dist/src/services/remote_review_abort.d.ts +3 -0
  223. package/dist/src/services/remote_review_abort.js +11 -0
  224. package/dist/src/services/remote_token_jobs.d.ts +1 -0
  225. package/dist/src/services/remote_token_jobs.js +20 -0
  226. package/dist/src/services/remote_tokens.d.ts +13 -0
  227. package/dist/src/services/remote_tokens.js +57 -0
  228. package/dist/src/services/replies.d.ts +7 -0
  229. package/dist/src/services/replies.js +248 -0
  230. package/dist/src/services/review.d.ts +35 -0
  231. package/dist/src/services/review.js +671 -0
  232. package/dist/src/services/setup.d.ts +29 -0
  233. package/dist/src/services/setup.js +364 -0
  234. package/dist/src/services/webhook.d.ts +22 -0
  235. package/dist/src/services/webhook.js +401 -0
  236. package/dist/src/store/app_db.d.ts +10 -0
  237. package/dist/src/store/app_db.js +85 -0
  238. package/dist/src/store/cache_db.d.ts +4 -0
  239. package/dist/src/store/cache_db.js +63 -0
  240. package/dist/src/store/deliveries.d.ts +12 -0
  241. package/dist/src/store/deliveries.js +23 -0
  242. package/dist/src/store/drift.d.ts +4 -0
  243. package/dist/src/store/drift.js +22 -0
  244. package/dist/src/store/findings.d.ts +31 -0
  245. package/dist/src/store/findings.js +64 -0
  246. package/dist/src/store/installations.d.ts +9 -0
  247. package/dist/src/store/installations.js +35 -0
  248. package/dist/src/store/job_logs.d.ts +5 -0
  249. package/dist/src/store/job_logs.js +17 -0
  250. package/dist/src/store/jobs.d.ts +27 -0
  251. package/dist/src/store/jobs.js +86 -0
  252. package/dist/src/store/migrations.d.ts +5 -0
  253. package/dist/src/store/migrations.js +258 -0
  254. package/dist/src/store/remote_review_inputs.d.ts +11 -0
  255. package/dist/src/store/remote_review_inputs.js +25 -0
  256. package/dist/src/store/remote_tokens.d.ts +10 -0
  257. package/dist/src/store/remote_tokens.js +45 -0
  258. package/dist/src/store/replies.d.ts +24 -0
  259. package/dist/src/store/replies.js +79 -0
  260. package/dist/src/store/repos.d.ts +11 -0
  261. package/dist/src/store/repos.js +59 -0
  262. package/dist/src/store/reviews.d.ts +86 -0
  263. package/dist/src/store/reviews.js +220 -0
  264. package/dist/src/store/rows.d.ts +179 -0
  265. package/dist/src/store/rows.js +4 -0
  266. package/dist/src/store/sessions.d.ts +8 -0
  267. package/dist/src/store/sessions.js +34 -0
  268. package/dist/src/store/skill_state.d.ts +3 -0
  269. package/dist/src/store/skill_state.js +24 -0
  270. package/dist/src/store/sqlite.d.ts +39 -0
  271. package/dist/src/store/sqlite.js +77 -0
  272. package/dist/src/store/subjects.d.ts +15 -0
  273. package/dist/src/store/subjects.js +78 -0
  274. package/dist/src/tools/codegraph.d.ts +58 -0
  275. package/dist/src/tools/codegraph.js +165 -0
  276. package/dist/src/tools/codegraph_exec.d.ts +13 -0
  277. package/dist/src/tools/codegraph_exec.js +66 -0
  278. package/dist/src/types.d.ts +89 -0
  279. package/dist/src/types.js +1 -0
  280. package/dist/src/util/atomic.d.ts +4 -0
  281. package/dist/src/util/atomic.js +78 -0
  282. package/dist/src/util/keyed_lock.d.ts +3 -0
  283. package/dist/src/util/keyed_lock.js +33 -0
  284. package/dist/src/util/log.d.ts +11 -0
  285. package/dist/src/util/log.js +42 -0
  286. package/dist/src/util/redact.d.ts +5 -0
  287. package/dist/src/util/redact.js +30 -0
  288. package/dist/src/util/runtime.d.ts +90 -0
  289. package/dist/src/util/runtime.js +206 -0
  290. package/dist/src/util/time.d.ts +9 -0
  291. package/dist/src/util/time.js +36 -0
  292. package/dist/src/version.d.ts +1 -0
  293. package/dist/src/version.js +5 -0
  294. package/package.json +54 -0
@@ -0,0 +1,24 @@
1
+ import { cacheGet, cacheSet } from "./cache_db.js";
2
+ import { isNotFound, readTextFile } from "../util/runtime.js";
3
+ export async function readState(repo) {
4
+ const value = await cacheGet("state", repo);
5
+ if (value)
6
+ return JSON.parse(value);
7
+ try {
8
+ const legacy = JSON.parse(await readTextFile(`.cache/${repo}/state.json`));
9
+ if (legacy.options.maxPrMonths === undefined &&
10
+ legacy.options.maxPrYears !== undefined) {
11
+ legacy.options.maxPrMonths = legacy.options.maxPrYears * 12;
12
+ }
13
+ await writeState(legacy);
14
+ return legacy;
15
+ }
16
+ catch (error) {
17
+ if (isNotFound(error))
18
+ return undefined;
19
+ throw error;
20
+ }
21
+ }
22
+ export async function writeState(state) {
23
+ await cacheSet("state", state.repo, JSON.stringify(state));
24
+ }
@@ -0,0 +1,39 @@
1
+ /** Thin compatibility layer over `node:sqlite`, preserving the `@db/sqlite`
2
+ * surface the store layer was written against: a synchronous `Database`,
3
+ * `prepare<T>()` generics, `.get()/.all()/.run()`, and `db.changes`.
4
+ *
5
+ * Two behaviours of the JSR driver must be reproduced on top of
6
+ * `node:sqlite`:
7
+ * - `undefined` parameters bind as SQL NULL (node:sqlite rejects undefined)
8
+ * - `db.changes` reports the rows modified by the most recent `run()`, which
9
+ * `node:sqlite` only returns from the statement call
10
+ */
11
+ import { type StatementSync } from "node:sqlite";
12
+ type BindValue = string | number | bigint | boolean | null | Uint8Array;
13
+ type BindObject = Record<string, BindValue | undefined>;
14
+ type Params = (BindValue | undefined)[] | [BindObject];
15
+ /** Where a `Statement` reports its modified-row count back to its owner. */
16
+ export type ChangeSink = {
17
+ recordChanges: (changes: number | bigint) => void;
18
+ };
19
+ export declare class Statement<Row = Record<string, unknown>> {
20
+ #private;
21
+ constructor(statement: StatementSync, sink: ChangeSink);
22
+ get(...params: Params): Row | undefined;
23
+ all(...params: Params): Row[];
24
+ run(...params: Params): {
25
+ changes: number | bigint;
26
+ lastInsertRowid: number | bigint;
27
+ };
28
+ }
29
+ export declare class Database implements ChangeSink {
30
+ #private;
31
+ constructor(path: string);
32
+ /** Rows modified by the most recent `run()`. */
33
+ get changes(): number;
34
+ recordChanges(changes: number | bigint): void;
35
+ exec(sql: string): void;
36
+ prepare<Row = Record<string, unknown>>(sql: string): Statement<Row>;
37
+ close(): void;
38
+ }
39
+ export {};
@@ -0,0 +1,77 @@
1
+ /** Thin compatibility layer over `node:sqlite`, preserving the `@db/sqlite`
2
+ * surface the store layer was written against: a synchronous `Database`,
3
+ * `prepare<T>()` generics, `.get()/.all()/.run()`, and `db.changes`.
4
+ *
5
+ * Two behaviours of the JSR driver must be reproduced on top of
6
+ * `node:sqlite`:
7
+ * - `undefined` parameters bind as SQL NULL (node:sqlite rejects undefined)
8
+ * - `db.changes` reports the rows modified by the most recent `run()`, which
9
+ * `node:sqlite` only returns from the statement call
10
+ */
11
+ import { DatabaseSync } from "node:sqlite";
12
+ export class Statement {
13
+ #statement;
14
+ #sink;
15
+ constructor(statement, sink) {
16
+ this.#statement = statement;
17
+ this.#sink = sink;
18
+ }
19
+ get(...params) {
20
+ return this.#statement.get(...bind(args(params)));
21
+ }
22
+ all(...params) {
23
+ return this.#statement.all(...bind(args(params)));
24
+ }
25
+ run(...params) {
26
+ const result = this.#statement.run(...bind(args(params)));
27
+ this.#sink.recordChanges(result.changes);
28
+ return {
29
+ changes: result.changes,
30
+ lastInsertRowid: result.lastInsertRowid,
31
+ };
32
+ }
33
+ }
34
+ /** Normalizes call arguments: a single object binds by name, everything else
35
+ * is a positional list with `undefined` mapped to NULL. */
36
+ function args(params) {
37
+ if (params.length === 1 && isBindObject(params[0]))
38
+ return [params[0]];
39
+ return params.map((value) => value === undefined ? null : value);
40
+ }
41
+ function isBindObject(value) {
42
+ return (typeof value === "object" &&
43
+ value !== null &&
44
+ !Array.isArray(value) &&
45
+ !(value instanceof Uint8Array));
46
+ }
47
+ // node:sqlite's declared input union is narrower than what it accepts at
48
+ // runtime (named-parameter objects and Uint8Array blobs both work).
49
+ function bind(params) {
50
+ return params;
51
+ }
52
+ export class Database {
53
+ #database;
54
+ #changes = 0;
55
+ constructor(path) {
56
+ // node --test runs test files in parallel processes, and the cache/app
57
+ // databases are shared; node:sqlite defaults to no busy timeout while
58
+ // @db/sqlite tolerated contention. Wait rather than throw SQLITE_BUSY.
59
+ this.#database = new DatabaseSync(path, { timeout: 5_000 });
60
+ }
61
+ /** Rows modified by the most recent `run()`. */
62
+ get changes() {
63
+ return this.#changes;
64
+ }
65
+ recordChanges(changes) {
66
+ this.#changes = Number(changes);
67
+ }
68
+ exec(sql) {
69
+ this.#database.exec(sql);
70
+ }
71
+ prepare(sql) {
72
+ return new Statement(this.#database.prepare(sql), this);
73
+ }
74
+ close() {
75
+ this.#database.close();
76
+ }
77
+ }
@@ -0,0 +1,15 @@
1
+ import type { RevisionFile } from "../review/revision.ts";
2
+ import type { SubjectRevisionRow, SubjectRow } from "./rows.ts";
3
+ export declare function getOrCreateRemoteSubject(repo: string, branch: string, tokenId: string): SubjectRow;
4
+ export declare function deleteSubjectRevision(subjectId: string): void;
5
+ export declare function getOrCreatePrSubject(repo: string, prNumber: number): SubjectRow;
6
+ export declare function getSubjectRevision(subjectId: string): SubjectRevisionRow | undefined;
7
+ export declare function saveSubjectRevision(input: {
8
+ subjectId: string;
9
+ reviewId: string;
10
+ files: RevisionFile[];
11
+ visiblePaths: string[];
12
+ guideBuiltAt: string | null;
13
+ }): void;
14
+ export declare function parseRevisionFiles(row: SubjectRevisionRow): RevisionFile[];
15
+ export declare function parseVisiblePaths(row: SubjectRevisionRow): Set<string>;
@@ -0,0 +1,78 @@
1
+ import { getAppDb } from "./app_db.js";
2
+ import { nowIso } from "../util/time.js";
3
+ export function getOrCreateRemoteSubject(repo, branch, tokenId) {
4
+ const db = getAppDb();
5
+ const existing = db
6
+ .prepare(`SELECT * FROM subjects
7
+ WHERE kind = 'remote' AND repo = ? AND branch = ? AND token_id = ?`)
8
+ .get(repo, branch, tokenId);
9
+ if (existing)
10
+ return existing;
11
+ const row = {
12
+ id: crypto.randomUUID(),
13
+ kind: "remote",
14
+ repo,
15
+ pr_number: null,
16
+ branch,
17
+ token_id: tokenId,
18
+ created_at: nowIso(),
19
+ updated_at: nowIso(),
20
+ };
21
+ db.prepare(`INSERT INTO subjects (id, kind, repo, pr_number, branch, token_id, created_at, updated_at)
22
+ VALUES (?, 'remote', ?, NULL, ?, ?, ?, ?)`).run(row.id, repo, branch, tokenId, row.created_at, row.updated_at);
23
+ return row;
24
+ }
25
+ export function deleteSubjectRevision(subjectId) {
26
+ getAppDb()
27
+ .prepare(`DELETE FROM subject_revisions WHERE subject_id = ?`)
28
+ .run(subjectId);
29
+ }
30
+ export function getOrCreatePrSubject(repo, prNumber) {
31
+ const db = getAppDb();
32
+ const existing = db
33
+ .prepare(`SELECT * FROM subjects WHERE kind = 'pr' AND repo = ? AND pr_number = ?`)
34
+ .get(repo, prNumber);
35
+ if (existing)
36
+ return existing;
37
+ const row = {
38
+ id: crypto.randomUUID(),
39
+ kind: "pr",
40
+ repo,
41
+ pr_number: prNumber,
42
+ branch: null,
43
+ token_id: null,
44
+ created_at: nowIso(),
45
+ updated_at: nowIso(),
46
+ };
47
+ db.prepare(`INSERT INTO subjects (id, kind, repo, pr_number, branch, token_id, created_at, updated_at)
48
+ VALUES (?, 'pr', ?, ?, NULL, NULL, ?, ?)`).run(row.id, repo, prNumber, row.created_at, row.updated_at);
49
+ return row;
50
+ }
51
+ export function getSubjectRevision(subjectId) {
52
+ return getAppDb()
53
+ .prepare(`SELECT * FROM subject_revisions WHERE subject_id = ?`)
54
+ .get(subjectId);
55
+ }
56
+ export function saveSubjectRevision(input) {
57
+ const createdAt = nowIso();
58
+ getAppDb()
59
+ .prepare(`INSERT INTO subject_revisions
60
+ (subject_id, review_id, files_json, visible_paths_json, guide_built_at, created_at)
61
+ VALUES (?, ?, ?, ?, ?, ?)
62
+ ON CONFLICT(subject_id) DO UPDATE SET
63
+ review_id = excluded.review_id,
64
+ files_json = excluded.files_json,
65
+ visible_paths_json = excluded.visible_paths_json,
66
+ guide_built_at = excluded.guide_built_at,
67
+ created_at = excluded.created_at`)
68
+ .run(input.subjectId, input.reviewId, JSON.stringify(input.files), JSON.stringify(input.visiblePaths), input.guideBuiltAt, createdAt);
69
+ getAppDb()
70
+ .prepare(`UPDATE subjects SET updated_at = ? WHERE id = ?`)
71
+ .run(createdAt, input.subjectId);
72
+ }
73
+ export function parseRevisionFiles(row) {
74
+ return JSON.parse(row.files_json);
75
+ }
76
+ export function parseVisiblePaths(row) {
77
+ return new Set(JSON.parse(row.visible_paths_json));
78
+ }
@@ -0,0 +1,58 @@
1
+ /** The version co-maintainer is built against. codegraph's CLI output shape is
2
+ * what the reviewer parses, so this is pinned rather than tracking latest: a
3
+ * format change upstream must be an explicit upgrade here, not a surprise on
4
+ * someone else's machine. Raise it, adjust whatever reads the output, done. */
5
+ export declare const CODEGRAPH_VERSION = "1.6.0";
6
+ export declare const CODEGRAPH_PACKAGE = "@colbymchenry/codegraph";
7
+ export type CommandResult = {
8
+ code: number;
9
+ stdout: string;
10
+ stderr: string;
11
+ };
12
+ export type Runner = (command: string, args: string[]) => Promise<CommandResult>;
13
+ export type EnsureOptions = {
14
+ /** Skips the prompt and installs. Set by `--allow-tool-install`. */
15
+ allowInstall?: boolean;
16
+ interactive?: boolean;
17
+ run?: Runner;
18
+ confirm?: (question: string) => boolean;
19
+ log?: (message: string) => void;
20
+ /** Overrides `toolsDir()`, for tests. */
21
+ root?: string;
22
+ };
23
+ export declare function runCommand(command: string, args: string[]): Promise<CommandResult>;
24
+ /** One version per directory, so several can sit side by side and an upgrade
25
+ * never half-overwrites a working install. */
26
+ export declare function versionDir(version: string, root?: string): string;
27
+ export declare function binaryPath(version: string, root?: string): string;
28
+ /** codegraph prints a bare semver on `--version`, but be lenient: any leading
29
+ * `v`, surrounding whitespace or trailing build text is tolerated so a future
30
+ * release that decorates the line does not read as "not installed". */
31
+ export declare function parseVersion(stdout: string): string | undefined;
32
+ export type Presence = {
33
+ state: "ok";
34
+ version: string;
35
+ path: string;
36
+ } | {
37
+ state: "mismatch";
38
+ version: string;
39
+ path: string;
40
+ } | {
41
+ state: "missing";
42
+ };
43
+ export declare function detect(version?: string, root?: string, run?: Runner): Promise<Presence>;
44
+ export declare function installCommand(version?: string, root?: string): {
45
+ command: string;
46
+ args: string[];
47
+ };
48
+ export declare function installHint(version?: string, root?: string): string;
49
+ /** Makes sure the pinned codegraph is available, asking first. Returns the path
50
+ * to the binary, or exits: co-maintainer indexes the repository with it, so
51
+ * declining is a decision not to run this command, not a degraded mode. */
52
+ export declare function ensureCodegraph(options?: EnsureOptions): Promise<string>;
53
+ /** Review paths must not call `Deno.exit` (plan §8.7, S13). */
54
+ export declare function ensureCodegraphForReview(options?: EnsureOptions): Promise<{
55
+ path: string;
56
+ } | {
57
+ reason: string;
58
+ }>;
@@ -0,0 +1,165 @@
1
+ import { toolsDir } from "../config.js";
2
+ import { commandOutput, isWindows, mkdir, stat } from "../util/runtime.js";
3
+ import { askConfirm } from "../cli/prompt.js";
4
+ /** The version co-maintainer is built against. codegraph's CLI output shape is
5
+ * what the reviewer parses, so this is pinned rather than tracking latest: a
6
+ * format change upstream must be an explicit upgrade here, not a surprise on
7
+ * someone else's machine. Raise it, adjust whatever reads the output, done. */
8
+ export const CODEGRAPH_VERSION = "1.6.0";
9
+ export const CODEGRAPH_PACKAGE = "@colbymchenry/codegraph";
10
+ export async function runCommand(command, args) {
11
+ // Windows resolves npm and other shims through the shell, not as bare exes.
12
+ const windows = isWindows();
13
+ const output = await commandOutput(windows ? "cmd" : command, {
14
+ args: windows ? ["/c", command, ...args] : args,
15
+ stdout: "piped",
16
+ stderr: "piped",
17
+ });
18
+ return {
19
+ code: output.code,
20
+ stdout: new TextDecoder().decode(output.stdout),
21
+ stderr: new TextDecoder().decode(output.stderr),
22
+ };
23
+ }
24
+ /** One version per directory, so several can sit side by side and an upgrade
25
+ * never half-overwrites a working install. */
26
+ export function versionDir(version, root = toolsDir()) {
27
+ return `${root}/codegraph/${version}`;
28
+ }
29
+ export function binaryPath(version, root = toolsDir()) {
30
+ const dir = versionDir(version, root);
31
+ return isWindows()
32
+ ? `${dir}/node_modules/.bin/codegraph.cmd`
33
+ : `${dir}/node_modules/.bin/codegraph`;
34
+ }
35
+ /** codegraph prints a bare semver on `--version`, but be lenient: any leading
36
+ * `v`, surrounding whitespace or trailing build text is tolerated so a future
37
+ * release that decorates the line does not read as "not installed". */
38
+ export function parseVersion(stdout) {
39
+ // No `\b` before the number: a `v` prefix is a word character, so a boundary
40
+ // would refuse to match the common `v1.6.0` spelling.
41
+ const match = stdout.match(/(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)/);
42
+ return match?.[1];
43
+ }
44
+ export async function detect(version = CODEGRAPH_VERSION, root = toolsDir(), run = runCommand) {
45
+ const path = binaryPath(version, root);
46
+ try {
47
+ await stat(path);
48
+ }
49
+ catch {
50
+ return { state: "missing" };
51
+ }
52
+ const result = await run(path, ["--version"]);
53
+ const found = parseVersion(result.stdout) ?? parseVersion(result.stderr);
54
+ if (!found)
55
+ return { state: "missing" };
56
+ return found === version
57
+ ? { state: "ok", version: found, path }
58
+ : { state: "mismatch", version: found, path };
59
+ }
60
+ export function installCommand(version = CODEGRAPH_VERSION, root = toolsDir()) {
61
+ return {
62
+ command: "npm",
63
+ args: [
64
+ "install",
65
+ `${CODEGRAPH_PACKAGE}@${version}`,
66
+ "--prefix",
67
+ versionDir(version, root),
68
+ "--no-audit",
69
+ "--no-fund",
70
+ ],
71
+ };
72
+ }
73
+ export function installHint(version = CODEGRAPH_VERSION, root = toolsDir()) {
74
+ const { command, args } = installCommand(version, root);
75
+ return `${command} ${args.join(" ")}`;
76
+ }
77
+ async function defaultConfirm(question) {
78
+ return await askConfirm(question);
79
+ }
80
+ /** Makes sure the pinned codegraph is available, asking first. Returns the path
81
+ * to the binary, or exits: co-maintainer indexes the repository with it, so
82
+ * declining is a decision not to run this command, not a degraded mode. */
83
+ export async function ensureCodegraph(options = {}) {
84
+ const root = options.root ?? toolsDir();
85
+ const run = options.run ?? runCommand;
86
+ const log = options.log ?? ((message) => console.log(message));
87
+ const interactive = options.interactive ?? process.stdin.isTTY === true;
88
+ const present = await detect(CODEGRAPH_VERSION, root, run);
89
+ if (present.state === "ok")
90
+ return present.path;
91
+ if (present.state === "mismatch") {
92
+ log(`[codegraph] expected ${CODEGRAPH_VERSION} but found ${present.version} ` +
93
+ `at ${present.path}; reinstalling the pinned version`);
94
+ }
95
+ if (!options.allowInstall) {
96
+ if (!interactive) {
97
+ log(`[codegraph] co-maintainer needs codegraph ${CODEGRAPH_VERSION} to index the repository.\n` +
98
+ `Run it with --allow-tool-install, or install it yourself:\n ${installHint(CODEGRAPH_VERSION, root)}`);
99
+ process.exit(1);
100
+ }
101
+ const confirm = options.confirm ?? defaultConfirm;
102
+ const approved = confirm(`co-maintainer needs codegraph ${CODEGRAPH_VERSION} to index the repository.\n` +
103
+ `Install it into ${versionDir(CODEGRAPH_VERSION, root)} (your global PATH is not touched)?`);
104
+ if (!approved) {
105
+ log("[codegraph] declined; nothing was installed");
106
+ process.exit(1);
107
+ }
108
+ }
109
+ const { command, args } = installCommand(CODEGRAPH_VERSION, root);
110
+ log(`[codegraph] installing ${CODEGRAPH_PACKAGE}@${CODEGRAPH_VERSION}`);
111
+ await mkdir(versionDir(CODEGRAPH_VERSION, root), { recursive: true });
112
+ const result = await run(command, args);
113
+ if (result.code !== 0) {
114
+ log(`[codegraph] install failed (exit ${result.code}): ${result.stderr.trim() || result.stdout.trim()}`);
115
+ process.exit(1);
116
+ }
117
+ const after = await detect(CODEGRAPH_VERSION, root, run);
118
+ if (after.state !== "ok") {
119
+ log(`[codegraph] install finished but ${binaryPath(CODEGRAPH_VERSION, root)} is still not usable`);
120
+ process.exit(1);
121
+ }
122
+ log(`[codegraph] ready at ${after.path}`);
123
+ return after.path;
124
+ }
125
+ /** Review paths must not call `Deno.exit` (plan §8.7, S13). */
126
+ export async function ensureCodegraphForReview(options = {}) {
127
+ const root = options.root ?? toolsDir();
128
+ const run = options.run ?? runCommand;
129
+ const log = options.log ?? ((message) => console.error(message));
130
+ const interactive = options.interactive ?? process.stdin.isTTY === true;
131
+ const present = await detect(CODEGRAPH_VERSION, root, run);
132
+ if (present.state === "ok")
133
+ return { path: present.path };
134
+ if (present.state === "mismatch") {
135
+ log(`[codegraph] expected ${CODEGRAPH_VERSION} but found ${present.version}; reinstalling`);
136
+ }
137
+ if (!options.allowInstall) {
138
+ if (!interactive) {
139
+ return {
140
+ reason: `codegraph ${CODEGRAPH_VERSION} is not installed (use --allow-tool-install)`,
141
+ };
142
+ }
143
+ const confirm = options.confirm ?? defaultConfirm;
144
+ const approved = confirm(`co-maintainer needs codegraph ${CODEGRAPH_VERSION} to index this repository.\n` +
145
+ `Install into ${versionDir(CODEGRAPH_VERSION, root)}?`);
146
+ if (!approved)
147
+ return { reason: "codegraph install declined" };
148
+ }
149
+ const { command, args } = installCommand(CODEGRAPH_VERSION, root);
150
+ log(`[codegraph] installing ${CODEGRAPH_PACKAGE}@${CODEGRAPH_VERSION}`);
151
+ await mkdir(versionDir(CODEGRAPH_VERSION, root), { recursive: true });
152
+ const result = await run(command, args);
153
+ if (result.code !== 0) {
154
+ return {
155
+ reason: result.stderr.trim() ||
156
+ result.stdout.trim() ||
157
+ `install failed (exit ${result.code})`,
158
+ };
159
+ }
160
+ const after = await detect(CODEGRAPH_VERSION, root, run);
161
+ if (after.state !== "ok") {
162
+ return { reason: "install finished but the binary is still not usable" };
163
+ }
164
+ return { path: after.path };
165
+ }
@@ -0,0 +1,13 @@
1
+ import type { CommandResult } from "./codegraph.ts";
2
+ export declare const LOCAL_CODEGRAPH_DIR = ".co-maintainer-codegraph";
3
+ export declare const SERVER_CODEGRAPH_DIR = ".codegraph";
4
+ export type ExecOptions = {
5
+ timeoutMs?: number | null;
6
+ codegraphDir?: string;
7
+ env?: Record<string, string>;
8
+ };
9
+ export type CodegraphRunner = (binary: string, args: string[], worktree: string) => Promise<CommandResult>;
10
+ /** Run the codegraph CLI in a repo root (plan §13.4). No `cmd /c` wrapper. */
11
+ export declare function execCodegraph(binary: string, args: string[], cwd: string, options?: ExecOptions): Promise<CommandResult>;
12
+ export declare function createCodegraphRunner(indexDir: string): CodegraphRunner;
13
+ export declare function runCodegraphTool(binary: string, args: string[], worktree: string, runner: CodegraphRunner): Promise<string>;
@@ -0,0 +1,66 @@
1
+ import { commandOutput, commandSpawn, envToObject, } from "../util/runtime.js";
2
+ export const LOCAL_CODEGRAPH_DIR = ".co-maintainer-codegraph";
3
+ export const SERVER_CODEGRAPH_DIR = ".codegraph";
4
+ const TOOL_TIMEOUT_MS = 60_000;
5
+ const INDEX_COMMANDS = new Set(["init", "sync", "index", "unlock"]);
6
+ /** Run the codegraph CLI in a repo root (plan §13.4). No `cmd /c` wrapper. */
7
+ export async function execCodegraph(binary, args, cwd, options = {}) {
8
+ const indexDir = options.codegraphDir ?? LOCAL_CODEGRAPH_DIR;
9
+ const env = {
10
+ ...envToObject(),
11
+ CODEGRAPH_DIR: indexDir,
12
+ ...options.env,
13
+ };
14
+ const timeoutMs = options.timeoutMs === undefined ? TOOL_TIMEOUT_MS : options.timeoutMs;
15
+ const commandOptions = {
16
+ args,
17
+ cwd,
18
+ env,
19
+ stdout: "piped",
20
+ stderr: "piped",
21
+ };
22
+ if (timeoutMs === null) {
23
+ const output = await commandOutput(binary, commandOptions);
24
+ return decode(output);
25
+ }
26
+ const proc = commandSpawn(binary, commandOptions);
27
+ let timedOut = false;
28
+ const timer = setTimeout(() => {
29
+ timedOut = true;
30
+ proc.kill("SIGKILL");
31
+ }, timeoutMs);
32
+ const output = await proc.output();
33
+ clearTimeout(timer);
34
+ if (timedOut) {
35
+ return {
36
+ code: 1,
37
+ stdout: "",
38
+ stderr: `codegraph ${args[0] ?? ""} timed out after ${timeoutMs / 1000}s`,
39
+ };
40
+ }
41
+ return decode(output);
42
+ }
43
+ function decode(output) {
44
+ return {
45
+ code: output.code,
46
+ stdout: new TextDecoder().decode(output.stdout),
47
+ stderr: new TextDecoder().decode(output.stderr),
48
+ };
49
+ }
50
+ export function createCodegraphRunner(indexDir) {
51
+ return (binary, args, worktree) => {
52
+ const timeoutMs = INDEX_COMMANDS.has(args[0] ?? "") ? null : undefined;
53
+ return execCodegraph(binary, args, worktree, {
54
+ timeoutMs,
55
+ codegraphDir: indexDir,
56
+ });
57
+ };
58
+ }
59
+ export async function runCodegraphTool(binary, args, worktree, runner) {
60
+ const result = await runner(binary, args, worktree);
61
+ const output = result.stdout.trim() || result.stderr.trim();
62
+ if (result.code !== 0) {
63
+ return `codegraph ${args[0]} exited ${result.code}: ${output}`;
64
+ }
65
+ return output || "(no output)";
66
+ }
@@ -0,0 +1,89 @@
1
+ /** Cross-layer contracts only. A type owned by one layer (Fact, Source,
2
+ * PullRequest, State) lives next to the code that owns it instead, in
3
+ * `knowledge/types.ts` — see PLAN.md Section 3. */
4
+ export type Json = Record<string, unknown>;
5
+ export type Options = {
6
+ command: "probe" | "init" | "remake" | "review";
7
+ repo: string;
8
+ prNumber?: number;
9
+ debug: boolean;
10
+ logTime: boolean;
11
+ /** Reviews every changed file with no own/upstream split — the behavior
12
+ * before scope.ts existed. Off by default: a re-review round's diff can
13
+ * carry a merge from the default branch that dwarfs the PR's own change,
14
+ * and neither a human reviewer nor this flag's absence asks anyone to read
15
+ * it as if the PR authored it. */
16
+ reviewUpstream?: boolean;
17
+ /** CLI review enables codegraph by default (`--disable-codegraph` to skip).
18
+ * Server jobs still follow per-repo settings. Indexing adds tokens and time;
19
+ * measured PR recall gains were small when this was opt-in only (K25). */
20
+ useCodegraph?: boolean;
21
+ improveMatrix: number;
22
+ ghConcurrent: number;
23
+ aiConcurrent: number;
24
+ envPath?: string;
25
+ auth: "gh" | "pat";
26
+ githubPat?: string;
27
+ ai: "none" | "openrouter" | "hetzner";
28
+ aiToken?: string;
29
+ lowModel?: string;
30
+ highModel?: string;
31
+ synthesisVersion: number;
32
+ includeCodebase: boolean;
33
+ includePullRequests: boolean;
34
+ includePullRequestChanges: boolean;
35
+ includeCommitHistory: boolean;
36
+ includeHowRepoWorks: boolean;
37
+ maxCommits?: number;
38
+ maxPrMonths?: number;
39
+ maxPullRequestChangeLines?: number;
40
+ maxComments?: number;
41
+ };
42
+ export type GitHubClient = {
43
+ request<T>(endpoint: string): Promise<T>;
44
+ pages<T>(endpoint: string, limit?: number, progress?: (page: number, fetched: number) => void): Promise<T[]>;
45
+ /** POST JSON. Optional so collect-test fakes stay GET-only. Review
46
+ * posting requires it. */
47
+ write?<T>(endpoint: string, body: unknown): Promise<T>;
48
+ /** GitHub Checks API operations. Optional so read-only test clients and
49
+ * older integrations can continue to review without check runs. */
50
+ createCheckRun?<T>(endpoint: string, body: unknown): Promise<T>;
51
+ updateCheckRun?<T>(endpoint: string, body: unknown): Promise<T>;
52
+ };
53
+ export type AiToolCall = {
54
+ id: string;
55
+ type: "function";
56
+ function: {
57
+ name: string;
58
+ arguments: string;
59
+ };
60
+ };
61
+ export type AiMessage = {
62
+ role: "system" | "user" | "assistant" | "tool";
63
+ content?: string | null;
64
+ tool_calls?: AiToolCall[];
65
+ tool_call_id?: string;
66
+ name?: string;
67
+ };
68
+ export type AiRequest = {
69
+ system?: string;
70
+ prompt: string;
71
+ messages?: AiMessage[];
72
+ tools?: Json[];
73
+ maxTokens: number;
74
+ job: string;
75
+ reasoningEffort?: "max" | "xhigh" | "high" | "medium" | "low" | "none";
76
+ };
77
+ export type AiResponse = {
78
+ text: string;
79
+ toolCalls?: AiToolCall[];
80
+ tokensIn: number;
81
+ tokensOut: number;
82
+ cost?: number;
83
+ model: string;
84
+ provider: "openrouter" | "hetzner";
85
+ };
86
+ export type AiProvider = {
87
+ supportsTools?: boolean;
88
+ complete(request: AiRequest): Promise<AiResponse>;
89
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ /** Remove leftover `.<name>.tmp-*` files from an interrupted write.
2
+ * Only deletes temps older than `minAgeMs` so concurrent writers are not disturbed. */
3
+ export declare function cleanStaleTempFiles(directory: string, baseName: string, minAgeMs?: number): Promise<void>;
4
+ export declare function writeTextFileAtomic(path: string, text: string): Promise<void>;