@bilalimamoglu/sift 0.2.1 → 0.2.2

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
@@ -1,5 +1,7 @@
1
1
  # sift
2
2
 
3
+ <img src="assets/brand/sift-logo-badge-monochrome.svg" alt="sift logo" width="88" />
4
+
3
5
  `sift` is a small command-output reducer for agent workflows.
4
6
 
5
7
  Instead of feeding a model the full output of `pytest`, `git diff`, `npm audit`, `tsc --noEmit`, `eslint .`, or `terraform plan`, you run the command through `sift`. It captures the output, trims the noise, and returns a much smaller answer.
@@ -24,7 +26,21 @@ npm install -g @bilalimamoglu/sift
24
26
 
25
27
  ## One-time setup
26
28
 
27
- For OpenAI-hosted models:
29
+ The easiest path is the guided setup:
30
+
31
+ ```bash
32
+ sift config setup
33
+ ```
34
+
35
+ That writes a machine-wide config to:
36
+
37
+ ```text
38
+ ~/.config/sift/config.yaml
39
+ ```
40
+
41
+ After that, any terminal can use `sift` without per-project setup. A repo-local config can still override it later.
42
+
43
+ If you want to set things up manually, for OpenAI-hosted models:
28
44
 
29
45
  ```bash
30
46
  export SIFT_PROVIDER=openai
@@ -33,12 +49,30 @@ export SIFT_MODEL=gpt-5-nano
33
49
  export OPENAI_API_KEY=your_openai_api_key
34
50
  ```
35
51
 
36
- Or generate a config file:
52
+ Or write a template config file:
37
53
 
38
54
  ```bash
39
55
  sift config init
40
56
  ```
41
57
 
58
+ For a manual machine-wide template:
59
+
60
+ ```bash
61
+ sift config init --global
62
+ ```
63
+
64
+ That writes:
65
+
66
+ ```text
67
+ ~/.config/sift/config.yaml
68
+ ```
69
+
70
+ Then keep the API key in your shell profile so every terminal can use it:
71
+
72
+ ```bash
73
+ export OPENAI_API_KEY=your_openai_api_key
74
+ ```
75
+
42
76
  If you use a different OpenAI-compatible endpoint, switch to `provider: openai-compatible` and use either the endpoint's native API key env var or the generic fallback:
43
77
 
44
78
  ```bash
@@ -124,6 +158,7 @@ Built-in JSON and verdict flows return strict error objects on provider or model
124
158
  Useful commands:
125
159
 
126
160
  ```bash
161
+ sift config setup
127
162
  sift config init
128
163
  sift config show
129
164
  sift config validate
@@ -195,6 +230,15 @@ The workflow:
195
230
  5. creates and pushes the `vX.Y.Z` tag
196
231
  6. creates a GitHub Release
197
232
 
233
+ ## Brand assets
234
+
235
+ Curated public logo assets live in `assets/brand/`.
236
+
237
+ Included SVG sets:
238
+ - badge/app: teal, black, monochrome
239
+ - icon-only: teal, black, monochrome
240
+ - 24px icon: teal, black, monochrome
241
+
198
242
  ## License
199
243
 
200
244
  MIT
package/dist/cli.js CHANGED
@@ -13,12 +13,17 @@ import YAML from "yaml";
13
13
  import os from "os";
14
14
  import path from "path";
15
15
  var DEFAULT_CONFIG_FILENAME = "sift.config.yaml";
16
- var DEFAULT_CONFIG_SEARCH_PATHS = [
17
- path.resolve(process.cwd(), "sift.config.yaml"),
18
- path.resolve(process.cwd(), "sift.config.yml"),
19
- path.join(os.homedir(), ".config", "sift", "config.yaml"),
20
- path.join(os.homedir(), ".config", "sift", "config.yml")
21
- ];
16
+ function getDefaultGlobalConfigPath() {
17
+ return path.join(os.homedir(), ".config", "sift", "config.yaml");
18
+ }
19
+ function getDefaultConfigSearchPaths() {
20
+ return [
21
+ path.resolve(process.cwd(), "sift.config.yaml"),
22
+ path.resolve(process.cwd(), "sift.config.yml"),
23
+ getDefaultGlobalConfigPath(),
24
+ path.join(os.homedir(), ".config", "sift", "config.yml")
25
+ ];
26
+ }
22
27
  var INSUFFICIENT_SIGNAL_TEXT = "Insufficient signal in the provided input.";
23
28
  var GENERIC_JSON_CONTRACT = '{"answer":string,"evidence":string[],"risks":string[]}';
24
29
  var CAPTURE_OMITTED_MARKER = "\n...[captured output omitted]...\n";
@@ -32,7 +37,7 @@ function findConfigPath(explicitPath) {
32
37
  }
33
38
  return resolved;
34
39
  }
35
- for (const candidate of DEFAULT_CONFIG_SEARCH_PATHS) {
40
+ for (const candidate of getDefaultConfigSearchPaths()) {
36
41
  if (fs.existsSync(candidate)) {
37
42
  return candidate;
38
43
  }
@@ -339,8 +344,11 @@ function resolveConfig(options = {}) {
339
344
  import fs2 from "fs";
340
345
  import path3 from "path";
341
346
  import YAML2 from "yaml";
342
- function writeExampleConfig(targetPath) {
343
- const resolved = path3.resolve(targetPath ?? DEFAULT_CONFIG_FILENAME);
347
+ function writeExampleConfig(options = {}) {
348
+ if (options.global && options.targetPath) {
349
+ throw new Error("Use either --path <path> or --global, not both.");
350
+ }
351
+ const resolved = options.global ? getDefaultGlobalConfigPath() : path3.resolve(options.targetPath ?? DEFAULT_CONFIG_FILENAME);
344
352
  if (fs2.existsSync(resolved)) {
345
353
  throw new Error(`Config file already exists at ${resolved}`);
346
354
  }
@@ -349,6 +357,226 @@ function writeExampleConfig(targetPath) {
349
357
  fs2.writeFileSync(resolved, yaml, "utf8");
350
358
  return resolved;
351
359
  }
360
+ function writeConfigFile(options) {
361
+ const resolved = path3.resolve(options.targetPath);
362
+ if (!options.overwrite && fs2.existsSync(resolved)) {
363
+ throw new Error(`Config file already exists at ${resolved}`);
364
+ }
365
+ const yaml = YAML2.stringify(options.config);
366
+ fs2.mkdirSync(path3.dirname(resolved), { recursive: true });
367
+ fs2.writeFileSync(resolved, yaml, {
368
+ encoding: "utf8",
369
+ mode: 384
370
+ });
371
+ try {
372
+ fs2.chmodSync(resolved, 384);
373
+ } catch {
374
+ }
375
+ return resolved;
376
+ }
377
+
378
+ // src/commands/config-setup.ts
379
+ import fs3 from "fs";
380
+ import path4 from "path";
381
+ import { createInterface } from "readline/promises";
382
+ import { clearLine, cursorTo, emitKeypressEvents, moveCursor } from "readline";
383
+ import { stdin as defaultStdin, stdout as defaultStdout, stderr as defaultStderr } from "process";
384
+ function createTerminalIO() {
385
+ let rl;
386
+ function getInterface() {
387
+ if (!rl) {
388
+ rl = createInterface({
389
+ input: defaultStdin,
390
+ output: defaultStdout,
391
+ terminal: true
392
+ });
393
+ }
394
+ return rl;
395
+ }
396
+ async function select(prompt, options) {
397
+ const input = defaultStdin;
398
+ const output = defaultStdout;
399
+ const promptLine = `${prompt} (use \u2191/\u2193 and Enter)`;
400
+ let index = 0;
401
+ const lineCount = options.length + 1;
402
+ emitKeypressEvents(input);
403
+ input.resume();
404
+ const wasRaw = input.isTTY ? input.isRaw : false;
405
+ input.setRawMode?.(true);
406
+ const render = () => {
407
+ cursorTo(output, 0);
408
+ clearLine(output, 0);
409
+ output.write(`${promptLine}
410
+ `);
411
+ for (let optionIndex = 0; optionIndex < options.length; optionIndex += 1) {
412
+ clearLine(output, 0);
413
+ output.write(`${optionIndex === index ? "\u203A" : " "} ${options[optionIndex]}
414
+ `);
415
+ }
416
+ moveCursor(output, 0, -lineCount);
417
+ };
418
+ render();
419
+ return await new Promise((resolve, reject) => {
420
+ const onKeypress = (_value, key) => {
421
+ if (key.ctrl && key.name === "c") {
422
+ cleanup();
423
+ reject(new Error("Aborted."));
424
+ return;
425
+ }
426
+ if (key.name === "up") {
427
+ index = index === 0 ? options.length - 1 : index - 1;
428
+ render();
429
+ return;
430
+ }
431
+ if (key.name === "down") {
432
+ index = (index + 1) % options.length;
433
+ render();
434
+ return;
435
+ }
436
+ if (key.name === "return" || key.name === "enter") {
437
+ const selected = options[index] ?? options[0];
438
+ cleanup();
439
+ resolve(selected ?? "OpenAI");
440
+ }
441
+ };
442
+ const cleanup = () => {
443
+ input.off("keypress", onKeypress);
444
+ moveCursor(output, 0, lineCount);
445
+ cursorTo(output, 0);
446
+ clearLine(output, 0);
447
+ output.write("\n");
448
+ input.setRawMode?.(Boolean(wasRaw));
449
+ };
450
+ input.on("keypress", onKeypress);
451
+ });
452
+ }
453
+ return {
454
+ stdinIsTTY: Boolean(defaultStdin.isTTY),
455
+ stdoutIsTTY: Boolean(defaultStdout.isTTY),
456
+ ask(prompt) {
457
+ return getInterface().question(prompt);
458
+ },
459
+ select,
460
+ write(message) {
461
+ defaultStdout.write(message);
462
+ },
463
+ error(message) {
464
+ defaultStderr.write(message);
465
+ },
466
+ close() {
467
+ rl?.close();
468
+ }
469
+ };
470
+ }
471
+ function resolveSetupPath(targetPath) {
472
+ return targetPath ? path4.resolve(targetPath) : getDefaultGlobalConfigPath();
473
+ }
474
+ function buildOpenAISetupConfig(apiKey) {
475
+ return {
476
+ ...defaultConfig,
477
+ provider: {
478
+ ...defaultConfig.provider,
479
+ provider: "openai",
480
+ model: "gpt-5-nano",
481
+ baseUrl: "https://api.openai.com/v1",
482
+ apiKey
483
+ }
484
+ };
485
+ }
486
+ async function promptForProvider(io) {
487
+ if (io.select) {
488
+ const choice = await io.select("Select provider", ["OpenAI"]);
489
+ if (choice === "OpenAI") {
490
+ return "openai";
491
+ }
492
+ }
493
+ while (true) {
494
+ const answer = (await io.ask("Provider [OpenAI]: ")).trim().toLowerCase();
495
+ if (answer === "" || answer === "openai") {
496
+ return "openai";
497
+ }
498
+ io.error("Only OpenAI is supported in guided setup right now.\n");
499
+ }
500
+ }
501
+ async function promptForApiKey(io) {
502
+ while (true) {
503
+ const answer = (await io.ask("Enter your OpenAI API key: ")).trim();
504
+ if (answer.length > 0) {
505
+ return answer;
506
+ }
507
+ io.error("API key cannot be empty.\n");
508
+ }
509
+ }
510
+ async function promptForOverwrite(io, targetPath) {
511
+ while (true) {
512
+ const answer = (await io.ask(
513
+ `Config file already exists at ${targetPath}. Overwrite? [y/N]: `
514
+ )).trim().toLowerCase();
515
+ if (answer === "" || answer === "n" || answer === "no") {
516
+ return false;
517
+ }
518
+ if (answer === "y" || answer === "yes") {
519
+ return true;
520
+ }
521
+ io.error("Please answer y or n.\n");
522
+ }
523
+ }
524
+ async function configSetup(options = {}) {
525
+ void options.global;
526
+ const io = options.io ?? createTerminalIO();
527
+ try {
528
+ if (!io.stdinIsTTY || !io.stdoutIsTTY) {
529
+ io.error(
530
+ "sift config setup is interactive and requires a TTY. Use 'sift config init --global' for a non-interactive template.\n"
531
+ );
532
+ return 1;
533
+ }
534
+ const resolvedPath = resolveSetupPath(options.targetPath);
535
+ if (fs3.existsSync(resolvedPath)) {
536
+ const shouldOverwrite = await promptForOverwrite(io, resolvedPath);
537
+ if (!shouldOverwrite) {
538
+ io.write("Aborted.\n");
539
+ return 1;
540
+ }
541
+ }
542
+ const provider = await promptForProvider(io);
543
+ if (provider !== "openai") {
544
+ io.error("Unsupported provider selection.\n");
545
+ return 1;
546
+ }
547
+ io.write("Using OpenAI defaults.\n");
548
+ io.write("Default model: gpt-5-nano\n");
549
+ io.write("Default base URL: https://api.openai.com/v1\n");
550
+ io.write(
551
+ "You can change these later by editing the config file or running 'sift config show --show-secrets'.\n"
552
+ );
553
+ const apiKey = await promptForApiKey(io);
554
+ const config = buildOpenAISetupConfig(apiKey);
555
+ const writtenPath = writeConfigFile({
556
+ targetPath: resolvedPath,
557
+ config,
558
+ overwrite: true
559
+ });
560
+ io.write(`Wrote ${writtenPath}
561
+ `);
562
+ io.write(
563
+ "This is your machine-wide default config. Repo-local sift.config.yaml can still override it later.\n"
564
+ );
565
+ const activeConfigPath = findConfigPath();
566
+ if (activeConfigPath && path4.resolve(activeConfigPath) !== path4.resolve(writtenPath)) {
567
+ io.write(
568
+ `Note: ${activeConfigPath} currently overrides this machine-wide config in the current directory.
569
+ `
570
+ );
571
+ }
572
+ io.write("Try:\n");
573
+ io.write(" sift doctor\n");
574
+ io.write(" sift exec --preset test-status -- pytest\n");
575
+ return 0;
576
+ } finally {
577
+ io.close?.();
578
+ }
579
+ }
352
580
 
353
581
  // src/commands/config.ts
354
582
  var MASKED_SECRET = "***";
@@ -369,9 +597,12 @@ function maskConfigSecrets(value) {
369
597
  }
370
598
  return output;
371
599
  }
372
- function configInit(targetPath) {
373
- const path4 = writeExampleConfig(targetPath);
374
- process.stdout.write(`${path4}
600
+ function configInit(targetPath, global = false) {
601
+ const path5 = writeExampleConfig({
602
+ targetPath,
603
+ global
604
+ });
605
+ process.stdout.write(`${path5}
375
606
  `);
376
607
  }
377
608
  function configShow(configPath, showSecrets = false) {
@@ -396,10 +627,11 @@ function configValidate(configPath) {
396
627
  }
397
628
 
398
629
  // src/commands/doctor.ts
399
- function runDoctor(config) {
630
+ function runDoctor(config, configPath) {
400
631
  const lines = [
401
632
  "sift doctor",
402
633
  "mode: local config completeness check",
634
+ `configPath: ${configPath ?? "(defaults only)"}`,
403
635
  `provider: ${config.provider.provider}`,
404
636
  `model: ${config.provider.model}`,
405
637
  `baseUrl: ${config.provider.baseUrl}`,
@@ -1429,6 +1661,15 @@ function buildCommandPreview(request) {
1429
1661
  }
1430
1662
  return (request.command ?? []).join(" ");
1431
1663
  }
1664
+ function getExecSuccessShortcut(args) {
1665
+ if (args.exitCode !== 0) {
1666
+ return null;
1667
+ }
1668
+ if (args.presetName === "typecheck-summary" && args.capturedOutput.trim() === "") {
1669
+ return "No type errors.";
1670
+ }
1671
+ return null;
1672
+ }
1432
1673
  async function runExec(request) {
1433
1674
  const hasArgvCommand = Array.isArray(request.command) && request.command.length > 0;
1434
1675
  const hasShellCommand = typeof request.shellCommand === "string";
@@ -1493,6 +1734,7 @@ async function runExec(request) {
1493
1734
  throw childSpawnError;
1494
1735
  }
1495
1736
  const exitCode = normalizeChildExitCode(childStatus, childSignal);
1737
+ const capturedOutput = capture.render();
1496
1738
  if (request.config.runtime.verbose) {
1497
1739
  process.stderr.write(
1498
1740
  `${pc2.dim("sift")} child_exit=${exitCode} captured_chars=${capture.getTotalChars()} capture_truncated=${capture.wasTruncated()}
@@ -1500,9 +1742,25 @@ async function runExec(request) {
1500
1742
  );
1501
1743
  }
1502
1744
  if (!bypassed) {
1745
+ const execSuccessShortcut = getExecSuccessShortcut({
1746
+ presetName: request.presetName,
1747
+ exitCode,
1748
+ capturedOutput
1749
+ });
1750
+ if (execSuccessShortcut && !request.dryRun) {
1751
+ if (request.config.runtime.verbose) {
1752
+ process.stderr.write(
1753
+ `${pc2.dim("sift")} exec_shortcut=${request.presetName}
1754
+ `
1755
+ );
1756
+ }
1757
+ process.stdout.write(`${execSuccessShortcut}
1758
+ `);
1759
+ return exitCode;
1760
+ }
1503
1761
  const output = await runSift({
1504
1762
  ...request,
1505
- stdin: capture.render()
1763
+ stdin: capturedOutput
1506
1764
  });
1507
1765
  process.stdout.write(`${output}
1508
1766
  `);
@@ -1541,6 +1799,12 @@ function getPreset(config, name) {
1541
1799
  var require2 = createRequire(import.meta.url);
1542
1800
  var pkg = require2("../package.json");
1543
1801
  var cli = cac("sift");
1802
+ var HELP_BANNER = [
1803
+ " \\\\ //",
1804
+ " \\\\//",
1805
+ " ||",
1806
+ " o"
1807
+ ].join("\n");
1544
1808
  function toNumber(value) {
1545
1809
  if (value === void 0 || value === null || value === "") {
1546
1810
  return void 0;
@@ -1726,10 +1990,23 @@ applySharedOptions(
1726
1990
  });
1727
1991
  cli.command(
1728
1992
  "config <action>",
1729
- "Config commands: init | show | validate (show/validate use resolved runtime config)"
1730
- ).usage("config <init|show|validate> [options]").example("config init").example("config show").example("config validate --config ./sift.config.yaml").option("--path <path>", "Target config path for init").option("--config <path>", "Path to config file").option("--show-secrets", "Show secret values in config show").action((action, options) => {
1993
+ "Config commands: setup | init | show | validate (show/validate use resolved runtime config)"
1994
+ ).usage("config <setup|init|show|validate> [options]").example("config setup").example("config setup --global").example("config setup --path ~/.config/sift/config.yaml").example("config init").example("config init --global").example("config show").example("config validate --config ./sift.config.yaml").option("--path <path>", "Target config path for init or setup").option(
1995
+ "--global",
1996
+ "Use the machine-wide config path (~/.config/sift/config.yaml) for init or setup"
1997
+ ).option("--config <path>", "Path to config file").option("--show-secrets", "Show secret values in config show").action(async (action, options) => {
1998
+ if (action === "setup") {
1999
+ process.exitCode = await configSetup({
2000
+ targetPath: options.path,
2001
+ global: Boolean(options.global)
2002
+ });
2003
+ return;
2004
+ }
1731
2005
  if (action === "init") {
1732
- configInit(options.path);
2006
+ configInit(
2007
+ options.path,
2008
+ Boolean(options.global)
2009
+ );
1733
2010
  return;
1734
2011
  }
1735
2012
  if (action === "show") {
@@ -1746,11 +2023,12 @@ cli.command(
1746
2023
  throw new Error(`Unknown config action: ${action}`);
1747
2024
  });
1748
2025
  cli.command("doctor", "Check local runtime config completeness").usage("doctor [options]").option("--config <path>", "Path to config file").action((options) => {
2026
+ const configPath = findConfigPath(options.config);
1749
2027
  const config = resolveConfig({
1750
2028
  configPath: options.config,
1751
2029
  env: process.env
1752
2030
  });
1753
- process.exitCode = runDoctor(config);
2031
+ process.exitCode = runDoctor(config, configPath);
1754
2032
  });
1755
2033
  cli.command("presets <action> [name]", "Preset commands: list | show").usage("presets <list|show> [name] [options]").example("presets list").example("presets show infra-risk").option("--config <path>", "Path to config file").option("--internal", "Show internal preset fields in presets show").action((action, name, options) => {
1756
2034
  const config = resolveConfig({
@@ -1783,7 +2061,13 @@ applySharedOptions(
1783
2061
  options
1784
2062
  });
1785
2063
  });
1786
- cli.help();
2064
+ cli.help((sections) => [
2065
+ {
2066
+ body: `${HELP_BANNER}
2067
+ `
2068
+ },
2069
+ ...sections
2070
+ ]);
1787
2071
  cli.version(pkg.version);
1788
2072
  async function main() {
1789
2073
  cli.parse(process.argv, { run: false });
package/dist/index.js CHANGED
@@ -6,12 +6,17 @@ import pc2 from "picocolors";
6
6
  // src/constants.ts
7
7
  import os from "os";
8
8
  import path from "path";
9
- var DEFAULT_CONFIG_SEARCH_PATHS = [
10
- path.resolve(process.cwd(), "sift.config.yaml"),
11
- path.resolve(process.cwd(), "sift.config.yml"),
12
- path.join(os.homedir(), ".config", "sift", "config.yaml"),
13
- path.join(os.homedir(), ".config", "sift", "config.yml")
14
- ];
9
+ function getDefaultGlobalConfigPath() {
10
+ return path.join(os.homedir(), ".config", "sift", "config.yaml");
11
+ }
12
+ function getDefaultConfigSearchPaths() {
13
+ return [
14
+ path.resolve(process.cwd(), "sift.config.yaml"),
15
+ path.resolve(process.cwd(), "sift.config.yml"),
16
+ getDefaultGlobalConfigPath(),
17
+ path.join(os.homedir(), ".config", "sift", "config.yml")
18
+ ];
19
+ }
15
20
  var INSUFFICIENT_SIGNAL_TEXT = "Insufficient signal in the provided input.";
16
21
  var GENERIC_JSON_CONTRACT = '{"answer":string,"evidence":string[],"risks":string[]}';
17
22
  var CAPTURE_OMITTED_MARKER = "\n...[captured output omitted]...\n";
@@ -971,6 +976,15 @@ function buildCommandPreview(request) {
971
976
  }
972
977
  return (request.command ?? []).join(" ");
973
978
  }
979
+ function getExecSuccessShortcut(args) {
980
+ if (args.exitCode !== 0) {
981
+ return null;
982
+ }
983
+ if (args.presetName === "typecheck-summary" && args.capturedOutput.trim() === "") {
984
+ return "No type errors.";
985
+ }
986
+ return null;
987
+ }
974
988
  async function runExec(request) {
975
989
  const hasArgvCommand = Array.isArray(request.command) && request.command.length > 0;
976
990
  const hasShellCommand = typeof request.shellCommand === "string";
@@ -1035,6 +1049,7 @@ async function runExec(request) {
1035
1049
  throw childSpawnError;
1036
1050
  }
1037
1051
  const exitCode = normalizeChildExitCode(childStatus, childSignal);
1052
+ const capturedOutput = capture.render();
1038
1053
  if (request.config.runtime.verbose) {
1039
1054
  process.stderr.write(
1040
1055
  `${pc2.dim("sift")} child_exit=${exitCode} captured_chars=${capture.getTotalChars()} capture_truncated=${capture.wasTruncated()}
@@ -1042,9 +1057,25 @@ async function runExec(request) {
1042
1057
  );
1043
1058
  }
1044
1059
  if (!bypassed) {
1060
+ const execSuccessShortcut = getExecSuccessShortcut({
1061
+ presetName: request.presetName,
1062
+ exitCode,
1063
+ capturedOutput
1064
+ });
1065
+ if (execSuccessShortcut && !request.dryRun) {
1066
+ if (request.config.runtime.verbose) {
1067
+ process.stderr.write(
1068
+ `${pc2.dim("sift")} exec_shortcut=${request.presetName}
1069
+ `
1070
+ );
1071
+ }
1072
+ process.stdout.write(`${execSuccessShortcut}
1073
+ `);
1074
+ return exitCode;
1075
+ }
1045
1076
  const output = await runSift({
1046
1077
  ...request,
1047
- stdin: capture.render()
1078
+ stdin: capturedOutput
1048
1079
  });
1049
1080
  process.stdout.write(`${output}
1050
1081
  `);
@@ -1141,7 +1172,7 @@ function findConfigPath(explicitPath) {
1141
1172
  }
1142
1173
  return resolved;
1143
1174
  }
1144
- for (const candidate of DEFAULT_CONFIG_SEARCH_PATHS) {
1175
+ for (const candidate of getDefaultConfigSearchPaths()) {
1145
1176
  if (fs.existsSync(candidate)) {
1146
1177
  return candidate;
1147
1178
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bilalimamoglu/sift",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Agent-first command-output reduction layer for agents, CI, and automation.",
5
5
  "type": "module",
6
6
  "bin": {