@haystackeditor/cli 0.9.0 → 0.10.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.
- package/README.md +51 -91
- 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 +4 -0
- package/dist/index.js +113 -3
- 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 +11 -11
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 analyzes them for bugs, intent drift, and rule violations.
|
|
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
|
-
|
|
13
|
+
The `setup` command walks you through an interactive wizard:
|
|
34
14
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
| `/setup-haystack-secrets` | Configure API keys, LLM credentials, secrets |
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
### `haystack skills`
|
|
26
|
+
### `haystack setup`
|
|
50
27
|
|
|
51
|
-
|
|
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,22 +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
|
-
|
|
63
|
+
haystack logout
|
|
89
64
|
```
|
|
90
65
|
|
|
91
66
|
### `haystack secrets`
|
|
@@ -94,13 +69,13 @@ Manage secrets that will be injected into your sandbox environment:
|
|
|
94
69
|
|
|
95
70
|
```bash
|
|
96
71
|
# List all secrets (keys only, values are never shown)
|
|
97
|
-
|
|
72
|
+
haystack secrets list
|
|
98
73
|
|
|
99
74
|
# Set a secret
|
|
100
|
-
|
|
75
|
+
haystack secrets set OPENAI_API_KEY sk-xxx
|
|
101
76
|
|
|
102
77
|
# Delete a secret
|
|
103
|
-
|
|
78
|
+
haystack secrets delete OPENAI_API_KEY
|
|
104
79
|
```
|
|
105
80
|
|
|
106
81
|
Secrets are encrypted and stored securely. They're automatically injected as environment variables when the sandbox runs your app.
|
|
@@ -109,10 +84,10 @@ Secrets are encrypted and stored securely. They're automatically injected as env
|
|
|
109
84
|
|
|
110
85
|
```bash
|
|
111
86
|
# Org-scoped (available to all repos in the org)
|
|
112
|
-
|
|
87
|
+
haystack secrets set API_KEY xxx --scope org --scope-id myorg
|
|
113
88
|
|
|
114
89
|
# Repo-scoped (available only to this repo)
|
|
115
|
-
|
|
90
|
+
haystack secrets set API_KEY xxx --scope repo --scope-id owner/repo
|
|
116
91
|
```
|
|
117
92
|
|
|
118
93
|
### `haystack submit`
|
|
@@ -120,14 +95,14 @@ npx @haystackeditor/cli secrets set API_KEY xxx --scope repo --scope-id owner/re
|
|
|
120
95
|
Create a PR from current changes. Runs pre-PR triage (code review, rules validation, intent drift), pushes your branch, and opens the PR.
|
|
121
96
|
|
|
122
97
|
```bash
|
|
123
|
-
haystack submit # Triage
|
|
98
|
+
haystack submit # Triage -> create PR -> wait for analysis
|
|
124
99
|
haystack submit --title "Fix auth" # Custom PR title
|
|
125
100
|
haystack submit --draft # Create as draft PR
|
|
126
101
|
haystack submit --force # Skip triage checks
|
|
127
102
|
haystack submit --no-wait # Don't wait for analysis results
|
|
128
103
|
```
|
|
129
104
|
|
|
130
|
-
**Review routing**: By default, PRs go to the auto-merge queue
|
|
105
|
+
**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
106
|
|
|
132
107
|
```bash
|
|
133
108
|
haystack submit --review # Needs review (goes to assignment queue)
|
|
@@ -148,7 +123,7 @@ haystack triage 42 --json # Machine-readable JSON output
|
|
|
148
123
|
haystack triage 42 --no-wait # Don't wait if analysis is pending
|
|
149
124
|
```
|
|
150
125
|
|
|
151
|
-
The `--json` output includes `agentFixPrompt` fields
|
|
126
|
+
The `--json` output includes `agentFixPrompt` fields -- ready-to-paste instructions for coding agents to fix each finding.
|
|
152
127
|
|
|
153
128
|
### `haystack check-pending`
|
|
154
129
|
|
|
@@ -165,49 +140,34 @@ haystack check-pending --clear # Clear pending state
|
|
|
165
140
|
|
|
166
141
|
## Configuration
|
|
167
142
|
|
|
168
|
-
The `
|
|
169
|
-
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
dev_server:
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
- name: build
|
|
184
|
-
run: pnpm build
|
|
185
|
-
- name: lint
|
|
186
|
-
run: pnpm lint
|
|
143
|
+
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`:
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"version": "1",
|
|
148
|
+
"name": "my-app",
|
|
149
|
+
"dev_server": {
|
|
150
|
+
"command": "pnpm dev",
|
|
151
|
+
"port": 3000,
|
|
152
|
+
"ready_pattern": "Local:",
|
|
153
|
+
"env": {
|
|
154
|
+
"SKIP_AUTH": "true"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
187
158
|
```
|
|
188
159
|
|
|
189
|
-
### Customizing After Init
|
|
190
|
-
|
|
191
|
-
| If your app has... | Add this |
|
|
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 |
|
|
196
|
-
|
|
197
|
-
Use `/setup-haystack` in Claude Code for AI-assisted configuration of flows and fixtures.
|
|
198
|
-
|
|
199
160
|
---
|
|
200
161
|
|
|
201
162
|
## How It Works
|
|
202
163
|
|
|
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
|
|
164
|
+
1. Run `haystack setup` to configure your repos (or `haystack init` for local-only config)
|
|
165
|
+
2. Install the [Haystack GitHub App](https://haystackeditor.com/github-app)
|
|
166
|
+
3. When PRs are opened, Haystack automatically:
|
|
167
|
+
- Analyzes the code for bugs, intent drift, and rule violations
|
|
210
168
|
- Reports results on the PR
|
|
169
|
+
- Routes the PR to the right inbox tab (Good to Merge, Issues Found, etc.)
|
|
170
|
+
- If auto-merge is enabled, clean PRs merge automatically
|
|
211
171
|
|
|
212
172
|
## License
|
|
213
173
|
|
|
@@ -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>;
|