@haystackeditor/cli 0.7.2 → 0.8.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 +59 -12
- package/dist/assets/hooks/agent-context/detect.ts +136 -0
- package/dist/assets/hooks/agent-context/format.ts +99 -0
- package/dist/assets/hooks/agent-context/index.ts +39 -0
- package/dist/assets/hooks/agent-context/parsers/claude.ts +253 -0
- package/dist/assets/hooks/agent-context/parsers/gemini.ts +155 -0
- package/dist/assets/hooks/agent-context/parsers/opencode.ts +174 -0
- package/dist/assets/hooks/agent-context/tsconfig.json +13 -0
- package/dist/assets/hooks/agent-context/types.ts +58 -0
- package/dist/assets/hooks/llm-rules-template.md +35 -0
- package/dist/assets/hooks/package.json +11 -0
- package/dist/assets/hooks/scripts/commit-msg.sh +4 -0
- package/dist/assets/hooks/scripts/post-commit.sh +4 -0
- package/dist/assets/hooks/scripts/pre-commit.sh +92 -0
- package/dist/assets/hooks/scripts/pre-push.sh +5 -0
- package/dist/assets/hooks/scripts/prepare-commit-msg.sh +3 -0
- package/dist/assets/hooks/truncation-checker/ast-analyzer.ts +528 -0
- package/dist/assets/hooks/truncation-checker/index.ts +595 -0
- package/dist/assets/hooks/truncation-checker/tsconfig.json +13 -0
- package/dist/commands/config.d.ts +14 -0
- package/dist/commands/config.js +89 -0
- package/dist/commands/hooks.d.ts +17 -0
- package/dist/commands/hooks.js +269 -0
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +20 -239
- package/dist/commands/secrets.d.ts +15 -0
- package/dist/commands/secrets.js +83 -0
- package/dist/commands/skills.d.ts +8 -0
- package/dist/commands/skills.js +215 -0
- package/dist/index.js +107 -7
- package/dist/types.d.ts +32 -8
- package/dist/utils/hooks.d.ts +26 -0
- package/dist/utils/hooks.js +226 -0
- package/dist/utils/skill.d.ts +1 -1
- package/dist/utils/skill.js +481 -13
- package/package.json +2 -2
package/dist/utils/skill.js
CHANGED
|
@@ -104,17 +104,32 @@ grep '"name":' .haystack.json
|
|
|
104
104
|
}
|
|
105
105
|
\`\`\`
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
---
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
- Pick a **small, stable PR** that won't change
|
|
111
|
-
- Document what the expected output should be
|
|
112
|
-
- Add it as a comment in the flow description
|
|
109
|
+
## STOP - Confirm Golden URLs and Golden Data
|
|
113
110
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
111
|
+
**If you identified backend services that need verification, you MUST ask the user with a proposed example:**
|
|
112
|
+
|
|
113
|
+
First, explore the codebase to find:
|
|
114
|
+
- Example inputs used in tests, scripts, or documentation
|
|
115
|
+
- Default/demo values in config files or environment variables
|
|
116
|
+
- IDs or URLs referenced in comments or READMEs
|
|
117
|
+
|
|
118
|
+
Then propose a specific example:
|
|
119
|
+
|
|
120
|
+
> I found these backend services that need verification:
|
|
121
|
+
> - **[service name]**: [what it does]
|
|
122
|
+
>
|
|
123
|
+
> For backend verification, I need **golden test data** - stable inputs that produce predictable output.
|
|
124
|
+
>
|
|
125
|
+
> **Based on what I found in the codebase, here's my suggestion:**
|
|
126
|
+
> - **Golden URL/Input**: \`[specific example you found, e.g., a test ID, demo endpoint, or sample file]\`
|
|
127
|
+
> - **Command**: \`[the command to run with this input]\`
|
|
128
|
+
> - **Expected Output**: \`[what success looks like based on the code]\`
|
|
129
|
+
>
|
|
130
|
+
> Does this look right? Or should I use different test data?
|
|
131
|
+
|
|
132
|
+
**Wait for the user to confirm or provide alternatives before adding backend flows.**
|
|
118
133
|
|
|
119
134
|
---
|
|
120
135
|
|
|
@@ -153,6 +168,92 @@ grep -r "https://" src/ --include="*.ts" --include="*.tsx" | grep -v node_module
|
|
|
153
168
|
|
|
154
169
|
---
|
|
155
170
|
|
|
171
|
+
## STOP - Confirm Frontend Golden URLs
|
|
172
|
+
|
|
173
|
+
**Goldens are URLs with real staging data** - they let verification test pages that need actual content to render properly. You need MULTIPLE goldens to cover different scenarios.
|
|
174
|
+
|
|
175
|
+
**Before writing flows for dynamic routes, explore the codebase to find example data:**
|
|
176
|
+
|
|
177
|
+
1. Check test files (\`*.test.ts\`, \`cypress/\`, \`playwright/\`) for example IDs/URLs
|
|
178
|
+
2. Check README/docs for demo links or example usage
|
|
179
|
+
3. Check seed data, fixtures, or mock files
|
|
180
|
+
4. Check \`.env.example\` or config for staging URLs
|
|
181
|
+
5. Check existing routes to understand the URL format
|
|
182
|
+
|
|
183
|
+
**Then propose MULTIPLE golden URLs (aim for 3-5) covering different scenarios:**
|
|
184
|
+
|
|
185
|
+
> I found these dynamic routes that need staging data to render:
|
|
186
|
+
>
|
|
187
|
+
> **[Route Name]** (\`/path/:param1/:param2\`)
|
|
188
|
+
>
|
|
189
|
+
> | Golden | URL | Why |
|
|
190
|
+
> |--------|-----|-----|
|
|
191
|
+
> | Large dataset | \`/path/example/large-123\` | Tests performance with many items |
|
|
192
|
+
> | Typical case | \`/path/example/medium-456\` | Common user scenario |
|
|
193
|
+
> | Edge case | \`/path/example/empty-789\` | Tests empty/minimal state |
|
|
194
|
+
> | Error state | \`/path/example/error-000\` | Tests error handling UI |
|
|
195
|
+
>
|
|
196
|
+
> **Evidence:**
|
|
197
|
+
> - Found \`large-123\` in \`cypress/fixtures/test-data.json\`
|
|
198
|
+
> - Found \`medium-456\` in README as demo example
|
|
199
|
+
> - Found \`empty-789\` in \`src/tests/edge-cases.test.ts\`
|
|
200
|
+
>
|
|
201
|
+
> Does this look right?
|
|
202
|
+
> 1. ✅ Yes, use these goldens
|
|
203
|
+
> 2. 🔄 Use different URLs (I'll provide them)
|
|
204
|
+
|
|
205
|
+
**Add goldens to \`verification.goldens\` in \`.haystack.json\` using this EXACT format:**
|
|
206
|
+
|
|
207
|
+
\`\`\`json
|
|
208
|
+
{
|
|
209
|
+
"verification": {
|
|
210
|
+
"goldens": {
|
|
211
|
+
"frontend": [
|
|
212
|
+
{
|
|
213
|
+
"id": "large-dataset",
|
|
214
|
+
"description": "100+ items - tests rendering performance",
|
|
215
|
+
"route": "/the/actual/url/with/data"
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"id": "typical-case",
|
|
219
|
+
"description": "Standard 10-20 items",
|
|
220
|
+
"route": "/another/url/with/typical/data"
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"id": "empty-state",
|
|
224
|
+
"description": "No items - tests empty state UI",
|
|
225
|
+
"route": "/url/that/shows/empty/state"
|
|
226
|
+
}
|
|
227
|
+
],
|
|
228
|
+
"backend": [
|
|
229
|
+
{
|
|
230
|
+
"id": "golden-input-1",
|
|
231
|
+
"description": "Known-good input for pipeline testing",
|
|
232
|
+
"input": { "type": "github_pr", "value": "owner/repo#123" }
|
|
233
|
+
}
|
|
234
|
+
]
|
|
235
|
+
},
|
|
236
|
+
"commands": [...]
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
\`\`\`
|
|
240
|
+
|
|
241
|
+
**DO NOT:**
|
|
242
|
+
- Ask the user what format to use - USE THIS FORMAT
|
|
243
|
+
- Put goldens at the top level - they go inside \`verification.goldens\`
|
|
244
|
+
- Use a flat object like \`{ "goldens": { "name": "url" } }\` - use the array format above
|
|
245
|
+
- Embed golden URLs only in flow steps - also add them to the goldens section for discoverability
|
|
246
|
+
|
|
247
|
+
**CRITICAL**:
|
|
248
|
+
- Propose MULTIPLE goldens (3-5), not just one
|
|
249
|
+
- Cover different scenarios: large, typical, empty, error states
|
|
250
|
+
- Always propose SPECIFIC URLs, never ask the user to freestyle
|
|
251
|
+
- Show WHERE you found the example data (file path, line if possible)
|
|
252
|
+
- Make sure URLs match the app's actual route format (check \`<Route path=\`)
|
|
253
|
+
- Wait for confirmation before writing flows with these URLs
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
156
257
|
## Step 5: Write Flows
|
|
157
258
|
|
|
158
259
|
Flows tell the Planner about your app's routes, UI elements, and user journeys.
|
|
@@ -596,6 +697,49 @@ The verification Planner needs to find UI elements by selectors. Generic selecto
|
|
|
596
697
|
|
|
597
698
|
---
|
|
598
699
|
|
|
700
|
+
## ⚠️ CRITICAL RULE: Every Button MUST Have \`data-testid\` and \`aria-label\`
|
|
701
|
+
|
|
702
|
+
**This is non-negotiable.** Every \`<button>\` element in the codebase MUST have:
|
|
703
|
+
1. \`data-testid="descriptive-name"\` - For automated testing/verification
|
|
704
|
+
2. \`aria-label="Action description"\` - For accessibility AND verification
|
|
705
|
+
|
|
706
|
+
**Why?** The verification system clicks buttons by their \`data-testid\` or \`aria-label\`. Without these, buttons are invisible to automation. We learned this the hard way - a theme toggle button without these attributes broke our entire verification pipeline.
|
|
707
|
+
|
|
708
|
+
\`\`\`tsx
|
|
709
|
+
// ❌ WRONG - No identifiers, verification cannot click this
|
|
710
|
+
<button onClick={toggleTheme}>
|
|
711
|
+
<SunIcon />
|
|
712
|
+
</button>
|
|
713
|
+
|
|
714
|
+
// ✅ CORRECT - Both data-testid AND aria-label
|
|
715
|
+
<button
|
|
716
|
+
onClick={toggleTheme}
|
|
717
|
+
data-testid="theme-toggle"
|
|
718
|
+
aria-label="Toggle dark mode"
|
|
719
|
+
>
|
|
720
|
+
<SunIcon />
|
|
721
|
+
</button>
|
|
722
|
+
\`\`\`
|
|
723
|
+
|
|
724
|
+
**For button wrapper components**, pass through these props:
|
|
725
|
+
\`\`\`tsx
|
|
726
|
+
interface ButtonProps {
|
|
727
|
+
'data-testid'?: string;
|
|
728
|
+
'aria-label'?: string;
|
|
729
|
+
// ...other props
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
function IconButton({ 'data-testid': testId, 'aria-label': ariaLabel, ...props }: ButtonProps) {
|
|
733
|
+
return (
|
|
734
|
+
<button data-testid={testId} aria-label={ariaLabel} {...props}>
|
|
735
|
+
{props.children}
|
|
736
|
+
</button>
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
\`\`\`
|
|
740
|
+
|
|
741
|
+
---
|
|
742
|
+
|
|
599
743
|
## What to Add
|
|
600
744
|
|
|
601
745
|
### 1. \`aria-label\` on Interactive Elements
|
|
@@ -727,13 +871,21 @@ Forms should have proper labels and descriptions:
|
|
|
727
871
|
|
|
728
872
|
## Step 1: Scan for Missing Identifiers
|
|
729
873
|
|
|
874
|
+
**Start with buttons - these are the most critical:**
|
|
875
|
+
|
|
730
876
|
\`\`\`bash
|
|
731
|
-
# Find buttons
|
|
732
|
-
grep -rn "<button" src/ --include="*.tsx" | grep -v "
|
|
877
|
+
# CRITICAL: Find ALL buttons missing data-testid (fix ALL of these!)
|
|
878
|
+
grep -rn "<button" src/ --include="*.tsx" | grep -v "data-testid" | head -30
|
|
879
|
+
|
|
880
|
+
# CRITICAL: Find ALL buttons missing aria-label (fix ALL of these!)
|
|
881
|
+
grep -rn "<button" src/ --include="*.tsx" | grep -v "aria-label" | head -30
|
|
733
882
|
|
|
734
|
-
# Find icon-only buttons (
|
|
883
|
+
# Find icon-only buttons (MUST have aria-label since no visible text)
|
|
735
884
|
grep -rn "<button.*Icon\\|<button.*>.*</.*Icon>" src/ --include="*.tsx" | head -20
|
|
736
885
|
|
|
886
|
+
# Find button wrapper components that need to pass through data-testid/aria-label
|
|
887
|
+
grep -rn "function.*Button\\|const.*Button.*=" src/ --include="*.tsx" | head -10
|
|
888
|
+
|
|
737
889
|
# Find modals/dialogs without role
|
|
738
890
|
grep -rn "modal\\|dialog\\|Modal\\|Dialog" src/ --include="*.tsx" | grep -v "role=" | head -20
|
|
739
891
|
|
|
@@ -744,6 +896,8 @@ grep -rn "<input\\|<select\\|<textarea" src/ --include="*.tsx" | grep -v "aria-l
|
|
|
744
896
|
ls src/components/ src/pages/ 2>/dev/null
|
|
745
897
|
\`\`\`
|
|
746
898
|
|
|
899
|
+
**Every button found without \`data-testid\` or \`aria-label\` MUST be fixed.**
|
|
900
|
+
|
|
747
901
|
---
|
|
748
902
|
|
|
749
903
|
## Step 2: Prioritize by Impact
|
|
@@ -916,31 +1070,345 @@ Follow .agents/skills/prepare-haystack.md to add accessibility attributes that m
|
|
|
916
1070
|
|
|
917
1071
|
Run this BEFORE /setup-haystack to ensure your codebase has good selectors.
|
|
918
1072
|
`;
|
|
1073
|
+
const SECRETS_COMMAND_CONTENT = `# Set Up Haystack Secrets
|
|
1074
|
+
|
|
1075
|
+
Follow .agents/skills/setup-haystack-secrets.md to configure secrets (API keys, credentials) for Haystack verification.
|
|
1076
|
+
`;
|
|
1077
|
+
const SECRETS_SKILL_CONTENT = `# Set Up Haystack Secrets
|
|
1078
|
+
|
|
1079
|
+
**Your job**: Help the user configure secrets needed for Haystack verification, especially LLM API keys for backend services.
|
|
1080
|
+
|
|
1081
|
+
---
|
|
1082
|
+
|
|
1083
|
+
## Step 1: Detect Services That Need Secrets
|
|
1084
|
+
|
|
1085
|
+
Scan the codebase to identify services that might need API keys or credentials:
|
|
1086
|
+
|
|
1087
|
+
\`\`\`bash
|
|
1088
|
+
# Find LLM/AI SDK usage
|
|
1089
|
+
grep -rn "openai\\|anthropic\\|bedrock\\|OPENAI_API_KEY\\|ANTHROPIC_API_KEY" --include="*.ts" --include="*.py" --include="*.js" | grep -v node_modules | head -20
|
|
1090
|
+
|
|
1091
|
+
# Find environment variable references
|
|
1092
|
+
grep -rn "process.env\\.\\|os.environ\\|env\\." --include="*.ts" --include="*.py" --include="*.js" | grep -v node_modules | grep -iE "key|secret|token|api" | head -20
|
|
1093
|
+
|
|
1094
|
+
# Check for .env.example or similar
|
|
1095
|
+
cat .env.example .env.sample 2>/dev/null || echo "No .env example found"
|
|
1096
|
+
|
|
1097
|
+
# Check existing secrets in .haystack.json
|
|
1098
|
+
grep -A10 '"secrets"' .haystack.json 2>/dev/null || echo "No secrets configured yet"
|
|
1099
|
+
\`\`\`
|
|
1100
|
+
|
|
1101
|
+
---
|
|
1102
|
+
|
|
1103
|
+
## Step 2: Categorize the Secrets Found
|
|
1104
|
+
|
|
1105
|
+
Group secrets by type and service:
|
|
1106
|
+
|
|
1107
|
+
| Category | Examples | Storage Recommendation |
|
|
1108
|
+
|----------|----------|------------------------|
|
|
1109
|
+
| **LLM API Keys** | \`OPENAI_API_KEY\`, \`ANTHROPIC_API_KEY\` | Haystack Secrets (encrypted) |
|
|
1110
|
+
| **Cloud Credentials** | \`AWS_ACCESS_KEY_ID\`, \`GCP_SERVICE_ACCOUNT\` | Use OIDC in CI, not static keys |
|
|
1111
|
+
| **Database** | \`DATABASE_URL\`, \`REDIS_URL\` | Haystack Secrets or passthrough |
|
|
1112
|
+
| **Third-party APIs** | \`STRIPE_KEY\`, \`SENDGRID_KEY\` | Haystack Secrets |
|
|
1113
|
+
| **Internal Services** | \`API_TOKEN\`, \`WEBHOOK_SECRET\` | Haystack Secrets |
|
|
1114
|
+
|
|
1115
|
+
---
|
|
1116
|
+
|
|
1117
|
+
## STOP - Present Findings and Ask User
|
|
1118
|
+
|
|
1119
|
+
**You MUST present what you found and ask the user before proceeding:**
|
|
1120
|
+
|
|
1121
|
+
> I scanned the codebase and found these services that may need secrets for verification:
|
|
1122
|
+
>
|
|
1123
|
+
> **Services detected:**
|
|
1124
|
+
> - [Service 1]: Uses \`OPENAI_API_KEY\` for [purpose]
|
|
1125
|
+
> - [Service 2]: Uses \`DATABASE_URL\` for [purpose]
|
|
1126
|
+
> - ...
|
|
1127
|
+
>
|
|
1128
|
+
> **Questions:**
|
|
1129
|
+
>
|
|
1130
|
+
> 1. **LLM API Keys**: Which provider do you use?
|
|
1131
|
+
> - OpenAI (\`OPENAI_API_KEY\`)
|
|
1132
|
+
> - Anthropic (\`ANTHROPIC_API_KEY\`)
|
|
1133
|
+
> - AWS Bedrock (use OIDC, no static keys needed)
|
|
1134
|
+
> - Other: ___
|
|
1135
|
+
>
|
|
1136
|
+
> 2. **For each secret**, should verification use:
|
|
1137
|
+
> - **Real credentials** (stored securely in Haystack Secrets)
|
|
1138
|
+
> - **Test/sandbox credentials** (separate keys for verification only)
|
|
1139
|
+
> - **Mock/skip** (service not needed for visual verification)
|
|
1140
|
+
>
|
|
1141
|
+
> 3. **Do you have separate test credentials?**
|
|
1142
|
+
> - Yes, I have sandbox/test API keys
|
|
1143
|
+
> - No, I'll use production keys (with usage limits)
|
|
1144
|
+
> - I need to create test credentials first
|
|
1145
|
+
|
|
1146
|
+
**Wait for the user's response before continuing.**
|
|
1147
|
+
|
|
1148
|
+
---
|
|
1149
|
+
|
|
1150
|
+
## Step 3: Configure Secrets in .haystack.json
|
|
1151
|
+
|
|
1152
|
+
Based on user's answers, add secrets to the config:
|
|
1153
|
+
|
|
1154
|
+
\`\`\`json
|
|
1155
|
+
{
|
|
1156
|
+
"secrets": {
|
|
1157
|
+
"OPENAI_API_KEY": {
|
|
1158
|
+
"description": "OpenAI API key for analysis service",
|
|
1159
|
+
"required": true,
|
|
1160
|
+
"services": ["analysis"]
|
|
1161
|
+
},
|
|
1162
|
+
"DATABASE_URL": {
|
|
1163
|
+
"description": "PostgreSQL connection string",
|
|
1164
|
+
"required": false,
|
|
1165
|
+
"services": ["api"]
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
\`\`\`
|
|
1170
|
+
|
|
1171
|
+
### Secret Properties
|
|
1172
|
+
|
|
1173
|
+
| Property | Description |
|
|
1174
|
+
|----------|-------------|
|
|
1175
|
+
| \`description\` | What this secret is for (shown to user) |
|
|
1176
|
+
| \`required\` | If \`true\`, verification fails without it |
|
|
1177
|
+
| \`services\` | Which services need this secret (for scoping) |
|
|
1178
|
+
|
|
1179
|
+
---
|
|
1180
|
+
|
|
1181
|
+
## Step 4: Guide User to Store Secrets
|
|
1182
|
+
|
|
1183
|
+
### Option A: Haystack CLI (Recommended)
|
|
1184
|
+
|
|
1185
|
+
\`\`\`bash
|
|
1186
|
+
# Store secrets securely (encrypted at rest)
|
|
1187
|
+
npx @haystackeditor/cli secrets set OPENAI_API_KEY
|
|
1188
|
+
# Prompts for value, never shown in terminal history
|
|
1189
|
+
|
|
1190
|
+
# Verify it's stored
|
|
1191
|
+
npx @haystackeditor/cli secrets list
|
|
1192
|
+
\`\`\`
|
|
1193
|
+
|
|
1194
|
+
### Option B: Environment Variables (Local Development)
|
|
1195
|
+
|
|
1196
|
+
\`\`\`bash
|
|
1197
|
+
# Add to .env.local (gitignored)
|
|
1198
|
+
echo 'OPENAI_API_KEY=sk-...' >> .env.local
|
|
1199
|
+
|
|
1200
|
+
# Make sure .env.local is in .gitignore
|
|
1201
|
+
grep -q '.env.local' .gitignore || echo '.env.local' >> .gitignore
|
|
1202
|
+
\`\`\`
|
|
1203
|
+
|
|
1204
|
+
### Option C: CI/CD Secrets (GitHub Actions)
|
|
1205
|
+
|
|
1206
|
+
\`\`\`yaml
|
|
1207
|
+
# .github/workflows/haystack.yml
|
|
1208
|
+
env:
|
|
1209
|
+
OPENAI_API_KEY: \${{ secrets.OPENAI_API_KEY }}
|
|
1210
|
+
\`\`\`
|
|
1211
|
+
|
|
1212
|
+
---
|
|
1213
|
+
|
|
1214
|
+
## Step 5: Handle Cloud Provider Credentials
|
|
1215
|
+
|
|
1216
|
+
**Never store long-lived cloud credentials.** Use OIDC instead:
|
|
1217
|
+
|
|
1218
|
+
### AWS (Bedrock, S3, etc.)
|
|
1219
|
+
|
|
1220
|
+
\`\`\`yaml
|
|
1221
|
+
# GitHub Actions with OIDC - no secrets needed!
|
|
1222
|
+
jobs:
|
|
1223
|
+
verify:
|
|
1224
|
+
permissions:
|
|
1225
|
+
id-token: write
|
|
1226
|
+
contents: read
|
|
1227
|
+
steps:
|
|
1228
|
+
- uses: aws-actions/configure-aws-credentials@v4
|
|
1229
|
+
with:
|
|
1230
|
+
role-to-assume: arn:aws:iam::123456789:role/haystack-verify
|
|
1231
|
+
aws-region: us-west-2
|
|
1232
|
+
\`\`\`
|
|
1233
|
+
|
|
1234
|
+
### GCP (Vertex AI, Cloud Storage, etc.)
|
|
1235
|
+
|
|
1236
|
+
\`\`\`yaml
|
|
1237
|
+
jobs:
|
|
1238
|
+
verify:
|
|
1239
|
+
permissions:
|
|
1240
|
+
id-token: write
|
|
1241
|
+
steps:
|
|
1242
|
+
- uses: google-github-actions/auth@v2
|
|
1243
|
+
with:
|
|
1244
|
+
workload_identity_provider: projects/123/locations/global/...
|
|
1245
|
+
service_account: haystack-verify@project.iam.gserviceaccount.com
|
|
1246
|
+
\`\`\`
|
|
1247
|
+
|
|
1248
|
+
---
|
|
1249
|
+
|
|
1250
|
+
## Step 6: Test Secret Access
|
|
1251
|
+
|
|
1252
|
+
After storing secrets, verify they're accessible:
|
|
1253
|
+
|
|
1254
|
+
\`\`\`bash
|
|
1255
|
+
# Test locally
|
|
1256
|
+
npx @haystackeditor/cli secrets test
|
|
1257
|
+
|
|
1258
|
+
# Or check manually
|
|
1259
|
+
npx @haystackeditor/cli secrets get OPENAI_API_KEY --masked
|
|
1260
|
+
# Shows: sk-...xxxx (last 4 chars only)
|
|
1261
|
+
\`\`\`
|
|
1262
|
+
|
|
1263
|
+
---
|
|
1264
|
+
|
|
1265
|
+
## Step 7: Update .haystack.json
|
|
1266
|
+
|
|
1267
|
+
Add the secrets configuration:
|
|
1268
|
+
|
|
1269
|
+
\`\`\`bash
|
|
1270
|
+
# Show what to add
|
|
1271
|
+
cat << 'EOF'
|
|
1272
|
+
Add this to your .haystack.json:
|
|
1273
|
+
|
|
1274
|
+
{
|
|
1275
|
+
"secrets": {
|
|
1276
|
+
"OPENAI_API_KEY": {
|
|
1277
|
+
"description": "OpenAI API key for LLM analysis",
|
|
1278
|
+
"required": true
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
EOF
|
|
1283
|
+
\`\`\`
|
|
1284
|
+
|
|
1285
|
+
---
|
|
1286
|
+
|
|
1287
|
+
## Common Patterns
|
|
1288
|
+
|
|
1289
|
+
### Analysis Pipeline with LLM
|
|
1290
|
+
|
|
1291
|
+
\`\`\`json
|
|
1292
|
+
{
|
|
1293
|
+
"services": {
|
|
1294
|
+
"analysis": {
|
|
1295
|
+
"root": "packages/analysis",
|
|
1296
|
+
"command": "pnpm start",
|
|
1297
|
+
"env": {
|
|
1298
|
+
"PR_IDENTIFIER": "$PR_IDENTIFIER"
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
},
|
|
1302
|
+
"secrets": {
|
|
1303
|
+
"OPENAI_API_KEY": {
|
|
1304
|
+
"description": "OpenAI API for code analysis",
|
|
1305
|
+
"required": true,
|
|
1306
|
+
"services": ["analysis"]
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
\`\`\`
|
|
1311
|
+
|
|
1312
|
+
### Multiple LLM Providers
|
|
1313
|
+
|
|
1314
|
+
\`\`\`json
|
|
1315
|
+
{
|
|
1316
|
+
"secrets": {
|
|
1317
|
+
"OPENAI_API_KEY": {
|
|
1318
|
+
"description": "OpenAI for embeddings",
|
|
1319
|
+
"services": ["search"]
|
|
1320
|
+
},
|
|
1321
|
+
"ANTHROPIC_API_KEY": {
|
|
1322
|
+
"description": "Claude for code review",
|
|
1323
|
+
"services": ["review"]
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
\`\`\`
|
|
1328
|
+
|
|
1329
|
+
### Database + API Keys
|
|
1330
|
+
|
|
1331
|
+
\`\`\`json
|
|
1332
|
+
{
|
|
1333
|
+
"secrets": {
|
|
1334
|
+
"DATABASE_URL": {
|
|
1335
|
+
"description": "PostgreSQL for test database",
|
|
1336
|
+
"required": true
|
|
1337
|
+
},
|
|
1338
|
+
"STRIPE_TEST_KEY": {
|
|
1339
|
+
"description": "Stripe test mode key",
|
|
1340
|
+
"required": false
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
\`\`\`
|
|
1345
|
+
|
|
1346
|
+
---
|
|
1347
|
+
|
|
1348
|
+
## Security Best Practices
|
|
1349
|
+
|
|
1350
|
+
1. **Use test/sandbox credentials** when available (OpenAI, Stripe, etc. offer these)
|
|
1351
|
+
2. **Set usage limits** on API keys used for verification
|
|
1352
|
+
3. **Never commit secrets** - use \`.gitignore\` for \`.env.local\`
|
|
1353
|
+
4. **Rotate regularly** - especially if verification runs on external PRs
|
|
1354
|
+
5. **Scope narrowly** - use \`services\` array to limit which services see which secrets
|
|
1355
|
+
6. **Prefer OIDC** for cloud providers instead of static credentials
|
|
1356
|
+
|
|
1357
|
+
---
|
|
1358
|
+
|
|
1359
|
+
## Troubleshooting
|
|
1360
|
+
|
|
1361
|
+
### "Secret not found" errors
|
|
1362
|
+
|
|
1363
|
+
\`\`\`bash
|
|
1364
|
+
# Check if secret is stored
|
|
1365
|
+
npx @haystackeditor/cli secrets list
|
|
1366
|
+
|
|
1367
|
+
# Check if it's in the right scope
|
|
1368
|
+
npx @haystackeditor/cli secrets get SECRET_NAME --show-metadata
|
|
1369
|
+
\`\`\`
|
|
1370
|
+
|
|
1371
|
+
### "Permission denied" for cloud resources
|
|
1372
|
+
|
|
1373
|
+
- Verify OIDC trust policy includes your repo
|
|
1374
|
+
- Check IAM role has required permissions
|
|
1375
|
+
- Ensure GitHub Actions has \`id-token: write\` permission
|
|
1376
|
+
|
|
1377
|
+
### Secrets work locally but not in CI
|
|
1378
|
+
|
|
1379
|
+
- Secrets stored via CLI are user-scoped by default
|
|
1380
|
+
- For CI, use GitHub Secrets or organization-level Haystack secrets
|
|
1381
|
+
- Check \`npx @haystackeditor/cli secrets list --scope=org\`
|
|
1382
|
+
`;
|
|
919
1383
|
export async function createSkillFile() {
|
|
920
1384
|
const skillDir = path.join(process.cwd(), '.agents', 'skills');
|
|
921
1385
|
const setupPath = path.join(skillDir, 'setup-haystack.md');
|
|
922
1386
|
const refPath = path.join(skillDir, 'haystack-reference.md');
|
|
923
1387
|
const prepPath = path.join(skillDir, 'prepare-haystack.md');
|
|
1388
|
+
const secretsPath = path.join(skillDir, 'setup-haystack-secrets.md');
|
|
924
1389
|
// Create directory if needed
|
|
925
1390
|
await fs.mkdir(skillDir, { recursive: true });
|
|
926
1391
|
// Write all skill files
|
|
927
1392
|
await fs.writeFile(setupPath, SKILL_CONTENT, 'utf-8');
|
|
928
1393
|
await fs.writeFile(refPath, REFERENCE_CONTENT, 'utf-8');
|
|
929
1394
|
await fs.writeFile(prepPath, PREPARE_VERIFICATION_CONTENT, 'utf-8');
|
|
1395
|
+
await fs.writeFile(secretsPath, SECRETS_SKILL_CONTENT, 'utf-8');
|
|
930
1396
|
return setupPath;
|
|
931
1397
|
}
|
|
932
1398
|
/**
|
|
933
1399
|
* Create the .claude/commands/ files for Claude Code slash commands
|
|
934
|
-
* Users can invoke with /setup-haystack or /
|
|
1400
|
+
* Users can invoke with /setup-haystack, /prepare-haystack, or /setup-haystack-secrets
|
|
935
1401
|
*/
|
|
936
1402
|
export async function createClaudeCommand() {
|
|
937
1403
|
const commandDir = path.join(process.cwd(), '.claude', 'commands');
|
|
938
1404
|
const setupPath = path.join(commandDir, 'setup-haystack.md');
|
|
939
1405
|
const prepPath = path.join(commandDir, 'prepare-haystack.md');
|
|
1406
|
+
const secretsPath = path.join(commandDir, 'setup-haystack-secrets.md');
|
|
940
1407
|
// Create directory if needed
|
|
941
1408
|
await fs.mkdir(commandDir, { recursive: true });
|
|
942
1409
|
// Write command files
|
|
943
1410
|
await fs.writeFile(setupPath, CLAUDE_COMMAND_CONTENT, 'utf-8');
|
|
944
1411
|
await fs.writeFile(prepPath, PREPARE_HAYSTACK_COMMAND, 'utf-8');
|
|
1412
|
+
await fs.writeFile(secretsPath, SECRETS_COMMAND_CONTENT, 'utf-8');
|
|
945
1413
|
return setupPath;
|
|
946
1414
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haystackeditor/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Set up Haystack verification for your project",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"haystack": "./dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"build": "tsc",
|
|
10
|
+
"build": "tsc && rm -rf dist/assets && cp -r src/assets dist/assets",
|
|
11
11
|
"dev": "tsc --watch",
|
|
12
12
|
"start": "node dist/index.js",
|
|
13
13
|
"prepublishOnly": "npm run build"
|