@haystackeditor/cli 0.10.3 → 0.11.0

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.
@@ -45,7 +45,7 @@ const INTENT_DRIFT_SCHEMA = `{
45
45
  "line": 42,
46
46
  "severity": "error | warning | info",
47
47
  "message": "Description of the drift or incomplete fulfillment",
48
- "pattern": "intent_drift | incomplete_fulfillment"
48
+ "pattern": "intent_drift | incomplete_fulfillment | scope_creep"
49
49
  }
50
50
  ],
51
51
  "summary": "Brief 1-sentence summary",
@@ -261,12 +261,20 @@ The agent didn't finish everything that was asked:
261
261
  - Agent said "I'll skip X for now" for something the user explicitly requested
262
262
  - Tests not written when user asked for tests
263
263
 
264
+ ### Scope Creep
265
+ The agent added functionality the user never asked for:
266
+ - User asks to fix a bug → agent also adds a cooldown, retry logic, or caching layer
267
+ - User asks to investigate → agent proactively "fixes" things beyond what was discussed
268
+ - User asks for one feature → agent bundles in extra features "while we're at it"
269
+ - Any new mechanism not traceable to a user instruction
270
+
264
271
  ## Red flags to search for in the diff
265
272
 
266
273
  - Fixed/hardcoded values where dynamic behavior was requested
267
274
  - TODO, FIXME, placeholder, stub comments in new code
268
275
  - Empty function bodies or early returns
269
276
  - Interface fields that are declared but never assigned anywhere
277
+ - New mechanisms (cooldowns, retries, caches, rate limits) not requested by the user
270
278
 
271
279
  ## Output
272
280
 
@@ -277,15 +285,15 @@ ${INTENT_DRIFT_SCHEMA}
277
285
  \`\`\`
278
286
 
279
287
  - Set \`sessionsChecked\` to the number of trace files you read
280
- - Set \`passed\` to \`true\` if no drift or incomplete fulfillment found
288
+ - Set \`passed\` to \`true\` if no drift, incomplete fulfillment, or scope creep found
281
289
  - Set \`passed\` to \`false\` if any issues with severity "error" were found
282
- - For each issue, set \`pattern\` to either "intent_drift" or "incomplete_fulfillment"
290
+ - For each issue, set \`pattern\` to "intent_drift", "incomplete_fulfillment", or "scope_creep"
283
291
  - You MUST write the result file even if no issues are found
284
292
 
285
293
  ## Severity guidelines
286
294
 
287
- - **error**: Core user intent violated — the main thing they asked for is wrong or missing
288
- - **warning**: Secondary requirement missed or approximated
295
+ - **error**: Core user intent violated — the main thing they asked for is wrong or missing, or large unrequested feature added
296
+ - **warning**: Secondary requirement missed or approximated, or small unrequested mechanism added
289
297
  - **info**: Minor simplification that mostly still works as intended${precomputedDiff ? `
290
298
 
291
299
  ## Diff (precomputed)
@@ -17,7 +17,7 @@ const TRIAGE_DIR = '.haystack/triage';
17
17
  const TIMEOUT_MS = 300_000; // 5 minutes per checker
18
18
  /** Max agentic turns per checker — kept tight since these are read-only tasks */
19
19
  const MAX_TURNS = {
20
- 'code-review': 10,
20
+ 'code-review': 5,
21
21
  'rules-validator': 10,
22
22
  'intent-drift': 10,
23
23
  };
@@ -13,7 +13,7 @@ export interface TriageIssue {
13
13
  /** For rules validator: rule ID (e.g., PR001) */
14
14
  rule?: string;
15
15
  /** For intent drift: drift pattern */
16
- pattern?: 'intent_drift' | 'incomplete_fulfillment';
16
+ pattern?: 'intent_drift' | 'incomplete_fulfillment' | 'scope_creep';
17
17
  }
18
18
  export interface CodeReviewResult {
19
19
  checker: 'code-review';
package/dist/types.d.ts CHANGED
@@ -56,7 +56,9 @@ declare const DevServerSchema: z.ZodObject<{
56
56
  /** Valid AI reviewer source identifiers that the merge queue can wait for */
57
57
  export declare const AI_REVIEWER_SOURCES: readonly ["cursor", "coderabbit", "copilot", "greptile", "sourcery", "qodo", "codeant", "ellipsis", "korbit", "panto", "bito", "graphite", "deepsource", "snyk", "codacy", "sonarcloud", "sweep", "codeguru", "codereviewer", "codium"];
58
58
  export declare const AI_REVIEWER_DISPLAY_NAMES: Record<typeof AI_REVIEWER_SOURCES[number], string>;
59
- declare const BabysitterConfigSchema: z.ZodObject<{
59
+ /** Maps friendly source names to exact GitHub bot login usernames. */
60
+ export declare const AI_REVIEWER_BOT_USERNAMES: Record<typeof AI_REVIEWER_SOURCES[number], string>;
61
+ declare const MergeQueueConfigSchema: z.ZodObject<{
60
62
  /** Enable the Haystack auto-merge queue */
61
63
  merge_queue: z.ZodOptional<z.ZodBoolean>;
62
64
  /** Automatically rebase PRs that develop merge conflicts after pushes to the base branch */
@@ -74,8 +76,36 @@ declare const BabysitterConfigSchema: z.ZodObject<{
74
76
  default_minutes?: number | undefined;
75
77
  authors?: Record<string, number> | undefined;
76
78
  }>>;
77
- /** AI reviewer sources to wait for before starting the merge grace period */
78
- expected_ai_reviewers: z.ZodOptional<z.ZodArray<z.ZodEnum<["cursor", "coderabbit", "copilot", "greptile", "sourcery", "qodo", "codeant", "ellipsis", "korbit", "panto", "bito", "graphite", "deepsource", "snyk", "codacy", "sonarcloud", "sweep", "codeguru", "codereviewer", "codium"]>, "many">>;
79
+ /** Batch multiple PRs into one CI run */
80
+ batch_merge: z.ZodOptional<z.ZodObject<{
81
+ /** Enable batch merging */
82
+ enabled: z.ZodOptional<z.ZodBoolean>;
83
+ /** Minimum PRs to form a batch */
84
+ min_batch_size: z.ZodOptional<z.ZodNumber>;
85
+ /** Maximum PRs per batch (cap: 20) */
86
+ max_batch_size: z.ZodOptional<z.ZodNumber>;
87
+ }, "strip", z.ZodTypeAny, {
88
+ enabled?: boolean | undefined;
89
+ min_batch_size?: number | undefined;
90
+ max_batch_size?: number | undefined;
91
+ }, {
92
+ enabled?: boolean | undefined;
93
+ min_batch_size?: number | undefined;
94
+ max_batch_size?: number | undefined;
95
+ }>>;
96
+ /** Wait for external AI code reviewers before merging — all bots must post */
97
+ wait_for_reviewer: z.ZodOptional<z.ZodObject<{
98
+ /** GitHub bot usernames to wait for (e.g. "cursor-bugbot[bot]") */
99
+ bots: z.ZodArray<z.ZodString, "many">;
100
+ /** Give up waiting after this many minutes (default: 30) */
101
+ timeout_minutes: z.ZodOptional<z.ZodNumber>;
102
+ }, "strip", z.ZodTypeAny, {
103
+ bots: string[];
104
+ timeout_minutes?: number | undefined;
105
+ }, {
106
+ bots: string[];
107
+ timeout_minutes?: number | undefined;
108
+ }>>;
79
109
  }, "strip", z.ZodTypeAny, {
80
110
  merge_queue?: boolean | undefined;
81
111
  auto_resolve_conflicts?: boolean | undefined;
@@ -83,7 +113,15 @@ declare const BabysitterConfigSchema: z.ZodObject<{
83
113
  default_minutes?: number | undefined;
84
114
  authors?: Record<string, number> | undefined;
85
115
  } | undefined;
86
- expected_ai_reviewers?: ("cursor" | "coderabbit" | "copilot" | "greptile" | "sourcery" | "qodo" | "codeant" | "ellipsis" | "korbit" | "panto" | "bito" | "graphite" | "deepsource" | "snyk" | "codacy" | "sonarcloud" | "sweep" | "codeguru" | "codereviewer" | "codium")[] | undefined;
116
+ batch_merge?: {
117
+ enabled?: boolean | undefined;
118
+ min_batch_size?: number | undefined;
119
+ max_batch_size?: number | undefined;
120
+ } | undefined;
121
+ wait_for_reviewer?: {
122
+ bots: string[];
123
+ timeout_minutes?: number | undefined;
124
+ } | undefined;
87
125
  }, {
88
126
  merge_queue?: boolean | undefined;
89
127
  auto_resolve_conflicts?: boolean | undefined;
@@ -91,7 +129,15 @@ declare const BabysitterConfigSchema: z.ZodObject<{
91
129
  default_minutes?: number | undefined;
92
130
  authors?: Record<string, number> | undefined;
93
131
  } | undefined;
94
- expected_ai_reviewers?: ("cursor" | "coderabbit" | "copilot" | "greptile" | "sourcery" | "qodo" | "codeant" | "ellipsis" | "korbit" | "panto" | "bito" | "graphite" | "deepsource" | "snyk" | "codacy" | "sonarcloud" | "sweep" | "codeguru" | "codereviewer" | "codium")[] | undefined;
132
+ batch_merge?: {
133
+ enabled?: boolean | undefined;
134
+ min_batch_size?: number | undefined;
135
+ max_batch_size?: number | undefined;
136
+ } | undefined;
137
+ wait_for_reviewer?: {
138
+ bots: string[];
139
+ timeout_minutes?: number | undefined;
140
+ } | undefined;
95
141
  }>;
96
142
  declare const ServiceSchema: z.ZodObject<{
97
143
  /** Root directory relative to repo root */
@@ -1109,8 +1155,8 @@ export declare const HaystackConfigSchema: z.ZodObject<{
1109
1155
  services?: string[] | undefined;
1110
1156
  description?: string | undefined;
1111
1157
  }>>>;
1112
- /** Babysitter configuration for autonomous PR maintenance */
1113
- babysitter: z.ZodOptional<z.ZodObject<{
1158
+ /** Merge queue configuration for autonomous PR maintenance */
1159
+ merge_queue: z.ZodOptional<z.ZodObject<{
1114
1160
  /** Enable the Haystack auto-merge queue */
1115
1161
  merge_queue: z.ZodOptional<z.ZodBoolean>;
1116
1162
  /** Automatically rebase PRs that develop merge conflicts after pushes to the base branch */
@@ -1128,8 +1174,36 @@ export declare const HaystackConfigSchema: z.ZodObject<{
1128
1174
  default_minutes?: number | undefined;
1129
1175
  authors?: Record<string, number> | undefined;
1130
1176
  }>>;
1131
- /** AI reviewer sources to wait for before starting the merge grace period */
1132
- expected_ai_reviewers: z.ZodOptional<z.ZodArray<z.ZodEnum<["cursor", "coderabbit", "copilot", "greptile", "sourcery", "qodo", "codeant", "ellipsis", "korbit", "panto", "bito", "graphite", "deepsource", "snyk", "codacy", "sonarcloud", "sweep", "codeguru", "codereviewer", "codium"]>, "many">>;
1177
+ /** Batch multiple PRs into one CI run */
1178
+ batch_merge: z.ZodOptional<z.ZodObject<{
1179
+ /** Enable batch merging */
1180
+ enabled: z.ZodOptional<z.ZodBoolean>;
1181
+ /** Minimum PRs to form a batch */
1182
+ min_batch_size: z.ZodOptional<z.ZodNumber>;
1183
+ /** Maximum PRs per batch (cap: 20) */
1184
+ max_batch_size: z.ZodOptional<z.ZodNumber>;
1185
+ }, "strip", z.ZodTypeAny, {
1186
+ enabled?: boolean | undefined;
1187
+ min_batch_size?: number | undefined;
1188
+ max_batch_size?: number | undefined;
1189
+ }, {
1190
+ enabled?: boolean | undefined;
1191
+ min_batch_size?: number | undefined;
1192
+ max_batch_size?: number | undefined;
1193
+ }>>;
1194
+ /** Wait for external AI code reviewers before merging — all bots must post */
1195
+ wait_for_reviewer: z.ZodOptional<z.ZodObject<{
1196
+ /** GitHub bot usernames to wait for (e.g. "cursor-bugbot[bot]") */
1197
+ bots: z.ZodArray<z.ZodString, "many">;
1198
+ /** Give up waiting after this many minutes (default: 30) */
1199
+ timeout_minutes: z.ZodOptional<z.ZodNumber>;
1200
+ }, "strip", z.ZodTypeAny, {
1201
+ bots: string[];
1202
+ timeout_minutes?: number | undefined;
1203
+ }, {
1204
+ bots: string[];
1205
+ timeout_minutes?: number | undefined;
1206
+ }>>;
1133
1207
  }, "strip", z.ZodTypeAny, {
1134
1208
  merge_queue?: boolean | undefined;
1135
1209
  auto_resolve_conflicts?: boolean | undefined;
@@ -1137,7 +1211,15 @@ export declare const HaystackConfigSchema: z.ZodObject<{
1137
1211
  default_minutes?: number | undefined;
1138
1212
  authors?: Record<string, number> | undefined;
1139
1213
  } | undefined;
1140
- expected_ai_reviewers?: ("cursor" | "coderabbit" | "copilot" | "greptile" | "sourcery" | "qodo" | "codeant" | "ellipsis" | "korbit" | "panto" | "bito" | "graphite" | "deepsource" | "snyk" | "codacy" | "sonarcloud" | "sweep" | "codeguru" | "codereviewer" | "codium")[] | undefined;
1214
+ batch_merge?: {
1215
+ enabled?: boolean | undefined;
1216
+ min_batch_size?: number | undefined;
1217
+ max_batch_size?: number | undefined;
1218
+ } | undefined;
1219
+ wait_for_reviewer?: {
1220
+ bots: string[];
1221
+ timeout_minutes?: number | undefined;
1222
+ } | undefined;
1141
1223
  }, {
1142
1224
  merge_queue?: boolean | undefined;
1143
1225
  auto_resolve_conflicts?: boolean | undefined;
@@ -1145,11 +1227,36 @@ export declare const HaystackConfigSchema: z.ZodObject<{
1145
1227
  default_minutes?: number | undefined;
1146
1228
  authors?: Record<string, number> | undefined;
1147
1229
  } | undefined;
1148
- expected_ai_reviewers?: ("cursor" | "coderabbit" | "copilot" | "greptile" | "sourcery" | "qodo" | "codeant" | "ellipsis" | "korbit" | "panto" | "bito" | "graphite" | "deepsource" | "snyk" | "codacy" | "sonarcloud" | "sweep" | "codeguru" | "codereviewer" | "codium")[] | undefined;
1230
+ batch_merge?: {
1231
+ enabled?: boolean | undefined;
1232
+ min_batch_size?: number | undefined;
1233
+ max_batch_size?: number | undefined;
1234
+ } | undefined;
1235
+ wait_for_reviewer?: {
1236
+ bots: string[];
1237
+ timeout_minutes?: number | undefined;
1238
+ } | undefined;
1149
1239
  }>>;
1150
1240
  }, "strip", z.ZodTypeAny, {
1151
1241
  version: "1";
1152
1242
  name?: string | undefined;
1243
+ merge_queue?: {
1244
+ merge_queue?: boolean | undefined;
1245
+ auto_resolve_conflicts?: boolean | undefined;
1246
+ merge_debounce?: {
1247
+ default_minutes?: number | undefined;
1248
+ authors?: Record<string, number> | undefined;
1249
+ } | undefined;
1250
+ batch_merge?: {
1251
+ enabled?: boolean | undefined;
1252
+ min_batch_size?: number | undefined;
1253
+ max_batch_size?: number | undefined;
1254
+ } | undefined;
1255
+ wait_for_reviewer?: {
1256
+ bots: string[];
1257
+ timeout_minutes?: number | undefined;
1258
+ } | undefined;
1259
+ } | undefined;
1153
1260
  services?: Record<string, {
1154
1261
  command: string;
1155
1262
  type?: "server" | "batch" | undefined;
@@ -1251,18 +1358,26 @@ export declare const HaystackConfigSchema: z.ZodObject<{
1251
1358
  services?: string[] | undefined;
1252
1359
  description?: string | undefined;
1253
1360
  }> | undefined;
1254
- babysitter?: {
1361
+ }, {
1362
+ version: "1";
1363
+ name?: string | undefined;
1364
+ merge_queue?: {
1255
1365
  merge_queue?: boolean | undefined;
1256
1366
  auto_resolve_conflicts?: boolean | undefined;
1257
1367
  merge_debounce?: {
1258
1368
  default_minutes?: number | undefined;
1259
1369
  authors?: Record<string, number> | undefined;
1260
1370
  } | undefined;
1261
- expected_ai_reviewers?: ("cursor" | "coderabbit" | "copilot" | "greptile" | "sourcery" | "qodo" | "codeant" | "ellipsis" | "korbit" | "panto" | "bito" | "graphite" | "deepsource" | "snyk" | "codacy" | "sonarcloud" | "sweep" | "codeguru" | "codereviewer" | "codium")[] | undefined;
1371
+ batch_merge?: {
1372
+ enabled?: boolean | undefined;
1373
+ min_batch_size?: number | undefined;
1374
+ max_batch_size?: number | undefined;
1375
+ } | undefined;
1376
+ wait_for_reviewer?: {
1377
+ bots: string[];
1378
+ timeout_minutes?: number | undefined;
1379
+ } | undefined;
1262
1380
  } | undefined;
1263
- }, {
1264
- version: "1";
1265
- name?: string | undefined;
1266
1381
  services?: Record<string, {
1267
1382
  command: string;
1268
1383
  type?: "server" | "batch" | undefined;
@@ -1364,15 +1479,6 @@ export declare const HaystackConfigSchema: z.ZodObject<{
1364
1479
  services?: string[] | undefined;
1365
1480
  description?: string | undefined;
1366
1481
  }> | undefined;
1367
- babysitter?: {
1368
- merge_queue?: boolean | undefined;
1369
- auto_resolve_conflicts?: boolean | undefined;
1370
- merge_debounce?: {
1371
- default_minutes?: number | undefined;
1372
- authors?: Record<string, number> | undefined;
1373
- } | undefined;
1374
- expected_ai_reviewers?: ("cursor" | "coderabbit" | "copilot" | "greptile" | "sourcery" | "qodo" | "codeant" | "ellipsis" | "korbit" | "panto" | "bito" | "graphite" | "deepsource" | "snyk" | "codacy" | "sonarcloud" | "sweep" | "codeguru" | "codereviewer" | "codium")[] | undefined;
1375
- } | undefined;
1376
1482
  }>;
1377
1483
  export type HaystackConfig = z.infer<typeof HaystackConfigSchema>;
1378
1484
  export type VerificationCommand = z.infer<typeof VerificationCommandSchema>;
@@ -1392,7 +1498,7 @@ export type DockerService = z.infer<typeof DockerServiceSchema>;
1392
1498
  export type DatabaseConfig = z.infer<typeof DatabaseConfigSchema>;
1393
1499
  export type NetworkConfig = z.infer<typeof NetworkConfigSchema>;
1394
1500
  export type SecretDeclaration = z.infer<typeof SecretDeclarationSchema>;
1395
- export type BabysitterConfig = z.infer<typeof BabysitterConfigSchema>;
1501
+ export type MergeQueueConfig = z.infer<typeof MergeQueueConfigSchema>;
1396
1502
  /**
1397
1503
  * Project detection results
1398
1504
  */
package/dist/types.js CHANGED
@@ -94,8 +94,31 @@ export const AI_REVIEWER_DISPLAY_NAMES = {
94
94
  codereviewer: 'CodeReviewBot',
95
95
  codium: 'Codium',
96
96
  };
97
+ /** Maps friendly source names to exact GitHub bot login usernames. */
98
+ export const AI_REVIEWER_BOT_USERNAMES = {
99
+ cursor: 'cursor-bugbot[bot]',
100
+ coderabbit: 'coderabbitai[bot]',
101
+ copilot: 'copilot[bot]',
102
+ greptile: 'greptile[bot]',
103
+ sourcery: 'sourcery-ai[bot]',
104
+ qodo: 'qodo-merge-pro[bot]',
105
+ codeant: 'codeant-ai[bot]',
106
+ ellipsis: 'ellipsis-dev[bot]',
107
+ korbit: 'korbit-ai[bot]',
108
+ panto: 'pantoaibot[bot]',
109
+ bito: 'bito[bot]',
110
+ graphite: 'graphite-app[bot]',
111
+ deepsource: 'deepsource-io[bot]',
112
+ snyk: 'snyk[bot]',
113
+ codacy: 'codacy-production[bot]',
114
+ sonarcloud: 'sonarcloud[bot]',
115
+ sweep: 'sweep-ai[bot]',
116
+ codeguru: 'aws-codeguru-reviewer[bot]',
117
+ codereviewer: 'codereviewbot[bot]',
118
+ codium: 'codiumai[bot]',
119
+ };
97
120
  const AIReviewerSourceSchema = z.enum(AI_REVIEWER_SOURCES);
98
- const BabysitterConfigSchema = z.object({
121
+ const MergeQueueConfigSchema = z.object({
99
122
  /** Enable the Haystack auto-merge queue */
100
123
  merge_queue: z.boolean().optional(),
101
124
  /** Automatically rebase PRs that develop merge conflicts after pushes to the base branch */
@@ -107,8 +130,22 @@ const BabysitterConfigSchema = z.object({
107
130
  /** Per-author wait time overrides (author login → minutes) */
108
131
  authors: z.record(z.string(), z.number()).optional(),
109
132
  }).optional(),
110
- /** AI reviewer sources to wait for before starting the merge grace period */
111
- expected_ai_reviewers: z.array(AIReviewerSourceSchema).optional(),
133
+ /** Batch multiple PRs into one CI run */
134
+ batch_merge: z.object({
135
+ /** Enable batch merging */
136
+ enabled: z.boolean().optional(),
137
+ /** Minimum PRs to form a batch */
138
+ min_batch_size: z.number().optional(),
139
+ /** Maximum PRs per batch (cap: 20) */
140
+ max_batch_size: z.number().optional(),
141
+ }).optional(),
142
+ /** Wait for external AI code reviewers before merging — all bots must post */
143
+ wait_for_reviewer: z.object({
144
+ /** GitHub bot usernames to wait for (e.g. "cursor-bugbot[bot]") */
145
+ bots: z.array(z.string()),
146
+ /** Give up waiting after this many minutes (default: 30) */
147
+ timeout_minutes: z.number().optional(),
148
+ }).optional(),
112
149
  });
113
150
  // =============================================================================
114
151
  // SERVICE (for monorepos)
@@ -253,6 +290,6 @@ export const HaystackConfigSchema = z.object({
253
290
  network: NetworkConfigSchema.optional(),
254
291
  /** Secrets required by this project */
255
292
  secrets: z.record(z.string(), SecretDeclarationSchema).optional(),
256
- /** Babysitter configuration for autonomous PR maintenance */
257
- babysitter: BabysitterConfigSchema.optional(),
293
+ /** Merge queue configuration for autonomous PR maintenance */
294
+ merge_queue: MergeQueueConfigSchema.optional(),
258
295
  });
@@ -105,4 +105,20 @@ export declare function fetchAnalysisResults(owner: string, repo: string, prNumb
105
105
  * This is the rich analysis data shown on the Haystack feed — includes
106
106
  * verdict, rating, structured findings, verified bugs, and agent fix prompts.
107
107
  */
108
+ export interface AutoFixManifest {
109
+ jobId: string;
110
+ repo: string;
111
+ prNumber: number;
112
+ fixedItems: Array<{
113
+ category: string;
114
+ summary: string;
115
+ }>;
116
+ leftForReview: Array<{
117
+ category: string;
118
+ summary: string;
119
+ reason: string;
120
+ }>;
121
+ timestamp: string;
122
+ }
123
+ export declare function fetchAutoFixManifest(owner: string, repo: string, prNumber: number, token?: string): Promise<AutoFixManifest | null>;
108
124
  export declare function fetchRatingSynthesis(owner: string, repo: string, prNumber: number, token?: string): Promise<RatingSynthesis | null>;
@@ -160,11 +160,29 @@ export async function fetchAnalysisResults(owner, repo, prNumber, token) {
160
160
  autoMerged,
161
161
  };
162
162
  }
163
- /**
164
- * Fetch the full rating synthesis for a PR.
165
- * This is the rich analysis data shown on the Haystack feed — includes
166
- * verdict, rating, structured findings, verified bugs, and agent fix prompts.
167
- */
163
+ export async function fetchAutoFixManifest(owner, repo, prNumber, token) {
164
+ const url = `${PROD_API_BASE}/v3/result/${owner}/${repo}/${prNumber}/auto-fix-manifest.json`;
165
+ try {
166
+ const response = await fetch(url, {
167
+ headers: apiHeaders(token),
168
+ });
169
+ if (!response.ok)
170
+ return null;
171
+ const data = await response.json();
172
+ if (data.download_url) {
173
+ const s3Resp = await fetch(data.download_url, {
174
+ headers: { 'User-Agent': 'Haystack-CLI' },
175
+ });
176
+ if (!s3Resp.ok)
177
+ return null;
178
+ return await s3Resp.json();
179
+ }
180
+ return data;
181
+ }
182
+ catch {
183
+ return null;
184
+ }
185
+ }
168
186
  export async function fetchRatingSynthesis(owner, repo, prNumber, token) {
169
187
  // Use v3/result endpoint (same as the Lambda serves generic files)
170
188
  const url = `${PROD_API_BASE}/v3/result/${owner}/${repo}/${prNumber}/rating_synthesis.json`;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Lightweight CLI telemetry — fire-and-forget error tracking via PostHog HTTP API.
3
+ *
4
+ * Events go to the same PostHog project as the web app, enabling unified
5
+ * alerting across frontend, CLI, and backend.
6
+ *
7
+ * All calls are non-blocking and never throw — telemetry failures must not
8
+ * break CLI commands.
9
+ */
10
+ /**
11
+ * Track an error event. Fire-and-forget — never throws.
12
+ */
13
+ export declare function trackError(errorType: string, metadata?: Record<string, unknown>): void;
14
+ /**
15
+ * Track a setup event (non-error). Fire-and-forget.
16
+ */
17
+ export declare function trackSetupEvent(step: string, metadata?: Record<string, unknown>): void;
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Lightweight CLI telemetry — fire-and-forget error tracking via PostHog HTTP API.
3
+ *
4
+ * Events go to the same PostHog project as the web app, enabling unified
5
+ * alerting across frontend, CLI, and backend.
6
+ *
7
+ * All calls are non-blocking and never throw — telemetry failures must not
8
+ * break CLI commands.
9
+ */
10
+ import { readFileSync } from 'node:fs';
11
+ import { dirname, resolve } from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+ import * as os from 'os';
14
+ const POSTHOG_HOST = 'https://us.i.posthog.com';
15
+ const POSTHOG_API_KEY = process.env.HAYSTACK_POSTHOG_KEY || 'phc_7wHrCoMlcwuftLe6N38hPb9wKHb4OK0wNw9ayeRySad';
16
+ const CURRENT_FILE_DIR = dirname(fileURLToPath(import.meta.url));
17
+ const PACKAGE_JSON_PATH = resolve(CURRENT_FILE_DIR, '../../package.json');
18
+ /**
19
+ * Stable machine identifier for PostHog distinct_id.
20
+ * Uses hostname + username so events are attributable per-machine.
21
+ * Never throws — falls back to hostname-only or 'anonymous'.
22
+ */
23
+ function getDistinctId() {
24
+ try {
25
+ const hostname = os.hostname();
26
+ try {
27
+ return `cli:${os.userInfo().username}@${hostname}`;
28
+ }
29
+ catch {
30
+ return `cli:${hostname}`;
31
+ }
32
+ }
33
+ catch {
34
+ return 'cli:anonymous';
35
+ }
36
+ }
37
+ /**
38
+ * Track an error event. Fire-and-forget — never throws.
39
+ */
40
+ export function trackError(errorType, metadata) {
41
+ try {
42
+ if (typeof globalThis.fetch !== 'function')
43
+ return;
44
+ const event = {
45
+ api_key: POSTHOG_API_KEY,
46
+ distinct_id: getDistinctId(),
47
+ event: 'cli_error',
48
+ properties: {
49
+ error_type: errorType,
50
+ cli_version: getCLIVersion(),
51
+ os: process.platform,
52
+ arch: process.arch,
53
+ node_version: process.version,
54
+ timestamp: new Date().toISOString(),
55
+ ...metadata,
56
+ },
57
+ timestamp: new Date().toISOString(),
58
+ };
59
+ // Fire-and-forget — never block the CLI
60
+ globalThis.fetch(`${POSTHOG_HOST}/capture/`, {
61
+ method: 'POST',
62
+ headers: { 'Content-Type': 'application/json' },
63
+ body: JSON.stringify(event),
64
+ }).catch(() => { });
65
+ }
66
+ catch {
67
+ // Telemetry must never crash the CLI
68
+ }
69
+ }
70
+ /**
71
+ * Track a setup event (non-error). Fire-and-forget.
72
+ */
73
+ export function trackSetupEvent(step, metadata) {
74
+ try {
75
+ if (typeof globalThis.fetch !== 'function')
76
+ return;
77
+ const event = {
78
+ api_key: POSTHOG_API_KEY,
79
+ distinct_id: getDistinctId(),
80
+ event: 'cli_setup',
81
+ properties: {
82
+ step,
83
+ cli_version: getCLIVersion(),
84
+ os: process.platform,
85
+ arch: process.arch,
86
+ timestamp: new Date().toISOString(),
87
+ ...metadata,
88
+ },
89
+ timestamp: new Date().toISOString(),
90
+ };
91
+ globalThis.fetch(`${POSTHOG_HOST}/capture/`, {
92
+ method: 'POST',
93
+ headers: { 'Content-Type': 'application/json' },
94
+ body: JSON.stringify(event),
95
+ }).catch(() => { });
96
+ }
97
+ catch {
98
+ // Telemetry must never crash the CLI
99
+ }
100
+ }
101
+ function getCLIVersion() {
102
+ try {
103
+ const packageJson = JSON.parse(readFileSync(PACKAGE_JSON_PATH, 'utf8'));
104
+ return typeof packageJson.version === 'string' ? packageJson.version : 'unknown';
105
+ }
106
+ catch {
107
+ return 'unknown';
108
+ }
109
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haystackeditor/cli",
3
- "version": "0.10.3",
3
+ "version": "0.11.0",
4
4
  "description": "Set up Haystack for your project — automated PR review, triage, and merge queue",
5
5
  "type": "module",
6
6
  "bin": {