@vincemakes/kiso-tools-node 0.25.0 → 0.26.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/dist/index.d.ts +15 -0
- package/dist/index.js +45 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -106,6 +106,21 @@ export interface WorkspaceToolsOptions {
|
|
|
106
106
|
* kiso over an unusually large or unusually small tree can move them,
|
|
107
107
|
* and the gate can set them low enough to observe the stop.
|
|
108
108
|
*/
|
|
109
|
+
/**
|
|
110
|
+
* DC-49 — roots a WALK may not descend into, as realpaths.
|
|
111
|
+
*
|
|
112
|
+
* The CLI passes `$KISO_HOME`. With the workspace at `~` the walks
|
|
113
|
+
* otherwise report the user's own session logs as though they were
|
|
114
|
+
* their work — and `~/.kiso` escapes the dot-name skip only by
|
|
115
|
+
* accident, so a KISO_HOME anywhere else is walked in full.
|
|
116
|
+
*
|
|
117
|
+
* DISCOVERY, NOT ACCESS. This removes a directory from what a walk
|
|
118
|
+
* FINDS when it descends from above. A path the user or the model
|
|
119
|
+
* NAMES is still served, at the root or inside it: the exclusion is
|
|
120
|
+
* not a permission, and anything the model can name it can still
|
|
121
|
+
* read.
|
|
122
|
+
*/
|
|
123
|
+
readonly excludeRoots?: readonly string[];
|
|
109
124
|
readonly limits?: {
|
|
110
125
|
/** search_text: skip a file larger than this (default 1 MiB). */
|
|
111
126
|
readonly searchMaxFileBytes?: number;
|
package/dist/index.js
CHANGED
|
@@ -562,7 +562,35 @@ export function searchTextTool(opts) {
|
|
|
562
562
|
// the call budget: a silent skip is a result the model cannot
|
|
563
563
|
// tell is incomplete.
|
|
564
564
|
let skippedFiles = 0;
|
|
565
|
+
let excludedDirs = 0;
|
|
565
566
|
let filesSeen = 0;
|
|
567
|
+
// DC-49 — realpath on BOTH sides, so a symlinked HOME still
|
|
568
|
+
// matches (the reviewer's constraint (d): `/tmp` is a symlink to
|
|
569
|
+
// `/private/tmp` on darwin, and a raw string compare there is a
|
|
570
|
+
// gate that passes on one machine and not another).
|
|
571
|
+
//
|
|
572
|
+
// A root that CONTAINS the search root is not an exclusion: the
|
|
573
|
+
// caller pointed at it, and refusing would make an explicit path
|
|
574
|
+
// unservable. That is the reviewer's constraint (c), expressed
|
|
575
|
+
// where it belongs — in the predicate, not in four call sites.
|
|
576
|
+
const realOrSelf = (p) => {
|
|
577
|
+
try {
|
|
578
|
+
return realpathSync(p);
|
|
579
|
+
}
|
|
580
|
+
catch {
|
|
581
|
+
return p;
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
const searchRootReal = realOrSelf(root);
|
|
585
|
+
const excluded = (opts.excludeRoots ?? [])
|
|
586
|
+
.map(realOrSelf)
|
|
587
|
+
.filter((ex) => !(searchRootReal === ex || searchRootReal.startsWith(`${ex}/`)));
|
|
588
|
+
const isExcluded = (dir) => {
|
|
589
|
+
if (excluded.length === 0)
|
|
590
|
+
return false;
|
|
591
|
+
const r = realOrSelf(dir);
|
|
592
|
+
return excluded.some((ex) => r === ex || r.startsWith(`${ex}/`));
|
|
593
|
+
};
|
|
566
594
|
// An explicit flag, NOT `stoppedAt > 0`: a wall-clock budget can
|
|
567
595
|
// expire before the first file is scanned, and a zero-valued
|
|
568
596
|
// sentinel would then read as "never stopped" — the walk would
|
|
@@ -742,8 +770,18 @@ export function searchTextTool(opts) {
|
|
|
742
770
|
if (entry.name.startsWith(".") || entry.name === "node_modules")
|
|
743
771
|
continue;
|
|
744
772
|
const full = join(dir, entry.name);
|
|
745
|
-
if (entry.isDirectory())
|
|
773
|
+
if (entry.isDirectory()) {
|
|
774
|
+
// DC-49 — a walk does not DESCEND into an excluded root.
|
|
775
|
+
// Reaching it from above is what is refused; being
|
|
776
|
+
// pointed at it is not (see `excluded` below, which is
|
|
777
|
+
// seeded from the SEARCH ROOT and therefore empty when
|
|
778
|
+
// the root is itself excluded).
|
|
779
|
+
if (isExcluded(full)) {
|
|
780
|
+
excludedDirs += 1;
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
746
783
|
await walk(full, depth + 1);
|
|
784
|
+
}
|
|
747
785
|
else if (entry.isFile())
|
|
748
786
|
await scanFile(full);
|
|
749
787
|
}
|
|
@@ -766,6 +804,12 @@ export function searchTextTool(opts) {
|
|
|
766
804
|
content += `\n… ${unreadableDirs} unreadable ${unreadableDirs === 1 ? "directory" : "directories"} skipped`;
|
|
767
805
|
// DC-54 — one merged sentence, and only when it happened. A note
|
|
768
806
|
// that always fires says nothing.
|
|
807
|
+
// DC-49 — what a WALK did not enter, in the same discipline the
|
|
808
|
+
// file skips already follow: a scoped result that reads as total
|
|
809
|
+
// is one the model cannot tell is scoped.
|
|
810
|
+
if (excludedDirs > 0) {
|
|
811
|
+
content += `\n… ${excludedDirs} ${excludedDirs === 1 ? "directory" : "directories"} excluded`;
|
|
812
|
+
}
|
|
769
813
|
if (skippedFiles > 0 || stopped) {
|
|
770
814
|
const parts = [];
|
|
771
815
|
if (skippedFiles > 0)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-tools-node",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.1",
|
|
4
4
|
"description": "kiso coding tools for Node hosts — read file, list directory, search text, write/edit file, shell command.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"test": "vitest run"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@vincemakes/kiso-core": "0.
|
|
24
|
+
"@vincemakes/kiso-core": "0.26.1"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/node": "^26.1.2",
|