@bilalimamoglu/sift 0.4.0 → 0.4.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 CHANGED
@@ -8,9 +8,15 @@
8
8
 
9
9
  Your AI agent should not be reading 13,000 lines of test output.
10
10
 
11
- On the largest real fixture in the benchmark:
12
- **Before:** 128 failures, 198K raw-output tokens, agent reconstructs the failure shape from scratch.
13
- **After:** 6 lines, 129 `standard` tokens, agent acts on a grouped diagnosis immediately.
11
+ If 125 tests fail for one reason, it should pay for that reason once.
12
+
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.
14
+
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
14
20
 
15
21
  ```bash
16
22
  sift exec --preset test-status -- pytest -q
@@ -28,13 +34,14 @@ sift exec --preset test-status -- pytest -q
28
34
  - Decision: stop and act.
29
35
  ```
30
36
 
31
- If 125 tests fail for one reason, the agent should pay for that reason once.
37
+ On the largest real fixture in the benchmark:
38
+ `198K` raw-output tokens -> `129` `standard` tokens.
32
39
 
33
- ## What it is
40
+ Same diagnosis. Far less work.
34
41
 
35
- Developers using coding agents — Claude Code, Codex, Cursor, Windsurf, Copilot, or any LLM-driven workflow that runs shell commands and reads the output.
42
+ ## What it is
36
43
 
37
- `sift` sits between the command and the agent. It captures noisy output, groups repeated failures into root-cause buckets, and returns a short diagnosis with an anchor, a likely fix, and a decision signal. The agent gets a map instead of a wall of text.
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.
38
45
 
39
46
  ## Install
40
47
 
package/dist/cli.js CHANGED
@@ -7594,7 +7594,7 @@ function buildGenericRawSlice(args) {
7594
7594
 
7595
7595
  // src/core/run.ts
7596
7596
  var RETRY_DELAY_MS = 300;
7597
- var PROVIDER_PENDING_NOTICE_DELAY_MS = 150;
7597
+ var PENDING_NOTICE_DELAY_MS = 150;
7598
7598
  function estimateTokenCount(text) {
7599
7599
  return Math.max(1, Math.ceil(text.length / 4));
7600
7600
  }
@@ -7675,17 +7675,16 @@ function buildDryRunOutput(args) {
7675
7675
  async function delay(ms) {
7676
7676
  await new Promise((resolve) => setTimeout(resolve, ms));
7677
7677
  }
7678
- function startProviderPendingNotice() {
7679
- if (!process.stderr.isTTY) {
7678
+ function startPendingNotice(message, enabled) {
7679
+ if (!enabled) {
7680
7680
  return () => {
7681
7681
  };
7682
7682
  }
7683
- const message = "sift waiting for provider...";
7684
7683
  let shown = false;
7685
7684
  const timer = setTimeout(() => {
7686
7685
  shown = true;
7687
7686
  process.stderr.write(`${message}\r`);
7688
- }, PROVIDER_PENDING_NOTICE_DELAY_MS);
7687
+ }, PENDING_NOTICE_DELAY_MS);
7689
7688
  return () => {
7690
7689
  clearTimeout(timer);
7691
7690
  if (!shown) {
@@ -7715,7 +7714,10 @@ async function generateWithRetry(args) {
7715
7714
  responseMode: args.responseMode,
7716
7715
  jsonResponseFormat: args.request.config.provider.jsonResponseFormat
7717
7716
  });
7718
- const stopPendingNotice = startProviderPendingNotice();
7717
+ const stopPendingNotice = startPendingNotice(
7718
+ "sift waiting for provider...",
7719
+ Boolean(process.stderr.isTTY)
7720
+ );
7719
7721
  try {
7720
7722
  try {
7721
7723
  return await generate();
@@ -9281,6 +9283,10 @@ async function runExec(request) {
9281
9283
  cwd: commandCwd,
9282
9284
  stdio: ["inherit", "pipe", "pipe"]
9283
9285
  });
9286
+ const stopChildPendingNotice = startPendingNotice(
9287
+ "sift waiting for child command...",
9288
+ Boolean(process.stderr.isTTY) && !request.quiet
9289
+ );
9284
9290
  const handleChunk = (chunk) => {
9285
9291
  const text = Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk);
9286
9292
  if (bypassed) {
@@ -9301,21 +9307,25 @@ async function runExec(request) {
9301
9307
  };
9302
9308
  child.stdout.on("data", handleChunk);
9303
9309
  child.stderr.on("data", handleChunk);
9304
- await new Promise((resolve, reject) => {
9305
- child.on("error", (error) => {
9306
- reject(error);
9307
- });
9308
- child.on("close", (status, signal) => {
9309
- childStatus = status;
9310
- childSignal = signal;
9311
- resolve();
9310
+ try {
9311
+ await new Promise((resolve, reject) => {
9312
+ child.on("error", (error) => {
9313
+ reject(error);
9314
+ });
9315
+ child.on("close", (status, signal) => {
9316
+ childStatus = status;
9317
+ childSignal = signal;
9318
+ resolve();
9319
+ });
9312
9320
  });
9313
- }).catch((error) => {
9321
+ } catch (error) {
9314
9322
  if (error instanceof Error) {
9315
9323
  throw error;
9316
9324
  }
9317
9325
  throw new Error("Failed to start child process.");
9318
- });
9326
+ } finally {
9327
+ stopChildPendingNotice();
9328
+ }
9319
9329
  const exitCode = normalizeChildExitCode(childStatus, childSignal);
9320
9330
  const capturedOutput = capture.render();
9321
9331
  const autoWatchDetected = !request.watch && looksLikeWatchStream(capturedOutput);
package/dist/index.d.ts CHANGED
@@ -162,6 +162,7 @@ interface RunResult {
162
162
  stats: RunStats | null;
163
163
  }
164
164
 
165
+ declare function startPendingNotice(message: string, enabled: boolean): () => void;
165
166
  declare function runSift(request: RunRequest): Promise<string>;
166
167
  declare function runSiftWithStats(request: RunRequest): Promise<RunResult>;
167
168
 
@@ -178,4 +179,4 @@ interface ResolveOptions {
178
179
  }
179
180
  declare function resolveConfig(options?: ResolveOptions): SiftConfig;
180
181
 
181
- export { BoundedCapture, type DetailLevel, type ExecRequest, type GenerateInput, type GenerateResult, type Goal, type InputConfig, type JsonResponseFormatMode, type LLMProvider, type NativeProviderName, type OutputFormat, type PartialSiftConfig, type PreparedInput, type PresetDefinition, type PromptPolicyName, type ProviderConfig, type ProviderName, type ProviderProfile, type ProviderProfiles, type RawSliceStrategy, type ResolveOptions, type ResponseMode, type RunRequest, type RuntimeConfig, type SiftConfig, type TestStatusRemainingMode, type UsageInfo, buildCommandPreview, getExecSuccessShortcut, looksInteractivePrompt, mergeDefined, normalizeChildExitCode, resolveConfig, runExec, runSift, runSiftWithStats };
182
+ export { BoundedCapture, type DetailLevel, type ExecRequest, type GenerateInput, type GenerateResult, type Goal, type InputConfig, type JsonResponseFormatMode, type LLMProvider, type NativeProviderName, type OutputFormat, type PartialSiftConfig, type PreparedInput, type PresetDefinition, type PromptPolicyName, type ProviderConfig, type ProviderName, type ProviderProfile, type ProviderProfiles, type RawSliceStrategy, type ResolveOptions, type ResponseMode, type RunRequest, type RuntimeConfig, type SiftConfig, type TestStatusRemainingMode, type UsageInfo, buildCommandPreview, getExecSuccessShortcut, looksInteractivePrompt, mergeDefined, normalizeChildExitCode, resolveConfig, runExec, runSift, runSiftWithStats, startPendingNotice };
package/dist/index.js CHANGED
@@ -5774,7 +5774,7 @@ function buildGenericRawSlice(args) {
5774
5774
 
5775
5775
  // src/core/run.ts
5776
5776
  var RETRY_DELAY_MS = 300;
5777
- var PROVIDER_PENDING_NOTICE_DELAY_MS = 150;
5777
+ var PENDING_NOTICE_DELAY_MS = 150;
5778
5778
  function estimateTokenCount(text) {
5779
5779
  return Math.max(1, Math.ceil(text.length / 4));
5780
5780
  }
@@ -5855,17 +5855,16 @@ function buildDryRunOutput(args) {
5855
5855
  async function delay(ms) {
5856
5856
  await new Promise((resolve) => setTimeout(resolve, ms));
5857
5857
  }
5858
- function startProviderPendingNotice() {
5859
- if (!process.stderr.isTTY) {
5858
+ function startPendingNotice(message, enabled) {
5859
+ if (!enabled) {
5860
5860
  return () => {
5861
5861
  };
5862
5862
  }
5863
- const message = "sift waiting for provider...";
5864
5863
  let shown = false;
5865
5864
  const timer = setTimeout(() => {
5866
5865
  shown = true;
5867
5866
  process.stderr.write(`${message}\r`);
5868
- }, PROVIDER_PENDING_NOTICE_DELAY_MS);
5867
+ }, PENDING_NOTICE_DELAY_MS);
5869
5868
  return () => {
5870
5869
  clearTimeout(timer);
5871
5870
  if (!shown) {
@@ -5895,7 +5894,10 @@ async function generateWithRetry(args) {
5895
5894
  responseMode: args.responseMode,
5896
5895
  jsonResponseFormat: args.request.config.provider.jsonResponseFormat
5897
5896
  });
5898
- const stopPendingNotice = startProviderPendingNotice();
5897
+ const stopPendingNotice = startPendingNotice(
5898
+ "sift waiting for provider...",
5899
+ Boolean(process.stderr.isTTY)
5900
+ );
5899
5901
  try {
5900
5902
  try {
5901
5903
  return await generate();
@@ -7293,6 +7295,10 @@ async function runExec(request) {
7293
7295
  cwd: commandCwd,
7294
7296
  stdio: ["inherit", "pipe", "pipe"]
7295
7297
  });
7298
+ const stopChildPendingNotice = startPendingNotice(
7299
+ "sift waiting for child command...",
7300
+ Boolean(process.stderr.isTTY) && !request.quiet
7301
+ );
7296
7302
  const handleChunk = (chunk) => {
7297
7303
  const text = Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk);
7298
7304
  if (bypassed) {
@@ -7313,21 +7319,25 @@ async function runExec(request) {
7313
7319
  };
7314
7320
  child.stdout.on("data", handleChunk);
7315
7321
  child.stderr.on("data", handleChunk);
7316
- await new Promise((resolve, reject) => {
7317
- child.on("error", (error) => {
7318
- reject(error);
7319
- });
7320
- child.on("close", (status, signal) => {
7321
- childStatus = status;
7322
- childSignal = signal;
7323
- resolve();
7322
+ try {
7323
+ await new Promise((resolve, reject) => {
7324
+ child.on("error", (error) => {
7325
+ reject(error);
7326
+ });
7327
+ child.on("close", (status, signal) => {
7328
+ childStatus = status;
7329
+ childSignal = signal;
7330
+ resolve();
7331
+ });
7324
7332
  });
7325
- }).catch((error) => {
7333
+ } catch (error) {
7326
7334
  if (error instanceof Error) {
7327
7335
  throw error;
7328
7336
  }
7329
7337
  throw new Error("Failed to start child process.");
7330
- });
7338
+ } finally {
7339
+ stopChildPendingNotice();
7340
+ }
7331
7341
  const exitCode = normalizeChildExitCode(childStatus, childSignal);
7332
7342
  const capturedOutput = capture.render();
7333
7343
  const autoWatchDetected = !request.watch && looksLikeWatchStream(capturedOutput);
@@ -7853,5 +7863,6 @@ export {
7853
7863
  resolveConfig,
7854
7864
  runExec,
7855
7865
  runSift,
7856
- runSiftWithStats
7866
+ runSiftWithStats,
7867
+ startPendingNotice
7857
7868
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bilalimamoglu/sift",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Agent-first command-output reduction layer for agents, CI, and automation.",
5
5
  "type": "module",
6
6
  "bin": {