@ariso-ai/ivan 1.0.24 → 1.0.26

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 (189) hide show
  1. package/.github/workflows/ivanagent.yml +1 -1
  2. package/README.md +29 -0
  3. package/dist/database/migration.d.ts +3 -2
  4. package/dist/database/migration.d.ts.map +1 -1
  5. package/dist/database/migration.js +4 -2
  6. package/dist/database/migration.js.map +1 -1
  7. package/dist/database/migrations/015_create_learnings_tables.d.ts +3 -0
  8. package/dist/database/migrations/015_create_learnings_tables.d.ts.map +1 -0
  9. package/dist/database/migrations/015_create_learnings_tables.js +32 -0
  10. package/dist/database/migrations/015_create_learnings_tables.js.map +1 -0
  11. package/dist/database/migrations/016_create_reviews_table.d.ts +3 -0
  12. package/dist/database/migrations/016_create_reviews_table.d.ts.map +1 -0
  13. package/dist/database/migrations/016_create_reviews_table.js +19 -0
  14. package/dist/database/migrations/016_create_reviews_table.js.map +1 -0
  15. package/dist/database/migrations/index.d.ts +1 -0
  16. package/dist/database/migrations/index.d.ts.map +1 -1
  17. package/dist/database/migrations/index.js +2 -0
  18. package/dist/database/migrations/index.js.map +1 -1
  19. package/dist/database/types.d.ts +1 -1
  20. package/dist/database/types.d.ts.map +1 -1
  21. package/dist/database.d.ts +1 -1
  22. package/dist/database.d.ts.map +1 -1
  23. package/dist/index.js +13 -5
  24. package/dist/index.js.map +1 -1
  25. package/dist/learnings/builder.d.ts +25 -0
  26. package/dist/learnings/builder.d.ts.map +1 -0
  27. package/dist/learnings/builder.js +247 -0
  28. package/dist/learnings/builder.js.map +1 -0
  29. package/dist/learnings/database.d.ts +45 -0
  30. package/dist/learnings/database.d.ts.map +1 -0
  31. package/dist/learnings/database.js +63 -0
  32. package/dist/learnings/database.js.map +1 -0
  33. package/dist/learnings/embeddings.d.ts +20 -0
  34. package/dist/learnings/embeddings.d.ts.map +1 -0
  35. package/dist/learnings/embeddings.js +54 -0
  36. package/dist/learnings/embeddings.js.map +1 -0
  37. package/dist/learnings/evidence-writer.d.ts +13 -0
  38. package/dist/learnings/evidence-writer.d.ts.map +1 -0
  39. package/dist/learnings/evidence-writer.js +182 -0
  40. package/dist/learnings/evidence-writer.js.map +1 -0
  41. package/dist/learnings/extractor.d.ts +32 -0
  42. package/dist/learnings/extractor.d.ts.map +1 -0
  43. package/dist/learnings/extractor.js +265 -0
  44. package/dist/learnings/extractor.js.map +1 -0
  45. package/dist/learnings/github-evidence.d.ts +89 -0
  46. package/dist/learnings/github-evidence.d.ts.map +1 -0
  47. package/dist/learnings/github-evidence.js +255 -0
  48. package/dist/learnings/github-evidence.js.map +1 -0
  49. package/dist/learnings/github-ingestion.d.ts +18 -0
  50. package/dist/learnings/github-ingestion.d.ts.map +1 -0
  51. package/dist/learnings/github-ingestion.js +29 -0
  52. package/dist/learnings/github-ingestion.js.map +1 -0
  53. package/dist/learnings/heuristics.d.ts +17 -0
  54. package/dist/learnings/heuristics.d.ts.map +1 -0
  55. package/dist/learnings/heuristics.js +52 -0
  56. package/dist/learnings/heuristics.js.map +1 -0
  57. package/dist/learnings/id.d.ts +11 -0
  58. package/dist/learnings/id.d.ts.map +1 -0
  59. package/dist/learnings/id.js +36 -0
  60. package/dist/learnings/id.js.map +1 -0
  61. package/dist/learnings/index.d.ts +9 -0
  62. package/dist/learnings/index.d.ts.map +1 -0
  63. package/dist/learnings/index.js +55 -0
  64. package/dist/learnings/index.js.map +1 -0
  65. package/dist/learnings/ingest-pr-command.d.ts +8 -0
  66. package/dist/learnings/ingest-pr-command.d.ts.map +1 -0
  67. package/dist/learnings/ingest-pr-command.js +15 -0
  68. package/dist/learnings/ingest-pr-command.js.map +1 -0
  69. package/dist/learnings/ingest-repo-command.d.ts +9 -0
  70. package/dist/learnings/ingest-repo-command.d.ts.map +1 -0
  71. package/dist/learnings/ingest-repo-command.js +62 -0
  72. package/dist/learnings/ingest-repo-command.js.map +1 -0
  73. package/dist/learnings/init-command.d.ts +15 -0
  74. package/dist/learnings/init-command.d.ts.map +1 -0
  75. package/dist/learnings/init-command.js +36 -0
  76. package/dist/learnings/init-command.js.map +1 -0
  77. package/dist/learnings/install-hooks-command.d.ts +17 -0
  78. package/dist/learnings/install-hooks-command.d.ts.map +1 -0
  79. package/dist/learnings/install-hooks-command.js +241 -0
  80. package/dist/learnings/install-hooks-command.js.map +1 -0
  81. package/dist/learnings/learning-writer.d.ts +6 -0
  82. package/dist/learnings/learning-writer.d.ts.map +1 -0
  83. package/dist/learnings/learning-writer.js +42 -0
  84. package/dist/learnings/learning-writer.js.map +1 -0
  85. package/dist/learnings/parser.d.ts +12 -0
  86. package/dist/learnings/parser.d.ts.map +1 -0
  87. package/dist/learnings/parser.js +133 -0
  88. package/dist/learnings/parser.js.map +1 -0
  89. package/dist/learnings/paths.d.ts +6 -0
  90. package/dist/learnings/paths.d.ts.map +1 -0
  91. package/dist/learnings/paths.js +11 -0
  92. package/dist/learnings/paths.js.map +1 -0
  93. package/dist/learnings/query-command.d.ts +9 -0
  94. package/dist/learnings/query-command.d.ts.map +1 -0
  95. package/dist/learnings/query-command.js +37 -0
  96. package/dist/learnings/query-command.js.map +1 -0
  97. package/dist/learnings/query.d.ts +7 -0
  98. package/dist/learnings/query.d.ts.map +1 -0
  99. package/dist/learnings/query.js +65 -0
  100. package/dist/learnings/query.js.map +1 -0
  101. package/dist/learnings/rebuild-command.d.ts +8 -0
  102. package/dist/learnings/rebuild-command.d.ts.map +1 -0
  103. package/dist/learnings/rebuild-command.js +16 -0
  104. package/dist/learnings/rebuild-command.js.map +1 -0
  105. package/dist/learnings/record-types.d.ts +98 -0
  106. package/dist/learnings/record-types.d.ts.map +1 -0
  107. package/dist/learnings/record-types.js +5 -0
  108. package/dist/learnings/record-types.js.map +1 -0
  109. package/dist/learnings/repository.d.ts +18 -0
  110. package/dist/learnings/repository.d.ts.map +1 -0
  111. package/dist/learnings/repository.js +73 -0
  112. package/dist/learnings/repository.js.map +1 -0
  113. package/dist/learnings/types.d.ts +17 -0
  114. package/dist/learnings/types.d.ts.map +1 -0
  115. package/dist/learnings/types.js +4 -0
  116. package/dist/learnings/types.js.map +1 -0
  117. package/dist/learnings/validator.d.ts +14 -0
  118. package/dist/learnings/validator.d.ts.map +1 -0
  119. package/dist/learnings/validator.js +44 -0
  120. package/dist/learnings/validator.js.map +1 -0
  121. package/dist/learnings/weighting.d.ts +33 -0
  122. package/dist/learnings/weighting.d.ts.map +1 -0
  123. package/dist/learnings/weighting.js +106 -0
  124. package/dist/learnings/weighting.js.map +1 -0
  125. package/dist/services/address-executor.d.ts +0 -1
  126. package/dist/services/address-executor.d.ts.map +1 -1
  127. package/dist/services/address-executor.js +1 -4
  128. package/dist/services/address-executor.js.map +1 -1
  129. package/dist/services/address-task-executor.d.ts +0 -1
  130. package/dist/services/address-task-executor.d.ts.map +1 -1
  131. package/dist/services/address-task-executor.js +18 -14
  132. package/dist/services/address-task-executor.js.map +1 -1
  133. package/dist/services/claude-cli-executor.d.ts +1 -1
  134. package/dist/services/claude-cli-executor.d.ts.map +1 -1
  135. package/dist/services/claude-executor.d.ts +1 -1
  136. package/dist/services/claude-executor.d.ts.map +1 -1
  137. package/dist/services/claude-executor.js +2 -2
  138. package/dist/services/claude-executor.js.map +1 -1
  139. package/dist/services/git-interfaces.d.ts +3 -3
  140. package/dist/services/git-interfaces.d.ts.map +1 -1
  141. package/dist/services/git-manager-cli.d.ts +1 -1
  142. package/dist/services/git-manager-cli.d.ts.map +1 -1
  143. package/dist/services/git-manager-cli.js +36 -13
  144. package/dist/services/git-manager-cli.js.map +1 -1
  145. package/dist/services/git-manager-pat.d.ts +2 -2
  146. package/dist/services/git-manager-pat.d.ts.map +1 -1
  147. package/dist/services/git-manager-pat.js +31 -8
  148. package/dist/services/git-manager-pat.js.map +1 -1
  149. package/dist/services/github-api-client.d.ts +39 -3
  150. package/dist/services/github-api-client.d.ts.map +1 -1
  151. package/dist/services/github-api-client.js +76 -8
  152. package/dist/services/github-api-client.js.map +1 -1
  153. package/dist/services/index.d.ts +2 -1
  154. package/dist/services/index.d.ts.map +1 -1
  155. package/dist/services/index.js.map +1 -1
  156. package/dist/services/job-manager.d.ts +1 -1
  157. package/dist/services/job-manager.d.ts.map +1 -1
  158. package/dist/services/job-manager.js.map +1 -1
  159. package/dist/services/openai-service.d.ts +1 -0
  160. package/dist/services/openai-service.d.ts.map +1 -1
  161. package/dist/services/openai-service.js +54 -0
  162. package/dist/services/openai-service.js.map +1 -1
  163. package/dist/services/pr-service-cli.d.ts +1 -1
  164. package/dist/services/pr-service-cli.d.ts.map +1 -1
  165. package/dist/services/pr-service-pat.d.ts +1 -1
  166. package/dist/services/pr-service-pat.d.ts.map +1 -1
  167. package/dist/services/pr-service-pat.js +2 -2
  168. package/dist/services/pr-service-pat.js.map +1 -1
  169. package/dist/services/repository-manager-cli.d.ts +1 -1
  170. package/dist/services/repository-manager-cli.d.ts.map +1 -1
  171. package/dist/services/repository-manager-cli.js.map +1 -1
  172. package/dist/services/repository-manager-pat.d.ts +3 -3
  173. package/dist/services/repository-manager-pat.d.ts.map +1 -1
  174. package/dist/services/repository-manager-pat.js.map +1 -1
  175. package/dist/services/review-executor.d.ts +8 -0
  176. package/dist/services/review-executor.d.ts.map +1 -0
  177. package/dist/services/review-executor.js +159 -0
  178. package/dist/services/review-executor.js.map +1 -0
  179. package/dist/services/service-factory.d.ts +1 -1
  180. package/dist/services/service-factory.d.ts.map +1 -1
  181. package/dist/services/task-executor.d.ts +3 -2
  182. package/dist/services/task-executor.d.ts.map +1 -1
  183. package/dist/services/task-executor.js +54 -13
  184. package/dist/services/task-executor.js.map +1 -1
  185. package/dist/types/non-interactive-config.d.ts +5 -0
  186. package/dist/types/non-interactive-config.d.ts.map +1 -1
  187. package/dist/web-server.d.ts.map +1 -1
  188. package/dist/web-server.js.map +1 -1
  189. package/package.json +7 -5
@@ -0,0 +1,255 @@
1
+ // GitHub evidence fetching: type definitions for all PR data structures and two
2
+ // fetch strategies—REST/GraphQL via a PAT token, or the `gh` CLI for token-free use.
3
+ import { execSync } from 'child_process';
4
+ import { ConfigManager } from '../config.js';
5
+ import { GitHubAPIClient } from '../services/github-api-client.js';
6
+ /**
7
+ * Lists all PR numbers for the repo, using the configured auth type.
8
+ * Returns numbers in descending order (newest first).
9
+ */
10
+ export async function fetchAllPullRequestNumbers(repoPath, options = {}) {
11
+ const configManager = new ConfigManager();
12
+ const authType = configManager.getGithubAuthType();
13
+ const state = options.state ?? 'merged';
14
+ const limit = options.limit ?? 100;
15
+ if (authType === 'pat') {
16
+ const pat = configManager.getGithubPat();
17
+ if (!pat) {
18
+ throw new Error('GitHub PAT is not configured. Please run "ivan configure" and set your PAT.');
19
+ }
20
+ return fetchAllPullRequestNumbersPat(repoPath, pat, state, limit);
21
+ }
22
+ return fetchAllPullRequestNumbersCli(repoPath, state, limit);
23
+ }
24
+ async function fetchAllPullRequestNumbersPat(repoPath, pat, state, limit) {
25
+ const repo = GitHubAPIClient.getRepoInfoFromRemote(repoPath);
26
+ const client = new GitHubAPIClient(pat);
27
+ return client.listPullRequestNumbers(repo.owner, repo.repo, state, limit);
28
+ }
29
+ function fetchAllPullRequestNumbersCli(repoPath, state, limit) {
30
+ // gh pr list supports "merged" as a state directly
31
+ const raw = execSync(`gh pr list --state ${state} --json number --limit ${limit}`, { cwd: repoPath, encoding: 'utf8' });
32
+ const prs = JSON.parse(raw);
33
+ return Promise.resolve(prs.map((pr) => pr.number));
34
+ }
35
+ /**
36
+ * Entry point for PR evidence fetching: routes to the PAT-based REST/GraphQL path
37
+ * or the `gh` CLI path depending on the configured auth type.
38
+ */
39
+ export async function fetchGitHubPullRequestEvidence(repoPath, prNumber) {
40
+ const configManager = new ConfigManager();
41
+ const authType = configManager.getGithubAuthType();
42
+ if (authType === 'pat') {
43
+ const pat = configManager.getGithubPat();
44
+ if (!pat) {
45
+ throw new Error('GitHub PAT is not configured. Please run "ivan configure" and set your PAT.');
46
+ }
47
+ return fetchPatEvidence(repoPath, prNumber, pat);
48
+ }
49
+ return fetchCliEvidence(repoPath, prNumber);
50
+ }
51
+ /** Fetches all PR evidence via the GitHub REST API and GraphQL using a Personal Access Token. */
52
+ async function fetchPatEvidence(repoPath, prNumber, pat) {
53
+ const repo = GitHubAPIClient.getRepoInfoFromRemote(repoPath);
54
+ const client = new GitHubAPIClient(pat);
55
+ const owner = repo.owner;
56
+ const name = repo.repo;
57
+ const pr = await client.getPullRequestEvidence(owner, name, prNumber);
58
+ const reviewThreads = await client.getDetailedReviewThreads(owner, name, prNumber);
59
+ const checks = await client.getPRChecks(owner, name, prNumber);
60
+ return {
61
+ repository: { owner, name },
62
+ pullRequest: {
63
+ number: pr.number,
64
+ title: pr.title,
65
+ body: pr.body ?? '',
66
+ url: pr.url,
67
+ state: pr.state,
68
+ headRefName: pr.headRefName,
69
+ headSha: pr.headSha,
70
+ author: pr.author
71
+ },
72
+ issueComments: pr.issueComments,
73
+ reviews: pr.reviews,
74
+ reviewThreads: reviewThreads.map((thread) => ({
75
+ id: thread.id,
76
+ isResolved: thread.isResolved,
77
+ isOutdated: thread.isOutdated,
78
+ comments: thread.comments.nodes.map((comment) => ({
79
+ id: comment.id,
80
+ databaseId: comment.databaseId,
81
+ body: comment.body,
82
+ createdAt: comment.createdAt,
83
+ author: comment.author ? { login: comment.author.login } : undefined,
84
+ path: comment.path,
85
+ line: comment.line,
86
+ url: comment.url,
87
+ diffHunk: comment.diffHunk
88
+ }))
89
+ })),
90
+ files: pr.files,
91
+ checks: checks.map((check) => ({
92
+ name: check.name,
93
+ state: check.state,
94
+ link: check.link
95
+ }))
96
+ };
97
+ }
98
+ /** Fetches all PR evidence by shelling out to the `gh` CLI, normalising the output into `GitHubPullRequestEvidence`. */
99
+ async function fetchCliEvidence(repoPath, prNumber) {
100
+ const repoInfo = JSON.parse(execSync('gh repo view --json owner,name', {
101
+ cwd: repoPath,
102
+ encoding: 'utf8'
103
+ }));
104
+ const pr = JSON.parse(execSync([
105
+ 'gh pr view',
106
+ String(prNumber),
107
+ '--json',
108
+ 'number,title,body,headRefName,url,state,author,reviews,comments,files'
109
+ ].join(' '), {
110
+ cwd: repoPath,
111
+ encoding: 'utf8'
112
+ }));
113
+ function runGraphQL(query, variables) {
114
+ const payload = JSON.stringify({ query, variables });
115
+ return JSON.parse(execSync('gh api graphql --input -', {
116
+ cwd: repoPath,
117
+ encoding: 'utf8',
118
+ maxBuffer: 1024 * 1024 * 10,
119
+ input: payload
120
+ }));
121
+ }
122
+ const initialQuery = `
123
+ query($owner: String!, $repo: String!, $prNumber: Int!, $cursor: String) {
124
+ repository(owner: $owner, name: $repo) {
125
+ pullRequest(number: $prNumber) {
126
+ commits(last: 1) {
127
+ nodes {
128
+ commit {
129
+ oid
130
+ }
131
+ }
132
+ }
133
+ reviewThreads(first: 100, after: $cursor) {
134
+ pageInfo {
135
+ hasNextPage
136
+ endCursor
137
+ }
138
+ nodes {
139
+ id
140
+ isResolved
141
+ isOutdated
142
+ comments(first: 100) {
143
+ nodes {
144
+ id
145
+ databaseId
146
+ body
147
+ createdAt
148
+ path
149
+ line
150
+ url
151
+ diffHunk
152
+ author {
153
+ login
154
+ }
155
+ }
156
+ }
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
162
+ `;
163
+ const baseVariables = {
164
+ owner: repoInfo.owner.login,
165
+ repo: repoInfo.name,
166
+ prNumber
167
+ };
168
+ let threadResponse = runGraphQL(initialQuery, {
169
+ ...baseVariables,
170
+ cursor: null
171
+ });
172
+ const headSha = threadResponse.data?.repository?.pullRequest?.commits?.nodes?.[0]?.commit
173
+ ?.oid;
174
+ const allThreadNodes = [
175
+ ...(threadResponse.data?.repository?.pullRequest?.reviewThreads?.nodes ??
176
+ [])
177
+ ];
178
+ {
179
+ let pageInfo = threadResponse.data?.repository?.pullRequest?.reviewThreads?.pageInfo;
180
+ while (pageInfo?.hasNextPage) {
181
+ const nextResponse = runGraphQL(initialQuery, {
182
+ ...baseVariables,
183
+ cursor: pageInfo.endCursor
184
+ });
185
+ const nextNodes = nextResponse.data?.repository?.pullRequest?.reviewThreads?.nodes ?? [];
186
+ allThreadNodes.push(...nextNodes);
187
+ pageInfo =
188
+ nextResponse.data?.repository?.pullRequest?.reviewThreads?.pageInfo;
189
+ }
190
+ }
191
+ const checks = JSON.parse(execSync(`gh pr checks ${prNumber} --json name,state,link`, {
192
+ cwd: repoPath,
193
+ encoding: 'utf8'
194
+ }));
195
+ const reviewThreads = allThreadNodes;
196
+ return {
197
+ repository: {
198
+ owner: repoInfo.owner.login,
199
+ name: repoInfo.name
200
+ },
201
+ pullRequest: {
202
+ number: pr.number,
203
+ title: pr.title,
204
+ body: pr.body ?? '',
205
+ url: pr.url,
206
+ state: pr.state,
207
+ headRefName: pr.headRefName,
208
+ headSha,
209
+ author: pr.author ? { login: pr.author.login } : undefined
210
+ },
211
+ issueComments: (pr.comments ?? []).map((comment, index) => ({
212
+ id: comment.id ?? `issue-comment-${pr.number}-${index + 1}`,
213
+ body: comment.body ?? '',
214
+ createdAt: comment.createdAt ?? '',
215
+ author: comment.author ? { login: comment.author.login } : undefined,
216
+ url: comment.url
217
+ })),
218
+ reviews: (pr.reviews ?? []).map((review, index) => ({
219
+ id: review.id ?? `review-${pr.number}-${index + 1}`,
220
+ body: review.body ?? '',
221
+ state: review.state ?? 'COMMENTED',
222
+ submittedAt: review.submittedAt,
223
+ author: review.author ? { login: review.author.login } : undefined,
224
+ url: review.url
225
+ })),
226
+ reviewThreads: reviewThreads.map((thread) => ({
227
+ id: thread.id,
228
+ isResolved: thread.isResolved ?? false,
229
+ isOutdated: thread.isOutdated,
230
+ comments: (thread.comments?.nodes ?? []).map((comment) => ({
231
+ id: comment.id ?? '',
232
+ databaseId: comment.databaseId,
233
+ body: comment.body ?? '',
234
+ createdAt: comment.createdAt ?? '',
235
+ author: comment.author ? { login: comment.author.login } : undefined,
236
+ path: comment.path,
237
+ line: comment.line,
238
+ url: comment.url,
239
+ diffHunk: comment.diffHunk
240
+ }))
241
+ })),
242
+ files: (pr.files ?? []).map((file) => ({
243
+ path: file.path,
244
+ additions: file.additions,
245
+ deletions: file.deletions,
246
+ changeType: file.changeType
247
+ })),
248
+ checks: checks.map((check) => ({
249
+ name: check.name,
250
+ state: check.state,
251
+ link: check.link
252
+ }))
253
+ };
254
+ }
255
+ //# sourceMappingURL=github-evidence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-evidence.js","sourceRoot":"","sources":["../../src/learnings/github-evidence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { LearningsBuildResult } from './builder.js';
2
+ import type { BuildSignalsResult } from './evidence-writer.js';
3
+ /** Returned by `ingestPullRequestEvidence`; summarises the full ingestion outcome. */
4
+ export interface PullRequestIngestionResult {
5
+ writtenPaths: string[];
6
+ rebuild: LearningsBuildResult;
7
+ }
8
+ /**
9
+ * Fetches signals for a single PR without running extraction.
10
+ * Used by `ingest-repo` to batch multiple PRs before a single extract pass.
11
+ */
12
+ export declare function fetchPullRequestSignals(repoPath: string, prNumber: number): Promise<BuildSignalsResult>;
13
+ /**
14
+ * Runs the full ingestion pipeline for a single PR: fetches GitHub evidence,
15
+ * extracts learnings, and rebuilds the SQLite index.
16
+ */
17
+ export declare function ingestPullRequestEvidence(repoPath: string, prNumber: number): Promise<PullRequestIngestionResult>;
18
+ //# sourceMappingURL=github-ingestion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-ingestion.d.ts","sourceRoot":"","sources":["../../src/learnings/github-ingestion.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAQ/D,sFAAsF;AACtF,MAAM,WAAW,0BAA0B;IACzC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,OAAO,EAAE,oBAAoB,CAAC;CAC/B;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,CAAC,CAG7B;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,0BAA0B,CAAC,CAkBrC"}
@@ -0,0 +1,29 @@
1
+ // Orchestrates the full end-to-end PR ingestion pipeline:
2
+ // fetch GitHub evidence -> build in-memory signals -> extract learnings -> rebuild DB.
3
+ import { buildEvidenceSignalsFromPullRequest } from './evidence-writer.js';
4
+ import { extractLearningsFromEvidence } from './extractor.js';
5
+ import { fetchGitHubPullRequestEvidence } from './github-evidence.js';
6
+ import { ensureLearningsDirectories, resolveLearningsRepositoryContext } from './repository.js';
7
+ /**
8
+ * Fetches signals for a single PR without running extraction.
9
+ * Used by `ingest-repo` to batch multiple PRs before a single extract pass.
10
+ */
11
+ export async function fetchPullRequestSignals(repoPath, prNumber) {
12
+ const payload = await fetchGitHubPullRequestEvidence(repoPath, prNumber);
13
+ return buildEvidenceSignalsFromPullRequest(payload);
14
+ }
15
+ /**
16
+ * Runs the full ingestion pipeline for a single PR: fetches GitHub evidence,
17
+ * extracts learnings, and rebuilds the SQLite index.
18
+ */
19
+ export async function ingestPullRequestEvidence(repoPath, prNumber) {
20
+ const context = resolveLearningsRepositoryContext(repoPath);
21
+ ensureLearningsDirectories(context);
22
+ const { signals, contextCache } = await fetchPullRequestSignals(repoPath, prNumber);
23
+ const extraction = await extractLearningsFromEvidence(repoPath, signals, contextCache);
24
+ return {
25
+ writtenPaths: extraction.writtenPaths,
26
+ rebuild: extraction.rebuild
27
+ };
28
+ }
29
+ //# sourceMappingURL=github-ingestion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-ingestion.js","sourceRoot":"","sources":["../../src/learnings/github-ingestion.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,uFAAuF;AAGvF,OAAO,EAAE,mCAAmC,EAAE,MAAM,sBAAsB,CAAC;AAE3E,OAAO,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,8BAA8B,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EACL,0BAA0B,EAC1B,iCAAiC,EAClC,MAAM,iBAAiB,CAAC;AAQzB;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,QAAgB,EAChB,QAAgB;IAEhB,MAAM,OAAO,GAAG,MAAM,8BAA8B,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACzE,OAAO,mCAAmC,CAAC,OAAO,CAAC,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,QAAgB,EAChB,QAAgB;IAEhB,MAAM,OAAO,GAAG,iCAAiC,CAAC,QAAQ,CAAC,CAAC;IAC5D,0BAA0B,CAAC,OAAO,CAAC,CAAC;IAEpC,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,uBAAuB,CAC7D,QAAQ,EACR,QAAQ,CACT,CAAC;IACF,MAAM,UAAU,GAAG,MAAM,4BAA4B,CACnD,QAAQ,EACR,OAAO,EACP,YAAY,CACb,CAAC;IAEF,OAAO;QACL,YAAY,EAAE,UAAU,CAAC,YAAY;QACrC,OAAO,EAAE,UAAU,CAAC,OAAO;KAC5B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Returns true for text that carries no engineering signal worth extracting—
3
+ * e.g. empty strings or comments that are purely stylistic nits or typo fixes.
4
+ */
5
+ export declare function isLowSignalReviewText(text: string): boolean;
6
+ /**
7
+ * Detects whether a GitHub actor is a bot by matching well-known bot name patterns.
8
+ * Returns `undefined` when no name is provided (anonymous author).
9
+ */
10
+ export declare function classifyAuthorType(authorName?: string): string | undefined;
11
+ /**
12
+ * Maps a GitHub review state to a signal penalty list.
13
+ * `COMMENTED` reviews lack an explicit verdict, so they get a `review_comment_only` penalty;
14
+ * `APPROVED` and `CHANGES_REQUESTED` reviews carry no penalty here.
15
+ */
16
+ export declare function inferReviewStatePenalty(state?: string): string[];
17
+ //# sourceMappingURL=heuristics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heuristics.d.ts","sourceRoot":"","sources":["../../src/learnings/heuristics.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAiB3D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAU1E;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAehE"}
@@ -0,0 +1,52 @@
1
+ // Text and author classification helpers shared across weighting and extraction.
2
+ // These are pure functions with no I/O so they can be unit-tested in isolation.
3
+ /**
4
+ * Returns true for text that carries no engineering signal worth extracting—
5
+ * e.g. empty strings or comments that are purely stylistic nits or typo fixes.
6
+ */
7
+ export function isLowSignalReviewText(text) {
8
+ const normalized = text.trim().toLowerCase();
9
+ if (!normalized) {
10
+ return true;
11
+ }
12
+ const lowSignalPrefixes = [
13
+ 'nit:',
14
+ 'nit ',
15
+ 'style:',
16
+ 'style ',
17
+ 'typo:',
18
+ 'typo '
19
+ ];
20
+ return lowSignalPrefixes.some((prefix) => normalized.startsWith(prefix));
21
+ }
22
+ /**
23
+ * Detects whether a GitHub actor is a bot by matching well-known bot name patterns.
24
+ * Returns `undefined` when no name is provided (anonymous author).
25
+ */
26
+ export function classifyAuthorType(authorName) {
27
+ if (!authorName) {
28
+ return undefined;
29
+ }
30
+ return /(bot|github-actions|coderabbit(?:ai)?|copilot|assistant|ari|ivan|codex)/i.test(authorName)
31
+ ? 'bot'
32
+ : 'human';
33
+ }
34
+ /**
35
+ * Maps a GitHub review state to a signal penalty list.
36
+ * `COMMENTED` reviews lack an explicit verdict, so they get a `review_comment_only` penalty;
37
+ * `APPROVED` and `CHANGES_REQUESTED` reviews carry no penalty here.
38
+ */
39
+ export function inferReviewStatePenalty(state) {
40
+ if (!state) {
41
+ return [];
42
+ }
43
+ const normalized = state.toUpperCase();
44
+ if (normalized === 'APPROVED') {
45
+ return [];
46
+ }
47
+ if (normalized === 'COMMENTED') {
48
+ return ['review_comment_only'];
49
+ }
50
+ return [];
51
+ }
52
+ //# sourceMappingURL=heuristics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heuristics.js","sourceRoot":"","sources":["../../src/learnings/heuristics.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,gFAAgF;AAEhF;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAE7C,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,iBAAiB,GAAG;QACxB,MAAM;QACN,MAAM;QACN,QAAQ;QACR,QAAQ;QACR,OAAO;QACP,OAAO;KACR,CAAC;IAEF,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAAmB;IACpD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,0EAA0E,CAAC,IAAI,CACpF,UAAU,CACX;QACC,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,OAAO,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACpD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IACvC,IAAI,UAAU,KAAK,UAAU,EAAE,CAAC;QAC9B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,UAAU,KAAK,WAAW,EAAE,CAAC;QAC/B,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACjC,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -0,0 +1,11 @@
1
+ /** Converts an arbitrary string to a lowercase, hyphen-separated slug safe for use in IDs and paths. */
2
+ export declare function slugify(value: string): string;
3
+ /**
4
+ * Builds a stable `{prefix}_{20-hex-char SHA1}` ID from the given parts.
5
+ * Parts are joined with the ASCII unit-separator (U+001F) before hashing so
6
+ * order matters but separators cannot clash with part content.
7
+ */
8
+ export declare function createDeterministicId(prefix: string, ...parts: Array<string | undefined>): string;
9
+ /** Returns true when `id` matches the `{prefix}_[a-z0-9][a-z0-9_-]*` pattern (or the generic 2–8-char prefix form). */
10
+ export declare function isStableRecordId(id: string, prefix?: string): boolean;
11
+ //# sourceMappingURL=id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/learnings/id.ts"],"names":[],"mappings":"AAQA,wGAAwG;AACxG,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO7C;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC,GAClC,MAAM,CAQR;AAED,uHAAuH;AACvH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAOrE"}
@@ -0,0 +1,36 @@
1
+ // Stable, deterministic ID generation for learnings records.
2
+ // All IDs use a `{prefix}_{body}` convention so type can be inferred at a glance.
3
+ import { createHash } from 'crypto';
4
+ /** Pattern every record ID must match regardless of prefix. */
5
+ const GENERIC_ID_PATTERN = /^[a-z]{2,8}_[a-z0-9][a-z0-9_-]*$/;
6
+ /** Converts an arbitrary string to a lowercase, hyphen-separated slug safe for use in IDs and paths. */
7
+ export function slugify(value) {
8
+ return value
9
+ .trim()
10
+ .toLowerCase()
11
+ .replace(/[^a-z0-9]+/g, '-')
12
+ .replace(/^-+|-+$/g, '')
13
+ .replace(/-{2,}/g, '-');
14
+ }
15
+ /**
16
+ * Builds a stable `{prefix}_{20-hex-char SHA1}` ID from the given parts.
17
+ * Parts are joined with the ASCII unit-separator (U+001F) before hashing so
18
+ * order matters but separators cannot clash with part content.
19
+ */
20
+ export function createDeterministicId(prefix, ...parts) {
21
+ const seed = parts.filter(Boolean).join('\u001f');
22
+ if (!seed) {
23
+ throw new Error('Deterministic IDs require at least one non-empty seed');
24
+ }
25
+ const digest = createHash('sha1').update(seed).digest('hex').slice(0, 20);
26
+ return `${prefix}_${digest}`;
27
+ }
28
+ /** Returns true when `id` matches the `{prefix}_[a-z0-9][a-z0-9_-]*` pattern (or the generic 2–8-char prefix form). */
29
+ export function isStableRecordId(id, prefix) {
30
+ if (prefix) {
31
+ const escapedPrefix = prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
32
+ return new RegExp(`^${escapedPrefix}_[a-z0-9][a-z0-9_-]*$`).test(id);
33
+ }
34
+ return GENERIC_ID_PATTERN.test(id);
35
+ }
36
+ //# sourceMappingURL=id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.js","sourceRoot":"","sources":["../../src/learnings/id.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,kFAAkF;AAElF,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC,+DAA+D;AAC/D,MAAM,kBAAkB,GAAG,kCAAkC,CAAC;AAE9D,wGAAwG;AACxG,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,OAAO,KAAK;SACT,IAAI,EAAE;SACN,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAc,EACd,GAAG,KAAgC;IAEnC,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAClD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC3E,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1E,OAAO,GAAG,MAAM,IAAI,MAAM,EAAE,CAAC;AAC/B,CAAC;AAED,uHAAuH;AACvH,MAAM,UAAU,gBAAgB,CAAC,EAAU,EAAE,MAAe;IAC1D,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;QACpE,OAAO,IAAI,MAAM,CAAC,IAAI,aAAa,uBAAuB,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,OAAO,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACrC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { Command } from 'commander';
2
+ import { initLearningsStore } from './init-command.js';
3
+ import { installLearningsHooks } from './install-hooks-command.js';
4
+ import { queryLearnings } from './query.js';
5
+ import { rebuildLearningsDatabase } from './builder.js';
6
+ /** Registers the `learnings` subcommand tree (init, rebuild, query, ingest-pr, ingest-repo, install-hooks) on `program`. */
7
+ export declare function registerLearningsCommands(program: Command): void;
8
+ export { initLearningsStore, installLearningsHooks, queryLearnings, rebuildLearningsDatabase };
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/learnings/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,kBAAkB,EAAkB,MAAM,mBAAmB,CAAC;AAGvE,OAAO,EACL,qBAAqB,EAEtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAGxD,4HAA4H;AAC5H,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiEhE;AAED,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,cAAc,EACd,wBAAwB,EACzB,CAAC"}
@@ -0,0 +1,55 @@
1
+ // Entry point for the `learnings` command group.
2
+ // Registers all subcommands on the Commander program and re-exports the public API
3
+ // used by other parts of ivan (task executor, hooks, etc.).
4
+ import { initLearningsStore, runInitCommand } from './init-command.js';
5
+ import { runIngestPrCommand } from './ingest-pr-command.js';
6
+ import { runIngestRepoCommand } from './ingest-repo-command.js';
7
+ import { installLearningsHooks, runInstallHooksCommand } from './install-hooks-command.js';
8
+ import { queryLearnings } from './query.js';
9
+ import { runQueryCommand } from './query-command.js';
10
+ import { rebuildLearningsDatabase } from './builder.js';
11
+ import { runRebuildCommand } from './rebuild-command.js';
12
+ /** Registers the `learnings` subcommand tree (init, rebuild, query, ingest-pr, ingest-repo, install-hooks) on `program`. */
13
+ export function registerLearningsCommands(program) {
14
+ const learnings = program
15
+ .command('learnings')
16
+ .description('Manage repo-local learnings records and derived .ivan/db.sqlite');
17
+ learnings
18
+ .command('init')
19
+ .description('Initialize canonical learnings storage in the target repository')
20
+ .requiredOption('--repo <path>', 'Repository root path')
21
+ .action(runInitCommand);
22
+ learnings
23
+ .command('rebuild')
24
+ .description('Rebuild <repo>/.ivan/db.sqlite from canonical learnings records')
25
+ .requiredOption('--repo <path>', 'Repository root path')
26
+ .option('--if-stale', 'Skip rebuild if .ivan/db.sqlite is already up to date')
27
+ .action(runRebuildCommand);
28
+ learnings
29
+ .command('query')
30
+ .description('Query the local .ivan/db.sqlite without live GitHub access')
31
+ .requiredOption('--repo <path>', 'Repository root path')
32
+ .requiredOption('--text <text>', 'Search text')
33
+ .option('--limit <number>', 'Maximum learnings to return', '5')
34
+ .action(runQueryCommand);
35
+ learnings
36
+ .command('ingest-pr')
37
+ .description('Fetch GitHub PR evidence and write canonical evidence records')
38
+ .requiredOption('--repo <path>', 'Repository root path')
39
+ .requiredOption('--pr <number>', 'Pull request number')
40
+ .action(runIngestPrCommand);
41
+ learnings
42
+ .command('ingest-repo')
43
+ .description('Fetch evidence for all PRs in a repo and extract learnings in one pass')
44
+ .requiredOption('--repo <path>', 'Repository root path')
45
+ .option('--limit <number>', 'Maximum number of PRs to ingest', '100')
46
+ .option('--state <state>', 'PR state to fetch: open, closed, merged, or all', 'merged')
47
+ .action(runIngestRepoCommand);
48
+ learnings
49
+ .command('install-hooks')
50
+ .description('Install Claude Code hook scripts for UserPromptSubmit and PostToolUse(Edit|Write|MultiEdit)')
51
+ .requiredOption('--repo <path>', 'Repository root path')
52
+ .action(runInstallHooksCommand);
53
+ }
54
+ export { initLearningsStore, installLearningsHooks, queryLearnings, rebuildLearningsDatabase };
55
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/learnings/index.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,mFAAmF;AACnF,4DAA4D;AAG5D,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD,4HAA4H;AAC5H,MAAM,UAAU,yBAAyB,CAAC,OAAgB;IACxD,MAAM,SAAS,GAAG,OAAO;SACtB,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CACV,iEAAiE,CAClE,CAAC;IAEJ,SAAS;SACN,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CACV,iEAAiE,CAClE;SACA,cAAc,CAAC,eAAe,EAAE,sBAAsB,CAAC;SACvD,MAAM,CAAC,cAAc,CAAC,CAAC;IAE1B,SAAS;SACN,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CACV,iEAAiE,CAClE;SACA,cAAc,CAAC,eAAe,EAAE,sBAAsB,CAAC;SACvD,MAAM,CACL,YAAY,EACZ,uDAAuD,CACxD;SACA,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAE7B,SAAS;SACN,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,4DAA4D,CAAC;SACzE,cAAc,CAAC,eAAe,EAAE,sBAAsB,CAAC;SACvD,cAAc,CAAC,eAAe,EAAE,aAAa,CAAC;SAC9C,MAAM,CAAC,kBAAkB,EAAE,6BAA6B,EAAE,GAAG,CAAC;SAC9D,MAAM,CAAC,eAAe,CAAC,CAAC;IAE3B,SAAS;SACN,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CACV,+DAA+D,CAChE;SACA,cAAc,CAAC,eAAe,EAAE,sBAAsB,CAAC;SACvD,cAAc,CAAC,eAAe,EAAE,qBAAqB,CAAC;SACtD,MAAM,CAAC,kBAAkB,CAAC,CAAC;IAE9B,SAAS;SACN,OAAO,CAAC,aAAa,CAAC;SACtB,WAAW,CACV,wEAAwE,CACzE;SACA,cAAc,CAAC,eAAe,EAAE,sBAAsB,CAAC;SACvD,MAAM,CAAC,kBAAkB,EAAE,iCAAiC,EAAE,KAAK,CAAC;SACpE,MAAM,CACL,iBAAiB,EACjB,iDAAiD,EACjD,QAAQ,CACT;SACA,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAEhC,SAAS;SACN,OAAO,CAAC,eAAe,CAAC;SACxB,WAAW,CACV,6FAA6F,CAC9F;SACA,cAAc,CAAC,eAAe,EAAE,sBAAsB,CAAC;SACvD,MAAM,CAAC,sBAAsB,CAAC,CAAC;AACpC,CAAC;AAED,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,cAAc,EACd,wBAAwB,EACzB,CAAC"}
@@ -0,0 +1,8 @@
1
+ interface IngestPrCommandOptions {
2
+ repo: string;
3
+ pr: string;
4
+ }
5
+ /** Commander action handler: validates the PR number, calls `ingestPullRequestEvidence`, and prints a summary. */
6
+ export declare function runIngestPrCommand(options: IngestPrCommandOptions): Promise<void>;
7
+ export {};
8
+ //# sourceMappingURL=ingest-pr-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingest-pr-command.d.ts","sourceRoot":"","sources":["../../src/learnings/ingest-pr-command.ts"],"names":[],"mappings":"AAMA,UAAU,sBAAsB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,kHAAkH;AAClH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAUf"}
@@ -0,0 +1,15 @@
1
+ // CLI handler for `ivan learnings ingest-pr`.
2
+ // Fetches all evidence for a GitHub PR and runs the full extraction + rebuild pipeline.
3
+ import chalk from 'chalk';
4
+ import { ingestPullRequestEvidence } from './github-ingestion.js';
5
+ /** Commander action handler: validates the PR number, calls `ingestPullRequestEvidence`, and prints a summary. */
6
+ export async function runIngestPrCommand(options) {
7
+ const prNumber = parseInt(options.pr, 10);
8
+ if (Number.isNaN(prNumber) || prNumber <= 0) {
9
+ throw new Error('PR number must be a positive integer');
10
+ }
11
+ const result = await ingestPullRequestEvidence(options.repo, prNumber);
12
+ console.log(chalk.green('✅ GitHub PR evidence ingested'));
13
+ console.log(chalk.gray(`DB: ${result.rebuild.dbPath}`));
14
+ }
15
+ //# sourceMappingURL=ingest-pr-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingest-pr-command.js","sourceRoot":"","sources":["../../src/learnings/ingest-pr-command.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,wFAAwF;AAExF,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAOlE,kHAAkH;AAClH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAA+B;IAE/B,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAC1C,IAAI,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,yBAAyB,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAEvE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC,CAAC;IAC1D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,9 @@
1
+ interface IngestRepoCommandOptions {
2
+ repo: string;
3
+ limit?: string;
4
+ state?: string;
5
+ }
6
+ /** Commander action handler: lists all PRs, fetches signals for each, then runs a single extract + rebuild. */
7
+ export declare function runIngestRepoCommand(options: IngestRepoCommandOptions): Promise<void>;
8
+ export {};
9
+ //# sourceMappingURL=ingest-repo-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingest-repo-command.d.ts","sourceRoot":"","sources":["../../src/learnings/ingest-repo-command.ts"],"names":[],"mappings":"AAaA,UAAU,wBAAwB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,+GAA+G;AAC/G,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,IAAI,CAAC,CA2Ef"}