@haystackeditor/cli 0.9.0 → 0.10.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.
- package/README.md +149 -109
- package/dist/assets/hooks/package.json +2 -2
- package/dist/commands/dismiss.d.ts +17 -0
- package/dist/commands/dismiss.js +201 -0
- package/dist/commands/init.js +25 -28
- package/dist/commands/pr-status.d.ts +16 -0
- package/dist/commands/pr-status.js +188 -0
- package/dist/commands/setup.d.ts +13 -0
- package/dist/commands/setup.js +496 -0
- package/dist/commands/submit.d.ts +1 -0
- package/dist/commands/submit.js +29 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +117 -7
- package/dist/triage/prompts.d.ts +3 -3
- package/dist/triage/prompts.js +46 -16
- package/dist/triage/runner.js +21 -7
- package/dist/utils/pending-state.d.ts +2 -0
- package/package.json +15 -15
package/README.md
CHANGED
|
@@ -1,68 +1,43 @@
|
|
|
1
1
|
# @haystackeditor/cli
|
|
2
2
|
|
|
3
|
-
Set up Haystack
|
|
3
|
+
Set up Haystack for your project. When PRs are opened, Haystack automatically reviews them for bugs, intent drift, and rule violations — then routes them to the right place.
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
7
|
-
### Fastest: AI-Assisted Setup
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npx @haystackeditor/cli skills install
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
This auto-detects your coding CLI (Claude Code, Codex, Cursor) and installs the Haystack MCP server.
|
|
14
|
-
|
|
15
|
-
Then invoke the setup in your coding CLI:
|
|
16
|
-
|
|
17
|
-
| CLI | How to invoke |
|
|
18
|
-
|-----|---------------|
|
|
19
|
-
| **Claude Code** | `/setup-haystack` |
|
|
20
|
-
| **Codex CLI** | `/setup-haystack` or ask "set up haystack verification" |
|
|
21
|
-
| **Cursor** | `/setup-haystack` (in Composer) |
|
|
22
|
-
|
|
23
|
-
The AI will analyze your codebase, create `.haystack.json`, and configure verification flows.
|
|
24
|
-
|
|
25
|
-
### Alternative: Direct CLI Setup
|
|
26
|
-
|
|
27
|
-
For quick setup without AI assistance:
|
|
28
|
-
|
|
29
7
|
```bash
|
|
30
|
-
|
|
8
|
+
npm install -g @haystackeditor/cli
|
|
9
|
+
haystack login
|
|
10
|
+
haystack setup
|
|
31
11
|
```
|
|
32
12
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
### Additional Skills
|
|
13
|
+
The `setup` command walks you through an interactive wizard:
|
|
36
14
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
15
|
+
1. **Select repositories** to configure
|
|
16
|
+
2. **Scan for coding rules** (conventions your team follows)
|
|
17
|
+
3. **Scan for CI/bot signals** (checks to wait for before merging)
|
|
18
|
+
4. **Scan for review policies** (who should review what)
|
|
19
|
+
5. **Review and toggle** discovered items
|
|
20
|
+
6. **Write `.haystack.json`** to your repos
|
|
42
21
|
|
|
43
22
|
---
|
|
44
23
|
|
|
45
24
|
## CLI Commands
|
|
46
25
|
|
|
47
|
-
|
|
26
|
+
### `haystack setup`
|
|
48
27
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
Install AI skills into your coding CLI (auto-detects Claude Code, Codex, Cursor):
|
|
28
|
+
Interactive onboarding wizard — scan your repos and generate `.haystack.json`:
|
|
52
29
|
|
|
53
30
|
```bash
|
|
54
|
-
|
|
55
|
-
npx @haystackeditor/cli skills install --cli codex # Install for Codex
|
|
56
|
-
npx @haystackeditor/cli skills install --cli cursor # Install for Cursor
|
|
57
|
-
npx @haystackeditor/cli skills install --cli manual # Show manual setup
|
|
58
|
-
npx @haystackeditor/cli skills list # List available skills
|
|
31
|
+
haystack setup
|
|
59
32
|
```
|
|
60
33
|
|
|
61
34
|
### `haystack init`
|
|
62
35
|
|
|
36
|
+
Quick local setup — auto-detects your project and creates `.haystack.json` without scanning:
|
|
37
|
+
|
|
63
38
|
```bash
|
|
64
|
-
|
|
65
|
-
|
|
39
|
+
haystack init # Auto-detect and create config
|
|
40
|
+
haystack init --force # Overwrite existing .haystack.json
|
|
66
41
|
```
|
|
67
42
|
|
|
68
43
|
### `haystack status`
|
|
@@ -70,49 +45,22 @@ npx @haystackeditor/cli init -y # Accept all defaults
|
|
|
70
45
|
Check if your project is configured:
|
|
71
46
|
|
|
72
47
|
```bash
|
|
73
|
-
|
|
48
|
+
haystack status
|
|
74
49
|
```
|
|
75
50
|
|
|
76
51
|
### `haystack login`
|
|
77
52
|
|
|
78
|
-
Authenticate with GitHub (required for secrets
|
|
53
|
+
Authenticate with GitHub (required for setup and secrets):
|
|
79
54
|
|
|
80
55
|
```bash
|
|
81
|
-
|
|
56
|
+
haystack login
|
|
82
57
|
```
|
|
83
58
|
|
|
84
59
|
This uses GitHub's device flow - you'll get a code to enter at github.com/login/device.
|
|
85
60
|
|
|
86
61
|
```bash
|
|
87
62
|
# Log out (removes stored credentials)
|
|
88
|
-
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### `haystack secrets`
|
|
92
|
-
|
|
93
|
-
Manage secrets that will be injected into your sandbox environment:
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
# List all secrets (keys only, values are never shown)
|
|
97
|
-
npx @haystackeditor/cli secrets list
|
|
98
|
-
|
|
99
|
-
# Set a secret
|
|
100
|
-
npx @haystackeditor/cli secrets set OPENAI_API_KEY sk-xxx
|
|
101
|
-
|
|
102
|
-
# Delete a secret
|
|
103
|
-
npx @haystackeditor/cli secrets delete OPENAI_API_KEY
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
Secrets are encrypted and stored securely. They're automatically injected as environment variables when the sandbox runs your app.
|
|
107
|
-
|
|
108
|
-
**Scopes**: By default, secrets are user-scoped. You can also scope to an org or repo:
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
# Org-scoped (available to all repos in the org)
|
|
112
|
-
npx @haystackeditor/cli secrets set API_KEY xxx --scope org --scope-id myorg
|
|
113
|
-
|
|
114
|
-
# Repo-scoped (available only to this repo)
|
|
115
|
-
npx @haystackeditor/cli secrets set API_KEY xxx --scope repo --scope-id owner/repo
|
|
63
|
+
haystack logout
|
|
116
64
|
```
|
|
117
65
|
|
|
118
66
|
### `haystack submit`
|
|
@@ -120,14 +68,14 @@ npx @haystackeditor/cli secrets set API_KEY xxx --scope repo --scope-id owner/re
|
|
|
120
68
|
Create a PR from current changes. Runs pre-PR triage (code review, rules validation, intent drift), pushes your branch, and opens the PR.
|
|
121
69
|
|
|
122
70
|
```bash
|
|
123
|
-
haystack submit # Triage
|
|
71
|
+
haystack submit # Triage -> create PR -> wait for analysis
|
|
124
72
|
haystack submit --title "Fix auth" # Custom PR title
|
|
125
73
|
haystack submit --draft # Create as draft PR
|
|
126
74
|
haystack submit --force # Skip triage checks
|
|
127
75
|
haystack submit --no-wait # Don't wait for analysis results
|
|
128
76
|
```
|
|
129
77
|
|
|
130
|
-
**Review routing**: By default, PRs go to the auto-merge queue
|
|
78
|
+
**Review routing**: By default, PRs go to the auto-merge queue -- if analysis passes, the PR is merged automatically. Use `--review` to route it for human review instead:
|
|
131
79
|
|
|
132
80
|
```bash
|
|
133
81
|
haystack submit --review # Needs review (goes to assignment queue)
|
|
@@ -148,7 +96,7 @@ haystack triage 42 --json # Machine-readable JSON output
|
|
|
148
96
|
haystack triage 42 --no-wait # Don't wait if analysis is pending
|
|
149
97
|
```
|
|
150
98
|
|
|
151
|
-
The `--json` output includes `agentFixPrompt` fields
|
|
99
|
+
The `--json` output includes `agentFixPrompt` fields -- ready-to-paste instructions for coding agents to fix each finding.
|
|
152
100
|
|
|
153
101
|
### `haystack check-pending`
|
|
154
102
|
|
|
@@ -161,53 +109,145 @@ haystack check-pending --json # Machine-readable JSON
|
|
|
161
109
|
haystack check-pending --clear # Clear pending state
|
|
162
110
|
```
|
|
163
111
|
|
|
164
|
-
|
|
112
|
+
### `haystack dismiss`
|
|
165
113
|
|
|
166
|
-
|
|
114
|
+
Dismiss analysis findings for a PR, moving it from "Issues Found" to "Good to Merge" in the feed. The override is tied to the PR's current HEAD commit.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
haystack dismiss 42 # Dismiss findings for PR #42
|
|
118
|
+
haystack dismiss acme/widgets#99 # Dismiss for specific repo
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### `haystack undismiss`
|
|
122
|
+
|
|
123
|
+
Clear all overrides (dismissed findings and/or review-not-needed) for a PR, returning it to its original feed bucket.
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
haystack undismiss 42 # Undo overrides for PR #42
|
|
127
|
+
haystack undismiss acme/widgets#99 # Undo for specific repo
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### `haystack mark-reviewed`
|
|
131
|
+
|
|
132
|
+
Mark human review as not needed for a PR, moving it from "Needs Review" to "Good to Merge" in the feed. The override is tied to the PR's current HEAD commit.
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
haystack mark-reviewed 42 # Mark review not needed for PR #42
|
|
136
|
+
haystack mark-reviewed acme/widgets#99 # Mark for specific repo
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### `haystack pr-status`
|
|
140
|
+
|
|
141
|
+
Show what bucket a PR is in within the Haystack pipeline (analyzing, auto-fixing, good-to-merge, issues, needs-assignment, etc.):
|
|
167
142
|
|
|
168
|
-
|
|
143
|
+
```bash
|
|
144
|
+
haystack pr-status 42 # Current repo, PR #42
|
|
145
|
+
haystack pr-status acme/widgets#99 # Specific repo
|
|
146
|
+
haystack pr-status https://github.com/o/r/pull/1 # From URL
|
|
147
|
+
haystack pr-status 42 --json # Machine-readable output
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### `haystack config`
|
|
169
151
|
|
|
170
|
-
|
|
171
|
-
version: "1"
|
|
172
|
-
name: my-app
|
|
152
|
+
Manage user preferences:
|
|
173
153
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
154
|
+
```bash
|
|
155
|
+
# Agentic tool selection
|
|
156
|
+
haystack config agentic-tool # Show current setting
|
|
157
|
+
haystack config agentic-tool opencode # Use Haystack billing (default)
|
|
158
|
+
haystack config agentic-tool claude-code # Use your Claude Max subscription
|
|
159
|
+
haystack config agentic-tool codex # Use your ChatGPT subscription
|
|
160
|
+
|
|
161
|
+
# Auto-merge for safe PRs
|
|
162
|
+
haystack config auto-merge # Show current status
|
|
163
|
+
haystack config auto-merge on # Enable auto-merge
|
|
164
|
+
haystack config auto-merge off # Disable auto-merge
|
|
165
|
+
|
|
166
|
+
# AI reviewer wait list
|
|
167
|
+
haystack config wait-for-reviewers # Show status
|
|
168
|
+
haystack config wait-for-reviewers add cursor # Wait for Cursor BugBot
|
|
169
|
+
haystack config wait-for-reviewers add cursor coderabbit # Add multiple
|
|
170
|
+
haystack config wait-for-reviewers remove cursor # Stop waiting
|
|
171
|
+
haystack config wait-for-reviewers clear # Wait for none
|
|
172
|
+
```
|
|
180
173
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
174
|
+
### `haystack skills`
|
|
175
|
+
|
|
176
|
+
Manage AI skills for your coding CLI:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
haystack skills install # Auto-detect CLI and install
|
|
180
|
+
haystack skills install --cli codex # Install for Codex only
|
|
181
|
+
haystack skills install --cli manual # Show manual setup instructions
|
|
182
|
+
haystack skills list # List available skills
|
|
187
183
|
```
|
|
188
184
|
|
|
189
|
-
###
|
|
185
|
+
### `haystack hooks`
|
|
190
186
|
|
|
191
|
-
|
|
192
|
-
|-------------------|----------|
|
|
193
|
-
| Login/authentication | Auth bypass env var in `dev_server.env` |
|
|
194
|
-
| Key user journeys | Flows describing what to verify |
|
|
195
|
-
| API calls needing auth | Fixtures to mock responses |
|
|
187
|
+
Manage git hooks for AI agent quality checks:
|
|
196
188
|
|
|
197
|
-
|
|
189
|
+
```bash
|
|
190
|
+
# Install hooks + Entire CLI
|
|
191
|
+
haystack hooks install # Install with pinned Entire version
|
|
192
|
+
haystack hooks install --force # Overwrite existing hooks
|
|
193
|
+
haystack hooks install --skip-entire # Only install Haystack hooks
|
|
194
|
+
|
|
195
|
+
# Status and updates
|
|
196
|
+
haystack hooks status # Check installation status
|
|
197
|
+
haystack hooks update # Update Entire CLI to latest
|
|
198
|
+
|
|
199
|
+
# Session hooks (check-pending on CLI start)
|
|
200
|
+
haystack hooks install-session # Auto-detect CLIs
|
|
201
|
+
haystack hooks install-session --cli claude # Claude Code only
|
|
202
|
+
haystack hooks install-session --cli all # All detected CLIs
|
|
203
|
+
haystack hooks session-status # Check session hook status
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### `haystack policy`
|
|
207
|
+
|
|
208
|
+
Manage review policies (`.haystack/review-policy.md`):
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
# List and inspect
|
|
212
|
+
haystack policy list # List all policies
|
|
213
|
+
|
|
214
|
+
# Add policies
|
|
215
|
+
haystack policy add # Interactive add
|
|
216
|
+
haystack policy add "Database changes" # Start with name
|
|
217
|
+
haystack policy add-instruction "Never flag weak test coverage as needing review"
|
|
218
|
+
|
|
219
|
+
# Remove policies
|
|
220
|
+
haystack policy remove "Database changes"
|
|
221
|
+
|
|
222
|
+
# Initialize with defaults
|
|
223
|
+
haystack policy init # Create with sensible defaults
|
|
224
|
+
haystack policy init --force # Overwrite existing
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Configuration
|
|
230
|
+
|
|
231
|
+
The `setup` wizard writes `.haystack.json` to your repos with discovered rules, signals, and policies. You can also create a base config locally with `haystack init`:
|
|
232
|
+
|
|
233
|
+
```json
|
|
234
|
+
{
|
|
235
|
+
"version": "1",
|
|
236
|
+
"name": "my-app"
|
|
237
|
+
}
|
|
238
|
+
```
|
|
198
239
|
|
|
199
240
|
---
|
|
200
241
|
|
|
201
242
|
## How It Works
|
|
202
243
|
|
|
203
|
-
1. Run `
|
|
204
|
-
2.
|
|
205
|
-
3.
|
|
206
|
-
|
|
207
|
-
- Spins up your app in a cloud sandbox
|
|
208
|
-
- Runs verification commands
|
|
209
|
-
- Captures screenshots and evidence
|
|
244
|
+
1. Run `haystack setup` to configure your repos (or `haystack init` for local-only config)
|
|
245
|
+
2. Install the [Haystack GitHub App](https://haystackeditor.com/github-app)
|
|
246
|
+
3. When PRs are opened, Haystack automatically:
|
|
247
|
+
- Analyzes the code for bugs, intent drift, and rule violations
|
|
210
248
|
- Reports results on the PR
|
|
249
|
+
- Routes the PR to the right inbox tab (Good to Merge, Issues Found, etc.)
|
|
250
|
+
- If auto-merge is enabled, clean PRs merge automatically
|
|
211
251
|
|
|
212
252
|
## License
|
|
213
253
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Git hooks for AI agent quality checks (installed by @haystackeditor/cli)",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"tree-sitter": "
|
|
9
|
-
"tree-sitter-typescript": "
|
|
8
|
+
"tree-sitter": "0.22.4",
|
|
9
|
+
"tree-sitter-typescript": "0.23.2"
|
|
10
10
|
}
|
|
11
11
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dismiss & mark-reviewed commands — User overrides for PR findings.
|
|
3
|
+
*
|
|
4
|
+
* haystack dismiss <pr> — Dismiss analysis findings for a PR
|
|
5
|
+
* haystack mark-reviewed <pr> — Mark human review as not needed for a PR
|
|
6
|
+
*
|
|
7
|
+
* Each posts a user override to the Haystack API so the PR moves toward
|
|
8
|
+
* "Good to Merge" in the feed.
|
|
9
|
+
*
|
|
10
|
+
* Accepts a PR identifier in several formats:
|
|
11
|
+
* 123 # Uses current repo's owner/repo
|
|
12
|
+
* owner/repo#123 # Fully qualified
|
|
13
|
+
* https://github.com/owner/repo/pull/123 # GitHub URL
|
|
14
|
+
*/
|
|
15
|
+
export declare function dismissCommand(identifier: string): Promise<void>;
|
|
16
|
+
export declare function markReviewedCommand(identifier: string): Promise<void>;
|
|
17
|
+
export declare function undismissCommand(identifier: string): Promise<void>;
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dismiss & mark-reviewed commands — User overrides for PR findings.
|
|
3
|
+
*
|
|
4
|
+
* haystack dismiss <pr> — Dismiss analysis findings for a PR
|
|
5
|
+
* haystack mark-reviewed <pr> — Mark human review as not needed for a PR
|
|
6
|
+
*
|
|
7
|
+
* Each posts a user override to the Haystack API so the PR moves toward
|
|
8
|
+
* "Good to Merge" in the feed.
|
|
9
|
+
*
|
|
10
|
+
* Accepts a PR identifier in several formats:
|
|
11
|
+
* 123 # Uses current repo's owner/repo
|
|
12
|
+
* owner/repo#123 # Fully qualified
|
|
13
|
+
* https://github.com/owner/repo/pull/123 # GitHub URL
|
|
14
|
+
*/
|
|
15
|
+
import chalk from 'chalk';
|
|
16
|
+
import { loadToken } from './login.js';
|
|
17
|
+
import { parseRemoteUrl } from '../utils/git.js';
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// PR identifier parsing (shared with triage — duplicated to avoid circular deps)
|
|
20
|
+
// ============================================================================
|
|
21
|
+
function parsePRIdentifier(identifier, commandName) {
|
|
22
|
+
const urlMatch = identifier.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)\/?$/);
|
|
23
|
+
if (urlMatch) {
|
|
24
|
+
return { owner: urlMatch[1], repo: urlMatch[2], prNumber: parseInt(urlMatch[3], 10) };
|
|
25
|
+
}
|
|
26
|
+
const qualifiedMatch = identifier.match(/^([^/]+)\/([^#]+)#(\d+)$/);
|
|
27
|
+
if (qualifiedMatch) {
|
|
28
|
+
return { owner: qualifiedMatch[1], repo: qualifiedMatch[2], prNumber: parseInt(qualifiedMatch[3], 10) };
|
|
29
|
+
}
|
|
30
|
+
const numberMatch = identifier.match(/^#?(\d+)$/);
|
|
31
|
+
if (numberMatch) {
|
|
32
|
+
const prNumber = parseInt(numberMatch[1], 10);
|
|
33
|
+
try {
|
|
34
|
+
const remote = parseRemoteUrl('origin');
|
|
35
|
+
return { owner: remote.owner, repo: remote.repo, prNumber };
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
throw new Error(`Cannot determine repository for PR #${prNumber}.\n` +
|
|
39
|
+
`Use the full format: haystack ${commandName} owner/repo#${prNumber}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
throw new Error(`Invalid PR identifier: "${identifier}"\n\n` +
|
|
43
|
+
`Accepted formats:\n` +
|
|
44
|
+
` haystack ${commandName} 123\n` +
|
|
45
|
+
` haystack ${commandName} owner/repo#123\n` +
|
|
46
|
+
` haystack ${commandName} https://github.com/owner/repo/pull/123`);
|
|
47
|
+
}
|
|
48
|
+
// ============================================================================
|
|
49
|
+
// GitHub API helpers
|
|
50
|
+
// ============================================================================
|
|
51
|
+
async function getPRHeadSha(owner, repo, prNumber, token) {
|
|
52
|
+
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/pulls/${prNumber}`, {
|
|
53
|
+
headers: {
|
|
54
|
+
Authorization: `Bearer ${token}`,
|
|
55
|
+
Accept: 'application/vnd.github.v3+json',
|
|
56
|
+
'User-Agent': 'Haystack-CLI',
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
if (response.status === 404) {
|
|
61
|
+
throw new Error(`PR #${prNumber} not found in ${owner}/${repo}`);
|
|
62
|
+
}
|
|
63
|
+
throw new Error(`Failed to fetch PR: HTTP ${response.status}`);
|
|
64
|
+
}
|
|
65
|
+
const data = await response.json();
|
|
66
|
+
return data.head.sha;
|
|
67
|
+
}
|
|
68
|
+
// ============================================================================
|
|
69
|
+
// User override API
|
|
70
|
+
// ============================================================================
|
|
71
|
+
const HAYSTACK_API = 'https://haystackeditor.com';
|
|
72
|
+
async function postUserOverride(repoFullName, prNumber, commitSha, type, token) {
|
|
73
|
+
const response = await fetch(`${HAYSTACK_API}/api/user-override`, {
|
|
74
|
+
method: 'POST',
|
|
75
|
+
headers: {
|
|
76
|
+
Authorization: `Bearer ${token}`,
|
|
77
|
+
'Content-Type': 'application/json',
|
|
78
|
+
'User-Agent': 'Haystack-CLI',
|
|
79
|
+
},
|
|
80
|
+
body: JSON.stringify({ repoFullName, prNumber, commitSha, type }),
|
|
81
|
+
});
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
const body = await response.text().catch(() => '');
|
|
84
|
+
if (response.status === 401) {
|
|
85
|
+
throw new Error('Authentication failed. Run `haystack login` to re-authenticate.');
|
|
86
|
+
}
|
|
87
|
+
if (response.status === 403) {
|
|
88
|
+
throw new Error('Access denied — you may not have permission to this repository.');
|
|
89
|
+
}
|
|
90
|
+
throw new Error(`API error (${response.status}): ${body}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function deleteUserOverrides(repoFullName, prNumber, token) {
|
|
94
|
+
const response = await fetch(`${HAYSTACK_API}/api/user-override`, {
|
|
95
|
+
method: 'DELETE',
|
|
96
|
+
headers: {
|
|
97
|
+
Authorization: `Bearer ${token}`,
|
|
98
|
+
'Content-Type': 'application/json',
|
|
99
|
+
'User-Agent': 'Haystack-CLI',
|
|
100
|
+
},
|
|
101
|
+
body: JSON.stringify({ repoFullName, prNumber }),
|
|
102
|
+
});
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
const body = await response.text().catch(() => '');
|
|
105
|
+
if (response.status === 401) {
|
|
106
|
+
throw new Error('Authentication failed. Run `haystack login` to re-authenticate.');
|
|
107
|
+
}
|
|
108
|
+
if (response.status === 403) {
|
|
109
|
+
throw new Error('Access denied — you may not have permission to this repository.');
|
|
110
|
+
}
|
|
111
|
+
throw new Error(`API error (${response.status}): ${body}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// ============================================================================
|
|
115
|
+
// Shared runner
|
|
116
|
+
// ============================================================================
|
|
117
|
+
async function runOverride(identifier, type, commandName) {
|
|
118
|
+
// Parse PR identifier
|
|
119
|
+
let pr;
|
|
120
|
+
try {
|
|
121
|
+
pr = parsePRIdentifier(identifier, commandName);
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
console.error(chalk.red(`\n${err.message}\n`));
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
// Require auth
|
|
128
|
+
const token = await loadToken();
|
|
129
|
+
if (!token) {
|
|
130
|
+
console.error(chalk.red('\nNot logged in. Run `haystack login` first.\n'));
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
const prLabel = `${pr.owner}/${pr.repo}#${pr.prNumber}`;
|
|
134
|
+
const repoFullName = `${pr.owner}/${pr.repo}`;
|
|
135
|
+
const actionLabel = type === 'findings-dismissed'
|
|
136
|
+
? 'Dismissing findings'
|
|
137
|
+
: 'Marking review as not needed';
|
|
138
|
+
console.log(chalk.dim(`\n${actionLabel} for ${prLabel}...`));
|
|
139
|
+
// Get PR HEAD SHA
|
|
140
|
+
let headSha;
|
|
141
|
+
try {
|
|
142
|
+
headSha = await getPRHeadSha(pr.owner, pr.repo, pr.prNumber, token);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
console.error(chalk.red(`\n${err.message}\n`));
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
// Post the override
|
|
149
|
+
try {
|
|
150
|
+
await postUserOverride(repoFullName, pr.prNumber, headSha, type, token);
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
console.error(chalk.red(`\n${err.message}\n`));
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
// Success
|
|
157
|
+
if (type === 'findings-dismissed') {
|
|
158
|
+
console.log(chalk.green(`\n Findings dismissed for ${prLabel}`));
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
console.log(chalk.green(`\n Review marked as not needed for ${prLabel}`));
|
|
162
|
+
}
|
|
163
|
+
console.log(chalk.dim(` Commit: ${headSha.slice(0, 7)}`));
|
|
164
|
+
console.log(chalk.dim(' The PR will move to "Good to Merge" in the feed.\n'));
|
|
165
|
+
}
|
|
166
|
+
// ============================================================================
|
|
167
|
+
// Exported commands
|
|
168
|
+
// ============================================================================
|
|
169
|
+
export async function dismissCommand(identifier) {
|
|
170
|
+
await runOverride(identifier, 'findings-dismissed', 'dismiss');
|
|
171
|
+
}
|
|
172
|
+
export async function markReviewedCommand(identifier) {
|
|
173
|
+
await runOverride(identifier, 'review-not-needed', 'mark-reviewed');
|
|
174
|
+
}
|
|
175
|
+
export async function undismissCommand(identifier) {
|
|
176
|
+
let pr;
|
|
177
|
+
try {
|
|
178
|
+
pr = parsePRIdentifier(identifier, 'undismiss');
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
console.error(chalk.red(`\n${err.message}\n`));
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
const token = await loadToken();
|
|
185
|
+
if (!token) {
|
|
186
|
+
console.error(chalk.red('\nNot logged in. Run `haystack login` first.\n'));
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
const prLabel = `${pr.owner}/${pr.repo}#${pr.prNumber}`;
|
|
190
|
+
const repoFullName = `${pr.owner}/${pr.repo}`;
|
|
191
|
+
console.log(chalk.dim(`\nClearing overrides for ${prLabel}...`));
|
|
192
|
+
try {
|
|
193
|
+
await deleteUserOverrides(repoFullName, pr.prNumber, token);
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
console.error(chalk.red(`\n${err.message}\n`));
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
console.log(chalk.green(`\n Overrides cleared for ${prLabel}`));
|
|
200
|
+
console.log(chalk.dim(' The PR will return to its original feed bucket.\n'));
|
|
201
|
+
}
|
package/dist/commands/init.js
CHANGED
|
@@ -7,7 +7,8 @@ import chalk from 'chalk';
|
|
|
7
7
|
import * as path from 'path';
|
|
8
8
|
import { detectProject } from '../utils/detect.js';
|
|
9
9
|
import { saveConfig, configExists } from '../utils/config.js';
|
|
10
|
-
|
|
10
|
+
// HIDDEN: Verification flow — uncomment to re-enable
|
|
11
|
+
// import { createSkillFile, createClaudeCommand } from '../utils/skill.js';
|
|
11
12
|
import { validateConfigSecurity, formatSecurityReport } from '../utils/secrets.js';
|
|
12
13
|
export async function initCommand(options) {
|
|
13
14
|
console.log(chalk.cyan('\n🌾 Haystack Setup\n'));
|
|
@@ -36,23 +37,17 @@ export async function initCommand(options) {
|
|
|
36
37
|
const config = buildConfigFromDetection(detected);
|
|
37
38
|
const configPath = await saveConfig(config);
|
|
38
39
|
console.log(chalk.green(`✓ Created ${configPath}`));
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
// HIDDEN: Verification flow — uncomment to re-enable
|
|
41
|
+
// // Create skill file for agent discovery
|
|
42
|
+
// const skillPath = await createSkillFile();
|
|
43
|
+
// console.log(chalk.green(`✓ Created ${skillPath}`));
|
|
44
|
+
//
|
|
45
|
+
// // Create Claude Code slash command
|
|
46
|
+
// const commandPath = await createClaudeCommand();
|
|
47
|
+
// console.log(chalk.green(`✓ Created ${commandPath}`));
|
|
45
48
|
// Security validation
|
|
46
49
|
await runSecurityCheck(configPath);
|
|
47
|
-
|
|
48
|
-
console.log(chalk.cyan('\n━'.repeat(60)));
|
|
49
|
-
console.log(chalk.cyan.bold('\n📋 NEXT: Add verification flows\n'));
|
|
50
|
-
console.log(chalk.white('Base config created. Now an AI agent needs to add flows.\n'));
|
|
51
|
-
console.log(chalk.bold('Option 1: Claude Code'));
|
|
52
|
-
console.log(chalk.dim(' Run /setup-haystack in Claude Code\n'));
|
|
53
|
-
console.log(chalk.bold('Option 2: Other AI agents'));
|
|
54
|
-
console.log(chalk.dim(' Give your agent: "Read .agents/skills/setup-haystack.md and follow it"\n'));
|
|
55
|
-
console.log(chalk.cyan('━'.repeat(60) + '\n'));
|
|
50
|
+
console.log(chalk.green('\nDone! Run `haystack setup` to configure rules, signals, and policies.\n'));
|
|
56
51
|
}
|
|
57
52
|
/**
|
|
58
53
|
* Run security check on config file
|
|
@@ -91,12 +86,13 @@ function buildConfigFromDetection(detected) {
|
|
|
91
86
|
version: '1',
|
|
92
87
|
name: path.basename(process.cwd()),
|
|
93
88
|
services,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
89
|
+
// HIDDEN: Verification flow — uncomment to re-enable
|
|
90
|
+
// verification: {
|
|
91
|
+
// commands: [
|
|
92
|
+
// { name: 'build', run: `${detected.packageManager} build` },
|
|
93
|
+
// { name: 'lint', run: `${detected.packageManager} lint` },
|
|
94
|
+
// ],
|
|
95
|
+
// },
|
|
100
96
|
};
|
|
101
97
|
}
|
|
102
98
|
return {
|
|
@@ -108,11 +104,12 @@ function buildConfigFromDetection(detected) {
|
|
|
108
104
|
ready_pattern: detected.suggestedReadyPattern || 'Local:',
|
|
109
105
|
...(Object.keys(env).length > 0 ? { env } : {}),
|
|
110
106
|
},
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
107
|
+
// HIDDEN: Verification flow — uncomment to re-enable
|
|
108
|
+
// verification: {
|
|
109
|
+
// commands: [
|
|
110
|
+
// { name: 'build', run: `${detected.packageManager} build` },
|
|
111
|
+
// { name: 'lint', run: `${detected.packageManager} lint` },
|
|
112
|
+
// ],
|
|
113
|
+
// },
|
|
117
114
|
};
|
|
118
115
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pr-status command — Show the current Haystack status of a PR.
|
|
3
|
+
*
|
|
4
|
+
* Calls the classify-debug endpoint to determine which inbox bucket the PR
|
|
5
|
+
* is in (analyzing, auto-fixing, good-to-merge, issues, needs-assignment, etc.)
|
|
6
|
+
* and displays a human-readable summary.
|
|
7
|
+
*
|
|
8
|
+
* Accepts a PR identifier in several formats:
|
|
9
|
+
* 123 # Uses current repo's owner/repo
|
|
10
|
+
* owner/repo#123 # Fully qualified
|
|
11
|
+
* https://github.com/owner/repo/pull/123 # GitHub URL
|
|
12
|
+
*/
|
|
13
|
+
export interface PRStatusOptions {
|
|
14
|
+
json?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function prStatusCommand(identifier: string, options: PRStatusOptions): Promise<void>;
|