@almightygpt/core 0.4.0 → 0.5.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 (49) hide show
  1. package/dist/adapters/claude.d.ts +1 -1
  2. package/dist/adapters/claude.d.ts.map +1 -1
  3. package/dist/adapters/claude.js.map +1 -1
  4. package/dist/adapters/gemini.d.ts +1 -1
  5. package/dist/adapters/gemini.d.ts.map +1 -1
  6. package/dist/adapters/gemini.js.map +1 -1
  7. package/dist/adapters/mock.d.ts +1 -0
  8. package/dist/adapters/mock.d.ts.map +1 -1
  9. package/dist/adapters/mock.js +1 -0
  10. package/dist/adapters/mock.js.map +1 -1
  11. package/dist/adapters/openai.d.ts +1 -1
  12. package/dist/adapters/openai.d.ts.map +1 -1
  13. package/dist/adapters/openai.js.map +1 -1
  14. package/dist/adapters/types.d.ts +8 -0
  15. package/dist/adapters/types.d.ts.map +1 -1
  16. package/dist/adapters/types.js.map +1 -1
  17. package/dist/git/status.d.ts.map +1 -1
  18. package/dist/git/status.js +18 -5
  19. package/dist/git/status.js.map +1 -1
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +2 -2
  23. package/dist/index.js.map +1 -1
  24. package/dist/review/diff-filter.d.ts +37 -0
  25. package/dist/review/diff-filter.d.ts.map +1 -0
  26. package/dist/review/diff-filter.js +144 -0
  27. package/dist/review/diff-filter.js.map +1 -0
  28. package/dist/review/run-diff-review.d.ts.map +1 -1
  29. package/dist/review/run-diff-review.js +30 -8
  30. package/dist/review/run-diff-review.js.map +1 -1
  31. package/dist/review/run-worker-reviewer.d.ts.map +1 -1
  32. package/dist/review/run-worker-reviewer.js +31 -10
  33. package/dist/review/run-worker-reviewer.js.map +1 -1
  34. package/dist/review/write.d.ts +15 -0
  35. package/dist/review/write.d.ts.map +1 -1
  36. package/dist/review/write.js +29 -0
  37. package/dist/review/write.js.map +1 -1
  38. package/package.json +2 -1
  39. package/src/adapters/claude.ts +1 -1
  40. package/src/adapters/gemini.ts +1 -1
  41. package/src/adapters/mock.ts +1 -0
  42. package/src/adapters/openai.ts +1 -1
  43. package/src/adapters/types.ts +8 -0
  44. package/src/git/status.ts +21 -5
  45. package/src/index.ts +3 -1
  46. package/src/review/diff-filter.ts +190 -0
  47. package/src/review/run-diff-review.ts +40 -8
  48. package/src/review/run-worker-reviewer.ts +41 -10
  49. package/src/review/write.ts +42 -0
@@ -37,12 +37,16 @@ import {
37
37
  } from "../runs/folder.js";
38
38
  import type { RunMetadata } from "../runs/types.js";
39
39
  import { collectGitDiff } from "./diff.js";
40
+ import { filterDiffByIgnoreLists } from "./diff-filter.js";
40
41
  import { assembleMemory } from "./memory.js";
41
42
  import {
42
43
  buildReviewerSystemFraming,
43
44
  buildReviewerUserMessage,
44
45
  } from "./prompts.js";
45
- import { writeHumanReviewFile } from "./write.js";
46
+ import {
47
+ preflightReviewFileCollision,
48
+ writeHumanReviewFile,
49
+ } from "./write.js";
46
50
  import { BudgetTracker, BudgetExceededError } from "./budget.js";
47
51
 
48
52
  export interface DiffReviewOptions {
@@ -111,6 +115,16 @@ export async function runDiffReview(
111
115
  );
112
116
  }
113
117
 
118
+ // Preflight the review-file collision BEFORE any paid adapter call.
119
+ // (Codex review v0.5: previously the check ran post-adapter, burning API
120
+ // money on duplicate-topic runs that were going to fail anyway.)
121
+ await preflightReviewFileCollision(
122
+ opts.repoRoot,
123
+ config.reviewsDir,
124
+ opts.topic,
125
+ opts.force ?? false,
126
+ );
127
+
114
128
  // Create run folder up front so partial failures still leave an artifact.
115
129
  const runFolder = await createRunFolder({
116
130
  repoRoot: opts.repoRoot,
@@ -157,14 +171,29 @@ export async function runDiffReview(
157
171
  );
158
172
  }
159
173
 
174
+ // Codex review v0.5 P1 #2: filter ignored files BEFORE secret redaction
175
+ // and BEFORE the provider call. Honors .almightyignore + .gitignore +
176
+ // config.context.exclude. Redaction still runs on the survivors as
177
+ // defense-in-depth.
178
+ const filtered = await filterDiffByIgnoreLists({
179
+ repoRoot: opts.repoRoot,
180
+ diffText: diffResult.diff,
181
+ files: diffResult.files,
182
+ configExclude: config.context.exclude,
183
+ });
184
+
160
185
  const redaction = config.security.redactSecrets
161
- ? redactSecrets(diffResult.diff)
162
- : { text: diffResult.diff, redactions: [], totalCount: 0 };
186
+ ? redactSecrets(filtered.filteredDiff)
187
+ : { text: filtered.filteredDiff, redactions: [], totalCount: 0 };
163
188
 
164
189
  const manifest = buildContextManifest({
165
190
  inputSource: opts.range ? "diff-range" : "diff",
166
- filesIncluded: diffResult.files.map((p) => ({ path: p, bytes: 0 })),
167
- filesSkipped: [],
191
+ filesIncluded: filtered.filesIncluded.map((p) => ({ path: p, bytes: 0 })),
192
+ filesSkipped: filtered.filesSkipped.map((s) => ({
193
+ path: s.path,
194
+ bytes: 0,
195
+ skippedReason: s.reason,
196
+ })),
168
197
  diffText: redaction.text,
169
198
  redaction: {
170
199
  enabled: config.security.redactSecrets,
@@ -184,15 +213,18 @@ export async function runDiffReview(
184
213
  const userMessage = buildReviewerUserMessage({
185
214
  topic: opts.topic,
186
215
  diff: redaction.text,
187
- files: diffResult.files,
216
+ files: filtered.filesIncluded,
188
217
  });
189
218
 
190
219
  const budget = new BudgetTracker(config.budget);
191
220
  const estimatedTokensIn = Math.ceil(
192
221
  (systemPrompt.length + userMessage.length) / 4,
193
222
  );
223
+ // Codex review v0.5 P2 #5: previously hardcoded "gpt-4o" here, which
224
+ // could block a cheap Gemini run on an OpenAI cost estimate. Use the
225
+ // actual adapter's default model so the preflight matches reality.
194
226
  budget.preflightCheck({
195
- model: "gpt-4o", // estimator only; adapter resolves real model
227
+ model: adapter.defaultModel,
196
228
  estimatedTokensIn,
197
229
  maxOutputTokens: 4096,
198
230
  });
@@ -278,7 +310,7 @@ export async function runDiffReview(
278
310
  costUsd: adapterOut.costUsd,
279
311
  latencyMs: adapterOut.latencyMs,
280
312
  diffEmpty: diffResult.empty,
281
- filesReviewed: diffResult.files,
313
+ filesReviewed: filtered.filesIncluded,
282
314
  memorySources: memory.sources,
283
315
  memoryMissing: memory.missing,
284
316
  runId: runFolder.id,
@@ -45,6 +45,7 @@ import {
45
45
  } from "../runs/folder.js";
46
46
  import type { AgentMetrics, RunMetadata } from "../runs/types.js";
47
47
  import { collectGitDiff } from "./diff.js";
48
+ import { filterDiffByIgnoreLists } from "./diff-filter.js";
48
49
  import { assembleMemory } from "./memory.js";
49
50
  import {
50
51
  buildReviewerSystemFraming,
@@ -52,7 +53,10 @@ import {
52
53
  buildWorkerSystemFraming,
53
54
  buildWorkerUserMessage,
54
55
  } from "./prompts.js";
55
- import { writeHumanReviewFile } from "./write.js";
56
+ import {
57
+ preflightReviewFileCollision,
58
+ writeHumanReviewFile,
59
+ } from "./write.js";
56
60
  import { BudgetTracker, BudgetExceededError } from "./budget.js";
57
61
  import type { ReviewEventHandler } from "./events.js";
58
62
 
@@ -153,6 +157,16 @@ export async function runWorkerReviewerReview(
153
157
  ? `Worker and Reviewer are both ${workerCfg.provider}. Cross-vendor pairing catches more issues.`
154
158
  : undefined;
155
159
 
160
+ // Preflight the review-file collision BEFORE Worker or Reviewer fires.
161
+ // (Codex review v0.5: previously the check ran post-adapter, burning two
162
+ // adapter calls of API money on duplicate-topic runs.)
163
+ await preflightReviewFileCollision(
164
+ opts.repoRoot,
165
+ config.reviewsDir,
166
+ opts.topic,
167
+ opts.force ?? false,
168
+ );
169
+
156
170
  const runFolder = await createRunFolder({
157
171
  repoRoot: opts.repoRoot,
158
172
  runsDir: config.runsDir,
@@ -207,9 +221,19 @@ export async function runWorkerReviewerReview(
207
221
  );
208
222
  }
209
223
 
224
+ // Codex review v0.5 P1 #2: filter ignored files BEFORE redaction and
225
+ // BEFORE either adapter call. Honors .almightyignore + .gitignore +
226
+ // config.context.exclude.
227
+ const filtered = await filterDiffByIgnoreLists({
228
+ repoRoot: opts.repoRoot,
229
+ diffText: diffResult.diff,
230
+ files: diffResult.files,
231
+ configExclude: config.context.exclude,
232
+ });
233
+
210
234
  const redaction = config.security.redactSecrets
211
- ? redactSecrets(diffResult.diff)
212
- : { text: diffResult.diff, redactions: [], totalCount: 0 };
235
+ ? redactSecrets(filtered.filteredDiff)
236
+ : { text: filtered.filteredDiff, redactions: [], totalCount: 0 };
213
237
  emit({
214
238
  type: "redaction_complete",
215
239
  totalCount: redaction.totalCount,
@@ -218,8 +242,12 @@ export async function runWorkerReviewerReview(
218
242
 
219
243
  const manifest = buildContextManifest({
220
244
  inputSource: opts.range ? "diff-range" : "diff",
221
- filesIncluded: diffResult.files.map((p) => ({ path: p, bytes: 0 })),
222
- filesSkipped: [],
245
+ filesIncluded: filtered.filesIncluded.map((p) => ({ path: p, bytes: 0 })),
246
+ filesSkipped: filtered.filesSkipped.map((s) => ({
247
+ path: s.path,
248
+ bytes: 0,
249
+ skippedReason: s.reason,
250
+ })),
223
251
  diffText: redaction.text,
224
252
  redaction: {
225
253
  enabled: config.security.redactSecrets,
@@ -245,11 +273,14 @@ export async function runWorkerReviewerReview(
245
273
  const workerUser = buildWorkerUserMessage({
246
274
  topic: opts.topic,
247
275
  diff: redaction.text,
248
- files: diffResult.files,
276
+ files: filtered.filesIncluded,
249
277
  });
250
278
 
279
+ // Codex review v0.5 P2 #5: use the actual adapter's default model
280
+ // for the preflight estimate so a cheap Gemini Worker isn't blocked
281
+ // by an OpenAI cost estimate.
251
282
  budget.preflightCheck({
252
- model: "gpt-4o",
283
+ model: workerAdapter.defaultModel,
253
284
  estimatedTokensIn: Math.ceil(
254
285
  (workerSystem.length + workerUser.length) / 4,
255
286
  ),
@@ -299,14 +330,14 @@ export async function runWorkerReviewerReview(
299
330
  const reviewerUser = buildReviewerOfWorkerUserMessage({
300
331
  topic: opts.topic,
301
332
  diff: redaction.text,
302
- files: diffResult.files,
333
+ files: filtered.filesIncluded,
303
334
  workerOutput: workerOut.content,
304
335
  workerAgent: workerName,
305
336
  workerProvider: workerAdapter.provider,
306
337
  });
307
338
 
308
339
  budget.preflightCheck({
309
- model: "gpt-4o",
340
+ model: reviewerAdapter.defaultModel,
310
341
  estimatedTokensIn: Math.ceil(
311
342
  (reviewerSystem.length + reviewerUser.length) / 4,
312
343
  ),
@@ -454,7 +485,7 @@ export async function runWorkerReviewerReview(
454
485
  },
455
486
  metrics,
456
487
  totals,
457
- filesReviewed: diffResult.files,
488
+ filesReviewed: filtered.filesIncluded,
458
489
  redactionsTotal: redaction.totalCount,
459
490
  memoryMissing: [
460
491
  ...new Set([...workerMemory.missing, ...reviewerMemory.missing]),
@@ -86,6 +86,48 @@ export async function writeHumanReviewFile(
86
86
  return { path: relPath, bytes: content.length };
87
87
  }
88
88
 
89
+ /**
90
+ * Compute the review file path for a topic without writing anything. Used
91
+ * by the review pipelines to **preflight** collisions *before* any paid
92
+ * adapter call. Same sanitization rules as writeHumanReviewFile.
93
+ */
94
+ export function reviewFilePathFor(
95
+ reviewsDir: string,
96
+ topic: string,
97
+ ): string {
98
+ return join(reviewsDir, `${sanitizeTopic(topic)}.md`);
99
+ }
100
+
101
+ /**
102
+ * Preflight check: throws ReviewFileExistsError if the topic's review file
103
+ * already exists and `force` isn't set. Surfaces the error BEFORE any
104
+ * adapter is invoked so the user doesn't spend API money discovering an
105
+ * easily-avoidable collision.
106
+ *
107
+ * Returns the (relative) path so the caller doesn't have to recompute it.
108
+ */
109
+ export async function preflightReviewFileCollision(
110
+ repoRoot: string,
111
+ reviewsDir: string,
112
+ topic: string,
113
+ force = false,
114
+ ): Promise<string> {
115
+ const relPath = reviewFilePathFor(reviewsDir, topic);
116
+ const absPath = join(repoRoot, relPath);
117
+ if (existsSync(absPath) && !force) {
118
+ // Also run the git-status check to give a richer dirty-file message
119
+ // when applicable; falls through to the existence error below.
120
+ await assertSafeToWrite(repoRoot, relPath, false);
121
+ throw new ReviewFileExistsError(
122
+ `Refusing to overwrite ${relPath}. Pass --force to overwrite (the ` +
123
+ `previous version remains in git history). Alternatively pick a ` +
124
+ `different --topic to keep both reviews.`,
125
+ relPath,
126
+ );
127
+ }
128
+ return relPath;
129
+ }
130
+
89
131
  function sanitizeTopic(topic: string): string {
90
132
  // Allow letters, digits, dot, dash, underscore. Replace everything else
91
133
  // with a single dash. Trim leading/trailing dashes. Lowercase.