@almightygpt/core 0.3.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 (58) 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 +46 -6
  4. package/dist/adapters/claude.js.map +1 -1
  5. package/dist/adapters/gemini.d.ts +1 -1
  6. package/dist/adapters/gemini.d.ts.map +1 -1
  7. package/dist/adapters/gemini.js.map +1 -1
  8. package/dist/adapters/mock.d.ts +1 -0
  9. package/dist/adapters/mock.d.ts.map +1 -1
  10. package/dist/adapters/mock.js +1 -0
  11. package/dist/adapters/mock.js.map +1 -1
  12. package/dist/adapters/openai.d.ts +1 -1
  13. package/dist/adapters/openai.d.ts.map +1 -1
  14. package/dist/adapters/openai.js +20 -5
  15. package/dist/adapters/openai.js.map +1 -1
  16. package/dist/adapters/types.d.ts +20 -2
  17. package/dist/adapters/types.d.ts.map +1 -1
  18. package/dist/adapters/types.js.map +1 -1
  19. package/dist/git/status.d.ts.map +1 -1
  20. package/dist/git/status.js +18 -5
  21. package/dist/git/status.js.map +1 -1
  22. package/dist/index.d.ts +2 -2
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +2 -2
  25. package/dist/index.js.map +1 -1
  26. package/dist/review/diff-filter.d.ts +37 -0
  27. package/dist/review/diff-filter.d.ts.map +1 -0
  28. package/dist/review/diff-filter.js +144 -0
  29. package/dist/review/diff-filter.js.map +1 -0
  30. package/dist/review/events.d.ts +2 -0
  31. package/dist/review/events.d.ts.map +1 -1
  32. package/dist/review/run-diff-review.d.ts +2 -0
  33. package/dist/review/run-diff-review.d.ts.map +1 -1
  34. package/dist/review/run-diff-review.js +34 -8
  35. package/dist/review/run-diff-review.js.map +1 -1
  36. package/dist/review/run-worker-reviewer.d.ts.map +1 -1
  37. package/dist/review/run-worker-reviewer.js +43 -10
  38. package/dist/review/run-worker-reviewer.js.map +1 -1
  39. package/dist/review/write.d.ts +15 -0
  40. package/dist/review/write.d.ts.map +1 -1
  41. package/dist/review/write.js +29 -0
  42. package/dist/review/write.js.map +1 -1
  43. package/dist/runs/types.d.ts +6 -0
  44. package/dist/runs/types.d.ts.map +1 -1
  45. package/package.json +2 -1
  46. package/src/adapters/claude.ts +59 -8
  47. package/src/adapters/gemini.ts +1 -1
  48. package/src/adapters/mock.ts +1 -0
  49. package/src/adapters/openai.ts +35 -7
  50. package/src/adapters/types.ts +20 -2
  51. package/src/git/status.ts +21 -5
  52. package/src/index.ts +3 -1
  53. package/src/review/diff-filter.ts +190 -0
  54. package/src/review/events.ts +2 -0
  55. package/src/review/run-diff-review.ts +46 -8
  56. package/src/review/run-worker-reviewer.ts +53 -10
  57. package/src/review/write.ts +42 -0
  58. package/src/runs/types.ts +6 -0
@@ -1 +1 @@
1
- {"version":3,"file":"write.js","sourceRoot":"","sources":["../../src/review/write.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"write.js","sourceRoot":"","sources":["../../src/review/write.ts"],"names":[],"mappings":"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"}
@@ -17,6 +17,12 @@ export interface AgentMetrics {
17
17
  provider: string;
18
18
  model: string;
19
19
  tokensIn: number;
20
+ /**
21
+ * Portion of tokensIn served from the provider's prompt cache (Anthropic
22
+ * cache_read, OpenAI prompt_tokens_details.cached_tokens). Always less
23
+ * than or equal to tokensIn. Zero when caching didn't trigger.
24
+ */
25
+ cachedTokensIn?: number;
20
26
  tokensOut: number;
21
27
  costUsd: number;
22
28
  latencyMs: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/runs/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,OAAO,GACf,aAAa,GACb,aAAa,GACb,oBAAoB,GACpB,wBAAwB,CAAC;AAE7B,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,gBAAgB,CAAC;AAE9E,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,EAAE,EAAE,MAAM,CAAC;IACX,mBAAmB;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,4BAA4B;IAC5B,GAAG,CAAC,EAAE;QACJ,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,OAAO,CAAC;KAChB,CAAC;IACF,+BAA+B;IAC/B,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,MAAM,GAAG,kBAAkB,GAAG,KAAK,CAAC;QACpE,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,sDAAsD;IACtD,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAC7E,gCAAgC;IAChC,eAAe,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxD,oBAAoB;IACpB,MAAM,EAAE,SAAS,CAAC;IAClB,yBAAyB;IACzB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,wBAAwB;IACxB,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,+CAA+C;IAC/C,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,uCAAuC;IACvC,MAAM,EAAE;QACN,gBAAgB,EAAE,MAAM,CAAC;QACzB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,wDAAwD;IACxD,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B;AAED,MAAM,MAAM,cAAc,GACtB,UAAU,GACV,eAAe,GACf,UAAU,GACV,UAAU,CAAC;AAEf,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/runs/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almightygpt/core",
3
- "version": "0.3.0",
3
+ "version": "0.5.1",
4
4
  "description": "Core orchestrator, adapters, config, runs, and review logic for AlmightyGPT",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -26,6 +26,7 @@
26
26
  "@anthropic-ai/sdk": "^0.30.0",
27
27
  "@google/generative-ai": "^0.21.0",
28
28
  "execa": "^9.3.0",
29
+ "ignore": "^6.0.2",
29
30
  "openai": "^4.52.0",
30
31
  "p-limit": "^5.0.0",
31
32
  "yaml": "^2.4.0",
@@ -39,7 +39,7 @@ export class ClaudeAdapter implements Adapter {
39
39
  readonly provider = "anthropic";
40
40
 
41
41
  private readonly client: Anthropic | null;
42
- private readonly defaultModel: string;
42
+ readonly defaultModel: string;
43
43
  private readonly defaultMaxOutputTokens: number;
44
44
  private readonly defaultTimeoutMs: number;
45
45
 
@@ -76,7 +76,23 @@ export class ClaudeAdapter implements Adapter {
76
76
  {
77
77
  model,
78
78
  max_tokens: maxOutputTokens,
79
- system: input.systemPrompt,
79
+ // Pass system as a content-block array with cache_control:
80
+ // ephemeral on the (only) block. Anthropic caches the marked
81
+ // block for ~5 minutes; subsequent calls with the same prefix
82
+ // get charged at 10% of normal input rate for the cached span.
83
+ // The CLI's review flow re-sends the same memory (AGENTS.md +
84
+ // CODEX_AGENT.md + rules.md) on every run, so this is a near-
85
+ // perfect fit.
86
+ // cache_control isn't in the SDK 0.30.x .d.ts yet but the
87
+ // v1/messages API accepts it. Cast at the call site.
88
+ system: [
89
+ {
90
+ type: "text",
91
+ text: input.systemPrompt,
92
+ cache_control: { type: "ephemeral" },
93
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
94
+ } as any,
95
+ ],
80
96
  messages: [{ role: "user", content: input.userMessage }],
81
97
  },
82
98
  { timeout: timeoutMs },
@@ -102,13 +118,36 @@ export class ClaudeAdapter implements Adapter {
102
118
  );
103
119
  }
104
120
 
105
- const tokensIn = response.usage.input_tokens;
106
- const tokensOut = response.usage.output_tokens;
107
- const costUsd = estimateCostUsd(model, tokensIn, tokensOut);
121
+ // Anthropic usage fields with caching enabled:
122
+ // input_tokens — fresh-hit input tokens (full price)
123
+ // cache_creation_input_tokens tokens that just got written to the
124
+ // cache (1.25× of normal rate, one-time)
125
+ // cache_read_input_tokens — tokens served from existing cache
126
+ // (0.1× of normal rate)
127
+ // We surface cache_read as `cachedTokensIn`. Cache-creation tokens are
128
+ // folded into `tokensIn` total for visibility and cost-modeled at the
129
+ // normal rate (close enough — the 25% surcharge is small relative to
130
+ // the savings on subsequent reads).
131
+ const usage = response.usage as Anthropic.Usage & {
132
+ cache_creation_input_tokens?: number;
133
+ cache_read_input_tokens?: number;
134
+ };
135
+ const freshIn = usage.input_tokens;
136
+ const cacheCreationIn = usage.cache_creation_input_tokens ?? 0;
137
+ const cacheReadIn = usage.cache_read_input_tokens ?? 0;
138
+ const tokensIn = freshIn + cacheCreationIn + cacheReadIn;
139
+ const tokensOut = usage.output_tokens;
140
+ const costUsd = estimateCostUsdWithCache(
141
+ model,
142
+ freshIn + cacheCreationIn, // both billed at ~normal input rate
143
+ cacheReadIn,
144
+ tokensOut,
145
+ );
108
146
 
109
147
  return {
110
148
  content,
111
149
  tokensIn,
150
+ cachedTokensIn: cacheReadIn,
112
151
  tokensOut,
113
152
  costUsd,
114
153
  latencyMs: Date.now() - start,
@@ -118,9 +157,17 @@ export class ClaudeAdapter implements Adapter {
118
157
  }
119
158
  }
120
159
 
121
- function estimateCostUsd(
160
+ /**
161
+ * Anthropic cache read discount: cache_read_input_tokens are charged at
162
+ * roughly 10% of normal input rate. (See
163
+ * https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching.)
164
+ */
165
+ const CACHE_READ_DISCOUNT = 0.1;
166
+
167
+ function estimateCostUsdWithCache(
122
168
  model: string,
123
- tokensIn: number,
169
+ freshOrCreationIn: number,
170
+ cacheReadIn: number,
124
171
  tokensOut: number,
125
172
  ): number {
126
173
  const key = Object.keys(PRICING_USD_PER_1M).find((k) =>
@@ -129,5 +176,9 @@ function estimateCostUsd(
129
176
  if (!key) return 0;
130
177
  const rates = PRICING_USD_PER_1M[key];
131
178
  if (!rates) return 0;
132
- return (tokensIn / 1_000_000) * rates.input + (tokensOut / 1_000_000) * rates.output;
179
+ return (
180
+ (freshOrCreationIn / 1_000_000) * rates.input +
181
+ (cacheReadIn / 1_000_000) * rates.input * CACHE_READ_DISCOUNT +
182
+ (tokensOut / 1_000_000) * rates.output
183
+ );
133
184
  }
@@ -59,7 +59,7 @@ export class GeminiAdapter implements Adapter {
59
59
  readonly provider = "google";
60
60
 
61
61
  private readonly client: GoogleGenerativeAI | null;
62
- private readonly defaultModel: string;
62
+ readonly defaultModel: string;
63
63
  private readonly defaultMaxOutputTokens: number;
64
64
  private readonly defaultTimeoutMs: number;
65
65
 
@@ -12,6 +12,7 @@ import type { Adapter, AdapterInput, AdapterOutput } from "./types.js";
12
12
  export class MockAdapter implements Adapter {
13
13
  readonly name = "mock";
14
14
  readonly provider = "mock";
15
+ readonly defaultModel = "mock-1";
15
16
 
16
17
  constructor(
17
18
  private readonly opts: {
@@ -43,7 +43,7 @@ export class OpenAIAdapter implements Adapter {
43
43
  readonly provider = "openai";
44
44
 
45
45
  private readonly client: OpenAI | null;
46
- private readonly defaultModel: string;
46
+ readonly defaultModel: string;
47
47
  private readonly defaultMaxOutputTokens: number;
48
48
  private readonly defaultTimeoutMs: number;
49
49
 
@@ -118,13 +118,30 @@ export class OpenAIAdapter implements Adapter {
118
118
  }
119
119
 
120
120
  const content = choice.message.content;
121
- const tokensIn = response.usage?.prompt_tokens ?? 0;
122
- const tokensOut = response.usage?.completion_tokens ?? 0;
123
- const costUsd = estimateCostUsd(model, tokensIn, tokensOut);
121
+ // OpenAI prompt caching is automatic for prompts > 1024 tokens; the
122
+ // cached span is the longest common prefix across recent calls with
123
+ // the same model. The fraction cached comes back in
124
+ // prompt_tokens_details.cached_tokens. Cache reads are billed at ~50%
125
+ // of normal input rate.
126
+ const usage = response.usage as
127
+ | (typeof response.usage & {
128
+ prompt_tokens_details?: { cached_tokens?: number };
129
+ })
130
+ | undefined;
131
+ const tokensIn = usage?.prompt_tokens ?? 0;
132
+ const cacheReadIn = usage?.prompt_tokens_details?.cached_tokens ?? 0;
133
+ const tokensOut = usage?.completion_tokens ?? 0;
134
+ const costUsd = estimateCostUsdWithCache(
135
+ model,
136
+ tokensIn - cacheReadIn,
137
+ cacheReadIn,
138
+ tokensOut,
139
+ );
124
140
 
125
141
  return {
126
142
  content,
127
143
  tokensIn,
144
+ cachedTokensIn: cacheReadIn,
128
145
  tokensOut,
129
146
  costUsd,
130
147
  latencyMs: Date.now() - start,
@@ -134,9 +151,16 @@ export class OpenAIAdapter implements Adapter {
134
151
  }
135
152
  }
136
153
 
137
- function estimateCostUsd(
154
+ /**
155
+ * OpenAI cache-read discount: cached input tokens are billed at 50% of
156
+ * normal input rate. (See https://platform.openai.com/docs/guides/prompt-caching.)
157
+ */
158
+ const CACHE_READ_DISCOUNT = 0.5;
159
+
160
+ function estimateCostUsdWithCache(
138
161
  model: string,
139
- tokensIn: number,
162
+ freshTokensIn: number,
163
+ cacheReadIn: number,
140
164
  tokensOut: number,
141
165
  ): number {
142
166
  // Match by prefix so versioned model names (gpt-4o-2024-08-06) still resolve.
@@ -146,5 +170,9 @@ function estimateCostUsd(
146
170
  if (!key) return 0;
147
171
  const rates = PRICING_USD_PER_1M[key];
148
172
  if (!rates) return 0;
149
- return (tokensIn / 1_000_000) * rates.input + (tokensOut / 1_000_000) * rates.output;
173
+ return (
174
+ (freshTokensIn / 1_000_000) * rates.input +
175
+ (cacheReadIn / 1_000_000) * rates.input * CACHE_READ_DISCOUNT +
176
+ (tokensOut / 1_000_000) * rates.output
177
+ );
150
178
  }
@@ -32,11 +32,21 @@ export interface AdapterInput {
32
32
  export interface AdapterOutput {
33
33
  /** The model's text response. */
34
34
  content: string;
35
- /** Input token count (prompt). */
35
+ /** Input token count (prompt). Total — includes any cached portion. */
36
36
  tokensIn: number;
37
+ /**
38
+ * Subset of `tokensIn` that was served from the provider's prompt cache
39
+ * (cheaper). Anthropic returns `cache_read_input_tokens`. OpenAI returns
40
+ * `prompt_tokens_details.cached_tokens`. Adapters that don't support
41
+ * caching yet (or for which caching didn't trigger this run) leave this
42
+ * at 0 — meaning every input token was a fresh hit at full price.
43
+ */
44
+ cachedTokensIn?: number;
37
45
  /** Output token count (completion). */
38
46
  tokensOut: number;
39
- /** Estimated USD cost based on the model's published rates. */
47
+ /** Estimated USD cost based on the model's published rates, with cache
48
+ * discount applied to `cachedTokensIn` per provider's published rate
49
+ * (Anthropic: 90% off on cache reads; OpenAI: 50% off; Gemini: varies). */
40
50
  costUsd: number;
41
51
  /** Wall-clock latency in milliseconds. */
42
52
  latencyMs: number;
@@ -51,6 +61,14 @@ export interface Adapter {
51
61
  readonly name: string;
52
62
  /** Underlying provider family (e.g. "openai", "anthropic", "mock"). */
53
63
  readonly provider: string;
64
+ /**
65
+ * The model this adapter will call by default, exposed so the
66
+ * orchestrator can pass it to `BudgetTracker.preflightCheck` instead of
67
+ * the previous hardcoded "gpt-4o". (Codex review v0.5 P2 #5.) Adapters
68
+ * with no meaningful default — like Mock — return an opaque string and
69
+ * the budget tracker falls back to its conservative pricing.
70
+ */
71
+ readonly defaultModel: string;
54
72
  /** True if credentials are available and the adapter can be invoked. */
55
73
  isAvailable(): Promise<boolean>;
56
74
  /** Execute one model call. */
package/src/git/status.ts CHANGED
@@ -40,7 +40,7 @@ export async function checkGitStatus(
40
40
  const normalized = relativePath.replace(/\\/g, "/");
41
41
 
42
42
  try {
43
- const { stdout } = await execa(
43
+ const { stdout, stderr, exitCode } = await execa(
44
44
  "git",
45
45
  ["status", "--short", "--", normalized],
46
46
  {
@@ -50,20 +50,36 @@ export async function checkGitStatus(
50
50
  },
51
51
  );
52
52
 
53
+ // Codex review v0.5 P2 #4: previously we ignored exitCode and treated
54
+ // every empty-stdout result as "clean", which silently includes the
55
+ // case where we're not in a git repo at all. Now we look at exitCode
56
+ // and stderr to distinguish "no changes" (exit 0, stdout empty) from
57
+ // "not a git repo" (exit 128, stderr matches).
58
+ if (exitCode !== 0) {
59
+ const stderrText = (stderr ?? "").toString();
60
+ if (
61
+ stderrText.includes("not a git repository") ||
62
+ stderrText.includes("fatal: not a git repository")
63
+ ) {
64
+ return { dirty: false, notInGitRepo: true };
65
+ }
66
+ // Some other git failure (corrupt index, etc.) — treat conservatively
67
+ // as no-protection so the caller can decide. Most callers should
68
+ // refuse writes in this case.
69
+ return { dirty: false, notInGitRepo: true };
70
+ }
71
+
53
72
  if (stdout.trim().length === 0) {
54
73
  return { dirty: false };
55
74
  }
56
75
 
57
76
  return { dirty: true, porcelain: stdout };
58
77
  } catch (err) {
59
- // execa throws if git is missing. Surface as not-in-repo so callers can
60
- // proceed cautiously rather than crash.
78
+ // execa throws if git is missing entirely. Surface as not-in-repo.
61
79
  const msg = err instanceof Error ? err.message : String(err);
62
80
  if (msg.includes("ENOENT")) {
63
81
  return { dirty: false, notInGitRepo: true };
64
82
  }
65
- // "not a git repository" returns non-zero but we used reject:false above,
66
- // so this branch is rare. Still, treat unknown errors as no-protection.
67
83
  return { dirty: false, notInGitRepo: true };
68
84
  }
69
85
  }
package/src/index.ts CHANGED
@@ -13,7 +13,7 @@
13
13
  * - budget/ ✅ task #14 BudgetTracker + BudgetExceededError
14
14
  */
15
15
 
16
- export const VERSION = "0.3.0";
16
+ export const VERSION = "0.5.1";
17
17
 
18
18
  // Git safety primitives
19
19
  export {
@@ -113,6 +113,8 @@ export { collectGitDiff, type DiffOptions, type DiffResult } from "./review/diff
113
113
  export {
114
114
  writeHumanReviewFile,
115
115
  ReviewFileExistsError,
116
+ reviewFilePathFor,
117
+ preflightReviewFileCollision,
116
118
  type WriteReviewFileOptions,
117
119
  } from "./review/write.js";
118
120
  export {
@@ -0,0 +1,190 @@
1
+ /**
2
+ * Filter a unified git diff so files matching `.gitignore`,
3
+ * `.almightyignore`, or `config.context.exclude` never reach a provider.
4
+ *
5
+ * This is a stronger promise than redaction. Redaction is defense-in-depth
6
+ * on file *contents*; filtering removes whole files from the payload before
7
+ * the provider ever sees them.
8
+ *
9
+ * Codex review v0.5 flagged that the public docs and templates already
10
+ * promise this behavior — this module makes the implementation match.
11
+ */
12
+
13
+ import { readFile } from "node:fs/promises";
14
+ import { existsSync } from "node:fs";
15
+ import { join } from "node:path";
16
+ // `ignore` is a CJS package; under NodeNext module resolution TypeScript
17
+ // won't synth a default export even with esModuleInterop. The package
18
+ // exports `function ignore(...)` as the module itself, plus
19
+ // `module.exports.default = ignore`. Use a runtime require to access the
20
+ // callable form without fighting the type system.
21
+ import { createRequire } from "node:module";
22
+ import type { Ignore } from "ignore";
23
+ const require = createRequire(import.meta.url);
24
+ // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-explicit-any
25
+ const ignore = require("ignore") as (options?: Record<string, unknown>) => Ignore;
26
+
27
+ export interface DiffFilterOptions {
28
+ /** Repo root containing .gitignore and .almightyignore. */
29
+ repoRoot: string;
30
+ /** Raw unified diff text from `git diff`. */
31
+ diffText: string;
32
+ /** List of files touched by the diff (typically from collectGitDiff). */
33
+ files: string[];
34
+ /**
35
+ * Additional exclude globs from `config.context.exclude`. Gitignore
36
+ * syntax — see `ignore` package docs. Optional.
37
+ */
38
+ configExclude?: string[];
39
+ }
40
+
41
+ export interface DiffFilterResult {
42
+ /** Diff text with ignored files' sections removed. */
43
+ filteredDiff: string;
44
+ /** Files that survived filtering. */
45
+ filesIncluded: string[];
46
+ /** Files that were dropped, with a human-readable reason per file. */
47
+ filesSkipped: { path: string; reason: string }[];
48
+ }
49
+
50
+ /**
51
+ * Build an Ignore matcher loaded from `.gitignore`, `.almightyignore`, and
52
+ * any config-level exclude globs. Missing files are silently skipped.
53
+ *
54
+ * Important nuance: `.gitignore` rules normally only apply to UNTRACKED
55
+ * files (tracked files in a diff exist because git is tracking them). For
56
+ * AlmightyGPT's purposes we want the conservative behavior — "if it
57
+ * matches gitignore, don't send it" — because secret-bearing tracked
58
+ * files (someone committed `.env` once) are exactly the case we're
59
+ * protecting against. So we apply both sets equally.
60
+ */
61
+ async function buildIgnoreMatcher(
62
+ repoRoot: string,
63
+ configExclude: string[],
64
+ ): Promise<{
65
+ matcher: Ignore;
66
+ sources: { name: string; loaded: boolean; rules: number }[];
67
+ }> {
68
+ const ig = ignore();
69
+ const sources: { name: string; loaded: boolean; rules: number }[] = [];
70
+
71
+ for (const file of [".gitignore", ".almightyignore"]) {
72
+ const path = join(repoRoot, file);
73
+ if (existsSync(path)) {
74
+ const text = await readFile(path, "utf8");
75
+ const lines = text.split("\n").filter((l) => l.trim() && !l.trim().startsWith("#"));
76
+ ig.add(text);
77
+ sources.push({ name: file, loaded: true, rules: lines.length });
78
+ } else {
79
+ sources.push({ name: file, loaded: false, rules: 0 });
80
+ }
81
+ }
82
+
83
+ if (configExclude.length > 0) {
84
+ ig.add(configExclude.join("\n"));
85
+ sources.push({
86
+ name: "config.context.exclude",
87
+ loaded: true,
88
+ rules: configExclude.length,
89
+ });
90
+ }
91
+
92
+ return { matcher: ig, sources };
93
+ }
94
+
95
+ /**
96
+ * Parse a unified git diff into per-file sections. Each section starts
97
+ * with `diff --git a/<path> b/<path>` and runs until the next such header
98
+ * (or end of input).
99
+ *
100
+ * Returns an array of `{ path, section }` so each file's diff hunks can
101
+ * be filtered independently and then reassembled.
102
+ */
103
+ function splitDiffByFile(
104
+ diffText: string,
105
+ ): { path: string; section: string }[] {
106
+ if (!diffText.trim()) return [];
107
+
108
+ const out: { path: string; section: string }[] = [];
109
+ const lines = diffText.split("\n");
110
+ let current: { path: string; lines: string[] } | null = null;
111
+
112
+ const headerRe = /^diff --git a\/(.+?) b\/(.+?)$/;
113
+
114
+ for (const line of lines) {
115
+ const match = line.match(headerRe);
116
+ if (match) {
117
+ if (current) {
118
+ out.push({ path: current.path, section: current.lines.join("\n") });
119
+ }
120
+ // Prefer the "b/" path (post-image) since renames change it.
121
+ // For most diffs both paths match.
122
+ current = { path: match[2]!, lines: [line] };
123
+ } else if (current) {
124
+ current.lines.push(line);
125
+ }
126
+ // Lines before any header (rare; usually empty diff) are dropped.
127
+ }
128
+ if (current) {
129
+ out.push({ path: current.path, section: current.lines.join("\n") });
130
+ }
131
+ return out;
132
+ }
133
+
134
+ export async function filterDiffByIgnoreLists(
135
+ opts: DiffFilterOptions,
136
+ ): Promise<DiffFilterResult> {
137
+ const configExclude = opts.configExclude ?? [];
138
+ const { matcher } = await buildIgnoreMatcher(opts.repoRoot, configExclude);
139
+
140
+ // Parse the diff into per-file sections. If the diff is empty or has no
141
+ // file headers, nothing to filter — return as-is.
142
+ const sections = splitDiffByFile(opts.diffText);
143
+
144
+ if (sections.length === 0) {
145
+ return {
146
+ filteredDiff: opts.diffText,
147
+ filesIncluded: opts.files,
148
+ filesSkipped: [],
149
+ };
150
+ }
151
+
152
+ const includedSections: string[] = [];
153
+ const filesIncluded: string[] = [];
154
+ const filesSkipped: { path: string; reason: string }[] = [];
155
+
156
+ for (const { path, section } of sections) {
157
+ if (matcher.ignores(path)) {
158
+ filesSkipped.push({
159
+ path,
160
+ reason: "ignored by .almightyignore / .gitignore / config.context.exclude",
161
+ });
162
+ continue;
163
+ }
164
+ includedSections.push(section);
165
+ filesIncluded.push(path);
166
+ }
167
+
168
+ // Also reconcile the original `files` list: any path from collectGitDiff
169
+ // that isn't in the surviving sections is also skipped. Usually they
170
+ // match (collectGitDiff and splitDiffByFile use the same source) but
171
+ // they can diverge if collectGitDiff includes paths that have no diff
172
+ // hunks (e.g. permission-only changes).
173
+ const includedSet = new Set(filesIncluded);
174
+ for (const original of opts.files) {
175
+ if (!includedSet.has(original) && matcher.ignores(original)) {
176
+ if (!filesSkipped.some((s) => s.path === original)) {
177
+ filesSkipped.push({
178
+ path: original,
179
+ reason: "ignored by .almightyignore / .gitignore / config.context.exclude",
180
+ });
181
+ }
182
+ }
183
+ }
184
+
185
+ return {
186
+ filteredDiff: includedSections.join("\n"),
187
+ filesIncluded,
188
+ filesSkipped,
189
+ };
190
+ }
@@ -45,6 +45,8 @@ export interface AgentCompletedEvent {
45
45
  model: string;
46
46
  outputPath: string;
47
47
  tokensIn: number;
48
+ /** Cache-hit portion of tokensIn (Anthropic cache_read / OpenAI cached). */
49
+ cachedTokensIn?: number;
48
50
  tokensOut: number;
49
51
  costUsd: number;
50
52
  latencyMs: number;