scai 0.1.175 → 0.1.176
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/dist/agents/MainAgent.js +68 -77
- package/dist/agents/fileCheckStep.js +13 -5
- package/dist/agents/guardPolicy.js +43 -0
- package/dist/fileRules/ignoredPaths.js +17 -0
- package/dist/pipeline/modules/fileSearchModule.js +8 -6
- package/dist/utils/resolveTargetsToFiles.js +5 -0
- package/package.json +1 -1
package/dist/agents/MainAgent.js
CHANGED
|
@@ -28,6 +28,8 @@ import { structuralPreloadStep } from "./structuralPreloadStep.js";
|
|
|
28
28
|
import { extractFileReferences } from "../utils/extractFileReferences.js";
|
|
29
29
|
import { PREFILTER_STOP_WORDS } from "../fileRules/stopWords.js";
|
|
30
30
|
import { MAX_WELL_KNOWN_REPO_FILES, WELL_KNOWN_REPO_FILE_BASENAMES } from "../fileRules/wellKnownRepoFiles.js";
|
|
31
|
+
import { isPathIgnoredByFolderGlobs } from "../fileRules/ignoredPaths.js";
|
|
32
|
+
import { canExecutePhase, canExecuteRoute, canExecuteScope } from "./guardPolicy.js";
|
|
31
33
|
import chalk from "chalk";
|
|
32
34
|
import path from "path";
|
|
33
35
|
import fs from "fs";
|
|
@@ -172,12 +174,12 @@ export class MainAgent {
|
|
|
172
174
|
break;
|
|
173
175
|
}
|
|
174
176
|
// ---------------- INFORMATION ACQUISITION ----------------
|
|
175
|
-
const canRouteSearchExpansion = this.
|
|
177
|
+
const canRouteSearchExpansion = canExecuteRoute(this.context, "search-expand");
|
|
176
178
|
if (!canRouteSearchExpansion) {
|
|
177
179
|
this.logLine("PLAN", "infoPlanGen", undefined, "skipped (routing disallows search expansion)", { highlight: false });
|
|
178
180
|
}
|
|
179
|
-
else if (this.
|
|
180
|
-
this.
|
|
181
|
+
else if (canExecutePhase(this.context, "planning") &&
|
|
182
|
+
canExecuteScope(this.context, "planning")) {
|
|
181
183
|
const t = this.startTimer();
|
|
182
184
|
await infoPlanGenStep.run(this.context);
|
|
183
185
|
const infoPlan = this.context.analysis?.planSuggestion?.plan ?? { steps: [] };
|
|
@@ -208,7 +210,7 @@ export class MainAgent {
|
|
|
208
210
|
*/
|
|
209
211
|
async runResearch() {
|
|
210
212
|
var _a, _b;
|
|
211
|
-
if (!this.
|
|
213
|
+
if (!canExecuteRoute(this.context, "research")) {
|
|
212
214
|
this.logLine("RESEARCH", "taskStepSeed", undefined, "skipped (route disallows research)");
|
|
213
215
|
return;
|
|
214
216
|
}
|
|
@@ -298,7 +300,7 @@ export class MainAgent {
|
|
|
298
300
|
var _a, _b;
|
|
299
301
|
if (!this.context.task)
|
|
300
302
|
return;
|
|
301
|
-
if (!this.
|
|
303
|
+
if (!canExecutePhase(this.context, "planning") || !canExecuteScope(this.context, "planning"))
|
|
302
304
|
return;
|
|
303
305
|
(_a = this.context).analysis || (_a.analysis = {});
|
|
304
306
|
(_b = this.context.task).taskSteps || (_b.taskSteps = []);
|
|
@@ -478,6 +480,9 @@ export class MainAgent {
|
|
|
478
480
|
// Step-level iterations
|
|
479
481
|
// ---------------------------
|
|
480
482
|
const stepAction = await this.runStepIterations(taskStep);
|
|
483
|
+
if (stepAction === "expand-scope") {
|
|
484
|
+
this.applyScopeExpansionRouteHint();
|
|
485
|
+
}
|
|
481
486
|
this.finishTaskStep(taskStep, stepCount, stepAction);
|
|
482
487
|
}
|
|
483
488
|
this.logLine("TASK", "Max task step limit reached — stopping work loop", undefined, undefined, { highlight: false });
|
|
@@ -517,6 +522,8 @@ export class MainAgent {
|
|
|
517
522
|
return "request-feedback";
|
|
518
523
|
if (nextAction === "redo-step")
|
|
519
524
|
continue;
|
|
525
|
+
if (nextAction === "expand-scope")
|
|
526
|
+
return "expand-scope";
|
|
520
527
|
}
|
|
521
528
|
return "continue";
|
|
522
529
|
}
|
|
@@ -532,7 +539,7 @@ export class MainAgent {
|
|
|
532
539
|
await this.executeResearchTaskStep(taskStep);
|
|
533
540
|
return;
|
|
534
541
|
}
|
|
535
|
-
if (this.
|
|
542
|
+
if (canExecutePhase(this.context, "analysis") && canExecuteScope(this.context, "analysis")) {
|
|
536
543
|
const tAnalysis = this.startTimer();
|
|
537
544
|
await analysisPlanGenStep.run(this.context);
|
|
538
545
|
this.logLine("PLAN", "analysisPlanGen", tAnalysis(), undefined, { highlight: false });
|
|
@@ -545,7 +552,9 @@ export class MainAgent {
|
|
|
545
552
|
if (this.context.analysis)
|
|
546
553
|
this.context.analysis.planSuggestion = undefined;
|
|
547
554
|
}
|
|
548
|
-
if (
|
|
555
|
+
if (canExecutePhase(this.context, "transform") &&
|
|
556
|
+
canExecuteScope(this.context, "transform") &&
|
|
557
|
+
canExecuteRoute(this.context, "transform")) {
|
|
549
558
|
const tTransform = this.startTimer();
|
|
550
559
|
await transformPlanGenStep.run(this.context);
|
|
551
560
|
this.logLine("PLAN", "transformPlanGen", tTransform(), undefined, { highlight: false });
|
|
@@ -558,7 +567,7 @@ export class MainAgent {
|
|
|
558
567
|
}
|
|
559
568
|
if (this.context.analysis)
|
|
560
569
|
this.context.analysis.planSuggestion = undefined;
|
|
561
|
-
if (this.
|
|
570
|
+
if (canExecutePhase(this.context, "write") && canExecuteScope(this.context, "write")) {
|
|
562
571
|
const tWrite = this.startTimer();
|
|
563
572
|
await writeFileStep.run({ query: this.query, context: this.context });
|
|
564
573
|
this.logLine("WRITE", "writeFileStep", tWrite());
|
|
@@ -1165,6 +1174,8 @@ export class MainAgent {
|
|
|
1165
1174
|
const existsOrResearch = (filePath) => {
|
|
1166
1175
|
if (filePath.startsWith("__research__/"))
|
|
1167
1176
|
return true;
|
|
1177
|
+
if (isPathIgnoredByFolderGlobs(filePath))
|
|
1178
|
+
return false;
|
|
1168
1179
|
return fs.existsSync(filePath);
|
|
1169
1180
|
};
|
|
1170
1181
|
let removedRelated = 0;
|
|
@@ -1218,7 +1229,7 @@ export class MainAgent {
|
|
|
1218
1229
|
*/
|
|
1219
1230
|
getTaskStepBudget() {
|
|
1220
1231
|
const scope = this.context.analysis?.scopeType ?? "repo-wide";
|
|
1221
|
-
if (this.
|
|
1232
|
+
if (canExecuteRoute(this.context, "research"))
|
|
1222
1233
|
return 10;
|
|
1223
1234
|
if (scope === "multi-file")
|
|
1224
1235
|
return 7;
|
|
@@ -1231,7 +1242,7 @@ export class MainAgent {
|
|
|
1231
1242
|
* Example: require at least two analyzed files plus one understanding signal.
|
|
1232
1243
|
*/
|
|
1233
1244
|
isResearchGateSatisfied() {
|
|
1234
|
-
if (!this.
|
|
1245
|
+
if (!canExecuteRoute(this.context, "research"))
|
|
1235
1246
|
return true;
|
|
1236
1247
|
const scope = this.context.analysis?.scopeType ?? "repo-wide";
|
|
1237
1248
|
const researchPlanCount = this.context.task?.taskSteps?.filter(s => typeof s.action === "string" && s.action.startsWith("research-")).length ?? 0;
|
|
@@ -1302,29 +1313,45 @@ export class MainAgent {
|
|
|
1302
1313
|
this.context.task.status = status;
|
|
1303
1314
|
this.persistTaskDataForRun();
|
|
1304
1315
|
}
|
|
1316
|
+
applyScopeExpansionRouteHint() {
|
|
1317
|
+
var _a;
|
|
1318
|
+
(_a = this.context).analysis || (_a.analysis = {});
|
|
1319
|
+
const route = this.context.analysis.routingDecision;
|
|
1320
|
+
if (!route)
|
|
1321
|
+
return;
|
|
1322
|
+
route.decision = "needs-info";
|
|
1323
|
+
route.allowSearch = true;
|
|
1324
|
+
route.scopeLocked = false;
|
|
1325
|
+
route.rationale = `${route.rationale}; stepReasoning=expand-scope`;
|
|
1326
|
+
this.logLine("TASK", "Route updated from step reasoning", undefined, "expand-scope requested; re-enabled search expansion");
|
|
1327
|
+
}
|
|
1305
1328
|
startTaskStep(taskStep, stepCount) {
|
|
1306
1329
|
this.ensureWorkingFilesLoaded([taskStep.filePath], "Current task step");
|
|
1330
|
+
const db = getDbForRepo();
|
|
1331
|
+
this.ensureTaskIdentityForPersistence(db);
|
|
1307
1332
|
this.context.task.currentStep = taskStep;
|
|
1308
1333
|
taskStep.taskId = this.taskId;
|
|
1309
1334
|
taskStep.stepIndex = stepCount;
|
|
1310
1335
|
taskStep.status = "pending";
|
|
1311
|
-
persistTaskStepInsert(taskStep,
|
|
1336
|
+
persistTaskStepInsert(taskStep, db);
|
|
1312
1337
|
const displayPath = this.formatTaskStepDisplayPath(taskStep.filePath);
|
|
1313
1338
|
this.logLine("NEW STEP", `Processing taskStep ${stepCount}`, undefined, displayPath, { highlight: true });
|
|
1314
1339
|
taskStep.startTime = Date.now();
|
|
1315
|
-
persistTaskStepStart(taskStep,
|
|
1340
|
+
persistTaskStepStart(taskStep, db);
|
|
1316
1341
|
}
|
|
1317
1342
|
finishTaskStep(taskStep, stepCount, stepAction) {
|
|
1343
|
+
const db = getDbForRepo();
|
|
1344
|
+
this.ensureTaskIdentityForPersistence(db);
|
|
1318
1345
|
const displayPath = this.formatTaskStepDisplayPath(taskStep.filePath);
|
|
1319
1346
|
taskStep.endTime = Date.now();
|
|
1320
1347
|
if (stepAction === "complete") {
|
|
1321
1348
|
taskStep.status = "completed";
|
|
1322
|
-
persistTaskStepCompletion(taskStep,
|
|
1349
|
+
persistTaskStepCompletion(taskStep, db);
|
|
1323
1350
|
this.logLine("STEP-DONE", `Completed taskStep ${stepCount}`, undefined, displayPath, { highlight: false });
|
|
1324
1351
|
return;
|
|
1325
1352
|
}
|
|
1326
1353
|
taskStep.status = "pending";
|
|
1327
|
-
persistTaskStepCompletion(taskStep,
|
|
1354
|
+
persistTaskStepCompletion(taskStep, db);
|
|
1328
1355
|
this.logLine("STEP", `Pending taskStep ${stepCount}`, undefined, displayPath);
|
|
1329
1356
|
}
|
|
1330
1357
|
/**
|
|
@@ -1336,69 +1363,6 @@ export class MainAgent {
|
|
|
1336
1363
|
? filePath.replace("__research__/", "research/")
|
|
1337
1364
|
: filePath;
|
|
1338
1365
|
}
|
|
1339
|
-
/* ───────────── execution gates ───────────── */
|
|
1340
|
-
/**
|
|
1341
|
-
* Gate model:
|
|
1342
|
-
* 1) Phase + scope gates decide coarse permissions (what broad work is allowed).
|
|
1343
|
-
* 2) Route gate decides finer sub-decisions within those allowed areas (what to do next).
|
|
1344
|
-
*/
|
|
1345
|
-
/**
|
|
1346
|
-
* Gate 1: Is this kind of work allowed at all?
|
|
1347
|
-
* Plain meaning: checks capability rules (e.g. read-only vs file-writing).
|
|
1348
|
-
* Example: for docs-only mode, analysis/planning are blocked, and writes are limited.
|
|
1349
|
-
*/
|
|
1350
|
-
canExecutePhase(phase) {
|
|
1351
|
-
const constraints = this.context.executionControl?.constraints;
|
|
1352
|
-
const docsOnly = constraints?.docsOnly ?? false;
|
|
1353
|
-
let allowed = false;
|
|
1354
|
-
switch (phase) {
|
|
1355
|
-
case "analysis":
|
|
1356
|
-
case "planning":
|
|
1357
|
-
allowed = !docsOnly;
|
|
1358
|
-
break;
|
|
1359
|
-
case "transform":
|
|
1360
|
-
case "write":
|
|
1361
|
-
allowed = constraints?.allowFileWrites ?? false;
|
|
1362
|
-
break;
|
|
1363
|
-
}
|
|
1364
|
-
return allowed;
|
|
1365
|
-
}
|
|
1366
|
-
/* ───────────── scope gates ───────────── */
|
|
1367
|
-
/**
|
|
1368
|
-
* Gate 2: Is this work allowed for the current scope size?
|
|
1369
|
-
* Plain meaning: checks scope rules (none/single/multi/repo-wide).
|
|
1370
|
-
* Example: if scope is "analysis", only analysis/planning run and transform/write are blocked.
|
|
1371
|
-
*/
|
|
1372
|
-
canExecuteScope(phase) {
|
|
1373
|
-
const scope = this.context.analysis?.scopeType ?? "repo-wide";
|
|
1374
|
-
let allowed = false;
|
|
1375
|
-
switch (scope) {
|
|
1376
|
-
case "none":
|
|
1377
|
-
allowed = phase === "analysis" || phase === "planning";
|
|
1378
|
-
break;
|
|
1379
|
-
default:
|
|
1380
|
-
allowed = true;
|
|
1381
|
-
}
|
|
1382
|
-
return allowed;
|
|
1383
|
-
}
|
|
1384
|
-
/**
|
|
1385
|
-
* Gate 3: Does this request path want this action right now?
|
|
1386
|
-
* Plain meaning: checks route-specific intent from routingDecision.
|
|
1387
|
-
* Example: search expansion is skipped when routing says allowSearch=false.
|
|
1388
|
-
*/
|
|
1389
|
-
canExecuteRoute(action) {
|
|
1390
|
-
const routing = this.context.analysis?.routingDecision;
|
|
1391
|
-
switch (action) {
|
|
1392
|
-
case "search-expand":
|
|
1393
|
-
return routing?.allowSearch ?? true;
|
|
1394
|
-
case "transform":
|
|
1395
|
-
return routing?.allowTransform ?? true;
|
|
1396
|
-
case "research":
|
|
1397
|
-
return routing?.allowResearch ?? false;
|
|
1398
|
-
default:
|
|
1399
|
-
return true;
|
|
1400
|
-
}
|
|
1401
|
-
}
|
|
1402
1366
|
/* ----------------------------------- */
|
|
1403
1367
|
/* ------------- helpers ------------- */
|
|
1404
1368
|
/* ----------------------------------- */
|
|
@@ -1479,6 +1443,33 @@ export class MainAgent {
|
|
|
1479
1443
|
}
|
|
1480
1444
|
}
|
|
1481
1445
|
}
|
|
1446
|
+
/**
|
|
1447
|
+
* Ensures the current task id exists in the active DB before step persistence.
|
|
1448
|
+
* Example: if repo/db context switched, re-create task row and rebind step taskIds.
|
|
1449
|
+
*/
|
|
1450
|
+
ensureTaskIdentityForPersistence(db) {
|
|
1451
|
+
var _a;
|
|
1452
|
+
const activeTaskId = this.taskId ?? this.context.task?.id;
|
|
1453
|
+
if (!activeTaskId) {
|
|
1454
|
+
this.taskId = bootTaskForRepo(this.context, db, this.logLine.bind(this));
|
|
1455
|
+
this.context.task.id = this.taskId;
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
const row = db.prepare("SELECT id FROM tasks WHERE id = ?").get(activeTaskId);
|
|
1459
|
+
if (row?.id) {
|
|
1460
|
+
this.taskId = activeTaskId;
|
|
1461
|
+
this.context.task.id = activeTaskId;
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
const reboundTaskId = bootTaskForRepo(this.context, db, this.logLine.bind(this));
|
|
1465
|
+
this.taskId = reboundTaskId;
|
|
1466
|
+
this.context.task.id = reboundTaskId;
|
|
1467
|
+
(_a = this.context.task).taskSteps || (_a.taskSteps = []);
|
|
1468
|
+
for (const step of this.context.task.taskSteps) {
|
|
1469
|
+
step.taskId = reboundTaskId;
|
|
1470
|
+
}
|
|
1471
|
+
this.logLine("TASK", "taskId rebound", undefined, `previous=${activeTaskId}, rebound=${reboundTaskId}`);
|
|
1472
|
+
}
|
|
1482
1473
|
}
|
|
1483
1474
|
function scoreCandidateFiles(filePaths, relatedFileScores, retrievalQuery) {
|
|
1484
1475
|
const explicitRefs = extractFileReferences(retrievalQuery, { lowercase: true });
|
|
@@ -3,6 +3,7 @@ import { generate } from "../lib/generate.js";
|
|
|
3
3
|
import { logInputOutput } from "../utils/promptLogHelper.js";
|
|
4
4
|
import { cleanupModule } from "../pipeline/modules/cleanupModule.js";
|
|
5
5
|
import path from "path";
|
|
6
|
+
import { isPathIgnoredByFolderGlobs } from "../fileRules/ignoredPaths.js";
|
|
6
7
|
export async function fileCheckStep(context) {
|
|
7
8
|
var _a;
|
|
8
9
|
context.analysis ?? (context.analysis = {});
|
|
@@ -10,9 +11,7 @@ export async function fileCheckStep(context) {
|
|
|
10
11
|
const intent = context.analysis.intent;
|
|
11
12
|
const planSuggestion = context.analysis.planSuggestion?.text ?? "";
|
|
12
13
|
// Step 1: gather known files from initContext only
|
|
13
|
-
const knownFiles = new Set([
|
|
14
|
-
...(context.initContext?.relatedFiles ?? []),
|
|
15
|
-
]);
|
|
14
|
+
const knownFiles = new Set((context.initContext?.relatedFiles ?? []).filter(filePath => !isPathIgnoredByFolderGlobs(filePath)));
|
|
16
15
|
// Step 2: extract file names from normalizedQuery or planSuggestion
|
|
17
16
|
const extractedFiles = extractFilesFromAnalysis(context.analysis);
|
|
18
17
|
// Step 3: populate focus with safe defaults
|
|
@@ -138,16 +137,25 @@ Task:
|
|
|
138
137
|
// Merge parsed output safely
|
|
139
138
|
if (Array.isArray(parsed.selectedFiles)) {
|
|
140
139
|
const existing = new Set(context.analysis.focus.selectedFiles);
|
|
140
|
+
const safeSelected = parsed.selectedFiles
|
|
141
|
+
.filter((f) => typeof f === "string" && knownFiles.has(f) && !isPathIgnoredByFolderGlobs(f));
|
|
141
142
|
context.analysis.focus.selectedFiles = [
|
|
142
143
|
...context.analysis.focus.selectedFiles,
|
|
143
|
-
...
|
|
144
|
+
...safeSelected.filter((f) => !existing.has(f))
|
|
144
145
|
];
|
|
145
146
|
}
|
|
146
147
|
if (Array.isArray(parsed.candidateFiles)) {
|
|
147
148
|
const existing = new Set(context.analysis.focus.candidateFiles);
|
|
149
|
+
const safeCandidates = parsed.candidateFiles
|
|
150
|
+
.filter((f) => typeof f === "string")
|
|
151
|
+
.filter((f) => {
|
|
152
|
+
if (knownFiles.has(f))
|
|
153
|
+
return !isPathIgnoredByFolderGlobs(f);
|
|
154
|
+
return /^[\w\-./]+\.(js|ts|jsx|tsx|py|java|go|rs|cpp|c|cs|md)$/i.test(f);
|
|
155
|
+
});
|
|
148
156
|
context.analysis.focus.candidateFiles = [
|
|
149
157
|
...context.analysis.focus.candidateFiles,
|
|
150
|
-
...
|
|
158
|
+
...safeCandidates.filter((f) => !existing.has(f))
|
|
151
159
|
];
|
|
152
160
|
}
|
|
153
161
|
if (typeof parsed.rationale === "string") {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gate 1: capability constraints (execution control).
|
|
3
|
+
*/
|
|
4
|
+
export function canExecutePhase(context, phase) {
|
|
5
|
+
const constraints = context.executionControl?.constraints;
|
|
6
|
+
const docsOnly = constraints?.docsOnly ?? false;
|
|
7
|
+
switch (phase) {
|
|
8
|
+
case "analysis":
|
|
9
|
+
case "planning":
|
|
10
|
+
return !docsOnly;
|
|
11
|
+
case "transform":
|
|
12
|
+
case "write":
|
|
13
|
+
return constraints?.allowFileWrites ?? false;
|
|
14
|
+
default:
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Gate 2: scope constraints (none/single/multi/repo-wide).
|
|
20
|
+
*/
|
|
21
|
+
export function canExecuteScope(context, phase) {
|
|
22
|
+
const scope = context.analysis?.scopeType ?? "repo-wide";
|
|
23
|
+
if (scope === "none") {
|
|
24
|
+
return phase === "analysis" || phase === "planning";
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Gate 3: route constraints (dynamic routing decisions).
|
|
30
|
+
*/
|
|
31
|
+
export function canExecuteRoute(context, action) {
|
|
32
|
+
const routing = context.analysis?.routingDecision;
|
|
33
|
+
switch (action) {
|
|
34
|
+
case "search-expand":
|
|
35
|
+
return routing?.allowSearch ?? true;
|
|
36
|
+
case "transform":
|
|
37
|
+
return routing?.allowTransform ?? true;
|
|
38
|
+
case "research":
|
|
39
|
+
return routing?.allowResearch ?? false;
|
|
40
|
+
default:
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -43,3 +43,20 @@ export const IGNORED_FOLDER_GLOBS = [
|
|
|
43
43
|
'**/debug.log',
|
|
44
44
|
'**/Dockerfile',
|
|
45
45
|
];
|
|
46
|
+
export const IGNORED_DIR_NAMES = Array.from(new Set(IGNORED_FOLDER_GLOBS
|
|
47
|
+
.map((pattern) => {
|
|
48
|
+
const match = pattern.match(/^\*\*\/([^*\/]+)\/\*\*$/);
|
|
49
|
+
return match?.[1];
|
|
50
|
+
})
|
|
51
|
+
.filter((name) => !!name)));
|
|
52
|
+
/**
|
|
53
|
+
* Coarse path guard used by runtime search/selection fallback paths.
|
|
54
|
+
* Matches the same ignore-glob intent used during indexing.
|
|
55
|
+
*/
|
|
56
|
+
export function isPathIgnoredByFolderGlobs(filePath) {
|
|
57
|
+
const normalizedPath = String(filePath ?? "").replace(/\\/g, "/");
|
|
58
|
+
return IGNORED_FOLDER_GLOBS.some((pattern) => {
|
|
59
|
+
const cleanPattern = pattern.replace(/^\*\*\/?/, "").replace(/\/\*\*$/, "");
|
|
60
|
+
return cleanPattern.length > 0 && normalizedPath.includes(cleanPattern);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// File: src/modules/fileSearchModule.ts
|
|
2
|
-
import {
|
|
2
|
+
import { execFileSync } from "child_process";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import fs from "fs";
|
|
5
5
|
import { plannerSearchFiles } from "../../db/fileIndex.js";
|
|
6
6
|
import { Config } from "../../config.js";
|
|
7
7
|
import { getDbForRepo } from "../../db/client.js";
|
|
8
8
|
import { IGNORED_EXTENSIONS } from "../../fileRules/ignoredExtensions.js";
|
|
9
|
+
import { IGNORED_DIR_NAMES, isPathIgnoredByFolderGlobs } from "../../fileRules/ignoredPaths.js";
|
|
9
10
|
import { logInputOutput } from "../../utils/promptLogHelper.js";
|
|
10
11
|
async function fetchSummariesForPaths(paths) {
|
|
11
12
|
if (paths.length === 0)
|
|
@@ -63,14 +64,15 @@ export const fileSearchModule = {
|
|
|
63
64
|
// -------------------------------------------------
|
|
64
65
|
if (results.length === 0) {
|
|
65
66
|
try {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const stdout =
|
|
67
|
+
const excludeExtArgs = IGNORED_EXTENSIONS.map(ext => `--exclude=*${ext}`);
|
|
68
|
+
const excludeDirArgs = IGNORED_DIR_NAMES.map(dir => `--exclude-dir=${dir}`);
|
|
69
|
+
const grepArgs = ["-ril", ...excludeDirArgs, ...excludeExtArgs, subQuery, repoRoot];
|
|
70
|
+
const stdout = execFileSync("grep", grepArgs, { encoding: "utf8" });
|
|
70
71
|
results = stdout
|
|
71
72
|
.split("\n")
|
|
72
73
|
.filter(Boolean)
|
|
73
|
-
.map(f => ({ path: f }))
|
|
74
|
+
.map(f => ({ path: f }))
|
|
75
|
+
.filter(record => !isPathIgnoredByFolderGlobs(record.path));
|
|
74
76
|
}
|
|
75
77
|
catch (err) {
|
|
76
78
|
if (err?.status !== 1) {
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import fs from "fs/promises";
|
|
2
2
|
import path from "path";
|
|
3
|
+
import { isPathIgnoredByFolderGlobs } from "../fileRules/ignoredPaths.js";
|
|
3
4
|
export async function resolveTargetsToFiles(targets, exts) {
|
|
4
5
|
const files = [];
|
|
5
6
|
for (const target of targets) {
|
|
7
|
+
if (isPathIgnoredByFolderGlobs(target))
|
|
8
|
+
continue;
|
|
6
9
|
const stat = await fs.stat(target);
|
|
7
10
|
if (stat.isDirectory()) {
|
|
8
11
|
files.push(...await collectFilesRecursive(target, exts));
|
|
@@ -18,6 +21,8 @@ async function collectFilesRecursive(dir, exts) {
|
|
|
18
21
|
const files = [];
|
|
19
22
|
for (const entry of entries) {
|
|
20
23
|
const fullPath = path.join(dir, entry.name);
|
|
24
|
+
if (isPathIgnoredByFolderGlobs(fullPath))
|
|
25
|
+
continue;
|
|
21
26
|
if (entry.isDirectory()) {
|
|
22
27
|
files.push(...await collectFilesRecursive(fullPath, exts));
|
|
23
28
|
}
|