sift-light 1.0.5-1 → 1.0.5-6
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/CHANGELOG.md +6 -0
- package/package.json +4 -3
- package/plugins/sift-light/.claude-plugin/plugin.json +1 -1
- package/plugins/sift-light/.codex-plugin/plugin.json +1 -1
- package/plugins/sift-light/.mcp.json +1 -1
- package/plugins/sift-light/kimi.plugin.json +2 -2
- package/plugins/sift-light/omp-extension.mjs +81 -21
- package/plugins/sift-light/package.json +1 -1
- package/src/errors.ts +7 -1
- package/src/mcp-server.mjs +81 -21
- package/src/request-contract-catalog.ts +10 -6
- package/src/rg.ts +3 -1
- package/src/ripgrep-diagnostics.ts +86 -3
- package/src/scan-revisions.ts +3 -1
- package/src/tool-schema.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.5-6 — 2026-10-10
|
|
4
|
+
|
|
5
|
+
- Add structured `repairExamples` to invalid-regex and missing-path diagnostics. The examples preserve bounded search filters, never auto-convert regex input, and are omitted when redaction would make request data unsafe to echo.
|
|
6
|
+
- Extend model-facing guidance for `redact=true`, including its sensitive-search use cases and its boundary as display masking rather than filesystem sandboxing.
|
|
7
|
+
- Add regression coverage for the audited request failures and verify the same typed recovery details through the real MCP, ripgrep and packaged host paths.
|
|
8
|
+
|
|
3
9
|
## 1.0.5-1 — 2026-10-09
|
|
4
10
|
|
|
5
11
|
- Fix OMP plugin-cache searches when the generated bundle cannot resolve its platform ripgrep package. Bundled ripgrep remains preferred; an executable `rg` on `PATH` is now a bounded compatibility fallback, while explicit `SIFT_LIGHT_RG_PATH` values retain strict absolute-path and executable validation.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sift-light",
|
|
3
|
-
"version": "1.0.5-
|
|
3
|
+
"version": "1.0.5-6",
|
|
4
4
|
"description": "Context-efficient local search for files, documents, notes and logs across Pi, OMP and MCP clients",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agent",
|
|
@@ -90,7 +90,6 @@
|
|
|
90
90
|
"@huggingface/transformers": "4.3.1",
|
|
91
91
|
"@modelcontextprotocol/sdk": "1.31.0",
|
|
92
92
|
"@vscode/ripgrep": "1.18.0",
|
|
93
|
-
"typebox": "1.3.19",
|
|
94
93
|
"web-tree-sitter": "0.25.10",
|
|
95
94
|
"zod": "4.5.4"
|
|
96
95
|
},
|
|
@@ -104,12 +103,14 @@
|
|
|
104
103
|
"oxlint-tsgolint": "^7.0.2001",
|
|
105
104
|
"tree-sitter-bash": "0.25.1",
|
|
106
105
|
"tree-sitter-powershell": "0.26.4",
|
|
106
|
+
"typebox": "1.3.19",
|
|
107
107
|
"typescript": "7.0.2"
|
|
108
108
|
},
|
|
109
109
|
"peerDependencies": {
|
|
110
110
|
"@earendil-works/pi-ai": "*",
|
|
111
111
|
"@earendil-works/pi-coding-agent": "*",
|
|
112
|
-
"@earendil-works/pi-tui": "*"
|
|
112
|
+
"@earendil-works/pi-tui": "*",
|
|
113
|
+
"typebox": "*"
|
|
113
114
|
},
|
|
114
115
|
"peerDependenciesMeta": {
|
|
115
116
|
"@earendil-works/pi-ai": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sift-light",
|
|
3
|
-
"version": "1.0.5-
|
|
3
|
+
"version": "1.0.5-6",
|
|
4
4
|
"description": "Require sift-light for conventional local searches while keeping development tools available.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "baoer"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"args": [
|
|
14
14
|
"--yes",
|
|
15
15
|
"--package",
|
|
16
|
-
"sift-light-runtime@npm:sift-light@1.0.5-
|
|
16
|
+
"sift-light-runtime@npm:sift-light@1.0.5-6",
|
|
17
17
|
"sift-light-mcp",
|
|
18
18
|
"--stdio"
|
|
19
19
|
],
|
|
@@ -362,7 +362,7 @@ class RipgrepInputError extends SiftLightError {
|
|
|
362
362
|
code;
|
|
363
363
|
guidance;
|
|
364
364
|
details;
|
|
365
|
-
constructor(code, message, guidance) {
|
|
365
|
+
constructor(code, message, guidance, repairExamples = []) {
|
|
366
366
|
super(`${message} ${guidance}`);
|
|
367
367
|
this.name = "RipgrepInputError";
|
|
368
368
|
this.code = code;
|
|
@@ -375,7 +375,8 @@ class RipgrepInputError extends SiftLightError {
|
|
|
375
375
|
reason: guidance
|
|
376
376
|
}
|
|
377
377
|
],
|
|
378
|
-
recovery: { action: "manual", reason: guidance }
|
|
378
|
+
recovery: { action: "manual", reason: guidance },
|
|
379
|
+
...repairExamples.length > 0 ? { repairExamples } : {}
|
|
379
380
|
};
|
|
380
381
|
}
|
|
381
382
|
}
|
|
@@ -915,7 +916,7 @@ async function resolveRipgrepExecutable() {
|
|
|
915
916
|
}
|
|
916
917
|
|
|
917
918
|
// src/ripgrep-diagnostics.ts
|
|
918
|
-
import { resolve as resolve3 } from "path";
|
|
919
|
+
import { basename, resolve as resolve3 } from "path";
|
|
919
920
|
|
|
920
921
|
// src/redaction.ts
|
|
921
922
|
var PRIVATE_KEY = /-----BEGIN ([^-\r\n]*PRIVATE KEY)-----[\s\S]*?-----END \1-----/g;
|
|
@@ -1049,15 +1050,70 @@ function missingPathFromDiagnostics(stderr) {
|
|
|
1049
1050
|
}
|
|
1050
1051
|
return;
|
|
1051
1052
|
}
|
|
1052
|
-
|
|
1053
|
+
var RIPGREP_RECOVERY_FIELDS = [
|
|
1054
|
+
"path",
|
|
1055
|
+
"glob",
|
|
1056
|
+
"exclude",
|
|
1057
|
+
"literal",
|
|
1058
|
+
"ignoreCase",
|
|
1059
|
+
"hidden",
|
|
1060
|
+
"ignorePolicy",
|
|
1061
|
+
"wholeWord",
|
|
1062
|
+
"context",
|
|
1063
|
+
"scope",
|
|
1064
|
+
"redact"
|
|
1065
|
+
];
|
|
1066
|
+
function copyRipgrepRecoveryFields(request) {
|
|
1067
|
+
const result = {};
|
|
1068
|
+
for (const field of RIPGREP_RECOVERY_FIELDS) {
|
|
1069
|
+
const value = request[field];
|
|
1070
|
+
if (value !== undefined)
|
|
1071
|
+
result[field] = value;
|
|
1072
|
+
}
|
|
1073
|
+
return result;
|
|
1074
|
+
}
|
|
1075
|
+
function ripgrepRepairExamples(code, request, redact) {
|
|
1076
|
+
if (request === undefined || redact && containsSensitiveText(request))
|
|
1077
|
+
return [];
|
|
1078
|
+
if (code === "E_REGEX_INVALID") {
|
|
1079
|
+
return [
|
|
1080
|
+
{
|
|
1081
|
+
label: "\u6309\u539F\u6587\u67E5\u627E",
|
|
1082
|
+
request: {
|
|
1083
|
+
mode: "matches",
|
|
1084
|
+
...copyRipgrepRecoveryFields(request),
|
|
1085
|
+
pattern: request.pattern,
|
|
1086
|
+
literal: true
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
];
|
|
1090
|
+
}
|
|
1091
|
+
if (code === "E_SEARCH_PATH_NOT_FOUND" && request.path !== undefined) {
|
|
1092
|
+
const fileName = basename(request.path.replaceAll("\\", "/"));
|
|
1093
|
+
if (fileName === "." || fileName === "/" || fileName.length === 0)
|
|
1094
|
+
return [];
|
|
1095
|
+
return [
|
|
1096
|
+
{
|
|
1097
|
+
label: "\u6309\u6587\u4EF6\u540D\u67E5\u627E",
|
|
1098
|
+
request: {
|
|
1099
|
+
mode: "files",
|
|
1100
|
+
query: fileName,
|
|
1101
|
+
...request.redact === true ? { redact: true } : {}
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
];
|
|
1105
|
+
}
|
|
1106
|
+
return [];
|
|
1107
|
+
}
|
|
1108
|
+
function createRipgrepInputError(stderr, redact = false, request) {
|
|
1053
1109
|
const code = classifyRipgrepInputFailure(stderr);
|
|
1054
1110
|
if (code === "E_REGEX_INVALID") {
|
|
1055
|
-
return new RipgrepInputError(code, "ripgrep regex parse error: pattern is not a valid regular expression.", "Correct pattern or set literal=true for source text; no automatic literal conversion was attempted.");
|
|
1111
|
+
return new RipgrepInputError(code, "ripgrep regex parse error: pattern is not a valid regular expression.", "Correct pattern or set literal=true for source text; no automatic literal conversion was attempted.", ripgrepRepairExamples(code, request, redact));
|
|
1056
1112
|
}
|
|
1057
1113
|
if (code === "E_SEARCH_PATH_NOT_FOUND") {
|
|
1058
1114
|
const path = missingPathFromDiagnostics(stderr);
|
|
1059
1115
|
const location = path ? ` (${JSON.stringify(boundedRipgrepDiagnostic(path, redact))})` : "";
|
|
1060
|
-
return new RipgrepInputError(code, `A searched path${location} does not exist or became unavailable.`, "Provide an existing exact path, or use mode=files with query to discover an unknown filename; no retry or scope expansion was attempted.");
|
|
1116
|
+
return new RipgrepInputError(code, `A searched path${location} does not exist or became unavailable.`, "Provide an existing exact path, or use mode=files with query to discover an unknown filename; no retry or scope expansion was attempted.", ripgrepRepairExamples(code, request, redact));
|
|
1061
1117
|
}
|
|
1062
1118
|
return;
|
|
1063
1119
|
}
|
|
@@ -1278,7 +1334,7 @@ async function captureBatch(paths, revisions, signal, onRevisionError) {
|
|
|
1278
1334
|
if (signal?.aborted)
|
|
1279
1335
|
throw abortError();
|
|
1280
1336
|
}
|
|
1281
|
-
async function captureCandidateRevisions(executable, args2, cwd, maxFiles, signal, redact = false) {
|
|
1337
|
+
async function captureCandidateRevisions(executable, args2, cwd, maxFiles, signal, redact = false, request) {
|
|
1282
1338
|
const revisions = new Map;
|
|
1283
1339
|
let candidateCount = 0;
|
|
1284
1340
|
let enumerationTruncated = false;
|
|
@@ -1332,7 +1388,7 @@ async function captureCandidateRevisions(executable, args2, cwd, maxFiles, signa
|
|
|
1332
1388
|
await captureBatch(batch, revisions, signal, recordMetadataFailure);
|
|
1333
1389
|
});
|
|
1334
1390
|
const diagnostics = classifyRipgrepDiagnostics(result.stderr);
|
|
1335
|
-
const inputError = createRipgrepInputError(result.stderr, redact);
|
|
1391
|
+
const inputError = createRipgrepInputError(result.stderr, redact, request);
|
|
1336
1392
|
const unreadable = [...diagnostics.unreadable, ...diagnostics.recoverable, ...metadataFailures];
|
|
1337
1393
|
if (inputError)
|
|
1338
1394
|
throw inputError;
|
|
@@ -1658,7 +1714,7 @@ function createRipgrepRunner(options = {}) {
|
|
|
1658
1714
|
...policy.ripgrepGlobArguments(validatedSearchPath),
|
|
1659
1715
|
"--",
|
|
1660
1716
|
searchTarget
|
|
1661
|
-
], ripgrepCwd, maxSourceRevisionFiles, signal, request.redact);
|
|
1717
|
+
], ripgrepCwd, maxSourceRevisionFiles, signal, request.redact, request);
|
|
1662
1718
|
let ignoredFileCount = 0;
|
|
1663
1719
|
let ignoredFileSamples = [];
|
|
1664
1720
|
let filesystemCoverage = "complete";
|
|
@@ -1688,7 +1744,7 @@ function createRipgrepRunner(options = {}) {
|
|
|
1688
1744
|
...policy.ripgrepGlobArguments(validatedSearchPath),
|
|
1689
1745
|
"--",
|
|
1690
1746
|
searchTarget
|
|
1691
|
-
], ripgrepCwd, maxSourceRevisionFiles, signal, request.redact);
|
|
1747
|
+
], ripgrepCwd, maxSourceRevisionFiles, signal, request.redact, request);
|
|
1692
1748
|
const ignored = [...allCandidates.revisions.keys()].filter((path) => !before.revisions.has(path));
|
|
1693
1749
|
ignoredFileCount = ignored.length;
|
|
1694
1750
|
ignoredFileSamples = ignored.slice(0, 20).map((path) => displayPath(path, cwd).displayPath);
|
|
@@ -1732,7 +1788,7 @@ function createRipgrepRunner(options = {}) {
|
|
|
1732
1788
|
}
|
|
1733
1789
|
}));
|
|
1734
1790
|
const diagnostics = classifyRipgrepDiagnostics(stderr);
|
|
1735
|
-
const inputError = createRipgrepInputError(stderr, request.redact);
|
|
1791
|
+
const inputError = createRipgrepInputError(stderr, request.redact, request);
|
|
1736
1792
|
if (inputError)
|
|
1737
1793
|
throw inputError;
|
|
1738
1794
|
const filesystemDiagnostics = [...diagnostics.unreadable, ...diagnostics.recoverable];
|
|
@@ -1969,7 +2025,7 @@ function createCtagsStructureProvider(options = {}) {
|
|
|
1969
2025
|
// package.json
|
|
1970
2026
|
var package_default = {
|
|
1971
2027
|
name: "sift-light",
|
|
1972
|
-
version: "1.0.5-
|
|
2028
|
+
version: "1.0.5-6",
|
|
1973
2029
|
description: "Context-efficient local search for files, documents, notes and logs across Pi, OMP and MCP clients",
|
|
1974
2030
|
keywords: [
|
|
1975
2031
|
"ai-agent",
|
|
@@ -2059,7 +2115,6 @@ var package_default = {
|
|
|
2059
2115
|
"@huggingface/transformers": "4.3.1",
|
|
2060
2116
|
"@modelcontextprotocol/sdk": "1.31.0",
|
|
2061
2117
|
"@vscode/ripgrep": "1.18.0",
|
|
2062
|
-
typebox: "1.3.19",
|
|
2063
2118
|
"web-tree-sitter": "0.25.10",
|
|
2064
2119
|
zod: "4.5.4"
|
|
2065
2120
|
},
|
|
@@ -2073,12 +2128,14 @@ var package_default = {
|
|
|
2073
2128
|
"oxlint-tsgolint": "^7.0.2001",
|
|
2074
2129
|
"tree-sitter-bash": "0.25.1",
|
|
2075
2130
|
"tree-sitter-powershell": "0.26.4",
|
|
2131
|
+
typebox: "1.3.19",
|
|
2076
2132
|
typescript: "7.0.2"
|
|
2077
2133
|
},
|
|
2078
2134
|
peerDependencies: {
|
|
2079
2135
|
"@earendil-works/pi-ai": "*",
|
|
2080
2136
|
"@earendil-works/pi-coding-agent": "*",
|
|
2081
|
-
"@earendil-works/pi-tui": "*"
|
|
2137
|
+
"@earendil-works/pi-tui": "*",
|
|
2138
|
+
typebox: "*"
|
|
2082
2139
|
},
|
|
2083
2140
|
peerDependenciesMeta: {
|
|
2084
2141
|
"@earendil-works/pi-ai": {
|
|
@@ -11332,6 +11389,7 @@ var MODE_FIELD_SUMMARY = MODE_SUMMARY_MODES.map((mode) => modeFieldSummary(mode)
|
|
|
11332
11389
|
var REQUEST_FIELD_GUIDANCE = {
|
|
11333
11390
|
pattern: "pattern is regex by default; literal=true matches source text exactly",
|
|
11334
11391
|
literal: "literal=true makes pattern exact source text; anyOf/allOf already use literal semantics",
|
|
11392
|
+
redact: "redact=true masks credential-like values and private-key bodies in displayed evidence; it does not sandbox search or change counts",
|
|
11335
11393
|
path: "path must be an existing exact file or root; use mode=files+query for unknown names",
|
|
11336
11394
|
query: "files+query matches known filename/path text (not glob patterns); omit files query to list every file under path; concept/hybrid query is natural language",
|
|
11337
11395
|
anyOf: "anyOf is case-sensitive exact-literal OR; omit pattern, allOf, literal, ignoreCase, roles",
|
|
@@ -11348,6 +11406,7 @@ function fieldGuidance(field) {
|
|
|
11348
11406
|
}
|
|
11349
11407
|
var GUIDANCE_FIELDS = [
|
|
11350
11408
|
"pattern",
|
|
11409
|
+
"redact",
|
|
11351
11410
|
"literal",
|
|
11352
11411
|
"path",
|
|
11353
11412
|
"query",
|
|
@@ -11369,12 +11428,13 @@ var REQUEST_USAGE_GUIDANCE = [
|
|
|
11369
11428
|
].join("; ");
|
|
11370
11429
|
var MODEL_USAGE_GUIDANCE = [
|
|
11371
11430
|
"pattern is regex by default; literal=true matches source text exactly",
|
|
11372
|
-
"
|
|
11373
|
-
"
|
|
11374
|
-
|
|
11375
|
-
|
|
11376
|
-
"outline
|
|
11377
|
-
|
|
11431
|
+
"redact=true masks credentials and private-key bodies in displayed evidence; use it for configs, logs or user directories; it does not sandbox search",
|
|
11432
|
+
"path is an existing exact file/root; use files+query for unknown names",
|
|
11433
|
+
"anyOf/allOf are exact-literal OR/AND variants; exclude pattern/literal",
|
|
11434
|
+
`limit/context are ordinary-search budgets (limit <= ${String(MAX_PAGE_SIZE)}); omit for hybrid/concept/outline/structure/inspect`,
|
|
11435
|
+
"outline needs a concrete JS/TS/TSX/Python-capable source file, not a directory; structure needs a nonempty AST pattern for JS/TS/TSX/Go, no lang; use capabilities before unfamiliar language operations",
|
|
11436
|
+
"imports/tests are static JS/TS/TSX candidates",
|
|
11437
|
+
"selectors: inspect, hybrid and capabilities use their cataloged fields",
|
|
11378
11438
|
"exact, any-of and max_results are not parameters"
|
|
11379
11439
|
].join("; ");
|
|
11380
11440
|
var MODE_CONTRACT_DESCRIPTION = [
|
|
@@ -22368,7 +22428,7 @@ var siftLightSchema = _Object_({
|
|
|
22368
22428
|
description: "Named exact-literal checks for mode=audit. Each finding is present, absent_with_complete_coverage, or unknown."
|
|
22369
22429
|
})),
|
|
22370
22430
|
redact: Optional(Boolean2({
|
|
22371
|
-
description: "Optional display-only masking for credential-like values and private-key bodies. Default false. It never changes searched files, admitted matches, counts
|
|
22431
|
+
description: "Optional display-only masking for credential-like values and private-key bodies. Default false. Use it for configuration, logs and user-directory searches when returned text may contain secrets. It never changes the search scope, searched files, admitted matches, counts or cursor completeness, and it is not a sandbox."
|
|
22372
22432
|
})),
|
|
22373
22433
|
modifiedAfter: Optional(Integer({
|
|
22374
22434
|
minimum: 0,
|
package/src/errors.ts
CHANGED
|
@@ -51,7 +51,12 @@ export class RipgrepInputError extends SiftLightError {
|
|
|
51
51
|
readonly guidance: string;
|
|
52
52
|
readonly details: SiftLightDiagnosticDetails;
|
|
53
53
|
|
|
54
|
-
constructor(
|
|
54
|
+
constructor(
|
|
55
|
+
code: RipgrepInputErrorCode,
|
|
56
|
+
message: string,
|
|
57
|
+
guidance: string,
|
|
58
|
+
repairExamples: readonly SiftLightDiagnosticRepairExample[] = [],
|
|
59
|
+
) {
|
|
55
60
|
super(`${message} ${guidance}`);
|
|
56
61
|
this.name = "RipgrepInputError";
|
|
57
62
|
this.code = code;
|
|
@@ -65,6 +70,7 @@ export class RipgrepInputError extends SiftLightError {
|
|
|
65
70
|
},
|
|
66
71
|
],
|
|
67
72
|
recovery: { action: "manual", reason: guidance },
|
|
73
|
+
...(repairExamples.length > 0 ? { repairExamples } : {}),
|
|
68
74
|
};
|
|
69
75
|
}
|
|
70
76
|
}
|
package/src/mcp-server.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { URL as URL2 } from "node:url";
|
|
|
7
7
|
// package.json
|
|
8
8
|
var package_default = {
|
|
9
9
|
name: "sift-light",
|
|
10
|
-
version: "1.0.5-
|
|
10
|
+
version: "1.0.5-6",
|
|
11
11
|
description: "Context-efficient local search for files, documents, notes and logs across Pi, OMP and MCP clients",
|
|
12
12
|
keywords: [
|
|
13
13
|
"ai-agent",
|
|
@@ -97,7 +97,6 @@ var package_default = {
|
|
|
97
97
|
"@huggingface/transformers": "4.3.1",
|
|
98
98
|
"@modelcontextprotocol/sdk": "1.31.0",
|
|
99
99
|
"@vscode/ripgrep": "1.18.0",
|
|
100
|
-
typebox: "1.3.19",
|
|
101
100
|
"web-tree-sitter": "0.25.10",
|
|
102
101
|
zod: "4.5.4"
|
|
103
102
|
},
|
|
@@ -111,12 +110,14 @@ var package_default = {
|
|
|
111
110
|
"oxlint-tsgolint": "^7.0.2001",
|
|
112
111
|
"tree-sitter-bash": "0.25.1",
|
|
113
112
|
"tree-sitter-powershell": "0.26.4",
|
|
113
|
+
typebox: "1.3.19",
|
|
114
114
|
typescript: "7.0.2"
|
|
115
115
|
},
|
|
116
116
|
peerDependencies: {
|
|
117
117
|
"@earendil-works/pi-ai": "*",
|
|
118
118
|
"@earendil-works/pi-coding-agent": "*",
|
|
119
|
-
"@earendil-works/pi-tui": "*"
|
|
119
|
+
"@earendil-works/pi-tui": "*",
|
|
120
|
+
typebox: "*"
|
|
120
121
|
},
|
|
121
122
|
peerDependenciesMeta: {
|
|
122
123
|
"@earendil-works/pi-ai": {
|
|
@@ -570,7 +571,7 @@ class RipgrepInputError extends SiftLightError {
|
|
|
570
571
|
code;
|
|
571
572
|
guidance;
|
|
572
573
|
details;
|
|
573
|
-
constructor(code, message, guidance) {
|
|
574
|
+
constructor(code, message, guidance, repairExamples = []) {
|
|
574
575
|
super(`${message} ${guidance}`);
|
|
575
576
|
this.name = "RipgrepInputError";
|
|
576
577
|
this.code = code;
|
|
@@ -583,7 +584,8 @@ class RipgrepInputError extends SiftLightError {
|
|
|
583
584
|
reason: guidance
|
|
584
585
|
}
|
|
585
586
|
],
|
|
586
|
-
recovery: { action: "manual", reason: guidance }
|
|
587
|
+
recovery: { action: "manual", reason: guidance },
|
|
588
|
+
...repairExamples.length > 0 ? { repairExamples } : {}
|
|
587
589
|
};
|
|
588
590
|
}
|
|
589
591
|
}
|
|
@@ -965,6 +967,7 @@ var MODE_FIELD_SUMMARY = MODE_SUMMARY_MODES.map((mode) => modeFieldSummary(mode)
|
|
|
965
967
|
var REQUEST_FIELD_GUIDANCE = {
|
|
966
968
|
pattern: "pattern is regex by default; literal=true matches source text exactly",
|
|
967
969
|
literal: "literal=true makes pattern exact source text; anyOf/allOf already use literal semantics",
|
|
970
|
+
redact: "redact=true masks credential-like values and private-key bodies in displayed evidence; it does not sandbox search or change counts",
|
|
968
971
|
path: "path must be an existing exact file or root; use mode=files+query for unknown names",
|
|
969
972
|
query: "files+query matches known filename/path text (not glob patterns); omit files query to list every file under path; concept/hybrid query is natural language",
|
|
970
973
|
anyOf: "anyOf is case-sensitive exact-literal OR; omit pattern, allOf, literal, ignoreCase, roles",
|
|
@@ -981,6 +984,7 @@ function fieldGuidance(field) {
|
|
|
981
984
|
}
|
|
982
985
|
var GUIDANCE_FIELDS = [
|
|
983
986
|
"pattern",
|
|
987
|
+
"redact",
|
|
984
988
|
"literal",
|
|
985
989
|
"path",
|
|
986
990
|
"query",
|
|
@@ -1002,12 +1006,13 @@ var REQUEST_USAGE_GUIDANCE = [
|
|
|
1002
1006
|
].join("; ");
|
|
1003
1007
|
var MODEL_USAGE_GUIDANCE = [
|
|
1004
1008
|
"pattern is regex by default; literal=true matches source text exactly",
|
|
1005
|
-
"
|
|
1006
|
-
"
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
"outline
|
|
1010
|
-
|
|
1009
|
+
"redact=true masks credentials and private-key bodies in displayed evidence; use it for configs, logs or user directories; it does not sandbox search",
|
|
1010
|
+
"path is an existing exact file/root; use files+query for unknown names",
|
|
1011
|
+
"anyOf/allOf are exact-literal OR/AND variants; exclude pattern/literal",
|
|
1012
|
+
`limit/context are ordinary-search budgets (limit <= ${String(MAX_PAGE_SIZE)}); omit for hybrid/concept/outline/structure/inspect`,
|
|
1013
|
+
"outline needs a concrete JS/TS/TSX/Python-capable source file, not a directory; structure needs a nonempty AST pattern for JS/TS/TSX/Go, no lang; use capabilities before unfamiliar language operations",
|
|
1014
|
+
"imports/tests are static JS/TS/TSX candidates",
|
|
1015
|
+
"selectors: inspect, hybrid and capabilities use their cataloged fields",
|
|
1011
1016
|
"exact, any-of and max_results are not parameters"
|
|
1012
1017
|
].join("; ");
|
|
1013
1018
|
var MODE_CONTRACT_DESCRIPTION = [
|
|
@@ -2029,7 +2034,7 @@ async function resolveRipgrepExecutable() {
|
|
|
2029
2034
|
}
|
|
2030
2035
|
|
|
2031
2036
|
// src/ripgrep-diagnostics.ts
|
|
2032
|
-
import { resolve as resolve3 } from "node:path";
|
|
2037
|
+
import { basename, resolve as resolve3 } from "node:path";
|
|
2033
2038
|
var UNREADABLE_SUFFIX = /:\s+Permission denied(?:\s+\(os error 13\))?\s*$/iu;
|
|
2034
2039
|
var UNREADABLE_CODE = /\(os error 13\)\s*$/iu;
|
|
2035
2040
|
var RECOVERABLE_FILESYSTEM_CODE = /\(os error (?:4|5|22)\)\s*$/iu;
|
|
@@ -2068,15 +2073,70 @@ function missingPathFromDiagnostics(stderr) {
|
|
|
2068
2073
|
}
|
|
2069
2074
|
return;
|
|
2070
2075
|
}
|
|
2071
|
-
|
|
2076
|
+
var RIPGREP_RECOVERY_FIELDS = [
|
|
2077
|
+
"path",
|
|
2078
|
+
"glob",
|
|
2079
|
+
"exclude",
|
|
2080
|
+
"literal",
|
|
2081
|
+
"ignoreCase",
|
|
2082
|
+
"hidden",
|
|
2083
|
+
"ignorePolicy",
|
|
2084
|
+
"wholeWord",
|
|
2085
|
+
"context",
|
|
2086
|
+
"scope",
|
|
2087
|
+
"redact"
|
|
2088
|
+
];
|
|
2089
|
+
function copyRipgrepRecoveryFields(request) {
|
|
2090
|
+
const result = {};
|
|
2091
|
+
for (const field of RIPGREP_RECOVERY_FIELDS) {
|
|
2092
|
+
const value = request[field];
|
|
2093
|
+
if (value !== undefined)
|
|
2094
|
+
result[field] = value;
|
|
2095
|
+
}
|
|
2096
|
+
return result;
|
|
2097
|
+
}
|
|
2098
|
+
function ripgrepRepairExamples(code, request, redact) {
|
|
2099
|
+
if (request === undefined || redact && containsSensitiveText(request))
|
|
2100
|
+
return [];
|
|
2101
|
+
if (code === "E_REGEX_INVALID") {
|
|
2102
|
+
return [
|
|
2103
|
+
{
|
|
2104
|
+
label: "按原文查找",
|
|
2105
|
+
request: {
|
|
2106
|
+
mode: "matches",
|
|
2107
|
+
...copyRipgrepRecoveryFields(request),
|
|
2108
|
+
pattern: request.pattern,
|
|
2109
|
+
literal: true
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
];
|
|
2113
|
+
}
|
|
2114
|
+
if (code === "E_SEARCH_PATH_NOT_FOUND" && request.path !== undefined) {
|
|
2115
|
+
const fileName = basename(request.path.replaceAll("\\", "/"));
|
|
2116
|
+
if (fileName === "." || fileName === "/" || fileName.length === 0)
|
|
2117
|
+
return [];
|
|
2118
|
+
return [
|
|
2119
|
+
{
|
|
2120
|
+
label: "按文件名查找",
|
|
2121
|
+
request: {
|
|
2122
|
+
mode: "files",
|
|
2123
|
+
query: fileName,
|
|
2124
|
+
...request.redact === true ? { redact: true } : {}
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
];
|
|
2128
|
+
}
|
|
2129
|
+
return [];
|
|
2130
|
+
}
|
|
2131
|
+
function createRipgrepInputError(stderr, redact = false, request) {
|
|
2072
2132
|
const code = classifyRipgrepInputFailure(stderr);
|
|
2073
2133
|
if (code === "E_REGEX_INVALID") {
|
|
2074
|
-
return new RipgrepInputError(code, "ripgrep regex parse error: pattern is not a valid regular expression.", "Correct pattern or set literal=true for source text; no automatic literal conversion was attempted.");
|
|
2134
|
+
return new RipgrepInputError(code, "ripgrep regex parse error: pattern is not a valid regular expression.", "Correct pattern or set literal=true for source text; no automatic literal conversion was attempted.", ripgrepRepairExamples(code, request, redact));
|
|
2075
2135
|
}
|
|
2076
2136
|
if (code === "E_SEARCH_PATH_NOT_FOUND") {
|
|
2077
2137
|
const path = missingPathFromDiagnostics(stderr);
|
|
2078
2138
|
const location = path ? ` (${JSON.stringify(boundedRipgrepDiagnostic(path, redact))})` : "";
|
|
2079
|
-
return new RipgrepInputError(code, `A searched path${location} does not exist or became unavailable.`, "Provide an existing exact path, or use mode=files with query to discover an unknown filename; no retry or scope expansion was attempted.");
|
|
2139
|
+
return new RipgrepInputError(code, `A searched path${location} does not exist or became unavailable.`, "Provide an existing exact path, or use mode=files with query to discover an unknown filename; no retry or scope expansion was attempted.", ripgrepRepairExamples(code, request, redact));
|
|
2080
2140
|
}
|
|
2081
2141
|
return;
|
|
2082
2142
|
}
|
|
@@ -2297,7 +2357,7 @@ async function captureBatch(paths, revisions, signal, onRevisionError) {
|
|
|
2297
2357
|
if (signal?.aborted)
|
|
2298
2358
|
throw abortError();
|
|
2299
2359
|
}
|
|
2300
|
-
async function captureCandidateRevisions(executable, args, cwd, maxFiles, signal, redact = false) {
|
|
2360
|
+
async function captureCandidateRevisions(executable, args, cwd, maxFiles, signal, redact = false, request) {
|
|
2301
2361
|
const revisions = new Map;
|
|
2302
2362
|
let candidateCount = 0;
|
|
2303
2363
|
let enumerationTruncated = false;
|
|
@@ -2351,7 +2411,7 @@ async function captureCandidateRevisions(executable, args, cwd, maxFiles, signal
|
|
|
2351
2411
|
await captureBatch(batch, revisions, signal, recordMetadataFailure);
|
|
2352
2412
|
});
|
|
2353
2413
|
const diagnostics = classifyRipgrepDiagnostics(result.stderr);
|
|
2354
|
-
const inputError = createRipgrepInputError(result.stderr, redact);
|
|
2414
|
+
const inputError = createRipgrepInputError(result.stderr, redact, request);
|
|
2355
2415
|
const unreadable = [...diagnostics.unreadable, ...diagnostics.recoverable, ...metadataFailures];
|
|
2356
2416
|
if (inputError)
|
|
2357
2417
|
throw inputError;
|
|
@@ -2677,7 +2737,7 @@ function createRipgrepRunner(options = {}) {
|
|
|
2677
2737
|
...policy.ripgrepGlobArguments(validatedSearchPath),
|
|
2678
2738
|
"--",
|
|
2679
2739
|
searchTarget
|
|
2680
|
-
], ripgrepCwd, maxSourceRevisionFiles, signal, request.redact);
|
|
2740
|
+
], ripgrepCwd, maxSourceRevisionFiles, signal, request.redact, request);
|
|
2681
2741
|
let ignoredFileCount = 0;
|
|
2682
2742
|
let ignoredFileSamples = [];
|
|
2683
2743
|
let filesystemCoverage = "complete";
|
|
@@ -2707,7 +2767,7 @@ function createRipgrepRunner(options = {}) {
|
|
|
2707
2767
|
...policy.ripgrepGlobArguments(validatedSearchPath),
|
|
2708
2768
|
"--",
|
|
2709
2769
|
searchTarget
|
|
2710
|
-
], ripgrepCwd, maxSourceRevisionFiles, signal, request.redact);
|
|
2770
|
+
], ripgrepCwd, maxSourceRevisionFiles, signal, request.redact, request);
|
|
2711
2771
|
const ignored = [...allCandidates.revisions.keys()].filter((path) => !before.revisions.has(path));
|
|
2712
2772
|
ignoredFileCount = ignored.length;
|
|
2713
2773
|
ignoredFileSamples = ignored.slice(0, 20).map((path) => displayPath(path, cwd).displayPath);
|
|
@@ -2751,7 +2811,7 @@ function createRipgrepRunner(options = {}) {
|
|
|
2751
2811
|
}
|
|
2752
2812
|
}));
|
|
2753
2813
|
const diagnostics = classifyRipgrepDiagnostics(stderr);
|
|
2754
|
-
const inputError = createRipgrepInputError(stderr, request.redact);
|
|
2814
|
+
const inputError = createRipgrepInputError(stderr, request.redact, request);
|
|
2755
2815
|
if (inputError)
|
|
2756
2816
|
throw inputError;
|
|
2757
2817
|
const filesystemDiagnostics = [...diagnostics.unreadable, ...diagnostics.recoverable];
|
|
@@ -14857,7 +14917,7 @@ var siftLightSchema = Type.Object({
|
|
|
14857
14917
|
description: "Named exact-literal checks for mode=audit. Each finding is present, absent_with_complete_coverage, or unknown."
|
|
14858
14918
|
})),
|
|
14859
14919
|
redact: Type.Optional(Type.Boolean({
|
|
14860
|
-
description: "Optional display-only masking for credential-like values and private-key bodies. Default false. It never changes searched files, admitted matches, counts
|
|
14920
|
+
description: "Optional display-only masking for credential-like values and private-key bodies. Default false. Use it for configuration, logs and user-directory searches when returned text may contain secrets. It never changes the search scope, searched files, admitted matches, counts or cursor completeness, and it is not a sandbox."
|
|
14861
14921
|
})),
|
|
14862
14922
|
modifiedAfter: Type.Optional(Type.Integer({
|
|
14863
14923
|
minimum: 0,
|
|
@@ -169,6 +169,8 @@ export const REQUEST_FIELD_GUIDANCE: Partial<Record<RequestField, string>> = {
|
|
|
169
169
|
pattern: "pattern is regex by default; literal=true matches source text exactly",
|
|
170
170
|
literal:
|
|
171
171
|
"literal=true makes pattern exact source text; anyOf/allOf already use literal semantics",
|
|
172
|
+
redact:
|
|
173
|
+
"redact=true masks credential-like values and private-key bodies in displayed evidence; it does not sandbox search or change counts",
|
|
172
174
|
path: "path must be an existing exact file or root; use mode=files+query for unknown names",
|
|
173
175
|
query:
|
|
174
176
|
"files+query matches known filename/path text (not glob patterns); omit files query to list every file under path; concept/hybrid query is natural language",
|
|
@@ -193,6 +195,7 @@ export function fieldGuidance(field: RequestField): string {
|
|
|
193
195
|
|
|
194
196
|
const GUIDANCE_FIELDS = [
|
|
195
197
|
"pattern",
|
|
198
|
+
"redact",
|
|
196
199
|
"literal",
|
|
197
200
|
"path",
|
|
198
201
|
"query",
|
|
@@ -219,12 +222,13 @@ export const REQUEST_USAGE_GUIDANCE = [
|
|
|
219
222
|
|
|
220
223
|
export const MODEL_USAGE_GUIDANCE = [
|
|
221
224
|
"pattern is regex by default; literal=true matches source text exactly",
|
|
222
|
-
"
|
|
223
|
-
"
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
"outline
|
|
227
|
-
|
|
225
|
+
"redact=true masks credentials and private-key bodies in displayed evidence; use it for configs, logs or user directories; it does not sandbox search",
|
|
226
|
+
"path is an existing exact file/root; use files+query for unknown names",
|
|
227
|
+
"anyOf/allOf are exact-literal OR/AND variants; exclude pattern/literal",
|
|
228
|
+
`limit/context are ordinary-search budgets (limit <= ${String(MAX_PAGE_SIZE)}); omit for hybrid/concept/outline/structure/inspect`,
|
|
229
|
+
"outline needs a concrete JS/TS/TSX/Python-capable source file, not a directory; structure needs a nonempty AST pattern for JS/TS/TSX/Go, no lang; use capabilities before unfamiliar language operations",
|
|
230
|
+
"imports/tests are static JS/TS/TSX candidates",
|
|
231
|
+
"selectors: inspect, hybrid and capabilities use their cataloged fields",
|
|
228
232
|
"exact, any-of and max_results are not parameters",
|
|
229
233
|
].join("; ");
|
|
230
234
|
|
package/src/rg.ts
CHANGED
|
@@ -435,6 +435,7 @@ export function createRipgrepRunner(options: RipgrepRunnerOptions = {}) {
|
|
|
435
435
|
maxSourceRevisionFiles,
|
|
436
436
|
signal,
|
|
437
437
|
request.redact,
|
|
438
|
+
request,
|
|
438
439
|
);
|
|
439
440
|
let ignoredFileCount = 0;
|
|
440
441
|
let ignoredFileSamples: string[] = [];
|
|
@@ -475,6 +476,7 @@ export function createRipgrepRunner(options: RipgrepRunnerOptions = {}) {
|
|
|
475
476
|
maxSourceRevisionFiles,
|
|
476
477
|
signal,
|
|
477
478
|
request.redact,
|
|
479
|
+
request,
|
|
478
480
|
);
|
|
479
481
|
const ignored = [...allCandidates.revisions.keys()].filter(
|
|
480
482
|
(path) => !before.revisions.has(path),
|
|
@@ -528,7 +530,7 @@ export function createRipgrepRunner(options: RipgrepRunnerOptions = {}) {
|
|
|
528
530
|
}),
|
|
529
531
|
);
|
|
530
532
|
const diagnostics = classifyRipgrepDiagnostics(stderr);
|
|
531
|
-
const inputError = createRipgrepInputError(stderr, request.redact);
|
|
533
|
+
const inputError = createRipgrepInputError(stderr, request.redact, request);
|
|
532
534
|
if (inputError) throw inputError;
|
|
533
535
|
const filesystemDiagnostics = [...diagnostics.unreadable, ...diagnostics.recoverable];
|
|
534
536
|
if (hasRequestedRootUnreadable(diagnostics.unreadable, cwd, validatedSearchPath))
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import { resolve } from "node:path";
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { basename, resolve } from "node:path";
|
|
2
|
+
import {
|
|
3
|
+
RipgrepInputError,
|
|
4
|
+
type RipgrepInputErrorCode,
|
|
5
|
+
type SiftLightDiagnosticRepairExample,
|
|
6
|
+
} from "./errors.js";
|
|
7
|
+
import { containsSensitiveText, redactDiagnosticText } from "./redaction.js";
|
|
8
|
+
import type { SearchRequest } from "./types.js";
|
|
4
9
|
|
|
5
10
|
export interface RipgrepUnreadableDiagnostic {
|
|
6
11
|
message: string;
|
|
@@ -63,9 +68,85 @@ function missingPathFromDiagnostics(stderr: string): string | undefined {
|
|
|
63
68
|
return undefined;
|
|
64
69
|
}
|
|
65
70
|
|
|
71
|
+
export type RipgrepRecoveryRequest = Pick<
|
|
72
|
+
SearchRequest,
|
|
73
|
+
| "pattern"
|
|
74
|
+
| "path"
|
|
75
|
+
| "glob"
|
|
76
|
+
| "exclude"
|
|
77
|
+
| "literal"
|
|
78
|
+
| "ignoreCase"
|
|
79
|
+
| "hidden"
|
|
80
|
+
| "ignorePolicy"
|
|
81
|
+
| "wholeWord"
|
|
82
|
+
| "context"
|
|
83
|
+
| "scope"
|
|
84
|
+
| "redact"
|
|
85
|
+
>;
|
|
86
|
+
|
|
87
|
+
const RIPGREP_RECOVERY_FIELDS = [
|
|
88
|
+
"path",
|
|
89
|
+
"glob",
|
|
90
|
+
"exclude",
|
|
91
|
+
"literal",
|
|
92
|
+
"ignoreCase",
|
|
93
|
+
"hidden",
|
|
94
|
+
"ignorePolicy",
|
|
95
|
+
"wholeWord",
|
|
96
|
+
"context",
|
|
97
|
+
"scope",
|
|
98
|
+
"redact",
|
|
99
|
+
] as const satisfies readonly (keyof RipgrepRecoveryRequest)[];
|
|
100
|
+
|
|
101
|
+
function copyRipgrepRecoveryFields(request: RipgrepRecoveryRequest): Record<string, unknown> {
|
|
102
|
+
const result: Record<string, unknown> = {};
|
|
103
|
+
for (const field of RIPGREP_RECOVERY_FIELDS) {
|
|
104
|
+
const value = request[field];
|
|
105
|
+
if (value !== undefined) result[field] = value;
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function ripgrepRepairExamples(
|
|
111
|
+
code: RipgrepInputErrorCode,
|
|
112
|
+
request: RipgrepRecoveryRequest | undefined,
|
|
113
|
+
redact: boolean,
|
|
114
|
+
): readonly SiftLightDiagnosticRepairExample[] {
|
|
115
|
+
if (request === undefined || (redact && containsSensitiveText(request))) return [];
|
|
116
|
+
if (code === "E_REGEX_INVALID") {
|
|
117
|
+
return [
|
|
118
|
+
{
|
|
119
|
+
label: "按原文查找",
|
|
120
|
+
request: {
|
|
121
|
+
mode: "matches",
|
|
122
|
+
...copyRipgrepRecoveryFields(request),
|
|
123
|
+
pattern: request.pattern,
|
|
124
|
+
literal: true,
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
}
|
|
129
|
+
if (code === "E_SEARCH_PATH_NOT_FOUND" && request.path !== undefined) {
|
|
130
|
+
const fileName = basename(request.path.replaceAll("\\", "/"));
|
|
131
|
+
if (fileName === "." || fileName === "/" || fileName.length === 0) return [];
|
|
132
|
+
return [
|
|
133
|
+
{
|
|
134
|
+
label: "按文件名查找",
|
|
135
|
+
request: {
|
|
136
|
+
mode: "files",
|
|
137
|
+
query: fileName,
|
|
138
|
+
...(request.redact === true ? { redact: true } : {}),
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
|
|
66
146
|
export function createRipgrepInputError(
|
|
67
147
|
stderr: string,
|
|
68
148
|
redact = false,
|
|
149
|
+
request?: RipgrepRecoveryRequest,
|
|
69
150
|
): RipgrepInputError | undefined {
|
|
70
151
|
const code = classifyRipgrepInputFailure(stderr);
|
|
71
152
|
if (code === "E_REGEX_INVALID") {
|
|
@@ -73,6 +154,7 @@ export function createRipgrepInputError(
|
|
|
73
154
|
code,
|
|
74
155
|
"ripgrep regex parse error: pattern is not a valid regular expression.",
|
|
75
156
|
"Correct pattern or set literal=true for source text; no automatic literal conversion was attempted.",
|
|
157
|
+
ripgrepRepairExamples(code, request, redact),
|
|
76
158
|
);
|
|
77
159
|
}
|
|
78
160
|
if (code === "E_SEARCH_PATH_NOT_FOUND") {
|
|
@@ -82,6 +164,7 @@ export function createRipgrepInputError(
|
|
|
82
164
|
code,
|
|
83
165
|
`A searched path${location} does not exist or became unavailable.`,
|
|
84
166
|
"Provide an existing exact path, or use mode=files with query to discover an unknown filename; no retry or scope expansion was attempted.",
|
|
167
|
+
ripgrepRepairExamples(code, request, redact),
|
|
85
168
|
);
|
|
86
169
|
}
|
|
87
170
|
// Known input failures above intentionally omit ripgrep's raw pattern and path echo.
|
package/src/scan-revisions.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
boundedRipgrepDiagnostic,
|
|
6
6
|
classifyRipgrepDiagnostics,
|
|
7
7
|
createRipgrepInputError,
|
|
8
|
+
type RipgrepRecoveryRequest,
|
|
8
9
|
type RipgrepUnreadableDiagnostic,
|
|
9
10
|
} from "./ripgrep-diagnostics.js";
|
|
10
11
|
import { getSourceRevision, sameSourceRevision } from "./source.js";
|
|
@@ -48,6 +49,7 @@ export async function captureCandidateRevisions(
|
|
|
48
49
|
maxFiles: number,
|
|
49
50
|
signal?: AbortSignal,
|
|
50
51
|
redact = false,
|
|
52
|
+
request?: RipgrepRecoveryRequest,
|
|
51
53
|
): Promise<{
|
|
52
54
|
revisions: Map<string, SourceRevision>;
|
|
53
55
|
unreadable: RipgrepUnreadableDiagnostic[];
|
|
@@ -111,7 +113,7 @@ export async function captureCandidateRevisions(
|
|
|
111
113
|
},
|
|
112
114
|
);
|
|
113
115
|
const diagnostics = classifyRipgrepDiagnostics(result.stderr);
|
|
114
|
-
const inputError = createRipgrepInputError(result.stderr, redact);
|
|
116
|
+
const inputError = createRipgrepInputError(result.stderr, redact, request);
|
|
115
117
|
const unreadable = [...diagnostics.unreadable, ...diagnostics.recoverable, ...metadataFailures];
|
|
116
118
|
if (inputError) throw inputError;
|
|
117
119
|
if (result.code === 2 && diagnostics.other.length === 0 && unreadable.length > 0)
|
package/src/tool-schema.ts
CHANGED
|
@@ -234,7 +234,7 @@ export const siftLightSchema = Type.Object({
|
|
|
234
234
|
redact: Type.Optional(
|
|
235
235
|
Type.Boolean({
|
|
236
236
|
description:
|
|
237
|
-
"Optional display-only masking for credential-like values and private-key bodies. Default false. It never changes searched files, admitted matches, counts
|
|
237
|
+
"Optional display-only masking for credential-like values and private-key bodies. Default false. Use it for configuration, logs and user-directory searches when returned text may contain secrets. It never changes the search scope, searched files, admitted matches, counts or cursor completeness, and it is not a sandbox.",
|
|
238
238
|
}),
|
|
239
239
|
),
|
|
240
240
|
modifiedAfter: Type.Optional(
|