@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.
Files changed (3) hide show
  1. package/README.md +171 -199
  2. package/dist/cli.js +36 -4
  3. 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
  [![npm version](https://img.shields.io/npm/v/@bilalimamoglu/sift)](https://www.npmjs.com/package/@bilalimamoglu/sift)
4
12
  [![license](https://img.shields.io/github/license/bilalimamoglu/sift)](LICENSE)
5
13
  [![CI](https://img.shields.io/github/actions/workflow/status/bilalimamoglu/sift/ci.yml?branch=main&label=CI)](https://github.com/bilalimamoglu/sift/actions/workflows/ci.yml)
14
+ [![Node.js](https://img.shields.io/badge/Node.js-20+-green.svg)](https://nodejs.org/)
6
15
 
7
- <img src="assets/brand/sift-logo-minimal-teal-default.svg" alt="sift logo" width="140" />
16
+ <br />
8
17
 
9
- Your AI agent should not be reading 13,000 lines of test output.
18
+ ### Get Started
10
19
 
11
- If 125 tests fail for one reason, it should pay for that reason once.
20
+ ```bash
21
+ npm install -g @bilalimamoglu/sift
22
+ ```
12
23
 
13
- `sift` turns noisy command output into a short, structured diagnosis for coding agents, so they spend fewer tokens, cost less to run, and move through debug loops faster.
24
+ <sub>Works with pytest, vitest, jest, tsc, ESLint, webpack, Cargo, terraform, npm audit, and more.</sub>
14
25
 
15
- Instead of feeding an agent thousands of lines of logs, you give it:
16
- - the root cause
17
- - where it happens
18
- - what to fix
19
- - what to do next
26
+ </div>
20
27
 
21
- ```bash
22
- sift exec --preset test-status -- pytest -q
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
- On the largest real fixture in the benchmark:
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
- Same diagnosis. Far less work.
58
+ ---
41
59
 
42
- ## What it is
60
+ ## Benchmark Results
43
61
 
44
- `sift` sits between a noisy command and a coding agent. It captures output, groups repeated failures into root-cause buckets, and returns a short diagnosis with an anchor, a likely fix, and a decision signal.
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
- ## Install
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
- ```bash
49
- npm install -g @bilalimamoglu/sift
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
- Requires Node.js 20+.
74
+ Same diagnosis, less agent thrash.
53
75
 
54
- ## Try it in 60 seconds
76
+ Methodology and caveats: [BENCHMARK_NOTES.md](BENCHMARK_NOTES.md)
55
77
 
56
- If you already have an API key, you can try `sift` without any setup wizard:
78
+ ---
57
79
 
58
- ```bash
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
- You can also use a freeform prompt for non-test output:
82
+ `sift` keeps the explanation simple:
64
83
 
65
- ```bash
66
- sift exec "what changed?" -- git diff
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
- ## Set it up for daily use
89
+ Your agent spends tokens fixing, not reading.
70
90
 
71
- Guided setup writes a machine-wide config, verifies the provider, and makes the CLI easier to use day to day:
91
+ ---
72
92
 
73
- ```bash
74
- sift config setup
75
- sift doctor
76
- ```
93
+ ## Key Features
77
94
 
78
- Config lives at `~/.config/sift/config.yaml`. A repo-local `sift.config.yaml` can override it later.
95
+ <table>
96
+ <tr>
97
+ <td width="33%" valign="top">
79
98
 
80
- If you want your coding agent to use `sift` automatically, install the managed instruction block too:
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
- ```bash
83
- sift agent install codex
84
- sift agent install claude
85
- ```
102
+ </td>
103
+ <td width="33%" valign="top">
86
104
 
87
- Then run noisy commands through `sift`:
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
- ```bash
90
- sift exec --preset test-status -- <test command>
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
- Useful flags:
97
- - `--dry-run` to preview the reduced input and prompt without calling a provider
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
- If you prefer environment variables instead of setup:
114
+ </td>
115
+ </tr>
116
+ <tr>
117
+ <td width="33%" valign="top">
102
118
 
103
- ```bash
104
- # OpenAI
105
- export SIFT_PROVIDER=openai
106
- export SIFT_BASE_URL=https://api.openai.com/v1
107
- export SIFT_MODEL=gpt-5-nano
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
- ## Why it helps
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
- The core abstraction is a **bucket**: one distinct root cause, no matter how many tests it affects.
128
+ </td>
129
+ <td width="33%" valign="top">
123
130
 
124
- Instead of making an agent reason over 125 repeated tracebacks, `sift` compresses them into one actionable bucket with:
125
- - a label
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
- That changes the agent's job from "figure out what happened" to "act on the diagnosis."
134
+ </td>
135
+ </tr>
136
+ </table>
132
137
 
133
- ## How it works
138
+ ---
134
139
 
135
- `sift` follows a cheapest-first pipeline:
140
+ ## Setup and Agent Integration
136
141
 
137
- 1. Capture command output.
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
- It also returns a decision signal:
144
- - `stop and act` when the diagnosis is already actionable
145
- - `zoom` when one deeper pass is justified
146
- - raw logs only as a last resort
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
- For recognized formats, local heuristics can fully handle the output and skip the provider entirely.
153
+ You can also preview, inspect, or remove those blocks:
149
154
 
150
- The deepest local coverage today is test debugging, especially `pytest`, with growing support for `vitest` and `jest`. Other presets cover typecheck walls, lint failures, build errors, audit output, and Terraform risk detection.
155
+ ```bash
156
+ sift agent show codex
157
+ sift agent status
158
+ sift agent remove codex
159
+ ```
151
160
 
152
- ## Built-in presets
161
+ Command-first details live in [docs/cli-reference.md](docs/cli-reference.md).
153
162
 
154
- Every preset runs local heuristics first. When the heuristic confidently handles the output, the provider is never called.
163
+ ---
155
164
 
156
- | Preset | Heuristic | What it does |
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
- Presets marked **Deterministic** bypass the provider entirely for recognized output formats. Presets marked **Deterministic-first** try a local heuristic first and fall back to the provider only when the captured output is unsupported or ambiguous. Presets marked **Provider** always call the LLM but benefit from input sanitization and truncation.
167
+ ### 1. Install
168
168
 
169
169
  ```bash
170
- sift exec --preset typecheck-summary -- npx tsc --noEmit
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
- On an interactive terminal, `sift` also shows a small stderr footer so humans can see whether the provider was skipped:
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
- Suppress the footer with `--quiet`:
175
+ ### 2. Run Sift in front of a noisy command
185
176
 
186
177
  ```bash
187
- sift exec --preset typecheck-summary --quiet -- npx tsc --noEmit
178
+ sift exec --preset test-status -- pytest -q
188
179
  ```
189
180
 
190
- ## Strongest today
181
+ Other common entry points:
191
182
 
192
- `sift` is strongest when output is:
193
- - long
194
- - repetitive
195
- - triage-heavy
196
- - shaped by a small number of shared root causes
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
- ## Test debugging workflow
189
+ ### 3. Zoom only if needed
207
190
 
208
- This is where `sift` is strongest today.
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
- `sift rerun --remaining` narrows automatically for cached `pytest` runs.
204
+ ---
226
205
 
227
- For cached `vitest` and `jest` runs, it reruns the original full command and keeps the diagnosis focused on what still fails relative to the cached baseline.
206
+ ## Presets
228
207
 
229
- For other runners, rerun a narrowed command manually with `sift exec --preset test-status -- <narrowed command>`.
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 agent status
233
- sift agent show claude
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
- ## Where it helps less
226
+ ---
238
227
 
239
- `sift` adds less value when:
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
- When it cannot be confident, it tells you to zoom or read raw instead of pretending certainty.
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 config show
274
- sift config show --show-secrets
275
- sift config validate
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
- To switch between saved providers without editing files:
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 config use openai
282
- sift config use openrouter
249
+ sift exec --preset test-status --goal diagnose --format json -- pytest -q
250
+ sift rerun --goal diagnose --format json
283
251
  ```
284
252
 
285
- Minimal YAML config:
253
+ ---
286
254
 
287
- ```yaml
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
- input:
295
- stripAnsi: true
296
- redact: false
297
- maxCaptureChars: 400000
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
- runtime:
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, "utf8");
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", config.provider.apiKey ? "set" : "not set"),
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
- problems.push("Missing provider.apiKey");
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bilalimamoglu/sift",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Agent-first command-output reduction layer for agents, CI, and automation.",
5
5
  "type": "module",
6
6
  "bin": {