@bilalimamoglu/sift 0.4.1 → 0.4.3
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 +171 -199
- package/dist/cli.js +36 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,26 +1,45 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="assets/brand/sift-logo-minimal-teal-default.svg" alt="sift logo" width="220" />
|
|
4
|
+
|
|
1
5
|
# sift
|
|
2
6
|
|
|
7
|
+
### Turn noisy command output into actionable diagnoses for your coding agent
|
|
8
|
+
|
|
9
|
+
**Benchmark-backed test triage - Heuristic-first reductions - Agent-ready terminal workflows**
|
|
10
|
+
|
|
3
11
|
[](https://www.npmjs.com/package/@bilalimamoglu/sift)
|
|
4
12
|
[](LICENSE)
|
|
5
13
|
[](https://github.com/bilalimamoglu/sift/actions/workflows/ci.yml)
|
|
14
|
+
[](https://nodejs.org/)
|
|
6
15
|
|
|
7
|
-
<
|
|
16
|
+
<br />
|
|
8
17
|
|
|
9
|
-
|
|
18
|
+
### Get Started
|
|
10
19
|
|
|
11
|
-
|
|
20
|
+
```bash
|
|
21
|
+
npm install -g @bilalimamoglu/sift
|
|
22
|
+
```
|
|
12
23
|
|
|
13
|
-
|
|
24
|
+
<sub>Works with pytest, vitest, jest, tsc, ESLint, webpack, Cargo, terraform, npm audit, and more.</sub>
|
|
14
25
|
|
|
15
|
-
|
|
16
|
-
- the root cause
|
|
17
|
-
- where it happens
|
|
18
|
-
- what to fix
|
|
19
|
-
- what to do next
|
|
26
|
+
</div>
|
|
20
27
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Why Sift?
|
|
31
|
+
|
|
32
|
+
When an agent hits noisy output, it burns budget reading logs instead of fixing the problem.
|
|
33
|
+
|
|
34
|
+
`sift` sits in front of that output and reduces it into a small, actionable first pass. Your agent reads the diagnosis, not the wall of text.
|
|
35
|
+
|
|
36
|
+
Turn 13,000 lines of test output into 2 root causes.
|
|
37
|
+
|
|
38
|
+
<p align="center">
|
|
39
|
+
<img src="assets/readme/test-status-demo.gif" alt="sift turning a pytest failure wall into a short diagnosis" width="960" />
|
|
40
|
+
</p>
|
|
41
|
+
|
|
42
|
+
With `sift`, the same run becomes:
|
|
24
43
|
|
|
25
44
|
```text
|
|
26
45
|
- Tests did not pass.
|
|
@@ -34,272 +53,213 @@ sift exec --preset test-status -- pytest -q
|
|
|
34
53
|
- Decision: stop and act.
|
|
35
54
|
```
|
|
36
55
|
|
|
37
|
-
|
|
38
|
-
`198K` raw-output tokens -> `129` `standard` tokens.
|
|
56
|
+
In the largest benchmark fixture, sift compressed 198,026 raw output tokens to 129. That is what the agent reads instead of the full log.
|
|
39
57
|
|
|
40
|
-
|
|
58
|
+
---
|
|
41
59
|
|
|
42
|
-
##
|
|
60
|
+
## Benchmark Results
|
|
43
61
|
|
|
44
|
-
|
|
62
|
+
The output reduction above measures a single command's raw output. The table below measures the full end-to-end debug session: how many tokens, tool calls, and seconds the agent spends to reach the same diagnosis.
|
|
45
63
|
|
|
46
|
-
|
|
64
|
+
Real debug loop on a 640-test Python backend with 124 repeated setup errors, 3 contract failures, and 511 passing tests:
|
|
47
65
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
66
|
+
| Metric | Without sift | With sift | Reduction |
|
|
67
|
+
|--------|-------------:|----------:|----------:|
|
|
68
|
+
| Tokens | 52,944 | 20,049 | 62% fewer |
|
|
69
|
+
| Tool calls | 40.8 | 12 | 71% fewer |
|
|
70
|
+
| Wall-clock time | 244s | 85s | 65% faster |
|
|
71
|
+
| Commands | 15.5 | 6 | 61% fewer |
|
|
72
|
+
| Diagnosis | Same | Same | Same outcome |
|
|
51
73
|
|
|
52
|
-
|
|
74
|
+
Same diagnosis, less agent thrash.
|
|
53
75
|
|
|
54
|
-
|
|
76
|
+
Methodology and caveats: [BENCHMARK_NOTES.md](BENCHMARK_NOTES.md)
|
|
55
77
|
|
|
56
|
-
|
|
78
|
+
---
|
|
57
79
|
|
|
58
|
-
|
|
59
|
-
export OPENAI_API_KEY=your_openai_api_key
|
|
60
|
-
sift exec --preset test-status -- pytest -q
|
|
61
|
-
```
|
|
80
|
+
## How It Works
|
|
62
81
|
|
|
63
|
-
|
|
82
|
+
`sift` keeps the explanation simple:
|
|
64
83
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
84
|
+
1. **Capture output.** Run the noisy command or accept already-existing piped output.
|
|
85
|
+
2. **Run local heuristics.** Detect known failure shapes first so common cases stay cheap and deterministic.
|
|
86
|
+
3. **Return the diagnosis.** When heuristics are confident, `sift` gives the agent the root cause, anchor, and next step.
|
|
87
|
+
4. **Fall back only when needed.** If heuristics are not enough, `sift` uses a cheaper model instead of spending your main agent budget.
|
|
68
88
|
|
|
69
|
-
|
|
89
|
+
Your agent spends tokens fixing, not reading.
|
|
70
90
|
|
|
71
|
-
|
|
91
|
+
---
|
|
72
92
|
|
|
73
|
-
|
|
74
|
-
sift config setup
|
|
75
|
-
sift doctor
|
|
76
|
-
```
|
|
93
|
+
## Key Features
|
|
77
94
|
|
|
78
|
-
|
|
95
|
+
<table>
|
|
96
|
+
<tr>
|
|
97
|
+
<td width="33%" valign="top">
|
|
79
98
|
|
|
80
|
-
|
|
99
|
+
### Test Failure Triage
|
|
100
|
+
Collapse repeated pytest, vitest, and jest failures into a short diagnosis with root-cause buckets, anchors, and fix hints.
|
|
81
101
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
sift agent install claude
|
|
85
|
-
```
|
|
102
|
+
</td>
|
|
103
|
+
<td width="33%" valign="top">
|
|
86
104
|
|
|
87
|
-
|
|
105
|
+
### Typecheck and Lint Reduction
|
|
106
|
+
Group noisy `tsc` and ESLint output into the few issues that actually matter instead of dumping the whole log back into the model.
|
|
88
107
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
sift exec "what changed?" -- git diff
|
|
92
|
-
sift exec --preset audit-critical -- npm audit
|
|
93
|
-
sift exec --preset infra-risk -- terraform plan
|
|
94
|
-
```
|
|
108
|
+
</td>
|
|
109
|
+
<td width="33%" valign="top">
|
|
95
110
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
- `--show-raw` to print captured raw output to `stderr`
|
|
99
|
-
- `--fail-on` to let reduced results fail CI for commands such as `npm audit` or `terraform plan`
|
|
111
|
+
### Build Failure Extraction
|
|
112
|
+
Pull out the first concrete error from webpack, esbuild/Vite, Cargo, Go, GCC/Clang, and similar build output.
|
|
100
113
|
|
|
101
|
-
|
|
114
|
+
</td>
|
|
115
|
+
</tr>
|
|
116
|
+
<tr>
|
|
117
|
+
<td width="33%" valign="top">
|
|
102
118
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
export OPENAI_API_KEY=your_openai_api_key
|
|
109
|
-
|
|
110
|
-
# OpenRouter
|
|
111
|
-
export SIFT_PROVIDER=openrouter
|
|
112
|
-
export OPENROUTER_API_KEY=your_openrouter_api_key
|
|
113
|
-
|
|
114
|
-
# Any OpenAI-compatible endpoint
|
|
115
|
-
export SIFT_PROVIDER=openai-compatible
|
|
116
|
-
export SIFT_BASE_URL=https://your-endpoint/v1
|
|
117
|
-
export SIFT_PROVIDER_API_KEY=your_api_key
|
|
118
|
-
```
|
|
119
|
+
### Audit and Infra Risk
|
|
120
|
+
Surface high-impact `npm audit` findings and destructive `terraform plan` signals without making the agent read everything.
|
|
121
|
+
|
|
122
|
+
</td>
|
|
123
|
+
<td width="33%" valign="top">
|
|
119
124
|
|
|
120
|
-
|
|
125
|
+
### Heuristic-First by Default
|
|
126
|
+
Every built-in preset tries local parsing first. When the heuristic handles the output, no provider call is needed.
|
|
121
127
|
|
|
122
|
-
|
|
128
|
+
</td>
|
|
129
|
+
<td width="33%" valign="top">
|
|
123
130
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
- an affected count
|
|
127
|
-
- an anchor
|
|
128
|
-
- a likely fix
|
|
129
|
-
- a decision signal
|
|
131
|
+
### Agent and Automation Friendly
|
|
132
|
+
Use `sift` in Codex, Claude, CI, hooks, or shell scripts so downstream tooling gets short, structured answers instead of raw noise.
|
|
130
133
|
|
|
131
|
-
|
|
134
|
+
</td>
|
|
135
|
+
</tr>
|
|
136
|
+
</table>
|
|
132
137
|
|
|
133
|
-
|
|
138
|
+
---
|
|
134
139
|
|
|
135
|
-
|
|
140
|
+
## Setup and Agent Integration
|
|
136
141
|
|
|
137
|
-
|
|
138
|
-
2. Sanitize sensitive-looking material.
|
|
139
|
-
3. Apply local heuristics for known failure shapes.
|
|
140
|
-
4. Escalate to a cheaper provider only if needed.
|
|
141
|
-
5. Return a short diagnosis to the main agent.
|
|
142
|
+
Most built-in presets run entirely on local heuristics with no API key needed. For presets that fall back to a model (`diff-summary`, `log-errors`, or when heuristics are not confident enough), sift supports OpenAI-compatible and OpenRouter-compatible endpoints.
|
|
142
143
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
Set up the provider first, then install the managed instruction block for the agent you want to steer:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
sift config setup
|
|
148
|
+
sift doctor
|
|
149
|
+
sift agent install codex
|
|
150
|
+
sift agent install claude
|
|
151
|
+
```
|
|
147
152
|
|
|
148
|
-
|
|
153
|
+
You can also preview, inspect, or remove those blocks:
|
|
149
154
|
|
|
150
|
-
|
|
155
|
+
```bash
|
|
156
|
+
sift agent show codex
|
|
157
|
+
sift agent status
|
|
158
|
+
sift agent remove codex
|
|
159
|
+
```
|
|
151
160
|
|
|
152
|
-
|
|
161
|
+
Command-first details live in [docs/cli-reference.md](docs/cli-reference.md).
|
|
153
162
|
|
|
154
|
-
|
|
163
|
+
---
|
|
155
164
|
|
|
156
|
-
|
|
157
|
-
|--------|-----------|-------------|
|
|
158
|
-
| `test-status` | Deep | Bucket/anchor/decision system for pytest, vitest, jest. 30+ failure patterns, confidence-gated stop/zoom decisions. |
|
|
159
|
-
| `typecheck-summary` | Deterministic | Parses `tsc` output (standard and pretty formats), groups by error code, returns max 5 bullets. |
|
|
160
|
-
| `lint-failures` | Deterministic | Parses ESLint stylish output, groups by rule, distinguishes errors from warnings, detects fixable hints. |
|
|
161
|
-
| `audit-critical` | Deterministic | Extracts high/critical vulnerabilities from `npm audit` or similar. |
|
|
162
|
-
| `infra-risk` | Deterministic | Detects destructive signals in `terraform plan` output. Returns pass/fail verdict. |
|
|
163
|
-
| `build-failure` | Deterministic-first | Extracts the first concrete build error for recognized webpack, esbuild/Vite, Cargo, Go, GCC/Clang, and `tsc --build` output; falls back to the provider for unsupported formats. |
|
|
164
|
-
| `diff-summary` | Provider | Summarizes changes and risks in diff output. |
|
|
165
|
-
| `log-errors` | Provider | Extracts top error signals from log output. |
|
|
165
|
+
## Quick Start
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
### 1. Install
|
|
168
168
|
|
|
169
169
|
```bash
|
|
170
|
-
|
|
171
|
-
sift exec --preset lint-failures -- npx eslint src/
|
|
172
|
-
sift exec --preset build-failure -- npm run build
|
|
173
|
-
sift exec --preset audit-critical -- npm audit
|
|
174
|
-
sift exec --preset infra-risk -- terraform plan
|
|
170
|
+
npm install -g @bilalimamoglu/sift
|
|
175
171
|
```
|
|
176
172
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
```text
|
|
180
|
-
[sift: heuristic • LLM skipped • summary 47ms]
|
|
181
|
-
[sift: provider • LLM used • 380 tokens • summary 1.2s]
|
|
182
|
-
```
|
|
173
|
+
Requires Node.js 20+.
|
|
183
174
|
|
|
184
|
-
|
|
175
|
+
### 2. Run Sift in front of a noisy command
|
|
185
176
|
|
|
186
177
|
```bash
|
|
187
|
-
sift exec --preset
|
|
178
|
+
sift exec --preset test-status -- pytest -q
|
|
188
179
|
```
|
|
189
180
|
|
|
190
|
-
|
|
181
|
+
Other common entry points:
|
|
191
182
|
|
|
192
|
-
|
|
193
|
-
-
|
|
194
|
-
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
Best fits today:
|
|
199
|
-
- large `pytest`, `vitest`, or `jest` runs
|
|
200
|
-
- `tsc` type errors and `eslint` lint failures
|
|
201
|
-
- build failures from webpack, esbuild/Vite, Cargo, Go, GCC/Clang
|
|
202
|
-
- `npm audit` and `terraform plan`
|
|
203
|
-
- repeated CI blockers
|
|
204
|
-
- noisy diffs and log streams
|
|
183
|
+
```bash
|
|
184
|
+
sift exec --preset test-status -- npx vitest run
|
|
185
|
+
sift exec --preset test-status -- npx jest
|
|
186
|
+
sift exec "what changed?" -- git diff
|
|
187
|
+
```
|
|
205
188
|
|
|
206
|
-
|
|
189
|
+
### 3. Zoom only if needed
|
|
207
190
|
|
|
208
|
-
|
|
191
|
+
Think of the workflow like this:
|
|
209
192
|
|
|
210
|
-
Think of it like this:
|
|
211
193
|
- `standard` = map
|
|
212
194
|
- `focused` = zoom
|
|
213
195
|
- raw traceback = last resort
|
|
214
196
|
|
|
215
|
-
Typical loop:
|
|
216
|
-
|
|
217
197
|
```bash
|
|
218
|
-
sift exec --preset test-status -- <test command>
|
|
219
198
|
sift rerun
|
|
220
199
|
sift rerun --remaining --detail focused
|
|
221
200
|
```
|
|
222
201
|
|
|
223
202
|
If `standard` already gives you the root cause, anchor, and fix, stop there and act.
|
|
224
203
|
|
|
225
|
-
|
|
204
|
+
---
|
|
226
205
|
|
|
227
|
-
|
|
206
|
+
## Presets
|
|
228
207
|
|
|
229
|
-
|
|
208
|
+
| Preset | What it does | Needs provider? |
|
|
209
|
+
|--------|--------------|:---------------:|
|
|
210
|
+
| `test-status` | Groups pytest, vitest, and jest failures into root-cause buckets with anchors and fix suggestions. | No |
|
|
211
|
+
| `typecheck-summary` | Parses `tsc` output and groups issues by error code. | No |
|
|
212
|
+
| `lint-failures` | Parses ESLint output and groups failures by rule. | No |
|
|
213
|
+
| `build-failure` | Extracts the first concrete build error from common toolchains. | Fallback only |
|
|
214
|
+
| `audit-critical` | Pulls high and critical `npm audit` findings. | No |
|
|
215
|
+
| `infra-risk` | Detects destructive signals in `terraform plan`. | No |
|
|
216
|
+
| `diff-summary` | Summarizes change sets and likely risks in diff output. | Yes |
|
|
217
|
+
| `log-errors` | Extracts the strongest error signals from noisy logs. | Fallback only |
|
|
218
|
+
|
|
219
|
+
When output already exists in a pipeline, use pipe mode instead of `exec`:
|
|
230
220
|
|
|
231
221
|
```bash
|
|
232
|
-
sift
|
|
233
|
-
sift
|
|
234
|
-
sift agent remove claude
|
|
222
|
+
pytest -q 2>&1 | sift preset test-status
|
|
223
|
+
npm audit 2>&1 | sift preset audit-critical
|
|
235
224
|
```
|
|
236
225
|
|
|
237
|
-
|
|
226
|
+
---
|
|
238
227
|
|
|
239
|
-
|
|
240
|
-
- the output is already short and obvious
|
|
241
|
-
- the command is interactive or TUI-based
|
|
242
|
-
- the exact raw log matters
|
|
243
|
-
- the output does not expose enough evidence for reliable grouping
|
|
228
|
+
## Test Debugging Workflow
|
|
244
229
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
## Benchmark
|
|
248
|
-
|
|
249
|
-
On a real 640-test Python backend (125 repeated setup errors, 3 contract failures, 510 passing tests):
|
|
250
|
-
|
|
251
|
-
| Metric | Raw agent | sift-first | Reduction |
|
|
252
|
-
|--------|-----------|------------|-----------|
|
|
253
|
-
| Tokens | 305K | 600 | 99.8% |
|
|
254
|
-
| Tool calls | 16 | 7 | 56% |
|
|
255
|
-
| Diagnosis | Same | Same | — |
|
|
256
|
-
|
|
257
|
-
The table above is the single-fixture reduction story: the largest real test log in the benchmark shrank from `198026` raw tokens to `129` `standard` tokens.
|
|
258
|
-
|
|
259
|
-
The end-to-end workflow benchmark is a different metric:
|
|
260
|
-
- `62%` fewer total debugging tokens
|
|
261
|
-
- `71%` fewer tool calls
|
|
262
|
-
- `65%` faster wall-clock time
|
|
263
|
-
|
|
264
|
-
Both matter. The table shows how aggressively `sift` can compress one large noisy run. The workflow numbers show how that compounds across a full debug loop.
|
|
265
|
-
|
|
266
|
-
Methodology and caveats live in [BENCHMARK_NOTES.md](BENCHMARK_NOTES.md).
|
|
267
|
-
|
|
268
|
-
## Configuration
|
|
269
|
-
|
|
270
|
-
Inspect and validate config with:
|
|
230
|
+
For noisy test failures, start with the `test-status` preset and let `standard` be the default stop point.
|
|
271
231
|
|
|
272
232
|
```bash
|
|
273
|
-
sift
|
|
274
|
-
sift
|
|
275
|
-
sift
|
|
233
|
+
sift exec --preset test-status -- <test command>
|
|
234
|
+
sift rerun
|
|
235
|
+
sift rerun --remaining --detail focused
|
|
236
|
+
sift rerun --remaining --detail verbose --show-raw
|
|
276
237
|
```
|
|
277
238
|
|
|
278
|
-
|
|
239
|
+
Useful rules of thumb:
|
|
240
|
+
|
|
241
|
+
- If `standard` ends with `Decision: stop and act`, go read source and fix the issue.
|
|
242
|
+
- Use `sift rerun` after a change to refresh the same test command at `standard`.
|
|
243
|
+
- Use `sift rerun --remaining` to zoom into what still fails after the first pass.
|
|
244
|
+
- Treat raw traceback as the last resort, not the starting point.
|
|
245
|
+
|
|
246
|
+
For machine branching or automation, `test-status` also supports diagnose JSON:
|
|
279
247
|
|
|
280
248
|
```bash
|
|
281
|
-
sift
|
|
282
|
-
sift
|
|
249
|
+
sift exec --preset test-status --goal diagnose --format json -- pytest -q
|
|
250
|
+
sift rerun --goal diagnose --format json
|
|
283
251
|
```
|
|
284
252
|
|
|
285
|
-
|
|
253
|
+
---
|
|
286
254
|
|
|
287
|
-
|
|
288
|
-
provider:
|
|
289
|
-
provider: openai
|
|
290
|
-
model: gpt-5-nano
|
|
291
|
-
baseUrl: https://api.openai.com/v1
|
|
292
|
-
apiKey: YOUR_API_KEY
|
|
255
|
+
## Limitations
|
|
293
256
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
maxInputChars: 60000
|
|
257
|
+
- sift adds the most value when output is long, repetitive, and shaped by a small number of root causes. For short, obvious failures it may not save much.
|
|
258
|
+
- The deepest local heuristic coverage is in test debugging (pytest, vitest, jest). Other presets have solid heuristics but less depth.
|
|
259
|
+
- sift does not help with interactive or TUI-based commands.
|
|
260
|
+
- When heuristics cannot explain the output confidently, sift falls back to a provider. If no provider is configured, it returns what the heuristics could extract and signals that raw output may still be needed.
|
|
299
261
|
|
|
300
|
-
|
|
301
|
-
rawFallback: true
|
|
302
|
-
```
|
|
262
|
+
---
|
|
303
263
|
|
|
304
264
|
## Docs
|
|
305
265
|
|
|
@@ -308,6 +268,18 @@ runtime:
|
|
|
308
268
|
- Benchmark methodology: [BENCHMARK_NOTES.md](BENCHMARK_NOTES.md)
|
|
309
269
|
- Release notes: [release-notes](release-notes)
|
|
310
270
|
|
|
271
|
+
---
|
|
272
|
+
|
|
311
273
|
## License
|
|
312
274
|
|
|
313
275
|
MIT
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
<div align="center">
|
|
280
|
+
|
|
281
|
+
Built for agent-first terminal workflows.
|
|
282
|
+
|
|
283
|
+
[Report Bug](https://github.com/bilalimamoglu/sift/issues) | [Request Feature](https://github.com/bilalimamoglu/sift/issues)
|
|
284
|
+
|
|
285
|
+
</div>
|
package/dist/cli.js
CHANGED
|
@@ -485,7 +485,14 @@ function writeExampleConfig(options = {}) {
|
|
|
485
485
|
}
|
|
486
486
|
const yaml = YAML2.stringify(defaultConfig);
|
|
487
487
|
fs2.mkdirSync(path3.dirname(resolved), { recursive: true });
|
|
488
|
-
fs2.writeFileSync(resolved, yaml,
|
|
488
|
+
fs2.writeFileSync(resolved, yaml, {
|
|
489
|
+
encoding: "utf8",
|
|
490
|
+
mode: 384
|
|
491
|
+
});
|
|
492
|
+
try {
|
|
493
|
+
fs2.chmodSync(resolved, 384);
|
|
494
|
+
} catch {
|
|
495
|
+
}
|
|
489
496
|
return resolved;
|
|
490
497
|
}
|
|
491
498
|
function writeConfigFile(options) {
|
|
@@ -1807,8 +1814,29 @@ function escapeRegExp(value) {
|
|
|
1807
1814
|
}
|
|
1808
1815
|
|
|
1809
1816
|
// src/commands/doctor.ts
|
|
1817
|
+
var PLACEHOLDER_API_KEYS = [
|
|
1818
|
+
"YOUR_API_KEY",
|
|
1819
|
+
"your_api_key",
|
|
1820
|
+
"your-api-key",
|
|
1821
|
+
"sk-xxx",
|
|
1822
|
+
"sk-placeholder",
|
|
1823
|
+
"CHANGE_ME",
|
|
1824
|
+
"change_me",
|
|
1825
|
+
"TODO",
|
|
1826
|
+
"todo",
|
|
1827
|
+
"xxx",
|
|
1828
|
+
"XXX"
|
|
1829
|
+
];
|
|
1830
|
+
function isPlaceholderApiKey(key) {
|
|
1831
|
+
if (!key) return false;
|
|
1832
|
+
return PLACEHOLDER_API_KEYS.includes(key.trim());
|
|
1833
|
+
}
|
|
1834
|
+
function isRealApiKey(key) {
|
|
1835
|
+
return Boolean(key) && !isPlaceholderApiKey(key);
|
|
1836
|
+
}
|
|
1810
1837
|
function runDoctor(config, configPath) {
|
|
1811
1838
|
const ui = createPresentation(Boolean(process.stdout.isTTY));
|
|
1839
|
+
const apiKeyStatus = isRealApiKey(config.provider.apiKey) ? "set" : isPlaceholderApiKey(config.provider.apiKey) ? "placeholder (not a real key)" : "not set";
|
|
1812
1840
|
const lines = [
|
|
1813
1841
|
"sift doctor",
|
|
1814
1842
|
"A quick check for your local setup.",
|
|
@@ -1817,7 +1845,7 @@ function runDoctor(config, configPath) {
|
|
|
1817
1845
|
ui.labelValue("provider", config.provider.provider),
|
|
1818
1846
|
ui.labelValue("model", config.provider.model),
|
|
1819
1847
|
ui.labelValue("baseUrl", config.provider.baseUrl),
|
|
1820
|
-
ui.labelValue("apiKey",
|
|
1848
|
+
ui.labelValue("apiKey", apiKeyStatus),
|
|
1821
1849
|
ui.labelValue("maxCaptureChars", String(config.input.maxCaptureChars)),
|
|
1822
1850
|
ui.labelValue("maxInputChars", String(config.input.maxInputChars)),
|
|
1823
1851
|
ui.labelValue("rawFallback", String(config.runtime.rawFallback))
|
|
@@ -1831,8 +1859,12 @@ function runDoctor(config, configPath) {
|
|
|
1831
1859
|
if (!config.provider.model) {
|
|
1832
1860
|
problems.push("Missing provider.model");
|
|
1833
1861
|
}
|
|
1834
|
-
if ((config.provider.provider === "openai" || config.provider.provider === "openai-compatible" || config.provider.provider === "openrouter") && !config.provider.apiKey) {
|
|
1835
|
-
|
|
1862
|
+
if ((config.provider.provider === "openai" || config.provider.provider === "openai-compatible" || config.provider.provider === "openrouter") && !isRealApiKey(config.provider.apiKey)) {
|
|
1863
|
+
if (isPlaceholderApiKey(config.provider.apiKey)) {
|
|
1864
|
+
problems.push(`provider.apiKey looks like a placeholder: "${config.provider.apiKey}"`);
|
|
1865
|
+
} else {
|
|
1866
|
+
problems.push("Missing provider.apiKey");
|
|
1867
|
+
}
|
|
1836
1868
|
problems.push(
|
|
1837
1869
|
`Set one of: ${getProviderApiKeyEnvNames(
|
|
1838
1870
|
config.provider.provider,
|