@haystackeditor/cli 0.13.3 → 0.13.4
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.
- package/dist/assets/skills/submit.md +17 -0
- package/dist/commands/config.d.ts +12 -0
- package/dist/commands/config.js +96 -3
- package/dist/index.js +30 -3
- package/package.json +1 -1
|
@@ -85,6 +85,23 @@ Use `--auto-fix` only for issues like:
|
|
|
85
85
|
haystack submit --auto-fix
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
+
Repos that want auto-fix on by default for every `haystack submit` can opt
|
|
89
|
+
in via `.haystack.json`:
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"preferences": {
|
|
94
|
+
"auto_fix": true
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
When set, `haystack submit` applies the `haystack:auto-fix` label without
|
|
100
|
+
needing the explicit flag. Auto-fix is still alpha — use the repo-level
|
|
101
|
+
opt-in only when the team is comfortable with the sandbox fixer running on
|
|
102
|
+
mechanical findings. `haystack submit --no-auto-fix` overrides the repo
|
|
103
|
+
default for a single submission.
|
|
104
|
+
|
|
88
105
|
### Review Mode
|
|
89
106
|
|
|
90
107
|
> **Do NOT use `--review` unless the user explicitly asks for human review.**
|
|
@@ -21,5 +21,17 @@ export declare function getAutoMergeStatus(): Promise<void>;
|
|
|
21
21
|
export declare function enableAutoMerge(): Promise<void>;
|
|
22
22
|
export declare function disableAutoMerge(): Promise<void>;
|
|
23
23
|
export declare function handleAutoMerge(action?: string): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Check if auto-fix is enabled for the current repo.
|
|
26
|
+
* Returns false if .haystack.json is missing or on error.
|
|
27
|
+
*
|
|
28
|
+
* Mirrors isAutoMergeEnabled() — direct file read so missing config returns
|
|
29
|
+
* false instead of process.exit(1).
|
|
30
|
+
*/
|
|
31
|
+
export declare function isAutoFixEnabled(): Promise<boolean>;
|
|
32
|
+
export declare function getAutoFixStatus(): Promise<void>;
|
|
33
|
+
export declare function enableAutoFix(): Promise<void>;
|
|
34
|
+
export declare function disableAutoFix(): Promise<void>;
|
|
35
|
+
export declare function handleAutoFix(action?: string): Promise<void>;
|
|
24
36
|
export declare function handleWaitForReviewers(action?: string, reviewers?: string[]): Promise<void>;
|
|
25
37
|
export {};
|
package/dist/commands/config.js
CHANGED
|
@@ -11,6 +11,10 @@ import { execSync } from 'child_process';
|
|
|
11
11
|
import { resolveAuthContext } from '../utils/auth.js';
|
|
12
12
|
import { AI_REVIEWER_SOURCES, AI_REVIEWER_DISPLAY_NAMES, AI_REVIEWER_BOT_USERNAMES } from '../types.js';
|
|
13
13
|
const API_BASE = 'https://haystackeditor.com/api/preferences';
|
|
14
|
+
const REPO_PREFERENCE_DEFAULTS = {
|
|
15
|
+
auto_merge: false,
|
|
16
|
+
auto_fix: false,
|
|
17
|
+
};
|
|
14
18
|
const AGENTIC_TOOL_LABELS = {
|
|
15
19
|
'opencode': 'OpenCode (Haystack billing)',
|
|
16
20
|
'claude-code': 'Claude Code (your Claude Max subscription)',
|
|
@@ -49,8 +53,7 @@ function saveHaystackConfig(config, configPath) {
|
|
|
49
53
|
}
|
|
50
54
|
function getPreference(key) {
|
|
51
55
|
const { config } = loadHaystackConfig();
|
|
52
|
-
|
|
53
|
-
return config.preferences?.[key] ?? defaults[key];
|
|
56
|
+
return config.preferences?.[key] ?? REPO_PREFERENCE_DEFAULTS[key];
|
|
54
57
|
}
|
|
55
58
|
function setPreference(key, value) {
|
|
56
59
|
const { config, path } = loadHaystackConfig();
|
|
@@ -165,6 +168,19 @@ export async function handleAgenticTool(tool) {
|
|
|
165
168
|
// ============================================================================
|
|
166
169
|
// Auto-merge (repo-level via .haystack.json)
|
|
167
170
|
// ============================================================================
|
|
171
|
+
/**
|
|
172
|
+
* Surface unexpected errors when reading repo-level preferences while keeping
|
|
173
|
+
* the silent fallback for the expected "no .haystack.json" case. ENOENT means
|
|
174
|
+
* the user simply hasn't opted in; everything else (parse errors, permission
|
|
175
|
+
* denied, not-a-git-repo) is a config problem the user should see — silently
|
|
176
|
+
* defaulting to disabled would otherwise hide the misconfiguration.
|
|
177
|
+
*/
|
|
178
|
+
function logPreferenceLookupFailure(preference, err) {
|
|
179
|
+
if (err?.code === 'ENOENT')
|
|
180
|
+
return;
|
|
181
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
182
|
+
console.error(chalk.dim(`[haystack] Could not resolve ${preference} preference (${message}); defaulting to disabled.`));
|
|
183
|
+
}
|
|
168
184
|
/**
|
|
169
185
|
* Check if auto-merge is enabled for the current repo.
|
|
170
186
|
* Returns false if .haystack.json is missing or on error.
|
|
@@ -180,7 +196,8 @@ export async function isAutoMergeEnabled() {
|
|
|
180
196
|
const config = JSON.parse(raw);
|
|
181
197
|
return config.preferences?.auto_merge ?? false;
|
|
182
198
|
}
|
|
183
|
-
catch {
|
|
199
|
+
catch (err) {
|
|
200
|
+
logPreferenceLookupFailure('auto_merge', err);
|
|
184
201
|
return false;
|
|
185
202
|
}
|
|
186
203
|
}
|
|
@@ -235,6 +252,82 @@ export async function handleAutoMerge(action) {
|
|
|
235
252
|
}
|
|
236
253
|
}
|
|
237
254
|
// ============================================================================
|
|
255
|
+
// Auto-fix (repo-level via .haystack.json) — alpha
|
|
256
|
+
// ============================================================================
|
|
257
|
+
/**
|
|
258
|
+
* Check if auto-fix is enabled for the current repo.
|
|
259
|
+
* Returns false if .haystack.json is missing or on error.
|
|
260
|
+
*
|
|
261
|
+
* Mirrors isAutoMergeEnabled() — direct file read so missing config returns
|
|
262
|
+
* false instead of process.exit(1).
|
|
263
|
+
*/
|
|
264
|
+
export async function isAutoFixEnabled() {
|
|
265
|
+
try {
|
|
266
|
+
const root = execSync('git rev-parse --show-toplevel', { encoding: 'utf-8' }).trim();
|
|
267
|
+
const raw = readFileSync(resolve(root, '.haystack.json'), 'utf-8');
|
|
268
|
+
const config = JSON.parse(raw);
|
|
269
|
+
return config.preferences?.auto_fix ?? false;
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
logPreferenceLookupFailure('auto_fix', err);
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
export async function getAutoFixStatus() {
|
|
277
|
+
const enabled = getPreference('auto_fix');
|
|
278
|
+
console.log(chalk.bold('\nAuto-fix (alpha):'), enabled
|
|
279
|
+
? chalk.green('enabled')
|
|
280
|
+
: chalk.yellow('disabled'));
|
|
281
|
+
console.log();
|
|
282
|
+
if (enabled) {
|
|
283
|
+
console.log(chalk.dim('PRs submitted via `haystack submit` will be labeled'));
|
|
284
|
+
console.log(chalk.dim('haystack:auto-fix. Analysis will dispatch the alpha'));
|
|
285
|
+
console.log(chalk.dim('sandbox fixer for straightforward mechanical issues.'));
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
console.log(chalk.dim('Auto-fix is disabled. Issues surface in the Feed for review.'));
|
|
289
|
+
console.log(chalk.dim('Enable with: haystack config auto-fix on'));
|
|
290
|
+
}
|
|
291
|
+
console.log(chalk.yellow('⚠ Auto-fix is alpha. Expect rough edges.'));
|
|
292
|
+
console.log(chalk.dim('(Stored in .haystack.json — applies to all repo contributors)'));
|
|
293
|
+
console.log();
|
|
294
|
+
}
|
|
295
|
+
export async function enableAutoFix() {
|
|
296
|
+
setPreference('auto_fix', true);
|
|
297
|
+
console.log(chalk.green('\nAuto-fix (alpha) enabled.\n'));
|
|
298
|
+
console.log(chalk.dim('PRs submitted via `haystack submit` will be labeled'));
|
|
299
|
+
console.log(chalk.dim('haystack:auto-fix. The sandbox fixer will attempt'));
|
|
300
|
+
console.log(chalk.dim('straightforward mechanical fixes before issues hit the Feed.'));
|
|
301
|
+
console.log(chalk.yellow('⚠ Auto-fix is alpha. Expect rough edges.'));
|
|
302
|
+
console.log(chalk.dim('Commit .haystack.json to share with your team.\n'));
|
|
303
|
+
}
|
|
304
|
+
export async function disableAutoFix() {
|
|
305
|
+
setPreference('auto_fix', false);
|
|
306
|
+
console.log(chalk.yellow('\nAuto-fix disabled.\n'));
|
|
307
|
+
console.log(chalk.dim('Issues will surface in the Feed for human review.'));
|
|
308
|
+
console.log(chalk.dim('Re-enable with: haystack config auto-fix on'));
|
|
309
|
+
console.log(chalk.dim('Commit .haystack.json to share with your team.\n'));
|
|
310
|
+
}
|
|
311
|
+
export async function handleAutoFix(action) {
|
|
312
|
+
switch (action?.toLowerCase()) {
|
|
313
|
+
case 'on':
|
|
314
|
+
case 'enable':
|
|
315
|
+
case 'true':
|
|
316
|
+
return enableAutoFix();
|
|
317
|
+
case 'off':
|
|
318
|
+
case 'disable':
|
|
319
|
+
case 'false':
|
|
320
|
+
return disableAutoFix();
|
|
321
|
+
case 'status':
|
|
322
|
+
case undefined:
|
|
323
|
+
return getAutoFixStatus();
|
|
324
|
+
default:
|
|
325
|
+
console.error(chalk.red(`\nUnknown action: ${action}`));
|
|
326
|
+
console.log('\nUsage: haystack config auto-fix [on|off|status]\n');
|
|
327
|
+
process.exit(1);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// ============================================================================
|
|
238
331
|
// Wait-for-reviewers (repo-level via .haystack.json)
|
|
239
332
|
// ============================================================================
|
|
240
333
|
/** Reverse map: bot username → friendly source name */
|
package/dist/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import { Command } from 'commander';
|
|
|
23
23
|
import { statusCommand } from './commands/status.js';
|
|
24
24
|
import { initCommand } from './commands/init.js';
|
|
25
25
|
import { authListCommand, authUseCommand, loginCommand, logoutCommand, whoamiCommand, } from './commands/login.js';
|
|
26
|
-
import { handleAgenticTool, handleAutoMerge, handleWaitForReviewers, isAutoMergeEnabled } from './commands/config.js';
|
|
26
|
+
import { handleAgenticTool, handleAutoMerge, handleAutoFix, handleWaitForReviewers, isAutoMergeEnabled, isAutoFixEnabled } from './commands/config.js';
|
|
27
27
|
import { installSkills, listSkills } from './commands/skills.js';
|
|
28
28
|
import { hooksInstall, hooksStatus, hooksUpdate } from './commands/hooks.js';
|
|
29
29
|
import { submitCommand } from './commands/submit.js';
|
|
@@ -38,7 +38,7 @@ const program = new Command();
|
|
|
38
38
|
program
|
|
39
39
|
.name('haystack')
|
|
40
40
|
.description('Haystack CLI — automated PR review, triage, and merge queue')
|
|
41
|
-
.version('0.13.
|
|
41
|
+
.version('0.13.4');
|
|
42
42
|
program
|
|
43
43
|
.command('init')
|
|
44
44
|
.description('Create .haystack.json configuration')
|
|
@@ -110,7 +110,7 @@ program
|
|
|
110
110
|
.option('--draft', 'Create as draft PR')
|
|
111
111
|
.option('--review [reviewer]', 'Request human review (BLOCKS auto-merge — only use when explicitly asked)')
|
|
112
112
|
.option('--force', 'Skip pre-PR triage checks')
|
|
113
|
-
.option('--auto-fix', 'Alpha auto-fix for straightforward mechanical issues (
|
|
113
|
+
.option('--auto-fix', 'Alpha auto-fix for straightforward mechanical issues (default: from .haystack.json preferences.auto_fix; --no-auto-fix to disable)')
|
|
114
114
|
.option('--auto-merge', 'Apply auto-merge label (default: from .haystack.json, --no-auto-merge to disable)')
|
|
115
115
|
.option('--no-wait', 'Skip waiting for analysis results')
|
|
116
116
|
.option('--max-turns <n>', 'Max agentic turns per triage checker (overrides .haystack.json triage.maxTurns)', (v) => parseInt(v, 10))
|
|
@@ -189,6 +189,12 @@ Examples:
|
|
|
189
189
|
if (options.autoMerge === undefined) {
|
|
190
190
|
options.autoMerge = await isAutoMergeEnabled();
|
|
191
191
|
}
|
|
192
|
+
// Resolve --auto-fix default from .haystack.json (preferences.auto_fix).
|
|
193
|
+
// Auto-fix remains alpha — the explicit --auto-fix flag still surfaces
|
|
194
|
+
// discouragement warnings; opting in via repo config is the supported path.
|
|
195
|
+
if (options.autoFix === undefined) {
|
|
196
|
+
options.autoFix = await isAutoFixEnabled();
|
|
197
|
+
}
|
|
192
198
|
return submitCommand(options);
|
|
193
199
|
});
|
|
194
200
|
program
|
|
@@ -373,6 +379,27 @@ Examples:
|
|
|
373
379
|
haystack config auto-merge off # Disable auto-merge
|
|
374
380
|
`)
|
|
375
381
|
.action(handleAutoMerge);
|
|
382
|
+
config
|
|
383
|
+
.command('auto-fix [action]')
|
|
384
|
+
.description('(Alpha) Auto-fix mechanical issues on PRs submitted via haystack submit (on|off|status)')
|
|
385
|
+
.addHelpText('after', `
|
|
386
|
+
Actions:
|
|
387
|
+
on, enable Enable auto-fix for mechanical issues
|
|
388
|
+
off, disable Disable auto-fix
|
|
389
|
+
status Show current status (default)
|
|
390
|
+
|
|
391
|
+
⚠ Auto-fix is alpha. When enabled, PRs submitted via \`haystack submit\`
|
|
392
|
+
are labeled haystack:auto-fix. The Haystack analysis pipeline then
|
|
393
|
+
dispatches the sandbox fixer for issues classified as straightforward
|
|
394
|
+
and mechanical. Judgment calls, policy violations, and weak coverage
|
|
395
|
+
findings still surface in the Feed for human review.
|
|
396
|
+
|
|
397
|
+
Examples:
|
|
398
|
+
haystack config auto-fix # Show current status
|
|
399
|
+
haystack config auto-fix on # Enable auto-fix (alpha)
|
|
400
|
+
haystack config auto-fix off # Disable auto-fix
|
|
401
|
+
`)
|
|
402
|
+
.action(handleAutoFix);
|
|
376
403
|
config
|
|
377
404
|
.command('wait-for-reviewers [action] [reviewers...]')
|
|
378
405
|
.description('Configure which AI reviewers the merge queue waits for')
|